Evolution du HTTP : des fonctionnalités aux performances

HTTP est un protocole qui n’a au départ pas été créé pour être performant mais simplement pour fournir un format d’échanges de données sur des réseaux grâce à des messages formatés appelés requêtes et réponses HTTP.

Cependant, le web est devenu rapidement beaucoup plus populaire que ce qui avait été anticipé et cette adoption massive a logiquement amené une évolution rapide de celui-ci. L’idée ici est que les gens ont très rapidement voulu faire plus avec le web.

La diversification des applications du web et sa complexification rapide ont à la fois été permises par l’évolution en parallèle de HTTP et ont également poussé ce protocole à continuer d’évoluer.

HTTP s’est en effet progressivement développé en décrivant de nouvelles méthodes HTTP qui ont diversifié l’éventail des opérations permises par les clients, en créant le concept d’en-têtes HTTP et en créant de nouveaux en-têtes au cours du temps, etc.

D’un autre côté, le web est devenu de plus en plus complexe avec l’ajout de médias comme les images, les vidéos, les fichiers PDF ainsi que l’ajout de nouveaux langages côté client comme le CSS et le JavaScript. Ainsi, le nombre de ressources nécessaires pour l’affichage d’une page web a drastiquement augmenté en même temps que le poids des pages et cela a commencé à poser de sérieux défis au HTTP qui n’était pas un protocole créé avec une idée de performance.

Dans cette leçon, nous allons voir comment HTTP a réussi à évoluer et à s’adapter au cours de 30 dernières années et allons détailler les différentes évolutions majeures de ce protocole.

 

Les prémices de HTTP : HTTP/0.9

La première version « numérotée » de HTTP est HTTP/1.0 (sortie en 1996) tandis que la première version standardisée est HTTP/1.1 (sortie en 1997). Le nom « HTTP/0.9 » sert à designer la version de HTTP utilisée entre 1991 et 1996.

Au début des années 1990 et donc au début du web, les sites web étaient des sites statiques et principalement composés de texte et le protocole HTTP était également très rudimentaire :

  • Les requêtes se font sur une ligne ;
  • La seule méthode disponible est la méthode GET ;
  • Le serveur ferme immédiatement la connexion après l’envoi de la réponse.

Une requête HTTP ressemble alors à cela :

GET /une-page.html

Le serveur répond en renvoyant un document HTML.

Ici, il faut bien noter que le serveur envoie directement le contenu de la réponse, sans métadonnées puisque les en-têtes HTTP n’existent pas encore. Cela implique que le serveur ne peut renvoyer que des fichiers HTML et pas d’autres types de ressources (images, PDF, etc.).

 

Développement du protocole de transfert : HTTP/1.0

HTTP/1.0 est la première version officielle numéroté de HTTP. Cette version introduit de nombreuses nouvelles fonctionnalités et les requêtes HTTP deviennent plus complexes et également plus puissantes.

Parmi les évolutions, on peut notamment citer l’ajout du concept d’en-têtes HTTP et des méthodes POST et HEAD.

Le système de connexion reste lui le même que celui de HTTP/0.9 : dès l’envoi d’une réponse, le serveur clôt la connexion et il faut donc qu’une nouvelle connexion s’établisse entre le client et le serveur pour chaque nouvelle requête.

Les requêtes HTTP se décomposent alors de la façon suivante :

  • Une ligne de commande contenant la méthode utilisée, l’URL de la ressource et la version du protocole utilisée ;
  • Une ou plusieurs en-têtes HTTP qui permettent de transmettre des méta données (facultatif) ;
  • Une ligne vierge ;
  • Un corps de requête (facultatif).

Le serveur va également renvoyer une réponse plus complète que précédemment. Cette réponse inclut un code de statut qui permet d’indiquer si la requête a abouti ou pas ainsi que différents en-têtes dont un en-tête « content-type » qui permet d’indiquer le type de contenu renvoyé.

Une requête de récupération d’une image et la réponse serveur vont alors être de la forme suivante :


(requête)
GET /monimage.gif HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)

