6. Stream-Zuordnung und -Verwendung (Stream Mapping and Usage)
Ein QUIC-Stream bietet eine zuverlässige geordnete Bereitstellung von Bytes, garantiert jedoch keine Reihenfolge der Bereitstellung in Bezug auf Bytes auf anderen Streams. In Version 1 von QUIC werden die Stream-Daten, die HTTP-Frames enthalten, von QUIC STREAM-Frames getragen, aber diese Rahmung ist für die HTTP-Rahmungsschicht unsichtbar. Die Transportschicht puffert und ordnet empfangene Stream-Daten und stellt der Anwendung einen zuverlässigen Byte-Stream zur Verfügung. Obwohl QUIC eine ungeordnete Bereitstellung innerhalb eines Streams erlaubt, nutzt HTTP/3 diese Funktion nicht.
QUIC-Streams können entweder unidirektional sein und Daten nur vom Initiator zum Empfänger übertragen, oder bidirektional sein und Daten in beide Richtungen übertragen. Streams können entweder vom Client oder vom Server initiiert werden. Weitere Details zu QUIC-Streams finden Sie in Abschnitt 2 von [QUIC-TRANSPORT].
Wenn HTTP-Felder und -Daten über QUIC gesendet werden, übernimmt die QUIC-Schicht den größten Teil der Stream-Verwaltung. HTTP muss bei Verwendung von QUIC kein separates Multiplexing durchführen: Daten, die über einen QUIC-Stream gesendet werden, werden immer auf eine bestimmte HTTP-Transaktion oder auf den gesamten HTTP/3-Verbindungskontext abgebildet.
6.1. Bidirektionale Streams (Bidirectional Streams)
Alle vom Client initiierten bidirektionalen Streams werden für HTTP-Anfragen und -Antworten verwendet. Ein bidirektionaler Stream stellt sicher, dass die Antwort leicht mit der Anfrage korreliert werden kann. Diese Streams werden als Anforderungsstreams (Request Streams) bezeichnet.
Dies bedeutet, dass die erste Anfrage des Clients auf QUIC-Stream 0 erfolgt, mit nachfolgenden Anfragen auf den Streams 4, 8 usw. Um das Öffnen dieser Streams zu ermöglichen, SOLLTE (SHOULD) ein HTTP/3-Server von Null verschiedene Mindestwerte für die Anzahl der zulässigen Streams und das anfängliche Stream-Flusskontrollfenster konfigurieren. Um die Parallelität nicht unnötig einzuschränken, SOLLTEN (SHOULD) mindestens 100 Anforderungsstreams gleichzeitig zugelassen werden.
HTTP/3 verwendet keine vom Server initiierten bidirektionalen Streams, obwohl eine Erweiterung eine Verwendung für diese Streams definieren könnte. Clients MÜSSEN (MUST) den Empfang eines vom Server initiierten bidirektionalen Streams als Verbindungsfehler vom Typ H3_STREAM_CREATION_ERROR behandeln, es sei denn, eine solche Erweiterung wurde ausgehandelt.
6.2. Unidirektionale Streams (Unidirectional Streams)
Unidirektionale Streams in beide Richtungen werden für eine Reihe von Zwecken verwendet. Der Zweck wird durch einen Stream-Typ (Stream Type) angezeigt, der als Integer mit variabler Länge am Anfang des Streams gesendet wird. Das Format und die Struktur der Daten, die diesem Integer folgen, werden durch den Stream-Typ bestimmt.
Unidirectional Stream Header {
Stream Type (i),
}
Abbildung 1: Unidirektionaler Stream-Header
In diesem Dokument sind zwei Stream-Typen definiert: Steuerungsstreams (Control Streams) (Abschnitt 6.2.1) und Push-Streams (Push Streams) (Abschnitt 6.2.2). [QPACK] definiert zwei zusätzliche Stream-Typen. Andere Stream-Typen können durch Erweiterungen zu HTTP/3 definiert werden; siehe Abschnitt 9 für weitere Details. Einige Stream-Typen sind reserviert (Abschnitt 6.2.3).
Die Leistung von HTTP/3-Verbindungen in der frühen Phase ihrer Lebensdauer ist empfindlich gegenüber der Erstellung und dem Austausch von Daten auf unidirektionalen Streams. Endpunkte, die die Anzahl der Streams oder das Flusskontrollfenster dieser Streams übermäßig einschränken, erhöhen die Wahrscheinlichkeit, dass der entfernte Peer frühzeitig das Limit erreicht und blockiert wird. Insbesondere sollten Implementierungen berücksichtigen, dass entfernte Peers möglicherweise mit einigen der unidirektionalen Streams, die sie verwenden dürfen, das reservierte Stream-Verhalten (Abschnitt 6.2.3) ausüben möchten.
Jeder Endpunkt muss mindestens einen unidirektionalen Stream für den HTTP-Steuerungsstream erstellen. QPACK erfordert zwei zusätzliche unidirektionale Streams, und andere Erweiterungen könnten weitere Streams erfordern. Daher MÜSSEN (MUST) die von Clients und Servern gesendeten Transportparameter dem Peer erlauben, mindestens drei unidirektionale Streams zu erstellen. Diese Transportparameter SOLLTEN (SHOULD) auch jedem unidirektionalen Stream mindestens 1.024 Bytes Flusskontroll-Guthaben zur Verfügung stellen.
Beachten Sie, dass ein Endpunkt nicht verpflichtet ist, zusätzliche Guthaben zu gewähren, um mehr unidirektionale Streams zu erstellen, wenn sein Peer alle anfänglichen Guthaben verbraucht, bevor er die kritischen unidirektionalen Streams erstellt. Endpunkte SOLLTEN (SHOULD) zuerst den HTTP-Steuerungsstream sowie die durch obligatorische Erweiterungen (wie die QPACK-Encoder- und Decoder-Streams) erforderlichen unidirektionalen Streams erstellen und dann zusätzliche Streams erstellen, wie es ihr Peer zulässt.
Wenn der Stream-Header einen Stream-Typ anzeigt, der vom Empfänger nicht unterstützt wird, kann der Rest des Streams nicht verarbeitet werden, da die Semantik unbekannt ist. Empfänger unbekannter Stream-Typen MÜSSEN (MUST) entweder das Lesen des Streams abbrechen oder eingehende Daten ohne weitere Verarbeitung verwerfen. Wenn das Lesen abgebrochen wird, SOLLTE (SHOULD) der Empfänger den Fehlercode H3_STREAM_CREATION_ERROR oder einen reservierten Fehlercode (Abschnitt 8.1) verwenden. Der Empfänger DARF (MUST NOT) unbekannte Stream-Typen nicht als Verbindungsfehler irgendeiner Art betrachten.
Da bestimmte Stream-Typen den Verbindungszustand beeinflussen können, SOLLTE (SHOULD NOT) ein Empfänger Daten von eingehenden unidirektionalen Streams nicht verwerfen, bevor er den Stream-Typ gelesen hat.
Implementierungen KÖNNEN (MAY) Stream-Typen senden, bevor sie wissen, ob der Peer sie unterstützt. Stream-Typen, die den Zustand oder die Semantik vorhandener Protokollkomponenten ändern könnten, einschließlich QPACK oder anderer Erweiterungen, DÜRFEN (MUST NOT) jedoch nicht gesendet werden, bis bekannt ist, dass der Peer sie unterstützt.
Ein Sender kann einen unidirektionalen Stream schließen oder zurücksetzen, sofern nicht anders angegeben. Ein Empfänger MUSS (MUST) tolerieren, dass unidirektionale Streams vor dem Empfang des unidirektionalen Stream-Headers geschlossen oder zurückgesetzt werden.
6.2.1. Steuerungsstreams (Control Streams)
Ein Steuerungsstream wird durch einen Stream-Typ von 0x00 angezeigt. Daten auf diesem Stream bestehen aus HTTP/3-Frames, wie in Abschnitt 7.2 definiert.
Jede Seite MUSS (MUST) zu Beginn der Verbindung einen einzelnen Steuerungsstream initiieren und ihren SETTINGS-Frame als ersten Frame auf diesem Stream senden. Wenn der erste Frame des Steuerungsstreams ein anderer Frame-Typ ist, MUSS (MUST) dies als Verbindungsfehler vom Typ H3_MISSING_SETTINGS behandelt werden. Pro Peer ist nur ein Steuerungsstream zulässig; der Empfang eines zweiten Streams, der behauptet, ein Steuerungsstream zu sein, MUSS (MUST) als Verbindungsfehler vom Typ H3_STREAM_CREATION_ERROR behandelt werden. Der Sender DARF (MUST NOT) den Steuerungsstream nicht schließen, und der Empfänger DARF (MUST NOT) nicht verlangen, dass der Sender den Steuerungsstream schließt. Wenn einer der Steuerungsstreams zu irgendeinem Zeitpunkt geschlossen wird, MUSS (MUST) dies als Verbindungsfehler vom Typ H3_CLOSED_CRITICAL_STREAM behandelt werden. Verbindungsfehler werden in Abschnitt 8 beschrieben.
Da der Inhalt des Steuerungsstreams zur Verwaltung des Verhaltens anderer Streams verwendet wird, SOLLTEN (SHOULD) Endpunkte genügend Flusskontroll-Guthaben bereitstellen, um zu verhindern, dass der Steuerungsstream des Peers blockiert wird.
Anstelle eines einzelnen bidirektionalen Streams wird ein Paar unidirektionaler Streams verwendet. Dies ermöglicht es jedem Peer, Daten zu senden, sobald er dazu in der Lage ist. Je nachdem, ob 0-RTT auf der QUIC-Verbindung verfügbar ist, können entweder Client oder Server möglicherweise zuerst Stream-Daten senden.
6.2.2. Push-Streams (Push Streams)
Server-Push (Server Push) ist eine in HTTP/2 eingeführte optionale Funktion, die es einem Server ermöglicht, eine Antwort zu initiieren, bevor eine Anfrage gestellt wurde. Weitere Details finden Sie in Abschnitt 4.4.
Ein Push-Stream wird durch einen Stream-Typ von 0x01 angezeigt, gefolgt von der Push-ID (Push ID) des Versprechens, das er erfüllt, codiert als Integer mit variabler Länge. Die verbleibenden Daten auf diesem Stream bestehen aus HTTP/3-Frames, wie in Abschnitt 7.2 definiert, und erfüllen einen versprochenen Server-Push durch null oder mehr vorläufige HTTP-Antworten, gefolgt von einer einzelnen endgültigen HTTP-Antwort, wie in Abschnitt 4.1 definiert. Server-Push und Push-IDs werden in Abschnitt 4.6 beschrieben.
Nur Server können pushen; wenn ein Server einen vom Client initiierten Push-Stream erhält, MUSS (MUST) dies als Verbindungsfehler vom Typ H3_STREAM_CREATION_ERROR behandelt werden.
Push Stream Header {
Stream Type (i) = 0x01,
Push ID (i),
}
Abbildung 2: Push-Stream-Header
Ein Client SOLLTE (SHOULD NOT) das Lesen eines Push-Streams nicht abbrechen, bevor er den Push-Stream-Header gelesen hat, da dies zu Unstimmigkeiten zwischen Client und Server darüber führen könnte, welche Push-IDs bereits verbraucht wurden.
Jede Push-ID DARF (MUST) in einem Push-Stream-Header nur einmal verwendet werden. Wenn ein Client erkennt, dass ein Push-Stream-Header eine Push-ID enthält, die in einem anderen Push-Stream-Header verwendet wurde, MUSS (MUST) der Client dies als Verbindungsfehler vom Typ H3_ID_ERROR behandeln.
6.2.3. Reservierte Stream-Typen (Reserved Stream Types)
Stream-Typen im Format 0x1f * N + 0x21 für nicht-negative ganzzahlige Werte von N sind reserviert, um die Anforderung auszuüben, dass unbekannte Typen ignoriert werden. Diese Streams haben keine Semantik, und sie können gesendet werden, wenn Padding auf Anwendungsebene gewünscht wird. Sie KÖNNEN (MAY) auch auf Verbindungen gesendet werden, auf denen derzeit keine Daten übertragen werden. Endpunkte DÜRFEN (MUST NOT) beim Empfang nicht davon ausgehen, dass diese Streams eine Bedeutung haben.
Die Nutzlast und Länge des Streams werden auf beliebige Weise ausgewählt, die die sendende Implementierung wählt. Beim Senden eines reservierten Stream-Typs KANN (MAY) die Implementierung den Stream entweder sauber beenden oder zurücksetzen. Beim Zurücksetzen des Streams SOLLTE (SHOULD) entweder der Fehlercode H3_NO_ERROR oder ein reservierter Fehlercode (Abschnitt 8.1) verwendet werden.