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 :

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 :

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 :
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
L'implémentation du projet quiche
avec Nginx :

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