Updated HttpClient tutorial with 4.2 API changes
git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1240534 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
10f6da691e
commit
2737e5f066
|
@ -113,7 +113,7 @@ pwd
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
<formalpara>
|
<formalpara>
|
||||||
<title>SPNEGO/Kerberos:</title>
|
<title>SPNEGO:</title>
|
||||||
<para><literal>SPNEGO</literal> (<emphasis>S</emphasis>imple and
|
<para><literal>SPNEGO</literal> (<emphasis>S</emphasis>imple and
|
||||||
<emphasis>P</emphasis>rotected <literal>GSSAPI</literal>
|
<emphasis>P</emphasis>rotected <literal>GSSAPI</literal>
|
||||||
<emphasis>Nego</emphasis>tiation Mechanism) is a <literal>GSSAPI</literal>
|
<emphasis>Nego</emphasis>tiation Mechanism) is a <literal>GSSAPI</literal>
|
||||||
|
@ -124,6 +124,12 @@ pwd
|
||||||
At present HttpClient only supports the Kerberos sub-mechanism. </para>
|
At present HttpClient only supports the Kerberos sub-mechanism. </para>
|
||||||
</formalpara>
|
</formalpara>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<formalpara>
|
||||||
|
<title>Kerberos:</title>
|
||||||
|
<para>Kerberos authentication implementation. </para>
|
||||||
|
</formalpara>
|
||||||
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
@ -209,7 +215,13 @@ httpclient.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF, authpref);
|
||||||
<listitem>
|
<listitem>
|
||||||
<formalpara>
|
<formalpara>
|
||||||
<title>AuthPolicy.SPNEGO:</title>
|
<title>AuthPolicy.SPNEGO:</title>
|
||||||
<para>SPNEGO/Kerberos authentication</para>
|
<para>SPNEGO authentication</para>
|
||||||
|
</formalpara>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<formalpara>
|
||||||
|
<title>AuthPolicy.KERBEROS:</title>
|
||||||
|
<para>Kerberos authentication</para>
|
||||||
</formalpara>
|
</formalpara>
|
||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
@ -327,12 +339,12 @@ HttpResponse response = httpclient.execute(httpget, localContext);
|
||||||
|
|
||||||
AuthState proxyAuthState = (AuthState) localContext.getAttribute(
|
AuthState proxyAuthState = (AuthState) localContext.getAttribute(
|
||||||
ClientContext.PROXY_AUTH_STATE);
|
ClientContext.PROXY_AUTH_STATE);
|
||||||
System.out.println("Proxy auth scope: " + proxyAuthState.getAuthScope());
|
System.out.println("Proxy auth state: " + proxyAuthState.getState());
|
||||||
System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
|
System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
|
||||||
System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
|
System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
|
||||||
AuthState targetAuthState = (AuthState) localContext.getAttribute(
|
AuthState targetAuthState = (AuthState) localContext.getAttribute(
|
||||||
ClientContext.TARGET_AUTH_STATE);
|
ClientContext.TARGET_AUTH_STATE);
|
||||||
System.out.println("Target auth scope: " + targetAuthState.getAuthScope());
|
System.out.println("Target auth state: " + targetAuthState.getState());
|
||||||
System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
|
System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
|
||||||
System.out.println("Target auth credentials: " + targetAuthState.getCredentials());
|
System.out.println("Target auth credentials: " + targetAuthState.getCredentials());
|
||||||
]]></programlisting>
|
]]></programlisting>
|
||||||
|
@ -490,7 +502,7 @@ EntityUtils.consume(entity2);
|
||||||
supports <literal>SPNEGO</literal> authentication more completely.</para>
|
supports <literal>SPNEGO</literal> authentication more completely.</para>
|
||||||
<para>The Sun JRE provides the supporting classes to do nearly all the Kerberos and
|
<para>The Sun JRE provides the supporting classes to do nearly all the Kerberos and
|
||||||
<literal>SPNEGO</literal> token handling. This means that a lot of the setup is
|
<literal>SPNEGO</literal> token handling. This means that a lot of the setup is
|
||||||
for the GSS classes. The <classname>NegotiateScheme</classname> is a simple class to
|
for the GSS classes. The <classname>SPNegoScheme</classname> is a simple class to
|
||||||
handle marshalling the tokens and reading and writing the correct headers.</para>
|
handle marshalling the tokens and reading and writing the correct headers.</para>
|
||||||
<para>The best way to start is to grab the <literal>KerberosHttpClient.java</literal>
|
<para>The best way to start is to grab the <literal>KerberosHttpClient.java</literal>
|
||||||
file in examples and try and get it to work. There are a lot of issues that can
|
file in examples and try and get it to work. There are a lot of issues that can
|
||||||
|
@ -583,40 +595,6 @@ Value: 0x01
|
||||||
]]>
|
]]>
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
|
||||||
<title>Customizing <literal>SPNEGO</literal> authentication scheme</title>
|
|
||||||
<para>In order to customize <literal>SPNEGO</literal> support a new instance of
|
|
||||||
the <classname>NegotiateSchemeFactory</classname> class must be created and
|
|
||||||
registered with the authentication scheme registry of HttpClient. </para>
|
|
||||||
<programlisting><![CDATA[
|
|
||||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
|
||||||
NegotiateSchemeFactory nsf = new NegotiateSchemeFactory();
|
|
||||||
httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);
|
|
||||||
]]>
|
|
||||||
</programlisting>
|
|
||||||
<para>There are several options that can be used to customize the behaviour of
|
|
||||||
<classname>NegotiateSchemeFactory</classname>. </para>
|
|
||||||
<section>
|
|
||||||
<title>Strip port</title>
|
|
||||||
<para>Strips the port off service names e.g.
|
|
||||||
<literal>HTTP/webserver.ad.example.net:8080</literal> ->
|
|
||||||
<literal>HTTP/webserver.ad.example.net</literal></para>
|
|
||||||
<para>Found it useful when authenticating against JBoss Negotiation.</para>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<title>Custom <literal>SPNEGO</literal> token generator</title>
|
|
||||||
<para>Use this method to inject a custom
|
|
||||||
<interfacename>SpnegoTokenGenerator</interfacename> class to do the Kerberos
|
|
||||||
to <literal>SPNEGO</literal> token wrapping.
|
|
||||||
The <classname>BouncySpnegoTokenGenerator</classname> implementation is provided
|
|
||||||
as an unsupported contribution from the contrib package. This requires the
|
|
||||||
BouncyCastle libraries <ulink url="http://www.bouncycastle.org/java.html"
|
|
||||||
>"http://www.bouncycastle.org/java.html"</ulink>. Found especially useful
|
|
||||||
when using Java 1.5, which is known to provide only a limited support for
|
|
||||||
<literal>SPNEGO</literal> authentication.
|
|
||||||
</para>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
|
@ -270,37 +270,14 @@ sf.connectSocket(socket, address, null, params);
|
||||||
<interfacename>javax.net.ssl.SSLContext</interfacename> as a parameter and use
|
<interfacename>javax.net.ssl.SSLContext</interfacename> as a parameter and use
|
||||||
it to create custom configured SSL connections.</para>
|
it to create custom configured SSL connections.</para>
|
||||||
<programlisting><![CDATA[
|
<programlisting><![CDATA[
|
||||||
TrustManager easyTrustManager = new X509TrustManager() {
|
HttpParams params = new BasicHttpParams();
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkClientTrusted(
|
|
||||||
X509Certificate[] chain,
|
|
||||||
String authType) throws CertificateException {
|
|
||||||
// Oh, I am easy!
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkServerTrusted(
|
|
||||||
X509Certificate[] chain,
|
|
||||||
String authType) throws CertificateException {
|
|
||||||
// Oh, I am easy!
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public X509Certificate[] getAcceptedIssuers() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
SSLContext sslcontext = SSLContext.getInstance("TLS");
|
SSLContext sslcontext = SSLContext.getInstance("TLS");
|
||||||
sslcontext.init(null, new TrustManager[] { easyTrustManager }, null);
|
sslcontext.init(null, null, null);
|
||||||
|
|
||||||
SSLSocketFactory sf = new SSLSocketFactory(sslcontext);
|
SSLSocketFactory sf = new SSLSocketFactory(sslcontext);
|
||||||
SSLSocket socket = (SSLSocket) sf.createSocket();
|
SSLSocket socket = (SSLSocket) sf.createSocket(params);
|
||||||
socket.setEnabledCipherSuites(new String[] { "SSL_RSA_WITH_RC4_128_MD5" });
|
socket.setEnabledCipherSuites(new String[] { "SSL_RSA_WITH_RC4_128_MD5" });
|
||||||
|
|
||||||
HttpParams params = new BasicHttpParams();
|
|
||||||
params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000L);
|
params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000L);
|
||||||
InetSocketAddress address = new InetSocketAddress("locahost", 443);
|
InetSocketAddress address = new InetSocketAddress("locahost", 443);
|
||||||
sf.connectSocket(socket, address, null, params);
|
sf.connectSocket(socket, address, null, params);
|
||||||
|
@ -485,7 +462,7 @@ httpclient.setRoutePlanner(new HttpRoutePlanner() {
|
||||||
Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory());
|
Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory());
|
||||||
SchemeRegistry sr = new SchemeRegistry();
|
SchemeRegistry sr = new SchemeRegistry();
|
||||||
sr.register(http);
|
sr.register(http);
|
||||||
ClientConnectionManager connMrg = new SingleClientConnManager(sr);
|
ClientConnectionManager connMrg = new BasicClientConnectionManager(sr);
|
||||||
|
|
||||||
// Request new connection. This can be a long process
|
// Request new connection. This can be a long process
|
||||||
ClientConnectionRequest connRequest = connMrg.requestConnection(
|
ClientConnectionRequest connRequest = connMrg.requestConnection(
|
||||||
|
@ -539,27 +516,27 @@ try {
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title>Simple connection manager</title>
|
<title>Simple connection manager</title>
|
||||||
<para><classname>SingleClientConnManager</classname> is a simple connection manager that
|
<para><classname>BasicClientConnectionManager</classname> is a simple connection manager
|
||||||
maintains only one connection at a time. Even though this class is thread-safe it
|
that maintains only one connection at a time. Even though this class is thread-safe
|
||||||
ought to be used by one execution thread only.
|
it ought to be used by one execution thread only.
|
||||||
<classname>SingleClientConnManager</classname> will make an effort to reuse the
|
<classname>BasicClientConnectionManager</classname> will make an effort to reuse
|
||||||
connection for subsequent requests with the same route. It will, however, close the
|
the connection for subsequent requests with the same route. It will, however, close
|
||||||
existing connection and re-open it for the given route, if the route of the persistent
|
the existing connection and re-open it for the given route, if the route of the
|
||||||
connection does not match that of the connection request. If the connection has been
|
persistent connection does not match that of the connection request.
|
||||||
already been allocated, then
|
If the connection has been already been allocated, then <exceptionname>
|
||||||
<exceptionname>java.lang.IllegalStateException</exceptionname> is thrown.</para>
|
java.lang.IllegalStateException</exceptionname> is thrown.</para>
|
||||||
<para><classname>SingleClientConnManager</classname> is used by HttpClient per
|
<para><classname>BasicClientConnectionManager</classname> is used by HttpClient per
|
||||||
default.</para>
|
default.</para>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title>Pooling connection manager</title>
|
<title>Pooling connection manager</title>
|
||||||
<para><classname>ThreadSafeClientConnManager</classname> is a more complex
|
<para><classname>PoolingClientConnectionManager</classname> is a more complex
|
||||||
implementation that manages a pool of client connections and is able to service
|
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
|
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
|
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
|
connection available in the pool will be serviced by leasing a connection from
|
||||||
the pool rather than creating a brand new connection.</para>
|
the pool rather than creating a brand new connection.</para>
|
||||||
<para><classname>ThreadSafeClientConnManager</classname> maintains a maximum limit of
|
<para><classname>PoolingClientConnectionManager</classname> maintains a maximum limit of
|
||||||
connections on a per route basis and in total. Per default this implementation will
|
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
|
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
|
connections in total. For many real-world applications these limits may prove too
|
||||||
|
@ -573,14 +550,14 @@ schemeRegistry.register(
|
||||||
schemeRegistry.register(
|
schemeRegistry.register(
|
||||||
new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
|
new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
|
||||||
|
|
||||||
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(schemeRegistry);
|
PoolingClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry);
|
||||||
// Increase max total connection to 200
|
// Increase max total connection to 200
|
||||||
cm.setMaxTotalConnections(200);
|
cm.setMaxTotal(200);
|
||||||
// Increase default max connection per route to 20
|
// Increase default max connection per route to 20
|
||||||
cm.setDefaultMaxPerRoute(20);
|
cm.setDefaultMaxPerRoute(20);
|
||||||
// Increase max connections for localhost:80 to 50
|
// Increase max connections for localhost:80 to 50
|
||||||
HttpHost localhost = new HttpHost("locahost", 80);
|
HttpHost localhost = new HttpHost("locahost", 80);
|
||||||
cm.setMaxForRoute(new HttpRoute(localhost), 50);
|
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
|
||||||
|
|
||||||
HttpClient httpClient = new DefaultHttpClient(cm);
|
HttpClient httpClient = new DefaultHttpClient(cm);
|
||||||
]]></programlisting>
|
]]></programlisting>
|
||||||
|
@ -604,22 +581,23 @@ httpclient.getConnectionManager().shutdown();
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title>Multithreaded request execution</title>
|
<title>Multithreaded request execution</title>
|
||||||
<para>When equipped with a pooling connection manager such as ThreadSafeClientConnManager,
|
<para>When equipped with a pooling connection manager such as <classname>
|
||||||
HttpClient can be used to execute multiple requests simultaneously using multiple
|
PoolingClientConnectionManager</classname>, HttpClient can be used to execute multiple
|
||||||
threads of execution.</para>
|
requests simultaneously using multiple threads of execution.</para>
|
||||||
<para>The <classname>ThreadSafeClientConnManager</classname> will allocate connections based on
|
<para>The <classname>PoolingClientConnectionManager</classname> will allocate connections
|
||||||
its configuration. If all connections for a given route have already been leased, a
|
based on its configuration. If all connections for a given route have already been
|
||||||
request for a connection will block until a connection is released back to the pool. One
|
leased, a request for a connection will block until a connection is released back to
|
||||||
can ensure the connection manager does not block indefinitely in the connection request
|
the pool. One can ensure the connection manager does not block indefinitely in the
|
||||||
operation by setting <literal>'http.conn-manager.timeout'</literal> to a positive value.
|
connection request operation by setting <literal>'http.conn-manager.timeout'</literal>
|
||||||
If the connection request cannot be serviced within the given time period
|
to a positive value. If the connection request cannot be serviced within the given time
|
||||||
<exceptionname>ConnectionPoolTimeoutException</exceptionname> will be thrown.</para>
|
period <exceptionname>ConnectionPoolTimeoutException</exceptionname> will be thrown.
|
||||||
|
</para>
|
||||||
<programlisting><![CDATA[
|
<programlisting><![CDATA[
|
||||||
SchemeRegistry schemeRegistry = new SchemeRegistry();
|
SchemeRegistry schemeRegistry = new SchemeRegistry();
|
||||||
schemeRegistry.register(
|
schemeRegistry.register(
|
||||||
new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
||||||
|
|
||||||
ClientConnectionManager cm = new ThreadSafeClientConnManager(schemeRegistry);
|
ClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry);
|
||||||
HttpClient httpClient = new DefaultHttpClient(cm);
|
HttpClient httpClient = new DefaultHttpClient(cm);
|
||||||
|
|
||||||
// URIs to perform GETs on
|
// URIs to perform GETs on
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||||
|
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||||
|
<!--
|
||||||
|
====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
====================================================================
|
||||||
|
|
||||||
|
-->
|
||||||
|
<chapter id="fluent">
|
||||||
|
<title>Fluent API</title>
|
||||||
|
<section>
|
||||||
|
<title>Easy to use facade API</title>
|
||||||
|
<para>
|
||||||
|
As of version of 4.2 HttpClient comes with an easy to use facade API based on the concept
|
||||||
|
of a fluent interface. Fluent facade API exposes only the most fundamental functions of
|
||||||
|
HttpClient and is intended for simple use cases that do not require the full flexibility of
|
||||||
|
HttpClient. For instance, fluent facade API relieves the users from having to deal with
|
||||||
|
connection management and resource deallocation.
|
||||||
|
</para>
|
||||||
|
<para>Here are several examples of HTTP requests executed through the HC fluent API</para>
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
// Execute a GET with timeout settings and return response content as String.
|
||||||
|
Request.Get("http://somehost/")
|
||||||
|
.connectTimeout(1000)
|
||||||
|
.socketTimeout(1000)
|
||||||
|
.execute().returnContent().asString();
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
// Execute a POST with the 'expect-continue' handshake, using HTTP/1.1,
|
||||||
|
// containing a request body as String and return response content as byte array.
|
||||||
|
Request.Post("http://somehost/do-stuff")
|
||||||
|
.useExpectContinue()
|
||||||
|
.version(HttpVersion.HTTP_1_1)
|
||||||
|
.bodyString("Important stuff", ContentType.DEFAULT_TEXT)
|
||||||
|
.execute().returnContent().asBytes();
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
// Execute a POST with a custom header through the proxy containing a request body
|
||||||
|
// as an HTML form and save the result to the file
|
||||||
|
Request.Post("http://somehost/some-form")
|
||||||
|
.addHeader("X-Custom-header", "stuff")
|
||||||
|
.viaProxy(new HttpHost("myproxy", 8080))
|
||||||
|
.bodyForm(Form.form().add("username", "vip").add("password", "secret").build())
|
||||||
|
.execute().saveContent(new File("result.dump"));
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
<para>One can also use <classname>Executor</classname> directly in order to execute requests in
|
||||||
|
a specific security context whereby authentication details are cached and re-used for
|
||||||
|
subsequent requests.
|
||||||
|
</para>
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
Executor executor = Executor.newInstance()
|
||||||
|
.auth(new HttpHost("somehost"), "username", "password")
|
||||||
|
.auth(new HttpHost("myproxy", 8080), "username", "password")
|
||||||
|
.authPreemptive(new HttpHost("myproxy", 8080));
|
||||||
|
|
||||||
|
executor.execute(Request.Get("http://somehost/"))
|
||||||
|
.returnContent().asString();
|
||||||
|
|
||||||
|
executor.execute(Request.Post("http://somehost/do-stuff")
|
||||||
|
.useExpectContinue()
|
||||||
|
.bodyString("Important stuff", ContentType.DEFAULT_TEXT))
|
||||||
|
.returnContent().asString();
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
<section>
|
||||||
|
<title>Response handling</title>
|
||||||
|
<para>The fluent facade API generally relieves the users from having to deal with
|
||||||
|
connection management and resource deallocation. In most cases, though, this comes at
|
||||||
|
a price of having to buffer content of response messages in memory. It is highly
|
||||||
|
recommended to use <interfacename>ResponseHandler</interfacename> for HTTP response
|
||||||
|
processing in order to avoid having to buffer content in memory.</para>
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
Document result = Request.Get("http://somehost/content")
|
||||||
|
.execute().handleResponse(new ResponseHandler<Document>() {
|
||||||
|
|
||||||
|
public Document handleResponse(final HttpResponse response) throws IOException {
|
||||||
|
StatusLine statusLine = response.getStatusLine();
|
||||||
|
HttpEntity entity = response.getEntity();
|
||||||
|
if (statusLine.getStatusCode() >= 300) {
|
||||||
|
throw new HttpResponseException(
|
||||||
|
statusLine.getStatusCode(),
|
||||||
|
statusLine.getReasonPhrase());
|
||||||
|
}
|
||||||
|
if (entity == null) {
|
||||||
|
throw new ClientProtocolException("Response contains no content");
|
||||||
|
}
|
||||||
|
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
|
||||||
|
try {
|
||||||
|
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
|
||||||
|
ContentType contentType = ContentType.getOrDefault(entity);
|
||||||
|
if (!contentType.equals(ContentType.APPLICATION_XML)) {
|
||||||
|
throw new ClientProtocolException("Unexpected content type:" + contentType);
|
||||||
|
}
|
||||||
|
String charset = contentType.getCharset();
|
||||||
|
if (charset == null) {
|
||||||
|
charset = HTTP.DEFAULT_CONTENT_CHARSET;
|
||||||
|
}
|
||||||
|
return docBuilder.parse(entity.getContent(), charset);
|
||||||
|
} catch (ParserConfigurationException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
} catch (SAXException ex) {
|
||||||
|
throw new ClientProtocolException("Malformed XML document", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>Asynchronous execution</title>
|
||||||
|
<para>The fluent facade API can be used to execute multiple requests asynchronously using
|
||||||
|
background threads.
|
||||||
|
</para>
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
ExecutorService threadpool = Executors.newFixedThreadPool(2);
|
||||||
|
Async async = Async.newInstance().use(threadpool);
|
||||||
|
|
||||||
|
Request[] requests = new Request[] {
|
||||||
|
Request.Get("http://www.google.com/"),
|
||||||
|
Request.Get("http://www.yahoo.com/"),
|
||||||
|
Request.Get("http://www.apache.com/"),
|
||||||
|
Request.Get("http://www.apple.com/")
|
||||||
|
};
|
||||||
|
|
||||||
|
Queue<Future<Content>> queue = new LinkedList<Future<Content>>();
|
||||||
|
for (final Request request: requests) {
|
||||||
|
Future<Content> future = async.execute(request, new FutureCallback<Content>() {
|
||||||
|
|
||||||
|
public void failed(final Exception ex) {
|
||||||
|
System.out.println(ex.getMessage() + ": " + request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void completed(final Content content) {
|
||||||
|
System.out.println("Request completed: " + request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancelled() {
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
queue.add(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the queue
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</chapter>
|
|
@ -40,9 +40,10 @@ HttpResponse response = httpclient.execute(httpget);
|
||||||
HttpEntity entity = response.getEntity();
|
HttpEntity entity = response.getEntity();
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
InputStream instream = entity.getContent();
|
InputStream instream = entity.getContent();
|
||||||
int l;
|
try {
|
||||||
byte[] tmp = new byte[2048];
|
// do something useful
|
||||||
while ((l = instream.read(tmp)) != -1) {
|
} finally {
|
||||||
|
instream.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]]></programlisting>
|
]]></programlisting>
|
||||||
|
@ -65,28 +66,16 @@ if (entity != null) {
|
||||||
HttpGet httpget = new HttpGet(
|
HttpGet httpget = new HttpGet(
|
||||||
"http://www.google.com/search?hl=en&q=httpclient&btnG=Google+Search&aq=f&oq=");
|
"http://www.google.com/search?hl=en&q=httpclient&btnG=Google+Search&aq=f&oq=");
|
||||||
]]></programlisting>
|
]]></programlisting>
|
||||||
<para>HttpClient provides a number of utility methods to simplify creation and
|
<para>HttpClient provides <classname>URIBuilder</classname> utility class to simplify
|
||||||
modification of request URIs.</para>
|
creation and modification of request URIs.</para>
|
||||||
<para>URI can be assembled programmatically:</para>
|
|
||||||
<programlisting><![CDATA[
|
<programlisting><![CDATA[
|
||||||
URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search",
|
URIBuilder builder = new URIBuilder();
|
||||||
"q=httpclient&btnG=Google+Search&aq=f&oq=", null);
|
builder.setScheme("http").setHost("www.google.com").setPath("/search")
|
||||||
HttpGet httpget = new HttpGet(uri);
|
.setParameter("q", "httpclient")
|
||||||
System.out.println(httpget.getURI());
|
.setParameter("btnG", "Google Search")
|
||||||
]]></programlisting>
|
.setParameter("aq", "f")
|
||||||
<para>stdout ></para>
|
.setParameter("oq", "");
|
||||||
<programlisting><![CDATA[
|
URI uri = builder.build();
|
||||||
http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=
|
|
||||||
]]></programlisting>
|
|
||||||
<para>Query string can also be generated from individual parameters:</para>
|
|
||||||
<programlisting><![CDATA[
|
|
||||||
List<NameValuePair> qparams = new ArrayList<NameValuePair>();
|
|
||||||
qparams.add(new BasicNameValuePair("q", "httpclient"));
|
|
||||||
qparams.add(new BasicNameValuePair("btnG", "Google Search"));
|
|
||||||
qparams.add(new BasicNameValuePair("aq", "f"));
|
|
||||||
qparams.add(new BasicNameValuePair("oq", null));
|
|
||||||
URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search",
|
|
||||||
URLEncodedUtils.format(qparams, "UTF-8"), null);
|
|
||||||
HttpGet httpget = new HttpGet(uri);
|
HttpGet httpget = new HttpGet(uri);
|
||||||
System.out.println(httpget.getURI());
|
System.out.println(httpget.getURI());
|
||||||
]]></programlisting>
|
]]></programlisting>
|
||||||
|
@ -276,19 +265,16 @@ domain=localhost
|
||||||
supplied by the creator of the entity.</para>
|
supplied by the creator of the entity.</para>
|
||||||
<programlisting><![CDATA[
|
<programlisting><![CDATA[
|
||||||
StringEntity myEntity = new StringEntity("important message",
|
StringEntity myEntity = new StringEntity("important message",
|
||||||
"UTF-8");
|
ContentType.create("text/plain", "UTF-8"));
|
||||||
|
|
||||||
System.out.println(myEntity.getContentType());
|
System.out.println(myEntity.getContentType());
|
||||||
System.out.println(myEntity.getContentLength());
|
System.out.println(myEntity.getContentLength());
|
||||||
System.out.println(EntityUtils.getContentCharSet(myEntity));
|
|
||||||
System.out.println(EntityUtils.toString(myEntity));
|
System.out.println(EntityUtils.toString(myEntity));
|
||||||
System.out.println(EntityUtils.toByteArray(myEntity).length);
|
System.out.println(EntityUtils.toByteArray(myEntity).length);]]></programlisting>
|
||||||
]]></programlisting>
|
|
||||||
<para>stdout ></para>
|
<para>stdout ></para>
|
||||||
<programlisting><![CDATA[
|
<programlisting><![CDATA[
|
||||||
Content-Type: text/plain; charset=UTF-8
|
Content-Type: text/plain; charset=utf-8
|
||||||
17
|
17
|
||||||
UTF-8
|
|
||||||
important message
|
important message
|
||||||
17
|
17
|
||||||
]]></programlisting>
|
]]></programlisting>
|
||||||
|
@ -394,7 +380,7 @@ if (entity != null) {
|
||||||
<classname>FileEntity</classname>.</para>
|
<classname>FileEntity</classname>.</para>
|
||||||
<programlisting><![CDATA[
|
<programlisting><![CDATA[
|
||||||
File file = new File("somefile.txt");
|
File file = new File("somefile.txt");
|
||||||
FileEntity entity = new FileEntity(file, "text/plain; charset=\"UTF-8\"");
|
FileEntity entity = new FileEntity(file, ContentType.create("text/plain", "UTF-8"));
|
||||||
|
|
||||||
HttpPost httppost = new HttpPost("http://localhost/action.do");
|
HttpPost httppost = new HttpPost("http://localhost/action.do");
|
||||||
httppost.setEntity(entity);
|
httppost.setEntity(entity);
|
||||||
|
@ -404,34 +390,6 @@ httppost.setEntity(entity);
|
||||||
implement a custom <interfacename>HttpEntity</interfacename> class which is
|
implement a custom <interfacename>HttpEntity</interfacename> class which is
|
||||||
self-contained instead of using the generic <classname>InputStreamEntity</classname>.
|
self-contained instead of using the generic <classname>InputStreamEntity</classname>.
|
||||||
<classname>FileEntity</classname> can be a good starting point.</para>
|
<classname>FileEntity</classname> can be a good starting point.</para>
|
||||||
<section>
|
|
||||||
<title>Dynamic content entities</title>
|
|
||||||
<para>Often HTTP entities need to be generated dynamically based a particular
|
|
||||||
execution context. HttpClient provides support for dynamic entities by using
|
|
||||||
the <classname>EntityTemplate</classname> entity class and
|
|
||||||
<interfacename>ContentProducer</interfacename> interface. Content producers
|
|
||||||
are objects which produce their content on demand, by writing it out to an
|
|
||||||
output stream. They are expected to be able produce their content every time
|
|
||||||
they are requested to do so. So entities created with
|
|
||||||
<classname>EntityTemplate</classname> are generally self-contained and
|
|
||||||
repeatable.</para>
|
|
||||||
<programlisting><![CDATA[
|
|
||||||
ContentProducer cp = new ContentProducer() {
|
|
||||||
public void writeTo(OutputStream outstream) throws IOException {
|
|
||||||
Writer writer = new OutputStreamWriter(outstream, "UTF-8");
|
|
||||||
writer.write("<response>");
|
|
||||||
writer.write(" <content>");
|
|
||||||
writer.write(" important stuff");
|
|
||||||
writer.write(" </content>");
|
|
||||||
writer.write("</response>");
|
|
||||||
writer.flush();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
HttpEntity entity = new EntityTemplate(cp);
|
|
||||||
HttpPost httppost = new HttpPost("http://localhost/handler.do");
|
|
||||||
httppost.setEntity(entity);
|
|
||||||
]]></programlisting>
|
|
||||||
</section>
|
|
||||||
<section>
|
<section>
|
||||||
<title>HTML forms</title>
|
<title>HTML forms</title>
|
||||||
<para>Many applications need to simulate the process of submitting an
|
<para>Many applications need to simulate the process of submitting an
|
||||||
|
@ -659,15 +617,6 @@ Final target: http://www.google.ch
|
||||||
target server (i.e. the request has not been fully transmitted to the
|
target server (i.e. the request has not been fully transmitted to the
|
||||||
server).</para>
|
server).</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
|
||||||
<para>HttpClient will automatically retry those methods that have been fully
|
|
||||||
transmitted to the server, but the server failed to respond with an HTTP
|
|
||||||
status code (the server simply drops the connection without sending anything
|
|
||||||
back). In this case it is assumed that the request has not been processed by
|
|
||||||
the server and the application state has not changed. If this assumption may
|
|
||||||
not hold true for the web server your application is targeting it is highly
|
|
||||||
recommended to provide a custom exception handler.</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
@ -688,12 +637,20 @@ HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
|
||||||
// Do not retry if over max retry count
|
// Do not retry if over max retry count
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (exception instanceof NoHttpResponseException) {
|
if (exception instanceof InterruptedIOException) {
|
||||||
// Retry if the server dropped connection on us
|
// Timeout
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
if (exception instanceof SSLHandshakeException) {
|
if (exception instanceof UnknownHostException) {
|
||||||
// Do not retry on SSL handshake exception
|
// Unknown host
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (exception instanceof ConnectException) {
|
||||||
|
// Connection refused
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (exception instanceof SSLException) {
|
||||||
|
// SSL handshake exception
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
HttpRequest request = (HttpRequest) context.getAttribute(
|
HttpRequest request = (HttpRequest) context.getAttribute(
|
||||||
|
|
|
@ -66,6 +66,7 @@
|
||||||
<xi:include href="statemgmt.xml"/>
|
<xi:include href="statemgmt.xml"/>
|
||||||
<xi:include href="authentication.xml"/>
|
<xi:include href="authentication.xml"/>
|
||||||
<xi:include href="httpagent.xml"/>
|
<xi:include href="httpagent.xml"/>
|
||||||
|
<xi:include href="fluent.xml"/>
|
||||||
<xi:include href="caching.xml"/>
|
<xi:include href="caching.xml"/>
|
||||||
<xi:include href="advanced.xml"/>
|
<xi:include href="advanced.xml"/>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue