Présentation du protocole HTTP/2

Le protocole HTTP/2 rendra nos applications plus rapides, plus simples et plus robustes, une combinaison rare, en nous permettant d'annuler de nombreuses solutions de contournement HTTP/1.1 précédemment effectuées dans nos applications et de résoudre ces problèmes au niveau de la couche de transport elle-même. Mieux encore, elle offre également un certain nombre de possibilités inédites pour optimiser nos applications et améliorer les performances.

Les principaux objectifs de HTTP/2 sont de réduire la latence en activant le multiplexage complet des requêtes et des réponses, de minimiser la surcharge du protocole grâce à la compression efficace des champs d'en-tête HTTP, et d'ajouter la prise en charge de la hiérarchisation des requêtes et du serveur push. Pour mettre en œuvre ces exigences, il existe un grand nombre d'autres améliorations de protocole, telles que le contrôle de flux, la gestion des exceptions et les mécanismes de mise à niveau. Toutefois, il s'agit des fonctionnalités les plus importantes que tout développeur Web doit comprendre et exploiter dans ses applications.

Le protocole HTTP/2 ne modifie en aucune façon la sémantique d'application du protocole HTTP. Tous les concepts fondamentaux, tels que les méthodes HTTP, les codes d'état, les URI et les champs d'en-tête, restent en place. En revanche, HTTP/2 modifie la façon dont les données sont formatées (frames) et transmises entre le client et le serveur, qui gèrent tous deux l'ensemble du processus, et masque toute la complexité de nos applications dans la nouvelle couche de cadrage. Par conséquent, toutes les applications existantes peuvent être distribuées sans modification.

Pourquoi pas HTTP/1.2 ?

Pour atteindre les objectifs de performances définis par le groupe de travail HTTP, HTTP/2 introduit une nouvelle couche de cadrage binaire qui n'est pas rétrocompatible avec les serveurs et clients HTTP/1.x précédents. La version majeure du protocole passe donc à HTTP/2.

Cela dit, à moins que vous ne mettiez en œuvre un serveur Web (ou un client personnalisé) en utilisant des sockets TCP bruts, vous ne verrez aucune différence: tout le nouveau cadrage de bas niveau est effectué par le client et le serveur en votre nom. Les seules différences observables seront l'amélioration des performances et la disponibilité de nouvelles fonctionnalités telles que la hiérarchisation des requêtes, le contrôle de flux et la transmission du serveur.

Bref historique de SPDY et de HTTP/2

Développé par Google et annoncé mi-2009, SPDY était un protocole expérimental dont le but principal était de réduire la latence de chargement des pages Web en résolvant certaines des limitations de performances bien connues du protocole HTTP/1.1. Plus précisément, les objectifs décrits pour le projet ont été définis comme suit:

  • Ciblez une réduction de 50% du temps de chargement des pages (PLT).
  • Évitez que les auteurs de sites Web aient besoin de modifier votre contenu.
  • Réduisez la complexité du déploiement et évitez les modifications de l'infrastructure réseau.
  • Développez ce nouveau protocole en partenariat avec la communauté Open Source.
  • Recueillez des données de performances réelles pour (invalider) le protocole expérimental.

Peu de temps après l'annonce initiale, Mike Belshe et Roberto Peon, tous deux ingénieurs logiciels chez Google, ont partagé leurs premiers résultats, la documentation et le code source de la mise en œuvre expérimentale du nouveau protocole SPDY:

Jusqu'à présent, nous n'avons testé SPDY qu'en laboratoire. Les premiers résultats sont très encourageants : lorsque nous téléchargeons les 25 premiers sites Web via des connexions réseau domestiques simulées, nous constatons une amélioration significative des performances : le chargement des pages est jusqu'à 55% plus rapide. (Blog Chromium)

En 2012, le nouveau protocole expérimental a été pris en charge par Chrome, Firefox et Opera. Un nombre croissant de sites, grands (par exemple, Google, Twitter, Facebook) ou petits, déployaient SPDY dans leur infrastructure. En effet, SPDY était en bonne voie pour devenir une norme de facto grâce à une adoption croissante du secteur.

