HttpClient is the Jetty component that allows to make requests and interpret responses to HTTP servers.
This tutorial takes you through the steps necessary to use the HttpClient in the most effective way.
The HttpClient is by its nature asynchronous. This means that the code that sends the request does not wait for the response to arrive before continuing. Instead, the request is sent concurrently to your code, and the response is interpreted also concurrently with your code.
The HttpClient API offers you callbacks to interact with the request-response lifecycle. These callbacks will be called by the HttpClient implementation to allow further actions to be performed during each request-response event.
A request-response unit is called exchange, and represent the exchange of information with the HTTP server.
There are two main classes in the HttpClient API:
- org.eclipse.jetty.client.HttpClient, which manages the thread pooling, the proxy setting, the authentication settings, the connector type (blocking or non-blocking), the SSL settings and the timeouts. HttpClient manages the configuration that does not depend on a particular exchange.
- org.eclipse.jetty.client.HttpExchange, which is the base class that you normally have to subclass that represent the exchange with the HTTP server, and manages HTTP method, the request URI, HTTP headers, request content, HTTP response code, HTTP response headers and response content.
Before exchanging requests/responses with the HTTP server, you need to setup the HttpClient and then start it:
HttpClient client = new HttpClient(); client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); client.start();
You can also choose to setup the maximum number of connections per address (connections are pooled up to that maximum number), or to specify the thread pool, or the timeout:
HttpClient client = new HttpClient(); client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL); client.setMaxConnectionsPerAddress(200); // max 200 concurrent connections to every address client.setThreadPool(new QueuedThreadPool(250)); // max 250 threads client.setTimeout(30000); // 30 seconds timeout; if no server reply, the request expires client.start();
Remember to configure the HttpClient before starting it, or the settings will not have effect.
Since HttpClient does not have any settings related to a particular address, it can be used to exchange requests/responses with several HTTP servers. You normally need one HttpClient instance for all your needs, even if you plan to connect to multiple HTTP servers.
Once the HttpClient has been setup, exchanges may be initiated by using the HttpClient.send(HttpExchange exchange) method.
The exchange must be configured with two mandatory fields: the address to connect to, and the request URI, or equivalently with an absolute URL:
HttpExchange exchange = new HttpExchange(); exchange.setAddress(new Address("ping.host.com", 80)); exchange.setURI("/ping"); // Or, equivalently, this: exchange.setURL("http://ping.host.com/ping"); client.send(exchange); System.out.println("Exchange sent");
The most important thing to remember is that the send() method returns immediately after dispatching the exchange to a thread pool for execution, so the System.out is executed immediately after the send() method. It may be well true that the System.out is executed well before the exchange is actually executed, as well as it may be well true that the System.out is executed after the exchange is completely executed (when even the request has been received).
Beware of not assuming anything about the status of the exchange just because you called send().
Controlling the Exchange Progress
Because we used the HttpExchange class directly, without subclassing it, we have neither control nor notifications about the status of the exchange. The HttpExchange class exposes the following callback methods to be overridden to be notified of the status of the exchange:
- onRequestCommitted(), called when the request line and the request headers have been sent to the HTTP server.
- onRequestComplete(), called when the request content has been sent to the HTTP server.
- onResponseStatus(Buffer httpVersion, int statusCode, Buffer statusMessage), called when the response line has been processed; the three parameters hold, respectively, the HTTP version string (e.g. "HTTP/1.1"), the response status code (e.g. 200) and the response status message (e.g. "OK").
- onResponseHeader(Buffer name, Buffer value), called for each response header that has been processed; the parameters hold the name of the header (e.g. "Content-Length") and the value of the header (e.g. "16384").
- onResponseHeaderComplete(), called when all response headers have been processed.
- onResponseContent(Buffer content), called multiple times for each chunk of the response content; the parameter holds the chunk of the content that has been received.
- onResponseComplete(), called when the response content has been completely received.
Additionally four more methods can be overridden to be notified of non-normal conditions:
- onConnectionFailed(Throwable x), called when it is not possible to connect to the address specified in the exchange; the parameter holds the exception received while trying to connect.
- onException(Throwable x), called when a connection was possible, but an error happened later; the parameter holds the exception happened.
- <tt>onExpire(), called when the server did not respond before the timeout configured in the HttpClient.
- onRetry(), called when the exchange is resent (e.g. after an attempt to authenticate with no credentials).
You can of course extend HttpExchange directly, but it's probably best to use Jetty's built-in class org.eclipse.jetty.client.ContentExchange. This class overrides most of the callback methods above to allow easy retrieval of the response status code, response headers and response body.