Aller au contenu principal

5. Message Routing

Le routage des messages de requête HTTP est déterminé par chaque client en fonction de la ressource cible, de la configuration de proxy du client, et de l'établissement ou de la réutilisation d'une connexion entrante. Le routage des réponses correspondantes suit la même chaîne de connexions en sens inverse jusqu'au client.

5.1. Identification d'une ressource cible​

HTTP est utilisé dans une grande variété d'applications, allant d'ordinateurs généralistes à des appareils électroménagers. Dans certains cas, les options de communication sont codées en dur dans la configuration d'un client. Cependant, la plupart des clients HTTP s'appuient sur le même mécanisme d'identification de ressource et les mêmes techniques de configuration que les navigateurs Web généralistes.

Une communication HTTP est initiée par un agent utilisateur dans un certain but. Ce but est une combinaison de la sémantique de la requête, qui est définie dans [RFC7231], et d'une ressource cible à laquelle appliquer cette sémantique. Une référence d'URI (Section 2.7) est typiquement utilisée comme identifiant de la « ressource cible », que l'agent utilisateur résoudrait sous sa forme absolue afin d'obtenir l'« URI cible ». L'URI cible exclut le composant fragment de la référence, s'il existe, puisque les identifiants de fragment sont réservés au traitement côté client ([RFC3986], Section 3.5).

5.2. Connexion entrante​

Une fois l'URI cible déterminé, un client doit décider si une requête réseau est nécessaire pour accomplir la sémantique souhaitée et, le cas échéant, où cette requête doit être dirigée.

Si le client dispose d'un cache [RFC7234] et que la requête peut être satisfaite par celui-ci, alors la requête y est généralement dirigée en premier.

Si la requête n'est pas satisfaite par un cache, un client typique vérifie alors sa configuration pour déterminer si un proxy doit être utilisé pour satisfaire la requête. La configuration de proxy dépend de l'implémentation, mais elle repose souvent sur la correspondance de préfixe d'URI, la correspondance sélective d'autorité, ou les deux, et le proxy lui-même est généralement identifié par un URI « http » ou « https ». Si un proxy est applicable, le client se connecte en entrant en établissant (ou en réutilisant) une connexion vers ce proxy.

Si aucun proxy n'est applicable, un client typique invoque une routine de traitement, généralement propre au schéma de l'URI cible, pour se connecter directement à une autorité pour la ressource cible. La manière dont cela est accompli dépend du schéma de l'URI cible et est définie par la spécification qui lui est associée, de la même façon que cette spécification définit l'accès au serveur d'origine pour la résolution des schémas « http » (Section 2.7.1) et « https » (Section 2.7.2).

Les exigences HTTP relatives à la gestion des connexions sont définies à la Section 6.

5.3. Cible de requête​

Une fois une connexion entrante obtenue, le client envoie un message de requête HTTP (Section 3) avec une cible de requête dérivée de l'URI cible. Il existe quatre formats distincts de cible de requête, selon la méthode demandée et selon que la requête vise ou non un proxy.

request-target = origin-form
/ absolute-form
/ authority-form
/ asterisk-form

5.3.1. origin-form​

La forme la plus courante de cible de requête est origin-form.

origin-form    = absolute-path [ "?" query ]

Lorsqu'il adresse une requête directement à un serveur d'origine, autre qu'une requête CONNECT ou une requête OPTIONS à l'échelle du serveur (comme détaillé ci-dessous), un client MUST envoyer uniquement les composants de chemin absolu et de requête de l'URI cible comme cible de requête. Si le composant de chemin de l'URI cible est vide, le client MUST envoyer « / » comme chemin au sein de la forme origin-form de la cible de requête. Un champ d'en-tête Host est également envoyé, comme défini à la Section 5.4.

Par exemple, un client souhaitant récupérer une représentation de la ressource identifiée comme

http://www.example.org/where?q=now

directement depuis le serveur d'origine ouvrirait (ou réutiliserait) une connexion TCP vers le port 80 de l'hôte « www.example.org » et enverrait les lignes :

GET /where?q=now HTTP/1.1
Host: www.example.org

suivies du reste du message de requête.

5.3.2. absolute-form​

Lorsqu'il adresse une requête à un proxy, autre qu'une requête CONNECT ou une requête OPTIONS à l'échelle du serveur (comme détaillé ci-dessous), un client MUST envoyer l'URI cible sous la forme absolute-form comme cible de requête.

absolute-form  = absolute-URI