En constatant cette tendance, le HTTP-WG (HTTP Working Group) a lancé une nouvelle initiative visant à exploiter les leçons tirées de SPDY, à les développer et à les améliorer, et à fournir une norme "HTTP/2" officielle. Une nouvelle charte a été rédigée, un appel ouvert aux propositions HTTP/2 a été fait et, après de nombreuses discussions au sein du groupe de travail, la spécification SPDY a été adoptée comme point de départ du nouveau protocole HTTP/2.

Au cours des années suivantes, SPDY et HTTP/2 ont continué à évoluer en parallèle, SPDY jouant le rôle de branche expérimentale utilisée pour tester de nouvelles fonctionnalités et propositions pour la norme HTTP/2. Ce qui semble correct sur le papier peut ne pas fonctionner en pratique, et inversement, et SPDY a proposé une méthode pour tester et évaluer chaque proposition avant son inclusion dans la norme HTTP/2. Au bout du compte, ce processus a duré trois ans et a abouti à une douzaine de versions intermédiaires:

  • Mars 2012: appel à propositions pour le protocole HTTP/2
  • Novembre 2012: première version préliminaire du protocole HTTP/2 (basé sur SPDY)
  • Août 2014: publication des brouillons 17 et HPACK draft 12
  • Août 2014: dernier appel du groupe de travail pour le protocole HTTP/2
  • Février 2015: brouillons HTTP/2 et HPACK approuvés par l'IESG
  • Mai 2015: publication des documents RFC 7540 (HTTP/2) et RFC 7541 (HPACK)

Début 2015, l'IESG a examiné et approuvé la nouvelle norme HTTP/2 pour la publication. Peu de temps après, l'équipe Google Chrome a annoncé son calendrier d'abandon des extensions SPDY et NPN pour TLS:

Les principales modifications apportées à HTTP/2 par rapport à HTTP/1.1 visent à améliorer les performances. Certaines fonctionnalités clés, telles que le multiplexage, la compression des en-têtes, la hiérarchisation et la négociation de protocoles, sont issues d'un précédent protocole ouvert mais non standard nommé SPDY. Chrome est compatible avec SPDY depuis Chrome 6, mais comme la plupart des avantages sont présents dans HTTP/2, il est temps de dire au revoir. Nous prévoyons de supprimer la prise en charge de SPDY début 2016 et de supprimer également la prise en charge de l'extension TLS nommée NPN au profit d'ALPN dans Chrome. Les développeurs de serveurs sont vivement encouragés à passer à HTTP/2 et ALPN.

Nous sommes heureux d'avoir contribué au processus de normes ouvertes qui a conduit à HTTP/2, et nous espérons voir largement son adoption, compte tenu de l'engagement général du secteur concernant la normalisation et l'implémentation. (Blog Chromium)

La coévolution de SPDY et de développeurs de sites Web et de navigateurs ont permis à SPDY et à HTTP/2 de bénéficier d'une expérience concrète du nouveau protocole au fur et à mesure de son développement. Par conséquent, la norme HTTP/2 est l'une des normes les plus efficaces et les plus testées dès le départ. Au moment où HTTP/2 a été approuvé par l'IESG, il existait des dizaines d'implémentations client et serveur soigneusement testées et prêtes pour la production. Quelques semaines seulement après l'approbation du protocole final, de nombreux utilisateurs bénéficiaient déjà de ses avantages, car plusieurs navigateurs populaires (et de nombreux sites) ont déployé la compatibilité complète avec HTTP/2.

Objectifs de conception et techniques

Les versions antérieures du protocole HTTP ont été volontairement conçues pour simplifier l'implémentation: HTTP/0.9 était un protocole d'une ligne pour amorcer le World Wide Web, HTTP/1.0 décrivait les extensions populaires vers HTTP/0.9 dans une norme d'information. HTTP/1.1 a introduit une norme officielle de l'IETF. Consultez la page Bref historique du HTTP. En tant que tel, HTTP/0.9-1.x a fourni exactement ce qu'il avait prévu de faire: HTTP est l'un des protocoles d'application les plus largement adoptés sur Internet.

Malheureusement, la simplicité de l'implémentation a également affecté les performances de l'application: les clients HTTP/1.x doivent utiliser plusieurs connexions pour obtenir la simultanéité et réduire la latence ; HTTP/1.x ne compresse pas les en-têtes de requête et de réponse, ce qui entraîne un trafic réseau inutile ; HTTP/1.x ne permet pas de hiérarchiser efficacement les ressources, ce qui entraîne une mauvaise utilisation de la connexion TCP sous-jacente, etc.

Ces limites n'étaient pas fatales, mais à mesure que la portée, la complexité et l'importance des applications Web continuaient de croître dans notre vie quotidienne, elles ont pesé de plus en plus pour les développeurs et les utilisateurs du Web. C'est précisément la lacune que HTTP/2 a été conçue pour combler:

Le protocole HTTP/2 permet d'utiliser plus efficacement les ressources réseau et de réduire la perception de la latence en introduisant la compression des champs d'en-tête et en autorisant plusieurs échanges simultanés sur la même connexion. Plus précisément, il permet l'entrelacement de messages de requête et de réponse sur la même connexion et utilise un codage efficace pour les champs d'en-tête HTTP. Cela permet également de hiérarchiser les requêtes, ce qui permet aux requêtes plus importantes de s'exécuter plus rapidement, ce qui améliore les performances.

Le protocole obtenu est plus convivial pour le réseau, car moins de connexions TCP peuvent être utilisées par rapport à HTTP/1.x. Cela signifie une concurrence réduite avec les autres flux et des connexions plus longues, ce qui conduit à une meilleure utilisation de la capacité réseau disponible. Enfin, HTTP/2 permet également un traitement plus efficace des messages grâce à l'utilisation d'une trame de message binaire. (Hypertext Transfer Protocol version 2, brouillon 17)

Il est important de noter que HTTP/2 étend les normes HTTP précédentes, mais ne les remplace pas. La sémantique d'application du protocole HTTP est identique, et aucune modification n'a été apportée aux fonctionnalités proposées ni aux concepts fondamentaux tels que les méthodes HTTP, les codes d'état, les URI et les champs d'en-tête. Ces modifications n'entrent pas dans le cadre de l'effort HTTP/2. Cela dit, bien que l'API de haut niveau reste la même, il est important de comprendre comment les modifications de bas niveau répondent aux limitations de performances des protocoles précédents. Examinons brièvement la couche de cadrage binaire et ses caractéristiques.

Calque de cadrage binaire

Au cœur de toutes les améliorations de performances de HTTP/2 se trouve la nouvelle couche de cadrage binaire, qui détermine la manière dont les messages HTTP sont encapsulés et transférés entre le client et le serveur.

Couche de cadrage binaire HTTP/2

La "couche" fait référence à un choix de conception visant à introduire un nouveau mécanisme d'encodage optimisé entre l'interface de socket et l'API HTTP de niveau supérieur exposée à nos applications. La sémantique HTTP, telle que les verbes, les méthodes et les en-têtes, n'est pas affectée, mais la façon dont ils sont encodés lors du transfert est différente. Contrairement au protocole HTTP/1.x en texte brut délimité par un retour à la ligne, toute communication HTTP/2 est divisée en messages et en trames plus petits, chacun encodé au format binaire.

Par conséquent, le client et le serveur doivent utiliser le nouveau mécanisme d'encodage binaire pour se comprendre: un client HTTP/1.x ne comprendra pas un serveur HTTP/2 uniquement, et inversement. Heureusement, nos applications restent totalement inconscientes de toutes ces modifications, car le client et le serveur effectuent tous les cadrages nécessaires pour notre compte.

Flux, messages et cadres

L'introduction du nouveau mécanisme de cadrage binaire modifie la façon dont les données sont échangées entre le client et le serveur. Pour décrire ce processus, familiarisons-nous avec la terminologie HTTP/2:

  • Flux: flux bidirectionnel d'octets dans une connexion établie, pouvant transporter un ou plusieurs messages.
  • Message: séquence complète de trames associées à un message de requête ou de réponse logique.
  • Trame: plus petite unité de communication dans HTTP/2, chacune contenant un en-tête de frame, qui identifie au minimum le flux auquel appartient la trame.

