Skip to content
This repository has been archived by the owner on Jul 25, 2020. It is now read-only.

Commit

Permalink
jclouds was not properly retrying on an expect: 100-continue PUT requ…
Browse files Browse the repository at this point in the history
…est that requires re-athentication because of java protocol code

JCLOUDS-1179 This fix also addresses the same problem with other providers

No currently working tests because of square/okhttp#675

This is a common problem in other tools as well https://curl.haxx.se/mail/lib-2004-08/0002.html
  • Loading branch information
zack-shoylev committed Oct 13, 2016
1 parent b06795e commit 00f7ee0
Show file tree
Hide file tree
Showing 3 changed files with 331 additions and 0 deletions.
Expand Up @@ -255,6 +255,77 @@ public void testCreateWith408Retry() throws Exception {
}
}

public void testCreateWith401Retry() throws Exception {
// TODO: requires upgrade to okhttp mockwebserver 3.6

MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));

// PUT 1 - establishes auth tokens
// Respond to request
// This part will work in mockwebserver 3.6+
// server.enqueue(new MockResponse().setResponseCode(100).setStatus("Continue"));

// Finish request
server.enqueue(addCommonHeaders(new MockResponse()
.setResponseCode(201)
.addHeader("ETag", "d9f5eb4bba4e2f2f046e54611bc8196b")));

// PUT 2
// Respond to request
// server.enqueue(new MockResponse().setStatus("HTTP/1.1 100 Continue").clearHeaders());
// token expired!
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(401).setBody("401 Unauthorized")));
// re-auth
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access2.json"))));

// Finally success
server.enqueue(addCommonHeaders(new MockResponse()
.setResponseCode(201)
.addHeader("ETag", "d9f5eb4bba4e2f2f046e54611bc8196b")));

try {
Properties overrides = new Properties();
overrides.setProperty(PROPERTY_MAX_RETRIES, 5 + "");

SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift", overrides);
assertEquals(
api.getObjectApi("DFW", "myContainer").put("myObject1", PAYLOAD,
metadata(metadata)), "d9f5eb4bba4e2f2f046e54611bc8196b");

assertEquals(
api.getObjectApi("DFW", "myContainer").put("myObject2", PAYLOAD,
metadata(metadata)), "d9f5eb4bba4e2f2f046e54611bc8196b");

assertEquals(server.getRequestCount(), 5);

//////

// First auth (auth cache empty
assertAuthentication(server);

// PUT 1 request
RecordedRequest replace = server.takeRequest();
assertRequest(replace, "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject1");

// token expired

// PUT 2 request
replace = server.takeRequest();
assertRequest(replace, "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject2");

// PUT 2 request re-auth
assertAuthentication(server);

// PUT 2 request retry
replace = server.takeRequest();
assertRequest(replace, "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject2");

} finally {
server.shutdown();
}
}

