123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- /*
- * Copyright (C) Alibaba Cloud Computing
- * All rights reserved.
- *
- * 版权所有 (C)阿里云计算有限公司
- */
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using System.IO;
- using System.Net;
- using System.Reflection;
- using System.Security.Cryptography.X509Certificates;
- using System.Net.Security;
- using System.Linq;
- using Aliyun.OSS.Util;
- namespace Aliyun.OSS.Common.Communication
- {
- /// <summary>
- /// The default implementation of <see cref="ServiceClient"/> that
- /// used to communicate with Aliyun OSS via HTTP protocol.
- /// </summary>
- internal class ServiceClientImpl : ServiceClient
- {
- #region Embeded Classes
- /// <summary>
- /// Represents the async operation of requests in <see cref="ServiceClientImpl"/>.
- /// </summary>
- public class HttpAsyncResult : AsyncResult<ServiceResponse>
- {
- public HttpWebRequest WebRequest { get; set; }
-
- public ExecutionContext Context { get; set; }
- public HttpAsyncResult(AsyncCallback callback, object state)
- : base(callback, state)
- { }
- }
- /// <summary>
- /// Represents the response data of <see cref="ServiceClientImpl"/> requests.
- /// </summary>
- private class ResponseImpl : ServiceResponse
- {
- private bool _disposed;
- private HttpWebResponse _response;
- private readonly Exception _failure;
- private IDictionary<string, string> _headers;
-
- public override HttpStatusCode StatusCode
- {
- get { return _response.StatusCode; }
- }
-
- public override Exception Failure
- {
- get { return _failure; }
- }
- public override IDictionary<string, string> Headers
- {
- get
- {
- ThrowIfObjectDisposed();
- return _headers ?? (_headers = GetResponseHeaders(_response));
- }
- }
- public override Stream Content
- {
- get
- {
- ThrowIfObjectDisposed();
- try
- {
- return (_response != null) ? _response.GetResponseStream() : null;
- }
- catch (ProtocolViolationException ex)
- {
- throw new InvalidOperationException(ex.Message, ex);
- }
- }
- }
- public ResponseImpl(HttpWebResponse httpWebResponse)
- {
- _response = httpWebResponse;
- }
- public ResponseImpl(WebException failure)
- {
- var httpWebResponse = failure.Response as HttpWebResponse;
- _failure = failure;
- _response = httpWebResponse;
- }
- private static IDictionary<string, string> GetResponseHeaders(HttpWebResponse response)
- {
- var headers = response.Headers;
- var result = new Dictionary<string, string>(headers.Count);
- for (var i = 0; i < headers.Count; i++)
- {
- var key = headers.Keys[i];
- var value = headers.Get(key);
- result.Add(key, HttpUtils.Reencode(value, HttpUtils.Iso88591Charset, HttpUtils.Utf8Charset));
- }
- return result;
- }
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (_disposed)
- return;
- if (disposing)
- {
- if (_response != null)
- {
- _response.Close();
- _response = null;
- }
- _disposed = true;
- }
- }
- private void ThrowIfObjectDisposed()
- {
- if (_disposed)
- throw new ObjectDisposedException(GetType().Name);
- }
- }
- #endregion
- #region Constructors
- public ServiceClientImpl(ClientConfiguration configuration)
- : base(configuration)
- {
- }
- #endregion
- #region Implementations
- protected override ServiceResponse SendCore(ServiceRequest serviceRequest,
- ExecutionContext context)
- {
- var request = HttpFactory.CreateWebRequest(serviceRequest, Configuration);
- SetRequestContent(request, serviceRequest, false, null);
- try
- {
- var response = request.GetResponse() as HttpWebResponse;
- if (response.Server.ToLower() == "aliyunoss" || response.Headers.AllKeys.Any(s => s.ToLower().Contains("x-oss-")))
- {
- return new ResponseImpl(response);
- }
- else
- {
- return HandleException(new WebException("文件不存在."));
- }
- }
- catch (WebException ex)
- {
- return HandleException(ex);
- }
- }
- protected override IAsyncResult BeginSendCore(ServiceRequest serviceRequest,
- ExecutionContext context,
- AsyncCallback callback, object state)
- {
- var request = HttpFactory.CreateWebRequest(serviceRequest, Configuration);
- var asyncResult = new HttpAsyncResult(callback, state)
- {
- WebRequest = request,
- Context = context
- };
- SetRequestContent(request, serviceRequest, true,
- () => request.BeginGetResponse(OnGetResponseCompleted, asyncResult));
- return asyncResult;
- }
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
- Justification = "Catch the exception and set it to async result.")]
- private void OnGetResponseCompleted(IAsyncResult ar)
- {
- var asyncResult = ar.AsyncState as HttpAsyncResult;
- Debug.Assert(asyncResult != null && asyncResult.WebRequest != null);
- try
- {
- var response = asyncResult.WebRequest.EndGetResponse(ar) as HttpWebResponse;
- ServiceResponse res = new ResponseImpl(response);
- HandleResponse(res, asyncResult.Context.ResponseHandlers);
- asyncResult.Complete(res);
- }
- catch (WebException we)
- {
- try
- {
- var res = HandleException(we);
- HandleResponse(res, asyncResult.Context.ResponseHandlers);
- asyncResult.WebRequest.Abort();
- asyncResult.Complete(res);
- }
- catch (Exception ie)
- {
- asyncResult.WebRequest.Abort();
- asyncResult.Complete(ie);
- }
- }
- catch (Exception oe)
- {
- asyncResult.WebRequest.Abort();
- asyncResult.Complete(oe);
- }
- }
- /// <summary>
- /// 为了兼容.NET2.0,定义了OssAction,功能等价于.NET4.0中的System.Action
- /// </summary>
- private delegate void OssAction();
- private static void SetRequestContent(HttpWebRequest webRequest, ServiceRequest serviceRequest,
- bool async, OssAction asyncCallback)
- {
- var data = serviceRequest.BuildRequestContent();
- if (data == null ||
- (serviceRequest.Method != HttpMethod.Put &&
- serviceRequest.Method != HttpMethod.Post))
- {
- // Skip setting content body in this case.
- if (async)
- asyncCallback();
-
- return;
- }
- // Write data to the request stream.
- long userSetContentLength = -1;
- if (serviceRequest.Headers.ContainsKey(HttpHeaders.ContentLength))
- userSetContentLength = long.Parse(serviceRequest.Headers[HttpHeaders.ContentLength]);
- long streamLength = data.Length - data.Position;
- webRequest.ContentLength = (userSetContentLength >= 0 &&
- userSetContentLength <= streamLength) ? userSetContentLength : streamLength;
- if (async)
- {
- webRequest.BeginGetRequestStream(
- (ar) =>
- {
- using (var requestStream = webRequest.EndGetRequestStream(ar))
- {
- IoUtils.WriteTo(data, requestStream, webRequest.ContentLength);
- }
- asyncCallback();
- }, null);
- }
- else
- {
- using (var requestStream = webRequest.GetRequestStream())
- {
- IoUtils.WriteTo(data, requestStream, webRequest.ContentLength);
- }
- }
- }
- private static ServiceResponse HandleException(WebException ex)
- {
- var response = ex.Response as HttpWebResponse;
- if (response == null)
- throw ex;
- else
- return new ResponseImpl(ex);
- }
- #endregion
- }
- internal static class HttpFactory
- {
- public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
- {
- return true;
- }
- internal static HttpWebRequest CreateWebRequest(ServiceRequest serviceRequest, ClientConfiguration configuration)
- {
- var webRequest = WebRequest.Create(serviceRequest.BuildRequestUri()) as HttpWebRequest;
- SetRequestHeaders(webRequest, serviceRequest, configuration);
- SetRequestProxy(webRequest, configuration);
- if (webRequest.RequestUri.Scheme == "https")
- {
- ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);
- }
- return webRequest;
- }
- // Set request headers
- private static void SetRequestHeaders(HttpWebRequest webRequest, ServiceRequest serviceRequest,
- ClientConfiguration configuration)
- {
- webRequest.Timeout = configuration.ConnectionTimeout;
- webRequest.Method = serviceRequest.Method.ToString().ToUpperInvariant();
- // Because it is not allowed to set common headers
- // with the WebHeaderCollection.Add method,
- // we have to call an internal method to skip validation.
- foreach (var h in serviceRequest.Headers)
- HttpExtensions.AddInternal(webRequest.Headers, h.Key, h.Value);
- // Set user-agent
- if (!string.IsNullOrEmpty(configuration.UserAgent))
- webRequest.UserAgent = configuration.UserAgent;
- }
- // Set proxy
- private static void SetRequestProxy(HttpWebRequest webRequest, ClientConfiguration configuration)
- {
- // Perf Improvement:
- // If HttpWebRequest.Proxy is not set to null explicitly,
- // it will try to load the IE proxy settings including auto proxy detection,
- // which is quite time consuming.
- webRequest.Proxy = null;
- // Set proxy if proxy settings are specified.
- if (!string.IsNullOrEmpty(configuration.ProxyHost))
- {
- if (configuration.ProxyPort < 0)
- webRequest.Proxy = new WebProxy(configuration.ProxyHost);
- else
- webRequest.Proxy = new WebProxy(configuration.ProxyHost, configuration.ProxyPort);
- if (!string.IsNullOrEmpty(configuration.ProxyUserName))
- {
- webRequest.Proxy.Credentials = String.IsNullOrEmpty(configuration.ProxyDomain) ?
- new NetworkCredential(configuration.ProxyUserName, configuration.ProxyPassword ?? string.Empty) :
- new NetworkCredential(configuration.ProxyUserName, configuration.ProxyPassword ?? string.Empty,
- configuration.ProxyDomain);
- }
- }
- }
- }
- internal static class HttpExtensions
- {
- private static MethodInfo _addInternalMethod;
- private static readonly ICollection<PlatformID> MonoPlatforms =
- new List<PlatformID> { PlatformID.MacOSX, PlatformID.Unix };
- private static bool? _isMonoPlatform;
- internal static void AddInternal(WebHeaderCollection headers, 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 (_isMonoPlatform == false)
- // Encode headers for win platforms.
- value = HttpUtils.Reencode(value, HttpUtils.Utf8Charset, HttpUtils.Iso88591Charset);
- if (_addInternalMethod == null)
- {
- // Specify the internal method name for adding headers
- // mono: AddWithoutValidate
- // win: AddInternal
- var internalMethodName = (_isMonoPlatform == true) ? "AddWithoutValidate" : "AddInternal";
- var mi = typeof(WebHeaderCollection).GetMethod(
- internalMethodName,
- BindingFlags.NonPublic | BindingFlags.Instance,
- null,
- new Type[] { typeof(string), typeof(string) },
- null);
- _addInternalMethod = mi;
- }
- _addInternalMethod.Invoke(headers, new object[] { key, value });
- }
- }
- }
|