代码修改后的版本,全部提交
This commit is contained in:
554
COSXML/Network/CommandTask.cs
Normal file
554
COSXML/Network/CommandTask.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
413
COSXML/Network/HttpClient.cs
Normal file
413
COSXML/Network/HttpClient.cs
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
256
COSXML/Network/HttpClientConfig.cs
Normal file
256
COSXML/Network/HttpClientConfig.cs
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
56
COSXML/Network/HttpMethod.cs
Normal file
56
COSXML/Network/HttpMethod.cs
Normal 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);
|
||||
// }
|
||||
|
||||
// }
|
||||
//}
|
||||
23
COSXML/Network/HttpTask.cs
Normal file
23
COSXML/Network/HttpTask.cs
Normal 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.
|
||||
* Auth:bradyxiao
|
||||
* Date:2019/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
281
COSXML/Network/HttpUrl.cs
Normal 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
183
COSXML/Network/Request.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
874
COSXML/Network/RequestBody.cs
Normal file
874
COSXML/Network/RequestBody.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
39
COSXML/Network/Response.cs
Normal file
39
COSXML/Network/Response.cs
Normal 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) { }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
241
COSXML/Network/ResponseBody.cs
Normal file
241
COSXML/Network/ResponseBody.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
14
COSXML/Network/RetryStrategy.cs
Normal file
14
COSXML/Network/RetryStrategy.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
||||
58
COSXML/Network/TaskManager.cs
Normal file
58
COSXML/Network/TaskManager.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
|
||||
/* ==============================================================================
|
||||
* Copyright 2016-2019 Tencent Cloud. All Rights Reserved.
|
||||
* Auth:bradyxiao
|
||||
* Date:2019/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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user