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 { /// /// input: CosRequest; output: CosResponse /// 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"); } } /// /// excute request /// /// /// /// CosClientException /// CosServerException 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 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); } /// /// excute request /// /// /// /// CosClientException /// CosServerException 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 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 headers = cosRequest.GetRequestHeaders(); if (headers != null) { foreach (KeyValuePair 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; } /// /// add authorization /// /// QCloudSignSource /// 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; } /// /// cos response /// 分为两类: /// 一类下载文件 /// 一类直接读取数据 /// private class CosResponse : Response { private CosResult cosResult; private COSXML.Callback.OnSuccessCallback 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 successCallback, COSXML.Callback.OnFailedCallback failCallback):this(cosResult, saveFilePath, saveFileOffset, downloadProgressCallback) { this.successCallback = successCallback; this.faileCallback = failCallback; } /// /// response has been obtain, and parse headers from response /// 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 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; } /// /// error /// /// 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); } } } } }