Skip to content
Merged
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
2 changes: 1 addition & 1 deletion sdk/storage/Azure.Storage.Blobs/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "net",
"TagPrefix": "net/storage/Azure.Storage.Blobs",
"Tag": "net/storage/Azure.Storage.Blobs_9497f4a828"
"Tag": "net/storage/Azure.Storage.Blobs_62a8c8fa29"
}
2 changes: 2 additions & 0 deletions sdk/storage/Azure.Storage.Blobs/src/BlobClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ private void AddHeadersAndQueryParameters()
Diagnostics.LoggedHeaderNames.Add("x-ms-source-if-unmodified-since");
Diagnostics.LoggedHeaderNames.Add("x-ms-tag-count");
Diagnostics.LoggedHeaderNames.Add("x-ms-encryption-key-sha256");
Diagnostics.LoggedHeaderNames.Add("x-ms-copy-source-error-code");
Diagnostics.LoggedHeaderNames.Add("x-ms-copy-source-status-code");

Diagnostics.LoggedQueryParameters.Add("comp");
Diagnostics.LoggedQueryParameters.Add("maxresults");
Expand Down
21 changes: 21 additions & 0 deletions sdk/storage/Azure.Storage.Blobs/tests/AppendBlobClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,27 @@ public async Task AppendBlockFromUriAsync_Min()
}
}

[RecordedTest]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2024_08_04)]
public async Task AppendBlockFromUriAsync_SourceErrorAndStatusCode()
{
await using DisposingContainer test = await GetTestContainerAsync(publicAccessType: PublicAccessType.None);

AppendBlobClient sourceBlob = InstrumentClient(test.Container.GetAppendBlobClient(GetNewBlobName()));
AppendBlobClient destBlob = InstrumentClient(test.Container.GetAppendBlobClient(GetNewBlobName()));
await destBlob.CreateIfNotExistsAsync();

// Act
await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
destBlob.AppendBlockFromUriAsync(sourceBlob.Uri),
e =>
{
Assert.IsTrue(e.Message.Contains("CopySourceStatusCode: 409"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorCode: PublicAccessNotPermitted"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorMessage: Public access is not permitted on this storage account."));
});
}

[RecordedTest]
[TestCase(nameof(AppendBlobRequestConditions.LeaseId))]
[TestCase(nameof(AppendBlobRequestConditions.TagConditions))]
Expand Down
44 changes: 44 additions & 0 deletions sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1999,6 +1999,29 @@ public async Task StartCopyFromUriAsync()
Assert.IsTrue(operation.HasValue);
}

[RecordedTest]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2024_08_04)]
public async Task StartCopyFromUriAsync_SourceErrorAndStatusCode()
{
await using DisposingContainer test = await GetTestContainerAsync(publicAccessType: PublicAccessType.None);

// Arrange
BlobBaseClient srcBlob = InstrumentClient(test.Container.GetBlockBlobClient(GetNewBlobName()));
BlockBlobClient destBlob = InstrumentClient(test.Container.GetBlockBlobClient(GetNewBlobName()));

Uri sourceUri = srcBlob.GenerateSasUri(BlobSasPermissions.Read, GetUtcNow().AddDays(1));

// Act
await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
destBlob.StartCopyFromUriAsync(sourceUri),
e =>
{
Assert.IsTrue(e.Message.Contains("CopySourceStatusCode: 404"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorCode: BlobNotFound"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorMessage: The specified blob does not exist."));
});
}

[RecordedTest]
[TestCase(nameof(BlobRequestConditions.LeaseId))]
public async Task StartCopyFromUriAsync_InvalidSourceRequestConditions(string invalidSourceCondition)
Expand Down Expand Up @@ -3332,6 +3355,27 @@ await destBlob.SyncCopyFromUriAsync(
}
}

[RecordedTest]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2024_08_04)]
public async Task SyncCopyFromUriAsync_SourceErrorAndStatusCode()
{
await using DisposingContainer test = await GetTestContainerAsync(publicAccessType: PublicAccessType.None);

// Arrange
BlockBlobClient srcBlob = InstrumentClient(test.Container.GetBlockBlobClient(GetNewBlobName()));
BlockBlobClient destBlob = InstrumentClient(test.Container.GetBlockBlobClient(GetNewBlobName()));

// Act
await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
destBlob.SyncCopyFromUriAsync(srcBlob.Uri),
e =>
{
Assert.IsTrue(e.Message.Contains("CopySourceStatusCode: 409"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorCode: PublicAccessNotPermitted"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorMessage: Public access is not permitted on this storage account."));
});
}

[RecordedTest]
public async Task DeleteAsync()
{
Expand Down
42 changes: 42 additions & 0 deletions sdk/storage/Azure.Storage.Blobs/tests/BlockBlobClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,27 @@ await RetryAsync(
_retryStageBlockFromUri);
}

