Passa al contenuto principale

2. Panoramica del processo di compressione (Compression Process Overview)

Come HPACK, QPACK utilizza due tabelle per associare le righe di campo agli indici. La tabella statica (vedere la sezione 3.1) è predefinita e contiene righe di campo comuni (alcune con valori, altre senza). La tabella dinamica (vedere la sezione 3.2) è costruita dinamicamente e consente una codifica efficiente dei campi ripetuti nelle sezioni di campo codificate.

QPACK definisce stream unidirezionali per modificare e tracciare lo stato della tabella dinamica senza bloccare la codifica o la decodifica delle sezioni di campo. Uno stream encoder trasporta le modifiche della tabella dall'encoder al decoder, mentre uno stream decoder trasporta gli acknowledgment delle modifiche della tabella e degli utilizzi della tabella dal decoder all'encoder.

QPACK fornisce istruzioni di modellazione per le sezioni di campo codificate che consentono al decoder di sbloccarsi prima che lo stato della tabella dinamica richiesto sia disponibile. Ciò può verificarsi a causa della consegna non ordinata quando gli stream sono multiplexati.

2.1 Encoder

Un encoder converte una sezione di campo in una serie di rappresentazioni abbinandole alle voci nelle tabelle statiche o dinamiche. Le informazioni di riferimento oltre il nome della riga di campo possono essere codificate facendo riferimento a una voce per una riga di campo con un nome e un valore corrispondenti (vedere la sezione 3.2.3 di [RFC7541]) o facendo riferimento a una voce per una riga di campo con un nome corrispondente e un valore diverso (vedere la sezione 3.2.4 di [RFC7541]).

Un encoder NON DEVE fare riferimento a una riga di campo statica utilizzando un indice letterale Post-Base (vedere la sezione 4.5.5), e un decoder DEVE trattare la ricezione di un'istruzione Post-Base con un indice che fa riferimento alla tabella statica come un errore di stream di tipo QPACK_DECOMPRESSION_FAILED.

2.1.1 Limiti sugli inserimenti nella tabella dinamica (Limits on Dynamic Table Insertions)

L'encoder limita il numero massimo di voci nella tabella dinamica utilizzando i segnali del decoder (vedere la sezione 4.4.1). Un encoder NON DEVE inserire una voce nella tabella dinamica (o duplicare una voce esistente) se ciò causerebbe il superamento della capacità massima da parte della tabella dinamica. Per preservare lo spazio della tabella dinamica per inserimenti futuri, un encoder potrebbe scegliere di eliminare le voci duplicate impostando una capacità inferiore alla capacità massima (vedere la sezione 3.2.3).

Un encoder NON DEVE impostare la capacità della tabella dinamica su un valore maggiore della capacità massima segnalata dal decoder. In HTTP/3, questo massimo è il valore del parametro SETTINGS_QPACK_MAX_TABLE_CAPACITY (vedere la sezione 5) ricevuto dal decoder. Un encoder NON DEVE impostare la capacità della tabella dinamica su un valore maggiore di questo massimo anche se il decoder invia successivamente un parametro più grande.

Per ogni sezione di campo codificata, se la voce inserita più di recente nella tabella dinamica non è ancora evictable, l'encoder DEVE inserire un conteggio di inserimento richiesto (vedere la sezione 4.5.1) e un'istruzione di incremento del conteggio di inserimento (vedere la sezione 4.4.3). Il decoder confronta lo stato attuale della sua tabella dinamica con lo stato della tabella dinamica necessario per decodificare la sezione di campo. Se non è sufficiente, il decoder può bloccare l'elaborazione della sezione fino al raggiungimento della sincronizzazione (vedere la sezione 2.2).

2.1.2 Stream bloccati (Blocked Streams)

