When 3GPP set out to define the 5G core network (5GC), it used all the latest and greatest web technologies to radically reshape core network architecture. One tiny part of this is the use of HTTP/2 for signaling between functions. This made me wonder just how on the Internet today, web browsers and servers decide if HTTP/1.1 or HTTP/2 is used for communication. Yes, I went off a bit on a tangent there. It can’t be the TCP port, as port 443 is used with both protocols for encrypted connections. So there must be something else.
I didn’t know much about HTTP/2 so far, so my first thought was that maybe there is some backwards compatibility involved. But that’s not the case, HTTP/1.1 capable servers would not understand HTTP/2 requests at all. As one of the aims of HTTP/2 is to reduce the number of signaling round trips, I quickly discounted a separate negotiation phase after TCP session establishment as the potential solution as well. Instead, the HTTP protocol version negotiation is part of the TLS session establishment exchange. In 2014 I wrote about such a mechanism used by HTTP/2’s predecessor SPDY, but forgot all about it in the meantime. Today, the similar but nevertheless different implementation is referred to as ‘Application Layer Protocol Negotiation’ extension (ALPN). In short, the client (web browser) extends the TLS ‘Client Hello’ message with an ALPN extension and indicates that it supports h2 (HTTP/2) and http/1.1, in this order. The server then indicates in the TLS ‘Server Hello’ which of the two ‘application layer’ protocols it wants to use. So in principal, a straight forward thing.
The only slight problem when approaching this from a ‘seeing is believing’ point of view: When TLS 1.3 is used, most parts of the ‘Server Hello’ packet are already encrypted and the ALPN response part can’t be seen. Unfortunately, Wireshark doesn’t say so, which left me clueless for the better part of an hour. The ‘Internet’ wasn’t really helpful finding this fact as well. But I finally found a web server that was not yet supporing TLS 1.3 while supporing HTTP/2 (www.zeit.de). Here, the response can be decoded by Wireshark just fine. In case you want to try, here’s a Wireshark filter that tilters out TLS Client and Server ‘Hello’s’ that contain an ALPN extension:
tls.handshake.extension.type == 16
And another helpful tool when looking for this is ‘curl‘. In verbose mode, it indicates if the server returns HTTP/1.1 / HTTP/2 capabilities:
# curl -v --silent https://www.gitlab.com 2>&1 | grep "ALPN\|SSL connection using" * ALPN, offering h2 * ALPN, offering http/1.1 * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use h2