4. Formato wire (Wire Format)
4.1 Primitive (Primitives)
4.1.1 Interi con prefisso (Prefixed Integers)
Questo documento fa ampio uso dell'intero con prefisso della sezione 5.1 di [RFC7541]. Il formato di [RFC7541] è usato senza modifiche. Si noti, tuttavia, che QPACK utilizza alcune dimensioni di prefisso non effettivamente utilizzate in HPACK.
Le implementazioni QPACK DEVONO essere in grado di decodificare interi fino a 62 bit inclusi.
4.1.2 Letterali di stringa (String Literals)
Il letterale di stringa definito dalla sezione 5.2 di [RFC7541] è anche usato in tutto questo documento. Questo formato di stringa include la codifica Huffman opzionale.
HPACK definisce i letterali di stringa per iniziare su un confine di byte. Iniziano con un singolo flag di bit, indicato in questo documento come 'H' (che indica se la stringa è codificata Huffman), seguito dalla lunghezza codificata come un intero con prefisso di 7 bit, e infine il numero indicato di byte di dati. Quando la codifica Huffman è abilitata, la tabella Huffman dell'Appendice B di [RFC7541] è usata senza modifiche, e la lunghezza indica la dimensione della stringa codificata.
Questo documento espande la definizione dei letterali di stringa permettendo loro di iniziare diversamente da un confine di byte. Un "letterale di stringa con prefisso di N bit" inizia a metà byte, con i primi (8-N) bit allocati a un campo precedente. La stringa usa un bit per il flag Huffman, seguito dalla lunghezza codificata come un intero con prefisso di (N-1) bit. Il valore della dimensione del prefisso N può essere tra 2 e 8 inclusi. Il resto del letterale di stringa è invariato.
Un letterale di stringa senza lunghezza di prefisso annotata è un letterale di stringa con prefisso di 8 bit e segue le definizioni in [RFC7541] senza modifiche.
4.2 Stream encoder e decoder (Encoder and Decoder Streams)
QPACK definisce due tipi di stream unidirezionali:
-
Stream encoder è uno stream unidirezionale di tipo 0x02. Trasporta una sequenza non incorniciata di istruzioni encoder dall'encoder al decoder.
-
Stream decoder è uno stream unidirezionale di tipo 0x03. Trasporta una sequenza non incorniciata di istruzioni decoder dal decoder all'encoder.
Gli endpoint HTTP/3 contengono un encoder e un decoder QPACK. Ogni endpoint DEVE iniziare al massimo uno stream encoder e al massimo uno stream decoder. La ricezione di una seconda istanza di entrambi i tipi di stream DEVE essere trattata come un errore di connessione di tipo H3_STREAM_CREATION_ERROR.
Il mittente NON DEVE chiudere nessuno di questi stream, e il ricevitore NON DEVE richiedere che il mittente chiuda nessuno di questi stream. La chiusura di uno dei due tipi di stream unidirezionali DEVE essere trattata come un errore di connessione di tipo H3_CLOSED_CRITICAL_STREAM.
Un endpoint PUÒ evitare di creare uno stream encoder se non verrà utilizzato (ad esempio, se il suo encoder non desidera utilizzare la tabella dinamica o se la dimensione massima della tabella dinamica consentita dal peer è zero).
Un endpoint PUÒ evitare di creare uno stream decoder se il decoder imposta la capacità massima della tabella dinamica a zero.
Un endpoint DEVE consentire al suo peer di creare uno stream encoder e uno stream decoder anche se le impostazioni della connessione ne impediscono l'uso.
4.3 Istruzioni encoder (Encoder Instructions)
Un encoder invia istruzioni encoder sullo stream encoder per impostare la capacità della tabella dinamica e aggiungere voci alla tabella dinamica. Le istruzioni che aggiungono voci di tabella possono utilizzare voci esistenti per evitare di trasmettere informazioni ridondanti. Il nome può essere trasmesso come riferimento a una voce esistente nella tabella statica o dinamica, o come letterale di stringa. Per le voci che esistono già nella tabella dinamica, la voce completa può anche essere usata per riferimento, creando una voce duplicata.
4.3.1 Imposta capacità tabella dinamica (Set Dynamic Table Capacity)
Un encoder utilizza un'istruzione che inizia con il pattern di 3 bit '001' per informare il decoder di un cambiamento nella capacità della tabella dinamica. Segue la nuova capacità della tabella dinamica rappresentata come un intero con un prefisso di 5 bit; vedere la sezione 4.1.1.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 1 | Capacity (5+) |
+---+---+---+-------------------+
Figura 5: Imposta capacità tabella dinamica
La nuova capacità DEVE essere inferiore o uguale al limite descritto nella sezione 3.2.3. In HTTP/3, questo limite è il valore del parametro SETTINGS_QPACK_MAX_TABLE_CAPACITY (sezione 5) ricevuto dal decoder. Il decoder DEVE trattare un nuovo valore di capacità della tabella dinamica che supera questo limite come un errore di connessione di tipo QPACK_ENCODER_STREAM_ERROR.
La riduzione della capacità della tabella dinamica può causare l'evacuazione delle voci; vedere la sezione 3.2.2. Questo NON DEVE causare l'evacuazione di voci che non sono evacuabili; vedere la sezione 2.1.1. La modifica della capacità della tabella dinamica non viene riconosciuta, poiché non inserisce una voce.
4.3.2 Inserisci con riferimento al nome (Insert with Name Reference)
Un encoder aggiunge una voce alla tabella dinamica dove il nome del campo corrisponde al nome del campo di una voce memorizzata nella tabella statica o dinamica utilizzando un'istruzione che inizia con il pattern di 1 bit '1'. Il secondo bit ('T') indica se il riferimento è alla tabella statica o dinamica. Il seguente intero con prefisso di 6 bit (sezione 4.1.1) è usato per localizzare la voce di tabella per il nome del campo. Quando T=1, il numero rappresenta l'indice della tabella statica; quando T=0, il numero è l'indice relativo della voce nella tabella dinamica.
Il riferimento al nome del campo è seguito dal valore del campo rappresentato come letterale di stringa; vedere la sezione 4.1.2.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 | T | Name Index (6+) |
+---+---+-----------------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+
Figura 6: Inserisci riga di campo -- Nome indicizzato
4.3.3 Inserisci con nome letterale (Insert with Literal Name)
Un encoder aggiunge una voce alla tabella dinamica dove sia il nome del campo che il valore del campo sono rappresentati come letterali di stringa utilizzando un'istruzione che inizia con il pattern di 2 bit '01'.
Segue il nome rappresentato come letterale di stringa con prefisso di 6 bit, e il valore rappresentato come letterale di stringa con prefisso di 8 bit; vedere la sezione 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) |
+-------------------------------+
Figura 7: Inserisci riga di campo -- Nuovo nome
4.3.4 Duplica (Duplicate)
Un encoder duplica una voce esistente nella tabella dinamica utilizzando un'istruzione che inizia con il pattern di 3 bit '000'. Segue l'indice relativo della voce esistente rappresentato come intero con un prefisso di 5 bit; vedere la sezione 4.1.1.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | Index (5+) |
+---+---+---+-------------------+
Figura 8: Duplica
La voce esistente viene reinserita nella tabella dinamica senza rinviare né il nome né il valore. Questo è utile per evitare di aggiungere un riferimento a una voce più vecchia, il che potrebbe bloccare l'inserimento di nuove voci.
4.4 Istruzioni decoder (Decoder Instructions)
Un decoder invia istruzioni decoder sullo stream decoder per informare l'encoder sull'elaborazione delle sezioni di campo e sugli aggiornamenti della tabella per garantire la coerenza della tabella dinamica.
4.4.1 Riconoscimento di sezione (Section Acknowledgment)
Dopo aver elaborato una sezione di campo codificata il cui conteggio di inserimento richiesto dichiarato non è zero, il decoder emette un'istruzione di riconoscimento di sezione. L'istruzione inizia con il pattern di 1 bit '1', seguito dall'ID stream associato della sezione di campo codificato come intero con prefisso di 7 bit; vedere la sezione 4.1.1.
Questa istruzione è usata come descritto nelle sezioni 2.1.4 e 2.2.2.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 | Stream ID (7+) |
+---+---------------------------+
Figura 9: Riconoscimento di sezione
Se un encoder riceve un'istruzione di riconoscimento di sezione che fa riferimento a uno stream su cui ogni sezione di campo codificata con un conteggio di inserimento richiesto non zero è già stata riconosciuta, questo DEVE essere trattato come un errore di connessione di tipo QPACK_DECODER_STREAM_ERROR.
L'istruzione di riconoscimento di sezione può aumentare il conteggio ricevuto noto; vedere la sezione 2.1.4.
4.4.2 Cancellazione stream (Stream Cancellation)
Quando uno stream viene resettato o la lettura viene abbandonata, il decoder emette un'istruzione di cancellazione stream. L'istruzione inizia con il pattern di 2 bit '01', seguito dall'ID stream dello stream interessato codificato come intero con prefisso di 6 bit.
Questa istruzione è usata come descritto nella sezione 2.2.2.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 1 | Stream ID (6+) |
+---+---+-----------------------+
Figura 10: Cancellazione stream
4.4.3 Incremento conteggio inserimenti (Insert Count Increment)
L'istruzione di incremento conteggio inserimenti inizia con il pattern di 2 bit '00', seguito dall'incremento codificato come intero con prefisso di 6 bit. Questa istruzione aumenta il conteggio ricevuto noto (sezione 2.1.4) del valore del parametro incremento. Il decoder dovrebbe inviare un valore di incremento che aumenta il conteggio ricevuto noto al numero totale di inserimenti e duplicazioni della tabella dinamica elaborate finora.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | Increment (6+) |
+---+---+-----------------------+
Figura 11: Incremento conteggio inserimenti
Un encoder che riceve un campo incremento uguale a zero o uno che aumenta il conteggio ricevuto noto oltre ciò che l'encoder ha inviato DEVE trattare questo come un errore di connessione di tipo QPACK_DECODER_STREAM_ERROR.
4.5 Rappresentazioni di righe di campo (Field Line Representations)
Una sezione di campo codificata consiste in un prefisso e una sequenza possibilmente vuota di rappresentazioni definite in questa sezione. Ogni rappresentazione corrisponde a una singola riga di campo. Queste rappresentazioni fanno riferimento alla tabella statica o alla tabella dinamica in uno stato particolare, ma non modificano quello stato.
Una sezione di campo codificata è trasportata in un frame su uno stream definito dal protocollo circostante.
4.5.1 Prefisso sezione di campo codificata (Encoded Field Section Prefix)
Ogni sezione di campo codificata è preceduta da due interi. Il conteggio di inserimento richiesto è codificato come intero con un prefisso di 8 bit utilizzando la codifica descritta nella sezione 4.5.1.1. La Base è codificata come bit di segno ('S') e un valore Delta Base con un prefisso di 7 bit; vedere la sezione 4.5.1.2.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| Required Insert Count (8+) |
+---+---------------------------+
| S | Delta Base (7+) |
+---+---------------------------+
| Encoded Field Lines ...
+-------------------------------+
Figura 12: Sezione di campo codificata
4.5.1.1 Conteggio inserimento richiesto (Required Insert Count)
Il conteggio di inserimento richiesto identifica lo stato della tabella dinamica necessario per elaborare la sezione di campo codificata. I decoder bloccanti utilizzano il conteggio di inserimento richiesto per determinare quando è sicuro elaborare il resto della sezione di campo.
L'encoder trasforma il conteggio di inserimento richiesto come segue prima della codifica:
if ReqInsertCount == 0:
EncInsertCount = 0
else:
EncInsertCount = (ReqInsertCount mod (2 * MaxEntries)) + 1
Qui MaxEntries è il numero massimo di voci che la tabella dinamica può avere. La voce più piccola ha stringhe di nome e valore vuote e ha una dimensione di 32. Quindi, MaxEntries è calcolato come:
MaxEntries = floor( MaxTableCapacity / 32 )
MaxTableCapacity è la capacità massima della tabella dinamica come specificato dal decoder; vedere la sezione 3.2.3.
Questa codifica limita la lunghezza del prefisso su connessioni di lunga durata.
Il decoder può ricostruire il conteggio di inserimento richiesto utilizzando un algoritmo come il seguente. Se il decoder incontra un valore EncodedInsertCount che non avrebbe potuto essere prodotto da un encoder conforme, DEVE trattare questo come un errore di connessione di tipo QPACK_DECOMPRESSION_FAILED.
TotalNumberOfInserts è il numero totale di inserimenti nella tabella dinamica del decoder.
FullRange = 2 * MaxEntries
if EncodedInsertCount == 0:
ReqInsertCount = 0
else:
if EncodedInsertCount > FullRange:
Error
MaxValue = TotalNumberOfInserts + MaxEntries
# MaxWrapped è il valore più grande possibile di
# ReqInsertCount che è 0 mod 2 * MaxEntries
MaxWrapped = floor(MaxValue / FullRange) * FullRange
ReqInsertCount = MaxWrapped + EncodedInsertCount - 1
# Se ReqInsertCount supera MaxValue, il valore dell'encoder
# deve aver fatto il wrap una volta in meno
if ReqInsertCount > MaxValue:
if ReqInsertCount <= FullRange:
Error
ReqInsertCount -= FullRange
# Il valore di 0 deve essere codificato come 0
if ReqInsertCount == 0:
Error
Ad esempio, se la tabella dinamica è di 100 byte, allora il conteggio di inserimento richiesto sarà codificato modulo 6. Se un decoder ha ricevuto 10 inserimenti, allora un valore codificato di 4 indica che il conteggio di inserimento richiesto è 9 per la sezione di campo.
4.5.1.2 Base (Base)
La Base è usata per risolvere i riferimenti nella tabella dinamica come descritto nella sezione 3.2.5.
Per risparmiare spazio, la Base è codificata relativamente al conteggio di inserimento richiesto utilizzando un segno a un bit ('S' nella Figura 12) e il valore Delta Base. Un bit di segno di 0 indica che la Base è maggiore o uguale al valore del conteggio di inserimento richiesto; il decoder aggiunge il valore di Delta Base al conteggio di inserimento richiesto per determinare il valore della Base. Un bit di segno di 1 indica che la Base è minore del conteggio di inserimento richiesto; il decoder sottrae il valore di Delta Base dal conteggio di inserimento richiesto e sottrae anche uno per determinare il valore della Base. Cioè:
if Sign == 0:
Base = ReqInsertCount + DeltaBase
else:
Base = ReqInsertCount - DeltaBase - 1
Un encoder a singolo passaggio determina la Base prima di codificare una sezione di campo. Se l'encoder ha inserito voci nella tabella dinamica durante la codifica della sezione di campo e le sta referenziando, il conteggio di inserimento richiesto sarà maggiore della Base, quindi la differenza codificata è negativa e il bit di segno è impostato a 1. Se la sezione di campo non è stata codificata utilizzando rappresentazioni che fanno riferimento alla voce più recente nella tabella e non ha inserito nuove voci, la Base sarà maggiore del conteggio di inserimento richiesto, quindi la differenza codificata è positiva e il bit di segno è impostato a 0.
Il valore della Base NON DEVE essere negativo. Sebbene il protocollo funzionerebbe correttamente con una Base negativa e indicizzazione Post-Base, questo è inutilmente inefficiente. Se il valore del conteggio di inserimento richiesto è minore o uguale al valore della Delta Base, un endpoint DEVE trattare un blocco di campo con un bit di segno di 1 come non valido.
Un encoder che produce aggiornamenti di tabella prima di codificare una sezione di campo può impostare la Base al valore del conteggio di inserimento richiesto. In tal caso, sia il bit di segno che la Delta Base saranno impostati a zero.
Una sezione di campo che è stata codificata senza riferimenti alla tabella dinamica può usare qualsiasi valore per la Base; impostare Delta Base a zero è una delle codifiche più efficienti.
Ad esempio, con un conteggio di inserimento richiesto di 9, un bit di segno di 1 e una Delta Base di 2, la Base è 6 e abilita l'indicizzazione Post-Base per tre voci. In questo esempio, un indice relativo di 1 si riferisce alla quinta voce aggiunta alla tabella; un indice Post-Base di 1 si riferisce all'ottava voce.
4.5.2 Riga di campo indicizzata (Indexed Field Line)
Una rappresentazione di riga di campo indicizzata identifica una voce nella tabella statica o una voce nella tabella dinamica con un indice assoluto inferiore al valore della Base.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 | T | Index (6+) |
+---+---+-----------------------+
Figura 13: Riga di campo indicizzata
Questa rappresentazione inizia con il pattern di 1 bit '1', seguito dal bit 'T' che indica se il riferimento è alla tabella statica o dinamica. Il seguente intero con prefisso di 6 bit (sezione 4.1.1) è usato per localizzare la voce di tabella per la riga di campo. Quando T=1, il numero rappresenta l'indice della tabella statica; quando T=0, il numero è l'indice relativo della voce nella tabella dinamica.
4.5.3 Riga di campo indicizzata con indice Post-Base (Indexed Field Line with Post-Base Index)
Una rappresentazione di riga di campo indicizzata con indice Post-Base identifica una voce nella tabella dinamica con un indice assoluto maggiore o uguale al valore della Base.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 1 | Index (4+) |
+---+---+---+---+---------------+
Figura 14: Riga di campo indicizzata con indice Post-Base
Questa rappresentazione inizia con il pattern di 4 bit '0001'. Segue l'indice Post-Base (sezione 3.2.6) della riga di campo corrispondente, rappresentato come intero con un prefisso di 4 bit; vedere la sezione 4.1.1.
4.5.4 Riga di campo letterale con riferimento al nome (Literal Field Line with Name Reference)
Una rappresentazione di riga di campo letterale con riferimento al nome codifica una riga di campo dove il nome del campo corrisponde al nome del campo di una voce nella tabella statica o al nome del campo di una voce nella tabella dinamica con un indice assoluto inferiore al valore della Base.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 1 | N | T |Name Index (4+)|
+---+---+---+---+---------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+
Figura 15: Riga di campo letterale con riferimento al nome
Questa rappresentazione inizia con il pattern di 2 bit '01'. Il bit seguente, 'N', indica se un intermediario è autorizzato ad aggiungere questa riga di campo alla tabella dinamica sui salti successivi. Quando il bit 'N' è impostato, la riga di campo codificata DEVE sempre essere codificata con una rappresentazione letterale. In particolare, quando un peer invia una riga di campo che ha ricevuto rappresentata come riga di campo letterale con il bit 'N' impostato, DEVE usare una rappresentazione letterale per inoltrare questa riga di campo. Questo bit è inteso a proteggere i valori di campo che non devono essere messi a rischio comprimendoli; vedere la sezione 7.1 per maggiori dettagli.
Il quarto bit ('T') indica se il riferimento è alla tabella statica o dinamica. Il seguente intero con prefisso di 4 bit (sezione 4.1.1) è usato per localizzare la voce di tabella per il nome del campo. Quando T=1, il numero rappresenta l'indice della tabella statica; quando T=0, il numero è l'indice relativo della voce nella tabella dinamica.
Solo il nome del campo è preso dalla voce della tabella dinamica; il valore del campo è codificato come letterale di stringa con prefisso di 8 bit; vedere la sezione 4.1.2.
4.5.5 Riga di campo letterale con riferimento al nome Post-Base (Literal Field Line with Post-Base Name Reference)
Una rappresentazione di riga di campo letterale con riferimento al nome Post-Base codifica una riga di campo dove il nome del campo corrisponde al nome del campo di una voce della tabella dinamica con un indice assoluto maggiore o uguale al valore della Base.
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | N |NameIdx(3+)|
+---+---+---+---+---+-----------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length bytes) |
+-------------------------------+
Figura 16: Riga di campo letterale con riferimento al nome Post-Base
Questa rappresentazione inizia con il pattern di 4 bit '0000'. Il quinto bit è il bit 'N' come descritto nella sezione 4.5.4. Segue un indice Post-Base (sezione 3.2.6) della voce della tabella dinamica, codificato come intero con un prefisso di 3 bit; vedere la sezione 4.1.1.
Solo il nome del campo è preso dalla voce della tabella dinamica; il valore del campo è codificato come letterale di stringa con prefisso di 8 bit; vedere la sezione 4.1.2.
4.5.6 Riga di campo letterale con nome letterale (Literal Field Line with Literal Name)
Una rappresentazione di riga di campo letterale con nome letterale codifica un nome di campo e un valore di campo come letterali di stringa.
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) |
+-------------------------------+
Figura 17: Riga di campo letterale con nome letterale
Questa rappresentazione inizia con il pattern di 3 bit '001'. Il quarto bit è il bit 'N' come descritto nella sezione 4.5.4. Il nome segue, rappresentato come letterale di stringa con prefisso di 4 bit, quindi il valore, rappresentato come letterale di stringa con prefisso di 8 bit; vedere la sezione 4.1.2.