Il est demandé au proxy soit de servir cette requête depuis un cache valide, si possible, soit d'effectuer la même requête pour le compte du client auprès du prochain serveur proxy entrant ou directement auprès du serveur d'origine indiqué par la cible de requête. Les exigences relatives à ce « transfert » des messages sont définies à la Section 5.7.

Un exemple de ligne de requête de forme absolute-form serait :

GET http://www.example.org/pub/WWW/TheProject.html HTTP/1.1

Pour permettre la transition vers la forme absolute-form pour toutes les requêtes dans une éventuelle version future de HTTP, un serveur MUST accepter la forme absolute-form dans les requêtes, même si les clients HTTP/1.1 ne l'envoient que dans les requêtes adressées à des proxies.

5.3.3. authority-form​

La forme authority-form de cible de requête n'est utilisée que pour les requêtes CONNECT (Section 4.3.6 de [RFC7231]).

authority-form = authority

Lorsqu'il effectue une requête CONNECT pour établir un tunnel à travers un ou plusieurs proxies, un client MUST envoyer uniquement le composant authority de l'URI cible (à l'exclusion de tout userinfo et de son délimiteur « @ ») comme cible de requête. Par exemple,

CONNECT www.example.com:80 HTTP/1.1

5.3.4. asterisk-form​

La forme asterisk-form de cible de requête n'est utilisée que pour une requête OPTIONS à l'échelle du serveur (Section 4.3.7 de [RFC7231]).

asterisk-form  = "*"

Lorsqu'un client souhaite demander OPTIONS pour le serveur dans son ensemble, par opposition à une ressource nommée particulière de ce serveur, le client MUST envoyer uniquement « * » (%x2A) comme cible de requête. Par exemple,

OPTIONS * HTTP/1.1

Si un proxy reçoit une requête OPTIONS dont la cible de requête est sous forme absolute-form et dont l'URI a un chemin vide et aucun composant de requête, alors le dernier proxy de la chaîne de requêtes MUST envoyer une cible de requête « * » lorsqu'il transfère la requête au serveur d'origine indiqué.

Par exemple, la requête

OPTIONS http://www.example.org:8001 HTTP/1.1

serait transférée par le proxy final comme

OPTIONS * HTTP/1.1
Host: www.example.org:8001

après s'être connecté au port 8001 de l'hôte « www.example.org ».

5.4. Host​

Le champ d'en-tête « Host » d'une requête fournit les informations d'hôte et de port issues de l'URI cible, ce qui permet au serveur d'origine de distinguer les ressources lorsqu'il sert des requêtes pour plusieurs noms d'hôte sur une même adresse IP.

Host = uri-host [ ":" port ] ; Section 2.7.1

Un client MUST envoyer un champ d'en-tête Host dans tous les messages de requête HTTP/1.1. Si l'URI cible comprend un composant authority, alors un client MUST envoyer pour Host une field-value identique à ce composant authority, à l'exclusion de tout sous-composant userinfo et de son délimiteur « @ » (Section 2.7.1). Si le composant authority est absent ou indéfini pour l'URI cible, alors un client MUST envoyer un champ d'en-tête Host avec une field-value vide.

Comme la field-value de Host est une information critique pour le traitement d'une requête, un agent utilisateur SHOULD générer Host comme premier champ d'en-tête après la ligne de requête.

Par exemple, une requête GET adressée au serveur d'origine de http://www.example.org/pub/WWW/ commencerait par :

GET /pub/WWW/ HTTP/1.1
Host: www.example.org

Un client MUST envoyer un champ d'en-tête Host dans une requête HTTP/1.1 même si la cible de requête est sous forme absolute-form, car cela permet aux informations de Host d'être transférées à travers d'anciens proxies HTTP/1.0 qui pourraient ne pas avoir implémenté Host.

Lorsqu'un proxy reçoit une requête dont la cible de requête est sous forme absolute-form, le proxy MUST ignorer le champ d'en-tête Host reçu (s'il existe) et le remplacer à la place par les informations d'hôte de la cible de requête. Un proxy qui transfère une telle requête MUST générer une nouvelle field-value de Host fondée sur la cible de requête reçue, plutôt que de transférer la field-value de Host reçue.

Comme le champ d'en-tête Host agit comme un mécanisme de routage au niveau applicatif, il est une cible fréquente pour des logiciels malveillants cherchant à empoisonner un cache partagé ou à rediriger une requête vers un serveur non voulu. Un proxy d'interception est particulièrement vulnérable s'il s'appuie sur la field-value de Host pour rediriger des requêtes vers des serveurs internes, ou pour l'utiliser comme clé de cache dans un cache partagé, sans vérifier d'abord que la connexion interceptée vise bien une adresse IP valide pour cet hôte.

Un serveur MUST répondre par un code d'état 400 (Bad Request) à tout message de requête HTTP/1.1 qui ne comporte pas de champ d'en-tête Host, ainsi qu'à toute requête qui contient plus d'un champ d'en-tête Host ou un champ d'en-tête Host dont la field-value est invalide.

5.5. URI de requête effective​

Comme la cible de requête ne contient souvent qu'une partie de l'URI cible de l'agent utilisateur, un serveur reconstitue la cible voulue sous la forme d'un « URI de requête effective » afin de servir correctement la requête. Cette reconstitution fait intervenir à la fois la configuration locale du serveur et les informations communiquées dans la cible de requête, le champ d'en-tête Host et le contexte de la connexion.

Pour un agent utilisateur, l'URI de requête effective est l'URI cible.

Si la cible de requête est sous forme absolute-form, l'URI de requête effective est identique à la cible de requête. Sinon, l'URI de requête effective est construite comme suit :

Si la configuration du serveur (ou la passerelle sortante) fournit un schéma d'URI fixe, ce schéma est utilisé pour l'URI de requête effective. Sinon, si la requête est reçue sur une connexion TCP sécurisée par TLS, le schéma de l'URI de requête effective est « https » ; sinon, le schéma est « http ».

Si la configuration du serveur (ou la passerelle sortante) fournit un composant authority d'URI fixe, cette autorité est utilisée pour l'URI de requête effective. Sinon, si la cible de requête est sous forme authority-form, le composant authority de l'URI de requête effective est identique à la cible de requête. Sinon, si un champ d'en-tête Host non vide est fourni, le composant authority est identique à la field-value de Host. Autrement, le composant authority se voit attribuer le nom par défaut configuré pour le serveur et, si le numéro de port TCP entrant de la connexion diffère du port par défaut pour le schéma de l'URI de requête effective, alors un deux-points (« : ») et le numéro de port entrant (sous forme décimale) sont ajoutés au composant authority.

Si la cible de requête est sous forme authority-form ou asterisk-form, le composant combiné chemin et requête de l'URI de requête effective est vide. Sinon, le composant combiné chemin et requête est identique à la cible de requête.

Les composants de l'URI de requête effective, une fois déterminés comme ci-dessus, peuvent être combinés sous forme absolute-URI en concaténant le schéma, « :// », l'autorité, et le composant combiné chemin et requête.

Exemple 1 : le message suivant, reçu sur une connexion TCP non sécurisée

GET /pub/WWW/TheProject.html HTTP/1.1
Host: www.example.org:8080

a pour URI de requête effective

http://www.example.org:8080/pub/WWW/TheProject.html

Exemple 2 : le message suivant, reçu sur une connexion TCP sécurisée par TLS

OPTIONS * HTTP/1.1
Host: www.example.org

a pour URI de requête effective

https://www.example.org

Les destinataires d'une requête HTTP/1.0 qui ne comporte pas de champ d'en-tête Host peuvent avoir besoin d'utiliser des heuristiques (par exemple l'examen du chemin de l'URI à la recherche d'un élément propre à un hôte particulier) afin de deviner le composant authority de l'URI de requête effective.

Une fois l'URI de requête effective construite, un serveur d'origine doit décider s'il faut ou non fournir un service pour cet URI via la connexion sur laquelle la requête a été reçue. Par exemple, la requête peut avoir été mal dirigée, délibérément ou accidentellement, de sorte que les informations d'une cible de requête ou d'un champ d'en-tête Host reçus diffèrent de l'hôte ou du port sur lequel la connexion a été établie. Si la connexion provient d'une passerelle de confiance, cette incohérence peut être attendue ; sinon, elle peut indiquer une tentative de contourner des filtres de sécurité, de tromper le serveur pour qu'il fournisse un contenu non public, ou d'empoisonner un cache. Voir la Section 9 pour les considérations de sécurité relatives au routage des messages.

5.6. Association d'une réponse à une requête​

HTTP ne comprend aucun identifiant de requête permettant d'associer un message de requête donné au ou aux messages de réponse correspondants. Il s'appuie donc sur le fait que l'ordre d'arrivée des réponses corresponde exactement à l'ordre dans lequel les requêtes sont émises sur la même connexion. Plus d'un message de réponse par requête ne se produit que si une ou plusieurs réponses informatives (1xx, voir la Section 6.2 de [RFC7231]) précèdent une réponse finale à la même requête.

Un client qui a plus d'une requête en cours sur une connexion MUST tenir une liste des requêtes en cours dans l'ordre d'envoi et MUST associer chaque message de réponse reçu sur cette connexion à la requête d'ordre le plus élevé qui n'a pas encore reçu de réponse finale (non 1xx).

5.7. Transfert des messages​

Comme décrit à la Section 2.3, les intermédiaires peuvent jouer divers rôles dans le traitement des requêtes et des réponses HTTP. Certains intermédiaires servent à améliorer les performances ou la disponibilité. D'autres servent au contrôle d'accès ou au filtrage de contenu. Comme un flux HTTP présente des caractéristiques semblables à une architecture de type tuyau et filtre, il n'existe aucune limite intrinsèque à la mesure dans laquelle un intermédiaire peut enrichir (ou perturber) l'un ou l'autre sens du flux.

Un intermédiaire qui n'agit pas comme tunnel MUST implémenter le champ d'en-tête Connection, comme spécifié à la Section 6.1, et exclure du transfert les champs qui ne sont destinés qu'à la connexion entrante.

Un intermédiaire MUST NOT transférer un message à lui-même, sauf s'il est protégé contre une boucle de requêtes infinie. En général, un intermédiaire devrait reconnaître ses propres noms de serveur, y compris tout alias, toute variation locale ou toute adresse IP littérale, et répondre directement à ces requêtes.

5.7.1. Via​

Le champ d'en-tête « Via » indique la présence de protocoles et de destinataires intermédiaires entre l'agent utilisateur et le serveur (pour les requêtes) ou entre le serveur d'origine et le client (pour les réponses), de façon semblable au champ d'en-tête « Received » du courrier électronique (Section 3.6.7 de [RFC5322]). Via peut servir à suivre les transferts de messages, à éviter les boucles de requêtes, et à identifier les capacités protocolaires des expéditeurs le long de la chaîne requête/réponse.

Via = 1#( received-protocol RWS received-by [ RWS comment ] )

received-protocol = [ protocol-name "/" ] protocol-version
; see Section 6.7
received-by = ( uri-host [ ":" port ] ) / pseudonym
pseudonym = token

Plusieurs valeurs de champ Via représentent chacun des proxies ou des passerelles qui ont transféré le message. Chaque intermédiaire ajoute ses propres informations sur la manière dont le message a été reçu, de sorte que le résultat final est ordonné selon la séquence des destinataires du transfert.

Un proxy MUST envoyer un champ d'en-tête Via approprié, comme décrit ci-dessous, dans chaque message qu'il transfère. Une passerelle HTTP vers HTTP MUST envoyer un champ d'en-tête Via approprié dans chaque message de requête entrant et MAY envoyer un champ d'en-tête Via dans les messages de réponse transférés.

Pour chaque intermédiaire, le received-protocol indique le protocole et la version de protocole utilisés par l'expéditeur amont du message. La valeur de champ Via consigne donc les capacités protocolaires annoncées de la chaîne requête/réponse, de sorte qu'elles restent visibles pour les destinataires en aval ; cela peut être utile pour déterminer quelles fonctionnalités non rétrocompatibles peuvent être utilisées sans danger en réponse, ou dans une requête ultérieure, comme décrit à la Section 2.6. Par souci de concision, le protocol-name est omis lorsque le protocole reçu est HTTP.

La partie received-by de la valeur de champ est normalement l'hôte et le numéro de port facultatif d'un serveur ou d'un client destinataire qui a ensuite transféré le message. Cependant, si l'hôte réel est considéré comme une information sensible, un expéditeur MAY le remplacer par un pseudonyme. Si aucun port n'est fourni, un destinataire MAY l'interpréter comme signifiant que le message a été reçu sur le port TCP par défaut, s'il en existe un, pour le received-protocol.

Un expéditeur MAY générer des commentaires dans le champ d'en-tête Via pour identifier le logiciel de chaque destinataire, par analogie avec les champs d'en-tête User-Agent et Server. Cependant, tous les commentaires du champ Via sont facultatifs, et un destinataire MAY les supprimer avant de transférer le message.

Par exemple, un message de requête pourrait être envoyé depuis un agent utilisateur HTTP/1.0 vers un proxy interne nommé « fred », qui utilise HTTP/1.1 pour transférer la requête vers un proxy public situé à p.example.net, lequel achève la requête en la transférant au serveur d'origine situé à www.example.com. La requête reçue par www.example.com comporterait alors le champ d'en-tête Via suivant :

Via: 1.0 fred, 1.1 p.example.net

Un intermédiaire utilisé comme portail à travers un pare-feu réseau SHOULD NOT transférer les noms et les ports des hôtes situés à l'intérieur de la zone du pare-feu, sauf s'il est explicitement activé pour le faire. S'il n'est pas activé, un tel intermédiaire SHOULD remplacer chaque hôte received-by d'un hôte situé derrière le pare-feu par un pseudonyme approprié pour cet hôte.

Un intermédiaire MAY combiner une sous-séquence ordonnée d'entrées de champ d'en-tête Via en une seule entrée de ce type si les entrées ont des valeurs received-protocol identiques. Par exemple,

Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy

pourrait être réduit à

Via: 1.0 ricky, 1.1 mertz, 1.0 lucy

Un expéditeur SHOULD NOT combiner plusieurs entrées, sauf si elles relèvent toutes du même contrôle organisationnel et si les hôtes ont déjà été remplacés par des pseudonymes. Un expéditeur MUST NOT combiner des entrées qui ont des valeurs received-protocol différentes.

5.7.2. Transformations​

Certains intermédiaires comportent des fonctionnalités de transformation des messages et de leurs charges utiles. Un proxy peut, par exemple, convertir entre formats d'image afin d'économiser de l'espace de cache ou de réduire le volume de trafic sur une liaison lente. Cependant, des problèmes opérationnels peuvent survenir lorsque ces transformations sont appliquées à des charges utiles destinées à des applications critiques, telles que l'imagerie médicale ou l'analyse de données scientifiques, en particulier lorsque des contrôles d'intégrité ou des signatures numériques sont utilisés pour garantir que la charge utile reçue est identique à l'originale.

Un proxy HTTP vers HTTP est appelé « proxy transformateur » s'il est conçu ou configuré pour modifier les messages d'une manière sémantiquement significative (c'est-à-dire des modifications, au-delà de celles requises par le traitement HTTP normal, qui changent le message d'une manière qui serait significative pour l'expéditeur d'origine ou potentiellement significative pour les destinataires en aval). Par exemple, un proxy transformateur peut agir comme serveur d'annotation partagé (en modifiant les réponses pour y inclure des références à une base de données d'annotations locale), comme filtre de logiciels malveillants, comme transcodeur de format, ou comme filtre de confidentialité. Ces transformations sont présumées souhaitées par le client (ou l'organisation cliente) qui a sélectionné le proxy.

Si un proxy reçoit une cible de requête dont le nom d'hôte n'est pas un nom de domaine pleinement qualifié, il MAY ajouter son propre domaine au nom d'hôte qu'il a reçu lors du transfert de la requête. Un proxy MUST NOT changer le nom d'hôte si la cible de requête contient un nom de domaine pleinement qualifié.

Un proxy MUST NOT modifier les parties « absolute-path » et « query » de la cible de requête reçue lorsqu'il la transfère au prochain serveur entrant, sauf dans le cas indiqué ci-dessus consistant à remplacer un chemin vide par « / » ou « * ».

Un proxy MAY modifier le corps du message en appliquant ou en supprimant un codage de transfert (Section 4).

Un proxy MUST NOT transformer la charge utile (Section 3.3 de [RFC7231]) d'un message qui contient une directive de contrôle de cache no-transform (Section 5.2 de [RFC7234]).

Un proxy MAY transformer la charge utile d'un message qui ne contient pas de directive de contrôle de cache no-transform. Un proxy qui transforme une charge utile MUST ajouter un champ d'en-tête Warning avec le code d'avertissement 214 (« Transformation Applied ») s'il n'y en a pas déjà un dans le message (voir la Section 5.5 de [RFC7234]). Un proxy qui transforme la charge utile d'une réponse 200 (OK) peut informer davantage les destinataires en aval qu'une transformation a été appliquée en changeant le code d'état de la réponse en 203 (Non-Authoritative Information) (Section 6.3.4 de [RFC7231]).

Un proxy SHOULD NOT modifier les champs d'en-tête qui fournissent des informations sur les extrémités de la chaîne de communication, sur l'état de la ressource, ou sur la représentation sélectionnée (autre que la charge utile), sauf si la définition du champ autorise spécifiquement une telle modification ou si la modification est jugée nécessaire pour la confidentialité ou la sécurité.