Aller au contenu principal

4. Format de transmission (Wire Format)

4.1 Primitives

4.1.1 Entiers préfixés (Prefixed Integers)

Ce document utilise largement les entiers préfixés de la section 5.1 de [RFC7541]. Le format de [RFC7541] est utilisé sans modification. Notez cependant que QPACK utilise certaines tailles de préfixe qui ne sont pas réellement utilisées dans HPACK.

Les implémentations QPACK DOIVENT être capables de décoder des entiers jusqu'à 62 bits inclus.

4.1.2 Littéraux de chaîne (String Literals)

Le littéral de chaîne défini par la section 5.2 de [RFC7541] est également utilisé dans tout ce document. Ce format de chaîne inclut un encodage Huffman optionnel.

HPACK définit que les littéraux de chaîne commencent sur une limite d'octet. Ils commencent par un indicateur de bit unique, appelé 'H' dans ce document (indiquant si la chaîne est encodée en Huffman), suivi de la longueur encodée comme un entier préfixé de 7 bits, et enfin le nombre d'octets de données indiqué. Lorsque l'encodage Huffman est activé, la table Huffman de l'annexe B de [RFC7541] est utilisée sans modification, et la longueur indique la taille de la chaîne encodée.

Ce document étend la définition des littéraux de chaîne en leur permettant de commencer ailleurs que sur une limite d'octet. Un "littéral de chaîne préfixé de N bits" commence au milieu d'un octet, les premiers (8-N) bits étant alloués à un champ précédent. La chaîne utilise un bit pour l'indicateur Huffman, suivi de la longueur encodée comme un entier préfixé de (N-1) bits. La valeur de la taille de préfixe N peut être comprise entre 2 et 8 inclus. Le reste du littéral de chaîne n'est pas modifié.

Un littéral de chaîne sans longueur de préfixe notée est un littéral de chaîne préfixé de 8 bits et suit les définitions de [RFC7541] sans modification.

4.2 Flux d'encodeur et de décodeur (Encoder and Decoder Streams)

QPACK définit deux types de flux unidirectionnels :

  • Flux d'encodeur est un flux unidirectionnel de type 0x02. Il transporte une séquence non encadrée d'instructions d'encodeur de l'encodeur au décodeur.

  • Flux de décodeur est un flux unidirectionnel de type 0x03. Il transporte une séquence non encadrée d'instructions de décodeur du décodeur à l'encodeur.

Les points d'extrémité HTTP/3 contiennent un encodeur et un décodeur QPACK. Chaque point d'extrémité DOIT initier au plus un flux d'encodeur et au plus un flux de décodeur. La réception d'une deuxième instance de l'un ou l'autre type de flux DOIT être traitée comme une erreur de connexion de type H3_STREAM_CREATION_ERROR.

L'expéditeur NE DOIT PAS fermer l'un ou l'autre de ces flux, et le destinataire NE DOIT PAS demander que l'expéditeur ferme l'un ou l'autre de ces flux. La fermeture de l'un ou l'autre type de flux unidirectionnel DOIT être traitée comme une erreur de connexion de type H3_CLOSED_CRITICAL_STREAM.

Un point d'extrémité PEUT éviter de créer un flux d'encodeur s'il ne sera pas utilisé (par exemple, si son encodeur ne souhaite pas utiliser la table dynamique ou si la taille maximale de la table dynamique autorisée par le pair est zéro).

Un point d'extrémité PEUT éviter de créer un flux de décodeur si le décodeur définit la capacité maximale de la table dynamique à zéro.

Un point d'extrémité DOIT permettre à son pair de créer un flux d'encodeur et un flux de décodeur même si les paramètres de la connexion empêchent leur utilisation.

4.3 Instructions d'encodeur (Encoder Instructions)

Un encodeur envoie des instructions d'encodeur sur le flux d'encodeur pour définir la capacité de la table dynamique et ajouter des entrées à la table dynamique. Les instructions ajoutant des entrées de table peuvent utiliser des entrées existantes pour éviter de transmettre des informations redondantes. Le nom peut être transmis comme une référence à une entrée existante dans la table statique ou la table dynamique, ou comme un littéral de chaîne. Pour les entrées qui existent déjà dans la table dynamique, l'entrée complète peut également être utilisée par référence, créant une entrée en double.