Per una rappresentazione che fa riferimento alla tabella dinamica, se il decoder potrebbe bloccare l'elaborazione della sezione di campo, l'encoder DEVE assicurarsi che il numero di stream bloccati non superi il massimo consentito dal decoder. Uno stream è considerato bloccato se la voce della tabella dinamica più grande referenziata non è ancora stata ricevuta. Si noti che una rappresentazione può fare riferimento alle voci in qualsiasi ordine e gli encoder generalmente utilizzano indici relativi (vedere la sezione 3.2.5); pertanto, anche se la sezione di campo fa riferimento a una voce più vecchia, potrebbe comunque essere bloccata.

Un encoder PUÒ scegliere di utilizzare rappresentazioni che non fanno riferimento alla tabella dinamica se ciò eviterebbe di bloccare uno stream.

Quando si codifica una sezione di campo su uno stream che l'encoder si aspetta sia bloccato, l'encoder sceglie un valore Base (vedere la sezione 4.5.1) tale che il conteggio di inserimento richiesto venga raggiunto o verrà raggiunto presto. Se il conteggio di inserimento richiesto supera il numero di voci attualmente presenti nella tabella dinamica, la sezione di campo si blocca fino a quando le voci mancanti non vengono inserite nella tabella dinamica dal decoder (vedere la sezione 4.4.3).

Un'implementazione dell'encoder può decidere di utilizzare solo rappresentazioni che fanno riferimento alla tabella statica o utilizzano letterali. Un tale encoder non deve mai bloccare uno stream sull'elaborazione della tabella dinamica e non rischia mai di superare il numero massimo di stream bloccati. A scapito dell'efficienza di compressione, questa implementazione non deve nemmeno tenere traccia del numero di stream bloccati.

Si noti che la scelta di inserire una voce nella tabella dinamica e i riferimenti ad essa nelle sezioni di campo codificate sono decisioni indipendenti. Un encoder può inserire una voce nella tabella dinamica utilizzando l'istruzione "Insert With Name Reference" (vedere la sezione 4.3.2) senza mai fare riferimento a quella voce.

2.1.3 Evitare deadlock del controllo di flusso (Avoiding Flow-Control Deadlocks)

Per la scrittura sullo stream encoder, un encoder non dovrebbe attendere l'arrivo di dati sullo stream decoder prima di scrivere. Se un'implementazione fa questo, esiste un potenziale di deadlock. Un decoder concede crediti di controllo di flusso proporzionali all'encoder, quindi se l'encoder scrive in eccesso, il decoder può bloccarsi fino a quando non può leggere alcuni dati dallo stream encoder. Tuttavia, se l'encoder attende che il decoder invii un acknowledgment sullo stream decoder prima di scrivere qualsiasi altra cosa sullo stream encoder, può verificarsi un deadlock: il decoder non può inviare l'acknowledgment perché è bloccato in attesa che il credito di controllo di flusso venga sbloccato, e l'encoder non scriverà nulla che sbloccherebbe il credito di controllo di flusso perché sta aspettando l'acknowledgment dal decoder.

Per ulteriori informazioni sul controllo di flusso in QUIC, vedere la sezione 4 di [QUIC-TRANSPORT].

2.1.4 Conteggio ricevuto noto (Known Received Count)

Per identificare quale tabella dinamica l'encoder pensa che il decoder possieda, le sezioni di campo codificate (vedere la sezione 4.5.1) contengono un conteggio di inserimento richiesto. Il conteggio di inserimento richiesto indica l'indice assoluto più grande utilizzato per fare riferimento alla tabella dinamica nella sezione di campo codificata, codificato come intero. Utilizzando il conteggio di inserimento richiesto e il numero di voci di tabella che sono state evacuate da quando sono state riconosciute, le istruzioni sugli stream encoder e decoder possono utilizzare indici relativi o Post-Base per identificare le voci di tabella (vedere la sezione 3.2.5 e la sezione 3.2.6).

Il decoder invia un acknowledgment di sezione (vedere la sezione 4.4.1) dopo aver completato l'elaborazione di una sezione di campo codificata. Questo acknowledgment viene ricevuto dall'encoder dopo un ritardo di andata e ritorno. Il conteggio di inserimento richiesto massimo riconosciuto è l'indice assoluto più alto per il quale è stato ricevuto un acknowledgment di sezione, più uno; questo indice è anche chiamato "conteggio ricevuto noto" o "Known Received Count" del decoder.