(réponse)
200 OK
Date: Tue, 15 Nov 1994 08:12:32 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/gif
Content-Length: 1375
(contenu de l'image)

 

Standardisation et recherche de la performance : HTTP/1.1

HTTP/1.0 introduit de nombreuses nouvelles fonctionnalités par rapport à HTTP/0.9. Cependant, les fonctionnalités ajoutées ne sont pas le fruit d’un travail commun mais plutôt de besoins ponctuels par différents systèmes.

En effet, le web n’était à cette époque pas unifié ni normé comme aujourd’hui et les différents serveurs / navigateurs / systèmes développaient eux-mêmes leurs propres fonctionnalités et « forçaient » les autres à les adopter, posant de nombreux problèmes d’interopérabilité.

Face à la croissance très rapide du web, un besoin d’unification s’est vite fait sentir pour que le web puisse continuer à être utilisable et c’est la raison pour laquelle des groupes influents ont commencé à travailler sur des standards et notamment sur le standard HTTP/1.1 qui sera publié en 1997.

En plus de la standardisation du protocole, HTTP/1.1 introduit de nouvelles méthodes et en-têtes HTTP.

En termes de nouvelles méthodes, les méthodes PUT, DELETE, OPTIONS et TRACE sont directement introduites puis viendront plus tard les méthodes CONNECT et PATCH.

En termes d’en-tête HTTP, l’en-tête Host est standardisée et est désormais obligatoire dans chaque requête HTTP. Cet en-tête permet d’indiquer au serveur web quel hébergement virtuel utilisé. L’en-tête host permet ainsi de fait d’héberger différents domaines sur une même adresse IP.

HTTP/1.1 est également la première version de HTTP où l’optimisation des performances devient une problématique centrale. En effet, les pages web sont de plus en plus exigeantes en termes de nombre de ressources à charger et du poids de chaque ressource.

Cela n’était pas prévu lors de la création de HTTP : les fondations du protocole sont qu’on ne peut effectuer qu’une seule opération par requête. En plus de cela, HTTP souffre des limitations suivantes :

  • On ne peut envoyer qu’une requête à la fois car il faut attendre de recevoir la réponse serveur avant d’en envoyer une nouvelle ;
  • Il faut établir une nouvelle connexion entre le client et le serveur après chaque réponse.

La version HTTP/1.1 s’attaque à ces limitations en proposant des mécanismes de connexions persistantes ou « keep-alive » ainsi que le support des connexions en pipeline et des connexions parallèles.

Les connexions persistantes sont des connexions qui se poursuivent pendant un certain temps après la réponse du serveur. Autrement dit, la connexion est maintenue dans l’attente d’une nouvelle requête. Cela permet d’économiser le temps de la connexion et de la déconnexion.

Le « pipelining » est un mécanisme qui permet au client d’envoyer une nouvelle requête avant même d’avoir reçu la réponse du serveur (c’est-à-dire d’envoyer plusieurs requêtes « en même temps » en établissant plusieurs connexions simultanées). Les réponses du serveur doivent cependant arriver dans le même ordre que celui de l’envoi des requêtes.

Le principe de connexions parallèles, comme son nom l’indique, est d’établir plusieurs connexions simultanées de façon à pouvoir envoyer plus de requêtes en même temps. Les navigateurs de l’époque étaient capables de supporter de 2 à 8 connexions simultanées.

Outre cela, HTTP/1.1 introduit également le concept de négociation de contenu au niveau du langage, de l’encodage et du type (notamment via les en-tête Accept, Accept-Language et Accept-Encoding. Cela permet au client et au serveur de se mettre d’accord sur le contenu à échanger. HTTP/1.1 ajoute également de nouveaux mécanismes de caches plus perfectionnés.

Une ensemble de requêtes / réponses HTTP/1.1 ressemble à ceci :


(Request 1)
GET /en-US/docs/Glossary/Simple_header HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/en-US/docs/Glossary/Simple_header

(Response 1)
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Wed, 20 Jul 2016 10:55:30 GMT
Etag: "547fa7e369ef56031dd3bff2ace9fc0832eb251a"
Keep-Alive: timeout=5, max=1000
Last-Modified: Tue, 19 Jul 2016 00:59:33 GMT
Server: Apache
Transfer-Encoding: chunked
Vary: Cookie, Accept-Encoding

[content]

(Request 2)
GET /static/img/header-background.png HTTP/1.1
Host: developer.cdn.mozilla.net
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/en-US/docs/Glossary/Simple_header

(Response 2)
HTTP/1.1 200 OK
Age: 9578461
Cache-Control: public, max-age=315360000
Connection: keep-alive
Content-Length: 3077
Content-Type: image/png
Date: Thu, 31 Mar 2016 13:34:46 GMT
Last-Modified: Wed, 21 Oct 2015 18:27:50 GMT
Server: Apache

[image content of 3077 bytes]

 

La version de référence : HTTP/2 et SPDY

HTTP/2 est la version du protocole de transfert hypertexte de reference utilisée aujourd’hui. Il convient avant tout de noter que HTTP/2 ne modifie en rien la sémantique d’application de HTTP. Tous les concepts de base, tels que les méthodes HTTP, les codes d’état, les URI et les champs d’en-tête sont conservés tels quels. En revanche, HTTP/2 modifie la façon dont les données sont formatées et transportées entre le client et le serveur.

HTTP/2 a été publié en 2015 et trouve son origine dans un autre protocole appelé SPDY et développé par Google.

Au début des années 2010, Google a en effet commencé à effectuer des travaux pour améliorer les performances de HTTP qui n’est pas un protocole créé avec une idée de performance à la base et qui est donc très complexe à optimiser. Cela a donné lieu à la naissance de SPDY, un protocole réseau visant à augmenter les capacités du protocole HTTP sans toutefois remplacer ce dernier (SPDY fonctionne avec HTTP).

L’objectif principal de SPDY est de réduire la durée de téléchargement des pages Web. Pour cela, les requêtes sont classées par ordre de priorité (de sorte à ce que les requêtes les plus importantes comme le chargement d’un fichier de style ne soient pas bloquées par d’autres moins importantes comme le chargement d’une image en bas de page). En plus de cela, un mécanisme de multiplexing est mis en place.

Concrètement, le multiplexing permet d’envoyer plusieurs requêtes d’affilée lors d’une même connexion sans attendre à chaque fois la réponse du serveur tout comme le permettait théoriquement le pipelining. En pratique, toutefois, le pipelining était très complexe à mettre en place (notamment avec les connexions parallèles), mal supporté et pouvait entrainer des faille de sécurité ce qui n’est plus le cas avec le multiplexing.

En plus de cela, avec le multiplexing, les réponses du serveur peuvent désormais revenir dans n’importe quel ordre. En, d’autres termes, le serveur n’est plus obligé de renvoyer les réponses dans le même ordre que l’envoi des requêtes, ce qui permet d’éviter des blocages et rend le processus plus performant.

SPDY a été implémenté et testé par Google pendant plusieurs années avant que Google se rapproche du groupe de travail chargé de l’évolution de HTTP et leur passe les documents techniques liés à SPDY afin que ces avancées soient introduites dans HTTP même.

Cela va donc être l’une des fondations de HTTP/2. En plus du multiplexing et de la priorisation des requêtes, on notera également que HTTP/2 compresse désormais les en-têtes HTTP, de telle sorte qu’un même en-tête n’est pas renvoyé plusieurs inutilement. HTTP/2 améliore également les mécanismes de cache notamment en permettant au serveur de remplir le cache du client avant même que le client n’envoie une requête.

Enfin, il faut également souligner que HTTP/2 est désormais encodé directement en binaire et non plus en texte. Cela le rend donc impossible à lire ou à écrire à la main mais permet de nouvelles optimalisations du protocole.

Pour être tout à fait précis, les données sont découpées en « frames » (trames) codées en binaire qui sont ensuite mappées à des messages qui appartiennent à un flux particulier et qui sont tous multiplexés dans une seule connexion TCP. C’est la base qui permet toutes les autres fonctionnalités et optimisations de performances fournies par le protocole HTTP/2.

 

Le futur du HTTP : HTTP/3.0 et QUIC

Après SPDY, Google a continué à travailler sur les problématiques d’optimisation des performances sur le Web en général pour « rendre le Web plus rapide ».

Google a ainsi développé un nouveau protocole appelé QUIC pour « Quick UDP Internet Connections ». Ce protocole se situe au niveau de la couche transport des modèles OSI et TCP/IP et est censé fonctionner au-dessus d’un autre protocole de transport nommée UDP.

L’idée de Google était ici d’utiliser le protocole de transport UDP plutôt que le protocole commun TCP pour proposer des connexions HTTP et des transferts de données plus rapides.

Tout comme avec SPDY, Google a passé son travail sur QUIC à l’IETF (le groupe chargé de l’évolution de HTTP) afin que les avancées faites soient intégrées à HTTP. Cela a marqué le point de départ de « HTTP over QUIC » renommé ensuite HTTP/3.

Le support pour HTTP/3 a été ajouté à Chrome en septembre 2019 et à Firefox en novembre 2019. En janvier 2020, 2% des sites l’utilisent déjà. Nous reviendrons plus en détail sur HTTP/3 dans la fin de ce cours.