Aller au contenu principal

3. Tunnelisation UDP sur HTTP

  1. Tunnelisation UDP sur HTTP

Pour permettre la négociation d'un tunnel pour UDP sur HTTP, ce document définit le jeton de mise à niveau HTTP "connect-udp". Les tunnels UDP résultants utilisent le protocole Capsule (voir la section 3.2 de [HTTP-DGRAM]) avec des datagrammes HTTP au format défini à la section 5.

Pour initier un tunnel UDP associé à un seul flux HTTP, un client émet une requête contenant le jeton de mise à niveau "connect-udp". La cible du tunnel est indiquée par le client au proxy UDP via les variables "target_host" et "target_port" du modèle d'URI ; voir la section 2.

"target_host" prend en charge l'utilisation de noms DNS, de littéraux IPv6 et de littéraux IPv4. Notez que les identifiants de zone d'adressage de portée IPv6 ne sont pas pris en charge. En utilisant les termes IPv6address, IPv4address, reg-name et port de [URI], les variables "target_host" et "target_port" DOIVENT adhérer au format de la figure 2, en utilisant la notation de [ABNF]. De plus :

  • les variables "target_host" et "target_port" NE DOIVENT PAS être vides.

  • si "target_host" contient un littéral IPv6, les deux-points (":") DOIVENT être encodés en pourcentage. Par exemple, si l'hôte cible est "2001:db8::42", il sera encodé dans l'URI comme "2001%3Adb8%3A%3A42".

  • "target_port" DOIT représenter un entier compris entre 1 et 65535 inclus.

target_host = IPv6address / IPv4address / reg-name target_port = port

               Figure 2 : Format de variable de modèle d'URI

Lors de l'envoi de sa requête de proxy UDP, le client DOIT effectuer une expansion de modèle d'URI pour déterminer le chemin et la requête de sa demande.

Si la demande aboutit, le proxy UDP s'engage à convertir les datagrammes HTTP reçus en paquets UDP, et vice versa, jusqu'à la fermeture du tunnel.

En vertu de la définition du protocole Capsule (voir la section 3.2 de [HTTP-DGRAM]), les requêtes de proxy UDP ne transportent aucun contenu de message. De même, les réponses de proxy UDP réussies ne transportent également aucun contenu de message.

3.1. Traitement du proxy UDP

Lors de la réception d'une requête de proxy UDP :

  • si le destinataire est configuré pour utiliser un autre proxy HTTP, il agira comme un intermédiaire en transmettant la demande à un autre serveur HTTP. Notez que de tels intermédiaires peuvent avoir besoin de ré-encoder la demande s'ils la transmettent en utilisant une version de HTTP différente de celle utilisée pour la recevoir, car l'encodage de la demande diffère selon la version (voir ci-dessous).

  • sinon, le destinataire agira comme un proxy UDP. Il extrait les variables "target_host" et "target_port" de l'URI qu'il a reconstruit à partir des en-têtes de requête, décode leur encodage en pourcentage et établit un tunnel en ouvrant directement une socket UDP vers la cible demandée.

Contrairement à TCP, UDP est sans connexion. Le proxy UDP qui ouvre la socket UDP n'a aucun moyen de savoir si la destination est accessible. Par conséquent, il doit répondre à la demande sans attendre un paquet de la cible. Cependant, si "target_host" est un nom DNS, le proxy UDP DOIT effectuer une résolution DNS avant de répondre à la requête HTTP. Si des erreurs se produisent pendant ce processus, le proxy UDP DOIT rejeter la demande et DEVRAIT envoyer des détails à l'aide d'un champ d'en-tête Proxy-Status approprié [PROXY-STATUS]. Par exemple, si la résolution DNS renvoie une erreur, le proxy peut utiliser le type d'erreur de proxy dns_error de la section 2.3.2 de [PROXY-STATUS].

Les proxys UDP peuvent utiliser des sockets UDP connectées si leur système d'exploitation les prend en charge, car cela permet au proxy UDP de compter sur le noyau pour ne lui envoyer que des paquets UDP correspondant au bon 5-uplet. Si le proxy UDP utilise une socket non connectée, il DOIT valider l'adresse source IP et le port source UDP sur les paquets reçus pour s'assurer qu'ils correspondent à la demande du client. Les paquets qui ne correspondent pas DOIVENT être rejetés par le proxy UDP.

La durée de vie de la socket est liée au flux de requête. Le proxy UDP DOIT garder la socket ouverte tant que le flux de requête est ouvert. Si un proxy UDP est notifié par son système d'exploitation que sa socket n'est plus utilisable, il DOIT fermer le flux de requête. Par exemple, cela peut se produire lorsqu'un message ICMP Destination Unreachable est reçu ; voir la section 3.1 de [ICMP6]. Les proxys UDP PEUVENT choisir de fermer les sockets en raison d'une période d'inactivité, mais ils DOIVENT fermer le flux de requête lors de la fermeture de la socket. Les proxys UDP qui ferment les sockets après une période d'inactivité NE DEVRAIENT PAS utiliser une période inférieure à deux minutes ; voir la section 4.3 de [BEHAVE].

Une réponse réussie (telle que définie dans les sections 3.3 et 3.5) indique que le proxy UDP a ouvert une socket vers la cible demandée et est prêt à relayer les charges utiles UDP. Toute réponse autre qu'une réponse réussie indique que la demande a échoué ; ainsi, le client DOIT abandonner la demande.

