代码修改后的版本,全部提交

This commit is contained in:
ss001
2026-02-07 15:48:27 +08:00
parent cccbaa37c9
commit c2cda58c65
15604 changed files with 2455502 additions and 0 deletions

View File

@@ -0,0 +1,554 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using COSXML.Log;
using System.Reflection;
using System.IO;
using System.Net.Cache;
/**
* Copyright (c) 2018 Tencent Cloud. All rights reserved.
* 11/7/2018 9:34:42 AM
* bradyxiao
*/
namespace COSXML.Network
{
/// <summary>
/// network request and response
/// type1: command request
/// type2: upload file
/// type3: download file
/// difference: body progress
/// </summary>
public sealed class CommandTask
{
public const string TAG = "CommandTask";
/// <summary>
/// init connectionLimit and statueCode = 100 action
/// </summary>
/// <param name="config"></param>
public static void Init(HttpClientConfig config)
{
ServicePointManager.Expect100Continue = false;
ServicePointManager.DefaultConnectionLimit = config.ConnectionLimit;
}
/// <summary>
/// sync excute
/// </summary>
/// <param name="request"></param>
/// <param name="config"></param>
/// <returns></returns>
public static void Excute(Request request, Response response, HttpClientConfig config)
{
HttpWebRequest httpWebRequest = null;
HttpWebResponse httpWebResponse = null;
try
{
//step1: create HttpWebRequest by request.url
httpWebRequest = HttpWebRequest.Create(request.RequestUrlString) as HttpWebRequest;
httpWebRequest.AllowWriteStreamBuffering = false;
//bind webRequest
request.BindHttpWebRequest(httpWebRequest);
// handler request
HandleHttpWebRequest(httpWebRequest, request, config);
//final: get response
httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse;
//notify has been got response
request.onNotifyGetResponse();
//handle response for [100, 300)
HandleHttpWebResponse(httpWebResponse, response);
}
catch (WebException webEx)
{
if (webEx.Response != null && webEx.Response is HttpWebResponse)
{
//notify has been got response
request.onNotifyGetResponse();
httpWebResponse = (HttpWebResponse)webEx.Response;
//handle response for [400, 500]
HandleHttpWebResponse(httpWebResponse, response);
}
else
{
//QLog.E(TAG, webEx.Message, webEx);
throw;
}
}
catch (Exception ex)
{
//QLog.E(TAG, ex.Message, ex);
throw;
}
finally
{
if (httpWebResponse != null)
{
// print log
PrintResponseInfo(httpWebResponse);
httpWebResponse.Close();
//QLog.D("XIAO", "response close");
}
if (httpWebRequest != null)
{
httpWebRequest.Abort();
//QLog.D("XIAO", "request close");
}
QLog.D(TAG, "close");
}
}
/// <summary>
/// handle request
/// </summary>
/// <param name="httpWebRequest"></param>
/// <param name="request"></param>
/// <param name="config"></param>
private static void HandleHttpWebRequest(HttpWebRequest httpWebRequest, Request request, HttpClientConfig config)
{
HandleHttpWebRequestHeaders(request, httpWebRequest, config);
//setp5: send request content: body
if (request.Body != null)
{
httpWebRequest.ContentLength = request.Body.ContentLength;
request.Body.OnWrite(httpWebRequest.GetRequestStream());
}
//print request start log
PrintReqeustInfo(httpWebRequest);
}
/// <summary>
/// handle response
/// </summary>
/// <param name="httpWebResponse"></param>
/// <param name="response"></param>
private static void HandleHttpWebResponse(HttpWebResponse httpWebResponse, Response response)
{
HandleHttpWebResponseHeaders(response, httpWebResponse);
//handle body
response.Body.HandleResponseBody(httpWebResponse.GetResponseStream());
// close
//httpWebResponse.Close();
}
/// <summary>
/// async to excute
/// </summary>
/// <param name="request"></param>
/// <param name="response"></param>
/// <param name="config"></param>
public static void Schedue(Request request, Response response, HttpClientConfig config)
{
HttpWebRequest httpWebRequest = null;
RequestState requestState = new RequestState();
try
{
requestState.request = request;
requestState.response = response;
httpWebRequest = WebRequest.Create(request.RequestUrlString) as HttpWebRequest;
httpWebRequest.AllowWriteStreamBuffering = false;
//bind webRequest
request.BindHttpWebRequest(httpWebRequest);
//handle request header
HandleHttpWebRequestHeaders(request, httpWebRequest, config);
requestState.httpWebRequest = httpWebRequest;
//handle request body
if(request.Body != null)
{
httpWebRequest.ContentLength = request.Body.ContentLength;
httpWebRequest.BeginGetRequestStream(new AsyncCallback(AsyncRequestCallback), requestState);
}
else
{
//wait for response
httpWebRequest.BeginGetResponse(new AsyncCallback(AsyncResponseCallback), requestState);
}
//print log
PrintReqeustInfo(httpWebRequest);
}
catch (WebException webEx)
{
response.OnFinish(false, webEx);
//abort
requestState.Clear();
QLog.D(TAG, webEx.Message, webEx);
}
catch (Exception ex)
{
response.OnFinish(false, ex);
//abort
requestState.Clear();
QLog.E(TAG, ex.Message, ex);
}
}
public static void AsyncRequestCallback(IAsyncResult ar)
{
RequestState requestState = ar.AsyncState as RequestState;
Stream requestStream = null;
try
{
HttpWebRequest httpWebRequest = requestState.httpWebRequest;
requestStream = httpWebRequest.EndGetRequestStream(ar);
////开始写入数据
//requestState.request.Body.OnWrite(requestStream);
////wait for response
//httpWebRequest.BeginGetResponse(AsyncResponseCallback, requestState);
requestState.request.Body.StartHandleRequestBody(requestStream, delegate(Exception exception)
{
if (exception != null)
{
// handle request body throw exception
requestState.response.OnFinish(false, exception);
//abort
requestState.Clear();
QLog.E(TAG, exception.Message, exception);
}
else
{
//wait for response
httpWebRequest.BeginGetResponse(new AsyncCallback(AsyncResponseCallback), requestState);
}
});
}
catch (Exception ex)
{
requestState.response.OnFinish(false, ex);
//abort
requestState.Clear();
QLog.E(TAG, ex.Message, ex);
}
}
public static void AsyncResponseCallback(IAsyncResult ar)
{
RequestState requestState = ar.AsyncState as RequestState;
HttpWebResponse httpWebResponse = null;
try
{
HttpWebRequest httpWebRequest = requestState.httpWebRequest;
httpWebResponse = (HttpWebResponse)httpWebRequest.EndGetResponse(ar);
//nofity get response
requestState.request.onNotifyGetResponse();
requestState.httpWebResponse = httpWebResponse;
//handle response headers
HandleHttpWebResponseHeaders(requestState.response, httpWebResponse);
Stream responseStream = httpWebResponse.GetResponseStream();
requestState.response.Body.StartHandleResponseBody(responseStream, delegate(bool isSuccess, Exception ex)
{
PrintResponseInfo(httpWebResponse);
requestState.response.OnFinish(isSuccess,ex);
requestState.Clear();
});
}
catch (WebException webEx)
{
if (webEx.Response != null && webEx.Response is HttpWebResponse)
{
//nofity get response
requestState.request.onNotifyGetResponse();
//handle response for [400, 500]
httpWebResponse = (HttpWebResponse)webEx.Response;
requestState.httpWebResponse = httpWebResponse;
//handle response headers
HandleHttpWebResponseHeaders(requestState.response, httpWebResponse);
Stream responseStream = httpWebResponse.GetResponseStream();
requestState.response.Body.StartHandleResponseBody(responseStream, delegate(bool isSuccess, Exception ex)
{
PrintResponseInfo(httpWebResponse);
requestState.response.OnFinish(isSuccess,ex);
requestState.Clear();
});
}
else
{
requestState.response.OnFinish(false, webEx);
//abort
requestState.Clear();
QLog.E(TAG, webEx.Message, webEx);
}
}
catch (Exception ex)
{
requestState.response.OnFinish(false, ex);
//abort
requestState.Clear();
QLog.E(TAG, ex.Message, ex);
}
}
/// <summary>
/// handle request headers
/// </summary>
/// <param name="request"></param>
/// <param name="httpWebRequest"></param>
/// <param name="config"></param>
private static void HandleHttpWebRequestHeaders(Request request, HttpWebRequest httpWebRequest, HttpClientConfig config)
{
// set connect timeout
httpWebRequest.Timeout = config.ConnectionTimeoutMs;
//set read write timeout
httpWebRequest.ReadWriteTimeout = config.ReadWriteTimeoutMs;
// set request method
httpWebRequest.Method = request.Method.ToUpperInvariant();
// set user-agent
httpWebRequest.UserAgent = request.UserAgent;
//set host, net2.0 cannot set;
// set allow auto redirect
httpWebRequest.AllowAutoRedirect = config.AllowAutoRedirect;
// notice: it is not allowed to set common headers with the WebHeaderCollection.Accept
// such as: Connection,Content-Length,Content-Type,Date,Expect. Host,If-Modified-Since,Range, Referer,Transfer-Encoding,User-Agent,Proxy-Connection
//step2: set header and connection properity by request.heders
foreach (KeyValuePair<string, string> pair in request.Headers)
{
HttpHeaderHandle.AddHeader(httpWebRequest.Headers, pair.Key, pair.Value);
}
//step3: set proxy, default proxy = null, improte performation
SetRequestProxy(httpWebRequest, config);
//step4: https, default all true for "*.myqcloud.com"
if (request.IsHttps)
{
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationCertificate);
}
//初始化长度
httpWebRequest.ContentLength = 0L;
}
/// <summary>
/// headle response headers
/// </summary>
/// <param name="response"></param>
/// <param name="httpWebResponse"></param>
private static void HandleHttpWebResponseHeaders(Response response, HttpWebResponse httpWebResponse)
{
response.Code = (int)httpWebResponse.StatusCode;
response.Message = httpWebResponse.StatusDescription;
WebHeaderCollection headers = httpWebResponse.Headers;
if (headers != null)
{
Dictionary<string, List<string>> result = new Dictionary<string, List<string>>(headers.Count);
for (int i = 0; i < headers.Count; i++)
{
List<string> values = null;
if (headers.GetValues(i) != null)
{
values = new List<string>();
foreach (string value in headers.GetValues(i))
{
values.Add(value);
}
}
result.Add(headers.GetKey(i), values);
}
response.Headers = result;
}
response.ContentLength = httpWebResponse.ContentLength;
response.ContentType = httpWebResponse.ContentType;
if (response.Body != null)
{
response.Body.ContentLength = httpWebResponse.ContentLength;
response.ContentType = httpWebResponse.ContentType;
}
//handle header
response.HandleResponseHeader();
}
/// <summary>
/// set proxy
/// </summary>
/// <param name="httpWebRequest"></param>
/// <param name="config"></param>
private static void SetRequestProxy(HttpWebRequest httpWebRequest, HttpClientConfig config)
{
httpWebRequest.Proxy = null;
if (!String.IsNullOrEmpty(config.ProxyHost))
{
if (config.ProxyPort < 0)
{
httpWebRequest.Proxy = new WebProxy(config.ProxyHost);
}
else
{
httpWebRequest.Proxy = new WebProxy(config.ProxyHost, config.ProxyPort);
}
if (!String.IsNullOrEmpty(config.ProxyUserName))
{
httpWebRequest.Proxy.Credentials = String.IsNullOrEmpty(config.ProxyDomain) ?
new NetworkCredential(config.ProxyUserName, config.ProxyUserPassword ?? String.Empty) :
new NetworkCredential(config.ProxyUserName, config.ProxyUserPassword ?? String.Empty,
config.ProxyDomain);
}
httpWebRequest.PreAuthenticate = true; // 代理验证
}
}
/// <summary>
/// check certificate
/// </summary>
/// <param name="sender"></param>
/// <param name="certificate"></param>
/// <param name="chain"></param>
/// <param name="errors"></param>
/// <returns></returns>
private static bool CheckValidationCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true;
}
/// <summary>
/// print request info
/// </summary>
/// <param name="httpWebRequest"></param>
private static void PrintReqeustInfo(HttpWebRequest httpWebRequest)
{
StringBuilder requestLog = new StringBuilder("--->");
requestLog.Append(httpWebRequest.Method).Append(' ').Append(httpWebRequest.Address.AbsoluteUri).Append('\n');
int count = httpWebRequest.Headers.Count;
for (int i = 0; i < count; i++)
{
requestLog.Append(httpWebRequest.Headers.GetKey(i)).Append(":").Append(httpWebRequest.Headers.GetValues(i)[0]).Append('\n');
}
requestLog.Append("allow auto redirect: " + httpWebRequest.AllowAutoRedirect).Append('\n');
requestLog.Append("connect timeout: " + httpWebRequest.Timeout).Append('\n');
requestLog.Append("read write timeout: " + httpWebRequest.ReadWriteTimeout).Append('\n');
requestLog.Append("AllowWriteStreamBuffering: " + httpWebRequest.AllowWriteStreamBuffering).Append('\n');
//requestLog.Append("proxy: " + (httpWebRequest.Proxy == null ? "null" : ((WebProxy)httpWebRequest.Proxy).Address.ToString()));
requestLog.Append("<---");
QLog.D(TAG, requestLog.ToString());
}
/// <summary>
/// print response info
/// </summary>
/// <param name="httpWebResponse"></param>
private static void PrintResponseInfo(HttpWebResponse httpWebResponse)
{
StringBuilder responseLog = new StringBuilder("--->");
responseLog.Append(httpWebResponse.Method).Append(' ').Append(httpWebResponse.ResponseUri.AbsoluteUri).Append('\n');
responseLog.Append((int)httpWebResponse.StatusCode).Append(' ').Append(httpWebResponse.StatusDescription).Append('\n'); ;
int count = httpWebResponse.Headers.Count;
for (int i = 0; i < count; i++)
{
responseLog.Append(httpWebResponse.Headers.GetKey(i)).Append(":").Append(httpWebResponse.Headers.GetValues(i)[0]).Append('\n');
}
responseLog.Append("<---");
QLog.D(TAG, responseLog.ToString());
}
internal static class HttpHeaderHandle
{
private static MethodInfo addHeaderMethod;
private static readonly ICollection<PlatformID> monoPlatforms = new List<PlatformID> { PlatformID.MacOSX, PlatformID.Unix };
private static bool? isMonoPlatform;
internal static void AddHeader(WebHeaderCollection webHeaderCollection, string key, string value)
{
if (isMonoPlatform == null)
{
isMonoPlatform = monoPlatforms.Contains(Environment.OSVersion.Platform);
}
// HTTP headers should be encoded to iso-8859-1,
// however it will be encoded automatically by HttpWebRequest in mono.
if (false == isMonoPlatform)
{
// Encode headers for win platforms.
}
if (addHeaderMethod == null)
{
// Specify the internal method name for adding headers
// mono: AddWithoutValidate
// win: AddInternal
//var internalMethodName = (isMonoPlatform == false) ? "AddWithoutValidate" : "AddInternal";
var internalMethodName = "AddWithoutValidate";
QLog.D(TAG, internalMethodName.ToString());
var method = typeof(WebHeaderCollection).GetMethod(
internalMethodName,
BindingFlags.NonPublic | BindingFlags.Instance,
null,
new Type[] { typeof(string), typeof(string) },
null);
addHeaderMethod = method;
}
addHeaderMethod.Invoke(webHeaderCollection, new Object[] { key, value });
}
}
internal class RequestState
{
public HttpWebRequest httpWebRequest;
public HttpWebResponse httpWebResponse;
public Response response;
public Request request;
public RequestState()
{
httpWebRequest = null;
httpWebResponse = null;
response = null;
request = null;
}
public void Clear()
{
if (httpWebRequest != null) httpWebRequest.Abort();
if (httpWebResponse != null) httpWebResponse.Close();
QLog.D(TAG, "Close");
}
}
}
}

