diff --git a/sdk/storage/azure-storage-blob/assets.json b/sdk/storage/azure-storage-blob/assets.json index e8388dbaa150..7aed1248ffa7 100644 --- a/sdk/storage/azure-storage-blob/assets.json +++ b/sdk/storage/azure-storage-blob/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "java", "TagPrefix": "java/storage/azure-storage-blob", - "Tag": "java/storage/azure-storage-blob_e4ae407bc2" + "Tag": "java/storage/azure-storage-blob_0c7e10b175" } diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobTestBase.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobTestBase.java index 51abad78209e..1af61ef87f7f 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobTestBase.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobTestBase.java @@ -22,6 +22,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.test.TestMode; import com.azure.core.test.TestProxyTestBase; +import com.azure.core.test.http.MockHttpResponse; import com.azure.core.test.models.CustomMatcher; import com.azure.core.test.models.TestProxySanitizer; import com.azure.core.test.models.TestProxySanitizerType; @@ -37,7 +38,6 @@ import com.azure.identity.ChainedTokenCredentialBuilder; import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.identity.EnvironmentCredentialBuilder; -import com.azure.json.JsonProviders; import com.azure.json.JsonSerializable; import com.azure.json.JsonWriter; import com.azure.storage.blob.models.BlobContainerItem; @@ -68,7 +68,6 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -879,6 +878,9 @@ protected String generateShareName() { return generateResourceName(entityNo++); } + private HttpPipeline dataPlanePipeline; + private HttpHeaders genericHeaders; + protected String createFileAndDirectoryWithoutFileShareDependency(byte[] data, String shareName) throws IOException { String accountName = ENVIRONMENT.getPrimaryAccount().getName(); @@ -886,11 +888,8 @@ protected String createFileAndDirectoryWithoutFileShareDependency(byte[] data, S BearerTokenAuthenticationPolicy credentialPolicyDataPlane = new BearerTokenAuthenticationPolicy( getTokenCredential(ENVIRONMENT.getTestMode()), Constants.STORAGE_SCOPE); - //create share through management plane - createFileShareWithoutDependency(shareName); - //setup headers that will be used in every request - HttpHeaders genericHeaders = new HttpHeaders().set(X_MS_VERSION, "2025-07-05") + genericHeaders = new HttpHeaders().set(X_MS_VERSION, "2025-07-05") .set(HttpHeaderName.ACCEPT, "application/xml") .set(HttpHeaderName.HOST, accountName + ".file.core.windows.net") .set(HttpHeaderName.CONTENT_LENGTH, "0") @@ -904,8 +903,15 @@ protected String createFileAndDirectoryWithoutFileShareDependency(byte[] data, S policies.add(new RequestIdPolicy()); // create data plane pipeline - HttpPipeline dataPlanePipeline - = new HttpPipelineBuilder().policies(policies.toArray(new HttpPipelinePolicy[0])).build(); + dataPlanePipeline = new HttpPipelineBuilder().policies(policies.toArray(new HttpPipelinePolicy[0])).build(); + + // create share through data plane pipeline + String shareUrl = String.format("https://%s.file.core.windows.net/%s?restype=share", accountName, shareName); + + HttpResponse shareCreateResponse + = dataPlanePipeline.send(new HttpRequest(HttpMethod.PUT, new URL(shareUrl), genericHeaders)).block(); + assertNotNull(shareCreateResponse); + assertEquals(201, shareCreateResponse.getStatusCode()); // create directory String directoryName = generateBlobName(); @@ -945,48 +951,14 @@ protected String createFileAndDirectoryWithoutFileShareDependency(byte[] data, S return fileUrl; } - protected void createFileShareWithoutDependency(String shareName) throws IOException { - String shareID = getFileShareID(shareName); - Body shareBody = new Body(); - shareBody.setId(shareID); - shareBody.setName(shareName); - shareBody.setType("Microsoft.Storage/storageAccounts/fileServices/shares"); - - ByteArrayOutputStream shareJson = new ByteArrayOutputStream(); - try (JsonWriter jsonWriter = JsonProviders.createWriter(shareJson)) { - shareBody.toJson(jsonWriter); - } - HttpResponse response - = getManagementPlanePipeline().send(new HttpRequest(HttpMethod.PUT, new URL(getFileShareUri(shareID)), - new HttpHeaders(), Flux.just(ByteBuffer.wrap(shareJson.toByteArray())))).block(); - assertNotNull(response); - assertEquals(201, response.getStatusCode()); - } - protected void deleteFileShareWithoutDependency(String shareName) throws IOException { - String shareID = getFileShareID(shareName); - HttpResponse response = getManagementPlanePipeline() - .send(new HttpRequest(HttpMethod.DELETE, new URL(getFileShareUri(shareID)), new HttpHeaders())) - .block(); - assertNotNull(response); - assertEquals(200, response.getStatusCode()); - } - - protected HttpPipeline getManagementPlanePipeline() { - BearerTokenAuthenticationPolicy credentialPolicyManagementPlane = new BearerTokenAuthenticationPolicy( - getTokenCredential(ENVIRONMENT.getTestMode()), "https://management.azure.com/.default"); - return new HttpPipelineBuilder().policies(credentialPolicyManagementPlane).build(); - } + String shareUrl = String.format("https://%s.file.core.windows.net/%s?restype=share", + ENVIRONMENT.getPrimaryAccount().getName(), shareName); - protected String getFileShareID(String shareName) { - return String.format( - "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Storage/storageAccounts/" - + "%s/fileServices/default/shares/%s", - SUBSCRIPTION_ID, RESOURCE_GROUP_NAME, ENVIRONMENT.getPrimaryAccount().getName(), shareName); - } - - protected String getFileShareUri(String fileShareID) { - return "https://management.azure.com" + fileShareID + "?api-version=2024-01-01"; + HttpResponse shareDeleteResponse + = dataPlanePipeline.send(new HttpRequest(HttpMethod.DELETE, new URL(shareUrl), genericHeaders)).block(); + assertNotNull(shareDeleteResponse); + assertEquals(202, shareDeleteResponse.getStatusCode()); } public static final class Body implements JsonSerializable { @@ -1073,7 +1045,7 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { } } - //todo: change the copy of this method in StorageCommonTestUtils to take in TestMode instead of interception manager + //todo isbr: change the copy of this method in StorageCommonTestUtils to take in TestMode instead of interception manager protected static TokenCredential getTokenCredential(TestMode testMode) { if (testMode == TestMode.RECORD) { return new DefaultAzureCredentialBuilder().build(); @@ -1109,4 +1081,172 @@ protected static TokenCredential getTokenCredential(TestMode testMode) { return new MockTokenCredential(); } } + + protected static final class ListBlobsWithTimeoutTestClient implements HttpClient { + private final boolean isAsync; + + ListBlobsWithTimeoutTestClient(Boolean isAsync) { + this.isAsync = isAsync; + } + + private HttpResponse response(HttpRequest request, String xml) { + HttpHeaders headers = new HttpHeaders().set(HttpHeaderName.CONTENT_TYPE, "application/xml"); + return new MockHttpResponse(request, 200, headers, xml.getBytes(StandardCharsets.UTF_8)); + } + + private String buildFirstRequest(Boolean useDelimiter) { + String delimiterString = useDelimiter ? "/" : ""; + + return "" + + "" + + "3" + delimiterString + "" + "" + "blob1" + + "" + "" + "blob2" + "" + "" + "blob3" + "" + + "" + "MARKER--" + ""; + } + + private String buildSecondRequest(Boolean useDelimiter) { + String delimiterString = useDelimiter ? "/" : ""; + + return "" + + "" + + "MARKER--" + "3" + delimiterString + "" + "" + + "blob4" + "" + "" + "blob5" + "" + "" + + "" + ""; + } + + @Override + public Mono send(HttpRequest request) { + String url = request.getUrl().toString(); + HttpResponse response; + int delay = isAsync ? 4 : 8; + + if (url.contains("?restype=container&comp=list&maxresults=")) { + // flat first request + response = response(request, buildFirstRequest(false)); + } else if (url.contains("?restype=container&comp=list&marker=")) { + // flat second request + response = response(request, buildSecondRequest(false)); + } else if (url.contains("?restype=container&comp=list&delimiter=/&maxresults=")) { + // hierarchy first request + response = response(request, buildFirstRequest(true)); + } else if (url.contains("?restype=container&comp=list&delimiter=/&marker=")) { + // hierarchy second request + response = response(request, buildSecondRequest(true)); + } else { + // fallback + return Mono.just(new MockHttpResponse(request, 404)); + } + + return Mono.delay(Duration.ofSeconds(delay)).then(Mono.just(response)); + } + } + + protected static final class FindBlobsWithTimeoutClient implements HttpClient { + private final boolean isAsync; + + FindBlobsWithTimeoutClient(Boolean isAsync) { + this.isAsync = isAsync; + } + + private HttpResponse response(HttpRequest request, String xml) { + HttpHeaders headers = new HttpHeaders().set(HttpHeaderName.CONTENT_TYPE, "application/xml"); + return new MockHttpResponse(request, 200, headers, xml.getBytes(StandardCharsets.UTF_8)); + } + + private String buildFirstRequest() { + return "" + + "" + + ""dummyKey"='dummyValue'" + "3" + + "" + "" + "blob1" + "foo" + "" + + "" + "" + "dummyKey" + "dummyValue" + "" + "" + + "" + "" + "" + "blob2" + "foo" + + "" + "" + "" + "dummyKey" + "dummyValue" + "" + + "" + "" + "" + "" + "blob3" + + "foo" + "" + "" + "" + "dummyKey" + + "dummyValue" + "" + "" + "" + "" + "" + + "MARKER-" + ""; + } + + private String buildSecondRequest() { + return "" + + "" + + "MARKER-" + ""dummyKey"='dummyValue'" + + "3" + "" + "" + "blob4" + + "foo" + "" + "" + "" + "dummyKey" + + "dummyValue" + "" + "" + "" + "" + "" + + "blob5" + "foo" + "" + "" + "" + + "dummyKey" + "dummyValue" + "" + "" + "" + "" + + "" + "" + ""; + } + + @Override + public Mono send(HttpRequest request) { + String url = request.getUrl().toString(); + HttpResponse response; + int delay = isAsync ? 4 : 8; + + if (url.contains("marker")) { + // second request + response = response(request, buildSecondRequest()); + } else if (url.contains("?comp=blobs&where=%") || url.contains("?restype=container&comp=blobs&where=%")) { + // first request + response = response(request, buildFirstRequest()); + } else { + // fallback + return Mono.just(new MockHttpResponse(request, 404)); + } + + return Mono.delay(Duration.ofSeconds(delay)).then(Mono.just(response)); + } + } + + protected static final class ListContainersWithTimeoutTestClient implements HttpClient { + private final boolean isAsync; + + ListContainersWithTimeoutTestClient(Boolean isAsync) { + this.isAsync = isAsync; + } + + private HttpResponse response(HttpRequest request, String xml) { + HttpHeaders headers = new HttpHeaders().set(HttpHeaderName.CONTENT_TYPE, "application/xml"); + return new MockHttpResponse(request, 200, headers, xml.getBytes(StandardCharsets.UTF_8)); + } + + private String buildFirstRequest() { + return "" + + "" + + "3" + "" + "" + "container1" + + "" + "" + "container2" + "" + "" + + "container3" + "" + "" + + "/marker/marker" + ""; + } + + private String buildSecondRequest() { + return "" + + "" + + "/marker/marker" + "3" + "" + "" + + "container4" + "" + "" + "container5" + + "" + "" + "" + ""; + } + + @Override + public Mono send(HttpRequest request) { + String url = request.getUrl().toString(); + HttpResponse response; + int delay = isAsync ? 4 : 8; + + if (url.contains("?comp=list&maxresults=")) { + // flat first request + response = response(request, buildFirstRequest()); + } else if (url.contains("?comp=list&marker=")) { + // flat second request + response = response(request, buildSecondRequest()); + } else { + // fallback + return Mono.just(new MockHttpResponse(request, 404)); + } + + return Mono.delay(Duration.ofSeconds(delay)).then(Mono.just(response)); + } + } } diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerApiTests.java index 83828fdd7f8d..1f3ea9d5bf0f 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerApiTests.java @@ -1070,38 +1070,43 @@ public void listBlobsFlatError() { assertThrows(BlobStorageException.class, () -> cc.listBlobs().iterator().hasNext()); } + /* + * For listBlobsFlatWithTimeoutStillBackedByPagedStream and listBlobsHierWithTimeoutStillBackedByPagedStream: + * The custom http client returns a generic xml list of 5 blobs total. + * The api call should return 2 pages, one page of 3 blobs and one page of 2 blobs. + * Although each page is set to take 4 seconds to return, the timeout being set to 6 seconds should not cause the test to fail, + * as the timeout is only on the page request and not the entire stream of pages. + */ + @Test public void listBlobsFlatWithTimeoutStillBackedByPagedStream() { - int numBlobs = 5; - int pageResults = 3; - - for (int i = 0; i < numBlobs; i++) { - BlockBlobClient blob = cc.getBlobClient(generateBlobName()).getBlockBlobClient(); - blob.upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()); - } - - // when: "Consume results by page, then still have paging functionality" - assertDoesNotThrow( - () -> cc.listBlobs(new ListBlobsOptions().setMaxResultsPerPage(pageResults), Duration.ofSeconds(10)) + BlobContainerClient containerClient + = new BlobContainerClientBuilder().endpoint("https://account.blob.core.windows.net/") + .credential(new MockTokenCredential()) + .containerName("foo") + .httpClient(new ListBlobsWithTimeoutTestClient(false)) + .buildClient(); + + assertEquals(2, + containerClient.listBlobs(new ListBlobsOptions().setMaxResultsPerPage(3), Duration.ofSeconds(14)) .streamByPage() .count()); } @Test public void listBlobsHierWithTimeoutStillBackedByPagedStream() { - int numBlobs = 5; - int pageResults = 3; - - for (int i = 0; i < numBlobs; i++) { - BlockBlobClient blob = cc.getBlobClient(generateBlobName()).getBlockBlobClient(); - blob.upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()); - } - - // when: "Consume results by page, then still have paging functionality" - assertDoesNotThrow(() -> cc - .listBlobsByHierarchy("/", new ListBlobsOptions().setMaxResultsPerPage(pageResults), Duration.ofSeconds(10)) - .streamByPage() - .count()); + BlobContainerClient containerClient + = new BlobContainerClientBuilder().endpoint("https://account.blob.core.windows.net/") + .credential(new MockTokenCredential()) + .containerName("foo") + .httpClient(new ListBlobsWithTimeoutTestClient(false)) + .buildClient(); + + assertEquals(2, + containerClient + .listBlobsByHierarchy("/", new ListBlobsOptions().setMaxResultsPerPage(3), Duration.ofSeconds(14)) + .streamByPage() + .count()); } /* @@ -1656,26 +1661,28 @@ public void findBlobsError() { assertThrows(BlobStorageException.class, () -> cc.findBlobsByTags("garbageTag").streamByPage().count()); } - @SuppressWarnings("deprecation") + /* + * For findBlobsWithTimeoutStillBackedByPagedStream: + * The custom http client returns a generic xml list of 5 blobs total. + * The api call should return 2 pages, one page of 3 blobs and one page of 2 blobs. + * Although each page is set to take 4 seconds to return, the timeout being set to 6 seconds should not cause the test to fail, + * as the timeout is only on the page request and not the entire stream of pages. + */ + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2021-04-10") @Test public void findBlobsWithTimeoutStillBackedByPagedStream() { - int numBlobs = 5; - int pageResults = 3; - Map tags = Collections.singletonMap(tagKey, tagValue); - - for (int i = 0; i < numBlobs; i++) { - cc.getBlobClient(generateBlobName()) - .uploadWithResponse( - new BlobParallelUploadOptions(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()) - .setTags(tags), - null, null); - } + BlobContainerClient containerClient + = new BlobContainerClientBuilder().endpoint("https://account.blob.core.windows.net/") + .credential(new MockTokenCredential()) + .containerName("foo") + .httpClient(new FindBlobsWithTimeoutClient(false)) + .buildClient(); - // when: "Consume results by page, still have paging functionality" - assertDoesNotThrow(() -> cc.findBlobsByTags( - new FindBlobsOptions(String.format("\"%s\"='%s'", tagKey, tagValue)).setMaxResultsPerPage(pageResults), - Duration.ofSeconds(10), Context.NONE).streamByPage().count()); + assertEquals(2, + containerClient.findBlobsByTags( + new FindBlobsOptions(String.format("\"%s\"='%s'", "dummyKey", "dummyValue")).setMaxResultsPerPage(3), + Duration.ofSeconds(14), Context.NONE).streamByPage().count()); } @ParameterizedTest @@ -1760,6 +1767,7 @@ public void createURLSpecialCharsDecoded(String name) { } @Test + @PlaybackOnly public void rootExplicit() { cc = primaryBlobServiceClient.getBlobContainerClient(BlobContainerClient.ROOT_CONTAINER_NAME); // create root container if not exist. @@ -1771,6 +1779,7 @@ public void rootExplicit() { } @Test + @PlaybackOnly public void rootExplicitInEndpoint() { cc = primaryBlobServiceClient.getBlobContainerClient(BlobContainerClient.ROOT_CONTAINER_NAME); // create root container if not exist. @@ -1788,6 +1797,7 @@ public void rootExplicitInEndpoint() { } @Test + @PlaybackOnly public void blobClientBuilderRootImplicit() { cc = primaryBlobServiceClient.getBlobContainerClient(BlobContainerClient.ROOT_CONTAINER_NAME); // createroot container if not exist. @@ -1810,6 +1820,7 @@ public void blobClientBuilderRootImplicit() { } @Test + @PlaybackOnly public void containerClientBuilderRootImplicit() { cc = primaryBlobServiceClient.getBlobContainerClient(BlobContainerClient.ROOT_CONTAINER_NAME); // create root container if not exist. @@ -1832,10 +1843,10 @@ public void containerClientBuilderRootImplicit() { @Test public void serviceClientImplicitRoot() { - assertEquals(primaryBlobServiceClient.getBlobContainerClient(null).getBlobContainerName(), - BlobContainerAsyncClient.ROOT_CONTAINER_NAME); - assertEquals(primaryBlobServiceClient.getBlobContainerClient("").getBlobContainerName(), - BlobContainerAsyncClient.ROOT_CONTAINER_NAME); + assertEquals(BlobContainerAsyncClient.ROOT_CONTAINER_NAME, + primaryBlobServiceClient.getBlobContainerClient(null).getBlobContainerName()); + assertEquals(BlobContainerAsyncClient.ROOT_CONTAINER_NAME, + primaryBlobServiceClient.getBlobContainerClient("").getBlobContainerName()); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerAsyncApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerAsyncApiTests.java index 3bb26f5c4741..fee4ef24df05 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerAsyncApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerAsyncApiTests.java @@ -1122,38 +1122,46 @@ public void listBlobsFlatError() { StepVerifier.create(ccAsync.listBlobs()).verifyError(BlobStorageException.class); } + /* + * For listBlobsFlatWithTimeoutStillBackedByPagedFlux and listBlobsHierWithTimeoutStillBackedByPagedFlux: + * The custom http client returns a generic xml list of 5 blobs total. + * The api call should return 2 pages, one page of 3 blobs and one page of 2 blobs. + * Although each page is set to take 4 seconds to return, the timeout being set to 6 seconds should not cause the test to fail, + * as the timeout is only on the page request and not the entire stream of pages. + */ @Test public void listBlobsFlatWithTimeoutStillBackedByPagedFlux() { - int numBlobs = 5; - int pageResults = 3; - - Mono> createBlob = Flux.range(0, numBlobs).flatMap(i -> { - BlockBlobAsyncClient blob = ccAsync.getBlobAsyncClient(generateBlobName()).getBlockBlobAsyncClient(); - return blob.upload(DATA.getDefaultFlux(), DATA.getDefaultDataSize()); - }).collectList(); + BlobContainerAsyncClient containerClient + = new BlobContainerClientBuilder().endpoint("https://account.blob.core.windows.net/") + .credential(new MockTokenCredential()) + .containerName("foo") + .httpClient(new ListBlobsWithTimeoutTestClient(true)) + .buildAsyncClient(); - // when: "Consume results by page, then still have paging functionality" StepVerifier - .create(createBlob - .thenMany(ccAsync.listBlobs(new ListBlobsOptions().setMaxResultsPerPage(pageResults)).byPage())) + .create( + containerClient + .listBlobsFlatWithOptionalTimeout(new ListBlobsOptions().setMaxResultsPerPage(3), null, + Duration.ofSeconds(6)) + .byPage()) .expectNextCount(2) .verifyComplete(); } @Test public void listBlobsHierWithTimeoutStillBackedByPagedFlux() { - int numBlobs = 5; - int pageResults = 3; - - Mono> createBlob = Flux.range(0, numBlobs).flatMap(i -> { - BlockBlobAsyncClient blob = ccAsync.getBlobAsyncClient(generateBlobName()).getBlockBlobAsyncClient(); - return blob.upload(DATA.getDefaultFlux(), DATA.getDefaultDataSize()); - }).collectList(); + BlobContainerAsyncClient containerClient + = new BlobContainerClientBuilder().endpoint("https://account.blob.core.windows.net/") + .credential(new MockTokenCredential()) + .containerName("foo") + .httpClient(new ListBlobsWithTimeoutTestClient(true)) + .buildAsyncClient(); - // when: "Consume results by page, then still have paging functionality" StepVerifier - .create(createBlob.thenMany( - ccAsync.listBlobsByHierarchy("/", new ListBlobsOptions().setMaxResultsPerPage(pageResults)).byPage())) + .create(containerClient + .listBlobsHierarchyWithOptionalTimeout("/", new ListBlobsOptions().setMaxResultsPerPage(3), + Duration.ofSeconds(6)) + .byPage()) .expectNextCount(2) .verifyComplete(); } @@ -1776,33 +1784,27 @@ public void findBlobsError() { StepVerifier.create(ccAsync.findBlobsByTags("garbageTag").byPage()).verifyError(BlobStorageException.class); } - @SuppressWarnings("deprecation") + /* + * For findBlobsWithTimeoutStillBackedByPagedFlux: + * The custom http client returns a generic xml list of 5 blobs total. + * The api call should return 2 pages, one page of 3 blobs and one page of 2 blobs. + * Although each page is set to take 4 seconds to return, the timeout being set to 6 seconds should not cause the test to fail, + * as the timeout is only on the page request and not the entire stream of pages. + */ + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2021-04-10") @Test public void findBlobsWithTimeoutStillBackedByPagedFlux() { - int numBlobs = 5; - int pageResults = 3; - Map tags = Collections.singletonMap(tagKey, tagValue); - - Mono>> uploadBlob = Flux.range(0, numBlobs) - .flatMap(i -> ccAsync.getBlobAsyncClient(generateBlobName()) - .uploadWithResponse( - new BlobParallelUploadOptions(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()) - .setTags(tags))) - .collectList(); + BlobContainerAsyncClient containerClient + = new BlobContainerClientBuilder().endpoint("https://account.blob.core.windows.net/") + .credential(new MockTokenCredential()) + .containerName("foo") + .httpClient(new FindBlobsWithTimeoutClient(true)) + .buildAsyncClient(); - // when: "Consume results by page, still have paging functionality" - StepVerifier - .create( - uploadBlob - .thenMany( - ccAsync - .findBlobsByTags(new FindBlobsOptions(String.format("\"%s\"='%s'", tagKey, tagValue)) - .setMaxResultsPerPage(pageResults), Duration.ofSeconds(10), Context.NONE) - .byPage() - .count())) - .expectNextCount(1) - .verifyComplete(); + StepVerifier.create(containerClient.findBlobsByTags( + new FindBlobsOptions(String.format("\"%s\"='%s'", "dummyKey", "dummyValue")).setMaxResultsPerPage(3), + Duration.ofSeconds(6), Context.NONE).byPage()).expectNextCount(2).verifyComplete(); } @ParameterizedTest @@ -1859,6 +1861,7 @@ public void createURLSpecialCharsEncoded(String name) { } @Test + @PlaybackOnly public void rootExplicit() { ccAsync = primaryBlobServiceAsyncClient.getBlobContainerAsyncClient(BlobContainerClient.ROOT_CONTAINER_NAME); // create root container if not exist. @@ -1874,6 +1877,7 @@ public void rootExplicit() { } @Test + @PlaybackOnly public void rootExplicitInEndpoint() { ccAsync = primaryBlobServiceAsyncClient.getBlobContainerAsyncClient(BlobContainerClient.ROOT_CONTAINER_NAME); // create root container if not exist. @@ -1893,6 +1897,7 @@ public void rootExplicitInEndpoint() { } @Test + @PlaybackOnly public void blobClientBuilderRootImplicit() { ccAsync = primaryBlobServiceAsyncClient.getBlobContainerAsyncClient(BlobContainerClient.ROOT_CONTAINER_NAME); // createroot container if not exist. @@ -1917,6 +1922,7 @@ public void blobClientBuilderRootImplicit() { } @Test + @PlaybackOnly public void containerClientBuilderRootImplicit() { ccAsync = primaryBlobServiceAsyncClient.getBlobContainerAsyncClient(BlobContainerClient.ROOT_CONTAINER_NAME); // create root container if not exist. @@ -1945,10 +1951,10 @@ public void containerClientBuilderRootImplicit() { @Test public void serviceClientImplicitRoot() { - assertEquals(primaryBlobServiceAsyncClient.getBlobContainerAsyncClient(null).getBlobContainerName(), - BlobContainerAsyncClient.ROOT_CONTAINER_NAME); - assertEquals(primaryBlobServiceAsyncClient.getBlobContainerAsyncClient("").getBlobContainerName(), - BlobContainerAsyncClient.ROOT_CONTAINER_NAME); + assertEquals(BlobContainerAsyncClient.ROOT_CONTAINER_NAME, + primaryBlobServiceAsyncClient.getBlobContainerAsyncClient(null).getBlobContainerName()); + assertEquals(BlobContainerAsyncClient.ROOT_CONTAINER_NAME, + primaryBlobServiceAsyncClient.getBlobContainerAsyncClient("").getBlobContainerName()); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceApiTests.java index bec72862df0c..8fda9fd9cbd0 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceApiTests.java @@ -9,6 +9,7 @@ import com.azure.core.http.rest.PagedResponse; import com.azure.core.http.rest.Response; import com.azure.core.test.http.NoOpHttpClient; +import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.util.Context; import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.storage.blob.models.BlobAnalyticsLogging; @@ -289,28 +290,28 @@ public void listContainersAnonymous() { assertThrows(IllegalStateException.class, () -> anonymousClient.listBlobContainers().iterator()); } + /* + * For listContainersWithTimeoutStillBackedByPagedStream: + * The custom http client returns a generic xml list of 5 blobs total. + * The api call should return 2 pages, one page of 3 blobs and one page of 2 blobs. + * Although each page is set to take 4 seconds to return, the timeout being set to 6 seconds should not cause the test to fail, + * as the timeout is only on the page request and not the entire stream of pages. + */ + @Test public void listContainersWithTimeoutStillBackedByPagedStream() { - int numContainers = 5; - int pageResults = 3; - - List containers = new ArrayList<>(); - for (int i = 0; i < numContainers; i++) { - containers.add(primaryBlobServiceClient.createBlobContainer(generateContainerName())); - } + BlobServiceClient serviceClient + = new BlobServiceClientBuilder().endpoint("https://account.blob.core.windows.net/") + .credential(new MockTokenCredential()) + .httpClient(new ListContainersWithTimeoutTestClient(false)) + .buildClient(); - // when: "Consume results by page, then should still have paging functionality"" - assertDoesNotThrow( - () -> primaryBlobServiceClient - .listBlobContainers(new ListBlobContainersOptions().setMaxResultsPerPage(pageResults), - Duration.ofSeconds(10)) + assertEquals(2, + serviceClient + .listBlobContainers(new ListBlobContainersOptions().setMaxResultsPerPage(3), Duration.ofSeconds(14)) .streamByPage() .count()); - // cleanup: - for (BlobContainerClient container : containers) { - container.delete(); - } } @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2020-10-02") @@ -525,30 +526,27 @@ public void findBlobsAnonymous() { } - @SuppressWarnings("deprecation") + /* + * For findBlobsWithTimeoutStillBackedByPagedStream: + * The custom http client returns a generic xml list of 5 blobs total. + * The api call should return 2 pages, one page of 3 blobs and one page of 2 blobs. + * Although each page is set to take 4 seconds to return, the timeout being set to 6 seconds should not cause the test to fail, + * as the timeout is only on the page request and not the entire stream of pages. + */ + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2019-12-12") @Test public void findBlobsWithTimeoutStillBackedByPagedStream() { - int numBlobs = 5; - int pageResults = 3; - BlobContainerClient cc = primaryBlobServiceClient.createBlobContainer(generateContainerName()); - Map tags = Collections.singletonMap(tagKey, tagValue); - - for (int i = 0; i < numBlobs; i++) { - cc.getBlobClient(generateBlobName()) - .uploadWithResponse( - new BlobParallelUploadOptions(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()) - .setTags(tags), - null, null); - } - - // when: "Consume results by page, then still have paging functionality" - assertDoesNotThrow(() -> primaryBlobServiceClient.findBlobsByTags( - new FindBlobsOptions(String.format("\"%s\"='%s'", tagKey, tagValue)).setMaxResultsPerPage(pageResults), - Duration.ofSeconds(10), Context.NONE).streamByPage().count()); + BlobServiceClient serviceClient + = new BlobServiceClientBuilder().endpoint("https://account.blob.core.windows.net/") + .credential(new MockTokenCredential()) + .httpClient(new FindBlobsWithTimeoutClient(false)) + .buildClient(); - // cleanup: - cc.delete(); + assertEquals(2, + serviceClient.findBlobsByTags( + new FindBlobsOptions(String.format("\"%s\"='%s'", "dummyKey", "dummyValue")).setMaxResultsPerPage(3), + Duration.ofSeconds(14), Context.NONE).streamByPage().count()); } private static void validatePropsSet(BlobServiceProperties sent, BlobServiceProperties received) { @@ -1021,8 +1019,8 @@ public void restoreContainerWithResponse() { sleepIfRunningAgainstService(30000); Response response = primaryBlobServiceClient.undeleteBlobContainerWithResponse( - new UndeleteBlobContainerOptions(blobContainerItem.getName(), blobContainerItem.getVersion()), - Duration.ofMinutes(1), Context.NONE); + new UndeleteBlobContainerOptions(blobContainerItem.getName(), blobContainerItem.getVersion()), null, + Context.NONE); BlobContainerClient restoredContainerClient = response.getValue(); assertNotNull(response); diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAsyncApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAsyncApiTests.java index 55d8bd119202..8c679a5d2753 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAsyncApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAsyncApiTests.java @@ -8,6 +8,7 @@ import com.azure.core.http.rest.Response; import com.azure.core.test.TestMode; import com.azure.core.test.http.NoOpHttpClient; +import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.util.Context; import com.azure.core.util.paging.ContinuablePage; import com.azure.identity.DefaultAzureCredentialBuilder; @@ -285,20 +286,29 @@ public void listContainersAnonymous() { StepVerifier.create(anonymousClient.listBlobContainers()).verifyError(IllegalStateException.class); } + /* + * For listContainersWithTimeoutStillBackedByPagedFlux: + * The custom http client returns a generic xml list of 5 blobs total. + * The api call should return 2 pages, one page of 3 blobs and one page of 2 blobs. + * Although each page is set to take 4 seconds to return, the timeout being set to 6 seconds should not cause the test to fail, + * as the timeout is only on the page request and not the entire stream of pages. + */ + @Test public void listContainersWithTimeoutStillBackedByPagedFlux() { - int numContainers = 5; - int pageResults = 3; - - Mono> containersMono = Flux.range(0, numContainers) - .flatMap(i -> primaryBlobServiceAsyncClient.createBlobContainer(generateContainerName())) - .collectList(); + BlobServiceAsyncClient serviceClient + = new BlobServiceClientBuilder().endpoint("https://account.blob.core.windows.net/") + .credential(new MockTokenCredential()) + .httpClient(new ListContainersWithTimeoutTestClient(true)) + .buildAsyncClient(); - StepVerifier.create(containersMono.flatMapMany(containers -> primaryBlobServiceAsyncClient - .listBlobContainersWithOptionalTimeout(new ListBlobContainersOptions().setMaxResultsPerPage(pageResults), - Duration.ofSeconds(10)) - .byPage() - .count())).expectNextCount(1).verifyComplete(); + StepVerifier + .create(serviceClient + .listBlobContainersWithOptionalTimeout(new ListBlobContainersOptions().setMaxResultsPerPage(3), + Duration.ofSeconds(6)) + .byPage()) + .expectNextCount(2) + .verifyComplete(); } @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2020-10-02") @@ -500,28 +510,26 @@ public void findBlobsAnonymous() { StepVerifier.create(anonymousClient.findBlobsByTags("foo=bar")).verifyError(IllegalStateException.class); } - @SuppressWarnings("deprecation") + /* + * For findBlobsWithTimeoutStillBackedByPagedFlux: + * The custom http client returns a generic xml list of 5 blobs total. + * The api call should return 2 pages, one page of 3 blobs and one page of 2 blobs. + * Although each page is set to take 4 seconds to return, the timeout being set to 6 seconds should not cause the test to fail, + * as the timeout is only on the page request and not the entire stream of pages. + */ + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2019-12-12") @Test public void findBlobsWithTimeoutStillBackedByPagedFlux() { - int numBlobs = 5; - int pageResults = 3; - Map tags = Collections.singletonMap(tagKey, tagValue); - - Mono response = primaryBlobServiceAsyncClient.createBlobContainer(generateContainerName()).flatMap(cc -> { - Flux> upload = Flux.range(0, numBlobs) - .flatMap(i -> cc.getBlobAsyncClient(generateBlobName()) - .uploadWithResponse( - new BlobParallelUploadOptions(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()) - .setTags(tags))); - // when: "Consume results by page, then still have paging functionality" - return upload.then(primaryBlobServiceAsyncClient.findBlobsByTags( - new FindBlobsOptions(String.format("\"%s\"='%s'", tagKey, tagValue)).setMaxResultsPerPage(pageResults)) - .byPage() - .count()); - }); + BlobServiceAsyncClient serviceClient + = new BlobServiceClientBuilder().endpoint("https://account.blob.core.windows.net/") + .credential(new MockTokenCredential()) + .httpClient(new FindBlobsWithTimeoutClient(true)) + .buildAsyncClient(); - StepVerifier.create(response).expectNextCount(1).verifyComplete(); + StepVerifier.create(serviceClient.findBlobsByTags( + new FindBlobsOptions(String.format("\"%s\"='%s'", "dummyKey", "dummyValue")).setMaxResultsPerPage(3), + Duration.ofSeconds(6), Context.NONE).byPage()).expectNextCount(2).verifyComplete(); } private static void validatePropsSet(BlobServiceProperties sent, BlobServiceProperties received) { diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobApiTests.java index 7a00b9697dbc..7a4d162c7d85 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobApiTests.java @@ -1306,42 +1306,6 @@ private static Stream bufferedUploadSyncHandlePathingWithTransientFai return Stream.of(Arguments.of(11110, 0), Arguments.of(2 * Constants.MB + 11, 2)); } - /* - def "Upload NRF progress"() { - setup: - def data = getRandomData(BlockBlobURL.MAX_UPLOAD_BLOB_BYTES + 1) - def numBlocks = data.remaining() / BlockBlobURL.MAX_STAGE_BLOCK_BYTES - long prevCount = 0 - def mockReceiver = Mock(IProgressReceiver) - - - when: - TransferManager.uploadFromNonReplayableFlowable(Flowable.just(data), bu, BlockBlobURL.MAX_STAGE_BLOCK_BYTES, 10, - new TransferManagerUploadToBlockBlobOptions(mockReceiver, null, null, null, 20)).blockingGet() - data.position(0) - - then: - // We should receive exactly one notification of the completed progress. - 1 * mockReceiver.reportProgress(data.remaining()) */ - - /* - We should receive at least one notification reporting an intermediary value per block, but possibly more - notifications will be received depending on the implementation. We specify numBlocks - 1 because the last block - will be the total size as above. Finally, we assert that the number reported monotonically increases. - */ - /*(numBlocks - 1.._) * mockReceiver.reportProgress(!data.remaining()) >> { long bytesTransferred -> - if (!(bytesTransferred > prevCount)) { - throw new IllegalArgumentException("Reported progress should monotonically increase") - } else { - prevCount = bytesTransferred - } - } - - // We should receive no notifications that report more progress than the size of the file. - 0 * mockReceiver.reportProgress({ it > data.remaining() }) - notThrown(IllegalArgumentException) - }*/ - @LiveOnly @Test public void bufferedUploadOverwrite() throws IOException { @@ -1581,7 +1545,7 @@ public void uploadFromUrlSourceRequestConditions(BlobRequestConditions requestCo private static Stream uploadFromUrlSourceRequestConditionsSupplier() { return Stream.of( Arguments.of(new BlobRequestConditions().setIfMatch("dummy"), BlobErrorCode.SOURCE_CONDITION_NOT_MET), - Arguments.of(new BlobRequestConditions().setIfModifiedSince(OffsetDateTime.now().plusSeconds(20)), + Arguments.of(new BlobRequestConditions().setIfModifiedSince(OffsetDateTime.now().plusDays(10)), BlobErrorCode.CANNOT_VERIFY_COPY_SOURCE), Arguments.of(new BlobRequestConditions().setIfUnmodifiedSince(OffsetDateTime.now().minusDays(1)), BlobErrorCode.CANNOT_VERIFY_COPY_SOURCE)); diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobAsyncApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobAsyncApiTests.java index d35e0afad279..eeec87686794 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobAsyncApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobAsyncApiTests.java @@ -2531,10 +2531,12 @@ public void uploadFromUrlSourceRequestConditions(BlobRequestConditions requestCo Mono> response = sourceBlob.upload(DATA.getDefaultFlux(), null).flatMap(r -> { String sas = sourceBlob.generateSas(new BlobServiceSasSignatureValues(testResourceNamer.now().plusDays(1), new BlobContainerSasPermission().setReadPermission(true))); - BlobUploadFromUrlOptions options = new BlobUploadFromUrlOptions(sourceBlob.getBlobUrl() + "?" + sas) - .setSourceRequestConditions(requestConditions); - return blockBlobAsyncClient.upload(Flux.just(ByteBuffer.wrap(new byte[0])), 0, true) - .then(blockBlobAsyncClient.uploadFromUrlWithResponse(options)); + + return blockBlobAsyncClient.upload(Flux.just(ByteBuffer.wrap(new byte[0])), 0, true).flatMap(r2 -> { + BlobUploadFromUrlOptions options = new BlobUploadFromUrlOptions(sourceBlob.getBlobUrl() + "?" + sas) + .setSourceRequestConditions(requestConditions); + return blockBlobAsyncClient.uploadFromUrlWithResponse(options); + }); }); StepVerifier.create(response).verifyErrorSatisfies(r -> { @@ -2546,7 +2548,7 @@ public void uploadFromUrlSourceRequestConditions(BlobRequestConditions requestCo private static Stream uploadFromUrlSourceRequestConditionsSupplier() { return Stream.of( Arguments.of(new BlobRequestConditions().setIfMatch("dummy"), BlobErrorCode.SOURCE_CONDITION_NOT_MET), - Arguments.of(new BlobRequestConditions().setIfModifiedSince(OffsetDateTime.now().plusSeconds(10)), + Arguments.of(new BlobRequestConditions().setIfModifiedSince(OffsetDateTime.now().plusDays(10)), BlobErrorCode.CANNOT_VERIFY_COPY_SOURCE), Arguments.of(new BlobRequestConditions().setIfUnmodifiedSince(OffsetDateTime.now().minusDays(1)), BlobErrorCode.CANNOT_VERIFY_COPY_SOURCE)); diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/HttpFaultInjectingTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/HttpFaultInjectingTests.java index 565a9b7e44e2..11c9afd38598 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/HttpFaultInjectingTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/HttpFaultInjectingTests.java @@ -6,8 +6,6 @@ import com.azure.core.http.HttpClient; import com.azure.core.http.HttpClientProvider; import com.azure.core.http.HttpHeaderName; -import com.azure.core.http.HttpRequest; -import com.azure.core.http.HttpResponse; import com.azure.core.http.netty.NettyAsyncHttpClientProvider; import com.azure.core.http.okhttp.OkHttpAsyncClientProvider; import com.azure.core.test.TestMode; @@ -17,7 +15,6 @@ import com.azure.core.util.CoreUtils; import com.azure.core.util.HttpClientOptions; import com.azure.core.util.SharedExecutorService; -import com.azure.core.util.UrlBuilder; import com.azure.core.util.logging.ClientLogger; import com.azure.storage.blob.BlobClient; import com.azure.storage.blob.BlobClientBuilder; @@ -33,12 +30,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIf; -import reactor.core.publisher.Mono; import java.io.File; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.StandardOpenOption; @@ -56,6 +50,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import static com.azure.core.test.utils.TestUtils.getFaultInjectingHttpClient; import static com.azure.storage.blob.BlobTestBase.ENVIRONMENT; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -112,7 +107,7 @@ public void downloadToFileWithFaultInjection() throws IOException, InterruptedEx .containerName(containerClient.getBlobContainerName()) .blobName(containerClient.getBlobContainerName()) .credential(ENVIRONMENT.getPrimaryAccount().getCredential()) - .httpClient(new HttpFaultInjectingHttpClient(getFaultInjectingWrappedHttpClient())) + .httpClient(getFaultInjectingHttpClient(getFaultInjectingWrappedHttpClient(), false)) .retryOptions(new RequestRetryOptions(RetryPolicyType.FIXED, 4, null, 10L, 10L, null)) .buildClient(); @@ -155,7 +150,8 @@ public void downloadToFileWithFaultInjection() throws IOException, InterruptedEx countDownLatch.await(10, TimeUnit.MINUTES); - assertTrue(successCount.get() >= 450); + assertTrue(successCount.get() >= 450, + () -> "Expected over 450 successes, actual success count was: " + successCount.get()); // cleanup files.forEach(it -> { try { @@ -170,8 +166,8 @@ public void downloadToFileWithFaultInjection() throws IOException, InterruptedEx private HttpClient getFaultInjectingWrappedHttpClient() { switch (ENVIRONMENT.getHttpClientType()) { case NETTY: - return HttpClient.createDefault(new HttpClientOptions().readTimeout(Duration.ofSeconds(2)) - .responseTimeout(Duration.ofSeconds(2)) + return HttpClient.createDefault(new HttpClientOptions().readTimeout(Duration.ofSeconds(5)) + .responseTimeout(Duration.ofSeconds(5)) .setHttpClientProvider(NettyAsyncHttpClientProvider.class)); case OK_HTTP: @@ -216,95 +212,6 @@ private static Class getVertxClientProviderReflect return (Class) clazz; } - // For now a local implementation is here in azure-storage-blob until this is released in azure-core-test. - // Since this is a local definition with a clear set of configurations everything is simplified. - private static final class HttpFaultInjectingHttpClient implements HttpClient { - private final HttpClient wrappedHttpClient; - - HttpFaultInjectingHttpClient(HttpClient wrappedHttpClient) { - this.wrappedHttpClient = wrappedHttpClient; - } - - @Override - public Mono send(HttpRequest request) { - return send(request, Context.NONE); - } - - @Override - public Mono send(HttpRequest request, Context context) { - URL originalUrl = request.getUrl(); - request.setHeader(UPSTREAM_URI_HEADER, originalUrl.toString()).setUrl(rewriteUrl(originalUrl)); - String faultType = faultInjectorHandling(); - request.setHeader(HTTP_FAULT_INJECTOR_RESPONSE_HEADER, faultType); - - return wrappedHttpClient.send(request, context).map(response -> { - HttpRequest request1 = response.getRequest(); - request1.getHeaders().remove(UPSTREAM_URI_HEADER); - request1.setUrl(originalUrl); - - return response; - }); - } - - @Override - public HttpResponse sendSync(HttpRequest request, Context context) { - URL originalUrl = request.getUrl(); - request.setHeader(UPSTREAM_URI_HEADER, originalUrl.toString()).setUrl(rewriteUrl(originalUrl)); - String faultType = faultInjectorHandling(); - request.setHeader(HTTP_FAULT_INJECTOR_RESPONSE_HEADER, faultType); - - HttpResponse response = wrappedHttpClient.sendSync(request, context); - response.getRequest().setUrl(originalUrl); - response.getRequest().getHeaders().remove(UPSTREAM_URI_HEADER); - - return response; - } - - private static URL rewriteUrl(URL originalUrl) { - try { - return UrlBuilder.parse(originalUrl).setScheme("http").setHost("localhost").setPort(7777).toUrl(); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - } - - private static String faultInjectorHandling() { - // f: Full response - // p: Partial Response (full headers, 50% of body), then wait indefinitely - // pc: Partial Response (full headers, 50% of body), then close (TCP FIN) - // pa: Partial Response (full headers, 50% of body), then abort (TCP RST) - // pn: Partial Response (full headers, 50% of body), then finish normally - // n: No response, then wait indefinitely - // nc: No response, then close (TCP FIN) - // na: No response, then abort (TCP RST) - double random = ThreadLocalRandom.current().nextDouble(); - int choice = (int) (random * 100); - - if (choice >= 25) { - // 75% of requests complete without error. - return "f"; - } else if (choice >= 1) { - if (random <= 0.34D) { - return "n"; - } else if (random <= 0.67D) { - return "nc"; - } else { - return "na"; - } - } else { - if (random <= 0.25D) { - return "p"; - } else if (random <= 0.50D) { - return "pc"; - } else if (random <= 0.75D) { - return "pa"; - } else { - return "pn"; - } - } - } - } - private static boolean shouldRun() { String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT);