RestClient
using Polly;
using Relativity.Transfer.Resources;
using System;
using System.Globalization;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace Relativity.Transfer
{
public class RestClient : IRestClient
{
private const HttpStatusCode NoHttpStatusCode = (HttpStatusCode)0;
private const int HttpRequestGet = 1;
private const int HttpRequestPost = 2;
private const int HttpRequestDelete = 3;
private readonly HttpConnectionInfo connectionInfo;
private readonly ITransferLog transferLog;
public int MaxRetryAttempts { get; set; }
public double TimeoutSeconds { get; }
public RestClient(HttpConnectionInfo connectionInfo, ITransferLog log)
: this(connectionInfo, log, 300, 5)
{
}
public RestClient(HttpConnectionInfo connectionInfo, ITransferLog log, double timeoutSeconds, int maxRetryAttempts)
{
if (connectionInfo == null)
throw new ArgumentNullException("connectionInfo");
if (log == null)
throw new ArgumentNullException("log");
this.connectionInfo = connectionInfo;
transferLog = log;
TimeoutSeconds = timeoutSeconds;
MaxRetryAttempts = maxRetryAttempts;
}
[AsyncStateMachine(typeof(<RequestDeleteAsync>d__15<>))]
public Task<T> RequestDeleteAsync<T>(string endpoint, Func<int, TimeSpan> sleepDurationProvider, Action<Exception, TimeSpan, Context> onRetry, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage, CancellationToken token) where T : class, new
{
<RequestDeleteAsync>d__15<T> stateMachine = default(<RequestDeleteAsync>d__15<T>);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<T>.Create();
stateMachine.<>4__this = this;
stateMachine.endpoint = endpoint;
stateMachine.sleepDurationProvider = sleepDurationProvider;
stateMachine.onRetry = onRetry;
stateMachine.onEndpointErrorTitle = onEndpointErrorTitle;
stateMachine.onEndpointErrorMessage = onEndpointErrorMessage;
stateMachine.token = token;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
[AsyncStateMachine(typeof(<RequestGetAsync>d__16<>))]
public Task<T> RequestGetAsync<T>(string endpoint, Func<int, TimeSpan> sleepDurationProvider, Action<Exception, TimeSpan, Context> onRetry, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage, CancellationToken token) where T : class, new
{
<RequestGetAsync>d__16<T> stateMachine = default(<RequestGetAsync>d__16<T>);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<T>.Create();
stateMachine.<>4__this = this;
stateMachine.endpoint = endpoint;
stateMachine.sleepDurationProvider = sleepDurationProvider;
stateMachine.onRetry = onRetry;
stateMachine.onEndpointErrorTitle = onEndpointErrorTitle;
stateMachine.onEndpointErrorMessage = onEndpointErrorMessage;
stateMachine.token = token;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
[AsyncStateMachine(typeof(<RequestPostAsync>d__17<>))]
public Task<T> RequestPostAsync<T>(string endpoint, string content, Func<int, TimeSpan> sleepDurationProvider, Action<Exception, TimeSpan, Context> onRetry, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage, CancellationToken token) where T : class, new
{
<RequestPostAsync>d__17<T> stateMachine = default(<RequestPostAsync>d__17<T>);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<T>.Create();
stateMachine.<>4__this = this;
stateMachine.endpoint = endpoint;
stateMachine.content = content;
stateMachine.sleepDurationProvider = sleepDurationProvider;
stateMachine.onRetry = onRetry;
stateMachine.onEndpointErrorTitle = onEndpointErrorTitle;
stateMachine.onEndpointErrorMessage = onEndpointErrorMessage;
stateMachine.token = token;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
public Task<string> RequestJsonPostAsync(string endpoint, string content, Func<int, TimeSpan> sleepDurationProvider, Action<Exception, TimeSpan, Context> onRetry, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage, CancellationToken token)
{
return RequestAsync(2, endpoint, content, sleepDurationProvider, onRetry, onEndpointErrorTitle, onEndpointErrorMessage, token);
}
[AsyncStateMachine(typeof(<RequestAsync>d__19))]
private Task<string> RequestAsync(int method, string endpoint, string content, Func<int, TimeSpan> sleepDurationProvider, Action<Exception, TimeSpan, Context> onRetry, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage, CancellationToken token)
{
<RequestAsync>d__19 stateMachine = default(<RequestAsync>d__19);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
stateMachine.<>4__this = this;
stateMachine.method = method;
stateMachine.endpoint = endpoint;
stateMachine.content = content;
stateMachine.sleepDurationProvider = sleepDurationProvider;
stateMachine.onRetry = onRetry;
stateMachine.onEndpointErrorTitle = onEndpointErrorTitle;
stateMachine.onEndpointErrorMessage = onEndpointErrorMessage;
stateMachine.token = token;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
[AsyncStateMachine(typeof(<ExecuteCall>d__20))]
private Task<string> ExecuteCall(int method, string endpoint, string content, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage, CancellationToken token, bool forceTokenRefreshRetry = true)
{
<ExecuteCall>d__20 stateMachine = default(<ExecuteCall>d__20);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
stateMachine.<>4__this = this;
stateMachine.method = method;
stateMachine.endpoint = endpoint;
stateMachine.content = content;
stateMachine.onEndpointErrorTitle = onEndpointErrorTitle;
stateMachine.onEndpointErrorMessage = onEndpointErrorMessage;
stateMachine.token = token;
stateMachine.forceTokenRefreshRetry = forceTokenRefreshRetry;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
private void ConfigureHttpClient(HttpClient client, string endpoint, Func<HttpStatusCode, string> onEndpointErrorTitle)
{
client.BaseAddress = connectionInfo.Host;
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("X-CSRF-Header", string.Empty);
string authenticationHeader = connectionInfo.Credential.GetAuthenticationHeader();
if (string.IsNullOrEmpty(authenticationHeader))
throw CreateCredentialNotSupportedTransferException(endpoint, onEndpointErrorTitle, connectionInfo.Credential);
client.DefaultRequestHeaders.Add("Authorization", authenticationHeader);
if (TimeoutSeconds > 0)
client.Timeout = TimeSpan.FromSeconds(TimeoutSeconds);
}
private static void EnableTls12()
{
ServicePointManager.SecurityProtocol = (SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12);
}
private void RefreshCredentials(string methodName)
{
transferLog.LogInformation("Authorization issue occured. Trying to auto refresh credentials and make next call to {method}", methodName);
connectionInfo.Credential.RefreshCredentials();
}
private TransferException CreateExtendedTransferException(string endpoint, string methodName, HttpStatusCode statusCode, string json, Exception exception, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage, bool fatal)
{
transferLog.LogError(exception, fatal ? "Fatal attempt to call the HTTP '{Endpoint}' ({HttpMethod}) endpoint operation. HTTP StatusCode={StatusCode}, Response={JsonResponse}" : "Failed to call the HTTP '{Endpoint}' ({HttpMethod}) endpoint operation. HTTP StatusCode={StatusCode}, Response={JsonResponse}", endpoint, methodName, statusCode, json);
string text = onEndpointErrorTitle(statusCode);
if (string.IsNullOrEmpty(text))
text = CoreStrings.NoEndpointProvided;
string text2 = onEndpointErrorMessage(statusCode);
if (string.IsNullOrEmpty(text2))
text2 = CoreStrings.NoMessageProvided;
string text3 = GlobalSettings.Instance.FatalHttpStatusCodeDetailedMessage(statusCode);
if (string.IsNullOrEmpty(text3))
text3 = exception.Message;
if (string.IsNullOrEmpty(text3))
text3 = CoreStrings.NoMessageProvided;
string text4 = string.Format(CultureInfo.CurrentCulture, CoreStrings.HttpExceptionMessage, text, methodName, (int)statusCode, text2, text3);
if (statusCode == (HttpStatusCode)0)
text4 = string.Format(CultureInfo.CurrentCulture, CoreStrings.HttpNoStatusExceptionMessage, text, methodName, text2, text3);
text4 = text4.TrimEnd(Array.Empty<char>());
return new TransferException(text4, exception, fatal);
}
private TransferException CreateExtendedTransferException(string endpoint, string methodName, string json, WebException exception, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage, bool fatal)
{
transferLog.LogError(exception, fatal ? "Fatal attempt to call the HTTP '{Endpoint}' ({HttpMethod}) endpoint operation. Web Response Status={WebResponseStatus}, Response={JsonResponse}" : "Failed to call the HTTP '{Endpoint}' ({HttpMethod}) endpoint operation. Web Response Status={WebResponseStatus}, Response={JsonResponse}", endpoint, methodName, exception.Status, json);
string text = onEndpointErrorTitle((HttpStatusCode)0);
if (string.IsNullOrEmpty(text))
text = CoreStrings.NoEndpointProvided;
string text2 = onEndpointErrorMessage((HttpStatusCode)0);
if (string.IsNullOrEmpty(text2))
text2 = CoreStrings.NoMessageProvided;
string text3 = GlobalSettings.Instance.FatalWebExceptionStatusCodeDetailedMessage(exception.Status);
if (string.IsNullOrEmpty(text3))
text3 = exception.Message;
if (string.IsNullOrEmpty(text3))
text3 = CoreStrings.NoMessageProvided;
return new TransferException(string.Format(CultureInfo.CurrentCulture, CoreStrings.WebExceptionMessage, text, methodName, (int)exception.Status, text2, text3).TrimEnd(Array.Empty<char>()), exception, fatal);
}
private TransferException CreateTimeoutTransferException(string endpoint, string methodName, Exception exception, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage)
{
transferLog.LogError(exception, "Failed to call the HTTP '{Endpoint}' ({HttpMethod}) endpoint operation because it exceeded the {HttpTimeoutSeconds} second timeout.", endpoint, methodName, TimeoutSeconds);
string text = onEndpointErrorTitle(HttpStatusCode.RequestTimeout);
if (string.IsNullOrEmpty(text))
text = CoreStrings.NoEndpointProvided;
string text2 = onEndpointErrorMessage(HttpStatusCode.RequestTimeout);
if (string.IsNullOrEmpty(text2))
text2 = CoreStrings.NoMessageProvided;
string text3 = exception.Message;
if (string.IsNullOrEmpty(text3))
text3 = CoreStrings.NoMessageProvided;
return new TransferException(string.Format(CultureInfo.CurrentCulture, CoreStrings.HttpTimeoutExceptionMessage, text, methodName, TimeoutSeconds, text2, text3).TrimEnd(Array.Empty<char>()), exception, false);
}
private TransferException CreateCredentialNotSupportedTransferException(string endpoint, Func<HttpStatusCode, string> onEndpointErrorTitle, IHttpCredential credential)
{
transferLog.LogError("Failed to call the HTTP '{Endpoint}' endpoint operation because the supplied Transfer API credential object '{CredentialType}' is not supported.", endpoint, credential.GetType());
string text = onEndpointErrorTitle(HttpStatusCode.Unauthorized);
if (string.IsNullOrEmpty(text))
text = CoreStrings.NoEndpointProvided;
return new TransferException(string.Format(CultureInfo.CurrentCulture, CoreStrings.HttpCredentialNotSupportedExceptionMessage, text, credential.GetType().ToString()).TrimEnd(Array.Empty<char>()), true);
}
}
}