View File

@@ -0,0 +1,413 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using COSXML.Network;
using COSXML.Model;
using COSXML.Common;
using COSXML.Auth;
using COSXML.CosException;
using COSXML.Transfer;
using COSXML.Model.Tag;
using COSXML.Log;
using System.IO;
using COSXML.Model.Object;
using COSXML.Utils;
/**
* Copyright (c) 2018 Tencent Cloud. All rights reserved.
* 11/6/2018 8:52:29 PM
* bradyxiao
*/
namespace COSXML.Network
{
/// <summary>
/// input: CosRequest; output: CosResponse
/// </summary>
public sealed class HttpClient
{
private static string TAG = "HttpClient";
private static HttpClientConfig config;
private static QCloudCredentialProvider credentialsProvider;
private static HttpClient instance;
private static Object sync = new Object();
private static Object syncInstance = new Object();
private const int MAX_ACTIVIE_TASKS = 5;
private volatile int activieTasks = 0;
public static HttpClient GetInstance()
{
lock (syncInstance)
{
if (instance == null)
{
instance = new HttpClient();
}
}
return instance;
}
public static void Init(HttpClientConfig config, QCloudCredentialProvider credentialsProvider)
{
lock (sync)
{
if (config == null)
{
throw new CosClientException((int)CosClientError.INVALID_ARGUMENT, "HttpClientConfig = null");
}
HttpClient.config = config;
HttpClient.credentialsProvider = credentialsProvider;
// init grobal httpwebreqeust
CommandTask.Init(HttpClient.config);
}
}
private HttpClient()
{
if (config == null)
{
throw new CosClientException((int)CosClientError.INTERNA_LERROR, "need to call Init(HttpClientConfig, QCloudCredentialProvider) before");
}
}
/// <summary>
/// excute request
/// </summary>
/// <param name="cosRequest"></param>
/// <param name="cosResult"></param>
/// <exception cref="COSXML.CosException.CosClientException">CosClientException</exception>
/// <exception cref="COSXML.CosException.CosServerException">CosServerException</exception>
public void Excute(CosRequest cosRequest, CosResult cosResult)
{
//HttpTask httpTask = new HttpTask();
//httpTask.cosRequest = cosRequest;
//httpTask.cosResult = cosResult;
//httpTask.isSchedue = false;
InternalExcute(cosRequest, cosResult);
}
public void Schedue(CosRequest cosRequest, CosResult cosResult, COSXML.Callback.OnSuccessCallback<CosResult> successCallback,
COSXML.Callback.OnFailedCallback failCallback)
{
//HttpTask httpTask = new HttpTask();
//httpTask.cosRequest = cosRequest;
//httpTask.cosResult = cosResult;
//httpTask.isSchedue = true;
//httpTask.successCallback = successCallback;
//httpTask.failCallback = failCallback;
InternalSchedue(cosRequest, cosResult, successCallback, failCallback);
}
/// <summary>
/// excute request
/// </summary>
/// <param name="cosRequest"></param>
/// <param name="cosResult"></param>
/// <exception cref="COSXML.CosException.CosClientException">CosClientException</exception>
/// <exception cref="COSXML.CosException.CosServerException">CosServerException</exception>
public void InternalExcute(CosRequest cosRequest, CosResult cosResult)
{
try
{
Request request = CreateRequest(cosRequest);
//extern informations exchange
cosResult.ExternInfo(cosRequest);
Response response;
if (cosRequest is GetObjectRequest)
{
GetObjectRequest getObjectRequest = cosRequest as GetObjectRequest;
response = new CosResponse(cosResult, getObjectRequest.GetSaveFilePath(), getObjectRequest.GetLocalFileOffset(),
getObjectRequest.GetCosProgressCallback());
}
else
{
response = new CosResponse(cosResult, null, -1L, null);
}
cosRequest.BindRequest(request);
CommandTask.Excute(request, response, config);
}
catch (CosServerException)
{
throw ;
}
catch (CosClientException)
{
throw ;
}
catch (Exception ex)
{
throw new CosClientException((int)CosClientError.BAD_REQUEST, ex.Message, ex);
}
}
public void excute(Request request, Response response)
{
try
{
CommandTask.Excute(request, response, config);
}
catch (CosServerException)
{
throw ;
}
catch (CosClientException)
{
throw ;
}
catch (Exception ex)
{
throw new CosClientException((int)CosClientError.BAD_REQUEST, ex.Message, ex);
}
}
public void InternalSchedue(CosRequest cosRequest, CosResult cosResult, COSXML.Callback.OnSuccessCallback<CosResult> successCallback,
COSXML.Callback.OnFailedCallback failCallback)
{
try
{
Request request = CreateRequest(cosRequest);
Response response;
if (cosRequest is GetObjectRequest)
{
GetObjectRequest getObjectRequest = cosRequest as GetObjectRequest;
response = new CosResponse(cosResult, getObjectRequest.GetSaveFilePath(), getObjectRequest.GetLocalFileOffset(),
getObjectRequest.GetCosProgressCallback(), successCallback, failCallback);
}
else
{
response = new CosResponse(cosResult, null, -1L, null,successCallback, failCallback);
}
cosRequest.BindRequest(request);
CommandTask.Schedue(request, response, config);
}
catch (CosServerException serverException)
{
//throw serverException;
failCallback(null, serverException);
}
catch (CosClientException clientException)
{
//throw clientException;
failCallback(clientException, null);
}
catch (Exception ex)
{
//throw new CosClientException((int)CosClientError.BAD_REQUEST, ex.Message, ex);
failCallback(new CosClientException((int)CosClientError.BAD_REQUEST, ex.Message, ex), null);
}
}
private Request CreateRequest(CosRequest cosRequest)
{
cosRequest.CheckParameters();
string requestUrlWithSign = cosRequest.RequestURLWithSign;
Request request = new Request();
request.Method = cosRequest.Method;
if (requestUrlWithSign != null)
{
if (requestUrlWithSign.StartsWith("https"))
{
request.IsHttps = true;
}
else
{
request.IsHttps = false;
}
request.RequestUrlString = requestUrlWithSign;
}
else
{
request.IsHttps = (bool)cosRequest.IsHttps;
request.Url = CreateUrl(cosRequest);
request.Host = cosRequest.GetHost();
}
request.UserAgent = config.UserAgnet;
Dictionary<string, string> headers = cosRequest.GetRequestHeaders();
if (headers != null)
{
foreach (KeyValuePair<string, string> pair in headers)
{
request.AddHeader(pair.Key, pair.Value);
}
}
request.Body = cosRequest.GetRequestBody();
// cacluate md5
if (CheckNeedMd5(request, cosRequest.IsNeedMD5) && request.Body != null)
{
request.AddHeader(CosRequestHeaderKey.CONTENT_MD5, request.Body.GetMD5());
}
// content type header
if(request.Body != null && request.Body.ContentType != null &&
!request.Headers.ContainsKey(CosRequestHeaderKey.CONTENT_TYPE)) {
request.AddHeader(CosRequestHeaderKey.CONTENT_TYPE, request.Body.ContentType);
}
//cacluate sign, and add it.
if (requestUrlWithSign == null)
{
cosRequest.GetSignSourceProvider().setCosHost(cosRequest.GetCOSHost());
CheckSign(cosRequest.GetSignSourceProvider(), request);
}
return request;
}
private HttpUrl CreateUrl(CosRequest cosRequest)
{
HttpUrl httpUrl = new HttpUrl();
httpUrl.Scheme = (bool)cosRequest.IsHttps ? "https" : "http";
httpUrl.Host = cosRequest.GetHost();
httpUrl.Path = URLEncodeUtils.EncodePathOfURL(cosRequest.RequestPath);
httpUrl.SetQueryParameters(cosRequest.GetRequestParamters());
return httpUrl;
}
/// <summary>
/// add authorization
/// </summary>
/// <param name="qcloudSignSource">QCloudSignSource</param>
/// <param name="request"></param>
private void CheckSign(QCloudSignSource qcloudSignSource, Request request)
{
// has authorizaiton, notice: using request.Headers otherwise, error
if (request.Headers.ContainsKey(CosRequestHeaderKey.AUTHORIZAIION))
{
QLog.D(TAG, "has add authorizaiton in headers");
return;
}
//has no authorization, but signSourceProvider == null
if (qcloudSignSource == null)
{
QLog.D(TAG, "signSourceProvider == null");
return;
}
if (credentialsProvider == null) throw new ArgumentNullException("credentialsProvider == null");
CosXmlSigner signer = new CosXmlSigner();
signer.Sign(request, qcloudSignSource, credentialsProvider.GetQCloudCredentials());
}
private bool CheckNeedMd5(Request request, bool isNeedMd5)
{
bool result = isNeedMd5;
if (request.Headers.ContainsKey(CosRequestHeaderKey.CONTENT_MD5))
{
result = false;
}
return result;
}
/// <summary>
/// cos response
/// 分为两类:
/// 一类下载文件
/// 一类直接读取数据
/// </summary>
private class CosResponse : Response
{
private CosResult cosResult;
private COSXML.Callback.OnSuccessCallback<CosResult> successCallback;
private COSXML.Callback.OnFailedCallback faileCallback;
private const int MAX_BUFFER_SIZE = 4096;
public CosResponse(CosResult cosResult, string saveFilePath, long saveFileOffset, COSXML.Callback.OnProgressCallback downloadProgressCallback)
{
this.cosResult = cosResult;
if (saveFilePath != null)
{
this.Body = new ResponseBody(saveFilePath, saveFileOffset);
this.Body.ProgressCallback = downloadProgressCallback;
}
else
{
this.Body = new ResponseBody();
}
}
public CosResponse(CosResult cosResult, string saveFilePath, long saveFileOffset, COSXML.Callback.OnProgressCallback downloadProgressCallback,
COSXML.Callback.OnSuccessCallback<CosResult> successCallback,
COSXML.Callback.OnFailedCallback failCallback):this(cosResult, saveFilePath, saveFileOffset, downloadProgressCallback)
{
this.successCallback = successCallback;
this.faileCallback = failCallback;
}
/// <summary>
/// response has been obtain, and parse headers from response
/// </summary>
public override void HandleResponseHeader()
{
cosResult.httpCode = Code;
cosResult.httpMessage = Message;
cosResult.responseHeaders = Headers;
cosResult.InternalParseResponseHeaders();
if (Code >= 300)
{
this.Body.ParseStream = PaserServerError;
}
else
{
this.Body.ParseStream = cosResult.ParseResponseBody;
}
}
public void PaserServerError(Stream inputStream, string contentType, long contentLength)
{
CosServerException cosServerException = new CosServerException(cosResult.httpCode, cosResult.httpMessage);
List<string> values;
Headers.TryGetValue("x-cos-request-id", out values);
cosServerException.requestId = (values != null && values.Count > 0) ? values[0] : null;
Headers.TryGetValue("x-cos-trace-id", out values);
cosServerException.traceId = (values != null && values.Count > 0) ? values[0] : null;
if (inputStream != null)
{
CosServerError cosServerError = new CosServerError();
try
{
XmlParse.ParseCosError(inputStream, cosServerError);
if (cosServerException.requestId != null) cosServerException.requestId = cosServerError.requestId;
if (cosServerException.traceId != null) cosServerException.traceId = cosServerError.traceId;
cosServerException.resource = cosServerError.resource;
cosServerException.errorCode = cosServerError.code;
cosServerException.errorMessage = cosServerError.message;
}
catch (Exception ex)
{
QLog.D(TAG, ex.Message);
}
}
throw cosServerException;
}
/// <summary>
/// error
/// </summary>
/// <param name="ex"></param>
public override void OnFinish(bool isSuccess, Exception ex)
{
if (isSuccess) successCallback(cosResult);
else
{
if (ex is CosClientException) faileCallback(ex as CosClientException, null);
else if (ex is CosServerException) faileCallback(null, ex as CosServerException);
else faileCallback(new CosClientException((int)CosClientError.INTERNA_LERROR, ex.Message, ex), null);
}
}
}
}
}

