From cd79a9d87604abc55c4bb1451df8334e4003de35 Mon Sep 17 00:00:00 2001 From: adriancole Date: Sun, 24 Mar 2013 20:58:29 -0700 Subject: [PATCH 1/3] HttpUrlConnection reverts Content-Length=0 on POST unless doOutput is set to true --- .../http/internal/JavaUrlHttpCommandExecutorService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java index 3780868a52..cc2ca8a576 100644 --- a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java +++ b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java @@ -231,6 +231,11 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe protected void writeNothing(HttpURLConnection connection) { if (!HttpRequest.NON_PAYLOAD_METHODS.contains(connection.getRequestMethod())) { connection.setRequestProperty(CONTENT_LENGTH, "0"); + // support zero length posts. + if ("POST".equals(connection.getRequestMethod())) { + connection.setFixedLengthStreamingMode(0); + connection.setDoOutput(true); + } } } From 2334d824be8f6ce5b797e16a47a6805a9839c0ea Mon Sep 17 00:00:00 2001 From: adriancole Date: Sun, 24 Mar 2013 20:58:50 -0700 Subject: [PATCH 2/3] added missing annotation for trmk provides method --- .../org/jclouds/trmk/vcloud_0_8/TerremarkVCloudClient.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/TerremarkVCloudClient.java b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/TerremarkVCloudClient.java index 14ff021910..004fe55efb 100644 --- a/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/TerremarkVCloudClient.java +++ b/common/trmk/src/main/java/org/jclouds/trmk/vcloud_0_8/TerremarkVCloudClient.java @@ -44,6 +44,8 @@ import org.jclouds.trmk.vcloud_0_8.options.AddNodeOptions; import org.jclouds.trmk.vcloud_0_8.options.CloneVAppOptions; import org.jclouds.trmk.vcloud_0_8.options.InstantiateVAppTemplateOptions; +import com.google.inject.Provides; + /** * Provides access to VCloud resources via their REST API. *

@@ -129,6 +131,8 @@ public interface TerremarkVCloudClient { * * @return a listing of all orgs that the current user has access to. */ + @Provides + @org.jclouds.trmk.vcloud_0_8.endpoints.Org Map listOrgs(); VApp instantiateVAppTemplateInVDC(URI vDC, URI template, String appName, InstantiateVAppTemplateOptions... options); From f669ed136ed188254e70a990f38ea2fe17579565 Mon Sep 17 00:00:00 2001 From: adriancole Date: Sun, 24 Mar 2013 21:01:38 -0700 Subject: [PATCH 3/3] backfilled test that ensures login command on trmk sets content-length --- providers/trmk-ecloud/pom.xml | 5 + .../ecloud/TerremarkECloudClientMockTest.java | 123 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 providers/trmk-ecloud/src/test/java/org/jclouds/trmk/ecloud/TerremarkECloudClientMockTest.java diff --git a/providers/trmk-ecloud/pom.xml b/providers/trmk-ecloud/pom.xml index c658146909..fcd0d483b1 100644 --- a/providers/trmk-ecloud/pom.xml +++ b/providers/trmk-ecloud/pom.xml @@ -78,6 +78,11 @@ test-jar test + + com.google.mockwebserver + mockwebserver + test + org.jclouds.driver jclouds-log4j diff --git a/providers/trmk-ecloud/src/test/java/org/jclouds/trmk/ecloud/TerremarkECloudClientMockTest.java b/providers/trmk-ecloud/src/test/java/org/jclouds/trmk/ecloud/TerremarkECloudClientMockTest.java new file mode 100644 index 0000000000..8da4e8f29f --- /dev/null +++ b/providers/trmk-ecloud/src/test/java/org/jclouds/trmk/ecloud/TerremarkECloudClientMockTest.java @@ -0,0 +1,123 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.trmk.ecloud; + +import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; +import static org.jclouds.Constants.PROPERTY_MAX_RETRIES; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.net.URL; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicReference; + +import org.jclouds.ContextBuilder; +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; +import com.google.mockwebserver.MockResponse; +import com.google.mockwebserver.MockWebServer; +import com.google.mockwebserver.QueueDispatcher; +import com.google.mockwebserver.RecordedRequest; + +/** + * + * @author Adrian Cole + */ +@Test(singleThreaded = true) +public class TerremarkECloudClientMockTest { + + private static final Set modules = ImmutableSet. of( + new ExecutorServiceModule(sameThreadExecutor(), sameThreadExecutor())); + + static TerremarkECloudClient mockTerremarkECloudClient(String uri) { + Properties overrides = new Properties(); + overrides.setProperty(PROPERTY_MAX_RETRIES, "1"); + return ContextBuilder.newBuilder("trmk-ecloud") + .credentials("user", "password") + .endpoint(uri) + .overrides(overrides) + .modules(modules) + .build(TerremarkECloudApiMetadata.CONTEXT_TOKEN).getApi(); + } + + String versionXML = "0.8b-ext2.8URLv0.8/login"; + + @Test + public void testLoginSetsContentLength() throws IOException, InterruptedException { + MockWebServer server = new MockWebServer(); + AtomicReference url = setURLReplacingDispatcher(server); + server.enqueue(new MockResponse().setResponseCode(200).setBody(versionXML)); + server.enqueue(new MockResponse().setResponseCode(200) + .addHeader("x-vcloud-authorization", "cookie") + .setBody("")); + server.play(); + url.set(server.getUrl("/")); + + TerremarkECloudClient api = mockTerremarkECloudClient(url.get().toString()); + + try { + api.listOrgs(); + } finally { + RecordedRequest getVersions = server.takeRequest(); + assertEquals(getVersions.getRequestLine(), "GET /versions HTTP/1.1"); + + RecordedRequest login = server.takeRequest(); + assertEquals(login.getRequestLine(), "POST /v0.8/login HTTP/1.1"); + assertEquals(login.getHeader("Authorization"), "Basic dXNlcjpwYXNzd29yZA=="); + assertEquals(login.getHeader("Content-Length"), "0"); + + server.shutdown(); + } + } + + /** + * there's no built-in way to defer evaluation of a response body, hence this + * method, which allows us to send back links to the mock server. + */ + private AtomicReference setURLReplacingDispatcher(MockWebServer server) { + final AtomicReference url = new AtomicReference(); + + final QueueDispatcher dispatcher = new QueueDispatcher() { + protected final BlockingQueue responseQueue = new LinkedBlockingQueue(); + + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + MockResponse response = responseQueue.take(); + if (response.getBody() != null) { + String newBody = new String(response.getBody()).replace("URL", url.get().toString()); + response = response.setBody(newBody); + } + return response; + } + + @Override + public void enqueueResponse(MockResponse response) { + responseQueue.add(response); + } + }; + server.setDispatcher(dispatcher); + return url; + } +}