2009-07-08 15:07:17 -04:00
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
|
|
|
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
|
|
|
<!--
|
|
|
|
====================================================================
|
|
|
|
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.
|
|
|
|
====================================================================
|
|
|
|
-->
|
2009-07-09 15:30:05 -04:00
|
|
|
<chapter id="connmgmt">
|
2009-07-08 15:07:17 -04:00
|
|
|
<title>Connection management</title>
|
|
|
|
<section>
|
|
|
|
<title>Connection persistence</title>
|
|
|
|
<para>The process of establishing a connection from one host to another is quite complex and
|
|
|
|
involves multiple packet exchanges between two endpoints, which can be quite time
|
|
|
|
consuming. The overhead of connection handshaking can be significant, especially for
|
|
|
|
small HTTP messages. One can achieve a much higher data throughput if open connections
|
|
|
|
can be re-used to execute multiple requests.</para>
|
|
|
|
<para>HTTP/1.1 states that HTTP connections can be re-used for multiple requests per
|
2010-12-01 12:39:22 -05:00
|
|
|
default. HTTP/1.0 compliant endpoints can also use a mechanism to explicitly
|
2009-07-08 15:07:17 -04:00
|
|
|
communicate their preference to keep connection alive and use it for multiple requests.
|
|
|
|
HTTP agents can also keep idle connections alive for a certain period time in case a
|
2010-12-01 12:39:22 -05:00
|
|
|
connection to the same target host is needed for subsequent requests. The ability to
|
2009-07-08 15:07:17 -04:00
|
|
|
keep connections alive is usually refered to as connection persistence. HttpClient fully
|
|
|
|
supports connection persistence.</para>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>HTTP connection routing</title>
|
|
|
|
<para>HttpClient is capable of establishing connections to the target host either directly
|
2010-12-01 12:39:22 -05:00
|
|
|
or via a route that may involve multiple intermediate connections - also referred to as
|
2009-07-08 15:07:17 -04:00
|
|
|
hops. HttpClient differentiates connections of a route into plain, tunneled and layered.
|
|
|
|
The use of multiple intermediate proxies to tunnel connections to the target host is
|
|
|
|
referred to as proxy chaining.</para>
|
|
|
|
<para>Plain routes are established by connecting to the target or the first and only proxy.
|
|
|
|
Tunnelled routes are established by connecting to the first and tunnelling through a
|
|
|
|
chain of proxies to the target. Routes without a proxy cannot be tunnelled. Layered
|
|
|
|
routes are established by layering a protocol over an existing connection. Protocols can
|
|
|
|
only be layered over a tunnel to the target, or over a direct connection without
|
|
|
|
proxies.</para>
|
|
|
|
<section>
|
|
|
|
<title>Route computation</title>
|
2013-08-13 14:15:16 -04:00
|
|
|
<para>The <interfacename>RouteInfo</interfacename> interface represents information
|
|
|
|
about a definitive route to a target host involving one or more intermediate steps
|
|
|
|
or hops. <classname>HttpRoute</classname> is a concrete implementation of
|
2010-12-01 12:39:22 -05:00
|
|
|
the <interfacename>RouteInfo</interfacename>, which cannot be changed (is
|
2009-07-08 15:07:17 -04:00
|
|
|
immutable). <classname>HttpTracker</classname> is a mutable
|
2013-08-13 14:15:16 -04:00
|
|
|
<interfacename>RouteInfo</interfacename> implementation used internally by
|
2009-07-08 15:07:17 -04:00
|
|
|
HttpClient to track the remaining hops to the ultimate route target.
|
2013-08-13 14:15:16 -04:00
|
|
|
<classname>HttpTracker</classname> can be updated after a successful execution
|
2009-07-08 15:07:17 -04:00
|
|
|
of the next hop towards the route target. <classname>HttpRouteDirector</classname>
|
|
|
|
is a helper class that can be used to compute the next step in a route. This class
|
|
|
|
is used internally by HttpClient.</para>
|
|
|
|
<para><interfacename>HttpRoutePlanner</interfacename> is an interface representing a
|
|
|
|
strategy to compute a complete route to a given target based on the execution
|
|
|
|
context. HttpClient ships with two default
|
2013-08-13 14:15:16 -04:00
|
|
|
<interfacename>HttpRoutePlanner</interfacename> implementations.
|
|
|
|
<classname>SystemDefaultRoutePlanner</classname> is based on
|
|
|
|
<classname>java.net.ProxySelector</classname>. By default, it will pick up the
|
2009-07-08 15:07:17 -04:00
|
|
|
proxy settings of the JVM, either from system properties or from the browser running
|
2013-08-13 14:15:16 -04:00
|
|
|
the application. The <classname>DefaultProxyRoutePlanner</classname> implementation
|
|
|
|
does not make use of any Java system properties, nor any system or browser proxy
|
|
|
|
settings. It always computes routes via the same default proxy.</para>
|
2009-07-08 15:07:17 -04:00
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>Secure HTTP connections</title>
|
|
|
|
<para>HTTP connections can be considered secure if information transmitted between two
|
|
|
|
connection endpoints cannot be read or tampered with by an unauthorized third party.
|
|
|
|
The SSL/TLS protocol is the most widely used technique to ensure HTTP transport
|
|
|
|
security. However, other encryption techniques could be employed as well. Usually,
|
|
|
|
HTTP transport is layered over the SSL/TLS encrypted connection.</para>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>HTTP connection managers</title>
|
|
|
|
<section>
|
|
|
|
<title>Managed connections and connection managers</title>
|
|
|
|
<para>HTTP connections are complex, stateful, thread-unsafe objects which need to be
|
|
|
|
properly managed to function correctly. HTTP connections can only be used by one
|
|
|
|
execution thread at a time. HttpClient employs a special entity to manage access to
|
|
|
|
HTTP connections called HTTP connection manager and represented by the
|
2013-08-13 14:15:16 -04:00
|
|
|
<interfacename>HttpClientConnectionManager</interfacename> interface. The purpose of
|
|
|
|
an HTTP connection manager is to serve as a factory for new HTTP connections,
|
|
|
|
to manage life cycle of persistent connections and to synchronize access to
|
|
|
|
persistent connections making sure that only one thread can have access
|
|
|
|
to a connection at a time. Internally HTTP connection managers work with instances
|
|
|
|
of <interfacename>ManagedHttpClientConnection</interfacename> acting as a proxy
|
|
|
|
for a real connection that manages connection state and controls execution
|
|
|
|
of I/O operations. If a managed connection is released or get explicitly closed
|
2013-10-11 03:58:00 -04:00
|
|
|
by its consumer the underlying connection gets detached from its proxy and is
|
2013-08-13 14:15:16 -04:00
|
|
|
returned back to the manager. Even though the service consumer still holds
|
|
|
|
a reference to the proxy instance, it is no longer able to execute any
|
|
|
|
I/O operations or change the state of the real connection either intentionally
|
|
|
|
or unintentionally.</para>
|
2009-07-08 15:07:17 -04:00
|
|
|
<para>This is an example of acquiring a connection from a connection manager:</para>
|
|
|
|
<programlisting><![CDATA[
|
2013-08-13 14:15:16 -04:00
|
|
|
HttpClientContext context = HttpClientContext.create();
|
|
|
|
HttpClientConnectionManager connMrg = new BasicHttpClientConnectionManager();
|
|
|
|
HttpRoute route = new HttpRoute(new HttpHost("localhost", 80));
|
2009-07-08 15:07:17 -04:00
|
|
|
// Request new connection. This can be a long process
|
2013-08-13 14:15:16 -04:00
|
|
|
ConnectionRequest connRequest = connMrg.requestConnection(route, null);
|
2009-07-08 15:07:17 -04:00
|
|
|
// Wait for connection up to 10 sec
|
2013-08-13 14:15:16 -04:00
|
|
|
HttpClientConnection conn = connRequest.get(10, TimeUnit.SECONDS);
|
2009-07-08 15:07:17 -04:00
|
|
|
try {
|
2013-08-13 14:15:16 -04:00
|
|
|
// If not open
|
|
|
|
if (!conn.isOpen()) {
|
|
|
|
// establish connection based on its route info
|
|
|
|
connMrg.connect(conn, route, 1000, context);
|
|
|
|
// and mark it as route complete
|
|
|
|
connMrg.routeComplete(conn, route, context);
|
|
|
|
}
|
2009-07-08 15:07:17 -04:00
|
|
|
// Do useful things with the connection.
|
2013-08-13 14:15:16 -04:00
|
|
|
} finally {
|
|
|
|
connMrg.releaseConnection(conn, null, 1, TimeUnit.MINUTES);
|
2009-07-08 15:07:17 -04:00
|
|
|
}
|
|
|
|
]]></programlisting>
|
|
|
|
<para>The connection request can be terminated prematurely by calling
|
2013-08-13 14:15:16 -04:00
|
|
|
<methodname>ConnectionRequest#cancel()</methodname> if necessary. This will unblock
|
|
|
|
the thread blocked in the <methodname>ConnectionRequest#get()</methodname>
|
|
|
|
method.</para>
|
2009-07-08 15:07:17 -04:00
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>Simple connection manager</title>
|
2013-08-13 14:15:16 -04:00
|
|
|
<para><classname>BasicHttpClientConnectionManager</classname> is a simple connection
|
|
|
|
manager that maintains only one connection at a time. Even though this class
|
|
|
|
is thread-safe it ought to be used by one execution thread only.
|
|
|
|
<classname>BasicHttpClientConnectionManager</classname> will make an effort to reuse
|
2012-02-04 11:30:30 -05:00
|
|
|
the connection for subsequent requests with the same route. It will, however, close
|
|
|
|
the existing connection and re-open it for the given route, if the route of the
|
|
|
|
persistent connection does not match that of the connection request.
|
|
|
|
If the connection has been already been allocated, then <exceptionname>
|
|
|
|
java.lang.IllegalStateException</exceptionname> is thrown.</para>
|
2013-08-13 14:15:16 -04:00
|
|
|
<para>This connection manager implementation should be used inside an EJB
|
|
|
|
container.</para>
|
2009-07-08 15:07:17 -04:00
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>Pooling connection manager</title>
|
2013-08-13 14:15:16 -04:00
|
|
|
<para><classname>PoolingHttpClientConnectionManager</classname> is a more complex
|
2009-07-08 15:07:17 -04:00
|
|
|
implementation that manages a pool of client connections and is able to service
|
|
|
|
connection requests from multiple execution threads. Connections are pooled on a per
|
2010-12-01 12:39:22 -05:00
|
|
|
route basis. A request for a route for which the manager already has a persistent
|
|
|
|
connection available in the pool will be serviced by leasing a connection from
|
2009-07-08 15:07:17 -04:00
|
|
|
the pool rather than creating a brand new connection.</para>
|
2013-08-13 14:15:16 -04:00
|
|
|
<para><classname>PoolingHttpClientConnectionManager</classname> maintains a maximum
|
|
|
|
limit of connections on a per route basis and in total. Per default this
|
|
|
|
implementation will create no more than 2 concurrent connections per given route
|
|
|
|
and no more 20 connections in total. For many real-world applications these limits
|
|
|
|
may prove too constraining, especially if they use HTTP as a transport protocol for
|
|
|
|
their services.</para>
|
2009-07-08 15:07:17 -04:00
|
|
|
<para>This example shows how the connection pool parameters can be adjusted:</para>
|
|
|
|
<programlisting><![CDATA[
|
2013-08-13 14:15:16 -04:00
|
|
|
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
|
2009-10-02 07:33:00 -04:00
|
|
|
// Increase max total connection to 200
|
2012-02-04 11:30:30 -05:00
|
|
|
cm.setMaxTotal(200);
|
2009-10-02 07:33:00 -04:00
|
|
|
// Increase default max connection per route to 20
|
|
|
|
cm.setDefaultMaxPerRoute(20);
|
|
|
|
// Increase max connections for localhost:80 to 50
|
|
|
|
HttpHost localhost = new HttpHost("locahost", 80);
|
2012-02-04 11:30:30 -05:00
|
|
|
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
|
2013-08-13 14:15:16 -04:00
|
|
|
|
|
|
|
CloseableHttpClient httpClient = HttpClients.custom()
|
|
|
|
.setConnectionManager(cm)
|
|
|
|
.build();
|
2009-07-08 15:07:17 -04:00
|
|
|
]]></programlisting>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>Connection manager shutdown</title>
|
|
|
|
<para>When an HttpClient instance is no longer needed and is about to go out of scope it
|
|
|
|
is important to shut down its connection manager to ensure that all connections kept
|
|
|
|
alive by the manager get closed and system resources allocated by those connections
|
|
|
|
are released.</para>
|
|
|
|
<programlisting><![CDATA[
|
2013-08-13 14:15:16 -04:00
|
|
|
CloseableHttpClient httpClient = <...>
|
|
|
|
httpClient.close();
|
2009-07-08 15:07:17 -04:00
|
|
|
]]></programlisting>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>Multithreaded request execution</title>
|
2012-02-04 11:30:30 -05:00
|
|
|
<para>When equipped with a pooling connection manager such as <classname>
|
|
|
|
PoolingClientConnectionManager</classname>, HttpClient can be used to execute multiple
|
|
|
|
requests simultaneously using multiple threads of execution.</para>
|
|
|
|
<para>The <classname>PoolingClientConnectionManager</classname> will allocate connections
|
|
|
|
based on its configuration. If all connections for a given route have already been
|
|
|
|
leased, a request for a connection will block until a connection is released back to
|
|
|
|
the pool. One can ensure the connection manager does not block indefinitely in the
|
|
|
|
connection request operation by setting <literal>'http.conn-manager.timeout'</literal>
|
|
|
|
to a positive value. If the connection request cannot be serviced within the given time
|
|
|
|
period <exceptionname>ConnectionPoolTimeoutException</exceptionname> will be thrown.
|
|
|
|
</para>
|
2009-07-08 15:07:17 -04:00
|
|
|
<programlisting><![CDATA[
|
2013-08-13 14:15:16 -04:00
|
|
|
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
|
|
|
|
CloseableHttpClient httpClient = HttpClients.custom()
|
|
|
|
.setConnectionManager(cm)
|
|
|
|
.build();
|
2009-07-08 15:07:17 -04:00
|
|
|
|
|
|
|
// URIs to perform GETs on
|
|
|
|
String[] urisToGet = {
|
|
|
|
"http://www.domain1.com/",
|
|
|
|
"http://www.domain2.com/",
|
|
|
|
"http://www.domain3.com/",
|
|
|
|
"http://www.domain4.com/"
|
|
|
|
};
|
|
|
|
|
|
|
|
// create a thread for each URI
|
|
|
|
GetThread[] threads = new GetThread[urisToGet.length];
|
|
|
|
for (int i = 0; i < threads.length; i++) {
|
|
|
|
HttpGet httpget = new HttpGet(urisToGet[i]);
|
|
|
|
threads[i] = new GetThread(httpClient, httpget);
|
|
|
|
}
|
|
|
|
|
|
|
|
// start the threads
|
|
|
|
for (int j = 0; j < threads.length; j++) {
|
|
|
|
threads[j].start();
|
|
|
|
}
|
|
|
|
|
|
|
|
// join the threads
|
|
|
|
for (int j = 0; j < threads.length; j++) {
|
|
|
|
threads[j].join();
|
|
|
|
}
|
|
|
|
|
|
|
|
]]></programlisting>
|
2012-03-03 08:32:12 -05:00
|
|
|
<para>While <interfacename>HttpClient</interfacename> instances are thread safe and can be
|
|
|
|
shared between multiple threads of execution, it is highly recommended that each
|
|
|
|
thread maintains its own dedicated instance of <interfacename>HttpContext
|
|
|
|
</interfacename>.</para>
|
2009-07-08 15:07:17 -04:00
|
|
|
<programlisting><![CDATA[
|
|
|
|
static class GetThread extends Thread {
|
2013-08-13 14:15:16 -04:00
|
|
|
|
|
|
|
private final CloseableHttpClient httpClient;
|
2009-07-08 15:07:17 -04:00
|
|
|
private final HttpContext context;
|
|
|
|
private final HttpGet httpget;
|
2013-08-13 14:15:16 -04:00
|
|
|
|
|
|
|
public GetThread(CloseableHttpClient httpClient, HttpGet httpget) {
|
2009-07-08 15:07:17 -04:00
|
|
|
this.httpClient = httpClient;
|
2013-08-13 14:15:16 -04:00
|
|
|
this.context = HttpClientContext.create();
|
2009-07-08 15:07:17 -04:00
|
|
|
this.httpget = httpget;
|
|
|
|
}
|
2013-08-13 14:15:16 -04:00
|
|
|
|
2009-07-08 15:07:17 -04:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
try {
|
2013-08-13 14:15:16 -04:00
|
|
|
CloseableHttpResponse response = httpClient.execute(
|
|
|
|
httpget, context);
|
|
|
|
try {
|
|
|
|
HttpEntity entity = response.getEntity();
|
|
|
|
} finally {
|
|
|
|
response.close();
|
2009-07-08 15:07:17 -04:00
|
|
|
}
|
2013-08-13 14:15:16 -04:00
|
|
|
} catch (ClientProtocolException ex) {
|
|
|
|
// Handle protocol errors
|
|
|
|
} catch (IOException ex) {
|
|
|
|
// Handle I/O errors
|
2009-07-08 15:07:17 -04:00
|
|
|
}
|
|
|
|
}
|
2013-08-13 14:15:16 -04:00
|
|
|
|
2009-07-08 15:07:17 -04:00
|
|
|
}
|
|
|
|
]]></programlisting>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>Connection eviction policy</title>
|
2010-12-01 12:39:22 -05:00
|
|
|
<para>One of the major shortcomings of the classic blocking I/O model is that the network
|
2009-07-08 15:07:17 -04:00
|
|
|
socket can react to I/O events only when blocked in an I/O operation. When a connection
|
|
|
|
is released back to the manager, it can be kept alive however it is unable to monitor
|
|
|
|
the status of the socket and react to any I/O events. If the connection gets closed on
|
|
|
|
the server side, the client side connection is unable to detect the change in the
|
2010-12-01 12:39:22 -05:00
|
|
|
connection state (and react appropriately by closing the socket on its end).</para>
|
2009-07-08 15:07:17 -04:00
|
|
|
<para>HttpClient tries to mitigate the problem by testing whether the connection is 'stale',
|
|
|
|
that is no longer valid because it was closed on the server side, prior to using the
|
|
|
|
connection for executing an HTTP request. The stale connection check is not 100%
|
2015-01-15 11:28:53 -05:00
|
|
|
reliable. The only feasible solution that does not involve a one thread per socket
|
|
|
|
model for idle connections is a dedicated monitor thread used to evict connections
|
|
|
|
that are considered expired due to a long period of inactivity. The monitor thread can
|
|
|
|
periodically call
|
|
|
|
<methodname>ClientConnectionManager#closeExpiredConnections()</methodname> method to
|
2009-07-08 15:07:17 -04:00
|
|
|
close all expired connections and evict closed connections from the pool. It can also
|
|
|
|
optionally call <methodname>ClientConnectionManager#closeIdleConnections()</methodname>
|
|
|
|
method to close all connections that have been idle over a given period of time.</para>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
public static class IdleConnectionMonitorThread extends Thread {
|
|
|
|
|
2013-08-13 14:15:16 -04:00
|
|
|
private final HttpClientConnectionManager connMgr;
|
2009-07-08 15:07:17 -04:00
|
|
|
private volatile boolean shutdown;
|
|
|
|
|
2013-08-13 14:15:16 -04:00
|
|
|
public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
|
2009-07-08 15:07:17 -04:00
|
|
|
super();
|
|
|
|
this.connMgr = connMgr;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
try {
|
|
|
|
while (!shutdown) {
|
|
|
|
synchronized (this) {
|
|
|
|
wait(5000);
|
|
|
|
// Close expired connections
|
|
|
|
connMgr.closeExpiredConnections();
|
|
|
|
// Optionally, close connections
|
|
|
|
// that have been idle longer than 30 sec
|
|
|
|
connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (InterruptedException ex) {
|
|
|
|
// terminate
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void shutdown() {
|
|
|
|
shutdown = true;
|
|
|
|
synchronized (this) {
|
|
|
|
notifyAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
]]></programlisting>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>Connection keep alive strategy</title>
|
|
|
|
<para>The HTTP specification does not specify how long a persistent connection may be and
|
2010-12-01 12:39:22 -05:00
|
|
|
should be kept alive. Some HTTP servers use a non-standard <literal>Keep-Alive</literal>
|
2009-07-08 15:07:17 -04:00
|
|
|
header to communicate to the client the period of time in seconds they intend to keep
|
|
|
|
the connection alive on the server side. HttpClient makes use of this information if
|
|
|
|
available. If the <literal>Keep-Alive</literal> header is not present in the response,
|
|
|
|
HttpClient assumes the connection can be kept alive indefinitely. However, many HTTP
|
2010-12-01 12:39:22 -05:00
|
|
|
servers in general use are configured to drop persistent connections after a certain period
|
2009-07-08 15:07:17 -04:00
|
|
|
of inactivity in order to conserve system resources, quite often without informing the
|
|
|
|
client. In case the default strategy turns out to be too optimistic, one may want to
|
|
|
|
provide a custom keep-alive strategy.</para>
|
|
|
|
<programlisting><![CDATA[
|
2013-08-13 14:15:16 -04:00
|
|
|
ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
|
2009-07-08 15:07:17 -04:00
|
|
|
|
|
|
|
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
|
|
|
|
// Honor 'keep-alive' header
|
|
|
|
HeaderElementIterator it = new BasicHeaderElementIterator(
|
|
|
|
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
|
|
|
|
while (it.hasNext()) {
|
|
|
|
HeaderElement he = it.nextElement();
|
2013-08-13 14:15:16 -04:00
|
|
|
String param = he.getName();
|
2009-07-08 15:07:17 -04:00
|
|
|
String value = he.getValue();
|
|
|
|
if (value != null && param.equalsIgnoreCase("timeout")) {
|
|
|
|
try {
|
|
|
|
return Long.parseLong(value) * 1000;
|
|
|
|
} catch(NumberFormatException ignore) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
HttpHost target = (HttpHost) context.getAttribute(
|
2013-08-13 14:15:16 -04:00
|
|
|
HttpClientContext.HTTP_TARGET_HOST);
|
2009-07-08 15:07:17 -04:00
|
|
|
if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) {
|
|
|
|
// Keep alive for 5 seconds only
|
|
|
|
return 5 * 1000;
|
|
|
|
} else {
|
|
|
|
// otherwise keep alive for 30 seconds
|
|
|
|
return 30 * 1000;
|
|
|
|
}
|
|
|
|
}
|
2013-08-13 14:15:16 -04:00
|
|
|
|
|
|
|
};
|
|
|
|
CloseableHttpClient client = HttpClients.custom()
|
|
|
|
.setKeepAliveStrategy(myStrategy)
|
|
|
|
.build();
|
|
|
|
]]></programlisting>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>Connection socket factories</title>
|
|
|
|
<para>HTTP connections make use of a <classname>java.net.Socket</classname> object
|
|
|
|
internally to handle transmission of data across the wire. However they rely on
|
|
|
|
the <interfacename>ConnectionSocketFactory</interfacename> interface to create,
|
|
|
|
initialize and connect sockets. This enables the users of HttpClient to provide
|
|
|
|
application specific socket initialization code at runtime. <classname>
|
|
|
|
PlainConnectionSocketFactory</classname> is the default factory for creating and
|
|
|
|
initializing plain (unencrypted) sockets.</para>
|
|
|
|
<para>The process of creating a socket and that of connecting it to a host are decoupled, so
|
|
|
|
that the socket could be closed while being blocked in the connect operation.</para>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
HttpClientContext clientContext = HttpClientContext.create();
|
|
|
|
PlainConnectionSocketFactory sf = PlainConnectionSocketFactory.getSocketFactory();
|
|
|
|
Socket socket = sf.createSocket(clientContext);
|
|
|
|
int timeout = 1000; //ms
|
|
|
|
HttpHost target = new HttpHost("localhost");
|
|
|
|
InetSocketAddress remoteAddress = new InetSocketAddress(
|
|
|
|
InetAddress.getByAddress(new byte[] {127,0,0,1}), 80);
|
|
|
|
sf.connectSocket(timeout, socket, target, remoteAddress, null, clientContext);
|
|
|
|
]]></programlisting>
|
|
|
|
<section>
|
|
|
|
<title>Secure socket layering</title>
|
|
|
|
<para><interfacename>LayeredConnectionSocketFactory</interfacename> is an extension of
|
|
|
|
the <interfacename>ConnectionSocketFactory</interfacename> interface. Layered socket
|
|
|
|
factories are capable of creating sockets layered over an existing plain socket.
|
|
|
|
Socket layering is used primarily for creating secure sockets through proxies.
|
|
|
|
HttpClient ships with <classname>SSLSocketFactory</classname> that implements
|
|
|
|
SSL/TLS layering. Please note HttpClient does not use any custom encryption
|
|
|
|
functionality. It is fully reliant on standard Java Cryptography (JCE) and Secure
|
|
|
|
Sockets (JSEE) extensions.</para>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>Integration with connection manager</title>
|
|
|
|
<para>Custom connection socket factories can be associated with a particular
|
|
|
|
protocol scheme as as HTTP or HTTPS and then used to create a custom connection
|
|
|
|
manager.</para>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
ConnectionSocketFactory plainsf = <...>
|
|
|
|
LayeredConnectionSocketFactory sslsf = <...>
|
2013-08-14 14:41:58 -04:00
|
|
|
Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
|
2013-08-13 14:15:16 -04:00
|
|
|
.register("http", plainsf)
|
|
|
|
.register("https", sslsf)
|
|
|
|
.build();
|
|
|
|
|
2013-08-14 14:41:58 -04:00
|
|
|
HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(r);
|
2013-08-13 14:15:16 -04:00
|
|
|
HttpClients.custom()
|
|
|
|
.setConnectionManager(cm)
|
|
|
|
.build();
|
|
|
|
]]></programlisting>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>SSL/TLS customization</title>
|
2014-02-28 07:27:20 -05:00
|
|
|
<para>HttpClient makes use of <classname>SSLConnectionSocketFactory</classname>
|
|
|
|
to create SSL connections. <classname>SSLConnectionSocketFactory</classname> allows
|
|
|
|
for a high degree of customization. It can take an instance of
|
2013-08-13 14:15:16 -04:00
|
|
|
<interfacename>javax.net.ssl.SSLContext</interfacename> as a parameter and use
|
|
|
|
it to create custom configured SSL connections.</para>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
KeyStore myTrustStore = <...>
|
|
|
|
SSLContext sslContext = SSLContexts.custom()
|
|
|
|
.useTLS()
|
|
|
|
.loadTrustMaterial(myTrustStore)
|
|
|
|
.build();
|
|
|
|
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
|
|
|
|
]]></programlisting>
|
2014-02-28 07:27:20 -05:00
|
|
|
<para>Customization of <classname>SSLConnectionSocketFactory</classname> implies
|
|
|
|
a certain degree of familiarity with the concepts of the SSL/TLS protocol,
|
|
|
|
a detailed explanation of which is out of scope for this document. Please refer
|
|
|
|
to the <ulink
|
|
|
|
url="http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
|
|
|
|
Java Secure Socket Extension</ulink> for a detailed description of
|
2013-08-13 14:15:16 -04:00
|
|
|
<interfacename>javax.net.ssl.SSLContext</interfacename> and related
|
|
|
|
tools.</para>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>Hostname verification</title>
|
|
|
|
<para>In addition to the trust verification and the client authentication performed on
|
|
|
|
the SSL/TLS protocol level, HttpClient can optionally verify whether the target
|
|
|
|
hostname matches the names stored inside the server's X.509 certificate, once the
|
|
|
|
connection has been established. This verification can provide additional guarantees
|
|
|
|
of authenticity of the server trust material.
|
2015-01-15 11:28:53 -05:00
|
|
|
The <interfacename>javax.net.ssl.HostnameVerifier</interfacename> interface
|
|
|
|
represents a strategy for hostname verification. HttpClient ships with two
|
|
|
|
<interfacename>javax.net.ssl.HostnameVerifier</interfacename> implementations.
|
2013-08-13 14:15:16 -04:00
|
|
|
Important: hostname verification should not be confused with
|
|
|
|
SSL trust verification.</para>
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<formalpara>
|
2015-01-15 11:28:53 -05:00
|
|
|
<title><classname>DefaultHostnameVerifier</classname>:</title>
|
|
|
|
<para>The default implementation used by HttpClient is expected to be
|
|
|
|
compliant with RFC 2818. The hostname must match any of alternative
|
|
|
|
names specified by the certificate, or in case no alternative
|
|
|
|
names are given the most specific CN of the certificate subject. A
|
2013-08-13 14:15:16 -04:00
|
|
|
wildcard can occur in the CN, and in any of the subject-alts.</para>
|
|
|
|
</formalpara>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<formalpara>
|
2015-01-15 11:28:53 -05:00
|
|
|
<title><classname>NoopHostnameVerifier</classname>:</title>
|
2013-08-13 14:15:16 -04:00
|
|
|
<para>This hostname verifier essentially turns hostname verification off.
|
2015-01-15 11:28:53 -05:00
|
|
|
It accepts any SSL session as valid and matching the target host.
|
|
|
|
</para>
|
2013-08-13 14:15:16 -04:00
|
|
|
</formalpara>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
2015-01-15 11:28:53 -05:00
|
|
|
<para>Per default HttpClient uses the <classname>DefaultHostnameVerifier</classname>
|
2013-08-13 14:15:16 -04:00
|
|
|
implementation. One can specify a different hostname verifier implementation if
|
|
|
|
desired</para>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
SSLContext sslContext = SSLContexts.createSystemDefault();
|
|
|
|
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
|
|
|
|
sslContext,
|
2015-01-15 11:28:53 -05:00
|
|
|
NoopHostnameVerifier.INSTANCE);
|
|
|
|
]]></programlisting>
|
|
|
|
<para>As of version 4.4 HttpClient makes use the public suffix list kindly maintained
|
|
|
|
by Mozilla Foundation to make sure that wildcards in SSL certificates cannot be
|
|
|
|
misused to apply to multiple domains with a common top-level domain. HttpClient
|
|
|
|
ships with a copy of the list retrieved at the time of the release. The latest
|
|
|
|
revision of the list can found at
|
|
|
|
<ulink url="https://publicsuffix.org/list/effective_tld_names.dat">
|
|
|
|
https://publicsuffix.org/list/</ulink>. It is highly adviseable to make a local
|
|
|
|
copy of the lsit and download the list no more than once per day from its original
|
|
|
|
location.
|
|
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(
|
|
|
|
PublicSuffixMatcher.class.getResource("my-copy-effective_tld_names.dat"));
|
|
|
|
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
|
|
|
|
]]></programlisting>
|
|
|
|
<para>One can disable verification against the public suffic list by using
|
|
|
|
<code>null</code> matcher.
|
|
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(null);
|
2013-08-13 14:15:16 -04:00
|
|
|
]]></programlisting>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>HttpClient proxy configuration</title>
|
2015-01-15 11:28:53 -05:00
|
|
|
<para>Even though HttpClient is aware of complex routing schemes and proxy chaining, it
|
2013-08-13 14:15:16 -04:00
|
|
|
supports only simple direct or one hop proxy connections out of the box.</para>
|
|
|
|
<para>The simplest way to tell HttpClient to connect to the target host via a proxy is by
|
|
|
|
setting the default proxy parameter:</para>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
HttpHost proxy = new HttpHost("someproxy", 8080);
|
|
|
|
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
|
|
|
|
CloseableHttpClient httpclient = HttpClients.custom()
|
|
|
|
.setRoutePlanner(routePlanner)
|
|
|
|
.build();
|
|
|
|
]]></programlisting>
|
|
|
|
<para>One can also instruct HttpClient to use the standard JRE proxy selector to obtain proxy
|
|
|
|
information:</para>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(
|
|
|
|
ProxySelector.getDefault());
|
|
|
|
CloseableHttpClient httpclient = HttpClients.custom()
|
|
|
|
.setRoutePlanner(routePlanner)
|
|
|
|
.build();
|
|
|
|
]]></programlisting>
|
|
|
|
<para>Alternatively, one can provide a custom <interfacename>RoutePlanner</interfacename>
|
|
|
|
implementation in order to have a complete control over the process of HTTP route
|
|
|
|
computation:</para>
|
|
|
|
<programlisting><![CDATA[
|
|
|
|
HttpRoutePlanner routePlanner = new HttpRoutePlanner() {
|
|
|
|
|
|
|
|
public HttpRoute determineRoute(
|
|
|
|
HttpHost target,
|
|
|
|
HttpRequest request,
|
|
|
|
HttpContext context) throws HttpException {
|
|
|
|
return new HttpRoute(target, null, new HttpHost("someproxy", 8080),
|
|
|
|
"https".equalsIgnoreCase(target.getSchemeName()));
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
CloseableHttpClient httpclient = HttpClients.custom()
|
|
|
|
.setRoutePlanner(routePlanner)
|
|
|
|
.build();
|
|
|
|
}
|
|
|
|
}
|
2009-07-08 15:07:17 -04:00
|
|
|
]]></programlisting>
|
|
|
|
</section>
|
|
|
|
</chapter>
|