From 6c658f6450d37affeaf64154b01168f56cf52ae7 Mon Sep 17 00:00:00 2001 From: adriancole Date: Mon, 4 Feb 2013 10:32:03 -0800 Subject: [PATCH] fix issue #1297 put with zero length payload --- apis/s3/pom.xml | 5 ++ .../java/org/jclouds/s3/S3ClientMockTest.java | 84 +++++++++++++++++++ .../JavaUrlHttpCommandExecutorService.java | 22 +++-- project/pom.xml | 5 ++ 4 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 apis/s3/src/test/java/org/jclouds/s3/S3ClientMockTest.java diff --git a/apis/s3/pom.xml b/apis/s3/pom.xml index 364e2339ac..17de08738b 100644 --- a/apis/s3/pom.xml +++ b/apis/s3/pom.xml @@ -87,6 +87,11 @@ log4j test + + com.google.mockwebserver + mockwebserver + test + diff --git a/apis/s3/src/test/java/org/jclouds/s3/S3ClientMockTest.java b/apis/s3/src/test/java/org/jclouds/s3/S3ClientMockTest.java new file mode 100644 index 0000000000..6931d49d1a --- /dev/null +++ b/apis/s3/src/test/java/org/jclouds/s3/S3ClientMockTest.java @@ -0,0 +1,84 @@ +/** + * 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.s3; + +import static com.google.common.net.HttpHeaders.CONTENT_LENGTH; +import static com.google.common.net.HttpHeaders.ETAG; +import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; +import static org.jclouds.s3.reference.S3Constants.PROPERTY_S3_VIRTUAL_HOST_BUCKETS; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.net.URL; +import java.util.Properties; +import java.util.Set; + +import org.jclouds.ContextBuilder; +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.rest.RestContext; +import org.jclouds.s3.domain.S3Object; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +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.RecordedRequest; + +/** + * + * @author Adrian Cole + */ +@Test +public class S3ClientMockTest { + + private static final Set modules = ImmutableSet. of( + new ExecutorServiceModule(sameThreadExecutor(), sameThreadExecutor())); + + static RestContext getContext(URL server) { + Properties overrides = new Properties(); + overrides.setProperty(PROPERTY_S3_VIRTUAL_HOST_BUCKETS, "false"); + + return ContextBuilder.newBuilder("s3") + .credentials("accessKey", "secretKey") + .endpoint(server.toString()) + .modules(modules) + .overrides(overrides) + .build(S3ApiMetadata.CONTEXT_TOKEN); + } + + public void testZeroLengthPutHasContentLengthHeader() throws IOException, InterruptedException { + MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setBody("").addHeader(ETAG, "ABCDEF")); + server.play(); + + S3Client client = getContext(server.getUrl("/")).getApi(); + S3Object nada = client.newS3Object(); + nada.getMetadata().setKey("object"); + nada.setPayload(new byte[] {}); + + assertEquals(client.putObject("bucket", nada), "ABCDEF"); + + RecordedRequest request = server.takeRequest(); + assertEquals(request.getRequestLine(), "PUT /bucket/object HTTP/1.1"); + assertEquals(request.getHeaders(CONTENT_LENGTH), ImmutableList.of("0")); + server.shutdown(); + } +} 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 21d4c9b1eb..0816e20840 100644 --- a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java +++ b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java @@ -214,27 +214,33 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6755625 checkArgument(length < Integer.MAX_VALUE, "JDK 1.6 does not support >2GB chunks. Use chunked encoding, if possible."); - connection.setRequestProperty(CONTENT_LENGTH, length + ""); if (length > 0) { writePayloadToConnection(payload, connection); + } else { + writeNothing(connection); } } } else { - connection.setRequestProperty(CONTENT_LENGTH, "0"); - // for some reason POST/PUT undoes the content length header above. - if (ImmutableSet.of("POST", "PUT").contains(connection.getRequestMethod())) { - connection.setFixedLengthStreamingMode(0); - connection.setDoOutput(true); - } + writeNothing(connection); } return connection; } + protected void writeNothing(HttpURLConnection connection) { + connection.setRequestProperty(CONTENT_LENGTH, "0"); + // for some reason POST/PUT undoes the content length header above. + if (ImmutableSet.of("POST", "PUT").contains(connection.getRequestMethod())) { + connection.setFixedLengthStreamingMode(0); + connection.setDoOutput(true); + } + } + void writePayloadToConnection(Payload payload, HttpURLConnection connection) throws IOException { Long length = payload.getContentMetadata().getContentLength(); + connection.setRequestProperty(CONTENT_LENGTH, length.toString()); + connection.setRequestProperty("Expect", "100-continue"); connection.setFixedLengthStreamingMode(length.intValue()); connection.setDoOutput(true); - connection.setRequestProperty("Expect", "100-continue"); CountingOutputStream out = new CountingOutputStream(connection.getOutputStream()); try { payload.writeTo(out); diff --git a/project/pom.xml b/project/pom.xml index 6aaf69ffc2..cfe83a5cad 100644 --- a/project/pom.xml +++ b/project/pom.xml @@ -271,6 +271,11 @@ logback-core 1.0.7 + + com.google.mockwebserver + mockwebserver + 20121111 +