Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions generator/.DevConfigs/9d07dc1e-d82d-4f94-8700-c7b57f872123.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"services": [
{
"serviceName": "S3",
"type": "minor",
"changeLogMessages": [
"Added DownloadInitiatedEvent, DownloadCompletedEvent, and DownloadFailedEvent for downloads."
]
}
]
}
6 changes: 6 additions & 0 deletions sdk/src/Services/S3/Custom/Model/GetObjectResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
using Amazon.S3.Model.Internal.MarshallTransformations;
using Amazon.S3;
using Amazon.Runtime.Internal;
using Amazon.S3.Transfer;

namespace Amazon.S3.Model
{
Expand Down Expand Up @@ -1042,5 +1043,10 @@ internal WriteObjectProgressArgs(string bucketName, string key, string filePath,
/// True if writing is complete
/// </summary>
public bool IsCompleted { get; private set; }

/// <summary>
/// The original TransferUtilityDownloadRequest created by the user.
/// </summary>
public TransferUtilityDownloadRequest Request { get; internal set; }
}
}
34 changes: 34 additions & 0 deletions sdk/src/Services/S3/Custom/Transfer/Internal/DownloadCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,34 @@ static Logger Logger

IAmazonS3 _s3Client;
TransferUtilityDownloadRequest _request;
long _totalTransferredBytes;

#region Event Firing Methods

private void FireTransferInitiatedEvent()
{
var transferInitiatedEventArgs = new DownloadInitiatedEventArgs(_request, _request.FilePath);
_request.OnRaiseTransferInitiatedEvent(transferInitiatedEventArgs);
}

private void FireTransferCompletedEvent(TransferUtilityDownloadResponse response, string filePath, long transferredBytes, long totalBytes)
{
var transferCompletedEventArgs = new DownloadCompletedEventArgs(
_request,
response,
filePath,
transferredBytes,
totalBytes);
_request.OnRaiseTransferCompletedEvent(transferCompletedEventArgs);
}

private void FireTransferFailedEvent(string filePath, long transferredBytes, long totalBytes = -1)
{
var eventArgs = new DownloadFailedEventArgs(this._request, filePath, transferredBytes, totalBytes);
this._request.OnRaiseTransferFailedEvent(eventArgs);
}

#endregion

internal DownloadCommand(IAmazonS3 s3Client, TransferUtilityDownloadRequest request)
{
Expand Down Expand Up @@ -89,6 +117,12 @@ private void ValidateRequest()

void OnWriteObjectProgressEvent(object sender, WriteObjectProgressArgs e)
{
// Keep track of the total transferred bytes so that we can also return this value in case of failure
Interlocked.Add(ref _totalTransferredBytes, e.IncrementTransferred);

// Set the Request property to enable access to the original download request
e.Request = this._request;

this._request.OnRaiseProgressEvent(e);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,17 @@ internal partial class DownloadCommand : BaseCommand<TransferUtilityDownloadResp
public override async Task<TransferUtilityDownloadResponse> ExecuteAsync(CancellationToken cancellationToken)
{
ValidateRequest();

FireTransferInitiatedEvent();

GetObjectRequest getRequest = ConvertToGetObjectRequest(this._request);

var maxRetries = _s3Client.Config.MaxErrorRetry;
var retries = 0;
bool shouldRetry = false;
string mostRecentETag = null;
TransferUtilityDownloadResponse lastSuccessfulMappedResponse = null;
long? totalBytesFromResponse = null; // Track total bytes once we have response headers
do
{
shouldRetry = false;
Expand All @@ -54,12 +59,16 @@ public override async Task<TransferUtilityDownloadResponse> ExecuteAsync(Cancell
using (var response = await this._s3Client.GetObjectAsync(getRequest, cancellationToken)
.ConfigureAwait(continueOnCapturedContext: false))
{
// Capture total bytes from response headers as soon as we get them
totalBytesFromResponse = response.ContentLength;

if (!string.IsNullOrEmpty(mostRecentETag) && !string.Equals(mostRecentETag, response.ETag))
{
//if the eTag changed, we need to retry from the start of the file
mostRecentETag = response.ETag;
getRequest.ByteRange = null;
retries = 0;
Interlocked.Exchange(ref _totalTransferredBytes, 0);
shouldRetry = true;
WaitBeforeRetry(retries);
continue;
Expand Down Expand Up @@ -101,6 +110,8 @@ await response.WriteResponseStreamToFileAsync(this._request.FilePath, false, can
await response.WriteResponseStreamToFileAsync(this._request.FilePath, true, cancellationToken)
.ConfigureAwait(continueOnCapturedContext: false);
}

lastSuccessfulMappedResponse = ResponseMapper.MapGetObjectResponse(response);
}
}
catch (Exception exception)
Expand All @@ -109,6 +120,9 @@ await response.WriteResponseStreamToFileAsync(this._request.FilePath, true, canc
shouldRetry = HandleExceptionForHttpClient(exception, retries, maxRetries);
if (!shouldRetry)
{
// Pass total bytes if we have them from response headers, otherwise -1 for unknown
FireTransferFailedEvent(this._request.FilePath, Interlocked.Read(ref _totalTransferredBytes), totalBytesFromResponse ?? -1);

if (exception is IOException)
{
throw;
Expand All @@ -131,8 +145,9 @@ await response.WriteResponseStreamToFileAsync(this._request.FilePath, true, canc
WaitBeforeRetry(retries);
} while (shouldRetry);

// TODO map and return response
return new TransferUtilityDownloadResponse();
FireTransferCompletedEvent(lastSuccessfulMappedResponse, this._request.FilePath, Interlocked.Read(ref _totalTransferredBytes), totalBytesFromResponse ?? -1);

return lastSuccessfulMappedResponse;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copilot is saying that this could potentially be null but based on my reading of the code, if there is any failure we will throw exception early on, so it can never be null. thoughts?

}

private static bool HandleExceptionForHttpClient(Exception exception, int retries, int maxRetries)
Expand Down
Loading