View File

@@ -0,0 +1,256 @@
using System;
using System.Collections.Generic;
using System.Text;
using COSXML.Common;
/**
* Copyright (c) 2018 Tencent Cloud. All rights reserved.
* 11/6/2018 8:58:18 PM
* bradyxiao
*/
namespace COSXML.Network
{
public class HttpClientConfig
{
private string userAgent;
private bool allowAutoRedirect;
private int connectionTimeoutMs;
private int readWriteTimeoutMs;
private int maxRetry;
private int connectionLimit;
private string proxyHost;
private int proxyPort;
private string proxyUserName;
private string proxyUserPassword;
private string proxyDomain;
private HttpClientConfig(Builder builder)
{
this.userAgent = builder.userAgent;
this.allowAutoRedirect = builder.allowAutoRedirect;
this.connectionTimeoutMs = builder.connectionTimeoutMs;
this.readWriteTimeoutMs = builder.readWriteTimeoutMs;
this.maxRetry = builder.maxRetry;
this.connectionLimit = builder.connectionLimit;
this.proxyHost = builder.proxyHost;
this.proxyPort = builder.proxyPort;
this.proxyUserName = builder.proxyUserName;
this.proxyUserPassword = builder.proxyUserPassword;
this.proxyDomain = builder.proxyDomain;
}
public string UserAgnet
{
get
{
return userAgent;
}
}
public bool AllowAutoRedirect
{
get { return allowAutoRedirect; }
}
public int ConnectionTimeoutMs
{
get
{
return connectionTimeoutMs;
}
}
public int ReadWriteTimeoutMs
{
get
{
return readWriteTimeoutMs;
}
}
public int MaxRery
{
get
{
return maxRetry;
}
}
public int ConnectionLimit
{
get
{
return connectionLimit;
}
}
public string ProxyHost
{
get
{
return proxyHost;
}
}
public int ProxyPort
{
get
{
return proxyPort;
}
}
public string ProxyUserName
{
get
{
return proxyUserName;
}
}
public string ProxyUserPassword
{
get
{
return proxyUserPassword;
}
}
public string ProxyDomain
{
get
{
return proxyDomain;
}
}
public class Builder
{
internal string userAgent = CosVersion.GetUserAgent();
internal bool allowAutoRedirect = true;
internal int connectionTimeoutMs = 45000;
internal int readWriteTimeoutMs = 45000;
internal int maxRetry = 3;
internal int connectionLimit = 512;
internal int proxyPort = -1;
internal string proxyHost = null;
internal string proxyUserName;
internal string proxyUserPassword;
internal string proxyDomain;
public Builder() { }
public Builder AllowAutoRedirect(bool allow)
{
this.allowAutoRedirect = allow;
return this;
}
public Builder SetConnectionLimit(int connectionLimit)
{
if (connectionLimit > 2)
{
this.connectionLimit = connectionLimit;
}
return this;
}
public Builder SetMaxRetry(int maxRetry)
{
if (maxRetry > 0)
{
this.maxRetry = maxRetry;
}
return this;
}
public Builder SetConnectionTimeoutMs(int connectionTimeoutMs)
{
if (connectionTimeoutMs > 10000)
{
this.connectionTimeoutMs = connectionTimeoutMs;
}
return this;
}
public Builder SetReadWriteTimeoutMs(int readWriteTimeoutMs)
{
if (readWriteTimeoutMs > 10000)
{
this.readWriteTimeoutMs = readWriteTimeoutMs;
}
return this;
}
public Builder SetProxyHost(string host)
{
this.proxyHost = host;
return this;
}
public Builder SetProxyPort(int port)
{
this.proxyPort = port;
return this;
}
public Builder SetProxyUserName(string userName)
{
this.proxyUserName = userName;
return this;
}
public Builder SetProxyUserPassword(string password)
{
this.proxyUserPassword = password;
return this;
}
public Builder SetProxyDomain(string domain)
{
this.proxyDomain = domain;
return this;
}
public HttpClientConfig Build()
{
return new HttpClientConfig(this);
}
}
}
}