2.2 Decoder

Come in HPACK, il decoder elabora una serie di rappresentazioni e le aggiunge a un elenco di righe di campo. Le rappresentazioni che sono letterali aggiungono una riga di campo con un nome e un valore forniti staticamente. Le rappresentazioni che sono indicizzate aggiungono una riga di campo prendendo il nome e il valore da una voce della tabella statica o dinamica.

Il decoder riceve istruzioni encoder sullo stream encoder. Quando un'istruzione encoder inserisce una nuova voce nella tabella dinamica, il decoder crea una voce corrispondente nella propria tabella dinamica (vedere la sezione 4.3.1). Questa voce è quindi disponibile per essere referenziata dalle sezioni di campo codificate.

2.2.1 Stream bloccati (Blocked Streams)

Quando si elabora una sezione di campo codificata, il decoder può incontrare un riferimento a una voce della tabella dinamica che non è stata ancora ricevuta. In questo caso, il decoder sospende la decodifica della sezione di campo, il che blocca lo stream associato. Il decoder sblocca lo stream una volta che tutte le voci della tabella dinamica richieste sono state ricevute e inserite nella tabella dinamica.

Il decoder NON DEVE sbloccare uno stream prima di aver ricevuto tutte le voci della tabella dinamica a cui si fa riferimento nelle righe di campo codificate.

Data la consegna non ordinata e il rischio di blocco dello stream encoder, le implementazioni del decoder dovrebbero adottare una strategia di lettura equa tra tutti gli stream, inclusi gli stream encoder e decoder. Se uno stream non può essere elaborato a causa di un blocco, il decoder dovrebbe continuare a leggere da altri stream disponibili, incluso lo stream encoder, per sbloccare lo stream bloccato più rapidamente.

2.2.2 Sincronizzazione dello stato (State Synchronization)

Il decoder DEVE informare l'encoder di tutte le nuove voci di tabella a cui si fa riferimento dalle sezioni di campo decodificate. Ciò garantisce che l'encoder sappia quali riferimenti alla tabella lo stato del decoder potrebbe supportare. Senza queste informazioni, l'encoder potrebbe fare riferimento a una voce che è già stata evacuata dal decoder, il che porterebbe a un errore di decodifica (vedere la sezione 3.2.4).

Il decoder invia un acknowledgment di sezione dopo aver completato l'elaborazione di una sezione di campo codificata (vedere la sezione 4.4.1). Questo acknowledgment informa l'encoder che tutte le voci della tabella dinamica referenziate dalla sezione di campo precedentemente ricevuta sono state ricevute.

Quando si invia un acknowledgment di sezione, il decoder sceglie un valore Base per la codifica (vedere la sezione 4.5.1) che indica le voci inserite a cui si è fatto riferimento nella sezione di campo. Il decoder DEVE inviare questo acknowledgment sullo stream decoder (vedere la sezione 4.2) prima di restituire le righe di campo decodificate all'applicazione HTTP/3.

Alcuni riferimenti alla tabella dinamica potrebbero non generare un acknowledgment di sezione aggiuntivo se una sezione di campo codificata precedentemente decodificata ha già fatto riferimento alle stesse voci. L'encoder DEVE verificare che la voce di tabella più recente referenziata sia stata riconosciuta da un acknowledgment di sezione prima di evacuarla dalla sua tabella dinamica, anche se il decoder non la sta più utilizzando.

Il decoder può anche scegliere di non inviare un acknowledgment di sezione se ciò non cambierebbe lo stato noto dell'encoder (cioè, se tutti i riferimenti sono già stati riconosciuti).

Se uno stream viene resettato o abbandonato, il decoder invia un'istruzione di cancellazione dello stream (vedere la sezione 4.4.2) invece di un acknowledgment di sezione. Ciò informa l'encoder che la sezione di campo associata è stata ignorata e consente all'encoder di interrompere il tracciamento dello stato di quello stream.