4.3.1 Définir la capacité de la table dynamique (Set Dynamic Table Capacity)

Un encodeur utilise une instruction commençant par le motif de 3 bits '001' pour informer le décodeur d'un changement de capacité de la table dynamique. Ceci est suivi de la nouvelle capacité de la table dynamique représentée comme un entier avec un préfixe de 5 bits ; voir la section 4.1.1.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 1 | Capacity (5+) |
+---+---+---+-------------------+

Figure 5 : Définir la capacité de la table dynamique

La nouvelle capacité DOIT être inférieure ou égale à la limite décrite dans la section 3.2.3. Dans HTTP/3, cette limite est la valeur du paramètre SETTINGS_QPACK_MAX_TABLE_CAPACITY (section 5) reçu du décodeur. Le décodeur DOIT traiter une nouvelle valeur de capacité de table dynamique qui dépasse cette limite comme une erreur de connexion de type QPACK_ENCODER_STREAM_ERROR.

La réduction de la capacité de la table dynamique peut entraîner l'éviction d'entrées ; voir la section 3.2.2. Cela NE DOIT PAS entraîner l'éviction d'entrées qui ne sont pas évictables ; voir la section 2.1.1. La modification de la capacité de la table dynamique n'est pas acquittée, car elle n'insère pas d'entrée.

4.3.2 Insérer avec référence de nom (Insert with Name Reference)

Un encodeur ajoute une entrée à la table dynamique où le nom de champ correspond au nom de champ d'une entrée stockée dans la table statique ou la table dynamique en utilisant une instruction qui commence par le motif de 1 bit '1'. Le deuxième bit ('T') indique si la référence est à la table statique ou à la table dynamique. L'entier préfixé de 6 bits suivant (section 4.1.1) est utilisé pour localiser l'entrée de table pour le nom de champ. Lorsque T=1, le nombre représente l'index de table statique ; lorsque T=0, le nombre est l'index relatif de l'entrée dans la table dynamique.

La référence de nom de champ est suivie de la valeur de champ représentée comme un littéral de chaîne ; voir la section 4.1.2.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 1 | T | Name Index (6+) |
+---+---+-----------------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+

Figure 6 : Insérer ligne de champ -- Nom indexé

4.3.3 Insérer avec nom littéral (Insert with Literal Name)

Un encodeur ajoute une entrée à la table dynamique où le nom de champ et la valeur de champ sont tous deux représentés comme des littéraux de chaîne en utilisant une instruction qui commence par le motif de 2 bits '01'.

Ceci est suivi du nom représenté comme un littéral de chaîne préfixé de 6 bits, et de la valeur représentée comme un littéral de chaîne préfixé de 8 bits ; voir la section 4.1.2.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 1 | H | Name Length (5+) |
+---+---+---+-------------------+
| Name String (Length bytes) |
+---+---------------------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+

Figure 7 : Insérer ligne de champ -- Nouveau nom

4.3.4 Dupliquer (Duplicate)

Un encodeur duplique une entrée existante dans la table dynamique en utilisant une instruction qui commence par le motif de 3 bits '000'. Ceci est suivi de l'index relatif de l'entrée existante représenté comme un entier avec un préfixe de 5 bits ; voir la section 4.1.1.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | Index (5+) |
+---+---+---+-------------------+

Figure 8 : Dupliquer

L'entrée existante est réinsérée dans la table dynamique sans renvoyer ni le nom ni la valeur. Ceci est utile pour éviter d'ajouter une référence à une entrée plus ancienne, ce qui pourrait bloquer l'insertion de nouvelles entrées.

4.4 Instructions de décodeur (Decoder Instructions)

Un décodeur envoie des instructions de décodeur sur le flux de décodeur pour informer l'encodeur du traitement des sections de champ et des mises à jour de table afin d'assurer la cohérence de la table dynamique.

4.4.1 Accusé de réception de section (Section Acknowledgment)

Après avoir traité une section de champ encodée dont le comptage d'insertion requis déclaré n'est pas zéro, le décodeur émet une instruction d'accusé de réception de section. L'instruction commence par le motif de 1 bit '1', suivi de l'ID de flux associé de la section de champ encodé comme un entier préfixé de 7 bits ; voir la section 4.1.1.

Cette instruction est utilisée comme décrit dans les sections 2.1.4 et 2.2.2.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 1 | Stream ID (7+) |
+---+---------------------------+

