From 7a6ec6952357d23b3f17694236a253f385b14e10 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 4 Mar 2014 16:53:04 +0100 Subject: [PATCH] Fix custom config with older HttpClient Since HttpClient 4.3, custom configuration such as the connection timeout and the socket timeout are set in a RequestConfig object stored in the HttpContext. Unfortunately, older HttpClients are not supporting this infrastructure and new clients throw an exception when the deprecated API is used. This commit detects if the client is an "old" implementation and set the configuration through the deprecated means to restore full backward compatibility with these features. Issue: SPR-11442 --- .../HttpComponentsClientHttpRequest.java | 3 ++ ...ttpComponentsClientHttpRequestFactory.java | 40 +++++++++++++++++ ...mponentsClientHttpRequestFactoryTests.java | 44 ++++++++++++++++++- 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java index 8c3a9f5123..751dfa9d5c 100644 --- a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java @@ -70,6 +70,9 @@ final class HttpComponentsClientHttpRequest extends AbstractBufferingClientHttpR return this.httpRequest.getURI(); } + HttpContext getHttpContext() { + return httpContext; + } @Override protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException { diff --git a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java index 9aabb66a36..5edb555678 100644 --- a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java +++ b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java @@ -113,6 +113,7 @@ public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequest public void setConnectTimeout(int timeout) { Assert.isTrue(timeout >= 0, "Timeout must be a non-negative value"); this.connectTimeout = timeout; + setLegacyConnectionTimeout(getHttpClient(), connectTimeout); } /** @@ -123,6 +124,7 @@ public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequest public void setReadTimeout(int timeout) { Assert.isTrue(timeout >= 0, "Timeout must be a non-negative value"); this.socketTimeout= timeout; + setLegacyReadTimeout(getHttpClient(), socketTimeout); } /** @@ -222,6 +224,44 @@ public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequest return null; } + /** + * Apply the specified custom connection timeout for deprecated {@link HttpClient} + * instances. + *

As from HttpClient 4.3, default parameters have to be set in a + * {@link RequestConfig} instance instead of setting the parameters + * on the client. + *

Unfortunately, this behaviour is not backward compatible and older + * {@link HttpClient} implementations will ignore the {@link RequestConfig} + * object set in the context. + *

If the specified client is an older implementation, we set the + * custom connection timeout through the deprecated API. Otherwise, we just + * return as it is set per request with newer clients + * @param client the client to handle + * @param connectionTimeout the custom connection timeout + */ + @SuppressWarnings("deprecation") + private void setLegacyConnectionTimeout(HttpClient client, int connectionTimeout) { + if (org.apache.http.impl.client.AbstractHttpClient.class.isInstance(client)) { + client.getParams().setIntParameter( + org.apache.http.params.CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout); + } + } + + /** + * Apply the specified read timeout for deprecated {@link HttpClient} + * instances. + * @param client the client to handle + * @param readTimeout the custom read timeout + * @see #setLegacyConnectionTimeout(org.apache.http.client.HttpClient, int) + */ + @SuppressWarnings("deprecation") + private void setLegacyReadTimeout(HttpClient client, int readTimeout) { + if (org.apache.http.impl.client.AbstractHttpClient.class.isInstance(client)) { + client.getParams().setIntParameter( + org.apache.http.params.CoreConnectionPNames.SO_TIMEOUT, readTimeout); + } + } + /** * Shutdown hook that closes the underlying * {@link org.apache.http.conn.HttpClientConnectionManager ClientConnectionManager}'s diff --git a/spring-web/src/test/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactoryTests.java b/spring-web/src/test/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactoryTests.java index 0eabc67d93..5f4442f653 100644 --- a/spring-web/src/test/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactoryTests.java +++ b/spring-web/src/test/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,16 @@ package org.springframework.http.client; +import static org.junit.Assert.*; + +import java.net.URI; + +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.params.CoreConnectionPNames; import org.junit.Test; import org.springframework.http.HttpMethod; @@ -32,4 +42,36 @@ public class HttpComponentsClientHttpRequestFactoryTests extends AbstractHttpReq assertHttpMethod("patch", HttpMethod.PATCH); } + @SuppressWarnings("deprecation") + @Test + public void assertLegacyCustomConfig() { + HttpClient httpClient = new DefaultHttpClient(); // Does not support RequestConfig + HttpComponentsClientHttpRequestFactory hrf = new HttpComponentsClientHttpRequestFactory(httpClient); + hrf.setConnectTimeout(1234); + assertEquals(1234, httpClient.getParams().getIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 0)); + + hrf.setReadTimeout(4567); + assertEquals(4567, httpClient.getParams().getIntParameter(CoreConnectionPNames.SO_TIMEOUT, 0)); + } + + @Test + public void assertCustomConfig() throws Exception { + HttpClient httpClient = HttpClientBuilder.create().build(); + HttpComponentsClientHttpRequestFactory hrf = new HttpComponentsClientHttpRequestFactory(httpClient); + hrf.setConnectTimeout(1234); + hrf.setReadTimeout(4567); + + URI uri = new URI(baseUrl + "/status/ok"); + HttpComponentsClientHttpRequest request = (HttpComponentsClientHttpRequest) + hrf.createRequest(uri, HttpMethod.GET); + + Object config = request.getHttpContext().getAttribute(HttpClientContext.REQUEST_CONFIG); + assertNotNull("Request config should be set", config); + assertTrue("Wrong request config type" + config.getClass().getName(), + RequestConfig.class.isInstance(config)); + RequestConfig requestConfig = (RequestConfig) config; + assertEquals("Wrong custom connection timeout", 1234, requestConfig.getConnectTimeout()); + assertEquals("Wrong custom socket timeout", 4567, requestConfig.getSocketTimeout()); + + } }