Les proxys UDP NE DOIVENT PAS introduire de fragmentation au niveau de la couche IP lors de la transmission de datagrammes HTTP sur une socket UDP ; les datagrammes trop volumineux sont silencieusement rejetés. En IPv4, le bit Don't Fragment (DF) DOIT être défini, si possible, pour empêcher la fragmentation sur le chemin. Les extensions futures PEUVENT supprimer ces exigences.

Les implémenteurs de proxys UDP bénéficieront de la lecture des conseils de [UDP-USAGE].

3.2. Requête HTTP/1.1

Lors de l'utilisation de HTTP/1.1 [HTTP/1.1], une requête de proxy UDP répondra aux exigences suivantes :

  • la méthode DOIT être "GET".

  • la requête DOIT inclure un seul champ d'en-tête Host contenant l'origine du proxy UDP.

  • la requête DOIT inclure un champ d'en-tête Connection avec la valeur "Upgrade" (notez que cette exigence est insensible à la casse selon la section 7.6.1 de [HTTP]).

  • la requête DOIT inclure un champ d'en-tête Upgrade avec la valeur "connect-udp".

Une requête de proxy UDP qui ne respecte pas ces restrictions est malformée. Le destinataire d'une telle demande malformée DOIT répondre par une erreur et DEVRAIT utiliser le code d'état 400 (Bad Request).

Par exemple, si le client est configuré avec le modèle d'URI "https://example.org/.well-known/masque/udp/\{target_host\}/\{target_port\}/" et souhaite ouvrir un tunnel de proxy UDP vers la cible 192.0.2.6:443, il pourrait envoyer la requête suivante :

GET https://example.org/.well-known/masque/udp/192.0.2.6/443/ HTTP/1.1 Host: example.org Connection: Upgrade Upgrade: connect-udp Capsule-Protocol: ?1

                Figure 3 : Exemple de requête HTTP/1.1

Dans HTTP/1.1, ce protocole utilise la méthode GET pour imiter la conception du protocole WebSocket [WEBSOCKET].

3.3. Réponse HTTP/1.1

Le proxy UDP DOIT indiquer une réponse réussie en répondant avec les exigences suivantes :

  • le code d'état HTTP sur la réponse DOIT être 101 (Switching Protocols).

  • la réponse DOIT inclure un champ d'en-tête Connection avec la valeur "Upgrade" (notez que cette exigence est insensible à la casse selon la section 7.6.1 de [HTTP]).

  • la réponse DOIT inclure un seul champ d'en-tête Upgrade avec la valeur "connect-udp".

  • la réponse DOIT répondre aux exigences des réponses HTTP qui démarrent le protocole Capsule ; voir la section 3.2 de [HTTP-DGRAM].

Si l'une de ces exigences n'est pas satisfaite, le client DOIT traiter cette tentative de proxy comme un échec et abandonner la connexion.

Par exemple, le proxy UDP pourrait répondre avec :

HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: connect-udp Capsule-Protocol: ?1

                Figure 4 : Exemple de réponse HTTP/1.1

3.4. Requêtes HTTP/2 et HTTP/3

Lors de l'utilisation de HTTP/2 [HTTP/2] ou HTTP/3 [HTTP/3], les requêtes de proxy UDP utilisent HTTP Extended CONNECT. Cela nécessite que les serveurs envoient un paramètre HTTP tel que spécifié dans [EXT-CONNECT2] et [EXT-CONNECT3] et que les requêtes utilisent des champs de pseudo-en-tête HTTP avec les exigences suivantes :

  • Le champ de pseudo-en-tête :method DOIT être "CONNECT".

  • Le champ de pseudo-en-tête :protocol DOIT être "connect-udp".

  • Le champ de pseudo-en-tête :authority DOIT contenir l'autorité du proxy UDP.

  • Les champs de pseudo-en-tête :path et :scheme NE DOIVENT PAS être vides. Leurs valeurs DOIVENT contenir le schéma et le chemin du modèle d'URI une fois le processus d'expansion du modèle d'URI terminé.

Une requête de proxy UDP qui ne respecte pas ces restrictions est malformée (voir la section 8.1.1 de [HTTP/2] et la section 4.1.2 de [HTTP/3]).

Par exemple, si le client est configuré avec le modèle d'URI "https://example.org/.well-known/masque/udp/\{target_host\}/\{target_port\}/" et souhaite ouvrir un tunnel de proxy UDP vers la cible 192.0.2.6:443, il pourrait envoyer la requête suivante :

HEADERS :method = CONNECT :protocol = connect-udp :scheme = https :path = /.well-known/masque/udp/192.0.2.6/443/ :authority = example.org capsule-protocol = ?1

                  Figure 5 : Exemple de requête HTTP/2

3.5. Réponses HTTP/2 et HTTP/3

Le proxy UDP DOIT indiquer une réponse réussie en répondant avec les exigences suivantes :

  • le code d'état HTTP sur la réponse DOIT être dans la plage 2xx (Succès).

  • la réponse DOIT répondre aux exigences des réponses HTTP qui démarrent le protocole Capsule ; voir la section 3.2 de [HTTP-DGRAM].

Si l'une de ces exigences n'est pas satisfaite, le client DOIT traiter cette tentative de proxy comme un échec et abandonner la demande.

Par exemple, le proxy UDP pourrait répondre avec :

HEADERS :status = 200 capsule-protocol = ?1

                 Figure 6 : Exemple de réponse HTTP/2