Allow to globally configure the 'Connection: close' header

This commit is contained in:
Ignasi Barrera 2015-10-02 15:15:41 +02:00
parent f832ad00a7
commit 07e64a2d07
6 changed files with 117 additions and 1 deletions

View File

@ -307,6 +307,12 @@ public final class Constants {
*/ */
public static final String PROPERTY_STRIP_EXPECT_HEADER = "jclouds.strip-expect-header"; public static final String PROPERTY_STRIP_EXPECT_HEADER = "jclouds.strip-expect-header";
/**
* When true, add the Connection: close header. Useful when interacting with
* providers that don't properly support persistent connections. Defaults to false.
*/
public static final String PROPERTY_CONNECTION_CLOSE_HEADER = "jclouds.connection-close-header";
/** /**
* The maximum number of blob deletes happening in parallel at any point in time. * The maximum number of blob deletes happening in parallel at any point in time.
*/ */

View File

@ -18,6 +18,7 @@ package org.jclouds.apis.internal;
import static com.google.common.base.Objects.equal; import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.Constants.PROPERTY_CONNECTION_CLOSE_HEADER;
import static org.jclouds.Constants.PROPERTY_CONNECTION_TIMEOUT; import static org.jclouds.Constants.PROPERTY_CONNECTION_TIMEOUT;
import static org.jclouds.Constants.PROPERTY_ISO3166_CODES; import static org.jclouds.Constants.PROPERTY_ISO3166_CODES;
import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT; import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT;
@ -73,6 +74,7 @@ public abstract class BaseApiMetadata implements ApiMetadata {
props.setProperty(PROPERTY_SESSION_INTERVAL, 60 + ""); props.setProperty(PROPERTY_SESSION_INTERVAL, 60 + "");
props.setProperty(PROPERTY_PRETTY_PRINT_PAYLOADS, "true"); props.setProperty(PROPERTY_PRETTY_PRINT_PAYLOADS, "true");
props.setProperty(PROPERTY_STRIP_EXPECT_HEADER, "false"); props.setProperty(PROPERTY_STRIP_EXPECT_HEADER, "false");
props.setProperty(PROPERTY_CONNECTION_CLOSE_HEADER, "false");
// By default, we allow maximum parallel deletes to be equal to the number // By default, we allow maximum parallel deletes to be equal to the number
// of user threads since one thread is used to delete on blob. // of user threads since one thread is used to delete on blob.

View File

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.http.filters;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import com.google.common.net.HttpHeaders;
import com.google.inject.Singleton;
@Singleton
public class ConnectionCloseHeader implements HttpRequestFilter {
@Override
public HttpRequest filter(HttpRequest request) throws HttpException {
return request.toBuilder().addHeader(HttpHeaders.CONNECTION, "close").build();
}
}

View File

@ -51,6 +51,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Named; import javax.inject.Named;
import javax.ws.rs.FormParam; import javax.ws.rs.FormParam;
@ -65,6 +66,7 @@ import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter; import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpUtils; import org.jclouds.http.HttpUtils;
import org.jclouds.http.Uris.UriBuilder; import org.jclouds.http.Uris.UriBuilder;
import org.jclouds.http.filters.ConnectionCloseHeader;
import org.jclouds.http.filters.StripExpectHeader; import org.jclouds.http.filters.StripExpectHeader;
import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.http.options.HttpRequestOptions;
import org.jclouds.io.ContentMetadataCodec; import org.jclouds.io.ContentMetadataCodec;
@ -147,12 +149,14 @@ public class RestAnnotationProcessor implements Function<Invocation, HttpRequest
private final GetAcceptHeaders getAcceptHeaders; private final GetAcceptHeaders getAcceptHeaders;
private final Invocation caller; private final Invocation caller;
private final boolean stripExpectHeader; private final boolean stripExpectHeader;
private final boolean connectionCloseHeader;
@Inject @Inject
private RestAnnotationProcessor(Injector injector, @ApiVersion String apiVersion, @BuildVersion String buildVersion, private RestAnnotationProcessor(Injector injector, @ApiVersion String apiVersion, @BuildVersion String buildVersion,
HttpUtils utils, ContentMetadataCodec contentMetadataCodec, InputParamValidator inputParamValidator, HttpUtils utils, ContentMetadataCodec contentMetadataCodec, InputParamValidator inputParamValidator,
GetAcceptHeaders getAcceptHeaders, @Nullable @Named("caller") Invocation caller, GetAcceptHeaders getAcceptHeaders, @Nullable @Named("caller") Invocation caller,
@Named(Constants.PROPERTY_STRIP_EXPECT_HEADER) boolean stripExpectHeader) { @Named(Constants.PROPERTY_STRIP_EXPECT_HEADER) boolean stripExpectHeader,
@Named(Constants.PROPERTY_CONNECTION_CLOSE_HEADER) boolean connectionCloseHeader) {
this.injector = injector; this.injector = injector;
this.utils = utils; this.utils = utils;
this.contentMetadataCodec = contentMetadataCodec; this.contentMetadataCodec = contentMetadataCodec;
@ -162,6 +166,7 @@ public class RestAnnotationProcessor implements Function<Invocation, HttpRequest
this.getAcceptHeaders = getAcceptHeaders; this.getAcceptHeaders = getAcceptHeaders;
this.caller = caller; this.caller = caller;
this.stripExpectHeader = stripExpectHeader; this.stripExpectHeader = stripExpectHeader;
this.connectionCloseHeader = connectionCloseHeader;
} }
/** /**
@ -211,6 +216,9 @@ public class RestAnnotationProcessor implements Function<Invocation, HttpRequest
if (stripExpectHeader) { if (stripExpectHeader) {
requestBuilder.filter(new StripExpectHeader()); requestBuilder.filter(new StripExpectHeader());
} }
if (connectionCloseHeader) {
requestBuilder.filter(new ConnectionCloseHeader());
}
Multimap<String, Object> tokenValues = LinkedHashMultimap.create(); Multimap<String, Object> tokenValues = LinkedHashMultimap.create();

View File

@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.http.filters;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import org.jclouds.http.HttpRequest;
import org.testng.annotations.Test;
import com.google.common.net.HttpHeaders;
@Test(groups = "unit")
public class ConnectionCloseHeaderTest {
public void testConnectionHeaderIsAdded() {
HttpRequest request = HttpRequest.builder().method("POST").endpoint("http://localhost").build();
request = new ConnectionCloseHeader().filter(request);
assertTrue(request.getHeaders().containsKey(HttpHeaders.CONNECTION));
assertEquals(request.getHeaders().get(HttpHeaders.CONNECTION).size(), 1);
assertEquals(request.getHeaders().get(HttpHeaders.CONNECTION).iterator().next(), "close");
}
}

View File

@ -78,6 +78,7 @@ import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter; import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.http.IOExceptionRetryHandler; import org.jclouds.http.IOExceptionRetryHandler;
import org.jclouds.http.filters.ConnectionCloseHeader;
import org.jclouds.http.filters.StripExpectHeader; import org.jclouds.http.filters.StripExpectHeader;
import org.jclouds.http.functions.ParseFirstJsonValueNamed; import org.jclouds.http.functions.ParseFirstJsonValueNamed;
import org.jclouds.http.functions.ParseJson; import org.jclouds.http.functions.ParseJson;
@ -1440,6 +1441,34 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest {
assertEquals(request.getFilters().get(1).getClass(), StripExpectHeader.class); assertEquals(request.getFilters().get(1).getClass(), StripExpectHeader.class);
} }
@Test
public void testRequestFilterAddConnection() {
// First, verify that by default, the StripExpectHeader filter is not applied
Invokable<?, ?> method = method(TestRequestFilter.class, "post");
Invocation invocation = Invocation.create(method,
ImmutableList.<Object>of(HttpRequest.builder().method("POST").endpoint("http://localhost").build()));
GeneratedHttpRequest request = processor.apply(invocation);
assertEquals(request.getFilters().size(), 1);
assertEquals(request.getFilters().get(0).getClass(), TestRequestFilter1.class);
// Now let's create a new injector with the property set. Use that to create the annotation processor.
Properties overrides = new Properties();
overrides.setProperty(Constants.PROPERTY_CONNECTION_CLOSE_HEADER, "true");
Injector injector = ContextBuilder.newBuilder(forApiOnEndpoint(Callee.class, "http://localhost:9999"))
.modules(ImmutableSet.<Module> of(new MockModule(), new NullLoggingModule(), new AbstractModule() {
protected void configure() {
bind(new TypeLiteral<Supplier<URI>>() {
}).annotatedWith(Localhost2.class).toInstance(
Suppliers.ofInstance(URI.create("http://localhost:1111")));
}}))
.overrides(overrides).buildInjector();
RestAnnotationProcessor newProcessor = injector.getInstance(RestAnnotationProcessor.class);
// Verify that this time the filter is indeed applied as expected.
request = newProcessor.apply(invocation);
assertEquals(request.getFilters().size(), 2);
assertEquals(request.getFilters().get(1).getClass(), ConnectionCloseHeader.class);
}
public class TestEncoding { public class TestEncoding {
@GET @GET
@Path("/{path1}/{path2}") @Path("/{path1}/{path2}")