Chapter 5. HTTP client service

5.1. HttpClient facade

HttpClient interface represents the most essential contract for HTTP request execution. It imposes no restrictions or particular details on the request execution process and leaves the specifics of connection management, state management, authentication and redirect handling up to individual implementations. This should make it easier to decorate the interface with additional functionality such as response content caching.

DefaultHttpClient is the default implementation of the HttpClient interface. This class acts as a facade to a number of special purpose handler or strategy interface implementations responsible for handling of a particular aspect of the HTTP protocol such as redirect or authentication handling or making decision about connection persistence and keep alive duration. This enables the users to selectively replace default implementation of those aspects with custom, application specific ones.

DefaultHttpClient httpclient = new DefaultHttpClient();

httpclient.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy() {

    @Override
    public long getKeepAliveDuration(
            HttpResponse response,
            HttpContext context) {
        long keepAlive = super.getKeepAliveDuration(response, context);
        if (keepAlive == -1) {
            // Keep connections alive 5 seconds if a keep-alive value 
            // has not be explicitly set by the server 
            keepAlive = 5000;
        }
        return keepAlive;
    }
    
});

DefaultHttpClient also maintains a list of protocol interceptors intended for processing outgoing requests and incoming responses and provides methods for managing those interceptors. New protocol interceptors can be introduced to the protocol processor chain or removed from it if needed. Internally protocol interceptors are stored in a simple java.util.ArrayList. They are executed in the same natural order as they are added to the list.

DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.removeRequestInterceptorByClass(RequestUserAgent.class);
httpclient.addRequestInterceptor(new HttpRequestInterceptor() {

    public void process(
            HttpRequest request, HttpContext context)
            throws HttpException, IOException {
        request.setHeader(HTTP.USER_AGENT, "My-own-client");
    }
    
});

DefaultHttpClient is thread safe. It is recommended that the same instance of this class is reused for multiple request executions. When an instance of DefaultHttpClient is no longer needed and is about to go out of scope the connection manager associated with it must be shut down by calling the ClientConnectionManager#shutdown() method.

HttpClient httpclient = new DefaultHttpClient();
// Do something useful
httpclient.getConnectionManager().shutdown();

5.2. HttpClient parameters

These are parameters that be used to customize the behaviour of the default HttpClient implementation:

  • ClientPNames.HANDLE_REDIRECTS='http.protocol.handle-redirects':  defines whether redirects should be handled automatically. This parameter expects a value of type java.lang.Boolean. If this parameter is not set HttpClient will handle redirects automatically.

  • ClientPNames.REJECT_RELATIVE_REDIRECT='http.protocol.reject-relative-redirect':  defines whether relative redirects should be rejected. HTTP specification requires the location value be an absolute URI. This parameter expects a value of type java.lang.Boolean. If this parameter is not set relative redirects will be allowed.

  • ClientPNames.MAX_REDIRECTS='http.protocol.max-redirects':  defines the maximum number of redirects to be followed. The limit on number of redirects is intended to prevent infinite loops caused by broken server side scripts. This parameter expects a value of type java.lang.Integer. If this parameter is not set no more than 100 redirects will be allowed.

  • ClientPNames.ALLOW_CIRCULAR_REDIRECTS='http.protocol.allow-circular-redirects':  defines whether circular redirects (redirects to the same location) should be allowed. The HTTP spec is not sufficiently clear whether circular redirects are permitted, therefore optionally they can be enabled. This parameter expects a value of type java.lang.Boolean. If this parameter is not set circular redirects will be disallowed.

  • ClientPNames.CONNECTION_MANAGER_FACTORY_CLASS_NAME='http.connection-manager.factory-class-name':  defines the class name of the default ClientConnectionManager implementation. This parameter expects a value of type java.lang.String. If this parameter is not set SingleClientConnManager will be used per default.

  • ClientPNames.VIRTUAL_HOST='http.virtual-host':  defines the virtual host settings to be used in the Host header instead of the physical host. This parameter expects a value of type HttpHost. The HttpHost port does not have to be specified as it will be derived from the target. If this parameter is not set, the name or IP address (and port if required) of the target host will be used.

  • ClientPNames.DEFAULT_HEADERS='http.default-headers':  defines the request headers to be sent per default with each request. This parameter expects a value of type java.util.Collection containing Header objects.

  • ClientPNames.DEFAULT_HOST='http.default-host':  defines the default host. The default value will be used if the target host is not explicitly specified in the request URI (relative URIs). This parameter expects a value of type HttpHost.

5.3. Automatic redirect handling

HttpClient handles all types of redirects automatically, except those explicitly prohibited by the HTTP specification as requiring user intervention. See Other (status code 303) redirects on POST and PUT requests are converted to GET requests as required by the HTTP specification.

5.4. HTTP client and execution context

The DefaultHttpClient treats HTTP requests as immutable objects that are never supposed to change in the course of request execution. Instead, it creates a private mutable copy of the original request object, whose properties can be updated depending on the execution context. Therefore the final request properties such as the target host and request URI can be determined by examining the content of the local HTTP context after the request has been executed.

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.

DefaultHttpClient httpclient = new DefaultHttpClient();

HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost:8080/"); 
HttpResponse response = httpclient.execute(httpget, localContext);
HttpHost target = (HttpHost) localContext.getAttribute(
        ExecutionContext.HTTP_TARGET_HOST);
HttpUriRequest req = (HttpUriRequest) localContext.getAttribute(
        ExecutionContext.HTTP_REQUEST);

System.out.println("Target host: " + target);
System.out.println("Final request URI: " + req.getURI()); // relative URI (no proxy used)
System.out.println("Final request method: " + req.getMethod());

5.5. Compressed response content

The ContentEncodingHttpClient is a simple sub-class of DefaultHttpClient which adds support indicating to servers that it will support gzip and deflate compressed responses. It does this via the existing published APIs of HTTP Protocol Interceptors . Depending on the type of response (text will compress well versus images, which are typically already well-compressed), this can speed up responses due to the smaller amount of network traffic involved, along with saving bandwidth, which can be important in mobile environments. The RequestAcceptEncoding and ResponseContentEncoding interceptors used as also part of the published API and can be used by other DefaultHttpClient implementations. These provide transparent handling of gzip and deflate encoding, so it will not be apparent to clients that this processing has happened.

ContentEncodingHttpClient httpclient = new ContentEncodingHttpClient();
HttpGet httpget = new HttpGet("http://www.yahoo.com/");
HttpResponse response = httpclient.execute(httpget);

Header h = rsp.getFirstHeader("Content-Encoding");
if (h != null) {
    System.out.println("----------------------------------------");
    System.out.println("Response is " + h.getValue() + " encoded");
    System.out.println("----------------------------------------");
}

One can also add the RequestAcceptEncoding and ResponseContentEncoding interceptors to an instance of the DefaultHttpClient, if desired.

DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.addRequestInterceptor(new RequestAcceptEncoding());
httpclient.addResponseInterceptor(new ResponseContentEncoding());