Detecting HTTP/2 Support

Thanks to Ubuntu 16.04 which includes a fairly recent Nginx version, I have now enabled HTTP/2 on my private server. Of course, I also wanted to verify whether my configuration change had any effect - after all, there is no visible change (except for a little speed-up). One option that works really well is the HTTP/2 and SPDY indicator Chrome extension. But after some playing around with Python, I also found an easy way to detect HTTP/2 support using just Python's standard library.

The idea is pretty straightforward: Since HTTP/2 is usually served via TLS (at least that's what browsers support), the HTTP protocol version is negotiated during the TLS handshake using the Application-Layer Protocol Negotiation TLS extension (ALPN). The client sends a prioritized list of protocols it supports (i.e. HTTP/2, HTTP/1.1, SPDY) and the server picks the best option. Servers that don't support ALPN will just continue with HTTP/1.1 as if nothing had happened.

Using Python 3.5, it's easy to include ALPN during the TLS handshake:

#! /usr/bin/env python3
import socket
import ssl

HOST = ''
PORT = 443

ctx = ssl.create_default_context()
ctx.set_alpn_protocols(['h2', 'spdy/3', 'http/1.1'])

conn = ctx.wrap_socket(
   socket.socket(socket.AF_INET, socket.SOCK_STREAM), server_hostname=HOST)
conn.connect((HOST, PORT))

print('Next protocol:', conn.selected_alpn_protocol())

The list of valid protocol IDs is registered with IANA where "h2" is assigned to HTTP/2. If the server doesn't support ALPN at all, the script prints None. Otherwise you get one of the protocols from the list you offered.

Note that older versions of OpenSSL only supported ALPN's non-standardized predecessor Next Protocol Negotiation (NPN), but support for NPN has been disabled in Chrome recently.