La relation entre ces conditions peut être résumée comme suit:

  • Toutes les communications s'effectuent via une seule connexion TCP pouvant transporter un nombre illimité de flux bidirectionnels.
  • Chaque flux est associé à un identifiant unique et à des informations de priorité facultatives permettant de transmettre des messages bidirectionnels.
  • Chaque message est un message HTTP logique, tel qu'une requête ou une réponse, composé d'une ou de plusieurs trames.
  • La trame est la plus petite unité de communication qui transporte un type de données spécifique, par exemple les en-têtes HTTP, la charge utile des messages, etc. Les trames de différents flux peuvent être entrelacées, puis réassemblées via l'identifiant de flux intégré dans l'en-tête de chaque trame.

Flux, messages et trames HTTP/2

En bref, HTTP/2 décompose la communication par protocole HTTP en un échange de trames encodées en binaires, qui sont ensuite mappés à des messages appartenant à un flux particulier, qui sont tous multiplexés au sein d'une même connexion TCP. Cette base est à la base de toutes les autres fonctionnalités et optimisations de performances fournies par le protocole HTTP/2.

Multiplexage des requêtes et des réponses

Avec HTTP/1.x, si le client souhaite effectuer plusieurs requêtes parallèles pour améliorer les performances, plusieurs connexions TCP doivent être utilisées (consultez la section Utiliser plusieurs connexions TCP). Ce comportement est une conséquence directe du modèle de distribution HTTP/1.x, qui garantit qu'une seule réponse peut être fournie à la fois (mise en file d'attente des réponses) par connexion. Pire encore, cela entraîne également un blocage en tête de ligne et une utilisation inefficace de la connexion TCP sous-jacente.

La nouvelle couche de cadrage binaire dans HTTP/2 élimine ces limitations et permet le multiplexage complet des requêtes et des réponses, en permettant au client et au serveur de décomposer un message HTTP en trames indépendantes, de les entrelacer, puis de les réassembler à l'autre extrémité.

Multiplexage des requêtes et des réponses HTTP/2 au sein d'une connexion partagée

L'instantané capture plusieurs flux en cours de transfert au sein de la même connexion. Le client transmet une trame DATA (flux 5) au serveur, tandis que le serveur transmet une séquence entrelacée de trames au client pour les flux 1 et 3. Par conséquent, trois flux parallèles sont en cours de transfert.

