aliyun-net-sdk-core/DefaultAcsClient.cs (509 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Aliyun.Acs.Core.Auth;
using Aliyun.Acs.Core.Auth.Provider;
using Aliyun.Acs.Core.Exceptions;
using Aliyun.Acs.Core.Http;
using Aliyun.Acs.Core.Profile;
using Aliyun.Acs.Core.Reader;
using Aliyun.Acs.Core.Regions;
using Aliyun.Acs.Core.Retry;
using Aliyun.Acs.Core.Retry.Condition;
using Aliyun.Acs.Core.Timeout.Util;
using Aliyun.Acs.Core.Transform;
using Aliyun.Acs.Core.Utils;
namespace Aliyun.Acs.Core
{
public class DefaultAcsClient : IAcsClient
{
private static readonly HttpWebProxy WebProxy = new HttpWebProxy();
private readonly IClientProfile clientProfile;
private readonly AlibabaCloudCredentialsProvider credentialsProvider;
private readonly RetryPolicy retryPolicy;
private readonly UserAgent userAgentConfig = new UserAgent();
private bool autoRetry = true;
private int maxRetryNumber = 3;
public DefaultAcsClient()
{
retryPolicy = AutoRetry ? new RetryPolicy(maxRetryNumber, true) : new RetryPolicy();
}
public DefaultAcsClient(IClientProfile profile) : this()
{
clientProfile = profile;
credentialsProvider = new StaticCredentialsProvider(profile);
clientProfile.SetCredentialsProvider(credentialsProvider);
}
public DefaultAcsClient(IClientProfile profile, AlibabaCloudCredentials credentials) : this()
{
clientProfile = profile;
credentialsProvider = new StaticCredentialsProvider(credentials);
clientProfile.SetCredentialsProvider(credentialsProvider);
}
public DefaultAcsClient(IClientProfile profile, AlibabaCloudCredentialsProvider credentialsProvider) : this()
{
clientProfile = profile;
this.credentialsProvider = credentialsProvider;
clientProfile.SetCredentialsProvider(this.credentialsProvider);
}
[Obsolete("readTimeout is deprecated as does not match Properties rule, please use readTimeout instead.")]
public int readTimeout
{
get { return ReadTimeout; }
}
public int ReadTimeout { get; private set; }
[Obsolete("connectTimeout is deprecated as does not match Properties rule, please use connectTimeout instead.")]
public int connectTimeout
{
get { return ConnectTimeout; }
}
public int ConnectTimeout { get; private set; }
public bool IgnoreCertificate { get; private set; }
public int MaxRetryNumber
{
get { return maxRetryNumber; }
set { maxRetryNumber = value; }
}
public bool AutoRetry
{
get { return autoRetry; }
set { autoRetry = value; }
}
public T GetAcsResponse<T>(AcsRequest<T> request) where T : AcsResponse
{
var httpResponse = DoAction(request);
return ParseAcsResponse(request, httpResponse);
}
public T GetAcsResponse<T>(AcsRequest<T> request, bool autoRetry, int maxRetryNumber) where T : AcsResponse
{
var httpResponse = DoAction(request, autoRetry, maxRetryNumber);
return ParseAcsResponse(request, httpResponse);
}
public T GetAcsResponse<T>(AcsRequest<T> request, IClientProfile profile) where T : AcsResponse
{
var httpResponse = DoAction(request, profile);
return ParseAcsResponse(request, httpResponse);
}
public T GetAcsResponse<T>(AcsRequest<T> request, string regionId, Credential credential)
where T : AcsResponse
{
var httpResponse = DoAction(request, regionId, credential);
return ParseAcsResponse(request, httpResponse);
}
public CommonResponse GetCommonResponse(CommonRequest request)
{
var httpResponse = DoAction(request.BuildRequest());
string data = null;
if (httpResponse.Content != null)
{
data = Encoding.UTF8.GetString(httpResponse.Content);
}
var response = new CommonResponse
{
Data = data,
HttpResponse = httpResponse,
HttpStatus = httpResponse.Status
};
return response;
}
public HttpResponse DoAction<T>(AcsRequest<T> request) where T : AcsResponse
{
return DoAction(request, AutoRetry, MaxRetryNumber, clientProfile);
}
public HttpResponse DoAction<T>(AcsRequest<T> request, bool autoRetry, int maxRetryNumber)
where T : AcsResponse
{
return DoAction(request, autoRetry, maxRetryNumber, clientProfile);
}
public HttpResponse DoAction<T>(AcsRequest<T> request, IClientProfile profile) where T : AcsResponse
{
return DoAction(request, AutoRetry, MaxRetryNumber, profile);
}
public HttpResponse DoAction<T>(AcsRequest<T> request, string regionId, Credential credential)
where T : AcsResponse
{
var signer = Signer.GetSigner(new LegacyCredentials(credential));
FormatType? format = null;
if (null == request.RegionId)
{
request.RegionId = regionId;
}
if (request.ProductDomain == null)
{
request.ProductDomain = EndpointUserConfig.GetProductDomain(request.Product, request.RegionId);
if (request.ProductDomain == null)
{
request.SetProductDomain();
}
}
List<Endpoint> endpoints = null;
if (null != clientProfile)
{
format = clientProfile.GetFormat();
if (request.ProductDomain == null)
{
endpoints = clientProfile.GetEndpoints(request.Product, request.RegionId, request.LocationProduct,
request.LocationEndpointType);
}
}
return DoAction(request, AutoRetry, MaxRetryNumber, request.RegionId, credential, signer, format, endpoints);
}
public HttpResponse DoAction<T>(AcsRequest<T> request, bool autoRetry, int maxRetryNumber, IClientProfile profile) where T : AcsResponse
{
if (null == profile)
{
throw new ClientException("SDK.InvalidProfile", "No active profile found.");
}
var retry = autoRetry;
var retryNumber = maxRetryNumber;
var region = profile.GetRegionId();
if (null == request.RegionId)
{
request.RegionId = region;
}
if (request.ProductDomain == null)
{
request.ProductDomain = EndpointUserConfig.GetProductDomain(request.Product, request.RegionId);
if (request.ProductDomain == null)
{
request.SetProductDomain();
}
}
var credentials = credentialsProvider.GetCredentials();
if (credentials == null)
{
credentials = new DefaultCredentialProvider().GetAlibabaCloudClientCredential();
}
var signer = Signer.GetSigner(credentials);
var format = profile.GetFormat();
List<Endpoint> endpoints = null;
if (request.ProductDomain == null)
{
endpoints = clientProfile.GetEndpoints(request.Product, request.RegionId,
request.LocationProduct,
request.LocationEndpointType);
}
return DoAction(request, retry, retryNumber, request.RegionId, credentials, signer, format, endpoints);
}
public HttpResponse DoAction<T>(AcsRequest<T> request, bool autoRetry, int maxRetryNumber, string regionId,
Credential credential, Signer signer, FormatType? format, List<Endpoint> endpoints) where T : AcsResponse
{
return DoAction(request, autoRetry, maxRetryNumber, regionId, new LegacyCredentials(credential), signer,
format, endpoints);
}
private T ParseAcsResponse<T>(AcsRequest<T> request, HttpResponse httpResponse) where T : AcsResponse
{
CommonLog.LogInfo(request, httpResponse, CommonLog.ExecuteTime);
var format = httpResponse.ContentType;
if (httpResponse.isSuccess())
{
return ReadResponse(request, httpResponse, format);
}
try
{
var error = ReadError(request, httpResponse, format);
if (null != error.ErrorCode)
{
if (500 <= httpResponse.Status)
{
throw new ServerException(error.ErrorCode,
string.Format("{0}, the request url is {1}, the RequestId is {2}.", error.ErrorMessage,
httpResponse.Url ?? "empty", error.RequestId));
}
if (400 == httpResponse.Status && (error.ErrorCode.Equals("SignatureDoesNotMatch") ||
error.ErrorCode.Equals("IncompleteSignature")))
{
var errorMessage = error.ErrorMessage;
var re = new Regex(@"string to sign is:", RegexOptions.Compiled | RegexOptions.IgnoreCase);
var matches = re.Match(errorMessage);
if (matches.Success)
{
var errorStringToSign = errorMessage.Substring(matches.Index + matches.Length);
if (request.StringToSign.Equals(errorStringToSign))
{
throw new ClientException("SDK.InvalidAccessKeySecret",
"Specified Access Key Secret is not valid.", error.RequestId);
}
}
}
throw new ClientException(error.ErrorCode, error.ErrorMessage, error.RequestId);
}
}
catch (ServerException ex)
{
CommonLog.LogException(ex, ex.ErrorCode, ex.ErrorMessage);
throw new ServerException(ex.ErrorCode, ex.ErrorMessage, ex.RequestId);
}
catch (ClientException ex)
{
CommonLog.LogException(ex, ex.ErrorCode, ex.ErrorMessage);
throw new ClientException(ex.ErrorCode, ex.ErrorMessage, ex.RequestId);
}
var t = Activator.CreateInstance<T>();
t.HttpResponse = httpResponse;
return t;
}
public virtual HttpResponse DoAction<T>(AcsRequest<T> request, bool autoRetry, int maxRetryNumber,
string regionId,
AlibabaCloudCredentials credentials, Signer signer, FormatType? format, List<Endpoint> endpoints)
where T : AcsResponse
{
var httpStatusCode = "";
var retryAttemptTimes = 0;
ClientException exception;
RetryPolicyContext retryPolicyContext;
do
{
try
{
var watch = Stopwatch.StartNew();
FormatType? requestFormatType = request.AcceptFormat;
format = requestFormatType;
var domain = request.ProductDomain ??
Endpoint.FindProductDomain(regionId, request.Product, endpoints);
if (null == domain)
{
throw new ClientException("SDK.InvalidRegionId", "Can not find endpoint to access.");
}
var userAgent = UserAgent.Resolve(request.GetSysUserAgentConfig(), userAgentConfig);
DictionaryUtil.Add(request.Headers, "User-Agent", userAgent);
DictionaryUtil.Add(request.Headers, "x-acs-version", request.Version);
if (!string.IsNullOrWhiteSpace(request.ActionName))
{
DictionaryUtil.Add(request.Headers, "x-acs-action", request.ActionName);
}
var httpRequest = request.SignRequest(signer, credentials, format, domain);
ResolveTimeout(httpRequest, request.Product, request.Version, request.ActionName);
SetHttpsInsecure(IgnoreCertificate);
ResolveProxy(httpRequest, request);
var response = GetResponse(httpRequest);
httpStatusCode = response.Status.ToString();
PrintHttpDebugMsg(request, response);
watch.Stop();
CommonLog.ExecuteTime = watch.ElapsedMilliseconds;
return response;
}
catch (ClientException ex)
{
retryPolicyContext = new RetryPolicyContext(ex, httpStatusCode, retryAttemptTimes, request.Product,
request.Version,
request.ActionName, RetryCondition.BlankStatus);
CommonLog.LogException(ex, ex.ErrorCode, ex.ErrorMessage);
exception = ex;
}
Thread.Sleep(retryPolicy.GetDelayTimeBeforeNextRetry(retryPolicyContext));
} while ((retryPolicy.ShouldRetry(retryPolicyContext) & RetryCondition.NoRetry) != RetryCondition.NoRetry);
if (exception != null)
{
CommonLog.LogException(exception, exception.ErrorCode, exception.ErrorMessage);
throw new ClientException(exception.ErrorCode, exception.ErrorMessage);
}
return null;
}
private void PrintHttpDebugMsg(HttpRequest request, HttpResponse response)
{
var environmentDebugValue = Environment.GetEnvironmentVariable("DEBUG");
if (null != environmentDebugValue && environmentDebugValue.ToLower().Equals("sdk"))
{
if (null != request.Headers)
{
Console.WriteLine(
"> " + request.Method + "\n" +
"> " + request.Url + "\n"
);
DictionaryUtil.Print(request.Headers, '>');
Console.WriteLine(
"< " + response.Status
);
DictionaryUtil.Print(response.Headers, '<');
}
Environment.SetEnvironmentVariable("DEBUG", null);
}
}
private T ReadResponse<T>(AcsRequest<T> request, HttpResponse httpResponse, FormatType? format)
where T : AcsResponse
{
var reader = ReaderFactory.CreateInstance(format);
var context = new UnmarshallerContext();
var body = Encoding.UTF8.GetString(httpResponse.Content);
context.ResponseDictionary = request.CheckShowJsonItemName() ?
reader.Read(body, request.ActionName) :
reader.ReadForHideArrayItem(body, request.ActionName);
context.HttpResponse = httpResponse;
return request.GetResponse(context);
}
private AcsError ReadError<T>(AcsRequest<T> request, HttpResponse httpResponse, FormatType? format)
where T : AcsResponse
{
var responseEndpoint = "Error";
var reader = ReaderFactory.CreateInstance(format);
var context = new UnmarshallerContext();
var body = Encoding.Default.GetString(httpResponse.Content);
context.ResponseDictionary =
null == reader ? new Dictionary<string, string>() : reader.Read(body, responseEndpoint);
return AcsErrorUnmarshaller.Unmarshall(context);
}
public virtual HttpResponse GetResponse(HttpRequest httpRequest)
{
return HttpResponse.GetResponse(httpRequest);
}
public void AppendUserAgent(string key, string value)
{
userAgentConfig.AppendUserAgent(key, value);
}
public UserAgent GetUserAgentConfig()
{
return userAgentConfig;
}
public void SetConnectTimeoutInMilliSeconds(int connectTimeout)
{
ConnectTimeout = connectTimeout;
}
public void SetReadTimeoutInMilliSeconds(int readTimeout)
{
ReadTimeout = readTimeout;
}
private void ResolveTimeout(HttpRequest request, string product, string version, string actionName)
{
var apiReadTimeout = TimeoutConfig.GetSpecificApiReadTimeoutValue(product, version, actionName);
int finalReadTimeout;
if (request.ReadTimeout > 0)
{
finalReadTimeout = request.ReadTimeout;
}
else if (ReadTimeout > 0)
{
finalReadTimeout = ReadTimeout;
}
else if (apiReadTimeout > 0)
{
finalReadTimeout = apiReadTimeout;
}
else
{
finalReadTimeout = 0;
}
request.SetReadTimeoutInMilliSeconds(finalReadTimeout);
int finalConnectTimeout;
if (request.ConnectTimeout > 0)
{
finalConnectTimeout = request.ConnectTimeout;
}
else if (ConnectTimeout > 0)
{
finalConnectTimeout = ConnectTimeout;
}
else
{
finalConnectTimeout = 0;
}
request.SetConnectTimeoutInMilliSeconds(finalConnectTimeout);
}
public void SetHttpsInsecure(bool ignoreCertificate = false)
{
IgnoreCertificate = ignoreCertificate;
}
/// <summary>
/// Set Http Proxy
/// </summary>
/// <param name="httpProxy"></param>
public void SetHttpProxy(string httpProxy)
{
WebProxy.HttpProxy = httpProxy;
}
/// <summary>
/// Set Https Proxy
/// </summary>
/// <param name="httpsProxy"></param>
public void SetHttpsProxy(string httpsProxy)
{
WebProxy.HttpsProxy = httpsProxy;
}
/// <summary>
/// Set Proxy White List
/// </summary>
/// <param name="urls"></param>
public void SetNoProxy(string urls)
{
WebProxy.NoProxy = urls;
}
/// <summary>
/// Get Http Proxy
/// </summary>
/// <returns></returns>
public string GetHttpProxy()
{
return WebProxy.HttpProxy ?? Environment.GetEnvironmentVariable("HTTP_PROXY") ??
Environment.GetEnvironmentVariable("http_proxy");
}
/// <summary>
/// Get Https Proxy
/// </summary>
/// <returns></returns>
public string GetHttpsProxy()
{
return WebProxy.HttpsProxy ?? Environment.GetEnvironmentVariable("HTTPS_PROXY") ??
Environment.GetEnvironmentVariable("https_proxy");
}
/// <summary>
/// Get Proxy White List
/// </summary>
/// <returns></returns>
public string GetNoProxy()
{
return WebProxy.NoProxy ?? Environment.GetEnvironmentVariable("NO_PROXY") ??
Environment.GetEnvironmentVariable("no_proxy");
}
private void ResolveProxy<T>(HttpRequest httpRequest, AcsRequest<T> request) where T : AcsResponse
{
string authorization;
string proxy;
var noProxy = GetNoProxy() == null ? null : GetNoProxy().Split(',');
if (request.Protocol == ProtocolType.HTTP)
{
proxy = GetHttpProxy();
}
else
{
proxy = GetHttpsProxy();
}
if (!string.IsNullOrEmpty(proxy))
{
var originProxyUri = new Uri(proxy);
Uri finalProxyUri;
if (!string.IsNullOrEmpty(originProxyUri.UserInfo))
{
authorization =
Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(originProxyUri.UserInfo));
finalProxyUri = new Uri(originProxyUri.Scheme + "://" + originProxyUri.Authority);
var userInfoArray = originProxyUri.UserInfo.Split(':');
ICredentials credential = new NetworkCredential(userInfoArray[0], userInfoArray[1]);
httpRequest.WebProxy = new WebProxy(finalProxyUri, false, noProxy, credential);
if (httpRequest.Headers.ContainsKey("Authorization"))
{
httpRequest.Headers.Remove("Authorization");
}
httpRequest.Headers.Add("Authorization", "Basic " + authorization);
}
else
{
finalProxyUri = originProxyUri;
httpRequest.WebProxy = new WebProxy(finalProxyUri, false, noProxy);
}
}
}
public static void EnableLogger(string template = CommonLog.DefaultTemplate)
{
CommonLog.EnableLogger(template);
}
public static void DisableLogger()
{
CommonLog.DisableLogger();
}
}
}