Updated HttpClient tutorial to 4.3 APIs (fundamentals, conn mgmt and state mgmt chapters)
git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1513603 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
64ca74933b
commit
5e2c01472d
|
@ -49,14 +49,9 @@ public final class CookieSpecs {
|
|||
public static final String NETSCAPE = "netscape";
|
||||
|
||||
/**
|
||||
* The RFC 2109 compliant policy.
|
||||
* The RFC 2965 compliant policy (standard).
|
||||
*/
|
||||
public static final String RFC_2109 = "rfc2109";
|
||||
|
||||
/**
|
||||
* The RFC 2965 compliant policy.
|
||||
*/
|
||||
public static final String RFC_2965 = "rfc2965";
|
||||
public static final String STANDARD = "standard";
|
||||
|
||||
/**
|
||||
* The default 'best match' policy.
|
||||
|
|
|
@ -690,11 +690,12 @@ public class HttpClientBuilder {
|
|||
if (cookieSpecRegistry == null) {
|
||||
cookieSpecRegistry = RegistryBuilder.<CookieSpecProvider>create()
|
||||
.register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory())
|
||||
.register(CookieSpecs.STANDARD, new RFC2965SpecFactory())
|
||||
.register(CookieSpecs.BROWSER_COMPATIBILITY, new BrowserCompatSpecFactory())
|
||||
.register(CookieSpecs.NETSCAPE, new NetscapeDraftSpecFactory())
|
||||
.register(CookieSpecs.RFC_2109, new RFC2109SpecFactory())
|
||||
.register(CookieSpecs.RFC_2965, new RFC2965SpecFactory())
|
||||
.register(CookieSpecs.IGNORE_COOKIES, new IgnoreSpecFactory())
|
||||
.register("rfc2109", new RFC2109SpecFactory())
|
||||
.register("rfc2965", new RFC2965SpecFactory())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,10 @@ public class DefaultProxyRoutePlanner extends DefaultRoutePlanner {
|
|||
this.proxy = Args.notNull(proxy, "Proxy host");
|
||||
}
|
||||
|
||||
public DefaultProxyRoutePlanner(final HttpHost proxy) {
|
||||
this(proxy, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHost determineProxy(
|
||||
final HttpHost target,
|
||||
|
|
|
@ -65,6 +65,10 @@ public class SystemDefaultRoutePlanner extends DefaultRoutePlanner {
|
|||
this.proxySelector = proxySelector != null ? proxySelector : ProxySelector.getDefault();
|
||||
}
|
||||
|
||||
public SystemDefaultRoutePlanner(final ProxySelector proxySelector) {
|
||||
this(null, proxySelector);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHost determineProxy(
|
||||
final HttpHost target,
|
||||
|
|
|
@ -75,7 +75,7 @@ public class TestRequestConfig {
|
|||
.setRelativeRedirectsAllowed(false)
|
||||
.setCircularRedirectsAllowed(true)
|
||||
.setMaxRedirects(100)
|
||||
.setCookieSpec(CookieSpecs.RFC_2965)
|
||||
.setCookieSpec(CookieSpecs.STANDARD)
|
||||
.setLocalAddress(InetAddress.getLocalHost())
|
||||
.setProxy(new HttpHost("someproxy"))
|
||||
.setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM))
|
||||
|
@ -92,7 +92,7 @@ public class TestRequestConfig {
|
|||
Assert.assertEquals(false, config.isRelativeRedirectsAllowed());
|
||||
Assert.assertEquals(true, config.isCircularRedirectsAllowed());
|
||||
Assert.assertEquals(100, config.getMaxRedirects());
|
||||
Assert.assertEquals(CookieSpecs.RFC_2965, config.getCookieSpec());
|
||||
Assert.assertEquals(CookieSpecs.STANDARD, config.getCookieSpec());
|
||||
Assert.assertEquals(InetAddress.getLocalHost(), config.getLocalAddress());
|
||||
Assert.assertEquals(new HttpHost("someproxy"), config.getProxy());
|
||||
Assert.assertEquals(Arrays.asList(AuthSchemes.NTLM), config.getTargetPreferredAuthSchemes());
|
||||
|
|
|
@ -52,7 +52,6 @@ import org.apache.http.impl.cookie.BrowserCompatSpec;
|
|||
import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
|
||||
import org.apache.http.impl.cookie.IgnoreSpecFactory;
|
||||
import org.apache.http.impl.cookie.NetscapeDraftSpecFactory;
|
||||
import org.apache.http.impl.cookie.RFC2109SpecFactory;
|
||||
import org.apache.http.impl.cookie.RFC2965SpecFactory;
|
||||
import org.apache.http.message.BasicHttpRequest;
|
||||
import org.junit.Assert;
|
||||
|
@ -82,10 +81,9 @@ public class TestRequestAddCookies {
|
|||
|
||||
this.cookieSpecRegistry = RegistryBuilder.<CookieSpecProvider>create()
|
||||
.register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory())
|
||||
.register(CookieSpecs.STANDARD, new RFC2965SpecFactory())
|
||||
.register(CookieSpecs.BROWSER_COMPATIBILITY, new BrowserCompatSpecFactory())
|
||||
.register(CookieSpecs.NETSCAPE, new NetscapeDraftSpecFactory())
|
||||
.register(CookieSpecs.RFC_2109, new RFC2109SpecFactory())
|
||||
.register(CookieSpecs.RFC_2965, new RFC2965SpecFactory())
|
||||
.register(CookieSpecs.IGNORE_COOKIES, new IgnoreSpecFactory())
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -23,119 +23,6 @@
|
|||
-->
|
||||
<chapter id="connmgmt">
|
||||
<title>Connection management</title>
|
||||
<para>HttpClient assumes complete control over the process of connection initialization and
|
||||
termination as well as I/O operations on active connections. However various aspects of
|
||||
connection operations can be influenced using a number of parameters.</para>
|
||||
<section>
|
||||
<title>Connection parameters</title>
|
||||
<para>These are parameters that can influence connection operations:</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreConnectionPNames.SO_TIMEOUT</constant>='http.socket.timeout':</title>
|
||||
<para>defines the socket timeout (<literal>SO_TIMEOUT</literal>) in
|
||||
milliseconds, which is the timeout for waiting for data or, put differently,
|
||||
a maximum period inactivity between two consecutive data packets). A timeout
|
||||
value of zero is interpreted as an infinite timeout. This parameter expects
|
||||
a value of type <classname>java.lang.Integer</classname>. If this parameter
|
||||
is not set, read operations will not time out (infinite timeout).</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreConnectionPNames.TCP_NODELAY</constant>='http.tcp.nodelay':</title>
|
||||
<para>determines whether Nagle's algorithm is to be used. Nagle's algorithm
|
||||
tries to conserve bandwidth by minimizing the number of segments that are
|
||||
sent. When applications wish to decrease network latency and increase
|
||||
performance, they can disable Nagle's algorithm (that is enable
|
||||
<literal>TCP_NODELAY</literal>. Data will be sent earlier, at the cost
|
||||
of an increase in bandwidth consumption. This parameter expects a value of
|
||||
type <classname>java.lang.Boolean</classname>. If this parameter is not set,
|
||||
<literal>TCP_NODELAY</literal> will be enabled (no delay).</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreConnectionPNames.SOCKET_BUFFER_SIZE</constant>='http.socket.buffer-size':</title>
|
||||
<para>determines the size of the internal socket buffer used to buffer data
|
||||
while receiving / transmitting HTTP messages. This parameter expects a value
|
||||
of type <classname>java.lang.Integer</classname>. If this parameter is not
|
||||
set, HttpClient will allocate 8192 byte socket buffers.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreConnectionPNames.SO_LINGER</constant>='http.socket.linger':</title>
|
||||
<para>sets <literal>SO_LINGER</literal> with the specified linger time in
|
||||
seconds. The maximum timeout value is platform specific. Value 0 implies
|
||||
that the option is disabled. Value -1 implies that the JRE default is used.
|
||||
The setting only affects the socket close operation. If this parameter is
|
||||
not set, the value -1 (JRE default) will be assumed.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreConnectionPNames.CONNECTION_TIMEOUT</constant>='http.connection.timeout':</title>
|
||||
<para>determines the timeout in milliseconds until a connection is established.
|
||||
A timeout value of zero is interpreted as an infinite timeout. This
|
||||
parameter expects a value of type <classname>java.lang.Integer</classname>.
|
||||
If this parameter is not set, connect operations will not time out (infinite
|
||||
timeout).</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreConnectionPNames.STALE_CONNECTION_CHECK</constant>='http.connection.stalecheck':</title>
|
||||
<para>determines whether stale connection check is to be used. Disabling stale
|
||||
connection check may result in a noticeable performance improvement (the
|
||||
check can cause up to 30 millisecond overhead per request) at the risk of
|
||||
getting an I/O error when executing a request over a connection that has
|
||||
been closed at the server side. This parameter expects a value of type
|
||||
<classname>java.lang.Boolean</classname>. For performance critical
|
||||
operations the check should be disabled. If this parameter is not set, the
|
||||
stale connection check will be performed before each request execution.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreConnectionPNames.MAX_LINE_LENGTH</constant>='http.connection.max-line-length':</title>
|
||||
<para>determines the maximum line length limit. If set to a positive value, any
|
||||
HTTP line exceeding this limit will cause an
|
||||
<exceptionname>java.io.IOException</exceptionname>. A negative or zero
|
||||
value will effectively disable the check. This parameter expects a value of
|
||||
type <classname>java.lang.Integer</classname>. If this parameter is not set,
|
||||
no limit will be enforced.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreConnectionPNames.MAX_HEADER_COUNT</constant>='http.connection.max-header-count':</title>
|
||||
<para>determines the maximum HTTP header count allowed. If set to a positive
|
||||
value, the number of HTTP headers received from the data stream exceeding
|
||||
this limit will cause an <exceptionname>java.io.IOException</exceptionname>.
|
||||
A negative or zero value will effectively disable the check. This parameter
|
||||
expects a value of type <classname>java.lang.Integer</classname>. If this
|
||||
parameter is not set, no limit will be enforced.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE</constant>='http.connection.max-status-line-garbage':</title>
|
||||
<para>defines the maximum number of ignorable lines before we expect a HTTP
|
||||
response's status line. With HTTP/1.1 persistent connections, the problem
|
||||
arises that broken scripts could return a wrong
|
||||
<literal>Content-Length</literal> (there are more bytes sent than
|
||||
specified). Unfortunately, in some cases, this cannot be detected after the
|
||||
bad response, but only before the next one. So HttpClient must be able to
|
||||
skip those surplus lines this way. This parameter expects a value of type
|
||||
java.lang.Integer. 0 disallows all garbage/empty lines before the status
|
||||
line. Use <constant>java.lang.Integer#MAX_VALUE</constant> for unlimited
|
||||
number. If this parameter is not set, unlimited number will be
|
||||
assumed.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
<section>
|
||||
<title>Connection persistence</title>
|
||||
<para>The process of establishing a connection from one host to another is quite complex and
|
||||
|
@ -166,27 +53,27 @@
|
|||
proxies.</para>
|
||||
<section>
|
||||
<title>Route computation</title>
|
||||
<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
|
||||
<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
|
||||
the <interfacename>RouteInfo</interfacename>, which cannot be changed (is
|
||||
immutable). <classname>HttpTracker</classname> is a mutable
|
||||
<interfacename>RouteInfo</interfacename> implementation used internally by
|
||||
<interfacename>RouteInfo</interfacename> implementation used internally by
|
||||
HttpClient to track the remaining hops to the ultimate route target.
|
||||
<classname>HttpTracker</classname> can be updated after a successful execution
|
||||
<classname>HttpTracker</classname> can be updated after a successful execution
|
||||
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
|
||||
<interfacename>HttpRoutePlanner</interfacename> implementations.
|
||||
<classname>ProxySelectorRoutePlanner</classname> is based on
|
||||
<classname>java.net.ProxySelector</classname>. By default, it will pick up the
|
||||
<interfacename>HttpRoutePlanner</interfacename> implementations.
|
||||
<classname>SystemDefaultRoutePlanner</classname> is based on
|
||||
<classname>java.net.ProxySelector</classname>. By default, it will pick up the
|
||||
proxy settings of the JVM, either from system properties or from the browser running
|
||||
the application. The <classname>DefaultHttpRoutePlanner</classname> implementation does
|
||||
not make use of any Java system properties, nor any system or browser proxy settings.
|
||||
It computes routes based exclusively on the HTTP parameters described below.</para>
|
||||
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>
|
||||
</section>
|
||||
<section>
|
||||
<title>Secure HTTP connections</title>
|
||||
|
@ -197,364 +84,85 @@
|
|||
HTTP transport is layered over the SSL/TLS encrypted connection.</para>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>HTTP route parameters</title>
|
||||
<para>These are the parameters that can influence route computation:</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ConnRoutePNames.DEFAULT_PROXY</constant>='http.route.default-proxy':</title>
|
||||
<para>defines a proxy host to be used by default route planners that do not make
|
||||
use of JRE settings. This parameter expects a value of type
|
||||
<classname>HttpHost</classname>. If this parameter is not set, direct
|
||||
connections to the target will be attempted.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ConnRoutePNames.LOCAL_ADDRESS</constant>='http.route.local-address':</title>
|
||||
<para>defines a local address to be used by all default route planner. On
|
||||
machines with multiple network interfaces, this parameter can be used to
|
||||
select the network interface from which the connection originates. This
|
||||
parameter expects a value of type
|
||||
<classname>java.net.InetAddress</classname>. If this parameter is not
|
||||
set, a default local address will be used automatically.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ConnRoutePNames.FORCED_ROUTE</constant>='http.route.forced-route':</title>
|
||||
<para>defines an forced route to be used by all default route planner. Instead
|
||||
of computing a route, the given forced route will be returned, even if it
|
||||
points to a completely different target host. This parameter expects a value
|
||||
of type <classname>HttpRoute</classname>.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
<section>
|
||||
<title>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>SchemeSocketFactory</interfacename> interface to create, initialize and
|
||||
connect sockets. This enables the users of HttpClient to provide application specific
|
||||
socket initialization code at runtime. <classname>PlainSocketFactory</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[
|
||||
PlainSocketFactory sf = PlainSocketFactory.getSocketFactory();
|
||||
Socket socket = sf.createSocket();
|
||||
|
||||
HttpParams params = new BasicHttpParams();
|
||||
params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000L);
|
||||
InetSocketAddress address = new InetSocketAddress("locahost", 8080);
|
||||
sf.connectSocket(socket, address, null, params);
|
||||
]]></programlisting>
|
||||
<section>
|
||||
<title>Secure socket layering</title>
|
||||
<para><interfacename>SchemeLayeredSocketFactory</interfacename> is an extension of
|
||||
the <interfacename>SchemeSocketFactory</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>SSL/TLS customization</title>
|
||||
<para>HttpClient makes use of SSLSocketFactory to create SSL connections.
|
||||
<classname>SSLSocketFactory</classname> allows for a high degree of
|
||||
customization. It can take an instance of
|
||||
<interfacename>javax.net.ssl.SSLContext</interfacename> as a parameter and use
|
||||
it to create custom configured SSL connections.</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpParams params = new BasicHttpParams();
|
||||
SSLContext sslcontext = SSLContext.getInstance("TLS");
|
||||
sslcontext.init(null, null, null);
|
||||
|
||||
SSLSocketFactory sf = new SSLSocketFactory(sslcontext);
|
||||
SSLSocket socket = (SSLSocket) sf.createSocket(params);
|
||||
socket.setEnabledCipherSuites(new String[] { "SSL_RSA_WITH_RC4_128_MD5" });
|
||||
|
||||
params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000L);
|
||||
InetSocketAddress address = new InetSocketAddress("locahost", 443);
|
||||
sf.connectSocket(socket, address, null, params);
|
||||
]]></programlisting>
|
||||
<para>Customization of SSLSocketFactory 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
|
||||
<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.
|
||||
The <interfacename>X509HostnameVerifier</interfacename> interface
|
||||
represents a strategy for hostname verification. HttpClient ships with three
|
||||
<interfacename>X509HostnameVerifier</interfacename> implementations.
|
||||
Important: hostname verification should not be confused with
|
||||
SSL trust verification.</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><classname>StrictHostnameVerifier</classname>:</title>
|
||||
<para>The strict hostname verifier works the same way as Sun Java 1.4, Sun
|
||||
Java 5, Sun Java 6. It's also pretty close to IE6. This implementation
|
||||
appears to be compliant with RFC 2818 for dealing with wildcards. The
|
||||
hostname must match either the first CN, or any of the subject-alts. A
|
||||
wildcard can occur in the CN, and in any of the subject-alts.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><classname>BrowserCompatHostnameVerifier</classname>:</title>
|
||||
<para>This hostname verifier that works the same way as Curl and Firefox. The
|
||||
hostname must match either the first CN, or any of the subject-alts. A
|
||||
wildcard can occur in the CN, and in any of the subject-alts. The only
|
||||
difference between <classname>BrowserCompatHostnameVerifier</classname>
|
||||
and <classname>StrictHostnameVerifier</classname> is that a wildcard
|
||||
(such as "*.foo.com") with
|
||||
<classname>BrowserCompatHostnameVerifier</classname> matches all
|
||||
subdomains, including "a.b.foo.com".</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><classname>AllowAllHostnameVerifier</classname>:</title>
|
||||
<para>This hostname verifier essentially turns hostname verification off.
|
||||
This implementation is a no-op, and never throws
|
||||
<exceptionname>javax.net.ssl.SSLException</exceptionname>.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>Per default HttpClient uses the <classname>BrowserCompatHostnameVerifier</classname>
|
||||
implementation. One can specify a different hostname verifier implementation if
|
||||
desired</para>
|
||||
<programlisting><![CDATA[
|
||||
SSLContext sslcontext = SSLContext.getInstance("TLS");
|
||||
sslcontext.init(null, null, null);
|
||||
SSLSocketFactory sf = new SSLSocketFactory(
|
||||
sslcontext,
|
||||
SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
|
||||
]]></programlisting>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>Protocol schemes</title>
|
||||
<para>The <classname>Scheme</classname> class represents a protocol scheme such as "http" or
|
||||
"https" and contains a number of protocol properties such as the default port and the
|
||||
socket factory to be used to create the <classname>java.net.Socket</classname> instances
|
||||
for the given protocol. The <classname>SchemeRegistry</classname> class is used to maintain
|
||||
a set of <classname>Scheme</classname>s that HttpClient can choose from when trying to
|
||||
establish a connection by a request URI:</para>
|
||||
<programlisting><![CDATA[
|
||||
Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory());
|
||||
|
||||
SSLContext sslcontext = SSLContext.getInstance("TLS");
|
||||
sslcontext.init(null, null, null);
|
||||
SSLSocketFactory sf = new SSLSocketFactory(
|
||||
sslcontext,
|
||||
SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
|
||||
Scheme https = new Scheme("https", 443, sf);
|
||||
|
||||
SchemeRegistry sr = new SchemeRegistry();
|
||||
sr.register(http);
|
||||
sr.register(https);
|
||||
]]></programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>HttpClient proxy configuration</title>
|
||||
<para>Even though HttpClient is aware of complex routing scemes and proxy chaining, it
|
||||
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[
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
|
||||
HttpHost proxy = new HttpHost("someproxy", 8080);
|
||||
httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
|
||||
]]></programlisting>
|
||||
<para>One can also instruct HttpClient to use the standard JRE proxy selector to obtain proxy
|
||||
information:</para>
|
||||
<programlisting><![CDATA[
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
|
||||
ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
|
||||
httpclient.getConnectionManager().getSchemeRegistry(),
|
||||
ProxySelector.getDefault());
|
||||
httpclient.setRoutePlanner(routePlanner);
|
||||
]]></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[
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
httpclient.setRoutePlanner(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()));
|
||||
}
|
||||
|
||||
});
|
||||
]]></programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>HTTP connection managers</title>
|
||||
<section>
|
||||
<title>Connection operators</title>
|
||||
<para>Operated connections are client side connections whose underlying socket or
|
||||
state can be manipulated by an external entity, usually referred to as a connection
|
||||
operator. The <interfacename>OperatedClientConnection</interfacename> interface extends
|
||||
the <interfacename>HttpClientConnection</interfacename> interface and defines
|
||||
additional methods to manage connection sockets. The
|
||||
<interfacename>ClientConnectionOperator</interfacename> interface represents a
|
||||
strategy for creating <interfacename>OperatedClientConnection</interfacename>
|
||||
instances and updating the underlying socket of those objects. Implementations will
|
||||
most likely make use a <interfacename>SchemeSocketFactory</interfacename> to create
|
||||
<classname>java.net.Socket</classname> instances. The
|
||||
<interfacename>ClientConnectionOperator</interfacename> interface enables
|
||||
users of HttpClient to provide a custom strategy for connection operators as well as
|
||||
the ability to provide an alternative implementation of the
|
||||
<interfacename>OperatedClientConnection</interfacename> interface.</para>
|
||||
</section>
|
||||
<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
|
||||
<interfacename>ClientConnectionManager</interfacename> interface. The purpose of
|
||||
an HTTP connection manager is to serve as a factory for new HTTP connections, manage
|
||||
persistent connections and synchronize access to persistent connections making sure
|
||||
that only one thread can have access to a connection at a time.</para>
|
||||
<para>Internally HTTP connection managers work with instances of
|
||||
<interfacename>OperatedClientConnection</interfacename>, but they return
|
||||
instances of <interfacename>ManagedClientConnection</interfacename> to the service
|
||||
consumers. <interfacename>ManagedClientConnection</interfacename> acts as a wrapper
|
||||
for a <interfacename>OperatedClientConnection</interfacename> instance that manages
|
||||
its state and controls all I/O operations on that connection. It also abstracts away
|
||||
socket operations and provides convenience methods for opening and updating sockets
|
||||
in order to establish a route.
|
||||
<interfacename>ManagedClientConnection</interfacename> instances are aware of
|
||||
their link to the connection manager that spawned them and of the fact that they
|
||||
must be returned back to the manager when no longer in use.
|
||||
<interfacename>ManagedClientConnection</interfacename> classes also implement
|
||||
the <interfacename>ConnectionReleaseTrigger</interfacename> interface that can be
|
||||
used to trigger the release of the connection back to the manager. Once the
|
||||
connection release has been triggered the wrapped connection gets detached from the
|
||||
<interfacename>ManagedClientConnection</interfacename> wrapper and the
|
||||
<interfacename>OperatedClientConnection</interfacename> instance is returned
|
||||
back to the manager. Even though the service consumer still holds a reference to the
|
||||
<interfacename>ManagedClientConnection</interfacename> instance, it is no longer
|
||||
able to execute any I/O operation or change the state of the
|
||||
<interfacename>OperatedClientConnection</interfacename> either intentionally or
|
||||
unintentionally.</para>
|
||||
<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
|
||||
by its consumer the underyling connection gets detached from its proxy and is
|
||||
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>
|
||||
<para>This is an example of acquiring a connection from a connection manager:</para>
|
||||
<programlisting><![CDATA[
|
||||
Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory());
|
||||
SchemeRegistry sr = new SchemeRegistry();
|
||||
sr.register(http);
|
||||
ClientConnectionManager connMrg = new BasicClientConnectionManager(sr);
|
||||
|
||||
HttpClientContext context = HttpClientContext.create();
|
||||
HttpClientConnectionManager connMrg = new BasicHttpClientConnectionManager();
|
||||
HttpRoute route = new HttpRoute(new HttpHost("localhost", 80));
|
||||
// Request new connection. This can be a long process
|
||||
ClientConnectionRequest connRequest = connMrg.requestConnection(
|
||||
new HttpRoute(new HttpHost("localhost", 80)), null);
|
||||
|
||||
ConnectionRequest connRequest = connMrg.requestConnection(route, null);
|
||||
// Wait for connection up to 10 sec
|
||||
ManagedClientConnection conn = connRequest.getConnection(10, TimeUnit.SECONDS);
|
||||
HttpClientConnection conn = connRequest.get(10, TimeUnit.SECONDS);
|
||||
try {
|
||||
// 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);
|
||||
}
|
||||
// Do useful things with the connection.
|
||||
// Release it when done.
|
||||
conn.releaseConnection();
|
||||
} catch (IOException ex) {
|
||||
// Abort connection upon an I/O error.
|
||||
conn.abortConnection();
|
||||
throw ex;
|
||||
} finally {
|
||||
connMrg.releaseConnection(conn, null, 1, TimeUnit.MINUTES);
|
||||
}
|
||||
]]></programlisting>
|
||||
<para>The connection request can be terminated prematurely by calling
|
||||
<methodname>ClientConnectionRequest#abortRequest()</methodname> if necessary.
|
||||
This will unblock the thread blocked in the
|
||||
<methodname>ClientConnectionRequest#getConnection()</methodname> method.</para>
|
||||
<para><classname>BasicManagedEntity</classname> wrapper class can be used to ensure
|
||||
automatic release of the underlying connection once the response content has been
|
||||
fully consumed. HttpClient uses this mechanism internally to achieve transparent
|
||||
connection release for all responses obtained from
|
||||
<methodname>HttpClient#execute()</methodname> methods:</para>
|
||||
<programlisting><![CDATA[
|
||||
ClientConnectionRequest connRequest = connMrg.requestConnection(
|
||||
new HttpRoute(new HttpHost("localhost", 80)), null);
|
||||
ManagedClientConnection conn = connRequest.getConnection(10, TimeUnit.SECONDS);
|
||||
try {
|
||||
BasicHttpRequest request = new BasicHttpRequest("GET", "/");
|
||||
conn.sendRequestHeader(request);
|
||||
HttpResponse response = conn.receiveResponseHeader();
|
||||
conn.receiveResponseEntity(response);
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
BasicManagedEntity managedEntity = new BasicManagedEntity(entity, conn, true);
|
||||
// Replace entity
|
||||
response.setEntity(managedEntity);
|
||||
}
|
||||
// Do something useful with the response
|
||||
// The connection will be released automatically
|
||||
// as soon as the response content has been consumed
|
||||
} catch (IOException ex) {
|
||||
// Abort connection upon an I/O error.
|
||||
conn.abortConnection();
|
||||
throw ex;
|
||||
}
|
||||
]]></programlisting>
|
||||
<methodname>ConnectionRequest#cancel()</methodname> if necessary. This will unblock
|
||||
the thread blocked in the <methodname>ConnectionRequest#get()</methodname>
|
||||
method.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Simple connection manager</title>
|
||||
<para><classname>BasicClientConnectionManager</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>BasicClientConnectionManager</classname> will make an effort to reuse
|
||||
<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
|
||||
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>
|
||||
<para><classname>BasicClientConnectionManager</classname> is used by HttpClient per
|
||||
default.</para>
|
||||
<para>This connection manager implementation should be used inside an EJB
|
||||
container.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Pooling connection manager</title>
|
||||
<para><classname>PoolingClientConnectionManager</classname> is a more complex
|
||||
<para><classname>PoolingHttpClientConnectionManager</classname> is a more complex
|
||||
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
|
||||
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
|
||||
the pool rather than creating a brand new connection.</para>
|
||||
<para><classname>PoolingClientConnectionManager</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. Connection limits can be adjusted using the appropriate HTTP parameters.</para>
|
||||
<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>
|
||||
<para>This example shows how the connection pool parameters can be adjusted:</para>
|
||||
<programlisting><![CDATA[
|
||||
SchemeRegistry schemeRegistry = new SchemeRegistry();
|
||||
schemeRegistry.register(
|
||||
new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
||||
schemeRegistry.register(
|
||||
new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
|
||||
|
||||
PoolingClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry);
|
||||
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
|
||||
// Increase max total connection to 200
|
||||
cm.setMaxTotal(200);
|
||||
// Increase default max connection per route to 20
|
||||
|
@ -562,8 +170,10 @@ cm.setDefaultMaxPerRoute(20);
|
|||
// Increase max connections for localhost:80 to 50
|
||||
HttpHost localhost = new HttpHost("locahost", 80);
|
||||
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
|
||||
|
||||
HttpClient httpClient = new DefaultHttpClient(cm);
|
||||
|
||||
CloseableHttpClient httpClient = HttpClients.custom()
|
||||
.setConnectionManager(cm)
|
||||
.build();
|
||||
]]></programlisting>
|
||||
</section>
|
||||
<section>
|
||||
|
@ -573,13 +183,8 @@ HttpClient httpClient = new DefaultHttpClient(cm);
|
|||
alive by the manager get closed and system resources allocated by those connections
|
||||
are released.</para>
|
||||
<programlisting><![CDATA[
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
HttpGet httpget = new HttpGet("http://www.google.com/");
|
||||
HttpResponse response = httpclient.execute(httpget);
|
||||
HttpEntity entity = response.getEntity();
|
||||
System.out.println(response.getStatusLine());
|
||||
EntityUtils.consume(entity);
|
||||
httpclient.getConnectionManager().shutdown();
|
||||
CloseableHttpClient httpClient = <...>
|
||||
httpClient.close();
|
||||
]]></programlisting>
|
||||
</section>
|
||||
</section>
|
||||
|
@ -597,12 +202,10 @@ httpclient.getConnectionManager().shutdown();
|
|||
period <exceptionname>ConnectionPoolTimeoutException</exceptionname> will be thrown.
|
||||
</para>
|
||||
<programlisting><![CDATA[
|
||||
SchemeRegistry schemeRegistry = new SchemeRegistry();
|
||||
schemeRegistry.register(
|
||||
new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
||||
|
||||
ClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry);
|
||||
HttpClient httpClient = new DefaultHttpClient(cm);
|
||||
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
|
||||
CloseableHttpClient httpClient = HttpClients.custom()
|
||||
.setConnectionManager(cm)
|
||||
.build();
|
||||
|
||||
// URIs to perform GETs on
|
||||
String[] urisToGet = {
|
||||
|
@ -636,32 +239,34 @@ for (int j = 0; j < threads.length; j++) {
|
|||
</interfacename>.</para>
|
||||
<programlisting><![CDATA[
|
||||
static class GetThread extends Thread {
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
private final CloseableHttpClient httpClient;
|
||||
private final HttpContext context;
|
||||
private final HttpGet httpget;
|
||||
|
||||
public GetThread(HttpClient httpClient, HttpGet httpget) {
|
||||
|
||||
public GetThread(CloseableHttpClient httpClient, HttpGet httpget) {
|
||||
this.httpClient = httpClient;
|
||||
this.context = new BasicHttpContext();
|
||||
this.context = HttpClientContext.create();
|
||||
this.httpget = httpget;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
HttpResponse response = this.httpClient.execute(this.httpget, this.context);
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
// do something useful with the entity
|
||||
CloseableHttpResponse response = httpClient.execute(
|
||||
httpget, context);
|
||||
try {
|
||||
HttpEntity entity = response.getEntity();
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
// ensure the connection gets released to the manager
|
||||
EntityUtils.consume(entity);
|
||||
} catch (Exception ex) {
|
||||
this.httpget.abort();
|
||||
} catch (ClientProtocolException ex) {
|
||||
// Handle protocol errors
|
||||
} catch (IOException ex) {
|
||||
// Handle I/O errors
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
]]></programlisting>
|
||||
</section>
|
||||
|
@ -687,10 +292,10 @@ static class GetThread extends Thread {
|
|||
<programlisting><![CDATA[
|
||||
public static class IdleConnectionMonitorThread extends Thread {
|
||||
|
||||
private final ClientConnectionManager connMgr;
|
||||
private final HttpClientConnectionManager connMgr;
|
||||
private volatile boolean shutdown;
|
||||
|
||||
public IdleConnectionMonitorThread(ClientConnectionManager connMgr) {
|
||||
public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
|
||||
super();
|
||||
this.connMgr = connMgr;
|
||||
}
|
||||
|
@ -736,8 +341,7 @@ public static class IdleConnectionMonitorThread extends Thread {
|
|||
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[
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
httpclient.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
|
||||
ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
|
||||
|
||||
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
|
||||
// Honor 'keep-alive' header
|
||||
|
@ -745,7 +349,7 @@ httpclient.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
|
|||
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
|
||||
while (it.hasNext()) {
|
||||
HeaderElement he = it.nextElement();
|
||||
String param = he.getName();
|
||||
String param = he.getName();
|
||||
String value = he.getValue();
|
||||
if (value != null && param.equalsIgnoreCase("timeout")) {
|
||||
try {
|
||||
|
@ -755,7 +359,7 @@ httpclient.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
|
|||
}
|
||||
}
|
||||
HttpHost target = (HttpHost) context.getAttribute(
|
||||
ExecutionContext.HTTP_TARGET_HOST);
|
||||
HttpClientContext.HTTP_TARGET_HOST);
|
||||
if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) {
|
||||
// Keep alive for 5 seconds only
|
||||
return 5 * 1000;
|
||||
|
@ -764,8 +368,186 @@ httpclient.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
|
|||
return 30 * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
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 = <...>
|
||||
Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
|
||||
.register("http", plainsf)
|
||||
.register("https", sslsf)
|
||||
.build();
|
||||
|
||||
HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
|
||||
HttpClients.custom()
|
||||
.setConnectionManager(cm)
|
||||
.build();
|
||||
]]></programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>SSL/TLS customization</title>
|
||||
<para>HttpClient makes use of SSLSocketFactory to create SSL connections.
|
||||
<classname>SSLSocketFactory</classname> allows for a high degree of
|
||||
customization. It can take an instance of
|
||||
<interfacename>javax.net.ssl.SSLContext</interfacename> as a parameter and use
|
||||
it to create custom configured SSL connections.</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpClientContext clientContext = HttpClientContext.create();
|
||||
KeyStore myTrustStore = <...>
|
||||
SSLContext sslContext = SSLContexts.custom()
|
||||
.useTLS()
|
||||
.loadTrustMaterial(myTrustStore)
|
||||
.build();
|
||||
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
|
||||
]]></programlisting>
|
||||
<para>Customization of SSLSocketFactory 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
|
||||
<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.
|
||||
The <interfacename>X509HostnameVerifier</interfacename> interface
|
||||
represents a strategy for hostname verification. HttpClient ships with three
|
||||
<interfacename>X509HostnameVerifier</interfacename> implementations.
|
||||
Important: hostname verification should not be confused with
|
||||
SSL trust verification.</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><classname>StrictHostnameVerifier</classname>:</title>
|
||||
<para>The strict hostname verifier works the same way as Sun Java 1.4, Sun
|
||||
Java 5, Sun Java 6. It's also pretty close to IE6. This implementation
|
||||
appears to be compliant with RFC 2818 for dealing with wildcards. The
|
||||
hostname must match either the first CN, or any of the subject-alts. A
|
||||
wildcard can occur in the CN, and in any of the subject-alts.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><classname>BrowserCompatHostnameVerifier</classname>:</title>
|
||||
<para>This hostname verifier that works the same way as Curl and Firefox. The
|
||||
hostname must match either the first CN, or any of the subject-alts. A
|
||||
wildcard can occur in the CN, and in any of the subject-alts. The only
|
||||
difference between <classname>BrowserCompatHostnameVerifier</classname>
|
||||
and <classname>StrictHostnameVerifier</classname> is that a wildcard
|
||||
(such as "*.foo.com") with
|
||||
<classname>BrowserCompatHostnameVerifier</classname> matches all
|
||||
subdomains, including "a.b.foo.com".</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><classname>AllowAllHostnameVerifier</classname>:</title>
|
||||
<para>This hostname verifier essentially turns hostname verification off.
|
||||
This implementation is a no-op, and never throws
|
||||
<exceptionname>javax.net.ssl.SSLException</exceptionname>.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>Per default HttpClient uses the <classname>BrowserCompatHostnameVerifier</classname>
|
||||
implementation. One can specify a different hostname verifier implementation if
|
||||
desired</para>
|
||||
<programlisting><![CDATA[
|
||||
SSLContext sslContext = SSLContexts.createSystemDefault();
|
||||
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
|
||||
sslContext,
|
||||
SSLConnectionSocketFactory.STRICT_HOSTNAME_VERIFIER);
|
||||
]]></programlisting>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>HttpClient proxy configuration</title>
|
||||
<para>Even though HttpClient is aware of complex routing scemes and proxy chaining, it
|
||||
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();
|
||||
}
|
||||
}
|
||||
]]></programlisting>
|
||||
</section>
|
||||
</chapter>
|
||||
|
|
|
@ -34,17 +34,13 @@
|
|||
interface that defines the contract described above. </para>
|
||||
<para>Here is an example of request execution process in its simplest form:</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpClient httpclient = new DefaultHttpClient();
|
||||
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||
HttpGet httpget = new HttpGet("http://localhost/");
|
||||
HttpResponse response = httpclient.execute(httpget);
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
InputStream instream = entity.getContent();
|
||||
try {
|
||||
// do something useful
|
||||
} finally {
|
||||
instream.close();
|
||||
}
|
||||
CloseableHttpResponse response = httpclient.execute(httpget);
|
||||
try {
|
||||
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
]]></programlisting>
|
||||
<section>
|
||||
|
@ -69,13 +65,15 @@ HttpGet httpget = new HttpGet(
|
|||
<para>HttpClient provides <classname>URIBuilder</classname> utility class to simplify
|
||||
creation and modification of request URIs.</para>
|
||||
<programlisting><![CDATA[
|
||||
URIBuilder builder = new URIBuilder();
|
||||
builder.setScheme("http").setHost("www.google.com").setPath("/search")
|
||||
.setParameter("q", "httpclient")
|
||||
.setParameter("btnG", "Google Search")
|
||||
.setParameter("aq", "f")
|
||||
.setParameter("oq", "");
|
||||
URI uri = builder.build();
|
||||
URI uri = new URIBuilder()
|
||||
.setScheme("http")
|
||||
.setHost("www.google.com")
|
||||
.setPath("/search")
|
||||
.setParameter("q", "httpclient")
|
||||
.setParameter("btnG", "Google Search")
|
||||
.setParameter("aq", "f")
|
||||
.setParameter("oq", "")
|
||||
.build();
|
||||
HttpGet httpget = new HttpGet(uri);
|
||||
System.out.println(httpget.getURI());
|
||||
]]></programlisting>
|
||||
|
@ -282,21 +280,30 @@ important message
|
|||
</section>
|
||||
<section>
|
||||
<title>Ensuring release of low level resources</title>
|
||||
<para> In order to ensure proper release of system resources one must close the content
|
||||
stream associated with the entity.</para>
|
||||
<para> In order to ensure proper release of system resources one must close either
|
||||
the content stream associated with the entity or the response itself</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpResponse response;
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
InputStream instream = entity.getContent();
|
||||
try {
|
||||
// do something useful
|
||||
} finally {
|
||||
instream.close();
|
||||
CloseableHttpClient httpclient = <...>
|
||||
CloseableHttpResponse response = httpclient.execute(httpget);
|
||||
try {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
InputStream instream = entity.getContent();
|
||||
try {
|
||||
// do something useful
|
||||
} finally {
|
||||
instream.close();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
]]></programlisting>
|
||||
<para>Please note that the <methodname>HttpEntity#writeTo(OutputStream)</methodname>
|
||||
<para>The difference between closing the content stream and closing the response
|
||||
is that the former will attempt to keep the underlying connection alive
|
||||
by consuming the entity content while the latter immediately shuts down
|
||||
and discards the connection.</para>
|
||||
<para>Please note that the <methodname>HttpEntity#writeTo(OutputStream)</methodname>
|
||||
method is also required to ensure proper release of system resources once the
|
||||
entity has been fully written out. If this method obtains an instance of
|
||||
<classname>java.io.InputStream</classname> by calling
|
||||
|
@ -309,19 +316,20 @@ if (entity != null) {
|
|||
<para>There can be situations, however, when only a small portion of the entire response
|
||||
content needs to be retrieved and the performance penalty for consuming the
|
||||
remaining content and making the connection reusable is too high, in which case
|
||||
one can simply
|
||||
terminate the request by calling <methodname>HttpUriRequest#abort()</methodname>
|
||||
method.</para>
|
||||
one can terminate the content stream by closing the response.</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpGet httpget = new HttpGet("http://localhost/");
|
||||
HttpResponse response = httpclient.execute(httpget);
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
InputStream instream = entity.getContent();
|
||||
int byteOne = instream.read();
|
||||
int byteTwo = instream.read();
|
||||
// Do not need the rest
|
||||
httpget.abort();
|
||||
CloseableHttpClient httpclient = <...>
|
||||
CloseableHttpResponse response = httpclient.execute(httpget);
|
||||
try {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
InputStream instream = entity.getContent();
|
||||
int byteOne = instream.read();
|
||||
int byteTwo = instream.read();
|
||||
// Do not need the rest
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
]]></programlisting>
|
||||
<para>The connection will not be reused, but all level resources held by it will be
|
||||
|
@ -340,16 +348,20 @@ if (entity != null) {
|
|||
strongly discouraged unless the response entities originate from a trusted HTTP
|
||||
server and are known to be of limited length.</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpGet httpget = new HttpGet("http://localhost/");
|
||||
HttpResponse response = httpclient.execute(httpget);
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
long len = entity.getContentLength();
|
||||
if (len != -1 && len < 2048) {
|
||||
System.out.println(EntityUtils.toString(entity));
|
||||
} else {
|
||||
// Stream content out
|
||||
CloseableHttpClient httpclient = <...>
|
||||
CloseableHttpResponse response = httpclient.execute(httpget);
|
||||
try {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
long len = entity.getContentLength();
|
||||
if (len != -1 && len < 2048) {
|
||||
System.out.println(EntityUtils.toString(entity));
|
||||
} else {
|
||||
// Stream content out
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
]]></programlisting>
|
||||
<para>In some situations it may be necessary to be able to read entity content more than
|
||||
|
@ -359,8 +371,7 @@ if (entity != null) {
|
|||
the original entity to be read into a in-memory buffer. In all other ways the entity
|
||||
wrapper will be have the original one.</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpGet httpget = new HttpGet("http://localhost/");
|
||||
HttpResponse response = httpclient.execute(httpget);
|
||||
CloseableHttpResponse response = <...>
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
entity = new BufferedHttpEntity(entity);
|
||||
|
@ -401,7 +412,7 @@ httppost.setEntity(entity);
|
|||
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
|
||||
formparams.add(new BasicNameValuePair("param1", "value1"));
|
||||
formparams.add(new BasicNameValuePair("param2", "value2"));
|
||||
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, "UTF-8");
|
||||
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
|
||||
HttpPost httppost = new HttpPost("http://localhost/handler.do");
|
||||
httppost.setEntity(entity);
|
||||
]]></programlisting>
|
||||
|
@ -422,8 +433,8 @@ param1=value1¶m2=value2
|
|||
when using HTTP protocol versions that do not support chunk coding, such as
|
||||
HTTP/1.0.</para>
|
||||
<programlisting><![CDATA[
|
||||
StringEntity entity = new StringEntity("important message",
|
||||
"text/plain; charset=\"UTF-8\"");
|
||||
StringEntity entity = new StringEntity("important message",
|
||||
ContentType.create("plain/text", Consts.UTF_8));
|
||||
entity.setChunked(true);
|
||||
HttpPost httppost = new HttpPost("http://localhost/acrtion.do");
|
||||
httppost.setEntity(entity);
|
||||
|
@ -441,22 +452,32 @@ httppost.setEntity(entity);
|
|||
take care of ensuring release of the connection back to the connection manager
|
||||
regardless whether the request execution succeeds or causes an exception.</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpClient httpclient = new DefaultHttpClient();
|
||||
HttpGet httpget = new HttpGet("http://localhost/");
|
||||
HttpClient httpclient = <...>
|
||||
HttpRequest request = <...>
|
||||
|
||||
ResponseHandler<byte[]> handler = new ResponseHandler<byte[]>() {
|
||||
public byte[] handleResponse(
|
||||
HttpResponse response) throws ClientProtocolException, IOException {
|
||||
ResponseHandler<MyJsonObject> rh = new ResponseHandler<MyJsonObject>() {
|
||||
|
||||
@Override
|
||||
public JsonObject handleResponse(
|
||||
final HttpResponse response) throws IOException {
|
||||
StatusLine statusLine = response.getStatusLine();
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
return EntityUtils.toByteArray(entity);
|
||||
} else {
|
||||
return null;
|
||||
if (statusLine.getStatusCode() >= 300) {
|
||||
throw new HttpResponseException(
|
||||
statusLine.getStatusCode(),
|
||||
statusLine.getReasonPhrase());
|
||||
}
|
||||
if (entity == null) {
|
||||
throw new ClientProtocolException("Response contains no content");
|
||||
}
|
||||
Gson gson = new GsonBuilder().create();
|
||||
ContentType contentType = ContentType.getOrDefault(entity);
|
||||
Charset charset = contentType.getCharset();
|
||||
Reader reader = new InputStreamReader(entity.getContent(), charset);
|
||||
return gson.fromJson(reader, MyJsonObject.class);
|
||||
}
|
||||
};
|
||||
|
||||
byte[] response = httpclient.execute(httpget, handler);
|
||||
MyJsonObject myjson = client.execute(request, rh);
|
||||
]]></programlisting>
|
||||
</section>
|
||||
</section>
|
||||
|
@ -476,87 +497,51 @@ byte[] response = httpclient.execute(httpget, handler);
|
|||
<para><interfacename>HttpContext</interfacename> can contain arbitrary objects and
|
||||
therefore may be unsafe to share between multiple threads. It is recommended that
|
||||
each thread of execution maintains its own context.</para>
|
||||
<para>In the course of HTTP request execution HttpClient adds the following attributes to
|
||||
the execution context:</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ExecutionContext.HTTP_CONNECTION</constant>='http.connection':</title>
|
||||
<para><interfacename>HttpConnection</interfacename> instance representing the
|
||||
actual connection to the target server.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ExecutionContext.HTTP_TARGET_HOST</constant>='http.target_host':</title>
|
||||
<para><classname>HttpHost</classname> instance representing the connection
|
||||
target.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ExecutionContext.HTTP_PROXY_HOST</constant>='http.proxy_host':</title>
|
||||
<para><classname>HttpHost</classname> instance representing the connection
|
||||
proxy, if used</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ExecutionContext.HTTP_REQUEST</constant>='http.request':</title>
|
||||
<para><interfacename>HttpRequest</interfacename> instance representing the
|
||||
actual HTTP request.
|
||||
The final HttpRequest object in the execution context always represents
|
||||
the state of the message _exactly_ as it was sent to the target server.
|
||||
Per default HTTP/1.0 and HTTP/1.1 use relative request URIs.
|
||||
However if the request is sent via a proxy in a non-tunneling mode then
|
||||
the URI will be absolute.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ExecutionContext.HTTP_RESPONSE</constant>='http.response':</title>
|
||||
<para><interfacename>HttpResponse</interfacename> instance representing the
|
||||
actual HTTP response.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ExecutionContext.HTTP_REQ_SENT</constant>='http.request_sent':</title>
|
||||
<para><classname>java.lang.Boolean</classname> object representing the flag
|
||||
indicating whether the actual request has been fully transmitted to the
|
||||
connection target.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>For instance, in order to determine the final redirect target, one can examine the
|
||||
value of the <literal>http.target_host</literal> attribute after the request
|
||||
execution:</para>
|
||||
<para>One can use <classname>HttpClientContext</classname> adaptor class to simplify
|
||||
interractions with the context state.</para>
|
||||
<programlisting><![CDATA[
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
|
||||
HttpContext localContext = new BasicHttpContext();
|
||||
HttpGet httpget = new HttpGet("http://www.google.com/");
|
||||
|
||||
HttpResponse response = httpclient.execute(httpget, localContext);
|
||||
|
||||
HttpHost target = (HttpHost) localContext.getAttribute(
|
||||
ExecutionContext.HTTP_TARGET_HOST);
|
||||
|
||||
System.out.println("Final target: " + target);
|
||||
|
||||
HttpEntity entity = response.getEntity();
|
||||
EntityUtils.consume(entity);
|
||||
}
|
||||
HttpContext context = <...>
|
||||
HttpClientContext clientContext = HttpClientContext.adapt(context);
|
||||
HttpHost target = clientContext.getTargetHost();
|
||||
HttpRequest request = clientContext.getRequest();
|
||||
HttpResponse response = clientContext.getResponse();
|
||||
]]></programlisting>
|
||||
<para>stdout ></para>
|
||||
<para>Multiple request sequences that represent a logically related session should be
|
||||
executed with the same <interfacename>HttpContext</interfacename> instance to ensure
|
||||
automatic propagation of conversation context and state information between
|
||||
requests.</para>
|
||||
<para>In the following example the request configuration set by the initial request will be
|
||||
kept in the execution context and get propagatd to the consecutive requests sharing
|
||||
the same context.</para>
|
||||
<programlisting><![CDATA[
|
||||
Final target: http://www.google.ch
|
||||
CloseableHttpClient httpclient = <...>
|
||||
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setSocketTimeout(1000)
|
||||
.setConnectTimeout(1000)
|
||||
.build();
|
||||
|
||||
HttpGet httpget1 = new HttpGet("http://localhost/1");
|
||||
httpget1.setConfig(requestConfig);
|
||||
CloseableHttpResponse response1 = httpclient.execute(httpget1, context);
|
||||
try {
|
||||
HttpEntity entity1 = response1.getEntity();
|
||||
} finally {
|
||||
response1.close();
|
||||
}
|
||||
HttpGet httpget2 = new HttpGet("http://localhost/2");
|
||||
CloseableHttpResponse response2 = httpclient.execute(httpget2, context);
|
||||
try {
|
||||
HttpEntity entity2 = response2.getEntity();
|
||||
} finally {
|
||||
response2.close();
|
||||
}
|
||||
]]></programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>Exception handling</title>
|
||||
<para>HttpClient can throw two types of exceptions:
|
||||
<exceptionname>java.io.IOException</exceptionname> in case of an I/O failure such as
|
||||
<exceptionname>java.io.IOException</exceptionname> in case of an I/O failure such as
|
||||
socket timeout or an socket reset and <exceptionname>HttpException</exceptionname> that
|
||||
signals an HTTP failure such as a violation of the HTTP protocol. Usually I/O errors are
|
||||
considered non-fatal and recoverable, whereas HTTP protocol errors are considered fatal
|
||||
|
@ -628,12 +613,10 @@ Final target: http://www.google.ch
|
|||
implementation of the <interfacename>HttpRequestRetryHandler</interfacename>
|
||||
interface.</para>
|
||||
<programlisting><![CDATA[
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
|
||||
HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
|
||||
|
||||
public boolean retryRequest(
|
||||
IOException exception,
|
||||
IOException exception,
|
||||
int executionCount,
|
||||
HttpContext context) {
|
||||
if (executionCount >= 5) {
|
||||
|
@ -648,7 +631,7 @@ HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
|
|||
// Unknown host
|
||||
return false;
|
||||
}
|
||||
if (exception instanceof ConnectException) {
|
||||
if (exception instanceof ConnectTimeoutException) {
|
||||
// Connection refused
|
||||
return false;
|
||||
}
|
||||
|
@ -656,19 +639,20 @@ HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
|
|||
// SSL handshake exception
|
||||
return false;
|
||||
}
|
||||
HttpRequest request = (HttpRequest) context.getAttribute(
|
||||
ExecutionContext.HTTP_REQUEST);
|
||||
boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
|
||||
HttpClientContext clientContext = HttpClientContext.adapt(context);
|
||||
HttpRequest request = clientContext.getRequest();
|
||||
boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
|
||||
if (idempotent) {
|
||||
// Retry if the request is considered idempotent
|
||||
// Retry if the request is considered idempotent
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
httpclient.setHttpRequestRetryHandler(myRetryHandler);
|
||||
CloseableHttpClient httpclient = HttpClients.custom()
|
||||
.setRetryHandler(myRetryHandler)
|
||||
.build();
|
||||
]]></programlisting>
|
||||
</section>
|
||||
</section>
|
||||
|
@ -708,227 +692,32 @@ httpclient.setHttpRequestRetryHandler(myRetryHandler);
|
|||
<para>This is an example of how local context can be used to persist a processing state
|
||||
between consecutive requests:</para>
|
||||
<programlisting><![CDATA[
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
CloseableHttpClient httpclient = HttpClients.custom()
|
||||
.addInterceptorLast(new HttpRequestInterceptor() {
|
||||
|
||||
HttpContext localContext = new BasicHttpContext();
|
||||
public void process(
|
||||
final HttpRequest request,
|
||||
final HttpContext context) throws HttpException, IOException {
|
||||
AtomicInteger count = (AtomicInteger) context.getAttribute("count");
|
||||
request.addHeader("Count", Integer.toString(count.getAndIncrement()));
|
||||
}
|
||||
|
||||
})
|
||||
.build();
|
||||
|
||||
AtomicInteger count = new AtomicInteger(1);
|
||||
|
||||
HttpClientContext localContext = HttpClientContext.create();
|
||||
localContext.setAttribute("count", count);
|
||||
|
||||
httpclient.addRequestInterceptor(new HttpRequestInterceptor() {
|
||||
|
||||
public void process(
|
||||
final HttpRequest request,
|
||||
final HttpContext context) throws HttpException, IOException {
|
||||
AtomicInteger count = (AtomicInteger) context.getAttribute("count");
|
||||
request.addHeader("Count", Integer.toString(count.getAndIncrement()));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
HttpGet httpget = new HttpGet("http://localhost/");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
HttpResponse response = httpclient.execute(httpget, localContext);
|
||||
|
||||
HttpEntity entity = response.getEntity();
|
||||
EntityUtils.consume(entity);
|
||||
CloseableHttpResponse response = httpclient.execute(httpget, localContext);
|
||||
try {
|
||||
HttpEntity entity = response.getEntity();
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
]]></programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>HTTP parameters</title>
|
||||
<para>The HttpParams interface represents a collection of immutable values that define a runtime
|
||||
behavior of a component. In many ways <interfacename>HttpParams</interfacename> is
|
||||
similar to <interfacename>HttpContext</interfacename>. The main distinction between the
|
||||
two lies in their use at runtime. Both interfaces represent a collection of objects that
|
||||
are organized as a map of keys to object values, but serve distinct purposes:</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><interfacename>HttpParams</interfacename> is intended to contain simple
|
||||
objects: integers, doubles, strings, collections and objects that remain
|
||||
immutable at runtime.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<interfacename>HttpParams</interfacename> is expected to be used in the 'write
|
||||
once - ready many' mode. <interfacename>HttpContext</interfacename> is intended
|
||||
to contain complex objects that are very likely to mutate in the course of HTTP
|
||||
message processing. </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The purpose of <interfacename>HttpParams</interfacename> is to define a
|
||||
behavior of other components. Usually each complex component has its own
|
||||
<interfacename>HttpParams</interfacename> object. The purpose of
|
||||
<interfacename>HttpContext</interfacename> is to represent an execution
|
||||
state of an HTTP process. Usually the same execution context is shared among
|
||||
many collaborating objects.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<section>
|
||||
<title>Parameter hierarchies</title>
|
||||
<para>In the course of HTTP request execution <interfacename>HttpParams</interfacename>
|
||||
of the <interfacename>HttpRequest</interfacename> object are linked together with
|
||||
<interfacename>HttpParams</interfacename> of the
|
||||
<interfacename>HttpClient</interfacename> instance used to execute the request.
|
||||
This enables parameters set at the HTTP request level to take precedence over
|
||||
<interfacename>HttpParams</interfacename> set at the HTTP client level. The
|
||||
recommended practice is to set common parameters shared by all HTTP requests at the
|
||||
HTTP client level and selectively override specific parameters at the HTTP request
|
||||
level.</para>
|
||||
<programlisting><![CDATA[
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
|
||||
HttpVersion.HTTP_1_0); // Default to HTTP 1.0
|
||||
httpclient.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET,
|
||||
"UTF-8");
|
||||
|
||||
HttpGet httpget = new HttpGet("http://www.google.com/");
|
||||
httpget.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
|
||||
HttpVersion.HTTP_1_1); // Use HTTP 1.1 for this request only
|
||||
httpget.getParams().setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE,
|
||||
Boolean.FALSE);
|
||||
|
||||
httpclient.addRequestInterceptor(new HttpRequestInterceptor() {
|
||||
|
||||
public void process(
|
||||
final HttpRequest request,
|
||||
final HttpContext context) throws HttpException, IOException {
|
||||
System.out.println(request.getParams().getParameter(
|
||||
CoreProtocolPNames.PROTOCOL_VERSION));
|
||||
System.out.println(request.getParams().getParameter(
|
||||
CoreProtocolPNames.HTTP_CONTENT_CHARSET));
|
||||
System.out.println(request.getParams().getParameter(
|
||||
CoreProtocolPNames.USE_EXPECT_CONTINUE));
|
||||
System.out.println(request.getParams().getParameter(
|
||||
CoreProtocolPNames.STRICT_TRANSFER_ENCODING));
|
||||
}
|
||||
|
||||
});
|
||||
]]></programlisting>
|
||||
<para>stdout ></para>
|
||||
<programlisting><![CDATA[
|
||||
HTTP/1.1
|
||||
UTF-8
|
||||
false
|
||||
null
|
||||
]]></programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>HTTP parameters beans</title>
|
||||
<para>The <interfacename>HttpParams</interfacename> interface allows for a great deal of
|
||||
flexibility in handling configuration of components. Most importantly, new
|
||||
parameters can be introduced without affecting binary compatibility with older
|
||||
versions. However, <interfacename>HttpParams</interfacename> also has a certain
|
||||
disadvantage compared to regular Java beans:
|
||||
<interfacename>HttpParams</interfacename> cannot be assembled using a DI
|
||||
framework. To mitigate the limitation, HttpClient includes a number of bean classes
|
||||
that can used in order to initialize <interfacename>HttpParams</interfacename>
|
||||
objects using standard Java bean conventions.</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpParams params = new BasicHttpParams();
|
||||
HttpProtocolParamBean paramsBean = new HttpProtocolParamBean(params);
|
||||
paramsBean.setVersion(HttpVersion.HTTP_1_1);
|
||||
paramsBean.setContentCharset("UTF-8");
|
||||
paramsBean.setUseExpectContinue(true);
|
||||
|
||||
System.out.println(params.getParameter(
|
||||
CoreProtocolPNames.PROTOCOL_VERSION));
|
||||
System.out.println(params.getParameter(
|
||||
CoreProtocolPNames.HTTP_CONTENT_CHARSET));
|
||||
System.out.println(params.getParameter(
|
||||
CoreProtocolPNames.USE_EXPECT_CONTINUE));
|
||||
System.out.println(params.getParameter(
|
||||
CoreProtocolPNames.USER_AGENT));
|
||||
]]></programlisting>
|
||||
<para>stdout ></para>
|
||||
<programlisting><![CDATA[
|
||||
HTTP/1.1
|
||||
UTF-8
|
||||
false
|
||||
null
|
||||
]]></programlisting>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>HTTP request execution parameters</title>
|
||||
<para>These are parameters that can impact the process of request execution:</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreProtocolPNames.PROTOCOL_VERSION</constant>='http.protocol.version':</title>
|
||||
<para>defines HTTP protocol version used if not set explicitly on the request
|
||||
object. This parameter expects a value of type
|
||||
<interfacename>ProtocolVersion</interfacename>. If this parameter is not
|
||||
set HTTP/1.1 will be used.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreProtocolPNames.HTTP_ELEMENT_CHARSET</constant>='http.protocol.element-charset':</title>
|
||||
<para>defines the charset to be used for encoding HTTP protocol elements. This
|
||||
parameter expects a value of type <classname>java.lang.String</classname>.
|
||||
If this parameter is not set <literal>US-ASCII</literal> will be
|
||||
used.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreProtocolPNames.HTTP_CONTENT_CHARSET</constant>='http.protocol.content-charset':</title>
|
||||
<para>defines the charset to be used per default for content body coding. This
|
||||
parameter expects a value of type <classname>java.lang.String</classname>.
|
||||
If this parameter is not set <literal>ISO-8859-1</literal> will be
|
||||
used.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreProtocolPNames.USER_AGENT</constant>='http.useragent':</title>
|
||||
<para>defines the content of the <literal>User-Agent</literal> header. This
|
||||
parameter expects a value of type <classname>java.lang.String</classname>.
|
||||
If this parameter is not set, HttpClient will automatically generate a value
|
||||
for it.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreProtocolPNames.STRICT_TRANSFER_ENCODING</constant>='http.protocol.strict-transfer-encoding':</title>
|
||||
<para>defines whether responses with an invalid
|
||||
<literal>Transfer-Encoding</literal> header should be rejected. This
|
||||
parameter expects a value of type <classname>java.lang.Boolean</classname>.
|
||||
If this parameter is not set, invalid <literal>Transfer-Encoding</literal>
|
||||
values will be ignored.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreProtocolPNames.USE_EXPECT_CONTINUE</constant>='http.protocol.expect-continue':</title>
|
||||
<para>activates the <literal>Expect: 100-Continue</literal> handshake for the entity
|
||||
enclosing methods. The purpose of the <literal>Expect:
|
||||
100-Continue</literal> handshake is to allow the client that is sending
|
||||
a request message with a request body to determine if the origin server is
|
||||
willing to accept the request (based on the request headers) before the
|
||||
client sends the request body. The use of the <literal>Expect:
|
||||
100-continue</literal> handshake can result in a noticeable performance
|
||||
improvement for entity enclosing requests (such as <literal>POST</literal>
|
||||
and <literal>PUT</literal>) that require the target server's authentication.
|
||||
The <literal>Expect: 100-continue</literal> handshake should be used with
|
||||
caution, as it may cause problems with HTTP servers and proxies that do not
|
||||
support HTTP/1.1 protocol. This parameter expects a value of type
|
||||
<classname>java.lang.Boolean</classname>. If this parameter is not set,
|
||||
HttpClient will not attempt to use the handshake.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CoreProtocolPNames.WAIT_FOR_CONTINUE</constant>='http.protocol.wait-for-continue':</title>
|
||||
<para>defines the maximum period of time in milliseconds the client should spend
|
||||
waiting for a <literal>100-continue</literal> response. This parameter
|
||||
expects a value of type <classname>java.lang.Integer</classname>. If this
|
||||
parameter is not set HttpClient will wait 3 seconds for a confirmation
|
||||
before resuming the transmission of the request body.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</chapter>
|
||||
|
|
|
@ -70,9 +70,9 @@
|
|||
<para>
|
||||
HttpClient is NOT a browser. It is a client side HTTP transport library.
|
||||
HttpClient's purpose is to transmit and receive HTTP messages. HttpClient will not
|
||||
attempt to cache content, execute javascript embedded in HTML pages, try to guess
|
||||
content type, or reformat request / redirect location URIs, or other functionality
|
||||
unrelated to the HTTP transport.
|
||||
attempt to process content, execute javascript embedded in HTML pages, try to guess
|
||||
content type, if not explicitly set, or reformat request / redirect location URIs,
|
||||
or other functionality unrelated to the HTTP transport.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
|
|
@ -133,15 +133,8 @@ stdCookie.setAttribute(ClientCookie.PORT_ATTR, "80,8080");
|
|||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title>RFC 2109:</title>
|
||||
<para>Older version of the official HTTP state management specification
|
||||
superseded by RFC 2965.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title>RFC 2965:</title>
|
||||
<para>The official HTTP state management specification.</para>
|
||||
<title>Standard:</title>
|
||||
<para>RFC 2965 HTTP state management specification.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
|
@ -171,137 +164,61 @@ stdCookie.setAttribute(ClientCookie.PORT_ATTR, "80,8080");
|
|||
HttpClient pick up an appropriate compliance level at runtime based on the execution
|
||||
context.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>HTTP cookie and state management parameters</title>
|
||||
<para>These are parameters that be used to customize HTTP state management and the behaviour of
|
||||
individual cookie specifications:</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CookieSpecPNames.DATE_PATTERNS</constant>='http.protocol.cookie-datepatterns':</title>
|
||||
<para>defines valid date patterns to be used for parsing non-standard
|
||||
<literal>expires</literal> attribute. Only required for compatibility
|
||||
with non-compliant servers that still use <literal>expires</literal> defined
|
||||
in the Netscape draft instead of the standard <literal>max-age</literal>
|
||||
attribute. This parameter expects a value of type
|
||||
<interfacename>java.util.Collection</interfacename>. The collection
|
||||
elements must be of type <classname>java.lang.String</classname> compatible
|
||||
with the syntax of <classname>java.text.SimpleDateFormat</classname>. If
|
||||
this parameter is not set the choice of a default value is
|
||||
<interfacename>CookieSpec</interfacename> implementation specific.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>CookieSpecPNames.SINGLE_COOKIE_HEADER</constant>='http.protocol.single-cookie-header':</title>
|
||||
<para>defines whether cookies should be forced into a single
|
||||
<literal>Cookie</literal> request header. Otherwise, each cookie is
|
||||
formatted as a separate <literal>Cookie</literal> header. This parameter
|
||||
expects a value of type <classname>java.lang.Boolean</classname>. If this
|
||||
parameter is not set, the choice of a default value is CookieSpec
|
||||
implementation specific. Please note this parameter applies to strict cookie
|
||||
specifications (RFC 2109 and RFC 2965) only. Browser compatibility and
|
||||
netscape draft policies will always put all cookies into one request
|
||||
header.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ClientPNames.COOKIE_POLICY</constant>='http.protocol.cookie-policy':</title>
|
||||
<para>defines the name of a cookie specification to be used for HTTP state
|
||||
management. This parameter expects a value of type
|
||||
<classname>java.lang.String</classname>. If this parameter is not set,
|
||||
valid date patterns are <interfacename>CookieSpec</interfacename>
|
||||
implementation specific.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
<section>
|
||||
<title>Cookie specification registry</title>
|
||||
<para>HttpClient maintains a registry of available cookie specifications using
|
||||
the <classname>CookieSpecRegistry</classname> class. The following specifications are
|
||||
registered per default:</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title>compatibility:</title>
|
||||
<para> Browser compatibility (lenient policy).</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title>netscape:</title>
|
||||
<para>Netscape draft.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title>rfc2109:</title>
|
||||
<para>RFC 2109 (outdated strict policy).</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title>rfc2965:</title>
|
||||
<para>RFC 2965 (standard conformant strict policy).</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title>best-match:</title>
|
||||
<para>Best match meta-policy.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title>ignoreCookies:</title>
|
||||
<para>All cookies are ignored.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
<section>
|
||||
<title>Choosing cookie policy</title>
|
||||
<para>Cookie policy can be set at the HTTP client and overridden on the HTTP request level
|
||||
if required.</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpClient httpclient = new DefaultHttpClient();
|
||||
// force strict cookie policy per default
|
||||
httpclient.getParams().setParameter(
|
||||
ClientPNames.COOKIE_POLICY, CookiePolicy.RFC_2965);
|
||||
|
||||
HttpGet httpget = new HttpGet("http://www.broken-server.com/");
|
||||
// Override the default policy for this request
|
||||
httpget.getParams().setParameter(
|
||||
ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
|
||||
RequestConfig globalConfig = RequestConfig.custom()
|
||||
.setCookieSpec(CookieSpecs.BEST_MATCH)
|
||||
.build();
|
||||
CloseableHttpClient httpclient = HttpClients.custom()
|
||||
.setDefaultRequestConfig(globalConfig)
|
||||
.build();
|
||||
RequestConfig localConfig = RequestConfig.copy(globalConfig)
|
||||
.setCookieSpec(CookieSpecs.BROWSER_COMPATIBILITY)
|
||||
.build();
|
||||
HttpGet httpGet = new HttpGet("/");
|
||||
httpGet.setConfig(localConfig);
|
||||
]]></programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>Custom cookie policy</title>
|
||||
<para>In order to implement a custom cookie policy one should create a custom implementation
|
||||
of the <interfacename>CookieSpec</interfacename> interface, create a
|
||||
<interfacename>CookieSpecFactory</interfacename> implementation to create and
|
||||
<interfacename>CookieSpecProvider</interfacename> implementation to create and
|
||||
initialize instances of the custom specification and register the factory with
|
||||
HttpClient. Once the custom specification has been registered, it can be activated the
|
||||
same way as a standard cookie specification.</para>
|
||||
<programlisting><![CDATA[
|
||||
CookieSpecFactory csf = new CookieSpecFactory() {
|
||||
public CookieSpec newInstance(HttpParams params) {
|
||||
return new BrowserCompatSpec() {
|
||||
CookieSpecProvider easySpecProvider = new CookieSpecProvider() {
|
||||
|
||||
public CookieSpec create(HttpContext context) {
|
||||
|
||||
return new BrowserCompatSpec() {
|
||||
@Override
|
||||
public void validate(Cookie cookie, CookieOrigin origin)
|
||||
throws MalformedCookieException {
|
||||
throws MalformedCookieException {
|
||||
// Oh, I am easy
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
httpclient.getCookieSpecs().register("easy", csf);
|
||||
httpclient.getParams().setParameter(
|
||||
ClientPNames.COOKIE_POLICY, "easy");
|
||||
};
|
||||
Registry<CookieSpecProvider> cookieSpecReg = RegistryBuilder.<CookieSpecProvider>create()
|
||||
.register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory())
|
||||
.register(CookieSpecs.BROWSER_COMPATIBILITY, new BrowserCompatSpecFactory())
|
||||
.register("easy", easySpecProvider)
|
||||
.build();
|
||||
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setCookieSpec("easy")
|
||||
.build();
|
||||
|
||||
CloseableHttpClient httpclient = HttpClients.custom()
|
||||
.setDefaultCookieSpecRegistry(cookieSpecReg)
|
||||
.setDefaultRequestConfig(requestConfig)
|
||||
.build();
|
||||
]]></programlisting>
|
||||
</section>
|
||||
<section>
|
||||
|
@ -315,17 +232,18 @@ httpclient.getParams().setParameter(
|
|||
get garbage collected. Users can provide more complex implementations if
|
||||
necessary.</para>
|
||||
<programlisting><![CDATA[
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
// Create a local instance of cookie store
|
||||
CookieStore cookieStore = new MyCookieStore();
|
||||
CookieStore cookieStore = new BasicCookieStore();
|
||||
// Populate cookies if needed
|
||||
BasicClientCookie cookie = new BasicClientCookie("name", "value");
|
||||
cookie.setVersion(0);
|
||||
cookie.setDomain(".mycompany.com");
|
||||
cookie.setPath("/");
|
||||
cookieStore.addCookie(cookie);
|
||||
// Set the store
|
||||
httpclient.setCookieStore(cookieStore);
|
||||
// Set the store
|
||||
CloseableHttpClient httpclient = HttpClients.custom()
|
||||
.setDefaultCookieStore(cookieStore)
|
||||
.build();
|
||||
]]></programlisting>
|
||||
</section>
|
||||
<section>
|
||||
|
@ -335,7 +253,6 @@ httpclient.setCookieStore(cookieStore);
|
|||
<itemizedlist>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ClientContext.COOKIESPEC_REGISTRY</constant>='http.cookiespec-registry':</title>
|
||||
<para><classname>CookieSpecRegistry</classname> instance representing the actual
|
||||
cookie specification registry. The value of this attribute set in the local
|
||||
context takes precedence over the default one.</para>
|
||||
|
@ -343,21 +260,18 @@ httpclient.setCookieStore(cookieStore);
|
|||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ClientContext.COOKIE_SPEC</constant>='http.cookie-spec':</title>
|
||||
<para><interfacename>CookieSpec</interfacename> instance representing the actual
|
||||
cookie specification.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ClientContext.COOKIE_ORIGIN</constant>='http.cookie-origin':</title>
|
||||
<para><classname>CookieOrigin</classname> instance representing the actual
|
||||
details of the origin server.</para>
|
||||
</formalpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<formalpara>
|
||||
<title><constant>ClientContext.COOKIE_STORE</constant>='http.cookie-store':</title>
|
||||
<para><interfacename>CookieStore</interfacename> instance representing the actual
|
||||
cookie store. The value of this attribute set in the local context takes
|
||||
precedence over the default one.</para>
|
||||
|
@ -366,38 +280,26 @@ httpclient.setCookieStore(cookieStore);
|
|||
</itemizedlist>
|
||||
<para>The local <interfacename>HttpContext</interfacename> object can be used to customize
|
||||
the HTTP state management context prior to request execution, or to examine its state after
|
||||
the request has been executed:</para>
|
||||
the request has been executed. One can also use separate execution contexts in order
|
||||
to implement per user (or per thread) state management. A cookie specification registry
|
||||
and cookie store defined in the local context will take precedence over the default
|
||||
ones set at the HTTP client level</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpClient httpclient = new DefaultHttpClient();
|
||||
HttpContext localContext = new BasicHttpContext();
|
||||
HttpGet httpget = new HttpGet("http://localhost:8080/");
|
||||
HttpResponse response = httpclient.execute(httpget, localContext);
|
||||
CloseableHttpClient httpclient = <...>
|
||||
|
||||
CookieOrigin cookieOrigin = (CookieOrigin) localContext.getAttribute(
|
||||
ClientContext.COOKIE_ORIGIN);
|
||||
System.out.println("Cookie origin: " + cookieOrigin);
|
||||
CookieSpec cookieSpec = (CookieSpec) localContext.getAttribute(
|
||||
ClientContext.COOKIE_SPEC);
|
||||
System.out.println("Cookie spec used: " + cookieSpec);
|
||||
]]></programlisting>
|
||||
</section>
|
||||
<section>
|
||||
<title>Per user / thread state management</title>
|
||||
<para>One can use an individual local execution context in order to implement per user (or
|
||||
per thread) state management. A cookie specification registry and cookie store defined in
|
||||
the local context will take precedence over the default ones set at the HTTP client
|
||||
level.</para>
|
||||
<programlisting><![CDATA[
|
||||
HttpClient httpclient = new DefaultHttpClient();
|
||||
// Create a local instance of cookie store
|
||||
CookieStore cookieStore = new BasicCookieStore();
|
||||
// Create local HTTP context
|
||||
HttpContext localContext = new BasicHttpContext();
|
||||
// Bind custom cookie store to the local context
|
||||
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
|
||||
HttpGet httpget = new HttpGet("http://www.google.com/");
|
||||
// Pass local context as a parameter
|
||||
HttpResponse response = httpclient.execute(httpget, localContext);
|
||||
Registry<CookieSpecProvider> cookieSpecReg = <...>
|
||||
CookieStore cookieStore = <...>
|
||||
|
||||
HttpClientContext context = HttpClientContext.create();
|
||||
context.setCookieSpecRegistry(cookieSpecReg);
|
||||
context.setCookieStore(cookieStore);
|
||||
HttpGet httpget = new HttpGet("http://localhost/1");
|
||||
CloseableHttpResponse response1 = httpclient.execute(httpget, context);
|
||||
<...>
|
||||
// Cookie origin details
|
||||
CookieOrigin cookieOrigin = context.getCookieOrigin();
|
||||
// Cookie spec used
|
||||
CookieSpec cookieSpec = context.getCookieSpec();
|
||||
]]></programlisting>
|
||||
</section>
|
||||
</chapter>
|
||||
|
|
Loading…
Reference in New Issue