<PackageReference Include="Relativity.Transfer.Client" Version="5.0.7" />

RestClient

public class RestClient
using Polly; using Relativity.Transfer.Resources; using System; using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Relativity.Transfer { public class RestClient { 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; } public async 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 { return SerializationHelper.DeserializeFromJson<T>(await RequestAsync(3, endpoint, string.Empty, sleepDurationProvider, onRetry, onEndpointErrorTitle, onEndpointErrorMessage, token).ConfigureAwait(false)); } public async 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 { return SerializationHelper.DeserializeFromJson<T>(await RequestAsync(1, endpoint, string.Empty, sleepDurationProvider, onRetry, onEndpointErrorTitle, onEndpointErrorMessage, token).ConfigureAwait(false)); } public async 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 { return SerializationHelper.DeserializeFromJson<T>(await RequestAsync(2, endpoint, content, sleepDurationProvider, onRetry, onEndpointErrorTitle, onEndpointErrorMessage, token).ConfigureAwait(false)); } 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); } private async 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) { WebException webException; HttpWebResponse response; return await RetrySyntaxAsync.WaitAndRetryAsync(Policy.Handle<TransferException>((Func<TransferException, bool>)((TransferException e) => !e.Fatal)), MaxRetryAttempts, sleepDurationProvider, onRetry).ExecuteAsync<string>((Func<CancellationToken, Task<string>>)async delegate { ServicePointManager.SecurityProtocol = (SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12); using (HttpClient client = new HttpClient()) { 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); using (StringContent stringContent = new StringContent((!string.IsNullOrEmpty(content)) ? content : string.Empty, Encoding.UTF8, "application/json")) { Uri endpointUri = new Uri(endpoint, UriKind.Relative); string methodName = null; try { HttpResponseMessage response2; switch (method) { case 1: methodName = "GET"; transferLog.LogDebug("Preparing to call the HTTP '{Endpoint}' ({HttpMethod}) endpoint...", endpointUri, methodName); response2 = await client.GetAsync(endpointUri, token).ConfigureAwait(false); break; case 2: methodName = "POST"; transferLog.LogDebug("Preparing to call the HTTP '{Endpoint}' ({HttpMethod}) endpoint...", endpointUri, methodName); response2 = await client.PostAsync(endpointUri, stringContent, token).ConfigureAwait(false); break; case 3: methodName = "DELETE"; transferLog.LogDebug("Preparing to call the HTTP '{Endpoint}' ({HttpMethod}) endpoint...", endpointUri, methodName); response2 = await client.DeleteAsync(endpointUri, token).ConfigureAwait(false); break; default: throw new NotSupportedException($"""{method}"""); } return await ReadResponseAsync(endpointUri, methodName, response2, onEndpointErrorTitle, onEndpointErrorMessage).ConfigureAwait(false); } catch (TaskCanceledException exception) { if (token.IsCancellationRequested) throw; throw CreateTimeoutTransferException(endpoint, methodName, exception, onEndpointErrorTitle, onEndpointErrorMessage); } catch (HttpRequestException ex) { webException = (ex.InnerException as WebException); if (webException != null) { if (GlobalSettings.Instance.FatalWebExceptionStatusCodes.Any((WebExceptionStatus x) => x == webException.Status)) throw CreateExtendedTransferException(endpoint, methodName, string.Empty, webException, onEndpointErrorTitle, onEndpointErrorMessage, true); response = (webException.Response as HttpWebResponse); if (response != null) { bool fatal = GlobalSettings.Instance.FatalHttpStatusCodes.Any((HttpStatusCode x) => x == response.StatusCode); throw CreateExtendedTransferException(endpoint, methodName, response.StatusCode, string.Empty, webException, onEndpointErrorTitle, onEndpointErrorMessage, fatal); } throw CreateExtendedTransferException(endpoint, methodName, string.Empty, webException, onEndpointErrorTitle, onEndpointErrorMessage, false); } throw CreateExtendedTransferException(endpoint, methodName, (HttpStatusCode)0, string.Empty, ex, onEndpointErrorTitle, onEndpointErrorMessage, false); } } } }, token).ConfigureAwait(false); } private async Task<string> ReadResponseAsync(Uri endpoint, string methodName, HttpResponseMessage response, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage) { string json = string.Empty; try { json = await response.Content.ReadAsStringAsync().ConfigureAwait(false); response.EnsureSuccessStatusCode(); transferLog.LogDebug("Successfully called the HTTP '{Endpoint}' ({HttpMethod}) endpoint.", endpoint, methodName); return json; } catch (OperationCanceledException exception) { transferLog.LogInformation(exception, "The user cancelled the HTTP '{Endpoint}' ({HttpMethod}) endpoint operation.", endpoint, methodName); throw; } catch (Exception exception2) { bool fatal = GlobalSettings.Instance.FatalHttpStatusCodes.Any((HttpStatusCode candidate) => candidate == response.StatusCode); throw CreateExtendedTransferException(endpoint.ToString(), methodName, response.StatusCode, json, exception2, onEndpointErrorTitle, onEndpointErrorMessage, fatal); } } 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); } } }