3. États des flux
3. États des flux
Cette section décrit les flux en termes de leurs composants d'envoi ou de réception. Deux machines à états sont décrites: une pour les flux sur lesquels un point de terminaison transmet des données (Section 3.1) et une autre pour les flux sur lesquels un point de terminaison reçoit des données (Section 3.2).
Les flux unidirectionnels utilisent soit la machine à états d'envoi, soit celle de réception, selon le type de flux et le rôle du point de terminaison. Les flux bidirectionnels utilisent les deux machines à états aux deux points de terminaison. Pour la plupart, l'utilisation de ces machines à états est la même que le flux soit unidirectionnel ou bidirectionnel. Les conditions d'ouverture d'un flux sont légèrement plus complexes pour un flux bidirectionnel car l'ouverture de la partie d'envoi ou de réception provoque l'ouverture du flux dans les deux directions.
Les machines à états présentées dans cette section sont largement informatives. Ce document utilise les états de flux pour décrire les règles sur quand et comment différents types de trames peuvent être envoyés et les réactions attendues lorsque différents types de trames sont reçus. Bien que ces machines à états soient destinées à être utiles pour implémenter QUIC, ces états ne sont pas destinés à contraindre les implémentations. Une implémentation peut définir une machine à états différente tant que son comportement est cohérent avec une implémentation qui implémente ces états.
Dans certains cas, un seul événement ou action peut provoquer une transition à travers plusieurs états. Par exemple, l'envoi de STREAM avec un bit FIN défini peut provoquer deux transitions d'état pour un flux d'envoi: de l'état "Ready" à l'état "Send", et de l'état "Send" à l'état "Data Sent".
3.1 États des flux d'envoi
La Figure 2 montre les états pour la partie d'un flux qui envoie des données à un pair.
o
| Create Stream (Sending)
| Peer Creates Bidirectional Stream
v
+-------+
| Ready | Send RESET_STREAM
| |-----------------------.
+-------+ |
| |
| Send STREAM / |
| STREAM_DATA_BLOCKED |
v |
+-------+ |
| Send | Send RESET_STREAM |
| |---------------------->|
+-------+ |
| |
| Send STREAM + FIN |
v v
+-------+ +-------+
| Data | Send RESET_STREAM | Reset |
| Sent |------------------>| Sent |
+-------+ +-------+
| |
| Recv All ACKs | Recv ACK
v v
+-------+ +-------+
| Data | | Reset |
| Recvd | | Recvd |
+-------+ +-------+
Figure 2: États pour les parties d'envoi des flux
La partie d'envoi d'un flux que le point de terminaison initie (types 0 et 2 pour les clients, 1 et 3 pour les serveurs) est ouverte par l'application. L'état "Ready" représente un flux nouvellement créé qui est capable d'accepter des données de l'application. Les données de flux peuvent être mises en mémoire tampon dans cet état en préparation de l'envoi.
L'envoi de la première trame STREAM ou STREAM_DATA_BLOCKED provoque l'entrée d'une partie d'envoi d'un flux dans l'état "Send". Une implémentation peut choisir de reporter l'allocation d'un ID de flux à un flux jusqu'à ce qu'elle envoie la première trame STREAM et entre dans cet état, ce qui peut permettre une meilleure priorisation des flux.
La partie d'envoi d'un flux bidirectionnel initié par un pair (type 0 pour un serveur, type 1 pour un client) commence dans l'état "Ready" lorsque la partie de réception est créée.
Dans l'état "Send", un point de terminaison transmet -- et retransmet si nécessaire -- des données de flux dans des trames STREAM. Le point de terminaison respecte les limites de contrôle de flux définies par son pair et continue d'accepter et de traiter les trames MAX_STREAM_DATA. Un point de terminaison dans l'état "Send" génère des trames STREAM_DATA_BLOCKED s'il est bloqué dans l'envoi par les limites de contrôle de flux du flux (Section 4.1).
Après que l'application indique que toutes les données de flux ont été envoyées et qu'une trame STREAM contenant le bit FIN est envoyée, la partie d'envoi du flux entre dans l'état "Data Sent". À partir de cet état, le point de terminaison ne retransmet que les données de flux si nécessaire. Le point de terminaison n'a pas besoin de vérifier les limites de contrôle de flux ni d'envoyer des trames STREAM_DATA_BLOCKED pour un flux dans cet état. Des trames MAX_STREAM_DATA peuvent être reçues jusqu'à ce que le pair reçoive le décalage de flux final. Le point de terminaison peut ignorer en toute sécurité toutes les trames MAX_STREAM_DATA qu'il reçoit de son pair pour un flux dans cet état.
Une fois que toutes les données de flux ont été reconnues avec succès, la partie d'envoi du flux entre dans l'état "Data Recvd", qui est un état terminal.
À partir de n'importe quel état qui est "Ready", "Send" ou "Data Sent", une application peut signaler qu'elle souhaite abandonner la transmission des données de flux. Alternativement, un point de terminaison peut recevoir une trame STOP_SENDING de son pair. Dans les deux cas, le point de terminaison envoie une trame RESET_STREAM, ce qui provoque l'entrée du flux dans l'état "Reset Sent".
Un point de terminaison PEUT envoyer un RESET_STREAM comme première trame mentionnant un flux; cela provoque l'ouverture de la partie d'envoi de ce flux puis la transition immédiate vers l'état "Reset Sent".
Une fois qu'un paquet contenant un RESET_STREAM a été reconnu, la partie d'envoi du flux entre dans l'état "Reset Recvd", qui est un état terminal.
3.2 États des flux de réception
La Figure 3 montre les états pour la partie d'un flux qui reçoit des données d'un pair. Les états pour une partie de réception d'un flux ne reflètent que certains des états de la partie d'envoi du flux chez le pair. La partie de réception d'un flux ne suit pas les états de la partie d'envoi qui ne peuvent pas être observés, comme l'état "Ready". Au lieu de cela, la partie de réception d'un flux suit la livraison des données à l'application, dont certaines ne peuvent pas être observées par l'expéditeur.
o
| Recv STREAM / STREAM_DATA_BLOCKED / RESET_STREAM
| Create Bidirectional Stream (Sending)
| Recv MAX_STREAM_DATA / STOP_SENDING (Bidirectional)
| Create Higher-Numbered Stream
v
+-------+
| Recv | Recv RESET_STREAM
| |-----------------------.
+-------+ |
| |
| Recv STREAM + FIN |
v |
+-------+ |
| Size | Recv RESET_STREAM |
| Known |---------------------->|
+-------+ |
| |
| Recv All Data |
v v
+-------+ Recv RESET_STREAM +-------+
| Data |--- (optional) --->| Reset |
| Recvd | Recv All Data | Recvd |
+-------+<-- (optional) ----+-------+
| |
| App Read All Data | App Read Reset
v v
+-------+ +-------+
| Data | | Reset |
| Read | | Read |
+-------+ +-------+
Figure 3: États pour les parties de réception des flux
La partie de réception d'un flux initié par un pair (types 1 et 3 pour un client, ou 0 et 2 pour un serveur) est créée lorsque la première trame STREAM, STREAM_DATA_BLOCKED ou RESET_STREAM est reçue pour ce flux. Pour les flux bidirectionnels initiés par un pair, la réception d'une trame MAX_STREAM_DATA ou STOP_SENDING pour la partie d'envoi du flux crée également la partie de réception. L'état initial pour la partie de réception d'un flux est "Recv".
Pour un flux bidirectionnel, la partie de réception entre dans l'état "Recv" lorsque la partie d'envoi initiée par le point de terminaison (type 0 pour un client, type 1 pour un serveur) entre dans l'état "Ready".
Un point de terminaison ouvre un flux bidirectionnel lorsqu'une trame MAX_STREAM_DATA ou STOP_SENDING est reçue du pair pour ce flux. La réception d'une trame MAX_STREAM_DATA pour un flux non ouvert indique que le pair distant a ouvert le flux et fournit un crédit de contrôle de flux. La réception d'une trame STOP_SENDING pour un flux non ouvert indique que le pair distant ne souhaite plus recevoir de données sur ce flux. L'une ou l'autre trame peut arriver avant une trame STREAM ou STREAM_DATA_BLOCKED si des paquets sont perdus ou réordonnés.
Avant qu'un flux ne soit créé, tous les flux du même type avec des ID de flux de numéros inférieurs DOIVENT être créés. Cela garantit que l'ordre de création des flux est cohérent aux deux points de terminaison.
Dans l'état "Recv", le point de terminaison reçoit des trames STREAM et STREAM_DATA_BLOCKED. Les données entrantes sont mises en mémoire tampon et peuvent être réassemblées dans l'ordre correct pour la livraison à l'application. Au fur et à mesure que les données sont consommées par l'application et que l'espace de mémoire tampon devient disponible, le point de terminaison envoie des trames MAX_STREAM_DATA pour permettre au pair d'envoyer plus de données.
Lorsqu'une trame STREAM avec un bit FIN est reçue, la taille finale du flux est connue; voir Section 4.5. La partie de réception du flux entre alors dans l'état "Size Known". Dans cet état, le point de terminaison n'a plus besoin d'envoyer de trames MAX_STREAM_DATA; il reçoit seulement des retransmissions de données de flux.
Une fois que toutes les données du flux ont été reçues, la partie de réception entre dans l'état "Data Recvd". Cela peut se produire en raison de la réception de la même trame STREAM qui provoque la transition vers "Size Known". Après la réception de toutes les données, toutes les trames STREAM ou STREAM_DATA_BLOCKED pour le flux peuvent être rejetées.
L'état "Data Recvd" persiste jusqu'à ce que les données de flux aient été livrées à l'application. Une fois que les données de flux ont été livrées, le flux entre dans l'état "Data Read", qui est un état terminal.
La réception d'une trame RESET_STREAM dans l'état "Recv" ou "Size Known" provoque l'entrée du flux dans l'état "Reset Recvd". Cela peut provoquer l'interruption de la livraison des données de flux à l'application.
Il est possible que toutes les données de flux aient déjà été reçues lorsqu'un RESET_STREAM est reçu (c'est-à-dire, dans l'état "Data Recvd"). De même, il est possible que les données de flux restantes arrivent après la réception d'une trame RESET_STREAM (l'état "Reset Recvd"). Une implémentation est libre de gérer cette situation comme elle le souhaite.
L'envoi d'un RESET_STREAM signifie qu'un point de terminaison ne peut pas garantir la livraison des données de flux; cependant, il n'y a aucune exigence que les données de flux ne soient pas livrées si un RESET_STREAM est reçu. Une implémentation PEUT interrompre la livraison des données de flux, rejeter toutes les données qui n'ont pas été consommées et signaler la réception du RESET_STREAM. Un signal RESET_STREAM peut être supprimé ou retenu si les données de flux sont complètement reçues et mises en mémoire tampon pour être lues par l'application. Si le RESET_STREAM est supprimé, la partie de réception du flux reste dans "Data Recvd".
Une fois que l'application reçoit le signal indiquant que le flux a été réinitialisé, la partie de réception du flux passe à l'état "Reset Read", qui est un état terminal.
3.3 Types de trames autorisés
L'expéditeur d'un flux envoie seulement trois types de trames qui affectent l'état d'un flux soit chez l'expéditeur soit chez le récepteur: STREAM (Section 19.8), STREAM_DATA_BLOCKED (Section 19.13), et RESET_STREAM (Section 19.4).
Un expéditeur NE DOIT PAS envoyer aucune de ces trames à partir d'un état terminal ("Data Recvd" ou "Reset Recvd"). Un expéditeur NE DOIT PAS envoyer une trame STREAM ou STREAM_DATA_BLOCKED pour un flux dans l'état "Reset Sent" ou tout état terminal -- c'est-à-dire, après l'envoi d'une trame RESET_STREAM. Un récepteur pourrait recevoir l'une de ces trois trames dans n'importe quel état, en raison de la possibilité de livraison retardée de paquets les transportant. La réception de trames dans des états ultérieurs NE DOIT PAS être traitée comme une erreur et ne change pas l'état de la partie d'envoi.
Un récepteur envoie des trames MAX_STREAM_DATA (Section 19.10) et STOP_SENDING (Section 19.5).
Le récepteur envoie des trames MAX_STREAM_DATA uniquement dans l'état "Recv". Un récepteur peut envoyer une trame STOP_SENDING dans n'importe quel état où il n'a pas reçu de trame RESET_STREAM; c'est-à-dire, tout état autre que "Reset Recvd" ou "Reset Read". Cependant, il y a peu de valeur à envoyer une trame STOP_SENDING dans l'état "Data Recvd", car toutes les données de flux ont été reçues. Un expéditeur peut recevoir ces trames dans n'importe quel état en raison de la livraison retardée de paquets.
3.4 États des flux bidirectionnels
Un flux bidirectionnel est composé d'une partie d'envoi et d'une partie de réception. Les implémentations peuvent représenter les états du flux bidirectionnel comme des composites des états des parties d'envoi et de réception. Le modèle le plus simple présente le flux comme "ouvert" lorsque soit les parties d'envoi soit les parties de réception sont dans un état non-terminal et "fermé" lorsque les deux parties sont dans des états terminaux.
Le Tableau 2 montre une cartographie plus complexe des états de flux bidirectionnels qui correspond approximativement aux états de flux dans HTTP/2 [HTTP2]. Cela montre que plusieurs états sur les parties d'envoi ou de réception des flux sont mappés au même état composite. Notez que ce n'est qu'une possibilité pour une telle cartographie; cette cartographie exige que les données soient reconnues avant la transition vers un état "fermé" ou "semi-fermé".
| Partie d'envoi | Partie de réception | État composite |
|---|---|---|
| No Stream/Ready | No Stream/Recv *1 | idle |
| Ready/Send/Data Sent | Recv/Size Known | open |
| Ready/Send/Data Sent | Data Recvd/Data Read | half-closed (remote) |
| Ready/Send/Data Sent | Reset Recvd/Reset Read | half-closed (remote) |
| Data Recvd | Recv/Size Known | half-closed (local) |
| Reset Sent/Reset Recvd | Recv/Size Known | half-closed (local) |
| Reset Sent/Reset Recvd | Data Recvd/Data Read | closed |
| Reset Sent/Reset Recvd | Reset Recvd/Reset Read | closed |
| Data Recvd | Data Recvd/Data Read | closed |
| Data Recvd | Reset Recvd/Reset Read | closed |
Tableau 2: Cartographie possible des états de flux vers HTTP/2
Notez que l'état "No Stream" est hypothétique; ni un point de terminaison ni son pair n'ont créé le flux.
3.5 Transitions d'état sollicitées
Si une application n'est plus intéressée par les données qu'elle reçoit sur un flux, elle peut abandonner la lecture du flux et spécifier un code d'erreur d'application.
Si le flux est dans l'état "Recv" ou "Size Known", le transport DEVRAIT signaler cela en envoyant une trame STOP_SENDING pour provoquer la fermeture du flux dans la direction opposée. Cela indique généralement que l'application de réception ne lit plus les données qu'elle reçoit du flux, mais ce n'est pas une garantie que les données entrantes seront ignorées.
Les trames STREAM reçues après l'envoi d'une trame STOP_SENDING sont toujours comptées vers le contrôle de flux de la connexion et du flux, même si ces trames peuvent être rejetées à la réception.
Une trame STOP_SENDING demande que le point de terminaison de réception envoie une trame RESET_STREAM. Un point de terminaison qui reçoit une trame STOP_SENDING DOIT envoyer une trame RESET_STREAM si le flux est dans l'état "Ready" ou "Send". Si le flux est dans l'état "Data Sent", le point de terminaison PEUT reporter l'envoi de la trame RESET_STREAM jusqu'à ce que les paquets contenant des données en suspens soient reconnus ou déclarés perdus. Si des données en suspens sont déclarées perdues, le point de terminaison DEVRAIT envoyer une trame RESET_STREAM au lieu de retransmettre les données.
Un point de terminaison DEVRAIT copier le code d'erreur de la trame STOP_SENDING vers la trame RESET_STREAM qu'il envoie, mais il peut utiliser n'importe quel code d'erreur d'application. Le point de terminaison qui envoie une trame STOP_SENDING PEUT ignorer le code d'erreur dans toutes les trames RESET_STREAM reçues par la suite pour ce flux.
STOP_SENDING DEVRAIT être envoyé uniquement pour un flux qui n'a pas été réinitialisé par le pair. STOP_SENDING est le plus utile pour les flux dans l'état "Recv" ou "Size Known".
Un point de terminaison est censé envoyer une autre trame STOP_SENDING si un paquet contenant un STOP_SENDING précédent est perdu. Cependant, une fois que soit toutes les données de flux soit une trame RESET_STREAM ont été reçues pour le flux -- c'est-à-dire, le flux est dans n'importe quel état autre que "Recv" ou "Size Known" -- l'envoi d'une trame STOP_SENDING est inutile.
Un point de terminaison qui souhaite terminer les deux directions d'un flux bidirectionnel peut terminer une direction en envoyant une trame RESET_STREAM, et il peut encourager une terminaison rapide dans la direction opposée en envoyant une trame STOP_SENDING.