Figure 9 : Accusé de réception de section

Si un encodeur reçoit une instruction d'accusé de réception de section faisant référence à un flux sur lequel chaque section de champ encodée avec un comptage d'insertion requis non nul a déjà été acquittée, cela DOIT être traité comme une erreur de connexion de type QPACK_DECODER_STREAM_ERROR.

L'instruction d'accusé de réception de section peut augmenter le comptage de réception connu ; voir la section 2.1.4.

4.4.2 Annulation de flux (Stream Cancellation)

Lorsqu'un flux est réinitialisé ou que la lecture est abandonnée, le décodeur émet une instruction d'annulation de flux. L'instruction commence par le motif de 2 bits '01', suivi de l'ID de flux du flux affecté encodé comme un entier préfixé de 6 bits.

Cette instruction est utilisée comme décrit dans la section 2.2.2.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 1 | Stream ID (6+) |
+---+---+-----------------------+

Figure 10 : Annulation de flux

4.4.3 Incrément du comptage d'insertion (Insert Count Increment)

L'instruction d'incrément du comptage d'insertion commence par le motif de 2 bits '00', suivi de l'incrément encodé comme un entier préfixé de 6 bits. Cette instruction augmente le comptage de réception connu (section 2.1.4) de la valeur du paramètre d'incrément. Le décodeur devrait envoyer une valeur d'incrément qui augmente le comptage de réception connu jusqu'au nombre total d'insertions et de duplications de table dynamique traités jusqu'à présent.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | Increment (6+) |
+---+---+-----------------------+

Figure 11 : Incrément du comptage d'insertion

Un encodeur qui reçoit un champ d'incrément égal à zéro ou qui augmente le comptage de réception connu au-delà de ce que l'encodeur a envoyé DOIT traiter cela comme une erreur de connexion de type QPACK_DECODER_STREAM_ERROR.

4.5 Représentations de lignes de champ (Field Line Representations)

Une section de champ encodée se compose d'un préfixe et d'une séquence éventuellement vide de représentations définies dans cette section. Chaque représentation correspond à une seule ligne de champ. Ces représentations référencent la table statique ou la table dynamique dans un état particulier, mais elles ne modifient pas cet état.

Une section de champ encodée est transportée dans une trame sur un flux défini par le protocole englobant.

4.5.1 Préfixe de section de champ encodée (Encoded Field Section Prefix)

Chaque section de champ encodée est préfixée par deux entiers. Le comptage d'insertion requis est encodé comme un entier avec un préfixe de 8 bits utilisant l'encodage décrit dans la section 4.5.1.1. La base est encodée comme un bit de signe ('S') et une valeur de base delta avec un préfixe de 7 bits ; voir la section 4.5.1.2.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| Required Insert Count (8+) |
+---+---------------------------+
| S | Delta Base (7+) |
+---+---------------------------+
| Encoded Field Lines ...
+-------------------------------+

Figure 12 : Section de champ encodée

4.5.1.1 Comptage d'insertion requis (Required Insert Count)

Le comptage d'insertion requis identifie l'état de la table dynamique nécessaire pour traiter la section de champ encodée. Les décodeurs bloquants utilisent le comptage d'insertion requis pour déterminer quand il est sûr de traiter le reste de la section de champ.

L'encodeur transforme le comptage d'insertion requis comme suit avant l'encodage :

if ReqInsertCount == 0:
EncInsertCount = 0
else:
EncInsertCount = (ReqInsertCount mod (2 * MaxEntries)) + 1

Ici, MaxEntries est le nombre maximum d'entrées que la table dynamique peut avoir. La plus petite entrée a des chaînes de nom et de valeur vides et a une taille de 32. Par conséquent, MaxEntries est calculé comme suit :

MaxEntries = floor( MaxTableCapacity / 32 )

MaxTableCapacity est la capacité maximale de la table dynamique telle que spécifiée par le décodeur ; voir la section 3.2.3.

Cet encodage limite la longueur du préfixe sur les connexions de longue durée.

Le décodeur peut reconstruire le comptage d'insertion requis en utilisant un algorithme tel que le suivant. Si le décodeur rencontre une valeur EncodedInsertCount qui n'aurait pas pu être produite par un encodeur conforme, il DOIT traiter cela comme une erreur de connexion de type QPACK_DECOMPRESSION_FAILED.

