123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- 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.Model.CI;
- using COSXML.Utils;
- namespace COSXML.Network
- {
- /// <summary>
- /// input: CosRequest; output: CosResponse
- /// </summary>
- public sealed class HttpClient
- {
- private static string TAG = "HttpClient";
- private HttpClientConfig config;
- private static HttpClient instance;
- private Object sync = new Object();
- private static Object syncInstance = new Object();
- private const int MAX_ACTIVIE_TASKS = 5;
- public int MaxRetry { private get; set;} = 3;
- private volatile int activieTasks = 0;
- public static HttpClient GetInstance()
- {
- lock (syncInstance)
- {
- if (instance == null)
- {
- instance = new HttpClient();
- }
- }
- return instance;
- }
- public void Init(HttpClientConfig config)
- {
- if (this.config == null)
- {
- lock (sync)
- {
- if (this.config == null)
- {
- this.config = config;
- // init grobal httpwebreqeust
- CommandTask.Init(this.config);
- }
- }
- }
- }
- private HttpClient()
- {
- }
- /// <summary>
- /// excute request
- /// </summary>
- /// <param name="cosRequest"></param>
- /// <param name="cosResult"></param>
- /// <param name="credentialProvider"></param>
- /// <exception cref="COSXML.CosException.CosClientException">CosClientException</exception>
- /// <exception cref="COSXML.CosException.CosServerException">CosServerException</exception>
- public void Excute(CosRequest cosRequest, CosResult cosResult, QCloudCredentialProvider credentialProvider)
- {
- //HttpTask httpTask = new HttpTask();
- //httpTask.cosRequest = cosRequest;
- //httpTask.cosResult = cosResult;
- //httpTask.isSchedue = false;
- InternalExcute(cosRequest, cosResult, credentialProvider);
- }
- public void Schedue(CosRequest cosRequest, CosResult cosResult, COSXML.Callback.OnSuccessCallback<CosResult> successCallback, COSXML.Callback.OnFailedCallback failCallback, QCloudCredentialProvider credentialProvider)
- {
- //HttpTask httpTask = new HttpTask();
- //httpTask.cosRequest = cosRequest;
- //httpTask.cosResult = cosResult;
- //httpTask.isSchedue = true;
- //httpTask.successCallback = successCallback;
- //httpTask.failCallback = failCallback;
- InternalSchedue(cosRequest, cosResult, successCallback, failCallback, credentialProvider);
- }
- /// <summary>
- /// excute request
- /// </summary>
- /// <param name="cosRequest"></param>
- /// <param name="cosResult"></param>
- /// <param name="credentialProvider"></param>
- /// <exception cref="COSXML.CosException.CosClientException">CosClientException</exception>
- /// <exception cref="COSXML.CosException.CosServerException">CosServerException</exception>
- public void InternalExcute(CosRequest cosRequest, CosResult cosResult, QCloudCredentialProvider credentialProvider, int retryIndex = 0)
- {
- try
- {
- Request request = CreateRequest(cosRequest, credentialProvider);
- //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 if (cosRequest is GetSnapshotRequest)
- {
- GetSnapshotRequest getSnapshotRequest = cosRequest as GetSnapshotRequest;
- response = new CosResponse(cosResult, getSnapshotRequest.GetSaveFilePath(), 0, null);
- }
- else
- {
- response = new CosResponse(cosResult, null, -1L, null);
- }
- cosRequest.BindRequest(request);
- CommandTask.Excute(request, response, config);
- }
- catch (CosServerException serverException)
- {
- // 服务端5xx才重试
- if (serverException.statusCode >= 500 && retryIndex < MaxRetry)
- {
- InternalExcute(cosRequest, cosResult, credentialProvider, retryIndex + 1);
- }
- else
- {
- throw;
- }
- }
- catch (CosClientException)
- {
- // 客户端异常都重试
- if (retryIndex < MaxRetry)
- {
- InternalExcute(cosRequest, cosResult, credentialProvider, retryIndex + 1);
- }
- else
- {
- throw;
- }
-
- }
- catch (Exception ex)
- {
- // 未知异常也重试
- if (retryIndex < MaxRetry)
- {
- InternalExcute(cosRequest, cosResult, credentialProvider, retryIndex + 1);
- }
- else
- {
- throw new CosClientException((int)CosClientError.BadRequest, ex.Message, ex);
- }
- }
- }
- // public void Execute(Request request, Response response)
- // {
- // try
- // {
- // CommandTask.Excute(request, response, config);
- // }
- // catch (CosServerException)
- // {
- // throw;
- // }
- // catch (CosClientException)
- // {
- // throw;
- // }
- // catch (Exception ex)
- // {
- // throw new CosClientException((int)CosClientError.BadRequest, ex.Message, ex);
- // }
- // }
- public void InternalSchedue(CosRequest cosRequest, CosResult cosResult, COSXML.Callback.OnSuccessCallback<CosResult> successCallback, COSXML.Callback.OnFailedCallback failCallback, QCloudCredentialProvider credentialProvider)
- {
- try
- {
- Request request = CreateRequest(cosRequest, credentialProvider);
- cosResult.ExternInfo(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.BadRequest, ex.Message, ex), null);
- }
- }
- private Request CreateRequest(CosRequest cosRequest, QCloudCredentialProvider credentialProvider)
- {
- 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)
- {
- CheckSign(cosRequest.GetSignSourceProvider(), request, credentialProvider);
- }
- 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>
- /// <param name="credentialProvider"></param>
- private void CheckSign(IQCloudSignSource qcloudSignSource, Request request, QCloudCredentialProvider credentialProvider)
- {
- // has authorizaiton, notice: using request.Headers, otherwise, error
- if (request.Headers.ContainsKey(CosRequestHeaderKey.AUTHORIZAIION))
- {
- QLog.Debug(TAG, "has add authorizaiton in headers");
- return;
- }
- //has no authorization, but signSourceProvider == null
- if (qcloudSignSource == null)
- {
- QLog.Debug(TAG, "signSourceProvider == null");
- return;
- }
- if (credentialProvider == null)
- {
- throw new ArgumentNullException("credentialsProvider == null");
- }
- CosXmlSigner signer = new CosXmlSigner();
- signer.Sign(request, qcloudSignSource, credentialProvider.GetQCloudCredentialsCompat(request));
- }
- 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)
- {
- try
- {
- CosServerError cosServerError = XmlParse.Deserialize<CosServerError>(inputStream);
-
- cosServerException.SetCosServerError(cosServerError);
- }
- catch (Exception ex)
- {
- QLog.Debug(TAG, ex.Message);
- }
- }
- throw cosServerException;
- }
- /// <summary>
- /// error
- /// </summary>
- /// <param name="ex"></param>
- public override void OnFinish(bool isSuccess, Exception ex)
- {
- cosResult.RawContentBodyString = Body.rawContentBodyString;
- if (isSuccess && successCallback != null)
- {
- successCallback(cosResult);
- }
- else
- if (faileCallback != null)
- {
- 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.InternalError, ex.Message, ex), null);
- }
- }
- }
- }
- }
- }
|