hotpotato | efflux

hotpotato's project page

a high performance Java HTTP client

18/09/2010

What is hotpotato?

hotpotato - or hptt (from the common misspelling of http) - is (supposed to be) a Java high performance and throughput oriented HTTP client library, based on Netty. It was developed mostly towards server-side usage, where speed and low resource usage are the key factors, but can be used to build client applications as well.

Built for speed

Designed for high concurrency scenarios where multiple threads can use the same instance of a client without any worries for external or internal sinchronisation, hotpotato helps you reduce initialisation and/or preparation times and resource squandering. Among many small optimisations, connections are reused whenever possible, which results in a severe reduction of total request execution times by cutting connection establishment overhead.

Simple and familiar API

The API is extremely simple and, for anyone already familiar with Netty, comprehension will be simple.

The semantics of execute() (HttpClient interface) may look somewhat awkward but remember this library was built with server-side usage in mind, where often you end up repeating the requests to the same 4/5 hosts (if that many...).

For a more typical HTTP client API (to the likes of Apache HttpClient or similar libs), you should check the package org.factor45.hotpotato.session, which hides the "server-side'ness" of hotpotato (automatically handles redirections, digest auth, cookies, etc).

Note:

Until this software hits version 1.0, it's considered to be in BETA stage.
I encourage you to look & hack the code to your needs. Comments, improvements, patches, rants and praises are all welcome at the mailing list.


Repository

The project is currently hosted at GitHub.


Releases

Version 0.7.0


Documentation

Version 0.7.0

Note:

This documentation is still a work in progress. Chapter 2 is full of placeholders at this time. I'll keep updating the user guide, but you shouldn't expect a full document before version 1.0.

Items marked with * have been generated using docbook and JBoss's maven-jdocbook-plugin. The templates for the PDF were left untouched but the CSS for the HTML versions were slightly modified, hence the resemblance with JBoss's community documentation.


Performance

Coming soon...


Roadmap


License

hotpotato is distributed under Apache License, Version 2.0. Please see the enclosed NOTICE.txt, COPYRIGHT.txt, and LICENSE.txt for more information.


Mailing list & other useful stuff

Mailing list

The project's mailing list is hotpotato-users@googlegroups.com. At the moment there is no need for multiple lists so all discussions (dev, announcements, help, ranting) go in there.

It requires a google account, which you can create by clicking the link.

Dependencies

hotpotato needs only Netty 3.2 (Final) and JDK 1.6.

Useless links


Examples

The following are quick & dirty examples whose sole purpose is to give you an idea of how hotpotato is used. Please refer to documentation section.

Synchronous mode

When executing requests in synchronous mode, the calling thread will block (much like Apache's HttpClient).

// Create & initialise the client
HttpClient client = new DefaultHttpClient();
client.init();

// Setup the request
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_0,
                                             HttpMethod.GET, "/");

// Execute the request, turning the result into a String
HttpRequestFuture future = client.execute("hotpotato.factor45.org", 80, request,
                                          new BodyAsStringProcessor());
future.awaitUninterruptibly();
// Print some details about the request
System.out.println(future);

// If response was >= 200 and <= 299, print the body
if (future.isSuccessfulResponse()) {
    System.out.println(future.getProcessedResult());
}

// Cleanup
client.terminate();
  

Asynchronous mode

In asynchronous mode, the calling thread will proceed immediately after submitting the request. If any computation needs to be done when the request is terminated, a listener should be added to the object returned when submitting requests.

// Execute the request
HttpRequestFuture<String> future = client.execute("hotpotato.factor45.org", 80, request,
                                                  new BodyAsStringProcessor());
future.addListener(new HttpRequestFutureListener<String>() {
    @Override
    public void operationComplete(HttpRequestFuture<String> future) throws Exception {
        System.out.println(future);
        if (future.isSuccessfulResponse()) {
            System.out.println(future.getProcessedResult());
        }
        client.terminate();
    }
});
  

You may need to use the full URL as URI when using HTTP 1.1.

request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
                                 "http://www.google.pt/webhp?hl=pt-PT&tab=iw");
request.addHeader(HttpHeaders.Names.HOST, "www.google.pt");
future = client.execute("www.google.pt", 80, request, new BodyAsStringProcessor());
  

The following is a comprehensive example of request to three distinct servers.

final DefaultHttpClient client = new DefaultHttpClient();
client.setRequestTimeoutInMillis(5000);
client.init();

final CountDownLatch latch = new CountDownLatch(3);

HttpRequest request;
HttpRequestFuture<String> future;

request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
request.addHeader(HttpHeaders.Names.HOST, "hotpotato.factor45.org");
future = client.execute("hotpotato.factor45.org", 80, request,
                        new BodyAsStringProcessor());
future.addListener(new HttpRequestFutureListener<String>() {
    @Override
    public void operationComplete(HttpRequestFuture<String> future) throws Exception {
        System.out.println("\nHotpotato request: " + future);
        if (future.isSuccess()) {
            System.out.println(future.getResponse());
        } else {
            System.out.println(future.getResponse());
            future.getCause().printStackTrace();
        }
        if (future.isSuccessfulResponse()) {
            System.out.println(future.getProcessedResult());
        }
        latch.countDown();
    }
});

request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
                                 "http://www.google.pt/webhp?hl=pt-PT&tab=iw");
request.addHeader(HttpHeaders.Names.HOST, "www.google.pt");
future = client.execute("www.google.pt", 80, request, new BodyAsStringProcessor());
future.addListener(new HttpRequestFutureListener<String>() {
    @Override
    public void operationComplete(HttpRequestFuture<String> future) throws Exception {
        System.out.println("\nGoogle request: " + future);
        if (future.isSuccess()) {
            System.out.println(future.getResponse());
        } else {
            System.out.println(future.getResponse());
            future.getCause().printStackTrace();
        }
        if (future.isSuccessfulResponse()) {
            System.out.println(future.getProcessedResult());
        }
        latch.countDown();
    }
});

request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
                                 "http://twitter.com/");
future = client.execute("twitter.com", 80, request, new BodyAsStringProcessor());
request.addHeader(HttpHeaders.Names.HOST, "twitter.com");
future.addListener(new HttpRequestFutureListener<String>() {
    @Override
    public void operationComplete(HttpRequestFuture<String> future) throws Exception {
        System.out.println("\nTwitter request: " + future);
        if (future.isSuccess()) {
            System.out.println(future.getResponse());
        } else {
            System.out.println(future.getResponse());
            future.getCause().printStackTrace();
        }
        if (future.isSuccessfulResponse()) {
            System.out.println(future.getProcessedResult());
        }
        latch.countDown();
    }
});

try {
    latch.await();
} catch (InterruptedException e) {
    e.printStackTrace();
}

client.terminate();