La possibilité de décomposer un message HTTP en frames indépendants, de les entrelacer, puis de les réassembler à l'autre bout est l'amélioration la plus importante de HTTP/2. En fait, il entraîne de nombreux avantages en termes de performances sur l'ensemble de la pile de toutes les technologies Web, ce qui nous permet:

  • Entrelacez plusieurs requêtes en parallèle sans bloquer toutes les autres.
  • Entrelacez plusieurs réponses en parallèle sans bloquer aucune.
  • Utilisez une seule connexion pour diffuser plusieurs requêtes et réponses en parallèle.
  • Supprimez les solutions de contournement HTTP/1.x inutiles (consultez la section Optimiser pour HTTP/1.x, telles que les fichiers concaténés, les sprites d'image et la segmentation de domaine).
  • Réduisez le temps de chargement des pages en éliminant la latence inutile et en améliorant l'utilisation de la capacité réseau disponible.
  • Et bien plus encore...

La nouvelle couche de cadrage binaire dans HTTP/2 résout le problème de blocage en tête de ligne rencontré dans HTTP/1.x, et élimine la nécessité d'avoir plusieurs connexions pour permettre le traitement et la distribution parallèles des requêtes et des réponses. Ainsi, nos applications sont plus rapides, plus simples et moins coûteuses à déployer.

Hiérarchisation des flux

Une fois qu'un message HTTP peut être divisé en plusieurs trames individuelles et que nous autorisons le multiplexage des trames de plusieurs flux, l'ordre dans lequel les trames sont entrelacées et livrées par le client et le serveur devient un critère de performances essentiel. Pour faciliter ce processus, la norme HTTP/2 autorise chaque flux à avoir une pondération et une dépendance associées:

  • Chaque flux peut se voir attribuer une pondération entière comprise entre 1 et 256.
  • Chaque flux peut être associé à une dépendance explicite à un autre flux.

La combinaison de dépendances de flux et de pondérations permet au client de construire et de communiquer un "arbre de priorisation" qui exprime la façon dont il préfère recevoir des réponses. Le serveur peut ensuite utiliser ces informations pour prioriser le traitement par flux en contrôlant l'allocation du processeur, de la mémoire et d'autres ressources, et une fois les données de réponse disponibles, l'allocation de bande passante pour garantir une livraison optimale des réponses à priorité élevée au client.

Dépendances et pondérations des flux HTTP/2

Une dépendance de flux dans HTTP/2 est déclarée en référençant l'identifiant unique d'un autre flux en tant que parent. Si l'identifiant est omis, le flux est considéré comme dépendant du "flux racine". La déclaration d'une dépendance de flux indique que, si possible, le flux parent doit être alloué aux ressources avant ses dépendances. En d'autres termes, « Veuillez traiter et fournir la réponse D avant la réponse C ».

Les flux qui partagent le même parent (en d'autres termes, des flux frères) doivent se voir attribuer des ressources en fonction de leur pondération. Par exemple, si le flux A a une pondération de 12 et que son frère B a une pondération de 4, déterminer la proportion des ressources que chacun de ces flux doit recevoir:

  1. Additionner toutes les pondérations: 4 + 12 = 16
  2. Divisez la pondération de chaque flux par la pondération totale: A = 12/16, B = 4/16

Ainsi, le flux A devrait recevoir les trois quarts des ressources disponibles, et le flux B un quart des ressources disponibles. Le flux B devrait recevoir un tiers des ressources allouées au flux A. Passons en revue quelques autres exemples pratiques dans l'image ci-dessus. De gauche à droite :

  1. Ni le flux A, ni le flux B ne spécifient une dépendance parente et ne sont pas considérés comme dépendants du "flux racine" implicite. Le flux A a une pondération de 12, tandis que le flux B a une pondération de 4. Ainsi, en fonction des pondérations proportionnelles, le flux B doit recevoir un tiers des ressources allouées au flux A.
  2. Le flux D dépend du flux racine ; le flux C dépend du flux D. Ainsi, D devrait recevoir l'allocation complète des ressources avant C. Les pondérations sont sans conséquence, car la dépendance de C communique une préférence plus forte.
  3. Le flux D doit recevoir l'allocation totale des ressources avant C ; le flux C doit recevoir l'allocation complète des ressources avant A et B ; le flux B doit recevoir un tiers des ressources allouées au flux A.
  4. Le flux D doit recevoir l'allocation complète des ressources avant E et C ; E et C doivent recevoir une allocation égale avant A et B. A et B doivent recevoir une allocation proportionnelle en fonction de leurs pondérations.

Comme l'illustrent les exemples ci-dessus, la combinaison de dépendances de flux et de pondérations fournit un langage expressif pour la hiérarchisation des ressources, une fonctionnalité essentielle pour améliorer les performances de navigation lorsque nous disposons de nombreux types de ressources avec différentes dépendances et pondérations. Mieux encore, le protocole HTTP/2 permet également au client de mettre à jour ces préférences à tout moment, ce qui permet d'effectuer d'autres optimisations dans le navigateur. En d'autres termes, nous pouvons modifier les dépendances et réaffecter les pondérations en réponse à l'interaction de l'utilisateur et à d'autres signaux.

Une connexion par origine

Avec le nouveau mécanisme de cadrage binaire en place, HTTP/2 n'a plus besoin de plusieurs connexions TCP à des flux multiplex en parallèle. Chaque flux est divisé en plusieurs trames, qui peuvent être entrelacées et prioritaires. Par conséquent, toutes les connexions HTTP/2 sont persistantes et une seule connexion par origine est requise, ce qui offre de nombreux avantages en termes de performances.

Pour SPDY et HTTP/2, la fonctionnalité tueuse consiste en un multiplexage arbitraire sur un canal contrôlé par l'encombrement du puits unique. Je suis stupéfaite à quel point c'est important et à quel point cela fonctionne. L'une des métriques intéressantes à ce sujet est la fraction des connexions créées qui ne contiennent qu'une seule transaction HTTP (et qui fait que cette transaction supporte donc toute la surcharge). Pour HTTP/1, 74% des connexions actives ne comportent qu'une seule transaction. Les connexions persistantes ne sont tout simplement pas aussi utiles que nous le souhaitons. Mais avec HTTP/2, ce nombre chute à 25%. C'est un énorme avantage pour la réduction des frais généraux. (HTTP/2 correspond à "En direct dans Firefox", Patrick McManus).

La plupart des transferts HTTP sont courts et en rafales, tandis que le protocole TCP est optimisé pour les transferts de données groupés de longue durée. En réutilisant la même connexion, HTTP/2 peut à la fois utiliser plus efficacement chaque connexion TCP et réduire considérablement la surcharge globale du protocole. De plus, l'utilisation d'un nombre réduit de connexions réduit l'encombrement de la mémoire et du traitement le long du chemin de connexion complet (c'est-à-dire le client, les intermédiaires et les serveurs d'origine). Cela réduit les coûts opérationnels globaux, et améliore l'utilisation et la capacité du réseau. Par conséquent, le passage au protocole HTTP/2 devrait non seulement réduire la latence du réseau, mais également améliorer le débit et réduire les coûts opérationnels.

Contrôle de flux

Le contrôle de flux est un mécanisme permettant d'empêcher l'émetteur de submerger le destinataire de données qu'il ne souhaite ou ne peut pas traiter: le récepteur peut être occupé, soumis à une charge importante, ou n'être prêt à allouer qu'une quantité fixe de ressources pour un flux particulier. Par exemple, le client peut avoir demandé un flux vidéo volumineux à priorité élevée, mais l'utilisateur a mis la vidéo en pause. Le client souhaite maintenant suspendre ou limiter sa diffusion à partir du serveur pour éviter d'extraire et de mettre en mémoire tampon des données inutiles. Un serveur proxy peut également disposer de connexions en aval rapides et de connexions en amont lentes. De la même manière, il souhaite réguler la rapidité de diffusion des données en aval pour qu'elle corresponde à la vitesse en amont afin de contrôler l'utilisation des ressources, et ainsi de suite.

Les exigences ci-dessus vous rappellent-elles le contrôle de flux TCP ? Elles devraient, car le problème est effectivement identique (voir Contrôle de flux). Toutefois, comme les flux HTTP/2 sont multiplexés dans une seule connexion TCP, le contrôle de flux TCP n'est pas assez précis et ne fournit pas les API au niveau de l'application nécessaires pour réguler la diffusion de flux individuels. Pour résoudre ce problème, HTTP/2 fournit un ensemble de composants simples qui permettent au client et au serveur d'implémenter leur propre contrôle de flux au niveau du flux et de la connexion:

  • Le contrôle de flux est directionnel. Chaque récepteur peut choisir de définir la taille de fenêtre de son choix pour chaque flux et l'ensemble de la connexion.
  • Le contrôle de flux est basé sur le crédit. Chaque récepteur annonce sa connexion initiale et sa fenêtre de contrôle de flux de flux (en octets), qui est réduite chaque fois que l'expéditeur émet une trame DATA et incrémentée via une trame WINDOW_UPDATE envoyée par le récepteur.
  • Le contrôle de flux ne peut pas être désactivé. Une fois la connexion HTTP/2 établie, le client et le serveur échangent des trames SETTINGS, qui définissent la taille de la fenêtre de contrôle de flux dans les deux sens. La valeur par défaut de la fenêtre de contrôle de flux est définie sur 65 535 octets, mais le récepteur peut définir une taille de fenêtre maximale élevée (2^31-1 octets) et la maintenir en envoyant une trame WINDOW_UPDATE chaque fois que des données sont reçues.
  • Le contrôle de flux s'effectue saut par saut, et non de bout en bout. Autrement dit, un intermédiaire peut l'utiliser pour contrôler l'utilisation des ressources et mettre en œuvre des mécanismes d'allocation de ressources en fonction de critères et d'heuristiques propres.

Le protocole HTTP/2 ne spécifie pas d'algorithme particulier pour implémenter le contrôle de flux. Au lieu de cela, il fournit les éléments de base simples et reporte l'implémentation au client et au serveur, qui peuvent l'utiliser pour mettre en œuvre des stratégies personnalisées afin de réguler l'utilisation et l'allocation des ressources, ainsi que pour mettre en œuvre de nouvelles fonctionnalités de livraison susceptibles d'améliorer les performances réelles et perçues (voir Vitesse, performances et perception humaine) de nos applications Web.

Par exemple, le contrôle de flux au niveau de la couche d'application permet au navigateur de ne récupérer qu'une partie d'une ressource particulière, de mettre la récupération en attente en réduisant à zéro la fenêtre de contrôle du flux de flux, puis de la reprendre ultérieurement. En d'autres termes, elle permet au navigateur d'extraire un aperçu ou de lancer une première analyse d'une image, de l'afficher, de poursuivre l'extraction à priorité élevée et de reprendre l'exploration une fois le chargement des autres ressources critiques terminé.

La fonctionnalité Server Push

Une autre nouvelle fonctionnalité puissante de HTTP/2 est la capacité du serveur à envoyer plusieurs réponses pour une seule requête client. Autrement dit, en plus de la réponse à la requête d'origine, le serveur peut transmettre des ressources supplémentaires au client (figure 12-5), sans que celui-ci n'ait à les demander chacune explicitement.

Le serveur lance de nouveaux flux (promesses) pour les ressources push

Pourquoi aurons-nous besoin d'un tel mécanisme dans un navigateur ? Une application Web classique se compose de dizaines de ressources, qui sont toutes découvertes par le client en examinant le document fourni par le serveur. Par conséquent, pourquoi ne pas éliminer la latence supplémentaire et laisser le serveur transmettre les ressources associées à l'avance ? Le serveur sait déjà de quelles ressources le client aura besoin ; il s'agit de la transmission du serveur.

Si vous avez déjà intégré un fichier CSS, JavaScript ou tout autre élément via un URI de données (consultez la section Intégration des ressources), vous disposez déjà d'une expérience pratique de la transmission au serveur. En intégrant manuellement la ressource dans le document, nous la transmettons au client, sans attendre que celui-ci la demande. Avec HTTP/2, nous pouvons obtenir les mêmes résultats, mais avec des avantages supplémentaires en termes de performances. Les ressources push peuvent être:

  • Mise en cache par le client
  • Réutilisation sur différentes pages
  • Multiplexé avec d'autres ressources
  • Priorisé par le serveur
  • Refusé par le client

INTRODUCTION À PUSH_PROMISE

Tous les flux push du serveur sont initiés via des trames PUSH_PROMISE, qui signalent l'intention du serveur de transmettre les ressources décrites au client et qui doivent être transmises avant les données de réponse qui demandent les ressources transmises. Cet ordre de distribution est essentiel: le client doit savoir quelles ressources le serveur a l'intention de transférer afin d'éviter de créer des requêtes en double pour ces ressources. La stratégie la plus simple pour satisfaire à cette exigence consiste à envoyer toutes les trames PUSH_PROMISE, qui ne contiennent que les en-têtes HTTP de la ressource prometteuse, avant la réponse du parent (en d'autres termes, les trames DATA).

Une fois que le client a reçu une trame PUSH_PROMISE, il a la possibilité de refuser le flux (via une trame RST_STREAM) s'il le souhaite. (Cela peut se produire, par exemple, parce que la ressource se trouve déjà dans le cache.) Il s'agit d'une amélioration importante par rapport à HTTP/1.x. En revanche, l'utilisation de l'intégration des ressources, une "optimisation" courante pour HTTP/1.x, équivaut à un "transfert forcé": le client ne peut pas la désactiver, l'annuler ni la traiter individuellement.

Avec HTTP/2, le client conserve un contrôle total de l'utilisation du serveur push. Le client peut limiter le nombre de flux transmis simultanément, ajuster la fenêtre de contrôle de flux initiale pour contrôler la quantité de données transmises à la première ouverture du flux, ou désactiver complètement la transmission du serveur. Ces préférences sont communiquées via les frames SETTINGS au début de la connexion HTTP/2 et peuvent être mises à jour à tout moment.

Chaque ressource transférée est un flux qui, contrairement à une ressource intégrée, permet d'être multiplexée, prioritaire et traitée individuellement par le client. La seule restriction de sécurité, telle qu'elle est appliquée par le navigateur, est que les ressources transmises doivent respecter la règle de même origine: le serveur doit faire autorité pour le contenu fourni.

Compression d'en-têtes

Chaque transfert HTTP comporte un ensemble d'en-têtes qui décrivent la ressource transférée et ses propriétés. Dans HTTP/1.x, ces métadonnées sont toujours envoyées en texte brut et ajoutent entre 500 et 800 octets de surcharge par transfert, et parfois des kilo-octets de plus si des cookies HTTP sont utilisés. (Voir Mesurer et contrôler la surcharge du protocole.) Pour réduire cette surcharge et améliorer les performances, HTTP/2 compresse les métadonnées d'en-tête de requête et de réponse à l'aide du format de compression HPACK, qui utilise deux techniques simples, mais puissantes:

  1. Elle permet d'encoder les champs d'en-tête transmis à l'aide d'un code Huffman statique, ce qui réduit la taille de leur transfert individuel.
  2. Elle exige que le client et le serveur gèrent et mettent à jour une liste indexée de champs d'en-tête vus précédemment (en d'autres termes, il établit un contexte de compression partagé), qui est ensuite utilisé comme référence pour encoder efficacement les valeurs transmises précédemment.

