mirror of https://github.com/apache/jclouds.git
fix issue #1297 put with zero length payload
This commit is contained in:
parent
756e7724ef
commit
6c658f6450
|
@ -87,6 +87,11 @@
|
||||||
<artifactId>log4j</artifactId>
|
<artifactId>log4j</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.mockwebserver</groupId>
|
||||||
|
<artifactId>mockwebserver</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
|
|
|
@ -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<Module> modules = ImmutableSet.<Module> of(
|
||||||
|
new ExecutorServiceModule(sameThreadExecutor(), sameThreadExecutor()));
|
||||||
|
|
||||||
|
static RestContext<? extends S3Client,? extends S3AsyncClient> 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -214,12 +214,19 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
|
||||||
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6755625
|
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6755625
|
||||||
checkArgument(length < Integer.MAX_VALUE,
|
checkArgument(length < Integer.MAX_VALUE,
|
||||||
"JDK 1.6 does not support >2GB chunks. Use chunked encoding, if possible.");
|
"JDK 1.6 does not support >2GB chunks. Use chunked encoding, if possible.");
|
||||||
connection.setRequestProperty(CONTENT_LENGTH, length + "");
|
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
writePayloadToConnection(payload, connection);
|
writePayloadToConnection(payload, connection);
|
||||||
|
} else {
|
||||||
|
writeNothing(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
writeNothing(connection);
|
||||||
|
}
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeNothing(HttpURLConnection connection) {
|
||||||
connection.setRequestProperty(CONTENT_LENGTH, "0");
|
connection.setRequestProperty(CONTENT_LENGTH, "0");
|
||||||
// for some reason POST/PUT undoes the content length header above.
|
// for some reason POST/PUT undoes the content length header above.
|
||||||
if (ImmutableSet.of("POST", "PUT").contains(connection.getRequestMethod())) {
|
if (ImmutableSet.of("POST", "PUT").contains(connection.getRequestMethod())) {
|
||||||
|
@ -227,14 +234,13 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
|
||||||
connection.setDoOutput(true);
|
connection.setDoOutput(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
void writePayloadToConnection(Payload payload, HttpURLConnection connection) throws IOException {
|
void writePayloadToConnection(Payload payload, HttpURLConnection connection) throws IOException {
|
||||||
Long length = payload.getContentMetadata().getContentLength();
|
Long length = payload.getContentMetadata().getContentLength();
|
||||||
|
connection.setRequestProperty(CONTENT_LENGTH, length.toString());
|
||||||
|
connection.setRequestProperty("Expect", "100-continue");
|
||||||
connection.setFixedLengthStreamingMode(length.intValue());
|
connection.setFixedLengthStreamingMode(length.intValue());
|
||||||
connection.setDoOutput(true);
|
connection.setDoOutput(true);
|
||||||
connection.setRequestProperty("Expect", "100-continue");
|
|
||||||
CountingOutputStream out = new CountingOutputStream(connection.getOutputStream());
|
CountingOutputStream out = new CountingOutputStream(connection.getOutputStream());
|
||||||
try {
|
try {
|
||||||
payload.writeTo(out);
|
payload.writeTo(out);
|
||||||
|
|
|
@ -271,6 +271,11 @@
|
||||||
<artifactId>logback-core</artifactId>
|
<artifactId>logback-core</artifactId>
|
||||||
<version>1.0.7</version>
|
<version>1.0.7</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.mockwebserver</groupId>
|
||||||
|
<artifactId>mockwebserver</artifactId>
|
||||||
|
<version>20121111</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
Loading…
Reference in New Issue