View File

@@ -0,0 +1,56 @@
//using System;
//using System.Collections.Generic;
//using System.Text;
///**
//* Copyright (c) 2018 Tencent Cloud. All rights reserved.
//* 11/6/2018 11:56:45 AM
//* bradyxiao
// * copy from oktthp
//*/
//namespace COSXML.Network
//{
// public sealed class HttpMethod
// {
// private HttpMethod() { }
// public static bool InvalidatesCache(String method)
// {
// return method.Equals("POST", StringComparison.OrdinalIgnoreCase)
// || method.Equals("PATCH", StringComparison.OrdinalIgnoreCase)
// || method.Equals("PUT", StringComparison.OrdinalIgnoreCase)
// || method.Equals("DELETE", StringComparison.OrdinalIgnoreCase)
// || method.Equals("MOVE", StringComparison.OrdinalIgnoreCase); // WebDAV
// }
// public static bool RequiresRequestBody(string method)
// {
// return method.Equals("POST", StringComparison.OrdinalIgnoreCase)
// || method.Equals("PUT", StringComparison.OrdinalIgnoreCase)
// || method.Equals("PATCH", StringComparison.OrdinalIgnoreCase)
// || method.Equals("PROPPATCH", StringComparison.OrdinalIgnoreCase)// WebDAV
// || method.Equals("REPORT", StringComparison.OrdinalIgnoreCase); // CalDAV/CardDAV (defined in WebDAV Versioning)
// }
// public static bool PermitsRequestBody(string method)
// {
// return RequiresRequestBody(method)
// || method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase)
// || method.Equals("DELETE", StringComparison.OrdinalIgnoreCase) // Permitted as spec is ambiguous.
// || method.Equals("PROPFIND", StringComparison.OrdinalIgnoreCase) // (WebDAV) without body: request <allprop/>
// || method.Equals("MKCOL", StringComparison.OrdinalIgnoreCase) // (WebDAV) may contain a body, but behaviour is unspecified
// || method.Equals("LOCK", StringComparison.OrdinalIgnoreCase); // (WebDAV) body: create lock, without body: refresh lock
// }
// public static bool RedirectsWithBody(string method)
// {
// return method.Equals("PROPFIND", StringComparison.OrdinalIgnoreCase);
// }
// public static bool RedirectsToGet(string method)
// {
// return !method.Equals("PROPFIND", StringComparison.OrdinalIgnoreCase);
// }
// }
//}

View File

@@ -0,0 +1,23 @@
using COSXML.Model;
using System;
using System.Collections.Generic;
using System.Text;
/* ==============================================================================
* Copyright 2016-2019 Tencent Cloud. All Rights Reserved.
* Authbradyxiao
* Date2019/4/4 16:49:33
* ==============================================================================*/
namespace Tencent.QCloud.Cos.Sdk.Network
{
public class HttpTask
{
internal CosRequest cosRequest;
internal CosResult cosResult;
internal bool isSchedue = false;
internal COSXML.Callback.OnSuccessCallback<CosResult> successCallback;
internal COSXML.Callback.OnFailedCallback failCallback;
}
}

281
COSXML/Network/HttpUrl.cs Normal file
View File

