Nginx vient tout juste d'annoncer le support - en preview - du protocole Quic et de HTTP/3. Je me devais impérativement d'essayer ça rapidement 😁

Mais tout d'abord, revenons un peu sur le protocole HTTP et sa version 2.

HTTP

Le standard HTTP/2 n'est pourtant pas si vieux, et n'est pas encore présent sur tous nos sites. Effectivement, le standard date de 2015 et n'est utilisé pour l'instant que par moins de 50% des sites webs :

Source : https://w3techs.com/
HTTP/2 is used by 46.2% of all the websites.

C'est vrai que si l'on compare ces chiffres à ceux du déploiement de l'IPv6, en moins de 5 ans, HTTP/2 a su s'imposer.

Mais,est-ce bien réel ? Ce chiffre reflète t-il bien la réalité ?

En fait, pas du tout. À l'image du chiffrement de bout en bout, on se rappelle Zoom, le HTTP/2 subit le même effet. Effectivement, dans la grande majorité des cas, le client communique avec un reverse-proxy/edge proxy en HTTP/2 mais ça s'arrête ici...

La dernière portion, celle cachée dans votre infrastructure cloud, communique généralement elle, en HTTP/1.1. Nginx rappelle d'ailleurs très bien ce fait hier, lors de son annonce. Voici le schéma qui récapitule ces propos :

source : https://www.nginx.com

La principale amélioration apportée par HTTP/2 par rapport à la version 1.1 , qui n'avait pas évolué depuis quasiment 25 ans: est d'initialiser une seule requête TCP pour transporter toutes vos requêtes HTTP.

Dans la version 1.1 pour chaque élément d'une page ( HTML, images, style, JS ), on réalise une connexion TCP au serveur. Forcément dans le web "moderne" ça commence à faire beaucoup de connexions pour une seule page.

Dans la version 2 : une seule connexion va transporter tous les éléments d'une page ( streams ). On gagne forcément en rapidité de chargement d'une page.

Si tout fonctionne correctement et que la version 2 n'est pas encore adoptée par tous, pourquoi passer à la version 3 ?

QUIC

Car finalement, ce qui fait la force du HTTP/2 est également sa plus grande faiblesse. Quand vous transportez toutes vos requêtes dans une seule connexion, forcément tout va dépendre de cette même connexion.

Si un paquet est perdu lors de la transmission, alors toutes vos requêtes vont attendre la retransmission des données. Et la perte de paquet réseau à l'heure de la 5G ( ou de la 2G 😂 ) est un réel problème.

L'utilisation de TCP est donc problématique à ce niveau. C'est pourquoi, le protocole HTTP/3 va lui se baser sur un nouveau protocole de transport : Quic.

Pour faire court, car on peut faire des dizaines d'articles rien que sur un seul protocole réseau : ce nouveau protocole réseau utilise l'UDP comme mécanisme de transport afin d'échanger des données entre le client et le serveur. L'utilisation de l'UDP permet donc forcément d'être plus rapide. Mais en plus, Quic embarque directement le TLS...

Je ne vais pas m'attarder sur les détails du protocole, vous pouvez trouver plus d'informations sur celui-ci sur la page Wikipedia.

Alors plus de TCP pour la prochaine génération d'hébergement web ?

HTTP/3

En fait, pas vraiment. Comment le client web va savoir que votre page web parle le HTTP/1.1, HTTP/2 ou HTTP/3 ?

Dans le cas du HTTP/2, un handshake TLS permet de détecter que votre navigateur et le serveur sont capables d'utiliser cette version. Mais comme Quic utilise l'UDP ce n'est pas possible.

En réalité, votre client va continuer à initier une requête TCP lors de la première requête HTTP. Lors de ce premier échange, le serveur qui utilise du HTTP/3 va pouvoir envoyer un header spécifique Alt-Svc afin de spécifier le port UDP pour l'utilisation du HTTP/3.

Votre navigateur conservera l'information pour ce site afin de ne pas utiliser une requête TCP sytématiquement.

Maintenant que nous avons vu brièvement la théorie, si on compilait Nginx pour le support du http_v3 ?

Nginx

🚩 Juste pour information: Cloudfare a implémenté un patch au travers de son projet quiche. Mais il s'agit bien ici d'une nouvelle implémentation de Quic+HTTP/3 par Nginx et qui n'a aucun rapport avec ce projet. 🚩

Je vais utiliser Ubuntu 20.04 pour compiler cette version.

Tout d'abord je vais installer quelques pré-requis :

lfache@Midgar:~$ sudo apt install mercurial libpcre3 libpcre3-dev gcc make autoconf zlib1g zlib1g-dev -y

Enfin je vais cloner le dépôt, la version preview se trouve dans un dépôt spécifique :

