Repository: jclouds
Updated Branches:
  refs/heads/master b06795ebe -> 00f7ee073


jclouds was not properly retrying on an expect: 100-continue PUT request 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 
https://github.com/square/okhttp/issues/675

This is a common problem in other tools as well 
https://curl.haxx.se/mail/lib-2004-08/0002.html


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/00f7ee07
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/00f7ee07
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/00f7ee07

Branch: refs/heads/master
Commit: 00f7ee0738fb855119ab048a40832efe97bc6335
Parents: b06795e
Author: Zack Shoylev <zack.shoy...@rackspace.com>
Authored: Tue Oct 11 14:23:57 2016 -0500
Committer: Zack Shoylev <zack.shoy...@rackspace.com>
Committed: Thu Oct 13 12:17:19 2016 -0500

----------------------------------------------------------------------
 .../swift/v1/features/ObjectApiMockTest.java    |  71 ++++++
 .../src/test/resources/access2.json             | 249 +++++++++++++++++++
 .../BaseHttpCommandExecutorService.java         |  11 +
 3 files changed, 331 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/00f7ee07/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
----------------------------------------------------------------------
diff --git 
a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
 
b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
index 5e3d0f6..28299de 100644
--- 
a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
+++ 
b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
@@ -255,6 +255,77 @@ public class ObjectApiMockTest extends 
BaseOpenStackMockTest<SwiftApi> {
       }
    }
 
+   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();

http://git-wip-us.apache.org/repos/asf/jclouds/blob/00f7ee07/apis/openstack-swift/src/test/resources/access2.json
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/resources/access2.json 
b/apis/openstack-swift/src/test/resources/access2.json
new file mode 100644
index 0000000..7d0d9f3
--- /dev/null
+++ b/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"
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/00f7ee07/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java
 
b/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java
index ad286cc..f92329a 100644
--- 
a/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java
+++ 
b/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java
@@ -25,6 +25,7 @@ import static org.jclouds.http.HttpUtils.wirePayloadIfEnabled;
 import static org.jclouds.util.Throwables2.getFirstThrowableOfType;
 
 import java.io.IOException;
+import java.net.ProtocolException;
 import java.util.Set;
 
 import javax.annotation.Resource;
@@ -145,6 +146,16 @@ public abstract class BaseHttpCommandExecutorService<Q> 
implements HttpCommandEx
    }
 
    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);
    }
 

Reply via email to