/** upper-cases first char, and lower-cases rest!! **/
public void testGetWithoutKnowingServerMessesWithMetadataKeyCaseFormat() throws Exception {
MockWebServer server = mockOpenStackServer();
Expand Down
249 changes: 249 additions & 0 deletions apis/openstack-swift/src/test/resources/access2.json
@@ -0,0 +1,249 @@
{
"access":{
"token":{
"id":"bb03a23aa8271291a7bbb9bbb2aaaaaa",
"expires":"2013-08-02T16:55:24.229-05:00",
"tenant":{
"id":"888888",
"name":"888888"
},
"RAX-AUTH:authenticatedBy":[
"PASSWORD"
]
},
"serviceCatalog":[
{
"name":"cloudFilesCDN",
"endpoints":[
{
"region":"ORD",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
},
{
"region":"DFW",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
},
{
"region":"SYD",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
}
],
"type":"rax:object-cdn"
},
{
"name":"cloudFiles",
"endpoints":[
{
"region":"ORD",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"internalURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
},
{
"region":"DFW",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"internalURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
},
{
"region":"SYD",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"internalURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
}
],
"type":"object-store"
},
{
"name":"cloudLoadBalancers",
"endpoints":[
{
"region":"SYD",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
},
{
"region":"DFW",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
},
{
"region":"ORD",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
}
],
"type":"rax:load-balancer"
},
{
"name":"cloudDatabases",
"endpoints":[
{
"region":"SYD",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
},
{
"region":"DFW",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
},
{
"region":"ORD",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
}
],
"type":"rax:database"
},
{
"name":"cloudBlockStorage",
"endpoints":[
{
"region":"SYD",
"tenantId":"888888",
"publicURL":"URL/v1\/888888"
},
{
"region":"DFW",
"tenantId":"888888",
"publicURL":"URL/v1\/888888"
},
{
"region":"ORD",
"tenantId":"888888",
"publicURL":"URL/v1\/888888"
}
],
"type":"volume"
},
{
"name":"cloudServersOpenStack",
"endpoints":[
{
"region":"SYD",
"tenantId":"888888",
"publicURL":"URL/v2\/888888",
"versionInfo":"https:\/\/syd.servers.api.rackspacecloud.com\/v2",
"versionList":"https:\/\/syd.servers.api.rackspacecloud.com\/",
"versionId":"2"
},
{
"region":"DFW",
"tenantId":"888888",
"publicURL":"URL/v2\/888888",
"versionInfo":"https:\/\/dfw.servers.api.rackspacecloud.com\/v2",
"versionList":"https:\/\/dfw.servers.api.rackspacecloud.com\/",
"versionId":"2"
},
{
"region":"ORD",
"tenantId":"888888",
"publicURL":"URL/v2\/888888",
"versionInfo":"https:\/\/ord.servers.api.rackspacecloud.com\/v2",
"versionList":"https:\/\/ord.servers.api.rackspacecloud.com\/",
"versionId":"2"
}
],
"type":"compute"
},
{
"name":"autoscale",
"endpoints":[
{
"region":"ORD",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888",
"versionInfo":null,
"versionList":null,
"versionId":"1.0"
},
{
"region":"DFW",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888",
"versionInfo":null,
"versionList":null,
"versionId":"1.0"
}
],
"type":"rax:autoscale"
},
{
"name":"cloudMonitoring",
"endpoints":[
{
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
}
],
"type":"rax:monitor"
},
{
"name":"cloudBackup",
"endpoints":[
{
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
}
],
"type":"rax:backup"
},
{
"name":"cloudServers",
"endpoints":[
{
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888",
"versionInfo":"https:\/\/servers.api.rackspacecloud.com\/v1.0",
"versionList":"https:\/\/servers.api.rackspacecloud.com\/",
"versionId":"1.0"
}
],
"type":"compute"
},
{
"name":"cloudDNS",
"endpoints":[
{
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
}
],
"type":"rax:dns"
}
],
"user":{
"id":"335853",
"roles":[
{
"id":"10000150",
"description":"Checkmate Access role",
"name":"checkmate"
},
{
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"id":"5",
"description":"A Role that allows a user access to keystone Service methods",
"name":"object-store:default"
},
{
"tenantId":"888888",
"id":"6",
"description":"A Role that allows a user access to keystone Service methods",
"name":"compute:default"
},
{
"id":"3",
"description":"User Admin Role.",
"name":"identity:user-admin"
}
],
"name":"test",
"RAX-AUTH:defaultRegion":"ORD"
}
}
}
Expand Up @@ -25,6 +25,7 @@
import static org.jclouds.util.Throwables2.getFirstThrowableOfType;

import java.io.IOException;
import java.net.ProtocolException;
import java.util.Set;

import javax.annotation.Resource;
Expand Down Expand Up @@ -145,6 +146,16 @@ boolean shouldContinue(HttpCommand command, HttpResponse response) {
}

boolean shouldContinue(HttpCommand command, IOException response) {
// Even though Java does not want to handle it this way,
// treat a Protocol Exception on PUT with 100-Continue as a case of Unauthorized (and attempt to retry)
if (command.getCurrentRequest().getMethod().equals("PUT")
&& command.getCurrentRequest().getHeaders().containsEntry("Expect", "100-continue")
&& response instanceof ProtocolException
&& response.getMessage().equals("Server rejected operation")
) {
logger.debug("Caught a protocol exception on a 100-continue PUT request. Attempting to retry.");
return isIdempotent(command) && retryHandler.shouldRetryRequest(command, HttpResponse.builder().statusCode(401).message("Unauthorized").build());
}
return isIdempotent(command) && ioRetryHandler.shouldRetryRequest(command, response);
}

Expand Down

0 comments on commit 00f7ee0

Please sign in to comment.