/*
 * Copyright 2002-2018 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.
 * 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.springframework.http.client;

import java.io.Closeable;
import java.io.IOException;
import java.net.URI;

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.Configurable;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

ClientHttpRequestFactory implementation that uses Apache HttpComponents HttpClient to create requests.

Allows to use a pre-configured HttpClient instance - potentially with authentication, HTTP connection pooling, etc.

NOTE: Requires Apache HttpComponents 4.3 or higher, as of Spring 4.0.

Author:Oleg Kalnichevski, Arjen Poutsma, Stephane Nicoll, Juergen Hoeller
Since:3.1
/** * {@link org.springframework.http.client.ClientHttpRequestFactory} implementation that * uses <a href="http://hc.apache.org/httpcomponents-client-ga/">Apache HttpComponents * HttpClient</a> to create requests. * * <p>Allows to use a pre-configured {@link HttpClient} instance - * potentially with authentication, HTTP connection pooling, etc. * * <p><b>NOTE:</b> Requires Apache HttpComponents 4.3 or higher, as of Spring 4.0. * * @author Oleg Kalnichevski * @author Arjen Poutsma * @author Stephane Nicoll * @author Juergen Hoeller * @since 3.1 */
public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean { private HttpClient httpClient; @Nullable private RequestConfig requestConfig; private boolean bufferRequestBody = true;
Create a new instance of the HttpComponentsClientHttpRequestFactory with a default HttpClient based on system properties.
/** * Create a new instance of the {@code HttpComponentsClientHttpRequestFactory} * with a default {@link HttpClient} based on system properties. */
public HttpComponentsClientHttpRequestFactory() { this.httpClient = HttpClients.createSystem(); }
Create a new instance of the HttpComponentsClientHttpRequestFactory with the given HttpClient instance.
Params:
  • httpClient – the HttpClient instance to use for this request factory
/** * Create a new instance of the {@code HttpComponentsClientHttpRequestFactory} * with the given {@link HttpClient} instance. * @param httpClient the HttpClient instance to use for this request factory */
public HttpComponentsClientHttpRequestFactory(HttpClient httpClient) { this.httpClient = httpClient; }
Set the HttpClient used for synchronous execution.
/** * Set the {@code HttpClient} used for * {@linkplain #createRequest(URI, HttpMethod) synchronous execution}. */
public void setHttpClient(HttpClient httpClient) { Assert.notNull(httpClient, "HttpClient must not be null"); this.httpClient = httpClient; }
Return the HttpClient used for synchronous execution.
/** * Return the {@code HttpClient} used for * {@linkplain #createRequest(URI, HttpMethod) synchronous execution}. */
public HttpClient getHttpClient() { return this.httpClient; }
Set the connection timeout for the underlying RequestConfig. A timeout value of 0 specifies an infinite timeout.

Additional properties can be configured by specifying a RequestConfig instance on a custom HttpClient.

This options does not affect connection timeouts for SSL handshakes or CONNECT requests; for that, it is required to use the SocketConfig on the HttpClient itself.

Params:
  • timeout – the timeout value in milliseconds
See Also:
/** * Set the connection timeout for the underlying {@link RequestConfig}. * A timeout value of 0 specifies an infinite timeout. * <p>Additional properties can be configured by specifying a * {@link RequestConfig} instance on a custom {@link HttpClient}. * <p>This options does not affect connection timeouts for SSL * handshakes or CONNECT requests; for that, it is required to * use the {@link org.apache.http.config.SocketConfig} on the * {@link HttpClient} itself. * @param timeout the timeout value in milliseconds * @see RequestConfig#getConnectTimeout() * @see org.apache.http.config.SocketConfig#getSoTimeout */
public void setConnectTimeout(int timeout) { Assert.isTrue(timeout >= 0, "Timeout must be a non-negative value"); this.requestConfig = requestConfigBuilder().setConnectTimeout(timeout).build(); }
Set the timeout in milliseconds used when requesting a connection from the connection manager using the underlying RequestConfig. A timeout value of 0 specifies an infinite timeout.

Additional properties can be configured by specifying a RequestConfig instance on a custom HttpClient.

Params:
  • connectionRequestTimeout – the timeout value to request a connection in milliseconds
See Also:
/** * Set the timeout in milliseconds used when requesting a connection * from the connection manager using the underlying {@link RequestConfig}. * A timeout value of 0 specifies an infinite timeout. * <p>Additional properties can be configured by specifying a * {@link RequestConfig} instance on a custom {@link HttpClient}. * @param connectionRequestTimeout the timeout value to request a connection in milliseconds * @see RequestConfig#getConnectionRequestTimeout() */
public void setConnectionRequestTimeout(int connectionRequestTimeout) { this.requestConfig = requestConfigBuilder() .setConnectionRequestTimeout(connectionRequestTimeout).build(); }
Set the socket read timeout for the underlying RequestConfig. A timeout value of 0 specifies an infinite timeout.

Additional properties can be configured by specifying a RequestConfig instance on a custom HttpClient.

Params:
  • timeout – the timeout value in milliseconds
See Also:
/** * Set the socket read timeout for the underlying {@link RequestConfig}. * A timeout value of 0 specifies an infinite timeout. * <p>Additional properties can be configured by specifying a * {@link RequestConfig} instance on a custom {@link HttpClient}. * @param timeout the timeout value in milliseconds * @see RequestConfig#getSocketTimeout() */
public void setReadTimeout(int timeout) { Assert.isTrue(timeout >= 0, "Timeout must be a non-negative value"); this.requestConfig = requestConfigBuilder().setSocketTimeout(timeout).build(); }
Indicates whether this request factory should buffer the request body internally.

Default is true. When sending large amounts of data via POST or PUT, it is recommended to change this property to false, so as not to run out of memory.

Since:4.0
/** * Indicates whether this request factory should buffer the request body internally. * <p>Default is {@code true}. When sending large amounts of data via POST or PUT, it is * recommended to change this property to {@code false}, so as not to run out of memory. * @since 4.0 */
public void setBufferRequestBody(boolean bufferRequestBody) { this.bufferRequestBody = bufferRequestBody; } @Override public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { HttpClient client = getHttpClient(); HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri); postProcessHttpRequest(httpRequest); HttpContext context = createHttpContext(httpMethod, uri); if (context == null) { context = HttpClientContext.create(); } // Request configuration not set in the context if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) { // Use request configuration given by the user, when available RequestConfig config = null; if (httpRequest instanceof Configurable) { config = ((Configurable) httpRequest).getConfig(); } if (config == null) { config = createRequestConfig(client); } if (config != null) { context.setAttribute(HttpClientContext.REQUEST_CONFIG, config); } } if (this.bufferRequestBody) { return new HttpComponentsClientHttpRequest(client, httpRequest, context); } else { return new HttpComponentsStreamingClientHttpRequest(client, httpRequest, context); } }
Return a builder for modifying the factory-level RequestConfig.
Since:4.2
/** * Return a builder for modifying the factory-level {@link RequestConfig}. * @since 4.2 */
private RequestConfig.Builder requestConfigBuilder() { return (this.requestConfig != null ? RequestConfig.copy(this.requestConfig) : RequestConfig.custom()); }
Create a default RequestConfig to use with the given client. Can return null to indicate that no custom request config should be set and the defaults of the HttpClient should be used.

The default implementation tries to merge the defaults of the client with the local customizations of this factory instance, if any.

Params:
  • client – the HttpClient (or HttpAsyncClient) to check
See Also:
Returns:the actual RequestConfig to use (may be null)
Since:4.2
/** * Create a default {@link RequestConfig} to use with the given client. * Can return {@code null} to indicate that no custom request config should * be set and the defaults of the {@link HttpClient} should be used. * <p>The default implementation tries to merge the defaults of the client * with the local customizations of this factory instance, if any. * @param client the {@link HttpClient} (or {@code HttpAsyncClient}) to check * @return the actual RequestConfig to use (may be {@code null}) * @since 4.2 * @see #mergeRequestConfig(RequestConfig) */
@Nullable protected RequestConfig createRequestConfig(Object client) { if (client instanceof Configurable) { RequestConfig clientRequestConfig = ((Configurable) client).getConfig(); return mergeRequestConfig(clientRequestConfig); } return this.requestConfig; }
Merge the given HttpClient-level RequestConfig with the factory-level RequestConfig, if necessary.
Params:
  • clientConfig – the config held by the current
Returns:the merged request config
Since:4.2
/** * Merge the given {@link HttpClient}-level {@link RequestConfig} with * the factory-level {@link RequestConfig}, if necessary. * @param clientConfig the config held by the current * @return the merged request config * @since 4.2 */
protected RequestConfig mergeRequestConfig(RequestConfig clientConfig) { if (this.requestConfig == null) { // nothing to merge return clientConfig; } RequestConfig.Builder builder = RequestConfig.copy(clientConfig); int connectTimeout = this.requestConfig.getConnectTimeout(); if (connectTimeout >= 0) { builder.setConnectTimeout(connectTimeout); } int connectionRequestTimeout = this.requestConfig.getConnectionRequestTimeout(); if (connectionRequestTimeout >= 0) { builder.setConnectionRequestTimeout(connectionRequestTimeout); } int socketTimeout = this.requestConfig.getSocketTimeout(); if (socketTimeout >= 0) { builder.setSocketTimeout(socketTimeout); } return builder.build(); }
Create a Commons HttpMethodBase object for the given HTTP method and URI specification.
Params:
  • httpMethod – the HTTP method
  • uri – the URI
Returns:the Commons HttpMethodBase object
/** * Create a Commons HttpMethodBase object for the given HTTP method and URI specification. * @param httpMethod the HTTP method * @param uri the URI * @return the Commons HttpMethodBase object */
protected HttpUriRequest createHttpUriRequest(HttpMethod httpMethod, URI uri) { switch (httpMethod) { case GET: return new HttpGet(uri); case HEAD: return new HttpHead(uri); case POST: return new HttpPost(uri); case PUT: return new HttpPut(uri); case PATCH: return new HttpPatch(uri); case DELETE: return new HttpDelete(uri); case OPTIONS: return new HttpOptions(uri); case TRACE: return new HttpTrace(uri); default: throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod); } }
Template method that allows for manipulating the HttpUriRequest before it is returned as part of a HttpComponentsClientHttpRequest.

The default implementation is empty.

Params:
  • request – the request to process
/** * Template method that allows for manipulating the {@link HttpUriRequest} before it is * returned as part of a {@link HttpComponentsClientHttpRequest}. * <p>The default implementation is empty. * @param request the request to process */
protected void postProcessHttpRequest(HttpUriRequest request) { }
Template methods that creates a HttpContext for the given HTTP method and URI.

The default implementation returns null.

Params:
  • httpMethod – the HTTP method
  • uri – the URI
Returns:the http context
/** * Template methods that creates a {@link HttpContext} for the given HTTP method and URI. * <p>The default implementation returns {@code null}. * @param httpMethod the HTTP method * @param uri the URI * @return the http context */
@Nullable protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) { return null; }
Shutdown hook that closes the underlying ClientConnectionManager's connection pool, if any.
/** * Shutdown hook that closes the underlying * {@link org.apache.http.conn.HttpClientConnectionManager ClientConnectionManager}'s * connection pool, if any. */
@Override public void destroy() throws Exception { HttpClient httpClient = getHttpClient(); if (httpClient instanceof Closeable) { ((Closeable) httpClient).close(); } }
An alternative to HttpDelete that extends HttpEntityEnclosingRequestBase rather than HttpRequestBase and hence allows HTTP delete with a request body. For use with the RestTemplate exchange methods which allow the combination of HTTP DELETE with an entity.
Since:4.1.2
/** * An alternative to {@link org.apache.http.client.methods.HttpDelete} that * extends {@link org.apache.http.client.methods.HttpEntityEnclosingRequestBase} * rather than {@link org.apache.http.client.methods.HttpRequestBase} and * hence allows HTTP delete with a request body. For use with the RestTemplate * exchange methods which allow the combination of HTTP DELETE with an entity. * @since 4.1.2 */
private static class HttpDelete extends HttpEntityEnclosingRequestBase { public HttpDelete(URI uri) { super(); setURI(uri); } @Override public String getMethod() { return "DELETE"; } } }