@@ -0,0 +1,281 @@
using System;
using System.Collections.Generic;
using System.Text;
/**
* Copyright (c) 2018 Tencent Cloud. All rights reserved.
* 11/6/2018 11:37:45 AM
* bradyxiao
*/
namespace COSXML.Network
{
/**
* //最基本的划分
[scheme:]scheme-specific-part[#fragment]
//对scheme-specific-part进一步划分
[scheme:][//authority][path][?query][#fragment]
//对authority再次划分, 这是最细分的结构
[scheme:][//host:port][path][?query][#fragment]
*/
public sealed class HttpUrl
{
private string scheme;
private string userName = "";
private string userPwd = "";
private string host;
private int port = -1;
private string path = "/";
private Dictionary<string, string> queryParameters;
private string fragment;
public HttpUrl()
{
this.queryParameters = new Dictionary<string, string>();
}
public string Scheme
{
set
{
if (value == null) throw new ArgumentNullException("scheme == null");
if (value.Equals("http", StringComparison.OrdinalIgnoreCase))
{
this.scheme = "http";
}
else if (value.Equals("https", StringComparison.OrdinalIgnoreCase))
{
this.scheme = "https";
}
else
{
throw new ArgumentException("unexpected scheme: " + scheme);
}
}
get{return this.scheme;}
}
public string UserName
{
set
{
if (value == null) throw new ArgumentNullException("userName == null");
this.userName = value;
}
get{return this.userName;}
}
public string UserPassword
{
set
{
if (value == null) throw new ArgumentNullException("userPwd == null");
this.userPwd = value;
}
get {return this.userPwd;}
}
public string Host
{
set
{
if (value == null) throw new ArgumentNullException("host == null");
this.host = value;
}
get { return this.host; }
}
public int Port
{
set
{
if (value <= 0 || value >= 65535) throw new ArgumentException("unexpected port: " + port);
this.port = value;
}
get { return this.port; }
}
public string Path
{
set
{
if (value != null)
{
this.path = value; // need url encode
}
}
get { return path; }
}
public void SetQueryParameters(Dictionary<string, string> queryParameters)
{
if (queryParameters != null)
{
foreach (KeyValuePair<string, string> pair in queryParameters)
{
this.queryParameters.Add(pair.Key, pair.Value);
}
}
}
public Dictionary<string, string> GetQueryParameters()
{
return queryParameters;
}
public string Fragment
{
set
{
this.fragment = value;
}
get { return this.fragment; }
}
public override string ToString()
{
//if (scheme == null) throw new ArgumentNullException("scheme == null");
//if (host == null) throw new ArgumentNullException("host == null");
StringBuilder url = new StringBuilder();
url.Append(scheme)
.Append("://");
if (userName != String.Empty || userPwd != String.Empty)
{
url.Append(userName);
if (userPwd != String.Empty)
{
url.Append(':')
.Append(userPwd);
}
url.Append('@');
}
if (host.IndexOf(':') != -1)
{
url.Append('[')
.Append(host)
.Append(']');
}
else
{
url.Append(host);
}
int effectivePort = EffecivePort();
if (effectivePort != DefaultPort(scheme))
{
url.Append(':')
.Append(effectivePort);
}
url.Append(path);
StringBuilder query = new StringBuilder();
foreach (KeyValuePair<string, string> pair in queryParameters)
{
query.Append(pair.Key);
if (!String.IsNullOrEmpty(pair.Value))
{
query.Append('=').Append(pair.Value);
}
query.Append('&');
}
string queryString = query.ToString();
if (queryString.EndsWith("&"))
{
queryString = queryString.Remove(queryString.Length - 1);
url.Append('?');
url.Append(queryString);
}
if (fragment != null)
{
url.Append('#')
.Append(fragment);
}
return url.ToString();
}
public int EffecivePort()
{
return port != -1 ? port : DefaultPort(scheme);
}
private int DefaultPort(string scheme)
{
if (scheme.Equals("http", StringComparison.OrdinalIgnoreCase))
{
return 80;
}
else if (scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
{
return 443;
}
else
{
return -1;
}
}
//private int DelimiterOffset(string input, int pos, int limit, char delimiter)
//{
// for (int i = pos; i < limit; i++)
// {
// if (input[i] == delimiter) return i;
// }
// return limit;
//}
//private int DelimiterOffset(string input, int pos, int limit, string delimiters)
//{
// for (int i = pos; i < limit; i++)
// {
// if (delimiters.IndexOf(input[i]) != -1) return i;
// }
// return limit;
//}
//private void PathSegmentToString(StringBuilder outPut, List<string> pathSegments)
//{
// foreach (string path in pathSegments)
// {
// outPut.Append('/').Append(path);
// }
//}
//private void NamesAndValuesToQueryString(StringBuilder outPut, List<string> namesAndValues)
//{
// for (int i = 0, size = namesAndValues.Count; i < size; i += 2)
// {
// string name = namesAndValues[i];
// string value = namesAndValues[i + 1];
// if (i > 0) outPut.Append('&');
// outPut.Append(name);
// if (value != null)
// {
// outPut.Append('=');
// outPut.Append(value);
// }
// }
//}
}
}

183
COSXML/Network/Request.cs Normal file
View File

@@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Text;
using COSXML.Auth;
using COSXML.Log;
using COSXML.Common;
using System.Net;
/**
* Copyright (c) 2018 Tencent Cloud. All rights reserved.
* 11/2/2018 4:40:14 PM
* bradyxiao
*/
namespace COSXML.Network
{
public sealed class Request
{
private static string TAG = "Request";
private string method; // post put get delete head, etc.
private HttpUrl url; // shceme://host:port/path?query# etc.
private Dictionary<string, string> headers; // key : value
private RequestBody body; // file or byte, etc.
private Object tag; // package tag for request
private bool isHttps;
private string userAgent;
private string host;
private string urlString;
private HttpWebRequest realeHttpRequest;
public Request()
{
headers = new Dictionary<string, string>();
this.onNotifyGetResponse = this.HandleGetResponse;
}
public string Method
{
get { return method; }
set { method = value; }
}
public bool IsHttps
{
get { return isHttps; }
set { isHttps = value; }
}
public string UserAgent
{
get { return userAgent == null ? CosVersion.GetUserAgent() : userAgent; }
set { userAgent = value; }
}
public string Host
{
get { return host == null ? url.Host : host; }
set { host = value; }
}
public HttpUrl Url
{
get
{
//if (url == null) throw new ArgumentNullException("httpUrl == null");
return url;
}
set
{
if (value == null) throw new ArgumentNullException("httpUrl == null");
url = value;
}
}
public string RequestUrlString
{
get
{
if (urlString == null)
{
urlString = url.ToString();
}
return urlString;
}
set { urlString = value; }
}
public Dictionary<string, string> Headers
{
get { return headers; }
private set { }
}
public void AddHeader(string name, string value)
{
try
{
headers.Add(name, value);
}
catch (ArgumentNullException)
{
QLog.D(TAG, "AddHeader: name is null");
}
catch (ArgumentException)
{
if (String.IsNullOrEmpty(value)){ return; }
if (!String.IsNullOrEmpty(headers[name]))
{
headers[name] = headers[name] + ',' + value;
}
else
{
headers[name] = value;
}
}
}
public void SetHeader(string name, string value)
{
try
{
headers.Add(name, value);
}
catch (ArgumentNullException)
{
QLog.D(TAG, "SetHeader: name is null");
}
catch (ArgumentException)
{
headers[name] = value;
}
}
public RequestBody Body
{
get { return body; }
set { body = value; }
}
public Object Tag
{
get { return tag; }
set { tag = value; }
}
public override string ToString()
{
StringBuilder str = new StringBuilder();
str.Append("Request{method=").Append(method)
.Append(", url=").Append(url)
.Append(", tag=").Append(tag)
.Append('}');
return str.ToString();
}
public COSXML.Callback.OnNotifyGetResponse onNotifyGetResponse;
private void HandleGetResponse()
{
if (body != null)
{
body.OnNotifyGetResponse();
}
}
public void BindHttpWebRequest(HttpWebRequest httpWebRequest)
{
this.realeHttpRequest = httpWebRequest;
}
public void Cancel()
{
if (realeHttpRequest != null)
{
realeHttpRequest.Abort();
}
}
}
}

View File

@@ -0,0 +1,874 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using COSXML.Common;
using COSXML.Log;
using COSXML.Utils;
/**
* Copyright (c) 2018 Tencent Cloud. All rights reserved.
* 11/2/2018 4:33:28 PM
* bradyxiao
*/
namespace COSXML.Network
{
/// <summary>
/// request body for http request
/// </summary>
public abstract class RequestBody
{
protected static string TAG = typeof(RequestBody).Name;
public const int SEGMENT_SIZE = 64 * 1024;// 64kb
protected long contentLength;
protected string contentType;
protected Callback.OnProgressCallback progressCallback;
/// <summary>
/// body length
/// </summary>
public virtual long ContentLength
{ get{return contentLength;} set{contentLength = value;} }
/// <summary>
/// body mime type
/// </summary>
public virtual string ContentType
{ get { return contentType; } set { contentType = value; } }
/// <summary>
/// calculation content md5
/// </summary>
/// <returns></returns>
public abstract string GetMD5();
/// <summary>
/// Synchronization method: write data to outputStream
/// </summary>
/// <param name="outputStream"> output stream for writing data</param>
public abstract void OnWrite(Stream outputStream);
/// <summary>
/// Asynchronous method: handle request body
/// </summary>
/// <param name="outputStream"></param>
/// <param name="EndRequestBody"></param>
public abstract void StartHandleRequestBody(Stream outputStream, EndRequestBody endRequestBody);
public Callback.OnProgressCallback ProgressCallback
{
get { return progressCallback; }
set { progressCallback = value; }
}
/// <summary>
/// notify progress is complete!
/// </summary>
internal void OnNotifyGetResponse()
{
if (progressCallback != null && contentLength >= 0)
{
progressCallback(contentLength, contentLength);
}
}
/// <summary>
/// calculation progress
/// </summary>
/// <param name="complete"></param>
/// <param name="total"></param>
protected void UpdateProgress(long complete, long total)
{
if (total == 0)progressCallback(0, 0);
else if (complete < total) progressCallback(complete, total);
else progressCallback(total - 1, total);
}
}
public class RequestBodyState
{
public byte[] buffer;
public long complete;
public Stream outputStream;
public EndRequestBody endRequestBody;
}
public delegate void EndRequestBody(Exception exception);
public class ByteRequestBody : RequestBody
{
private readonly byte[] data;
//private RequestBodyState requestBodyState;
public ByteRequestBody(byte[] data)
{
this.data = data;
contentLength = data.Length;
contentType = CosRequestHeaderKey.APPLICATION_XML;
}
public override void OnWrite(Stream outputStream)
{
try
{
int completed = 0;
while (completed + SEGMENT_SIZE < contentLength)
{
outputStream.Write(data, completed, SEGMENT_SIZE);
outputStream.Flush();
completed += SEGMENT_SIZE;
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
}
if (completed < contentLength)
{
outputStream.Write(data, completed, (int)(contentLength - completed));//包括本身
outputStream.Flush();
if (progressCallback != null)
{
UpdateProgress(contentLength, contentLength);
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (outputStream != null)
{
outputStream.Flush();
outputStream.Close();
outputStream.Dispose();
//QLog.D("XIAO", "stream close");
outputStream = null;
}
}
}
public override string GetMD5()
{
return DigestUtils.GetMd5ToBase64(data);
}
public override void StartHandleRequestBody(Stream outputStream, EndRequestBody endRequestBody)
{
try
{
int completed = 0;
while (completed + SEGMENT_SIZE < contentLength)
{
outputStream.Write(data, completed, SEGMENT_SIZE);
outputStream.Flush();
completed += SEGMENT_SIZE;
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
}
if (completed < contentLength)
{
outputStream.Write(data, completed, (int)(contentLength - completed));//包括本身
outputStream.Flush();
if (progressCallback != null)
{
UpdateProgress(contentLength, contentLength);
}
}
endRequestBody(null);
}
catch (Exception ex)
{
endRequestBody(ex);
}
finally
{
if (outputStream != null)
{
outputStream.Flush();
outputStream.Close();
outputStream.Dispose();
//QLog.D("XIAO", "stream close");
outputStream = null;
}
}
}
private void AsyncStreamCallback(IAsyncResult ar)
{
RequestBodyState requestBodyState = ar.AsyncState as RequestBodyState;
Stream outputStream = null;
try
{
outputStream = requestBodyState.outputStream;
outputStream.EndWrite(ar);
if (progressCallback != null)
{
UpdateProgress(requestBodyState.complete, contentLength);
}
if(requestBodyState.complete < contentLength)
{
long remain = contentLength - requestBodyState.complete;
int count = (int)(remain > SEGMENT_SIZE ? SEGMENT_SIZE : remain);
int offset = (int)requestBodyState.complete;
requestBodyState.complete += count;
outputStream.BeginWrite(requestBodyState.buffer, offset, count, AsyncStreamCallback, requestBodyState);
}
else
{
if (outputStream != null) outputStream.Close();
//write over
requestBodyState.endRequestBody(null);
requestBodyState = null;
}
}
catch (Exception ex)
{
if (outputStream != null) outputStream.Close();
QLog.E(TAG, ex.Message, ex);
requestBodyState.endRequestBody(ex);
requestBodyState = null;
}
}
}
public class FileRequestBody : RequestBody
{
private readonly string srcPath;
private readonly long fileOffset;
//private RequestBodyState requestBodyState;
private FileStream fileStream;
public FileRequestBody(string srcPath, long fileOffset, long sendContentSize)
{
this.srcPath = srcPath;
this.fileOffset = fileOffset;
contentLength = sendContentSize;
}
public override void OnWrite(Stream outputStream)
{
FileStream fileStream = null;
try
{
byte[] buffer = new byte[SEGMENT_SIZE];
int bytesRead = 0;
long completed = bytesRead;
fileStream = new FileStream(srcPath, FileMode.Open, FileAccess.Read);
fileStream.Seek(fileOffset, SeekOrigin.Begin);//seek to designated position
long remain = contentLength - completed;
if (remain > 0)
{
while ((bytesRead = fileStream.Read(buffer, 0, (int)(buffer.Length > remain ? remain : buffer.Length))) != 0)
{
outputStream.Write(buffer, 0, bytesRead);
outputStream.Flush();
completed += bytesRead;
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
remain = contentLength - completed;
if (remain == 0) break;
}
}
else
{
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
}
buffer = null;
}
catch (Exception ex)
{
QLog.E(TAG, ex.Message, ex);
throw ;
}
finally
{
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
//QLog.D("XIAO", "stream close");
fileStream = null;
}
if (outputStream != null)
{
outputStream.Flush();
outputStream.Close();
outputStream.Dispose();
//QLog.D("XIAO", "stream close");
outputStream = null;
}
}
}
public override string GetMD5()
{
try
{
fileStream = new FileStream(srcPath, FileMode.Open, FileAccess.Read);
fileStream.Seek(fileOffset, SeekOrigin.Begin);
return DigestUtils.GetMd5ToBase64(fileStream, contentLength);
}
catch (Exception ex)
{
QLog.E(TAG, ex.Message, ex);
throw;
}
finally
{
if (fileStream != null) fileStream.Close();
}
}
public override void StartHandleRequestBody(Stream outputStream, EndRequestBody endRequestBody)
{
FileStream fileStream = null;
try
{
byte[] buffer = new byte[SEGMENT_SIZE];
int bytesRead = 0;
long completed = bytesRead;
fileStream = new FileStream(srcPath, FileMode.Open, FileAccess.Read);
fileStream.Seek(fileOffset, SeekOrigin.Begin);//seek to designated position
long remain = contentLength - completed;
if (remain > 0)
{
while ((bytesRead = fileStream.Read(buffer, 0, (int)(buffer.Length > remain ? remain : buffer.Length))) != 0)
{
outputStream.Write(buffer, 0, bytesRead);
outputStream.Flush();
completed += bytesRead;
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
remain = contentLength - completed;
if (remain == 0) break;
}
}
else
{
if (progressCallback != null)
{
UpdateProgress(completed, contentLength);
}
}
buffer = null;
endRequestBody(null);
}
catch (Exception ex)
{
QLog.E(TAG, ex.Message, ex);
endRequestBody(ex);
}
finally
{
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
//QLog.D("XIAO", "stream close");
fileStream = null;
}
if (outputStream != null)
{
outputStream.Flush();
outputStream.Close();
outputStream.Dispose();
//QLog.D("XIAO", "stream close");
outputStream = null;
}
}
}
private void AsyncStreamCallback(IAsyncResult ar)
{
RequestBodyState requestBodyState = ar.AsyncState as RequestBodyState;
Stream outputStream = null;
try
{
outputStream = requestBodyState.outputStream;
outputStream.EndWrite(ar);
outputStream.Flush();
if (progressCallback != null)
{
UpdateProgress(requestBodyState.complete, contentLength);
}
if (requestBodyState.complete < contentLength)
{
long remain = contentLength - requestBodyState.complete;
int count = fileStream.Read(requestBodyState.buffer, 0, (int)(requestBodyState.buffer.Length > remain ? remain : requestBodyState.buffer.Length));
requestBodyState.complete += count;
outputStream.BeginWrite(requestBodyState.buffer, 0, count, AsyncStreamCallback, requestBodyState);
}
else
{
if (fileStream != null) fileStream.Close();
if (outputStream != null) outputStream.Close();
//write over
requestBodyState.endRequestBody(null);
requestBodyState = null;
}
}
catch (Exception ex)
{
if (fileStream != null) fileStream.Close();
if (outputStream != null) outputStream.Close();
QLog.E(TAG, ex.Message, ex);
requestBodyState.endRequestBody(ex);
requestBodyState = null;
}
}
}
/**
* --boundary\r\n
* content-type: value\r\n
* content-disposition: form-data; name="key"\r\n
* content-transfer-encoding: encoding\r\n
* \r\n
* value\r\n
* --boundary--
*/
public class MultipartRequestBody : RequestBody
{
private readonly string DASHDASH = "--";
public static string BOUNDARY = "314159265358979323------------";
private readonly string CRLF = "\r\n";
private readonly string CONTENT_DISPOSITION = "Content-Disposition: form-data; ";
private Dictionary<string, string> parameters;
private string name;
private string fileName;
private byte[] data;
private string srcPath;
private long fileOffset;
private Stream fileStream;
private long realContentLength;
private RequestBodyState requestBodyState;
public MultipartRequestBody()
{
contentType = "multipart/form-data; boundary=" + BOUNDARY;
parameters = new Dictionary<string, string>();
contentLength = -1L;
}
public void AddParamters(Dictionary<string, string> parameters)
{
if (parameters != null)
{
foreach (KeyValuePair<string, string> pair in parameters)
{
this.parameters.Add(pair.Key, pair.Value);
}
}
}
public void AddParameter(string key, string value)
{
if (key != null)
{
parameters.Add(key, value);
}
}
public void AddData(byte[] data, string name, string fileName)
{
this.data = data;
this.name = name;
this.fileName = fileName;
this.realContentLength = data.Length;
}
public void AddData(string srcPath, long fileOffset, long sendContentSize, string name, string fileName)
{
this.srcPath = srcPath;
this.fileOffset = fileOffset;
this.name = name;
this.fileName = fileName;
realContentLength = sendContentSize;
}
//计算长度
public override long ContentLength
{
get
{
ComputerContentLength();
return base.ContentLength;
}
set { contentLength = value; }
}
private void ComputerContentLength()
{
if (contentLength != -1) return;
contentLength = 0;
if (parameters != null && parameters.Count > 0)
{
StringBuilder parametersBuilder = new StringBuilder();
foreach (KeyValuePair<string, string> pair in parameters)
{
parametersBuilder.Append(DASHDASH).Append(BOUNDARY).Append(CRLF);
parametersBuilder.Append(CONTENT_DISPOSITION).Append("name=\"").Append(pair.Key).Append("\"").Append(CRLF);
parametersBuilder.Append(CRLF);
parametersBuilder.Append(pair.Value).Append(CRLF);
}
string content = parametersBuilder.ToString();
byte[] data = Encoding.UTF8.GetBytes(content);
contentLength += data.Length;
}
if (name != null)
{
StringBuilder parametersBuilder = new StringBuilder();
parametersBuilder.Append(DASHDASH).Append(BOUNDARY).Append(CRLF);
parametersBuilder.Append(CONTENT_DISPOSITION).Append("name=\"").Append(name).Append("\"");
if (!String.IsNullOrEmpty(fileName))
{
parametersBuilder.Append("; filename=").Append("\"").Append(fileName).Append("\""); ;
}
parametersBuilder.Append(CRLF);
parametersBuilder.Append("Content-Type: ").Append("application/octet-stream").Append(CRLF);
parametersBuilder.Append(CRLF);
string content = parametersBuilder.ToString();
byte[] data = Encoding.UTF8.GetBytes(content);
contentLength += data.Length;
}
contentLength += realContentLength;
string endLine = CRLF + DASHDASH + BOUNDARY + DASHDASH + CRLF;
byte[] endData = Encoding.UTF8.GetBytes(endLine);
contentLength += endData.Length;
}
private void WriteParameters(Stream outputStream)
{
if (parameters != null && parameters.Count > 0)
{
StringBuilder parametersBuilder = new StringBuilder();
foreach (KeyValuePair<string, string> pair in parameters)
{
parametersBuilder.Append(DASHDASH).Append(BOUNDARY).Append(CRLF);
parametersBuilder.Append(CONTENT_DISPOSITION).Append("name=\"").Append(pair.Key).Append("\"").Append(CRLF);
parametersBuilder.Append(CRLF);
parametersBuilder.Append(pair.Value).Append(CRLF);
}
string content = parametersBuilder.ToString();
byte[] data = Encoding.UTF8.GetBytes(content);
outputStream.Write(data, 0, data.Length);
}
}
private void WriteFileParameters(Stream outputStream)
{
StringBuilder parametersBuilder = new StringBuilder();
parametersBuilder.Append(DASHDASH).Append(BOUNDARY).Append(CRLF);
parametersBuilder.Append(CONTENT_DISPOSITION).Append("name=\"").Append(name).Append("\"");
if (!String.IsNullOrEmpty(fileName))
{
parametersBuilder.Append("; filename=").Append("\"").Append(fileName).Append("\""); ;
}
parametersBuilder.Append(CRLF);
parametersBuilder.Append("Content-Type: ").Append("application/octet-stream").Append(CRLF);
parametersBuilder.Append(CRLF);
string content = parametersBuilder.ToString();
byte[] data = Encoding.UTF8.GetBytes(content);
outputStream.Write(data, 0, data.Length);
}
private void WriteEndLine(Stream outputStream)
{
string endLine = CRLF + DASHDASH + BOUNDARY + DASHDASH + CRLF;
byte[] data = Encoding.UTF8.GetBytes(endLine);
outputStream.Write(data, 0, data.Length);
}
public override void OnWrite(Stream outputStream)
{
//write paramters
WriteParameters(outputStream);
//写入content-disposition: form-data; name = "file"; filename = "xxx"\r\n
WriteFileParameters(outputStream);
outputStream.Flush();
//wrtie content: file or bintary
try
{
if (data != null)
{
int completed = 0;
while (completed + SEGMENT_SIZE < realContentLength)
{
outputStream.Write(data, completed, SEGMENT_SIZE);
outputStream.Flush();
completed += SEGMENT_SIZE;
if (progressCallback != null)
{
UpdateProgress(completed, realContentLength);
}
}
if (completed < realContentLength)
{
outputStream.Write(data, completed, (int)(realContentLength - completed));//包括本身
if (progressCallback != null)
{
UpdateProgress(realContentLength, realContentLength);
}
}
WriteEndLine(outputStream);
outputStream.Flush();
}
else if (srcPath != null)
{
byte[] buffer = new byte[SEGMENT_SIZE]; // 64kb
int bytesRead = 0;
long completed = bytesRead;
fileStream = new FileStream(srcPath, FileMode.Open, FileAccess.Read);
fileStream.Seek(fileOffset, SeekOrigin.Begin);
long remain = realContentLength - completed;
if (remain > 0)
{
while ((bytesRead = fileStream.Read(buffer, 0, (int)(buffer.Length > remain ? remain : buffer.Length))) != 0)
{
outputStream.Write(buffer, 0, bytesRead);
outputStream.Flush();
completed += bytesRead;
if (progressCallback != null)
{
UpdateProgress(completed, realContentLength);
}
remain = realContentLength - completed;
if (remain == 0) break;
}
}
else
{
if (progressCallback != null)
{
completed += bytesRead;
UpdateProgress(completed, realContentLength);
}
}
WriteEndLine(outputStream);
outputStream.Flush();
}
}
catch (Exception)
{
throw;
}
finally
{
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
//QLog.D("XIAO", "stream close");
fileStream = null;
}
if (outputStream != null)
{
outputStream.Flush();
outputStream.Close();
outputStream.Dispose();
//QLog.D("XIAO", "stream close");
outputStream = null;
}
}
}
public override string GetMD5()
{
throw new NotImplementedException();
}
public override void StartHandleRequestBody(Stream outputStream, EndRequestBody endRequestBody)
{
//write paramters
WriteParameters(outputStream);
//写入content-disposition: form-data; name = "file"; filename = "xxx"\r\n
WriteFileParameters(outputStream);
outputStream.Flush();
//wrtie content: file or bintary
try
{
if (data != null)
{
int completed = 0;
while (completed + SEGMENT_SIZE < realContentLength)
{
outputStream.Write(data, completed, SEGMENT_SIZE);
outputStream.Flush();
completed += SEGMENT_SIZE;
if (progressCallback != null)
{
UpdateProgress(completed, realContentLength);
}
}
if (completed < realContentLength)
{
outputStream.Write(data, completed, (int)(realContentLength - completed));//包括本身
if (progressCallback != null)
{
UpdateProgress(realContentLength, realContentLength);
}
}
WriteEndLine(outputStream);
outputStream.Flush();
}
else if (srcPath != null)
{
byte[] buffer = new byte[SEGMENT_SIZE]; // 64kb
int bytesRead = 0;
long completed = bytesRead;
fileStream = new FileStream(srcPath, FileMode.Open, FileAccess.Read);
fileStream.Seek(fileOffset, SeekOrigin.Begin);
long remain = realContentLength - completed;
if (remain > 0)
{
while ((bytesRead = fileStream.Read(buffer, 0, (int)(buffer.Length > remain ? remain : buffer.Length))) != 0)
{
outputStream.Write(buffer, 0, bytesRead);
outputStream.Flush();
completed += bytesRead;
if (progressCallback != null)
{
UpdateProgress(completed, realContentLength);
}
remain = realContentLength - completed;
if (remain == 0) break;
}
}
else
{
if (progressCallback != null)
{
completed += bytesRead;
UpdateProgress(completed, realContentLength);
}
}
WriteEndLine(outputStream);
outputStream.Flush();
}
endRequestBody(null);
}
catch (Exception ex)
{
endRequestBody(ex);
}
finally
{
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
//QLog.D("XIAO", "stream close");
fileStream = null;
}
if (outputStream != null)
{
outputStream.Flush();
outputStream.Close();
outputStream.Dispose();
//QLog.D("XIAO", "stream close");
outputStream = null;
}
}
}
private void AsyncStreamCallback(IAsyncResult ar)
{
RequestBodyState requestBodyState = ar.AsyncState as RequestBodyState;
Stream outputStream = null;
try
{
outputStream = requestBodyState.outputStream;
outputStream.EndWrite(ar);
if (progressCallback != null)
{
UpdateProgress(requestBodyState.complete, realContentLength);
}
if (requestBodyState.complete < realContentLength)
{
if (srcPath != null)
{
long remain = realContentLength - requestBodyState.complete;
int count = fileStream.Read(requestBodyState.buffer, 0, (int)(requestBodyState.buffer.Length > remain ? remain : requestBodyState.buffer.Length));
requestBodyState.complete += count;
outputStream.BeginWrite(requestBodyState.buffer, 0, count, AsyncStreamCallback, requestBodyState);
}
else if (data != null)
{
long remain = realContentLength - requestBodyState.complete;
int count = (int)(remain > SEGMENT_SIZE ? SEGMENT_SIZE : remain);
int offset = (int)requestBodyState.complete;
requestBodyState.complete += count;
outputStream.BeginWrite(requestBodyState.buffer, offset, count, AsyncStreamCallback, requestBodyState);
}
outputStream.Flush();
}
else
{
WriteEndLine(outputStream);
outputStream.Flush();
if (fileStream != null) fileStream.Close();
if (outputStream != null) outputStream.Close();
//write over
requestBodyState.endRequestBody(null);
}
}
catch (Exception ex)
{
if (fileStream != null) fileStream.Close();
if (outputStream != null) outputStream.Close();
QLog.E(TAG, ex.Message, ex);
requestBodyState.endRequestBody(ex);
}
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
/**
* Copyright (c) 2018 Tencent Cloud. All rights reserved.
* 11/2/2018 4:39:49 PM
* bradyxiao
*/
namespace COSXML.Network
{
public class Response
{
public int Code { get; set; }
public string Message { get; set; }
public Dictionary<string, List<string>> Headers { get; set; }
public long ContentLength { get; set; }
public string ContentType { get; set; }
public ResponseBody Body { get; set; }
public virtual void HandleResponseHeader() { }
/// <summary>
/// handle body successfully or throw exception
/// </summary>
/// <param name="ex"></param>
/// <param name="isSuccess"></param>
public virtual void OnFinish(bool isSuccess, Exception ex) { }
}
}

View File

@@ -0,0 +1,241 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using COSXML.Common;
using COSXML.Log;
/**
* Copyright (c) 2018 Tencent Cloud. All rights reserved.
* 11/28/2018 2:58:22 PM
* bradyxiao
*/
namespace COSXML.Network
{
public sealed class ResponseBody
{
private const int SEGMENT_SIZE = 4096;
private long contentLength = -1L;
private string contentType;
private COSXML.Callback.OnProgressCallback progressCallback;
private COSXML.Callback.OnParseStream parseStream;
private string filePath;
private long fileOffset;
private FileStream fileStream;
private bool isDownload = false;
private MemoryStream memoryStream;
public long ContentLength { get { return contentLength; } set { contentLength = value; } }
public string ContentType { get { return contentType; } set { contentType = value; } }
public COSXML.Callback.OnProgressCallback ProgressCallback { get { return progressCallback; } set { progressCallback = value; } }
public COSXML.Callback.OnParseStream ParseStream { get { return parseStream; } set { parseStream = value; } }
public ResponseBody()
{ }
public ResponseBody(string filePath, long fileOffset)
{
this.filePath = filePath;
this.fileOffset = fileOffset;
this.isDownload = true;
}
/// <summary>
/// handle cos response
/// </summary>
/// <param name="inputStream"></param>
/// <exception cref="CosServerException"> throw CosServerException </exception>
/// <exception cref="Exception">throw Exception</exception>
public void HandleResponseBody(Stream inputStream)
{
try
{
if (isDownload)
{
fileStream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
fileStream.Seek(fileOffset, SeekOrigin.Begin);
byte[] buffer = new byte[SEGMENT_SIZE];
int recvLen = 0;
long completed = recvLen;
while ((recvLen = inputStream.Read(buffer, 0, buffer.Length)) != 0)
{
fileStream.Write(buffer, 0, recvLen);
if (progressCallback != null)
{
completed += recvLen;
progressCallback(completed, contentLength);
}
}
fileStream.Flush();
}
else
{
if (parseStream != null)
{
parseStream(inputStream, contentType, contentLength);
}
}
}
catch (Exception ex)
{
QLog.E("ResponseBody", ex.Message, ex);
throw ;
}
finally
{
if (inputStream != null)
{
inputStream.Close();
inputStream.Dispose();
}
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
}
//if (memoryStream != null) memoryStream.Close();
}
}
public void StartHandleResponseBody(Stream inputStream, EndResponseBody endResponseBody)
{
ResponseBodyState responseBodyState = new ResponseBodyState();
responseBodyState.inputStream = inputStream;
responseBodyState.endResponseBody = endResponseBody;
responseBodyState.completed = 0L;
try
{
int count = (int)((contentLength > SEGMENT_SIZE || contentLength <= 0) ? SEGMENT_SIZE : contentLength);
byte[] buffer = new byte[count];
responseBodyState.buffer = buffer;
if (isDownload)
{
fileStream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
fileStream.Seek(fileOffset, SeekOrigin.Begin);
responseBodyState.inputStream.BeginRead(responseBodyState.buffer, 0, responseBodyState.buffer.Length, AsyncStreamCallback, responseBodyState);
}
else
{
memoryStream = new MemoryStream(count);
responseBodyState.buffer = buffer;
responseBodyState.inputStream.BeginRead(responseBodyState.buffer, 0, responseBodyState.buffer.Length, AsyncStreamCallback, responseBodyState);
}
}
catch (Exception ex)
{
responseBodyState.endResponseBody(false, ex);
responseBodyState.Clear();
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
}
if (memoryStream != null)
{
memoryStream.Close();
memoryStream.Dispose();
}
QLog.E("ResponseBody", ex.Message, ex);
}
}
private void AsyncStreamCallback(IAsyncResult ar)
{
ResponseBodyState responseBodyState = ar.AsyncState as ResponseBodyState;
Stream inputStream = responseBodyState.inputStream;
try
{
int recvLen = inputStream.EndRead(ar);
responseBodyState.completed += recvLen;
if (recvLen > 0)
{
if (isDownload)
{
fileStream.Write(responseBodyState.buffer, 0, recvLen);
if (progressCallback != null)
{
progressCallback(responseBodyState.completed, contentLength);
}
}
else
{
memoryStream.Write(responseBodyState.buffer, 0, recvLen);
}
inputStream.BeginRead(responseBodyState.buffer, 0, responseBodyState.buffer.Length, AsyncStreamCallback, responseBodyState);
}
else if (recvLen == 0)
{
if (isDownload)
{
fileStream.Flush();
}
else
{
memoryStream.Seek(0, SeekOrigin.Begin);
parseStream(memoryStream, contentType, responseBodyState.completed);
}
responseBodyState.endResponseBody(true, null);
responseBodyState.Clear();
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
}
if (memoryStream != null)
{
memoryStream.Close();
memoryStream.Dispose();
}
}
}
catch (Exception ex)
{
responseBodyState.endResponseBody(false, ex);
responseBodyState.Clear();
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
}
if (memoryStream != null)
{
memoryStream.Close();
memoryStream.Dispose();
}
QLog.E("ResponseBody", ex.Message, ex);
}
}
}
public delegate void EndResponseBody(bool isSuccess, Exception ex);
public class ResponseBodyState
{
public Stream inputStream;
public byte[] buffer;
public long completed;
public EndResponseBody endResponseBody;
public void Clear()
{
if (inputStream != null) inputStream.Close();
if (buffer != null) buffer = null;
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
/**
* Copyright (c) 2018 Tencent Cloud. All rights reserved.
* 11/21/2018 2:51:13 PM
* bradyxiao
*/
namespace COSXML.Network
{
public sealed class RetryStrategy
{
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Text;
/* ==============================================================================
* Copyright 2016-2019 Tencent Cloud. All Rights Reserved.
* Authbradyxiao
* Date2019/4/4 16:48:10
* ==============================================================================*/
namespace Tencent.QCloud.Cos.Sdk.Network
{
public class TaskManager
{
private Queue<HttpTask> httpTasks;
private static TaskManager instance;
private Object sync;
private TaskManager()
{
httpTasks = new Queue<HttpTask>(30);
sync = new Object();
}
public static TaskManager getInstance()
{
lock (typeof(TaskManager))
{
if (instance == null)
{
instance = new TaskManager();
}
}
return instance;
}
public void Enqueue(HttpTask httpTask)
{
lock (sync)
{
httpTasks.Enqueue(httpTask);
}
}
public HttpTask Dequeue()
{
HttpTask httpTask = null;
lock (sync)
{
if (httpTasks.Count != 0)
{
httpTask = httpTasks.Dequeue();
}
}
return httpTask;
}
}
}