HTTP/2.0 For Python

HTTP/2.0 For Python

The HTTPbis have spoken, HTTP/2.0 is happening. Major websites are beginning to adopt it (hello there Twitter!), and the spec is beginning to get nailed down. If you’re unfamiliar with HTTP/2.0 and all the fun things it brings you, Ilya Grigorik has been doing an excellent job evangelising for it. Take a look at this talk if you want a deep dive into HTTP/2.0. If you want a shorter discussion, this article is also a great reference.

HTTP/2.0 brings a number of advantages: it saves time setting up and tearing down TCP connections, it compresses HTTP headers, it provides flow control and connection multiplexing. For certain applications in certain use-cases, these can represent a huge advantage over HTTP/1.1.

It’s time for Python to get support for it. With that in mind, I’ve spent the last few weeks working on my brand new project. I give you: hyper.

Hyper

Hyper is an attempt to build a pure-Python HTTP/2.0 stack that perfectly matches the http.client API from Python’s standard library. This will allow hyper to be a drop-in replacement for http.client, providing all the HTTP/2.0 goodness to all Python programs without the need for potentially painful rewrites.

How does it work? Like this:

from hyper import HTTP20Connection

conn = HTTP20Connection('twitter.com:443')
conn.request('GET', '/')
resp = conn.getresponse()

print(resp.read())

That’s all it takes. The same API as http.client, but with all the fun of HTTP/2.0. For more examples, see the documentation. If you’re really excited, just go right ahead and download it using pip. Alternatively, take a look at the code on GitHub: it’s available under the MIT license, so everyone should be able to grab it and play with it.

Features

At the moment, hyper is in a very early alpha (version 0.0.1). I’ve proved that the mainline use-case works fine, but I’m sure there are plenty of bugs lying around, and there are several features I’d like to implement that I haven’t.

Nevertheless, the core feature set is already in place:

  • Can make HTTP/2.0 requests.
  • Obeys the HTTP/2.0 flow-control mechanisms.
  • Verifies TLS certificates.
  • Streaming uploads.
  • Streaming downloads.

There are plenty of features planned, with the headline one being transparent HTTP/2.0 and HTTP/1.1 interworking. Currently, hyper will only work against servers that fully support HTTP/2.0. I’m planning to implement an abstraction layer that should prevent the user from needing to know whether the server they’re contacting supports HTTP/2.0: hyper will transparently fall back to HTTP/1.1.

The only feature I have no intention of implementing support for is one of HTTP/2.0’s headline features: Server Push. I’ll discuss this omission later.

Warning

Please note that hyper is in a very early alpha. I do not recommend you use it in production: things will go wrong. However, I’d like as many of you as possible to take it out for a spin and report any bugs you find on the GitHub page. The more bugs you find the better hyper will get.

Restrictions

Currently, hyper requires Python 3.3 or higher. This unfortunate restriction is caused by the fact that earlier versions of the standard library’s ssl module are totally braindead and do not support the TLS Next-Protocol-Negotiation extension, which HTTP/2.0 requires. Until I can find a good way around this limitation, hyper will remain limited to at least Python 3.3. If you don’t like this (I certainly don’t), I encourage you to take your complaint up with the Python core development team.

Additionally, you currently need to find out whether your target server supports HTTP/2.0 or not before you point hyper at it, or it’ll raise an AssertionError. This is a necessary guard in this early release, but this will be improved in future releases.

Sadly, hyper is also currently inherently single-threaded. This means it has some unusual behaviours, but also that you need to implement your own threading mechanism on top of it if you want to use it in multi-threaded code. A suggested implementation is sketched out in the documentation, and I hope to provide example code in the near future. More long-term, hyper will become thread-safe and you’ll be able to simply have global connections.

The Omission of Server Push

I should briefly touch on the omission of Server Push from hyper. I’ve been on record in the past as saying that Server Push is fundamentally incompatible with the way most Python HTTP code is written. All the big Python HTTP client libraries are built primarily to be used from synchronous code: they expect to send a request and then block until the response comes back. They absolutely do not expect to receive multiple responses to the same request, and provide no API for receiving these responses.

I don’t believe it’s possible to write a pleasant API that correctly handles Server Push without using either an event loop or having hyper spawn its own background threads. Until asyncio becomes a standard, I don’t think hyper should force you to do the first, and it’s obnoxious for a library to spawn its own threads. This means that, for the moment, hyper does not support Server Push. I do not believe this to be a big loss for 90% of the potential users of hyper, so I judge it to be an acceptable limitation. If you strongly disagree, please get in touch so we can talk about why you see this issue differently to me.

Development

hyper is in early alpha and under active development on GitHub. I’d love it if people took the time to download and try out hyper. I’m particularly interested to see what kinds of bugs people hit: you’ll definitely hit them, and a detailed bug report would be really phenomenal.

All contributions are welcomed. Even if you try out hyper and find no bugs, drop me a line on Twitter and let me know that you used it: at this early stage I need all the feedback I can get.

Finally, I’d like to specially call out Sriram Ganesan, who graciously volunteered some of his time to work on hyper when it didn’t work at all. He provided some useful infrastructure when I was too busy to do it myself, and he made my work immeasurably easier: thanks so much Sriram!