RestClient
class RestClient
Represents a service class object to perform REST operations used by import/export API. This class cannot be inherited.
using Polly;
using Relativity.DataExchange.Logger;
using Relativity.DataExchange.Resources;
using Relativity.Logging;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Relativity.DataExchange
{
internal 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 RelativityInstanceInfo instance;
private readonly ILog logger;
public int MaxRetryAttempts { get; set; }
public double TimeoutSeconds { get; }
public RestClient(RelativityInstanceInfo instance, ILog log, double timeoutSeconds, int maxRetryAttempts)
{
if (instance == null)
throw new ArgumentNullException("instance");
if (log == null)
throw new ArgumentNullException("log");
this.instance = instance;
logger = log;
TimeoutSeconds = timeoutSeconds;
MaxRetryAttempts = maxRetryAttempts;
}
public Task<string> RequestPostStringAsync(string relativeEndpoint, string content, Func<int, TimeSpan> sleepDurationProvider, Action<Exception, TimeSpan, Context> onRetry, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage, CancellationToken token)
{
return RequestAsync(2, relativeEndpoint, content, sleepDurationProvider, onRetry, onEndpointErrorTitle, onEndpointErrorMessage, token);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This is done by design for this type of method.")]
internal static HttpStatusCode TryGetHttpStatusCode(WebException exception)
{
try {
HttpStatusCode? nullable = (exception.Response as HttpWebResponse)?.StatusCode ?? HttpStatusCode.InternalServerError;
return nullable.Value;
} catch {
return HttpStatusCode.InternalServerError;
}
}
[AsyncStateMachine(typeof(<RequestAsync>d__16))]
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__16 stateMachine = default(<RequestAsync>d__16);
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(<ReadResponseAsync>d__17))]
private Task<string> ReadResponseAsync(Uri endpoint, string methodName, HttpResponseMessage response, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage)
{
<ReadResponseAsync>d__17 stateMachine = default(<ReadResponseAsync>d__17);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
stateMachine.<>4__this = this;
stateMachine.endpoint = endpoint;
stateMachine.methodName = methodName;
stateMachine.response = response;
stateMachine.onEndpointErrorTitle = onEndpointErrorTitle;
stateMachine.onEndpointErrorMessage = onEndpointErrorMessage;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
private HttpServiceException CreateHttpServiceException(string endpoint, string methodName, HttpStatusCode statusCode, string json, Exception exception, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage, bool fatal)
{
logger.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}", new object[4] {
endpoint.Secure(),
methodName,
statusCode,
json
});
string text = onEndpointErrorTitle(statusCode);
if (string.IsNullOrEmpty(text))
text = Strings.NoEndpointProvided;
string text2 = onEndpointErrorMessage(statusCode);
if (string.IsNullOrEmpty(text2))
text2 = Strings.NoMessageProvided;
string text3 = ExceptionHelper.GetDetailedFatalMessage(statusCode);
if (string.IsNullOrEmpty(text3))
text3 = exception.Message;
if (string.IsNullOrEmpty(text3))
text3 = Strings.NoMessageProvided;
string text4 = string.Format(CultureInfo.CurrentCulture, Strings.HttpExceptionMessage, text, methodName, (int)statusCode, text2, text3);
if (statusCode == (HttpStatusCode)0)
text4 = string.Format(CultureInfo.CurrentCulture, Strings.HttpNoStatusExceptionMessage, text, methodName, text2, text3);
text4 = text4.TrimEnd(Array.Empty<char>());
return new HttpServiceException(text4, exception, statusCode, fatal);
}
private HttpServiceException CreateHttpServiceException(string endpoint, string methodName, string json, WebException exception, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage, bool fatal)
{
logger.LogError((Exception)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}", new object[4] {
endpoint.Secure(),
methodName,
exception.Status,
json
});
string text = onEndpointErrorTitle((HttpStatusCode)0);
if (string.IsNullOrEmpty(text))
text = Strings.NoEndpointProvided;
string text2 = onEndpointErrorMessage((HttpStatusCode)0);
if (string.IsNullOrEmpty(text2))
text2 = Strings.NoMessageProvided;
string text3 = ExceptionHelper.GetDetailedFatalMessage(exception.Status);
if (string.IsNullOrEmpty(text3))
text3 = exception.Message;
if (string.IsNullOrEmpty(text3))
text3 = Strings.NoMessageProvided;
string message = string.Format(CultureInfo.CurrentCulture, Strings.WebExceptionMessage, text, methodName, (int)exception.Status, text2, text3).TrimEnd(Array.Empty<char>());
HttpStatusCode statusCode = TryGetHttpStatusCode(exception);
return new HttpServiceException(message, exception, statusCode, fatal);
}
private HttpServiceException CreateTimeoutHttpServiceException(string endpoint, string methodName, Exception exception, Func<HttpStatusCode, string> onEndpointErrorTitle, Func<HttpStatusCode, string> onEndpointErrorMessage)
{
logger.LogError(exception, "Failed to call the HTTP '{Endpoint}' ({HttpMethod}) endpoint operation because it exceeded the {HttpTimeoutSeconds} second timeout.", new object[3] {
endpoint.Secure(),
methodName,
TimeoutSeconds
});
string text = onEndpointErrorTitle(HttpStatusCode.RequestTimeout);
if (string.IsNullOrEmpty(text))
text = Strings.NoEndpointProvided;
string text2 = onEndpointErrorMessage(HttpStatusCode.RequestTimeout);
if (string.IsNullOrEmpty(text2))
text2 = Strings.NoMessageProvided;
string text3 = exception.Message;
if (string.IsNullOrEmpty(text3))
text3 = Strings.NoMessageProvided;
return new HttpServiceException(string.Format(CultureInfo.CurrentCulture, Strings.HttpTimeoutExceptionMessage, text, methodName, TimeoutSeconds, text2, text3).TrimEnd(Array.Empty<char>()), exception, HttpStatusCode.RequestTimeout, false);
}
private HttpServiceException CreateCredentialNotSupportedException(string endpoint, Func<HttpStatusCode, string> onEndpointErrorTitle)
{
logger.LogError("Failed to call the HTTP '{Endpoint}' endpoint operation because the supplied Transfer API credential object '{CredentialType}' is not supported.", new object[2] {
endpoint.Secure(),
instance.Credentials.GetType()
});
string text = onEndpointErrorTitle(HttpStatusCode.Unauthorized);
if (string.IsNullOrEmpty(text))
text = Strings.NoEndpointProvided;
return new HttpServiceException(string.Format(CultureInfo.CurrentCulture, Strings.HttpCredentialNotSupportedExceptionMessage, text, instance.Credentials.GetType().ToString()).TrimEnd(Array.Empty<char>()), HttpStatusCode.Unauthorized, true);
}
private string GetAuthorizationHeader()
{
NetworkCredential networkCredential = instance.Credentials as NetworkCredential;
if (networkCredential == null)
return null;
if (networkCredential.UserName == "XxX_BearerTokenCredentials_XxX")
return string.Format(CultureInfo.InvariantCulture, "Bearer {0}", networkCredential.Password);
string s = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", networkCredential.UserName, networkCredential.Password);
return string.Format(CultureInfo.InvariantCulture, "Basic {0}", Convert.ToBase64String(Encoding.ASCII.GetBytes(s)));
}
}
}