2. Übersicht des Komprimierungsprozesses (Compression Process Overview)
Wie HPACK verwendet QPACK zwei Tabellen, um Feldzeilen mit Indizes zu verknüpfen. Die statische Tabelle (siehe Abschnitt 3.1) ist vordefiniert und enthält gemeinsame Feldzeilen (einige mit Werten, andere ohne). Die dynamische Tabelle (siehe Abschnitt 3.2) wird dynamisch aufgebaut und ermöglicht eine effiziente Kodierung sich wiederholender Felder in kodierten Feldabschnitten.
QPACK definiert unidirektionale Streams zum Ändern und Verfolgen des Zustands der dynamischen Tabelle, ohne die Kodierung oder Dekodierung von Feldabschnitten zu blockieren. Ein Encoder-Stream trägt Tabellenänderungen vom Encoder zum Decoder, während ein Decoder-Stream Bestätigungen von Tabellenänderungen und Tabellenverwendungen vom Decoder zum Encoder trägt.
QPACK bietet Modellierungsanweisungen für kodierte Feldabschnitte, die es dem Decoder ermöglichen, die Blockierung aufzuheben, bevor der erforderliche dynamische Tabellenzustand verfügbar ist. Dies kann aufgrund ungeordneter Zustellung auftreten, wenn Streams multiplext werden.
2.1 Encoder
Ein Encoder wandelt einen Feldabschnitt in eine Reihe von Darstellungen um, indem er sie mit Einträgen in den statischen oder dynamischen Tabellen abgleicht. Referenzinformationen über den Feldzeilennamen hinaus können kodiert werden, indem auf einen Eintrag für eine Feldzeile mit übereinstimmendem Namen und Wert verwiesen wird (siehe Abschnitt 3.2.3 von [RFC7541]) oder indem auf einen Eintrag für eine Feldzeile mit übereinstimmendem Namen und unterschiedlichem Wert verwiesen wird (siehe Abschnitt 3.2.4 von [RFC7541]).
Ein Encoder DARF NICHT auf eine statische Feldzeile unter Verwendung eines Post-Base-Literalindex verweisen (siehe Abschnitt 4.5.5), und ein Decoder MUSS den Empfang einer Post-Base-Anweisung mit einem Index, der auf die statische Tabelle verweist, als Stream-Fehler vom Typ QPACK_DECOMPRESSION_FAILED behandeln.
2.1.1 Grenzen für Einfügungen in die dynamische Tabelle (Limits on Dynamic Table Insertions)
Der Encoder begrenzt die maximale Anzahl von Einträgen in der dynamischen Tabelle unter Verwendung von Signalen vom Decoder (siehe Abschnitt 4.4.1). Ein Encoder DARF NICHT einen Eintrag in die dynamische Tabelle einfügen (oder einen vorhandenen Eintrag duplizieren), wenn dies dazu führen würde, dass die dynamische Tabelle ihre maximale Kapazität überschreitet. Um Platz in der dynamischen Tabelle für zukünftige Einfügungen zu bewahren, könnte ein Encoder wählen, doppelte Einträge zu löschen, indem er eine Kapazität festlegt, die unter der maximalen Kapazität liegt (siehe Abschnitt 3.2.3).
Ein Encoder DARF NICHT die Kapazität der dynamischen Tabelle auf einen Wert setzen, der größer ist als die maximale Kapazität, die der Decoder signalisiert hat. In HTTP/3 ist dieses Maximum der Wert des SETTINGS_QPACK_MAX_TABLE_CAPACITY-Parameters (siehe Abschnitt 5), der vom Decoder empfangen wurde. Ein Encoder DARF NICHT die Kapazität der dynamischen Tabelle auf einen Wert setzen, der größer ist als dieses Maximum, selbst wenn der Decoder anschließend einen größeren Parameter sendet.
Für jeden kodierten Feldabschnitt, wenn der zuletzt in die dynamische Tabelle eingefügte Eintrag noch nicht entfernbar ist, MUSS der Encoder einen erforderlichen Einfügungszähler (siehe Abschnitt 4.5.1) und eine Einfügungszähler-Inkrementanweisung (siehe Abschnitt 4.4.3) einfügen. Der Decoder vergleicht den aktuellen Zustand seiner dynamischen Tabelle mit dem Zustand der dynamischen Tabelle, der zum Dekodieren des Feldabschnitts erforderlich ist. Wenn er nicht ausreichend ist, kann der Decoder die Verarbeitung des Abschnitts blockieren, bis die Synchronisation erreicht ist (siehe Abschnitt 2.2).
2.1.2 Blockierte Streams (Blocked Streams)
Für eine Darstellung, die auf die dynamische Tabelle verweist, wenn der Decoder die Verarbeitung des Feldabschnitts blockieren könnte, MUSS der Encoder sicherstellen, dass die Anzahl der blockierten Streams das vom Decoder zugelassene Maximum nicht überschreitet. Ein Stream gilt als blockiert, wenn der größte referenzierte dynamische Tabelleneintrag noch nicht empfangen wurde. Beachten Sie, dass eine Darstellung in beliebiger Reihenfolge auf Einträge verweisen kann, und Encoder verwenden im Allgemeinen relative Indizes (siehe Abschnitt 3.2.5); daher kann ein Stream, selbst wenn der Feldabschnitt auf einen älteren Eintrag verweist, dennoch blockiert sein.
Ein Encoder KANN wählen, Darstellungen zu verwenden, die nicht auf die dynamische Tabelle verweisen, wenn dies die Blockierung eines Streams vermeiden würde.
Beim Kodieren eines Feldabschnitts auf einem Stream, von dem der Encoder erwartet, dass er blockiert wird, wählt der Encoder einen Basiswert (siehe Abschnitt 4.5.1) so, dass der erforderliche Einfügungszähler erreicht wird oder bald erreicht wird. Wenn der erforderliche Einfügungszähler die Anzahl der derzeit in der dynamischen Tabelle vorhandenen Einträge überschreitet, blockiert der Feldabschnitt, bis die fehlenden Einträge vom Decoder in die dynamische Tabelle eingefügt wurden (siehe Abschnitt 4.4.3).
Eine Encoder-Implementierung kann entscheiden, nur Darstellungen zu verwenden, die auf die statische Tabelle verweisen oder Literale verwenden. Ein solcher Encoder muss niemals einen Stream bei der Verarbeitung der dynamischen Tabelle blockieren und läuft niemals Gefahr, die maximale Anzahl blockierter Streams zu überschreiten. Auf Kosten der Komprimierungseffizienz muss diese Implementierung auch nicht die Anzahl der blockierten Streams verfolgen.
Beachten Sie, dass die Entscheidung, einen Eintrag in die dynamische Tabelle einzufügen, und die Verweise darauf in kodierten Feldabschnitten unabhängige Entscheidungen sind. Ein Encoder kann einen Eintrag in die dynamische Tabelle einfügen, indem er die "Insert With Name Reference"-Anweisung verwendet (siehe Abschnitt 4.3.2), ohne jemals auf diesen Eintrag zu verweisen.
2.1.3 Vermeidung von Flusskontroll-Deadlocks (Avoiding Flow-Control Deadlocks)
Beim Schreiben auf den Encoder-Stream sollte ein Encoder nicht auf das Eintreffen von Daten auf dem Decoder-Stream warten, bevor er schreibt. Wenn eine Implementierung dies tut, besteht Deadlock-Potenzial. Ein Decoder gewährt proportionale Flusskontroll-Credits zum Encoder, sodass, wenn der Encoder übermäßig schreibt, der Decoder blockieren kann, bis er einige Daten vom Encoder-Stream lesen kann. Wenn der Encoder jedoch wartet, bis der Decoder eine Bestätigung auf dem Decoder-Stream sendet, bevor er etwas anderes auf den Encoder-Stream schreibt, kann ein Deadlock auftreten: Der Decoder kann die Bestätigung nicht senden, weil er blockiert ist und auf die Freigabe der Flusskontroll-Credits wartet, und der Encoder wird nichts schreiben, was die Flusskontroll-Credits freigeben würde, weil er auf die Bestätigung vom Decoder wartet.
Weitere Informationen zur Flusskontrolle in QUIC finden Sie in Abschnitt 4 von [QUIC-TRANSPORT].
2.1.4 Bekannter Empfangszähler (Known Received Count)
Um zu identifizieren, welche dynamische Tabelle der Encoder denkt, dass der Decoder besitzt, enthalten kodierte Feldabschnitte (siehe Abschnitt 4.5.1) einen erforderlichen Einfügungszähler. Der erforderliche Einfügungszähler zeigt den größten absoluten Index an, der verwendet wird, um auf die dynamische Tabelle im kodierten Feldabschnitt zu verweisen, kodiert als Integer. Unter Verwendung des erforderlichen Einfügungszählers und der Anzahl der Tabelleneinträge, die seit ihrer Bestätigung entfernt wurden, können Anweisungen auf den Encoder- und Decoder-Streams relative oder Post-Base-Indizes verwenden, um Tabelleneinträge zu identifizieren (siehe Abschnitt 3.2.5 und Abschnitt 3.2.6).
Der Decoder sendet eine Abschnittsbestätigung (siehe Abschnitt 4.4.1), nachdem er die Verarbeitung eines kodierten Feldabschnitts abgeschlossen hat. Diese Bestätigung wird vom Encoder nach einer Round-Trip-Verzögerung empfangen. Der maximal bestätigte erforderliche Einfügungszähler ist der höchste absolute Index, für den eine Abschnittsbestätigung empfangen wurde, plus eins; dieser Index wird auch als "bekannter Empfangszähler" oder "Known Received Count" des Decoders bezeichnet.
2.2 Decoder
Wie in HPACK verarbeitet der Decoder eine Reihe von Darstellungen und fügt sie einer Liste von Feldzeilen hinzu. Darstellungen, die Literale sind, fügen eine Feldzeile mit einem statisch bereitgestellten Namen und Wert hinzu. Darstellungen, die indiziert sind, fügen eine Feldzeile hinzu, indem sie den Namen und Wert aus einem Eintrag der statischen oder dynamischen Tabelle nehmen.
Der Decoder empfängt Encoder-Anweisungen auf dem Encoder-Stream. Wenn eine Encoder-Anweisung einen neuen Eintrag in die dynamische Tabelle einfügt, erstellt der Decoder einen entsprechenden Eintrag in seiner eigenen dynamischen Tabelle (siehe Abschnitt 4.3.1). Dieser Eintrag ist dann verfügbar, um von kodierten Feldabschnitten referenziert zu werden.
2.2.1 Blockierte Streams (Blocked Streams)
Beim Verarbeiten eines kodierten Feldabschnitts kann der Decoder auf eine Referenz zu einem dynamischen Tabelleneintrag stoßen, der noch nicht empfangen wurde. In diesem Fall unterbricht der Decoder die Dekodierung des Feldabschnitts, was den zugehörigen Stream blockiert. Der Decoder hebt die Blockierung des Streams auf, sobald alle erforderlichen dynamischen Tabelleneinträge empfangen und in die dynamische Tabelle eingefügt wurden.
Der Decoder DARF NICHT einen Stream entsperren, bevor er alle dynamischen Tabelleneinträge empfangen hat, auf die in den kodierten Feldzeilen verwiesen wird.
Angesichts der ungeordneten Zustellung und des Risikos, dass der Encoder-Stream blockiert wird, sollten Decoder-Implementierungen eine faire Lesestrategie zwischen allen Streams übernehmen, einschließlich der Encoder- und Decoder-Streams. Wenn ein Stream aufgrund einer Blockierung nicht verarbeitet werden kann, sollte der Decoder weiterhin von anderen verfügbaren Streams lesen, einschließlich des Encoder-Streams, um den blockierten Stream schneller zu entsperren.
2.2.2 Zustandssynchronisation (State Synchronization)
Der Decoder MUSS den Encoder über alle neuen Tabelleneinträge informieren, auf die von dekodierten Feldabschnitten verwiesen wird. Dies stellt sicher, dass der Encoder weiß, welche Verweise auf die Tabelle der Zustand des Decoders unterstützen könnte. Ohne diese Information könnte der Encoder auf einen Eintrag verweisen, der bereits vom Decoder entfernt wurde, was zu einem Dekodierungsfehler führen würde (siehe Abschnitt 3.2.4).
Der Decoder sendet eine Abschnittsbestätigung, nachdem er die Verarbeitung eines kodierten Feldabschnitts abgeschlossen hat (siehe Abschnitt 4.4.1). Diese Bestätigung informiert den Encoder, dass alle dynamischen Tabelleneinträge, auf die vom zuvor empfangenen Feldabschnitt verwiesen wurde, empfangen wurden.
Beim Senden einer Abschnittsbestätigung wählt der Decoder einen Basiswert für die Kodierung (siehe Abschnitt 4.5.1), der die eingefügten Einträge anzeigt, auf die im Feldabschnitt verwiesen wurde. Der Decoder MUSS diese Bestätigung auf dem Decoder-Stream senden (siehe Abschnitt 4.2), bevor er die dekodierten Feldzeilen an die HTTP/3-Anwendung zurückgibt.
Einige Verweise auf die dynamische Tabelle erzeugen möglicherweise keine zusätzliche Abschnittsbestätigung, wenn ein zuvor dekodierter kodierter Feldabschnitt bereits auf dieselben Einträge verwiesen hat. Der Encoder MUSS überprüfen, dass der zuletzt referenzierte Tabelleneintrag durch eine Abschnittsbestätigung bestätigt wurde, bevor er ihn aus seiner dynamischen Tabelle entfernt, selbst wenn der Decoder ihn nicht mehr verwendet.
Der Decoder kann auch wählen, keine Abschnittsbestätigung zu senden, wenn dies den bekannten Zustand des Encoders nicht ändern würde (d. h. wenn alle Verweise bereits bestätigt wurden).
Wenn ein Stream zurückgesetzt oder abgebrochen wird, sendet der Decoder stattdessen eine Stream-Abbruchanweisung (siehe Abschnitt 4.4.2) anstelle einer Abschnittsbestätigung. Dies informiert den Encoder, dass der zugehörige Feldabschnitt ignoriert wurde, und ermöglicht es dem Encoder, die Verfolgung des Zustands dieses Streams zu beenden.