Le codage de Huffman permet de compresser les valeurs individuelles lors du transfert, et la liste indexée des valeurs transférées précédemment nous permet d'encoder les valeurs en double en transférant des valeurs d'index qui peuvent être utilisées pour rechercher et reconstruire efficacement les clés et les valeurs d'en-tête complètes.

HPACK: compression des en-têtes pour HTTP/2

Autre optimisation : le contexte de compression HPACK consiste en une table statique et dynamique : la table statique est définie dans la spécification et fournit une liste de champs d'en-tête HTTP courants que toutes les connexions sont susceptibles d'utiliser (par exemple, des noms d'en-tête valides). La table dynamique est initialement vide et est mise à jour en fonction des valeurs échangées au sein d'une connexion particulière. Par conséquent, la taille de chaque requête est réduite en utilisant le codage statique de Huffman pour les valeurs jusqu'à présent inédites et la substitution des index par les valeurs déjà présentes dans les tables statiques ou dynamiques de chaque côté.

Sécurité et performances du HPACK

Les premières versions de HTTP/2 et de SPDY utilisaient zlib, avec un dictionnaire personnalisé, pour compresser tous les en-têtes HTTP. Cela a permis de réduire de 85 à 88 % la taille des données d'en-tête transférées et de réduire considérablement la latence du temps de chargement des pages:

Sur le lien DSL à faible bande passante, pour lequel le lien d'importation n'est que de 375 Kbit/s, la compression d'en-tête de demande, en particulier, a permis d'améliorer considérablement le temps de chargement de la page pour certains sites (en d'autres termes, ceux qui émettent un grand nombre de demandes de ressources). Nous avons constaté une réduction de 45 à 1 142 ms du temps de chargement des pages simplement en raison de la compression de l'en-tête. (Livre blanc SPDY, chromium.org)

Cependant, au cours de l'été 2012, une attaque de sécurité "CRIME" a été publiée contre les algorithmes de compression TLS et SPDY, ce qui pourrait entraîner un piratage de session. En conséquence, l'algorithme de compression zlib a été remplacé par HPACK, spécialement conçu pour résoudre les problèmes de sécurité détectés, être efficace et simple à implémenter correctement et, bien sûr, permettre une bonne compression des métadonnées d'en-tête HTTP.

Pour en savoir plus sur l'algorithme de compression HPACK, consultez la page IETF HPACK – Header Compression for HTTP/2.

Documentation complémentaire