Skip to content

Commit e747987

Browse files
mgmt core, strict check on URL, but relax in LRO response processing (Azure#22750)
1 parent 1de5ac2 commit e747987

File tree

4 files changed

+30
-34
lines changed

4 files changed

+30
-34
lines changed

sdk/core/azure-core-management/src/main/java/com/azure/core/management/implementation/polling/PollingState.java

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -421,14 +421,7 @@ private PollingState initializeDataFor200StatusCode(HttpHeaders lroResponseHeade
421421
if (this.isPutOrPatchLro()) {
422422
String value = ProvisioningStateData.tryParseProvisioningState(lroResponseBody, this.serializerAdapter);
423423
if (value != null && !ProvisioningState.SUCCEEDED.equalsIgnoreCase(value)) {
424-
final URL azAsyncOpUrl;
425-
try {
426-
azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER);
427-
} catch (Util.MalformedUrlException mue) {
428-
return this.setData(new SynchronouslyFailedLroData(
429-
"Response with status code 200 contains a malformed Azure-AsyncOperation header",
430-
200, lroResponseHeaders.toMap(), lroResponseBody));
431-
}
424+
final URL azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER, true);
432425
if (azAsyncOpUrl == null) {
433426
return this.setData(new ProvisioningStateData(this.lroOperationUri, value));
434427
} else {
@@ -454,14 +447,7 @@ private PollingState initializeDataFor200StatusCode(HttpHeaders lroResponseHeade
454447
private PollingState initializeDataFor201StatusCode(HttpHeaders lroResponseHeaders,
455448
String lroResponseBody) {
456449
assertStatusCode(201);
457-
final URL azAsyncOpUrl;
458-
try {
459-
azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER);
460-
} catch (Util.MalformedUrlException mue) {
461-
return this.setData(new SynchronouslyFailedLroData(
462-
"Response with status code 201 contains a malformed Azure-AsyncOperation header",
463-
201, lroResponseHeaders.toMap(), lroResponseBody));
464-
}
450+
final URL azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER, true);
465451
final URL locationUrl = Util.getLocationUrl(lroResponseHeaders, LOGGER, true);
466452
if (azAsyncOpUrl != null) {
467453
if (this.isPostOrDeleteLro()) {
@@ -507,14 +493,7 @@ private PollingState initializeDataFor201StatusCode(HttpHeaders lroResponseHeade
507493
private PollingState initializeDataFor202StatusCode(HttpHeaders lroResponseHeaders,
508494
String lroResponseBody) {
509495
assertStatusCode(202);
510-
final URL azAsyncOpUrl;
511-
try {
512-
azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER);
513-
} catch (Util.MalformedUrlException mue) {
514-
return this.setData(new SynchronouslyFailedLroData(
515-
"Response with status code 202 contains a malformed Azure-AsyncOperation header",
516-
202, lroResponseHeaders.toMap(), lroResponseBody));
517-
}
496+
final URL azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER, true);
518497
final URL locationUrl = Util.getLocationUrl(lroResponseHeaders, LOGGER, true);
519498
if (azAsyncOpUrl != null) {
520499
return this.setData(new AzureAsyncOperationData(this.lroRequestMethod,

sdk/core/azure-core-management/src/main/java/com/azure/core/management/implementation/polling/Util.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import com.azure.core.util.logging.ClientLogger;
88

99
import java.net.MalformedURLException;
10+
import java.net.URI;
11+
import java.net.URISyntaxException;
1012
import java.net.URL;
1113

1214
/**
@@ -37,6 +39,18 @@ static URL getAzureAsyncOperationUrl(HttpHeaders headers, ClientLogger logger) {
3739
return getUrl("Azure-AsyncOperation", headers, logger, false);
3840
}
3941

42+
/**
43+
* Gets value of Azure-AsyncOperation header from the given Http headers.
44+
*
45+
* @param headers the Http headers
46+
* @param logger the logger
47+
* @param ignoreException whether to ignore malformed URL
48+
* @return the Azure-AsyncOperation header value if exists, null otherwise
49+
*/
50+
static URL getAzureAsyncOperationUrl(HttpHeaders headers, ClientLogger logger, boolean ignoreException) {
51+
return getUrl("Azure-AsyncOperation", headers, logger, ignoreException);
52+
}
53+
4054
/**
4155
* Gets value of Location header from the given Http headers.
4256
*
@@ -53,7 +67,7 @@ static URL getLocationUrl(HttpHeaders headers, ClientLogger logger) {
5367
*
5468
* @param headers the Http headers
5569
* @param logger the logger
56-
* @return the Location header value if exists, null otherwise
70+
* @param ignoreException whether to ignore malformed URL
5771
* @return the URL value of the given header, null if header does not exists.
5872
*/
5973
static URL getLocationUrl(HttpHeaders headers, ClientLogger logger, boolean ignoreException) {
@@ -73,8 +87,8 @@ private static URL getUrl(String urlHeaderName, HttpHeaders headers, ClientLogge
7387
String value = headers.getValue(urlHeaderName);
7488
if (value != null) {
7589
try {
76-
return new URL(value);
77-
} catch (MalformedURLException me) {
90+
return new URI(value).toURL();
91+
} catch (MalformedURLException | URISyntaxException | IllegalArgumentException me) {
7892
String message = "Malformed value '" + value + "' for URL header: '" + urlHeaderName + "'.";
7993
if (ignoreException) {
8094
logger.logExceptionAsError(new MalformedUrlException(message, me));

sdk/core/azure-core-management/src/test/java/com/azure/core/management/implementation/polling/LROPollerTests.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ public String getName() {
342342
@Test
343343
public void lro200SucceededNoPoll() {
344344
final String resourceEndpoint = "/resource/1";
345-
final String sampleVaultUpdateSucceededResponse = "{\"id\":\"/subscriptions/###/resourceGroups/rg-weidxu/providers/Microsoft.KeyVault/vaults/v1weidxu\",\"name\":\"v1weidxu\",\"type\":\"Microsoft.KeyVault/vaults\",\"location\":\"centralus\",\"tags\":{},\"properties\":{\"sku\":{\"family\":\"A\",\"name\":\"standard\"},\"tenantId\":\"###\",\"accessPolicies\":[],\"enabledForDeployment\":false,\"vaultUri\":\"https://v1weidxu.vault.azure.net/\",\"provisioningState\":\"Succeeded\"}}";
345+
final String sampleVaultUpdateSucceededResponse = "{\"id\":\"/subscriptions/000/resourceGroups/rg-weidxu/providers/Microsoft.KeyVault/vaults/v1weidxu\",\"name\":\"v1weidxu\",\"type\":\"Microsoft.KeyVault/vaults\",\"location\":\"centralus\",\"tags\":{},\"properties\":{\"sku\":{\"family\":\"A\",\"name\":\"standard\"},\"tenantId\":\"000\",\"accessPolicies\":[],\"enabledForDeployment\":false,\"vaultUri\":\"https://v1weidxu.vault.azure.net/\",\"provisioningState\":\"Succeeded\"}}";
346346
ResponseTransformer provisioningStateLroService = new ResponseTransformer() {
347347
@Override
348348
public com.github.tomakehurst.wiremock.http.Response transform(Request request,
@@ -416,7 +416,7 @@ public String getName() {
416416
@Test
417417
public void lro201AsyncOperationSucceededNoPoll() {
418418
final String resourceEndpoint = "/resource/1";
419-
final String sampleNicCreateSucceededResponse = "{\"name\":\"nic4159682782\",\"id\":\"/subscriptions/###/resourceGroups/javanwmrg59122/providers/Microsoft.Network/networkInterfaces/nic4159682782\",\"etag\":\"W/\\\"92581fdf-b55d-4ca1-a1fa-9de0cf117b4f\\\"\",\"location\":\"eastus\",\"tags\":{},\"properties\":{\"provisioningState\":\"Succeeded\",\"resourceGuid\":\"e0a8ecd1-faa0-468c-8e30-411ca27417a1\",\"ipConfigurations\":[{\"name\":\"primary\",\"id\":\"/subscriptions/ec0aa5f7-9e78-40c9-85cd-535c6305b380/resourceGroups/javanwmrg59122/providers/Microsoft.Network/networkInterfaces/nic4159682782/ipConfigurations/primary\",\"etag\":\"W/\\\"92581fdf-b55d-4ca1-a1fa-9de0cf117b4f\\\"\",\"type\":\"Microsoft.Network/networkInterfaces/ipConfigurations\",\"properties\":{\"provisioningState\":\"Succeeded\",\"privateIPAddress\":\"10.0.0.6\",\"privateIPAllocationMethod\":\"Dynamic\",\"subnet\":{\"id\":\"/subscriptions/ec0aa5f7-9e78-40c9-85cd-535c6305b380/resourceGroups/javanwmrg59122/providers/Microsoft.Network/virtualNetworks/neta3e8953331/subnets/subnet1\"},\"primary\":true,\"privateIPAddressVersion\":\"IPv4\"}}],\"dnsSettings\":{\"dnsServers\":[],\"appliedDnsServers\":[],\"internalDomainNameSuffix\":\"a4vv4vgg2cluvfhfgw43jtn2aa.bx.internal.cloudapp.net\"},\"enableAcceleratedNetworking\":false,\"enableIPForwarding\":false,\"hostedWorkloads\":[],\"tapConfigurations\":[]},\"type\":\"Microsoft.Network/networkInterfaces\"}";
419+
final String sampleNicCreateSucceededResponse = "{\"name\":\"nic4159682782\",\"id\":\"/subscriptions/000/resourceGroups/javanwmrg59122/providers/Microsoft.Network/networkInterfaces/nic4159682782\",\"etag\":\"W/\\\"92581fdf-b55d-4ca1-a1fa-9de0cf117b4f\\\"\",\"location\":\"eastus\",\"tags\":{},\"properties\":{\"provisioningState\":\"Succeeded\",\"resourceGuid\":\"e0a8ecd1-faa0-468c-8e30-411ca27417a1\",\"ipConfigurations\":[{\"name\":\"primary\",\"id\":\"/subscriptions/ec0aa5f7-9e78-40c9-85cd-535c6305b380/resourceGroups/javanwmrg59122/providers/Microsoft.Network/networkInterfaces/nic4159682782/ipConfigurations/primary\",\"etag\":\"W/\\\"92581fdf-b55d-4ca1-a1fa-9de0cf117b4f\\\"\",\"type\":\"Microsoft.Network/networkInterfaces/ipConfigurations\",\"properties\":{\"provisioningState\":\"Succeeded\",\"privateIPAddress\":\"10.0.0.6\",\"privateIPAllocationMethod\":\"Dynamic\",\"subnet\":{\"id\":\"/subscriptions/ec0aa5f7-9e78-40c9-85cd-535c6305b380/resourceGroups/javanwmrg59122/providers/Microsoft.Network/virtualNetworks/neta3e8953331/subnets/subnet1\"},\"primary\":true,\"privateIPAddressVersion\":\"IPv4\"}}],\"dnsSettings\":{\"dnsServers\":[],\"appliedDnsServers\":[],\"internalDomainNameSuffix\":\"a4vv4vgg2cluvfhfgw43jtn2aa.bx.internal.cloudapp.net\"},\"enableAcceleratedNetworking\":false,\"enableIPForwarding\":false,\"hostedWorkloads\":[],\"tapConfigurations\":[]},\"type\":\"Microsoft.Network/networkInterfaces\"}";
420420
ResponseTransformer provisioningStateLroService = new ResponseTransformer() {
421421
@Override
422422
public com.github.tomakehurst.wiremock.http.Response transform(Request request,
@@ -434,7 +434,7 @@ public com.github.tomakehurst.wiremock.http.Response transform(Request request,
434434
// 201 response with provisioningState=Succeeded.
435435
return new com.github.tomakehurst.wiremock.http.Response.Builder()
436436
.headers(new HttpHeaders(
437-
new HttpHeader("Azure-AsyncOperation", "https://management.azure.com/subscriptions/###/providers/Microsoft.Network/locations/eastus/operations/###")))
437+
new HttpHeader("Azure-AsyncOperation", "https://management.azure.com/subscriptions/000/providers/Microsoft.Network/locations/eastus/operations/123")))
438438
.body(sampleNicCreateSucceededResponse)
439439
.status(201)
440440
.build();
@@ -492,7 +492,7 @@ public String getName() {
492492
@Test
493493
public void lro201SucceededNoPoll() {
494494
final String resourceEndpoint = "/resource/1";
495-
final String sampleSearchServiceCreateSucceededResponse = "{\"id\":\"/subscriptions/###/resourceGroups/rg86829b7a87d74/providers/Microsoft.Search/searchServices/ss3edfb54d\",\"name\":\"ss3edfb54d\",\"type\":\"Microsoft.Search/searchServices\",\"location\":\"West US\",\"properties\":{\"replicaCount\":1,\"partitionCount\":1,\"status\":\"running\",\"statusDetails\":\"\",\"provisioningState\":\"succeeded\",\"hostingMode\":\"Default\",\"publicNetworkAccess\":\"Enabled\",\"networkRuleSet\":{\"ipRules\":[],\"bypass\":\"None\"},\"privateEndpointConnections\":[],\"sharedPrivateLinkResources\":[]},\"sku\":{\"name\":\"free\"}}";
495+
final String sampleSearchServiceCreateSucceededResponse = "{\"id\":\"/subscriptions/000/resourceGroups/rg86829b7a87d74/providers/Microsoft.Search/searchServices/ss3edfb54d\",\"name\":\"ss3edfb54d\",\"type\":\"Microsoft.Search/searchServices\",\"location\":\"West US\",\"properties\":{\"replicaCount\":1,\"partitionCount\":1,\"status\":\"running\",\"statusDetails\":\"\",\"provisioningState\":\"succeeded\",\"hostingMode\":\"Default\",\"publicNetworkAccess\":\"Enabled\",\"networkRuleSet\":{\"ipRules\":[],\"bypass\":\"None\"},\"privateEndpointConnections\":[],\"sharedPrivateLinkResources\":[]},\"sku\":{\"name\":\"free\"}}";
496496
ResponseTransformer provisioningStateLroService = new ResponseTransformer() {
497497
@Override
498498
public com.github.tomakehurst.wiremock.http.Response transform(Request request,
@@ -510,7 +510,7 @@ public com.github.tomakehurst.wiremock.http.Response transform(Request request,
510510
// 201 response with provisioningState=Succeeded.
511511
return new com.github.tomakehurst.wiremock.http.Response.Builder()
512512
.headers(new HttpHeaders(
513-
new HttpHeader("Location", "https://management.azure.com/subscriptions/###/resourceGroups/rg86829b7a87d74/providers/Microsoft.Search/searchServices/ss3edfb54d")))
513+
new HttpHeader("Location", "https://management.azure.com/subscriptions/000/resourceGroups/rg86829b7a87d74/providers/Microsoft.Search/searchServices/ss3edfb54d")))
514514
.body(sampleSearchServiceCreateSucceededResponse)
515515
.status(201)
516516
.build();

sdk/core/azure-core-management/src/test/java/com/azure/core/management/implementation/polling/UtilTests.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ public class UtilTests {
1717

1818
@Test
1919
public void testGetURL() throws MalformedURLException {
20-
String asyncOpUrl = "https://management.azure.com/subscriptions/###/providers/Microsoft.Network/locations/eastus/operations/###";
21-
String locationUrl = "https://management.azure.com/subscriptions/###/resourceGroups/rg86829b7a87d74/providers/Microsoft.Search/searchServices/ss3edfb54d";
20+
String asyncOpUrl = "https://management.azure.com/subscriptions/000/providers/Microsoft.Network/locations/eastus/operations/123";
21+
String locationUrl = "https://management.azure.com/subscriptions/000/resourceGroups/rg86829b7a87d74/providers/Microsoft.Search/searchServices/ss3edfb54d";
2222

2323
HttpHeaders headers = new HttpHeaders();
2424
headers.set("Azure-AsyncOperation", asyncOpUrl);
@@ -34,6 +34,9 @@ public void testGetMalformedURL() {
3434
asyncOpHeaders.set("Azure-AsyncOperation", "invalidUrl");
3535
Assertions.assertThrows(Util.MalformedUrlException.class, () -> Util.getAzureAsyncOperationUrl(asyncOpHeaders, logger));
3636

37+
asyncOpHeaders.set("Azure-AsyncOperation", "https://management.azure.com/subscriptions/000/providers/Microsoft.Network/locations/east us/operations/123");
38+
Assertions.assertThrows(Util.MalformedUrlException.class, () -> Util.getAzureAsyncOperationUrl(asyncOpHeaders, logger));
39+
3740
// malformed URL in location will be ignored
3841
HttpHeaders locationHeaders = new HttpHeaders();
3942
locationHeaders.set("Location", "invalidUrl");

0 commit comments

Comments
 (0)