6. Connection Management
La messagerie HTTP est indépendante du ou des protocoles de connexion sous-jacents de couche transport ou de couche session. HTTP présuppose seulement un transport fiable assurant une livraison dans l'ordre des requêtes et une livraison correspondante dans l'ordre des réponses. La correspondance entre les structures de requête et de réponse HTTP et les unités de données d'un protocole de transport sous-jacent sort du cadre de cette spécification.
Comme décrit à la Section 5.2, les protocoles de connexion particuliers à utiliser pour une interaction HTTP sont déterminés par la configuration du client et par l'URI cible. Par exemple, le schéma d'URI « http » (Section 2.7.1) indique une connexion par défaut de TCP sur IP, avec un port TCP par défaut de 80, mais le client peut être configuré pour utiliser un proxy via une autre connexion, un autre port ou un autre protocole.
Les implémentations HTTP sont censées pratiquer une gestion des connexions, laquelle comprend le maintien de l'état des connexions en cours, l'établissement d'une nouvelle connexion ou la réutilisation d'une connexion existante, le traitement des messages reçus sur une connexion, la détection des défaillances de connexion, et la fermeture de chaque connexion. La plupart des clients maintiennent plusieurs connexions en parallèle, y compris plus d'une connexion par point d'extrémité serveur. La plupart des serveurs sont conçus pour maintenir des milliers de connexions simultanées, tout en contrôlant les files d'attente de requêtes afin de permettre un usage équitable et de détecter les attaques par déni de service.
6.1. Connection
Le champ d'en-tête « Connection » permet à l'expéditeur d'indiquer les options de contrôle souhaitées pour la connexion en cours. Afin d'éviter de semer la confusion chez les destinataires en aval, un proxy ou une passerelle MUST supprimer ou remplacer toute option de connexion reçue avant de transférer le message.
Lorsqu'un champ d'en-tête autre que Connection est utilisé pour fournir des informations de contrôle pour la connexion en cours ou à son sujet, l'expéditeur MUST lister le field-name correspondant dans le champ d'en-tête Connection. Un proxy ou une passerelle MUST analyser un champ d'en-tête Connection reçu avant qu'un message ne soit transféré et, pour chaque connection-option de ce champ, supprimer du message tout ou partie des champs d'en-tête portant le même nom que la connection-option, puis supprimer le champ d'en-tête Connection lui-même (ou le remplacer par les propres options de connexion de l'intermédiaire pour le message transféré).
Ainsi, le champ d'en-tête Connection offre un moyen déclaratif de distinguer les champs d'en-tête qui ne sont destinés qu'au destinataire immédiat (« saut par saut ») de ceux qui sont destinés à tous les destinataires de la chaîne (« de bout en bout »), ce qui rend le message auto-descriptif et permet de déployer de futures extensions propres à une connexion sans craindre qu'elles ne soient transférées aveuglément par des intermédiaires plus anciens.
La valeur du champ d'en-tête Connection a la grammaire suivante :
Connection = 1#connection-option
connection-option = token
Les options de connexion sont insensibles à la casse.
Un expéditeur MUST NOT envoyer d'option de connexion correspondant à un champ d'en-tête destiné à tous les destinataires de la charge utile. Par exemple, Cache-Control n'est jamais approprié comme option de connexion (Section 5.2 de [RFC7234]).
Les options de connexion ne correspondent pas toujours à un champ d'en-tête présent dans le message, car un champ d'en-tête propre à une connexion peut ne pas être nécessaire s'il n'y a aucun paramètre associé à une option de connexion. En revanche, un champ d'en-tête propre à une connexion qui est reçu sans option de connexion correspondante indique généralement que le champ a été transféré de manière incorrecte par un intermédiaire et devrait être ignoré par le destinataire.
Lorsqu'ils définissent de nouvelles options de connexion, les auteurs de spécifications devraient recenser les noms de champs d'en-tête existants et s'assurer que la nouvelle option de connexion ne partage pas le même nom qu'un champ d'en-tête déjà déployé. Définir une nouvelle option de connexion revient essentiellement à réserver ce field-name potentiel pour transporter des informations supplémentaires liées à l'option de connexion, car il serait imprudent que les expéditeurs utilisent ce field-name à d'autres fins.
L'option de connexion « close » est définie pour qu'un expéditeur puisse signaler que cette connexion sera fermée après l'achèvement de la réponse. Par exemple,
Connection: close
dans les champs d'en-tête de la requête ou de la réponse indique que l'expéditeur va fermer la connexion après l'achèvement de l'échange requête/réponse en cours (Section 6.6).
Un client qui ne prend pas en charge les connexions persistantes MUST envoyer l'option de connexion « close » dans chaque message de requête.
Un serveur qui ne prend pas en charge les connexions persistantes MUST envoyer l'option de connexion « close » dans chaque message de réponse dont le code d'état n'est pas 1xx (Informational).
6.2. Établissement
Il sort du cadre de cette spécification de décrire comment les connexions sont établies via divers protocoles de couche transport ou de couche session. Chaque connexion ne s'applique qu'à une seule liaison de transport.
6.3. Persistance
HTTP/1.1 utilise par défaut des « connexions persistantes », ce qui permet de transporter plusieurs requêtes et réponses sur une seule connexion. L'option de connexion « close » sert à signaler qu'une connexion ne persistera pas après l'échange requête/réponse en cours. Les implémentations HTTP SHOULD prendre en charge les connexions persistantes.
Un destinataire détermine si une connexion est persistante ou non en se fondant sur la version de protocole du message le plus récemment reçu et sur son champ d'en-tête Connection (s'il existe) :
-
Si l'option de connexion « close » est présente, la connexion ne persistera pas après la réponse en cours ; sinon,
-
Si le protocole reçu est HTTP/1.1 (ou ultérieur), la connexion persistera après la réponse en cours ; sinon,
-
Si le protocole reçu est HTTP/1.0, que l'option de connexion « keep-alive » est présente, que le destinataire n'est pas un proxy, et que le destinataire souhaite honorer le mécanisme « keep-alive » de HTTP/1.0, la connexion persistera après la réponse en cours ; autrement,
-
La connexion sera fermée après la réponse en cours.
Un client MAY envoyer des requêtes supplémentaires sur une connexion persistante jusqu'à ce qu'il envoie ou reçoive une option de connexion « close », ou reçoive une réponse HTTP/1.0 sans option de connexion « keep-alive ».
Pour rester persistants, tous les messages d'une connexion doivent avoir une longueur de message définie par eux-mêmes (c'est-à-dire non définie par la fermeture de la connexion), comme décrit à la Section 3.3. Un serveur MUST lire le corps du message de requête en entier ou fermer la connexion après avoir envoyé sa réponse, car sinon les données restantes sur une connexion persistante seraient interprétées à tort comme la requête suivante. De même, un client MUST lire le corps du message de réponse en entier s'il a l'intention de réutiliser la même connexion pour une requête ultérieure.
Un serveur proxy MUST NOT maintenir de connexion persistante avec un client HTTP/1.0 (voir la Section 19.7.1 de [RFC2068] pour des informations et une discussion sur les problèmes posés par le champ d'en-tête Keep-Alive implémenté par de nombreux clients HTTP/1.0).
Voir l'Appendix A.1.2 pour plus d'informations sur la rétrocompatibilité avec les clients HTTP/1.0.
6.3.1. Nouvelle tentative de requêtes
Les connexions peuvent être fermées à tout moment, intentionnellement ou non. Les implémentations devraient anticiper la nécessité de se remettre d'événements de fermeture asynchrones.
Lorsqu'une connexion entrante est fermée prématurément, un client MAY ouvrir une nouvelle connexion et retransmettre automatiquement une séquence de requêtes interrompue si toutes ces requêtes ont des méthodes idempotentes (Section 4.2.2 de [RFC7231]). Un proxy MUST NOT réessayer automatiquement des requêtes non idempotentes.
Un agent utilisateur MUST NOT réessayer automatiquement une requête dont la méthode n'est pas idempotente, sauf s'il dispose d'un moyen de savoir que la sémantique de la requête est effectivement idempotente, quelle que soit la méthode, ou d'un moyen de détecter que la requête d'origine n'a jamais été appliquée. Par exemple, un agent utilisateur qui sait (de par sa conception ou sa configuration) qu'une requête POST vers une ressource donnée est sûre peut répéter cette requête automatiquement. De même, un agent utilisateur conçu spécifiquement pour opérer sur un dépôt de contrôle de version pourrait être capable de se remettre de conditions de défaillance partielles en vérifiant la ou les révisions de la ressource cible après l'échec d'une connexion, en revenant sur ou en corrigeant toute modification partiellement appliquée, puis en réessayant automatiquement les requêtes qui ont échoué.
Un client SHOULD NOT réessayer automatiquement une tentative de nouvelle tentative automatique qui a échoué.
6.3.2. Mise en pipeline
Un client qui prend en charge les connexions persistantes MAY « mettre en pipeline » ses requêtes (c'est-à-dire envoyer plusieurs requêtes sans attendre chaque réponse). Un serveur MAY traiter en parallèle une séquence de requêtes mises en pipeline si elles ont toutes des méthodes sûres (Section 4.2.1 de [RFC7231]), mais il MUST envoyer les réponses correspondantes dans le même ordre que celui dans lequel les requêtes ont été reçues.
Un client qui met des requêtes en pipeline SHOULD réessayer les requêtes restées sans réponse si la connexion se ferme avant qu'il ne reçoive toutes les réponses correspondantes. Lorsqu'il réessaie des requêtes mises en pipeline après l'échec d'une connexion (connexion non explicitement fermée par le serveur dans sa dernière réponse complète), un client MUST NOT mettre immédiatement en pipeline après l'établissement de la connexion, car la première requête restante du pipeline précédent a pu provoquer une réponse d'erreur qui peut être perdue à nouveau si plusieurs requêtes sont envoyées sur une connexion fermée prématurément (voir le problème de réinitialisation TCP décrit à la Section 6.6).
Les méthodes idempotentes (Section 4.2.2 de [RFC7231]) sont importantes pour la mise en pipeline, car elles peuvent être réessayées automatiquement après l'échec d'une connexion. Un agent utilisateur SHOULD NOT mettre des requêtes en pipeline après une méthode non idempotente, avant que le code d'état de la réponse finale à cette méthode n'ait été reçu, sauf si l'agent utilisateur dispose d'un moyen de détecter et de corriger les conditions de défaillance partielles concernant la séquence mise en pipeline.
Un intermédiaire qui reçoit des requêtes mises en pipeline MAY mettre ces requêtes en pipeline lorsqu'il les transfère en entrant, puisqu'il peut compter sur le ou les agents utilisateurs sortants pour déterminer quelles requêtes peuvent être mises en pipeline sans danger. Si la connexion entrante échoue avant la réception d'une réponse, l'intermédiaire de mise en pipeline MAY tenter de réessayer une séquence de requêtes qui n'ont pas encore reçu de réponse si toutes ces requêtes ont des méthodes idempotentes ; sinon, l'intermédiaire de mise en pipeline SHOULD transférer les réponses reçues puis fermer la ou les connexions sortantes correspondantes, afin que le ou les agents utilisateurs sortants puissent se rétablir en conséquence.
6.4. Concurrence
Un client devrait limiter le nombre de connexions simultanées ouvertes qu'il maintient vers un serveur donné.
Les révisions précédentes de HTTP fixaient un nombre précis de connexions comme plafond, mais cela s'est révélé peu pratique pour de nombreuses applications. Par conséquent, cette spécification n'impose pas de nombre maximal particulier de connexions, mais encourage plutôt les clients à être prudents lorsqu'ils ouvrent plusieurs connexions.
Plusieurs connexions sont typiquement utilisées pour éviter le problème de « blocage en tête de file » (head-of-line blocking), dans lequel une requête qui exige un traitement notable côté serveur et/ou qui porte une charge utile volumineuse bloque les requêtes suivantes sur la même connexion. Cependant, chaque connexion consomme des ressources serveur. En outre, l'utilisation de plusieurs connexions peut provoquer des effets secondaires indésirables sur des réseaux encombrés.
Notez qu'un serveur peut rejeter un trafic qu'il juge abusif ou caractéristique d'une attaque par déni de service, tel qu'un nombre excessif de connexions ouvertes provenant d'un même client.
6.5. Défaillances et délais d'attente
Les serveurs disposent généralement d'une valeur de délai d'attente au-delà de laquelle ils ne maintiendront plus une connexion inactive. Les serveurs proxy peuvent retenir une valeur plus élevée, puisqu'il est probable que le client établisse davantage de connexions à travers le même serveur proxy. L'utilisation de connexions persistantes n'impose aucune exigence sur la durée (ni sur l'existence) de ce délai d'attente, ni au client ni au serveur.
Un client ou un serveur qui souhaite déclencher un délai d'attente SHOULD effectuer une fermeture en douceur de la connexion. Les implémentations SHOULD surveiller en permanence les connexions ouvertes à la recherche d'un signal de fermeture reçu et y répondre de manière appropriée, car la fermeture rapide des deux côtés d'une connexion permet de récupérer les ressources système allouées.
Un client, un serveur ou un proxy MAY fermer la connexion de transport à tout moment. Par exemple, un client peut avoir commencé à envoyer une nouvelle requête au moment même où le serveur a décidé de fermer la connexion « inactive ». Du point de vue du serveur, la connexion est fermée alors qu'elle était inactive, mais du point de vue du client, une requête est en cours.
Un serveur SHOULD maintenir les connexions persistantes lorsque cela est possible et laisser les mécanismes de contrôle de flux du transport sous-jacent résoudre les surcharges temporaires, plutôt que de mettre fin aux connexions en comptant sur le fait que les clients réessaieront. Cette dernière technique peut aggraver la congestion du réseau.
Un client qui envoie un corps de message SHOULD surveiller la connexion réseau à la recherche d'une réponse d'erreur pendant qu'il transmet la requête. Si le client voit une réponse indiquant que le serveur ne souhaite pas recevoir le corps du message et qu'il ferme la connexion, le client SHOULD cesser immédiatement de transmettre le corps et fermer son côté de la connexion.
6.6. Fermeture
Le champ d'en-tête Connection (Section 6.1) fournit une option de connexion « close » qu'un expéditeur SHOULD envoyer lorsqu'il souhaite fermer la connexion après la paire requête/réponse en cours.
Un client qui envoie une option de connexion « close » MUST NOT envoyer de requêtes supplémentaires sur cette connexion (après celle qui contient « close ») et MUST fermer la connexion après avoir lu le message de réponse final correspondant à cette requête.
Un serveur qui reçoit une option de connexion « close » MUST déclencher une fermeture de la connexion (voir ci-dessous) après avoir envoyé la réponse finale à la requête qui contenait « close ». Le serveur SHOULD envoyer une option de connexion « close » dans sa réponse finale sur cette connexion. Le serveur MUST NOT traiter de requêtes supplémentaires reçues sur cette connexion.
Un serveur qui envoie une option de connexion « close » MUST déclencher une fermeture de la connexion (voir ci-dessous) après avoir envoyé la réponse contenant « close ». Le serveur MUST NOT traiter de requêtes supplémentaires reçues sur cette connexion.
Un client qui reçoit une option de connexion « close » MUST cesser d'envoyer des requêtes sur cette connexion et fermer la connexion après avoir lu le message de réponse contenant « close » ; si des requêtes mises en pipeline supplémentaires avaient été envoyées sur la connexion, le client SHOULD NOT supposer qu'elles seront traitées par le serveur.
Si un serveur effectue une fermeture immédiate d'une connexion TCP, il existe un risque important que le client ne puisse pas lire la dernière réponse HTTP. Si le serveur reçoit des données supplémentaires du client sur une connexion entièrement fermée, telles qu'une autre requête envoyée par le client avant qu'il ne reçoive la réponse du serveur, la pile TCP du serveur enverra un paquet de réinitialisation au client ; malheureusement, ce paquet de réinitialisation peut effacer les tampons d'entrée non acquittés du client avant qu'ils ne puissent être lus et interprétés par l'analyseur HTTP du client.
Pour éviter le problème de réinitialisation TCP, les serveurs ferment typiquement une connexion par étapes. D'abord, le serveur effectue une demi-fermeture en ne fermant que le côté écriture de la connexion en lecture/écriture. Le serveur continue ensuite à lire depuis la connexion jusqu'à ce qu'il reçoive une fermeture correspondante de la part du client, ou jusqu'à ce que le serveur soit raisonnablement certain que sa propre pile TCP a reçu l'acquittement du client pour le ou les paquets contenant la dernière réponse du serveur. Enfin, le serveur ferme complètement la connexion.
On ne sait pas si le problème de réinitialisation est propre à TCP ou s'il peut également se rencontrer dans d'autres protocoles de connexion de transport.
6.7. Upgrade
Le champ d'en-tête « Upgrade » vise à fournir un mécanisme simple pour passer de HTTP/1.1 à un autre protocole sur la même connexion. Un client MAY envoyer une liste de protocoles dans le champ d'en-tête Upgrade d'une requête pour inviter le serveur à basculer vers un ou plusieurs de ces protocoles, par ordre de préférence décroissant, avant d'envoyer la réponse finale. Un serveur MAY ignorer un champ d'en-tête Upgrade reçu s'il souhaite continuer à utiliser le protocole en cours sur cette connexion. Upgrade ne peut pas être utilisé pour exiger un changement de protocole.
Upgrade = 1#protocol
protocol = protocol-name ["/" protocol-version]
protocol-name = token
protocol-version = token
Un serveur qui envoie une réponse 101 (Switching Protocols) MUST envoyer un champ d'en-tête Upgrade pour indiquer le ou les nouveaux protocoles vers lesquels la connexion est basculée ; si plusieurs couches de protocole sont basculées, l'expéditeur MUST lister les protocoles par ordre croissant de couche. Un serveur MUST NOT basculer vers un protocole qui n'a pas été indiqué par le client dans le champ d'en-tête Upgrade de la requête correspondante. Un serveur MAY choisir d'ignorer l'ordre de préférence indiqué par le client et de sélectionner le ou les nouveaux protocoles en fonction d'autres facteurs, tels que la nature de la requête ou la charge actuelle du serveur.
Un serveur qui envoie une réponse 426 (Upgrade Required) MUST envoyer un champ d'en-tête Upgrade pour indiquer les protocoles acceptables, par ordre de préférence décroissant.
Un serveur MAY envoyer un champ d'en-tête Upgrade dans toute autre réponse pour annoncer qu'il implémente la prise en charge du basculement vers les protocoles listés, par ordre de préférence décroissant, lorsque cela est approprié pour une requête future.
Voici un exemple hypothétique envoyé par un client :
GET /hello.txt HTTP/1.1
Host: www.example.com
Connection: upgrade
Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
Les capacités et la nature de la communication de niveau applicatif après le changement de protocole dépendent entièrement du ou des nouveaux protocoles choisis. Cependant, immédiatement après avoir envoyé la réponse 101 (Switching Protocols), le serveur est censé continuer à répondre à la requête d'origine comme s'il avait reçu son équivalent au sein du nouveau protocole (c'est-à-dire que le serveur a encore une requête en cours à satisfaire après le changement de protocole, et est censé le faire sans exiger que la requête soit répétée).
Par exemple, si le champ d'en-tête Upgrade est reçu dans une requête GET et que le serveur décide de changer de protocole, il répond d'abord par un message 101 (Switching Protocols) en HTTP/1.1, puis enchaîne immédiatement avec l'équivalent, dans le nouveau protocole, d'une réponse à un GET sur la ressource cible. Cela permet de faire passer une connexion à des protocoles ayant la même sémantique que HTTP sans le coût de latence d'un aller-retour supplémentaire. Un serveur MUST NOT changer de protocole, sauf si la sémantique du message reçu peut être honorée par le nouveau protocole ; une requête OPTIONS peut être honorée par n'importe quel protocole.
Voici un exemple de réponse à la requête hypothétique ci-dessus :
HTTP/1.1 101 Switching Protocols
Connection: upgrade
Upgrade: HTTP/2.0
[... data stream switches to HTTP/2.0 with an appropriate response
(as defined by new protocol) to the "GET /hello.txt" request ...]
Lorsque Upgrade est envoyé, l'expéditeur MUST également envoyer un champ d'en-tête Connection (Section 6.1) contenant une option de connexion « upgrade », afin d'empêcher Upgrade d'être transféré accidentellement par des intermédiaires qui ne mettent peut-être pas en œuvre les protocoles listés. Un serveur MUST ignorer un champ d'en-tête Upgrade reçu dans une requête HTTP/1.0.
Un client ne peut pas commencer à utiliser un protocole mis à niveau sur la connexion avant d'avoir complètement envoyé le message de requête (c'est-à-dire que le client ne peut pas changer de protocole en cours d'envoi d'un message). Si un serveur reçoit à la fois un champ d'en-tête Upgrade et un champ d'en-tête Expect ayant l'attente « 100-continue » (Section 5.1.1 de [RFC7231]), le serveur MUST envoyer une réponse 100 (Continue) avant d'envoyer une réponse 101 (Switching Protocols).
Le champ d'en-tête Upgrade ne s'applique qu'au basculement de protocoles par-dessus la connexion existante ; il ne peut pas servir à changer le protocole de connexion (transport) sous-jacent, ni à transférer la communication existante vers une autre connexion. Pour ces usages, il est plus approprié d'utiliser une réponse 3xx (Redirection) (Section 6.4 de [RFC7231]).
Cette spécification ne définit que le nom de protocole « HTTP » pour l'usage de la famille des protocoles de transfert hypertexte, telle que définie par les règles de version de HTTP de la Section 2.6 et par les futures mises à jour de cette spécification. Des jetons supplémentaires devraient être enregistrés auprès de l'IANA en utilisant la procédure d'enregistrement définie à la Section 8.6.