[RecordedTest]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2024_08_04)]
public async Task StageBlobFromUriAsync_SourceErrorAndStatusCode()
{
// Arrange
var constants = TestConstants.Create(this);
await using DisposingContainer test = await GetTestContainerAsync(publicAccessType: PublicAccessType.None);
BlockBlobClient sourceBlob = InstrumentClient(test.Container.GetBlockBlobClient(GetNewBlobName()));
BlockBlobClient destBlob = InstrumentClient(test.Container.GetBlockBlobClient(GetNewBlobName()));

// Act
await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
destBlob.StageBlockFromUriAsync(sourceBlob.Uri, ToBase64(GetNewBlockName())),
e =>
{
Assert.IsTrue(e.Message.Contains("CopySourceStatusCode: 409"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorCode: PublicAccessNotPermitted"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorMessage: Public access is not permitted on this storage account."));
});
}

[RecordedTest]
[TestCase(nameof(BlobRequestConditions.IfModifiedSince))]
[TestCase(nameof(BlobRequestConditions.IfUnmodifiedSince))]
Expand Down Expand Up @@ -2821,6 +2842,27 @@ await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
e => Assert.AreEqual(BlobErrorCode.CannotVerifyCopySource.ToString(), e.ErrorCode));
}

[RecordedTest]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2024_08_04)]
public async Task SyncUploadFromUriAsync_SourceErrorAndStatusCode()
{
// Arrange
var constants = TestConstants.Create(this);
await using DisposingContainer test = await GetTestContainerAsync(publicAccessType: PublicAccessType.None);
BlockBlobClient sourceBlob = InstrumentClient(test.Container.GetBlockBlobClient(GetNewBlobName()));
BlockBlobClient destBlob = InstrumentClient(test.Container.GetBlockBlobClient(GetNewBlobName()));

// Act
await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
destBlob.SyncUploadFromUriAsync(sourceBlob.Uri),
e =>
{
Assert.IsTrue(e.Message.Contains("CopySourceStatusCode: 409"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorCode: PublicAccessNotPermitted"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorMessage: Public access is not permitted on this storage account."));
});
}

[RecordedTest]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2020_04_08)]
public async Task SyncUploadFromUriAsync_OverwriteSourceBlobProperties()
Expand Down
25 changes: 25 additions & 0 deletions sdk/storage/Azure.Storage.Blobs/tests/PageBlobClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3370,6 +3370,31 @@ public async Task UploadPagesFromUriAsync_Min()
}
}

[RecordedTest]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2024_08_04)]
public async Task UploadPagesFromUriAsync_SourceErrorAndStatusCode()
{
// Arrange
await using DisposingContainer test = await GetTestContainerAsync(publicAccessType: PublicAccessType.None);

PageBlobClient sourceBlob = InstrumentClient(test.Container.GetPageBlobClient(GetNewBlobName()));

PageBlobClient destBlob = InstrumentClient(test.Container.GetPageBlobClient(GetNewBlobName()));
await destBlob.CreateIfNotExistsAsync(Constants.KB);

HttpRange range = new HttpRange(0, Constants.KB);

// Act
await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
destBlob.UploadPagesFromUriAsync(sourceBlob.Uri, range, range),
e =>
{
Assert.IsTrue(e.Message.Contains("CopySourceStatusCode: 409"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorCode: PublicAccessNotPermitted"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorMessage: Public access is not permitted on this storage account."));
});
}

[RecordedTest]
[TestCase(nameof(PageBlobRequestConditions.LeaseId))]
[TestCase(nameof(PageBlobRequestConditions.IfSequenceNumberLessThanOrEqual))]
Expand Down
1 change: 1 addition & 0 deletions sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ internal static class HeaderNames
public const string LeaseId = "x-ms-lease-id";
public const string LastModified = "Last-Modified";
public const string ETag = "ETag";
public const string CopySourceErrorCode = "x-ms-copy-source-error-code";
}

internal static class ErrorCodes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ public override bool IsRetriableResponse(HttpMessage message)
return true;
}
}

// Retry select Copy Source Error Codes.
if (message.Response.Status >= 400 &&
message.Response.Headers.TryGetValue(Constants.HeaderNames.CopySourceErrorCode, out string copySourceError))
{
switch (copySourceError)
{
case Constants.ErrorCodes.InternalError:
case Constants.ErrorCodes.OperationTimedOut:
case Constants.ErrorCodes.ServerBusy:
return true;
}
}

return base.IsRetriableResponse(message);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,30 @@ public void IsRetriableResponse_StorageErrors_SecondaryUri(string errorCode)
Assert.IsTrue(classifier.IsRetriableResponse(message));
}

