Asynchronous HTTP with Gtk

potyl on 2009-06-28T09:29:15

I wrote a small demo for Champlain that shows how to query Flickr and to display thumbnails of geotagged pictures that are publicly available. I already had a demo script that downloads an image from an URL. So writing this new demo wasn't too difficult, yet buliding the basis for it (the HTTP download part) was tedious.

The basis of the HTTP protocol are quite simple. So simple in fact that a simple GET command can be easily issued by hand through telnet. But today we no longer communicate with HTTP 0.9 and the protocol has been greatly enhanced with proxy support, SSL, keep-alive, pipelining, web services (XML-RPC and SOAP) and so on. Writing an HTTP client from scratch is pure madness, specially when LWP does everything so easily.

So what's the problem if we already have a great HTTP client library in CPAN? The problem is that in a GUI all I/O has to be made asynchronously. For a GUI this is crucial, if I/O is NOT performed asynchronously then the GUI will simply freeze while it's waiting for the I/O to resume and stops responding to user input. As far as I know there's no easy way for performing asynchronous HTTP (or HTTPS) requests that integrate well with Glib's main loop, at least in Perl 5.

Libchamplain performs its HTTP requests through libsoup which is fully integrated with Glib's main loop. Sadly, libsoup doesn't have bindings for Perl 5 yet.

I though of using POE for this but it requires a whole rewrite of the application. With POE, the logic has to be written using the POE framework and the GUI is added latter as a different component. This makes the application no longer look like a Gtk2 application but more like a POE component that happens to have a GUI.

At the end I implemented a minimalist version of Libsoup in Perl 5 thanks to Net::HTTP::NB. My implementation is far from ideal but it works well enough for the purpose of the demo.

Remember when I said that writing an HTTP client fro scratch was pure madness, well I speak from experience and keep in mind that most of my work is still using Net::HTTP::NB for everything that involves the protocol. First of all my asynchronous client is not even fully asynchronous! In my version, the HTTP request is written with blocking I/O and only the response is read asynchronously. I tried to implement some basic version of keep-alive, it works but it is probably not that optimal. My library doesn't even have proxy support and it doesn't even keep track of the different TCP connections that the clients do.

I'm really thinking of writing the bindings my self. Although libsoup's API is quite extensive as it provides an HTTP client/server library for GNOME and this makes the task more time consuming. If anyone else would be interested in having libsoup's binding let me know. If I see that there's a demand beside my own personal need for a demo I will start the project sooner than expected.


AnyEvent

Aristotle on 2009-06-28T15:24:40

You probably wanted to use AnyEvent::HTTP, hooked in via AnyEvent::Impl::Glib.

Re:AnyEvent

potyl on 2009-06-28T18:33:05

You're right that that's what I wanted! AnyEvent::HTTP really works well with Glib's main loop!

For the curious here's how much the demo's code got simplified: difference.

Thanks!