Skip to content

Commit

Permalink
mgmt core, strict check on URL, but relax in LRO response processing (A…
Browse files Browse the repository at this point in the history
  • Loading branch information
weidongxu-microsoft authored Jul 13, 2021
1 parent 1de5ac2 commit e747987
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -421,14 +421,7 @@ private PollingState initializeDataFor200StatusCode(HttpHeaders lroResponseHeade
if (this.isPutOrPatchLro()) {
String value = ProvisioningStateData.tryParseProvisioningState(lroResponseBody, this.serializerAdapter);
if (value != null && !ProvisioningState.SUCCEEDED.equalsIgnoreCase(value)) {
final URL azAsyncOpUrl;
try {
azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER);
} catch (Util.MalformedUrlException mue) {
return this.setData(new SynchronouslyFailedLroData(
"Response with status code 200 contains a malformed Azure-AsyncOperation header",
200, lroResponseHeaders.toMap(), lroResponseBody));
}
final URL azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER, true);
if (azAsyncOpUrl == null) {
return this.setData(new ProvisioningStateData(this.lroOperationUri, value));
} else {
Expand All @@ -454,14 +447,7 @@ private PollingState initializeDataFor200StatusCode(HttpHeaders lroResponseHeade
private PollingState initializeDataFor201StatusCode(HttpHeaders lroResponseHeaders,
String lroResponseBody) {
assertStatusCode(201);
final URL azAsyncOpUrl;
try {
azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER);
} catch (Util.MalformedUrlException mue) {
return this.setData(new SynchronouslyFailedLroData(
"Response with status code 201 contains a malformed Azure-AsyncOperation header",
201, lroResponseHeaders.toMap(), lroResponseBody));
}
final URL azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER, true);
final URL locationUrl = Util.getLocationUrl(lroResponseHeaders, LOGGER, true);
if (azAsyncOpUrl != null) {
if (this.isPostOrDeleteLro()) {
Expand Down Expand Up @@ -507,14 +493,7 @@ private PollingState initializeDataFor201StatusCode(HttpHeaders lroResponseHeade
private PollingState initializeDataFor202StatusCode(HttpHeaders lroResponseHeaders,
String lroResponseBody) {
assertStatusCode(202);
final URL azAsyncOpUrl;
try {
azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER);
} catch (Util.MalformedUrlException mue) {
return this.setData(new SynchronouslyFailedLroData(
"Response with status code 202 contains a malformed Azure-AsyncOperation header",
202, lroResponseHeaders.toMap(), lroResponseBody));
}
final URL azAsyncOpUrl = Util.getAzureAsyncOperationUrl(lroResponseHeaders, LOGGER, true);
final URL locationUrl = Util.getLocationUrl(lroResponseHeaders, LOGGER, true);
if (azAsyncOpUrl != null) {
return this.setData(new AzureAsyncOperationData(this.lroRequestMethod,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.azure.core.util.logging.ClientLogger;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

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

/**
* Gets value of Azure-AsyncOperation header from the given Http headers.
*
* @param headers the Http headers
* @param logger the logger
* @param ignoreException whether to ignore malformed URL
* @return the Azure-AsyncOperation header value if exists, null otherwise
*/
static URL getAzureAsyncOperationUrl(HttpHeaders headers, ClientLogger logger, boolean ignoreException) {
return getUrl("Azure-AsyncOperation", headers, logger, ignoreException);
}

/**
* Gets value of Location header from the given Http headers.
*
Expand All @@ -53,7 +67,7 @@ static URL getLocationUrl(HttpHeaders headers, ClientLogger logger) {
*
* @param headers the Http headers
* @param logger the logger
* @return the Location header value if exists, null otherwise
* @param ignoreException whether to ignore malformed URL
* @return the URL value of the given header, null if header does not exists.
*/
static URL getLocationUrl(HttpHeaders headers, ClientLogger logger, boolean ignoreException) {
Expand All @@ -73,8 +87,8 @@ private static URL getUrl(String urlHeaderName, HttpHeaders headers, ClientLogge
String value = headers.getValue(urlHeaderName);
if (value != null) {
try {
return new URL(value);
} catch (MalformedURLException me) {
return new URI(value).toURL();
} catch (MalformedURLException | URISyntaxException | IllegalArgumentException me) {
String message = "Malformed value '" + value + "' for URL header: '" + urlHeaderName + "'.";
if (ignoreException) {
logger.logExceptionAsError(new MalformedUrlException(message, me));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ public String getName() {
@Test
public void lro200SucceededNoPoll() {
final String resourceEndpoint = "/resource/1";
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\"}}";
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\"}}";
ResponseTransformer provisioningStateLroService = new ResponseTransformer() {
@Override
public com.github.tomakehurst.wiremock.http.Response transform(Request request,
Expand Down Expand Up @@ -416,7 +416,7 @@ public String getName() {
@Test
public void lro201AsyncOperationSucceededNoPoll() {
final String resourceEndpoint = "/resource/1";
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\"}";
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\"}";
ResponseTransformer provisioningStateLroService = new ResponseTransformer() {
@Override
public com.github.tomakehurst.wiremock.http.Response transform(Request request,
Expand All @@ -434,7 +434,7 @@ public com.github.tomakehurst.wiremock.http.Response transform(Request request,
// 201 response with provisioningState=Succeeded.
return new com.github.tomakehurst.wiremock.http.Response.Builder()
.headers(new HttpHeaders(
new HttpHeader("Azure-AsyncOperation", "https://management.azure.com/subscriptions/###/providers/Microsoft.Network/locations/eastus/operations/###")))
new HttpHeader("Azure-AsyncOperation", "https://management.azure.com/subscriptions/000/providers/Microsoft.Network/locations/eastus/operations/123")))
.body(sampleNicCreateSucceededResponse)
.status(201)
.build();
Expand Down Expand Up @@ -492,7 +492,7 @@ public String getName() {
@Test
public void lro201SucceededNoPoll() {
final String resourceEndpoint = "/resource/1";
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\"}}";
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\"}}";
ResponseTransformer provisioningStateLroService = new ResponseTransformer() {
@Override
public com.github.tomakehurst.wiremock.http.Response transform(Request request,
Expand All @@ -510,7 +510,7 @@ public com.github.tomakehurst.wiremock.http.Response transform(Request request,
// 201 response with provisioningState=Succeeded.
return new com.github.tomakehurst.wiremock.http.Response.Builder()
.headers(new HttpHeaders(
new HttpHeader("Location", "https://management.azure.com/subscriptions/###/resourceGroups/rg86829b7a87d74/providers/Microsoft.Search/searchServices/ss3edfb54d")))
new HttpHeader("Location", "https://management.azure.com/subscriptions/000/resourceGroups/rg86829b7a87d74/providers/Microsoft.Search/searchServices/ss3edfb54d")))
.body(sampleSearchServiceCreateSucceededResponse)
.status(201)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public class UtilTests {

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

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

asyncOpHeaders.set("Azure-AsyncOperation", "https://management.azure.com/subscriptions/000/providers/Microsoft.Network/locations/east us/operations/123");
Assertions.assertThrows(Util.MalformedUrlException.class, () -> Util.getAzureAsyncOperationUrl(asyncOpHeaders, logger));

// malformed URL in location will be ignored
HttpHeaders locationHeaders = new HttpHeaders();
locationHeaders.set("Location", "invalidUrl");
Expand Down

0 comments on commit e747987

Please sign in to comment.