[Test]
[TestCase(Constants.ErrorCodes.ServerBusy)]
[TestCase(Constants.ErrorCodes.InternalError)]
[TestCase(Constants.ErrorCodes.OperationTimedOut)]
public void IsRetriableResponse_CopySourceErrors(string errorCode)
{
var response = new MockResponse(Constants.HttpStatusCode.NotFound);
response.AddHeader(new HttpHeader(Constants.HeaderNames.CopySourceErrorCode, errorCode));
HttpMessage message = BuildMessage(response);
Assert.IsTrue(classifier.IsRetriableResponse(message));
}

[Test]
[TestCase(Constants.ErrorCodes.ServerBusy)]
[TestCase(Constants.ErrorCodes.InternalError)]
[TestCase(Constants.ErrorCodes.OperationTimedOut)]
public void IsRetriableResponse_CopySourceErrors_SecondaryUri(string errorCode)
{
var response = new MockResponse(Constants.HttpStatusCode.NotFound);
response.AddHeader(new HttpHeader(Constants.HeaderNames.CopySourceErrorCode, errorCode));
HttpMessage message = BuildMessage(response, MockSecondaryUri);
Assert.IsTrue(classifier.IsRetriableResponse(message));
}

[TestCase("ContainerAlreadyExists", "If-Match", false)]
[TestCase("ContainerAlreadyExists","If-None-Match", false)]
[TestCase("ContainerAlreadyExists","If-Unmodified-Since", false)]
Expand Down
2 changes: 1 addition & 1 deletion sdk/storage/Azure.Storage.Files.Shares/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "net",
"TagPrefix": "net/storage/Azure.Storage.Files.Shares",
"Tag": "net/storage/Azure.Storage.Files.Shares_3a253f1460"
"Tag": "net/storage/Azure.Storage.Files.Shares_07c3d59f5a"
}
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ private void AddHeadersAndQueryParameters()
Diagnostics.LoggedHeaderNames.Add("x-ms-share-quota");
Diagnostics.LoggedHeaderNames.Add("x-ms-type");
Diagnostics.LoggedHeaderNames.Add("x-ms-write");
Diagnostics.LoggedHeaderNames.Add("x-ms-copy-source-error-code");
Diagnostics.LoggedHeaderNames.Add("x-ms-copy-source-status-code");

Diagnostics.LoggedQueryParameters.Add("comp");
Diagnostics.LoggedQueryParameters.Add("maxresults");
Expand Down
50 changes: 50 additions & 0 deletions sdk/storage/Azure.Storage.Files.Shares/tests/FileClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1998,6 +1998,24 @@ await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
e => Assert.AreEqual("CannotVerifyCopySource", e.ErrorCode));
}

[RecordedTest]
[ServiceVersion(Min = ShareClientOptions.ServiceVersion.V2024_08_04)]
public async Task StartCopyAsync_SourceErrorAndStatusCode()
{
await using DisposingFile test = await SharesClientBuilder.GetTestFileAsync();
ShareFileClient file = test.File;

// Act
await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
file.StartCopyAsync(sourceUri: s_invalidUri),
e =>
{
Assert.IsTrue(e.Message.Contains("CopySourceStatusCode: 400"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorCode: InvalidQueryParameterValue"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorMessage: Value for one of the query parameters specified in the request URI is invalid."));
});
}

[RecordedTest]
public async Task StartCopyAsync_CopySourceFileCreatedOnError()
{
Expand Down Expand Up @@ -4387,6 +4405,38 @@ await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
e => Assert.AreEqual("CannotVerifyCopySource", e.ErrorCode));
}

[RecordedTest]
[ServiceVersion(Min = ShareClientOptions.ServiceVersion.V2024_08_04)]
public async Task UploadRangeFromUriAsync_SourceErrorAndStatusCode()
{
// Arrange
await using DisposingShare test = await GetTestShareAsync(shareName: GetNewShareName());
ShareClient share = test.Share;

ShareDirectoryClient directory = InstrumentClient(share.GetDirectoryClient(GetNewDirectoryName()));
await directory.CreateIfNotExistsAsync();

ShareFileClient sourceFile = InstrumentClient(directory.GetFileClient(GetNewFileName()));

ShareFileClient destFile = directory.GetFileClient(GetNewFileName());
await destFile.CreateAsync(maxSize: Constants.KB);

HttpRange range = new HttpRange(0, Constants.KB);

// Act
await TestHelper.AssertExpectedExceptionAsync<RequestFailedException>(
destFile.UploadRangeFromUriAsync(
sourceUri: destFile.Uri,
range: range,
sourceRange: range),
e =>
{
Assert.IsTrue(e.Message.Contains("CopySourceStatusCode: 401"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorCode: NoAuthenticationInformation"));
Assert.IsTrue(e.Message.Contains("CopySourceErrorMessage: Server failed to authenticate the request. Please refer to the information in the www-authenticate header."));
});
}

[RecordedTest]
[ServiceVersion(Min = ShareClientOptions.ServiceVersion.V2021_06_08)]
[TestCase(null)]
Expand Down