TotalNumberOfInserts est le nombre total d'insertions dans la table dynamique du décodeur.

FullRange = 2 * MaxEntries
if EncodedInsertCount == 0:
ReqInsertCount = 0
else:
if EncodedInsertCount > FullRange:
Error
MaxValue = TotalNumberOfInserts + MaxEntries

# MaxWrapped est la plus grande valeur possible de
# ReqInsertCount qui est 0 mod 2 * MaxEntries
MaxWrapped = floor(MaxValue / FullRange) * FullRange
ReqInsertCount = MaxWrapped + EncodedInsertCount - 1

# Si ReqInsertCount dépasse MaxValue, la valeur de l'encodeur
# doit avoir bouclé une fois de moins
if ReqInsertCount > MaxValue:
if ReqInsertCount <= FullRange:
Error
ReqInsertCount -= FullRange

# La valeur de 0 doit être encodée comme 0
if ReqInsertCount == 0:
Error

Par exemple, si la table dynamique est de 100 octets, alors le comptage d'insertion requis sera encodé modulo 6. Si un décodeur a reçu 10 insertions, alors une valeur encodée de 4 indique que le comptage d'insertion requis est de 9 pour la section de champ.

4.5.1.2 Base (Base)

La base est utilisée pour résoudre les références dans la table dynamique comme décrit dans la section 3.2.5.

Pour économiser de l'espace, la base est encodée relativement au comptage d'insertion requis en utilisant un bit de signe à un bit ('S' dans la figure 12) et la valeur de base delta. Un bit de signe de 0 indique que la base est supérieure ou égale à la valeur du comptage d'insertion requis ; le décodeur ajoute la valeur de la base delta au comptage d'insertion requis pour déterminer la valeur de la base. Un bit de signe de 1 indique que la base est inférieure au comptage d'insertion requis ; le décodeur soustrait la valeur de la base delta du comptage d'insertion requis et soustrait également un pour déterminer la valeur de la base. C'est-à-dire :

if Sign == 0:
Base = ReqInsertCount + DeltaBase
else:
Base = ReqInsertCount - DeltaBase - 1

Un encodeur à passage unique détermine la base avant d'encoder une section de champ. Si l'encodeur a inséré des entrées dans la table dynamique lors de l'encodage de la section de champ et les référence, le comptage d'insertion requis sera supérieur à la base, donc la différence encodée est négative et le bit de signe est défini à 1. Si la section de champ n'a pas été encodée en utilisant des représentations qui référencent l'entrée la plus récente de la table et n'a inséré aucune nouvelle entrée, la base sera supérieure au comptage d'insertion requis, donc la différence encodée est positive et le bit de signe est défini à 0.

La valeur de la base NE DOIT PAS être négative. Bien que le protocole fonctionnerait correctement avec une base négative et une indexation post-base, cela est inutilement inefficace. Si la valeur du comptage d'insertion requis est inférieure ou égale à la valeur de la base delta, un point d'extrémité DOIT traiter un bloc de champ avec un bit de signe de 1 comme invalide.

Un encodeur qui produit des mises à jour de table avant d'encoder une section de champ peut définir la base à la valeur du comptage d'insertion requis. Dans ce cas, le bit de signe et la base delta seront tous deux définis à zéro.

Une section de champ qui a été encodée sans références à la table dynamique peut utiliser n'importe quelle valeur pour la base ; définir la base delta à zéro est l'un des encodages les plus efficaces.

Par exemple, avec un comptage d'insertion requis de 9, un bit de signe de 1 et une base delta de 2, la base est 6 et active l'indexation post-base pour trois entrées. Dans cet exemple, un index relatif de 1 fait référence à la cinquième entrée ajoutée à la table ; un index post-base de 1 fait référence à la huitième entrée.

4.5.2 Ligne de champ indexée (Indexed Field Line)

Une représentation de ligne de champ indexée identifie une entrée dans la table statique ou une entrée dans la table dynamique avec un index absolu inférieur à la valeur de la base.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 1 | T | Index (6+) |
+---+---+-----------------------+

Figure 13 : Ligne de champ indexée

Cette représentation commence par le motif de 1 bit '1', suivi du bit 'T' indiquant si la référence est à la table statique ou à la table dynamique. L'entier préfixé de 6 bits suivant (section 4.1.1) est utilisé pour localiser l'entrée de table pour la ligne de champ. Lorsque T=1, le nombre représente l'index de table statique ; lorsque T=0, le nombre est l'index relatif de l'entrée dans la table dynamique.

