Virtual Spring Cleaning (part 9 of X) wherein I retrieve content

Much of what I do involves retrieving stuff over the HTTP family of protocols. My go-to solutions are either the APIs of LWP::UserAgent/WWW::Mechanize or the API of AnyEvent::HTTP, depending on whether I want some kind of concurrency or not. Since I found Future as a somewhat nicer way of structuring callback hell a bit differently, I've looked around for a nice implementation of a HTTP client that works with various backends and maybe even without a backend.

I was unsuccessful in my search, so I wrote my own client, Future::HTTP based on the API of AnyEvent::HTTP, as that is the most basic API there is above writing to a socket yourself. It does not support fancy lowercase headers or cookie jars and doesn't have the nicer structural separation of reply headers and reply status, but it has served me well enough and has a small enough interface to be easily implementable for backends other than itself.

my $ua = Future::HTTP->new()->http_get(...)->then(sub {
    my( $body, $data ) = @_;
    ...
})

So far, I've implemented two backends, one based on AnyEvent::HTTP and the other based on HTTP::Tiny, because that's a client distributed with Perl since 5.14.0. The HTTP::Tiny-based backend executes the requests synchronously which makes it very easy for debugging to swich off the asynchronicity and also led me to some restructuring of the internal workings.

I have plans to add different APIs to the front. I've already written Future::HTTP::API::HTTPTiny, which will provide the API and helper functions of HTTP::Tiny with results being Futures.

What I'm looking for are backends using IO::Async and Mojolicious, as these are the remaining major event loops that people seem to use directly instead of using them implicitly through AnyEvent. Implementing these backends will be interesting as they will provide me with a learning experience with their respective frameworks.

3 Comments

Future:: is a namespace for future implementations and extensions, and your module is going to cause much confusion in its current namespace.

I'd suggest maybe HTTP::UserAgent::FutureBased or similar?

-- mst

Good to see more people using Futures.

There's a couple of existing distributions which already provide Future-based wrappers around a few HTTP clients:

Neither of these is suitable as a truly generic HTTP client API, being intended more as an abstraction for non-streaming REST requests. Sometimes that's enough, though - "10% of the features cover 80% of the use-cases".

I would recommend not basing an abstraction entirely around a returned Future, however. Better to return an object that you can extend later - the Future tells you that the response has been received, but provides no way to inspect current status or progress before completion. Something like:


my ($resp) = http_get(...)->response->get;
# $resp is an HTTP::Response instance

The AnyEvent::HTTP API does not appear particularly useful, but it should be simple enough to map the results using the ->transform(done => sub { ... }) idiom on the returned Future.

If you're going to change the name, don't forget that HTTP covers quite a broad area - servers, clients, proxies - so I'd suggest including UserAgent or Client in the name unless you really want to provide a server abstraction too.

One framework you didn't mention is POE - it's widely used and has at least one HTTP client.

Net::Async::Ping also returns Future. I have used it in my fork of wolicious: https://github.com/jnareb/wolicious

Leave a comment

About Max Maischein

user-pic I'm the Treasurer for the Frankfurt Perlmongers e.V. . I have organized Perl events including 9 German Perl Workshops and one YAPC::Europe.