HttpTransferCommand
using Relativity.Transfer.Http.Issues;
using Relativity.Transfer.Http.Paths;
using Relativity.Transfer.Http.Resources;
using Relativity.Transfer.Paths;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace Relativity.Transfer.Http
{
internal sealed class HttpTransferCommand : TransferPathCommandBase
{
private const string LogPrefix = "HTTP Transfer Command";
private readonly HttpClientConfiguration configuration;
private readonly ISoapFileTransferService fileTransferService;
private readonly HttpTransferIssueBuilderFactory issueBuilderFactory;
public HttpTransferCommand(ITransferLog log, ITransferRequest request, ITransferJobService jobService, IFileSystemService fileSystemService, HttpClientConfiguration configuration, ISoapFileTransferService fileTransferService)
: base(log, request, jobService, fileSystemService, configuration)
{
if (fileTransferService == null)
throw new ArgumentNullException("fileTransferService");
if (configuration == null)
throw new ArgumentNullException("configuration");
this.fileTransferService = fileTransferService;
this.configuration = configuration;
issueBuilderFactory = new HttpTransferIssueBuilderFactory(this.configuration);
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "There are numerous exceptions and exception types to check for improved error handling.")]
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Reviewed OK.")]
protected override TransferPathResult OnExecute(TransferPath path, CancellationToken token)
{
if (!(path == (TransferPath)null)) {
TransferPathResult transferPathResult = new TransferPathResult {
Path = path,
Status = TransferPathStatus.Started,
StartTime = new DateTime?(DateTime.Now)
};
try {
if (!token.IsCancellationRequested) {
LargeFileProgress progress = (base.LargeFileProgressProvider != null) ? new LargeFileProgress(OnLargeFileProgress, base.LargeFileProgressRateSeconds) : null;
TaskAwaiter<TransferPathResult> awaiter;
switch (base.Request.Direction) {
case TransferDirection.Download: {
Task<TransferPathResult> task2 = fileTransferService.DownloadAsync(path, token, progress);
task2.Wait(token);
awaiter = task2.GetAwaiter();
transferPathResult = awaiter.GetResult();
break;
}
case TransferDirection.Upload: {
Task<TransferPathResult> task = fileTransferService.UploadAsync(path, token, progress);
task.Wait(token);
awaiter = task.GetAwaiter();
transferPathResult = awaiter.GetResult();
break;
}
}
if (transferPathResult.Status == TransferPathStatus.Successful || transferPathResult.Status == TransferPathStatus.Skipped)
base.JobService.Statistics.TotalTransferredFiles++;
base.JobService.Statistics.TotalTransferredBytes += transferPathResult.BytesTransferred;
PublishTransferStatistics(false);
if (token.IsCancellationRequested) {
transferPathResult.Status = TransferPathStatus.Canceled;
transferPathResult.EndTime = DateTime.Now;
}
return transferPathResult;
}
transferPathResult.Status = TransferPathStatus.Canceled;
transferPathResult.EndTime = DateTime.Now;
return transferPathResult;
} catch (ArgumentException ex) {
base.Log.LogError(ex, "The transfer path contains one or more invalid data.", Array.Empty<object>());
IssueAttributes attribute = IssueAttributes.Error | IssueAttributes.File | IssueAttributes.InvalidCharacters;
PublishTransferIssue(path, attribute, ex);
throw new TransferException(ex.Message, ex, true);
} catch (UnauthorizedAccessException exception) {
return HandleUnauthorizedAccessException(path, exception, transferPathResult, configuration.MaxJobRetryAttempts);
} catch (IOException ex2) {
base.Log.LogError(ex2, $"""{FormatPathInfo(path)}""", Array.Empty<object>());
IssueAttributes attribute2 = IssueAttributes.File | IssueAttributes.Io | IssueAttributes.StorageReadWrite | GetWarningErrorAttribute();
PublishTransferIssue(path, attribute2, ex2);
throw new TransferException(ex2.Message, ex2, false);
} catch (OperationCanceledException) {
HandleCancel(transferPathResult);
return transferPathResult;
} catch (AggregateException ex4) {
IssueAttributes issueAttributes = HandleAggregateException(ex4, path);
if (issueAttributes.HasFlag(IssueAttributes.Error))
base.Log.LogError(ex4, $"""{FormatPathInfo(path)}", Array.Empty<object>());
else if (issueAttributes.HasFlag(IssueAttributes.Warning)) {
base.Log.LogWarning(ex4, $"""{FormatPathInfo(path)}""", Array.Empty<object>());
}
if (!issueAttributes.HasFlag(IssueAttributes.FileNotFound)) {
if (!base.FilePermissionErrorsRetry || !issueAttributes.HasFlag(IssueAttributes.ReadWritePermissions)) {
HttpPathStatusResolver httpPathStatusResolver = new HttpPathStatusResolver(new PathStatusResolver(configuration));
using (IEnumerator<Exception> enumerator = ex4.Flatten().InnerExceptions.GetEnumerator()) {
if (enumerator.MoveNext()) {
Exception current = enumerator.Current;
TransferPathStatus transferPathStatus = httpPathStatusResolver.Resolve(current);
if (transferPathStatus == TransferPathStatus.Fatal)
throw new TransferException(HttpErrorCodes.GetErrorMessage(current), current, true);
transferPathResult.Status = transferPathStatus;
transferPathResult.EndTime = DateTime.Now;
return transferPathResult;
}
}
throw new TransferException(ex4.Message, ex4, false);
}
transferPathResult.Status = TransferPathStatus.PermissionsError;
transferPathResult.EndTime = DateTime.Now;
return transferPathResult;
}
transferPathResult.Status = TransferPathStatus.FileNotFound;
transferPathResult.EndTime = DateTime.Now;
return transferPathResult;
} catch (Exception ex5) {
base.Log.LogError(ex5, $"""{FormatPathInfo(path)}", Array.Empty<object>());
IssueAttributes attribute3 = GetWarningErrorAttribute() | IssueAttributes.File;
PublishTransferIssue(path, attribute3, ex5);
throw new TransferException(ex5.Message, ex5, false);
}
}
throw new ArgumentNullException("path");
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Reviewed OK.")]
protected override void OnPreExecute(CancellationToken token)
{
if (!token.IsCancellationRequested)
try {
base.Log.LogInformation("{0} - Attempting to authenticate with Relativity.", "HTTP Transfer Command");
fileTransferService.Login();
base.Log.LogInformation("{0} - Successfully authenticated with Relativity.", "HTTP Transfer Command");
} catch (AggregateException ex) {
base.Log.LogError(ex, HttpStrings.AuthenticationError, Array.Empty<object>());
HandleAggregateException(ex, null);
} catch (Exception exception) {
base.Log.LogError(exception, HttpStrings.AuthenticationError, Array.Empty<object>());
PublishTransferIssue(null, IssueAttributes.Authentication | IssueAttributes.Warning, exception);
}
}
private IssueAttributes HandleAggregateException(AggregateException aggregateException, TransferPath path)
{
if (aggregateException == null)
throw new ArgumentNullException("aggregateException");
IssueAttributes issueAttributes = (path != (TransferPath)null) ? IssueAttributes.File : IssueAttributes.Job;
foreach (Exception innerException in aggregateException.Flatten().InnerExceptions) {
IssueAttributes issueAttributesFromException = GetIssueAttributesFromException(path, innerException);
issueAttributes |= issueAttributesFromException;
PublishTransferIssue(path, issueAttributesFromException, innerException);
}
return issueAttributes;
}
private IssueAttributes GetIssueAttributesFromException(TransferPath path, Exception exception)
{
IssueAttributes issueAttributes = (path != (TransferPath)null) ? IssueAttributes.File : IssueAttributes.Job;
if (!(exception.GetType() == typeof(UnauthorizedAccessException))) {
if (!(exception.GetType() == typeof(ArgumentException))) {
if (!(exception.GetType() == typeof(FileNotFoundException))) {
if (!(exception.GetType() == typeof(DirectoryNotFoundException))) {
if (!(exception.GetType() == typeof(HttpRequestException))) {
if (!(exception.GetType() == typeof(OverflowException)))
return issueAttributes | GetWarningErrorAttribute();
return issueAttributes | IssueAttributes.Error;
}
issueAttributes |= (IssueAttributes.File | IssueAttributes.Io);
if (exception.Message.IndexOf("409", StringComparison.OrdinalIgnoreCase) <= 0)
return issueAttributes | GetWarningErrorAttribute();
return issueAttributes | (IssueAttributes.FileNotFound | (base.FileNotFoundErrorsDisabled ? IssueAttributes.Warning : GetWarningErrorAttribute()));
}
return issueAttributes | (IssueAttributes.DirectoryNotFound | IssueAttributes.File | IssueAttributes.Io | GetWarningErrorAttribute());
}
return issueAttributes | (IssueAttributes.File | IssueAttributes.FileNotFound | IssueAttributes.Io | (base.FileNotFoundErrorsDisabled ? IssueAttributes.Warning : GetWarningErrorAttribute()));
}
return issueAttributes | (IssueAttributes.Error | IssueAttributes.File | IssueAttributes.InvalidCharacters);
}
return issueAttributes | (IssueAttributes.File | IssueAttributes.ReadWritePermissions | (base.FilePermissionErrorsRetry ? GetWarningErrorAttribute() : IssueAttributes.Error));
}
private void PublishTransferIssue(TransferPath path, IssueAttributes attribute, Exception exception)
{
ITransferIssue issue = issueBuilderFactory.Create().CreateFromException(path, exception).WithAttributes(attribute)
.WithMaxRetryAttempts(configuration.MaxJobRetryAttempts)
.WithRetryAttempt(base.JobService.Statistics.RetryAttempt)
.Build();
RegisterIssue(issue);
}
private string FormatPathInfo(TransferPath path)
{
return $"""{base.Request.Direction.ToString()}""{path.SourcePath}""{path.TargetFileName ?? string.Empty}""{path.TargetPath}""";
}
}
}