4.5.3 Ligne de champ indexée avec index post-base (Indexed Field Line with Post-Base Index)

Une représentation de ligne de champ indexée avec index post-base identifie une entrée dans la table dynamique avec un index absolu supérieur ou égal à la valeur de la base.

  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 1 | Index (4+) |
+---+---+---+---+---------------+

Figure 14 : Ligne de champ indexée avec index post-base

Cette représentation commence par le motif de 4 bits '0001'. Ceci est suivi de l'index post-base (section 3.2.6) de la ligne de champ correspondante, représenté comme un entier avec un préfixe de 4 bits ; voir la section 4.1.1.

4.5.4 Ligne de champ littérale avec référence de nom (Literal Field Line with Name Reference)

Une représentation de ligne de champ littérale avec référence de nom encode une ligne de champ où le nom de champ correspond au nom de champ d'une entrée dans la table statique ou au nom de champ d'une entrée dans la table dynamique avec un index absolu inférieur à la valeur de la base.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 1 | N | T |Name Index (4+)|
+---+---+---+---+---------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+

Figure 15 : Ligne de champ littérale avec référence de nom

Cette représentation commence par le motif de 2 bits '01'. Le bit suivant, 'N', indique si un intermédiaire est autorisé à ajouter cette ligne de champ à la table dynamique sur les sauts suivants. Lorsque le bit 'N' est défini, la ligne de champ encodée DOIT toujours être encodée avec une représentation littérale. En particulier, lorsqu'un pair envoie une ligne de champ qu'il a reçue représentée comme une ligne de champ littérale avec le bit 'N' défini, il DOIT utiliser une représentation littérale pour transmettre cette ligne de champ. Ce bit est destiné à protéger les valeurs de champ qui ne doivent pas être mises en danger en les compressant ; voir la section 7.1 pour plus de détails.

Le quatrième bit ('T') indique si la référence est à la table statique ou à la table dynamique. L'entier préfixé de 4 bits suivant (section 4.1.1) est utilisé pour localiser l'entrée de table pour le nom de champ. Lorsque T=1, le nombre représente l'index de table statique ; lorsque T=0, le nombre est l'index relatif de l'entrée dans la table dynamique.

Seul le nom de champ est pris de l'entrée de la table dynamique ; la valeur de champ est encodée comme un littéral de chaîne préfixé de 8 bits ; voir la section 4.1.2.

4.5.5 Ligne de champ littérale avec référence de nom post-base (Literal Field Line with Post-Base Name Reference)

Une représentation de ligne de champ littérale avec référence de nom post-base encode une ligne de champ où le nom de champ correspond au nom de champ d'une entrée de table dynamique avec un index absolu supérieur ou égal à la valeur de la base.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | N |NameIdx(3+)|
+---+---+---+---+---+-----------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+

Figure 16 : Ligne de champ littérale avec référence de nom post-base

Cette représentation commence par le motif de 4 bits '0000'. Le cinquième bit est le bit 'N' tel que décrit dans la section 4.5.4. Ceci est suivi d'un index post-base (section 3.2.6) de l'entrée de table dynamique, encodé comme un entier avec un préfixe de 3 bits ; voir la section 4.1.1.

Seul le nom de champ est pris de l'entrée de la table dynamique ; la valeur de champ est encodée comme un littéral de chaîne préfixé de 8 bits ; voir la section 4.1.2.

4.5.6 Ligne de champ littérale avec nom littéral (Literal Field Line with Literal Name)

Une représentation de ligne de champ littérale avec nom littéral encode un nom de champ et une valeur de champ comme des littéraux de chaîne.

     0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 1 | N | H |NameLen(3+)|
+---+---+---+---+---+-----------+
| Name String (Length bytes) |
+---+---------------------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+

Figure 17 : Ligne de champ littérale avec nom littéral

Cette représentation commence par le motif de 3 bits '001'. Le quatrième bit est le bit 'N' tel que décrit dans la section 4.5.4. Le nom suit, représenté comme un littéral de chaîne préfixé de 4 bits, puis la valeur, représentée comme un littéral de chaîne préfixé de 8 bits ; voir la section 4.1.2.