Peut-on créer un site web éco responsable avec WordPress ?
/https://medias.yama-cms.com/fbe88647624e76d5bcdec2d0e89dc506/2024-01/65a56ad8a3333360264726.png)
Le protocole HTTP est au cœur des métiers de l’internet mais c’est quoi en fait une requête HTTP allez en route marcel (solo de guitare endiablé alors que la caméra s’envole du camion).
Un protocole est une procédure pré-établie que deux entités peuvent suivre pour arriver à un résultat : par exemple, dans le cas du protocole HTTP, à échanger de l’information.
En informatique, on peut ainsi implémenter un protocole dans n’importe quel langage en suivant sa spécification : c’est le document qui détaille toutes les règles du protocole.
Dans cet article, on va essayer de démystifier les requêtes HTTP en soulevant le capôt : Plutôt qu’utiliser un client GUI comme insomnia ou bruno, ou même un client CLI comme curl ou httpie, on va mettre les mains dans le cambouis et faire des requêtes HTTP manuellement.
Mais tout d’abord, un peu de théorie…
HTTP est donc un protocole défini par une spécification, sous forme de RFC. La version actuelle, la RFC 9110, “HTTP Semantics”, fait environ 200 pages. Elle détaille la sémantique de ce protocole : Les schemas URI, la gestion du cache, les headers, les méthodes, les status codes…
HTTP a été défini et redéfini au fil des années dans multiples RFCs (“HTTP/1.1” RFC 2616 en 1999, RFC 2068 en 1997, “HTTP/1.0”, RFC 1945 en 1996, …).
Ces documents sont longs et exhaustifs, mais le document qui a défini HTTP pour la première fois, en 1991, tient sur une seule page et défini ce qu’on appelle maintenant “HTTP/0.9”. Ce protocole est très simple et suit 4 étapes selon “le style du protocole telnet
”1 :
GET <chemin-du-document>\n
.Au fil des années, le protocole à reçu des additions tant qu’à sa syntaxe et sémantique (verbes POST, DELETE etc, Headers, …) qu’aux façons d’établir une connexion (TCP vers UDP (QUIC), …). Cependant, une grosse partie est restée, ou est rétrocompatible.
À la RFC 9110 s’ajoutent la RFC 9111, 35 pages qui définissent les mécanismes de cache, et la RFC 9112, 46 pages qui définissent comment une requête HTTP/1.1 est encodée, décodée et comment la connexion s’opère, au niveau de la connexion TCP.
HTTP est défini en 3 RFCs pour pouvoir faire évoluer chacun de ces 3 axes indépendamment. En particulier, la RFC 9113 et la RFC 9114 définissent HTTP/2 et HTTP/3 respectivement. HTTP/3 utilise le protocole UDP plutôt que TCP, mais ces deux RFCs ne changent pas l’aspect sémantique du protocole.
TCP/IP est une suite de protocoles qui, ensemble, composent ce qu’on appelle Internet. Ces protocoles sont organisés en “couches” empilées. L’idée architecturale est que chaque strate permet d’abstraire le niveau inférieur et en simplifier l’utilisation pour les strates supérieures.
HTTP se situe tout en haut de la pile, dans la couche “Application”, aux cotés de SSH, FTP, Gopher, Telnet, DNS…2
Vient ensuite la couche “Transport” : TCP et UDP. La couche d’en dessous est la couche “Réseau” : le protocole IP définit comment les appareils se connectent les uns aux autres. Enfin, les couches “Liaison” et “Physique” décrivent comment on échange à travers, par exemple, un câble Ethernet ou un réseau sans fil.
Une couche peut aussi permettre de répondre à certains problèmes de la couche précédente : par exemple, le protocole IP permet d’envoyer des packets de données, mais ne garantit pas que ces packets soient reçus par le destinataire, ou dans le bon ordre.
Le protocole TCP met en place des mécanismes pour palier à ces problèmes : entre autres, la connexion/déconnexion avec l’hôte distant et la réception de chaque paquet est confirmée à l’envoyeur, et celui-ci renvoie les packets “perdus”. Chaque packet est ordonné, et le receveur s’assure d’avoir tous les packets dans le bon ordre avant de les passer à la couche supérieure.
Ces mécanismes ajoutent des coûts (plus de packets et d’échanges). À l’inverse, UDP embrasse ces aspects au profit d’une plus grande vitesse de transfert par rapport à TCP. Ainsi, le développeur peut choisir le protocole adapté à ses besoins : fiabilité (HTTP, …) ou rapidité (voix/video en temps réel, …) : et ces deux protocoles utilisent le même protocole IP.
curl
HTTP/0.9 est depuis longtemps déprécié (1996 !). Maintenant qu’on connaît la base du protocole, on va directement étudier HTTP/1.1 : sans rentrer dans les détails, cette version rajoute la plupart des éléments auxquels nous sommes familier (les méthodes, les headers et les status codes).
curl
est un outil pour envoyer des requêtes HTTP. On va faire une requête avec --verbose
pour que curl nous affiche toute la requête, plutôt que juste la réponse. Pour nos tests, on va requêter http://httpbin.org/uuid
3.
curl -v "http://httpbin.org/uuid"
GET /uuid HTTP/1.1
Host: httpbin.org
User-Agent: curl/8.4.0
Accept: */*
HTTP/1.1 200 OK
Date: Tue, 18 Jun 2024 12:29:01 GMT
Content-Type: application/json
Content-Length: 53
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
{
"uuid": "3574b13a-dcfd-4e8e-b0a5-cffb219fb201"
}
On peut distinguer 3 sections :
GET <path>
, mais on voit maintenant la version du protocole (HTTP/1.1
) et les Headers (Host
, User-Agent
, Accept
). La fin de la requête est marquée par une ligne vide.200 OK
, ainsi que les headers de la réponse (Date
, Content-Type
, Content-Length
, …). La fin de l’en-tête de réponse est elle aussi marquée par une ligne vide.Un protocole n’est pas réservé qu’aux machines. Les humains aussi établissent et suivent des protocoles entre eux : protocole de sécurité, protocole post-détection…
Et on peut aussi, bien entendu, suivre un protocole où l’un est un humain et l’autre est une machine : puisque la communication est entièrement codifiée, il n y a qu’à suivre les règles ! 4
Pour notre analyse, descendre jusqu’au niveau des segments TCP ou packets IP serait un peu trop complexe (et hors-sujet !), mais curl ou bruno ne nous laisserait rien à faire et je n’aurais rien à écrire. Entre en scène netcat
: un outil pour faire ”à peu près tout ce qui touche à TCP, UDP ou aux sockets Unix.” Grace à cet utilitaire, on va pouvoir bidouiller au niveau d’abstraction qui nous intéresse : une connexion TCP.
Voici un enregistrement asciinema5 d’une requête faite à la main :
nc httpbin.org 80
va ouvrir une connexion TCP avec le serveur httpbin.org (la résolution DNS se fait automatiquement), sur le port 80
./uuid
et un header Host
.nc
, avec Ctrl+C
. 6On y retrouve exactement le même contenu que quand on a fait la requête via curl
- sauf que cette fois, on a nous-même écrit la requête !
Nous avons vu comment envoyer une requête. Dans l’autre sens, on va maintenant jouer le rôle du serveur. On utilisera à nouveau netcat, cette fois, pour accepter des connexions TCP.
Cet enregistrement asciinema est accompagné d’une représentation du navigateur, pour aider à visualiser l’interaction client-serveur :
nc -l 8080
va ouvrir le port 8080 (-l pour listen).http://localhost:8080/ma-page
…nc
receptionne la requête du navigateur. On retrouve le verbe GET
, le chemin voulu /ma-page
, et la version du protocole, HTTP/1.1
. Ensuite sont listés des headers, puis un \n\n
pour signifier la fin de la requête.Ctrl+C
. Le navigateur peut maintenant afficher notre page HTML !Cette page est évidemment très simple, mais ce mécanisme est la base de toute requête HTTP. Ce que nous venons de faire, c’est en gros ce que chaque serveur Web fait ! (Bien entendu, de manière plus rapide et surtout, beaucoup plus complexe.)
À travers ces exemples, nous avons vu à quoi ressemble une requête HTTP, et comment un client (comme un navigateur web) et un serveur échangent des données.
Un client web va donc ouvrir une connexion vers un serveur distant et envoyer une requête, soit pour récupérer de la donnée, soit pour en envoyer.
Un serveur web, quant à lui, est un programme qui va écouter sur un port, accepter des requêtes, les traiter selon le protocole HTTP, et suivre sa configuration pour envoyer une réponse. Celle-ci peut être codée en dur (comme dans notre cas, en mémoire), ou le serveur devrait servir le contenu d’un fichier statique, ou passer la requête à un autre serveur (reverse-proxy, load-balancing, …) ou bien encore exécuter du code et générer une réponse de toute part.
On peut intuitivement en déduire que moins le serveur web à d’actions à effectuer pour répondre à une requête, plus il sera rapide à répondre. C’est ce qu’illustre entre autres l’article ”35 Million Hot Dogs: Benchmarking Caddy vs. Nginx” (englais) : peu importe le serveur, répondre avec un fichier statique est en général au moins deux fois plus rapide que de devoir reverse-proxy la réponse à un autre serveur (et c’est sans compter le temps de traitement de cet autre serveur !). 8
En pratique, c’est un des grands avantages des Sites Statiques : tous les fichiers du site sont pré-générés à l’avance via un SSG, et le serveur web n’a plus qu’à servir ces fichiers. À l’opposé, un Site Dynamique devra, à chaque requête, exécuter souvent plusieurs centaines de lignes de code afin de construire la page demandée.
“Le style du protocole telnet
” veut dire : l’utilisateur se connecte à une machine distante, via TCP, de manière interactive (requête-réponse), et text-based. Beaucoup des protocoles de cette époque suivent ce format et sont conçus pour être lu par un humain (IMAP/SMTP, …). ↩
Httpbin est un service en ligne pour tester les requêtes HTTP. L’endpoint /uuid
répond simplement avec un UUID. ↩
En général, les applications sont programmées de manière robuste pour ne pas crash quand le protocole n’est plus suivi (par exemple si un client parle un autre protocole) : La connexion est fermée, et un message d’erreur est loggé sur le serveur ou affiché par le navigateur. ↩
Asciinema est un outil qui enregistre le contenu du terminal. Si ça vous tente, vous pouvez reproduire l’expérience dans votre propre terminal ! ↩
On peut remarquer qu’ici, la connexion reste ouverte. C’est une des améliorations par rapport à HTTP/0.9 : le serveur ne ferme plus la connexion après la réponse, et on peut lui envoyer de nouvelles requêtes ! ↩
Il n’est cependant pas nécessaire de fermer la connexion ! Elle pourrait être réutilisée pour de futurs échanges (requêtes, SSE, header 103 Early Hints…) ↩
La vitesse de réponse dépend bien entendu de la taille de la réponse, et donc du fichier envoyé. Dans ces benchmarks, un fichier de 109K met environ 3 à 5 fois plus de temps qu’une réponse de 4.5K ! On voit ainsi tout l’intérêt de minimiser ses assets, minifier son code, et bien gérer son cache. ↩