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
This commit is contained in:
Stephane Nicoll 2014-03-04 16:53:04 +01:00
parent bea94d5302
commit 7a6ec69523
3 changed files with 86 additions and 1 deletions

View File

@ -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 {

View File

@ -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.
* <p>As from HttpClient 4.3, default parameters have to be set in a
* {@link RequestConfig} instance instead of setting the parameters
* on the client.
* <p>Unfortunately, this behaviour is not backward compatible and older
* {@link HttpClient} implementations will ignore the {@link RequestConfig}
* object set in the context.
* <p>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

View File

@ -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());
}
}