lfache@Midgar:~$ hg clone -b quic https://hg.nginx.org/nginx-quic

Et compilons le tout :

lfache@Midgar:~$ cd nginx-quic/
lfache@Midgar:~/nginx-quic$ ./auto/configure --with-debug \ 
--with-http_v3_module \
--with-cc-opt="-I../boringssl/include" \
--with-ld-opt="-L../boringssl/build/ssl  -L../boringssl/build/crypto"

Hélas vous devriez tomber rapidement sur une erreur :

./auto/configure: error: SSL modules require the OpenSSL library.
You can either do not enable the modules, or install the OpenSSL library
into the system, or build the OpenSSL library statically from the source
with nginx by using --with-openssl=<path> option.

N'installez pas la version de votre distribution car dans tous les cas, celle-ci ne sera pas patchée pour le support de Quic. Nous allons également la compiler :

lfache@Midgar:~/nginx-quic$ cd ..
lfache@Midgar:~$ git clone --depth 1 -b OpenSSL_1_1_1g-quic-draft-28 https://github.com/tatsuhiro-t/openssl
lfache@Midgar:~$ cd openssl
lfache@Midgar:~/openssl$ ./config enable-tls1_3 --openssldir=/etc/ssl
lfache@Midgar:~/openssl$ make -j$(nproc)
lfache@Midgar:~/openssl$ sudo make install_sw

Enfin je vais pouvoir relancer la compilation de Nginx :

lfache@Midgar:~/openssl$ cd ../nginx-quic/
lfache@Midgar:~/nginx-quic$ ./auto/configure --with-debug \ 
--with-http_v3_module \
--with-cc-opt="-I../boringssl/include" \
--with-ld-opt="-L../boringssl/build/ssl  -L../boringssl/build/crypto"

Et enfin l'installer :

lfache@Midgar:~/nginx-quic$ sudo make install

Il ne reste plus qu'à créer ma configuration avec le support du HTTP/3 :

lfache@Midgar:~$ sudo cat /usr/local/nginx/conf/nginx.conf
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}

http {
        log_format quic '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent" "$quic" "$http3"';

        access_log logs/access.log quic;

        server {
            # for better compatibility it's recommended
            # to use the same port for quic and https
            listen 8443 http3 reuseport;
            listen 8443 ssl;

            ssl_certificate     /etc/letsencrypt/live/quic.mydomain.com/fullchain.pem;
            ssl_certificate_key /etc/letsencrypt/live/quic.mydomain.com/privkey.pem;
            ssl_protocols       TLSv1.3;

            location / {
                # required for browsers to direct them into quic port
                add_header Alt-Svc '$http3=":8443"; ma=86400';
            }
        }
}

On retrouve ici notre header : Alt-Svc

Comme vous pouvez le voir, Quic intégrant le TLS, je vais avoir besoin d'un certificat. Je vais utiliser certbot pour en obtenir un :

lfache@Midgar:~$ sudo certbot certonly --standalone -d quic.mydomain.com

Enfin je vais pouvoir lancer Nginx :

lfache@Midgar:~$ sudo LD_LIBRARY_PATH=/usr/local/lib /usr/local/nginx/sbin/nginx

Et maintenant vérifions que tout fonctionne ! Les connexions HTTP/3 vont être enregistrées dans notre journal avec les informations de connexion Quic. J'utilise donc Firefox qui est normalement compatible et là ... :

[11/Jun/2020:09:48:43 +0000] "GET / HTTP/1.1" 304

En réalité le HTTP/3 n'est pas activé par défaut dans Firefox. Il faut l'activer avec about:config :

network.http.http3.enabled et le passer à true

Une fois cette modification réalisée :

nginx support http_v3 

Voilà, vous savez maintenant comment compiler Nginx avec le support de Quic et du HTTP/3 !

Bien évidemment tout ceci reste expérimental pour le moment et il faudra sûrement attendre quelques temps avant de voir débarquer une version stable de ces améliorations. Le protocole Quic n'étant lui même pas finalisé, il s'agit encore pour le moment de Draft.

Voici quelques liens utilisés pour la réalisation de cet article :

Introducing a Technology Preview of NGINX Support for QUIC and HTTP/3
First HTTP/3 with curl
ngtcp2/ngtcp2
ngtcp2 project is an effort to implement IETF QUIC protocol - ngtcp2/ngtcp2

L'implémentation du projet quiche avec Nginx :

Implementing HTTP3 QUIC nginx
The step-by-step guide on how to setup HTTP 3 Nginx support. Implementing HTTP3 QUIC NGINX docker. Curl with HTTP3. Cloudflare Quiche compiled nginx.

En tout cas  n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter, ou ici 👇