2. Aperçu du processus de compression (Compression Process Overview)
Comme HPACK, QPACK utilise deux tables pour associer des lignes de champ à des index. La table statique (voir la section 3.1) est prédéfinie et contient des lignes de champ communes (certaines avec des valeurs, d'autres sans). La table dynamique (voir la section 3.2) est construite dynamiquement et permet un encodage efficace des champs répétés dans les sections de champ encodées.
QPACK définit des flux unidirectionnels pour modifier et suivre l'état de la table dynamique sans bloquer l'encodage ou le décodage de sections de champ. Un flux d'encodeur transporte les modifications de la table de l'encodeur au décodeur, tandis qu'un flux de décodeur transporte les accusés de réception des modifications de la table et des utilisations de la table du décodeur à l'encodeur.
QPACK fournit des instructions de modélisation pour les sections de champ encodées qui permettent au décodeur d'annuler le blocage avant que l'état de la table dynamique nécessaire soit disponible. Cela peut se produire en raison de la livraison non ordonnée lorsque les flux sont multiplexés.
2.1 Encodeur (Encoder)
Un encodeur convertit une section de champ en une série de représentations en les faisant correspondre à des entrées dans les tables statique ou dynamique. Des informations de référence au-delà du nom de la ligne de champ peuvent être encodées en référençant une entrée pour une ligne de champ avec un nom et une valeur correspondants (voir la section 3.2.3 de [RFC7541]) ou en référençant une entrée pour une ligne de champ avec un nom correspondant et une valeur différente (voir la section 3.2.4 de [RFC7541]).
Un encodeur DOIT PAS faire référence à une ligne de champ statique en utilisant un index de post-Base littéral (voir la section 4.5.5), et un décodeur DOIT traiter la réception d'une instruction post-Base avec un index faisant référence à la table statique comme une erreur de flux de type QPACK_DECOMPRESSION_FAILED.
2.1.1 Limites d'insertion dans la table dynamique (Limits on Dynamic Table Insertions)
L'encodeur limite le nombre maximal d'entrées dans la table dynamique en utilisant des signaux du décodeur (voir la section 4.4.1). Un encodeur DOIT PAS insérer une entrée dans la table dynamique (ou dupliquer une entrée existante) si cela entraînerait le dépassement par la table dynamique de sa capacité maximale. Afin de préserver l'espace de la table dynamique pour les insertions futures, un encodeur pourrait choisir de supprimer les entrées en double en définissant une capacité qui est inférieure à la capacité maximale (voir la section 3.2.3).
Un encodeur DOIT PAS définir la capacité de la table dynamique à une valeur supérieure à la capacité maximale que le décodeur a signalée. En HTTP/3, ce maximum est la valeur du paramètre SETTINGS_QPACK_MAX_TABLE_CAPACITY (voir la section 5) reçu du décodeur. Un encodeur DOIT PAS définir la capacité de la table dynamique à une valeur supérieure à ce maximum même si le décodeur envoie ensuite un paramètre plus grand.
Pour chaque section de champ encodée, si l'entrée insérée la plus récemment dans la table dynamique n'est pas encore évictible, l'encodeur DOIT insérer un comptage d'insertions requis (voir la section 4.5.1) et une instruction d'incrémentation du comptage d'insertions (voir la section 4.4.3). Le décodeur compare l'état actuel de sa table dynamique avec l'état de la table dynamique nécessaire pour décoder la section de champ. S'il n'est pas suffisant, le décodeur peut bloquer le traitement de la section jusqu'à ce que la synchronisation soit atteinte (voir la section 2.2).
2.1.2 Flux bloqués (Blocked Streams)
Pour une représentation qui fait référence à la table dynamique, si le décodeur pourrait bloquer le traitement de la section de champ, l'encodeur DOIT s'assurer que le nombre de flux bloqués ne dépasse pas le maximum autorisé par le décodeur. Un flux est considéré comme bloqué si la plus grande entrée de table dynamique référencée n'est pas encore reçue. Notez qu'une représentation peut faire référence à des entrées dans n'importe quel ordre, et les encodeurs utilisent généralement les index relatifs (voir la section 3.2.5) ; par conséquent, même si la section de champ référence une ancienne entrée, elle peut toujours être bloquée.
Un encodeur PEUT choisir d'utiliser des représentations qui ne font pas référence à la table dynamique si cela éviterait de bloquer un flux.
Lors de l'encodage d'une section de champ sur un flux que l'encodeur s'attend à être bloqué, l'encodeur choisit une valeur de Base (voir la section 4.5.1) telle que le comptage d'insertions requis est atteint ou sera atteint bientôt. Si le comptage d'insertions requis dépasse le nombre d'entrées actuellement présentes dans la table dynamique, la section de champ bloque jusqu'à ce que les entrées manquantes soient insérées dans la table dynamique par le décodeur (voir la section 4.4.3).
Une implémentation d'encodeur peut décider de n'utiliser que des représentations qui font référence à la table statique ou utilisent des littéraux. Un encodeur de ce type n'a jamais besoin de bloquer un flux sur le traitement de la table dynamique et ne risque jamais de dépasser le nombre maximum de flux bloqués. Au détriment de l'efficacité de la compression, cette implémentation n'a pas non plus besoin de suivre le nombre de flux bloqués.
Notez que le choix d'insérer une entrée dans la table dynamique et les références à celle-ci dans les sections de champ encodées sont des décisions indépendantes. Un encodeur peut insérer une entrée dans la table dynamique en utilisant l'instruction « Insert With Name Reference » (voir la section 4.3.2) sans jamais faire référence à cette entrée.
2.1.3 Blocages de contrôle de flux (Avoiding Flow-Control Deadlocks)
Pour l'écriture sur le flux d'encodeur, un encodeur ne devrait pas attendre l'arrivée de données sur le flux de décodeur avant d'écrire. Si un implémentation fait cela, il existe un potentiel de blocage. Un décodeur effectue un débit proportionnel de crédits de contrôle de flux à l'encodeur, de sorte que si l'encodeur écrit en excès, le décodeur peut bloquer jusqu'à ce qu'il puisse lire certaines données du flux d'encodeur. Cependant, si l'encodeur attend que le décodeur envoie un accusé de réception sur le flux de décodeur avant d'écrire quoi que ce soit d'autre sur le flux d'encodeur, un blocage peut se produire : le décodeur ne peut pas envoyer l'accusé de réception car il est bloqué en attendant que le crédit de contrôle de flux soit débloqué, et l'encodeur n'écrira rien qui débloquerait le crédit de contrôle de flux car il attend l'accusé de réception du décodeur.
Pour plus d'informations sur le contrôle de flux dans QUIC, voir la section 4 de [QUIC-TRANSPORT].
2.1.4 Comptage des réceptions connues (Known Received Count)
Pour identifier quelle table dynamique l'encodeur pense que le décodeur possède, les sections de champ encodées (voir la section 4.5.1) contiennent un comptage d'insertions requis. Le comptage d'insertions requis indique le plus grand index absolu utilisé pour référencer la table dynamique dans la section de champ encodée, encodé en tant qu'entier. En utilisant le comptage d'insertions requis et le nombre d'entrées de table qui ont été évincées depuis qu'elles ont été reconnues, les instructions sur les flux d'encodeur et de décodeur peuvent utiliser des index relatifs ou post-Base pour identifier les entrées de table (voir la section 3.2.5 et la section 3.2.6).
Le décodeur envoie un accusé de réception de section (voir la section 4.4.1) après avoir terminé le traitement d'une section de champ encodée. Cet accusé de réception est reçu par l'encodeur après un délai d'aller-retour. Le comptage d'insertions requis maximal reconnu est l'indice absolu le plus élevé pour lequel un accusé de réception de section a été reçu, plus un ; cet indice est également appelé le « comptage des réceptions connues » ou « Known Received Count » du décodeur.
2.2 Décodeur (Decoder)
Comme dans HPACK, le décodeur traite une série de représentations et les ajoute à une liste de lignes de champ. Les représentations qui sont littérales ajoutent une ligne de champ avec un nom et une valeur statiquement fournis. Les représentations qui sont indexées ajoutent une ligne de champ en prenant le nom et la valeur depuis une entrée de la table statique ou dynamique.
Le décodeur reçoit des instructions d'encodeur sur le flux d'encodeur. Lorsqu'une instruction d'encodeur insère une nouvelle entrée dans la table dynamique, le décodeur crée une entrée correspondante dans sa propre table dynamique (voir la section 4.3.1). Cette entrée est alors disponible pour être référencée par les sections de champ encodées.
2.2.1 Flux bloqués (Blocked Streams)
Lors du traitement d'une section de champ encodée, le décodeur peut rencontrer une référence à une entrée de table dynamique qui n'a pas encore été reçue. Dans ce cas, le décodeur suspend le décodage de la section de champ, ce qui bloque le flux associé. Le décodeur annule le blocage du flux une fois que toutes les entrées requises de la table dynamique ont été reçues et insérées dans la table dynamique.
Le décodeur DOIT PAS débloquer un flux avant d'avoir reçu toutes les entrées de table dynamique qui sont référencées dans les lignes de champ encodées.
Étant donné la livraison non ordonnée et le risque de blocage du flux d'encodeur, les implementations de décodeur devraient adopter une stratégie de lecture équitable entre tous les flux, y compris les flux d'encodeur et de décodeur. Si un flux ne peut pas être traité en raison d'un blocage, le décodeur devrait continuer à lire sur d'autres flux disponibles, y compris le flux d'encodeur, pour débloquer le flux bloqué plus rapidement.
2.2.2 État de référence de l'encodeur (State Synchronization)
Le décodeur DOIT informer l'encodeur de toutes les nouvelles entrées de table qui sont référencées par les sections de champ décods. Cela garantit que l'encodeur sait quelles références de la table l'état du décodeur pourrait soutenir. Sans cette information, l'encodeur pourrait faire référence à une entrée qui a déjà été évincée par le décodeur, ce qui entraînerait un échec de décodage (voir la section 3.2.4).
Le décodeur envoie un accusé de réception de section après avoir terminé le traitement d'une section de champ encodée (voir la section 4.4.1). Cet accusé de réception informe l'encodeur que toutes les entrées de table dynamique référencées par la section de champ précédemment reçue ont été reçues.
Lorsqu'il envoie un accusé de réception de section, le décodeur choisit une valeur de base pour l'encodage (voir la section 4.5.1) qui indique les entrées insérées qui ont été référencées dans la section de champ. Le décodeur DOIT envoyer cet accusé de réception sur le flux de décodeur (voir la section 4.2) avant de renvoyer les lignes de champ décodées à l'application HTTP/3.
Certaines références à la table dynamique pourraient ne pas générer d'accusé de réception de section supplémentaire si une section de champ codée précédemment décods référençait déjà les mêmes entrées. L'encodeur DOIT vérifier que l'entrée de table la plus récente référencée a été reconnue par un accusé de réception de section avant de l'évincer de sa table dynamique, même si le décodeur ne l'utilise plus.
Le décodeur peut également choisir de ne pas envoyer un accusé de réception de section si cela ne changerait pas l'état connu de l'encodeur (c'est-à-dire, si toutes les références ont déjà été reconnues).
Si un flux est réinitialisé ou abandonné, alors le décodeur envoie une instruction d'annulation de flux (voir la section 4.4.2) au lieu d'un accusé de réception de section. Cela informe l'encodeur que la section de champ associée a été ignorée et permet à l'encodeur d'arrêter de suivre l'état de ce flux.