3. Stati degli stream
3. Stati degli stream
Questa sezione descrive gli stream in termini dei loro componenti di invio o ricezione. Vengono descritte due macchine a stati: una per gli stream sui quali un endpoint trasmette dati (Sezione 3.1) e un'altra per gli stream sui quali un endpoint riceve dati (Sezione 3.2).
Gli stream unidirezionali utilizzano la macchina a stati di invio o di ricezione, a seconda del tipo di stream e del ruolo dell'endpoint. Gli stream bidirezionali utilizzano entrambe le macchine a stati su entrambi gli endpoint. Per la maggior parte, l'uso di queste macchine a stati è lo stesso indipendentemente dal fatto che lo stream sia unidirezionale o bidirezionale. Le condizioni per l'apertura di uno stream sono leggermente più complesse per uno stream bidirezionale perché l'apertura della parte di invio o di ricezione causa l'apertura dello stream in entrambe le direzioni.
Le macchine a stati mostrate in questa sezione sono in gran parte informative. Questo documento utilizza gli stati degli stream per descrivere le regole su quando e come possono essere inviati diversi tipi di frame e le reazioni che ci si aspetta quando vengono ricevuti diversi tipi di frame. Sebbene queste macchine a stati siano destinate ad essere utili nell'implementazione di QUIC, questi stati non sono destinati a vincolare le implementazioni. Un'implementazione può definire una macchina a stati diversa finché il suo comportamento è coerente con un'implementazione che implementa questi stati.
In alcuni casi, un singolo evento o azione può causare una transizione attraverso più stati. Ad esempio, l'invio di STREAM con un bit FIN impostato può causare due transizioni di stato per uno stream di invio: dallo stato "Ready" allo stato "Send" e dallo stato "Send" allo stato "Data Sent".
3.1 Stati degli stream di invio
La Figura 2 mostra gli stati per la parte di uno stream che invia dati a un peer.
o
| Create Stream (Sending)
| Peer Creates Bidirectional Stream
v
+-------+
| Ready | Send RESET_STREAM
| |-----------------------.
+-------+ |
| |
| Send STREAM / |
| STREAM_DATA_BLOCKED |
v |
+-------+ |
| Send | Send RESET_STREAM |
| |---------------------->|
+-------+ |
| |
| Send STREAM + FIN |
v v
+-------+ +-------+
| Data | Send RESET_STREAM | Reset |
| Sent |------------------>| Sent |
+-------+ +-------+
| |
| Recv All ACKs | Recv ACK
v v
+-------+ +-------+
| Data | | Reset |
| Recvd | | Recvd |
+-------+ +-------+
Figura 2: Stati per le parti di invio degli stream
La parte di invio di uno stream che l'endpoint inizia (tipi 0 e 2 per i client, 1 e 3 per i server) è aperta dall'applicazione. Lo stato "Ready" rappresenta uno stream appena creato che è in grado di accettare dati dall'applicazione. I dati dello stream potrebbero essere bufferizzati in questo stato in preparazione per l'invio.
L'invio del primo frame STREAM o STREAM_DATA_BLOCKED causa l'entrata di una parte di invio di uno stream nello stato "Send". Un'implementazione potrebbe scegliere di rimandare l'allocazione di un ID di stream a uno stream fino a quando non invia il primo frame STREAM ed entra in questo stato, il che può consentire una migliore prioritizzazione degli stream.
La parte di invio di uno stream bidirezionale iniziato da un peer (tipo 0 per un server, tipo 1 per un client) inizia nello stato "Ready" quando viene creata la parte di ricezione.
Nello stato "Send", un endpoint trasmette -- e ritrasmette se necessario -- dati dello stream nei frame STREAM. L'endpoint rispetta i limiti di controllo di flusso impostati dal suo peer e continua ad accettare ed elaborare frame MAX_STREAM_DATA. Un endpoint nello stato "Send" genera frame STREAM_DATA_BLOCKED se è bloccato dall'invio dai limiti di controllo di flusso dello stream (Sezione 4.1).
Dopo che l'applicazione indica che tutti i dati dello stream sono stati inviati e viene inviato un frame STREAM contenente il bit FIN, la parte di invio dello stream entra nello stato "Data Sent". Da questo stato, l'endpoint ritrasmette solo i dati dello stream se necessario. L'endpoint non ha bisogno di controllare i limiti di controllo di flusso o inviare frame STREAM_DATA_BLOCKED per uno stream in questo stato. I frame MAX_STREAM_DATA potrebbero essere ricevuti fino a quando il peer riceve l'offset finale dello stream. L'endpoint può ignorare in sicurezza qualsiasi frame MAX_STREAM_DATA che riceve dal suo peer per uno stream in questo stato.
Una volta che tutti i dati dello stream sono stati riconosciuti con successo, la parte di invio dello stream entra nello stato "Data Recvd", che è uno stato terminale.
Da qualsiasi stato che sia "Ready", "Send" o "Data Sent", un'applicazione può segnalare che desidera abbandonare la trasmissione dei dati dello stream. In alternativa, un endpoint potrebbe ricevere un frame STOP_SENDING dal suo peer. In entrambi i casi, l'endpoint invia un frame RESET_STREAM, che causa l'entrata dello stream nello stato "Reset Sent".
Un endpoint PUÒ inviare un RESET_STREAM come primo frame che menziona uno stream; questo causa l'apertura della parte di invio di quello stream e quindi la transizione immediata allo stato "Reset Sent".
Una volta che un pacchetto contenente un RESET_STREAM è stato riconosciuto, la parte di invio dello stream entra nello stato "Reset Recvd", che è uno stato terminale.
3.2 Stati degli stream di ricezione
La Figura 3 mostra gli stati per la parte di uno stream che riceve dati da un peer. Gli stati per una parte di ricezione di uno stream rispecchiano solo alcuni degli stati della parte di invio dello stream presso il peer. La parte di ricezione di uno stream non traccia gli stati sulla parte di invio che non possono essere osservati, come lo stato "Ready". Invece, la parte di ricezione di uno stream traccia la consegna dei dati all'applicazione, alcuni dei quali non possono essere osservati dal mittente.
o
| Recv STREAM / STREAM_DATA_BLOCKED / RESET_STREAM
| Create Bidirectional Stream (Sending)
| Recv MAX_STREAM_DATA / STOP_SENDING (Bidirectional)
| Create Higher-Numbered Stream
v
+-------+
| Recv | Recv RESET_STREAM
| |-----------------------.
+-------+ |
| |
| Recv STREAM + FIN |
v |
+-------+ |
| Size | Recv RESET_STREAM |
| Known |---------------------->|
+-------+ |
| |
| Recv All Data |
v v
+-------+ Recv RESET_STREAM +-------+
| Data |--- (optional) --->| Reset |
| Recvd | Recv All Data | Recvd |
+-------+<-- (optional) ----+-------+
| |
| App Read All Data | App Read Reset
v v
+-------+ +-------+
| Data | | Reset |
| Read | | Read |
+-------+ +-------+
Figura 3: Stati per le parti di ricezione degli stream
La parte di ricezione di uno stream iniziato da un peer (tipi 1 e 3 per un client, o 0 e 2 per un server) viene creata quando viene ricevuto il primo frame STREAM, STREAM_DATA_BLOCKED o RESET_STREAM per quello stream. Per gli stream bidirezionali iniziati da un peer, la ricezione di un frame MAX_STREAM_DATA o STOP_SENDING per la parte di invio dello stream crea anche la parte di ricezione. Lo stato iniziale per la parte di ricezione di uno stream è "Recv".
Per uno stream bidirezionale, la parte di ricezione entra nello stato "Recv" quando la parte di invio iniziata dall'endpoint (tipo 0 per un client, tipo 1 per un server) entra nello stato "Ready".
Un endpoint apre uno stream bidirezionale quando viene ricevuto un frame MAX_STREAM_DATA o STOP_SENDING dal peer per quello stream. La ricezione di un frame MAX_STREAM_DATA per uno stream non aperto indica che il peer remoto ha aperto lo stream e sta fornendo credito di controllo di flusso. La ricezione di un frame STOP_SENDING per uno stream non aperto indica che il peer remoto non desidera più ricevere dati su questo stream. Entrambi i frame potrebbero arrivare prima di un frame STREAM o STREAM_DATA_BLOCKED se i pacchetti vengono persi o riordinati.
Prima che uno stream venga creato, tutti gli stream dello stesso tipo con ID di stream con numeri inferiori DEVONO essere creati. Ciò garantisce che l'ordine di creazione per gli stream sia coerente su entrambi gli endpoint.
Nello stato "Recv", l'endpoint riceve frame STREAM e STREAM_DATA_BLOCKED. I dati in arrivo sono bufferizzati e possono essere riassemblati nell'ordine corretto per la consegna all'applicazione. Man mano che i dati vengono consumati dall'applicazione e lo spazio del buffer diventa disponibile, l'endpoint invia frame MAX_STREAM_DATA per consentire al peer di inviare più dati.
Quando viene ricevuto un frame STREAM con un bit FIN, la dimensione finale dello stream è nota; vedere Sezione 4.5. La parte di ricezione dello stream entra quindi nello stato "Size Known". In questo stato, l'endpoint non ha più bisogno di inviare frame MAX_STREAM_DATA; riceve solo eventuali ritrasmissioni di dati dello stream.
Una volta che tutti i dati per lo stream sono stati ricevuti, la parte di ricezione entra nello stato "Data Recvd". Ciò potrebbe accadere come risultato della ricezione dello stesso frame STREAM che causa la transizione a "Size Known". Dopo che tutti i dati sono stati ricevuti, qualsiasi frame STREAM o STREAM_DATA_BLOCKED per lo stream può essere scartato.
Lo stato "Data Recvd" persiste fino a quando i dati dello stream non sono stati consegnati all'applicazione. Una volta che i dati dello stream sono stati consegnati, lo stream entra nello stato "Data Read", che è uno stato terminale.
La ricezione di un frame RESET_STREAM nello stato "Recv" o "Size Known" causa l'entrata dello stream nello stato "Reset Recvd". Ciò potrebbe causare l'interruzione della consegna dei dati dello stream all'applicazione.
È possibile che tutti i dati dello stream siano già stati ricevuti quando viene ricevuto un RESET_STREAM (cioè, nello stato "Data Recvd"). Allo stesso modo, è possibile che i dati dello stream rimanenti arrivino dopo aver ricevuto un frame RESET_STREAM (lo stato "Reset Recvd"). Un'implementazione è libera di gestire questa situazione come preferisce.
L'invio di un RESET_STREAM significa che un endpoint non può garantire la consegna dei dati dello stream; tuttavia, non c'è alcun requisito che i dati dello stream non vengano consegnati se viene ricevuto un RESET_STREAM. Un'implementazione PUÒ interrompere la consegna dei dati dello stream, scartare tutti i dati che non sono stati consumati e segnalare la ricezione del RESET_STREAM. Un segnale RESET_STREAM potrebbe essere soppresso o trattenuto se i dati dello stream sono completamente ricevuti e bufferizzati per essere letti dall'applicazione. Se il RESET_STREAM è soppresso, la parte di ricezione dello stream rimane in "Data Recvd".
Una volta che l'applicazione riceve il segnale che indica che lo stream è stato resettato, la parte di ricezione dello stream passa allo stato "Reset Read", che è uno stato terminale.
3.3 Tipi di frame consentiti
Il mittente di uno stream invia solo tre tipi di frame che influenzano lo stato di uno stream sia al mittente che al ricevitore: STREAM (Sezione 19.8), STREAM_DATA_BLOCKED (Sezione 19.13) e RESET_STREAM (Sezione 19.4).
Un mittente NON DEVE inviare nessuno di questi frame da uno stato terminale ("Data Recvd" o "Reset Recvd"). Un mittente NON DEVE inviare un frame STREAM o STREAM_DATA_BLOCKED per uno stream nello stato "Reset Sent" o qualsiasi stato terminale -- cioè, dopo aver inviato un frame RESET_STREAM. Un ricevitore potrebbe ricevere uno qualsiasi di questi tre frame in qualsiasi stato, a causa della possibilità di consegna ritardata dei pacchetti che li trasportano. La ricezione di frame in stati successivi NON DEVE essere trattata come un errore e non cambia lo stato della parte di invio.
Un ricevitore invia frame MAX_STREAM_DATA (Sezione 19.10) e STOP_SENDING (Sezione 19.5).
Il ricevitore invia frame MAX_STREAM_DATA solo nello stato "Recv". Un ricevitore può inviare un frame STOP_SENDING in qualsiasi stato in cui non ha ricevuto un frame RESET_STREAM; cioè, qualsiasi stato diverso da "Reset Recvd" o "Reset Read". Tuttavia, c'è poco valore nell'invio di un frame STOP_SENDING nello stato "Data Recvd", poiché tutti i dati dello stream sono stati ricevuti. Un mittente può ricevere questi frame in qualsiasi stato come risultato della consegna ritardata dei pacchetti.
3.4 Stati degli stream bidirezionali
Uno stream bidirezionale è composto da una parte di invio e una parte di ricezione. Le implementazioni possono rappresentare gli stati dello stream bidirezionale come compositi degli stati delle parti di invio e ricezione. Il modello più semplice presenta lo stream come "aperto" quando una delle parti di invio o ricezione è in uno stato non-terminale e "chiuso" quando entrambe le parti sono in stati terminali.
La Tabella 2 mostra una mappatura più complessa degli stati degli stream bidirezionali che corrisponde approssimativamente agli stati degli stream in HTTP/2 [HTTP2]. Questo mostra che più stati sulle parti di invio o ricezione degli stream sono mappati allo stesso stato composito. Si noti che questa è solo una possibilità per tale mappatura; questa mappatura richiede che i dati vengano riconosciuti prima della transizione a uno stato "chiuso" o "semi-chiuso".
| Parte di invio | Parte di ricezione | Stato composito |
|---|---|---|
| No Stream/Ready | No Stream/Recv *1 | idle |
| Ready/Send/Data Sent | Recv/Size Known | open |
| Ready/Send/Data Sent | Data Recvd/Data Read | half-closed (remote) |
| Ready/Send/Data Sent | Reset Recvd/Reset Read | half-closed (remote) |
| Data Recvd | Recv/Size Known | half-closed (local) |
| Reset Sent/Reset Recvd | Recv/Size Known | half-closed (local) |
| Reset Sent/Reset Recvd | Data Recvd/Data Read | closed |
| Reset Sent/Reset Recvd | Reset Recvd/Reset Read | closed |
| Data Recvd | Data Recvd/Data Read | closed |
| Data Recvd | Reset Recvd/Reset Read | closed |
Tabella 2: Possibile mappatura degli stati degli stream verso HTTP/2
Si noti che lo stato "No Stream" è ipotetico; né un endpoint né il suo peer hanno creato lo stream.
3.5 Transizioni di stato sollecitate
Se un'applicazione non è più interessata ai dati che sta ricevendo su uno stream, può interrompere la lettura dello stream e specificare un codice di errore dell'applicazione.
Se lo stream è nello stato "Recv" o "Size Known", il trasporto DOVREBBE segnalare questo inviando un frame STOP_SENDING per sollecitare la chiusura dello stream nella direzione opposta. Questo indica tipicamente che l'applicazione ricevente non sta più leggendo i dati che riceve dallo stream, ma non è una garanzia che i dati in arrivo verranno ignorati.
I frame STREAM ricevuti dopo aver inviato un frame STOP_SENDING sono ancora conteggiati verso il controllo di flusso della connessione e dello stream, anche se questi frame possono essere scartati alla ricezione.
Un frame STOP_SENDING richiede che l'endpoint ricevente invii un frame RESET_STREAM. Un endpoint che riceve un frame STOP_SENDING DEVE inviare un frame RESET_STREAM se lo stream è nello stato "Ready" o "Send". Se lo stream è nello stato "Data Sent", l'endpoint PUÒ rimandare l'invio del frame RESET_STREAM fino a quando i pacchetti contenenti dati in sospeso non vengono riconosciuti o dichiarati persi. Se qualsiasi dato in sospeso viene dichiarato perso, l'endpoint DOVREBBE inviare un frame RESET_STREAM invece di ritrasmettere i dati.
Un endpoint DOVREBBE copiare il codice di errore dal frame STOP_SENDING al frame RESET_STREAM che invia, ma può utilizzare qualsiasi codice di errore dell'applicazione. L'endpoint che invia un frame STOP_SENDING PUÒ ignorare il codice di errore in qualsiasi frame RESET_STREAM successivamente ricevuto per quello stream.
STOP_SENDING DOVREBBE essere inviato solo per uno stream che non è stato resettato dal peer. STOP_SENDING è più utile per gli stream negli stati "Recv" o "Size Known".
Un endpoint è tenuto a inviare un altro frame STOP_SENDING se un pacchetto contenente un STOP_SENDING precedente viene perso. Tuttavia, una volta che tutti i dati dello stream o un frame RESET_STREAM sono stati ricevuti per lo stream -- cioè, lo stream è in qualsiasi stato diverso da "Recv" o "Size Known" -- l'invio di un frame STOP_SENDING è inutile.
Un endpoint che desidera terminare entrambe le direzioni di uno stream bidirezionale può terminare una direzione inviando un frame RESET_STREAM e può incoraggiare la terminazione rapida nella direzione opposta inviando un frame STOP_SENDING.