5. Streams and Multiplexing (Streams und Multiplexing)
Ein „Stream" ist eine unabhängige, bidirektionale Sequenz von Frames, die zwischen Client und Server innerhalb einer HTTP/2-Verbindung ausgetauscht werden. Streams haben mehrere wichtige Eigenschaften:
-
Eine einzelne HTTP/2-Verbindung kann mehrere gleichzeitig geöffnete Streams enthalten, wobei beide Endpunkte Frames aus mehreren Streams verschachteln.
-
Streams können einseitig eingerichtet und verwendet oder vom Client oder Server gemeinsam genutzt werden.
-
Streams können von beiden Endpunkten geschlossen werden.
-
Die Reihenfolge, in der Frames auf einem Stream gesendet werden, ist bedeutsam. Empfänger verarbeiten Frames in der Reihenfolge, in der sie empfangen werden. Insbesondere ist die Reihenfolge von HEADERS- und DATA-Frames semantisch bedeutsam.
-
Streams werden durch eine Ganzzahl identifiziert. Stream-Identifikatoren werden Streams vom Endpunkt zugewiesen, der den Stream initiiert.
5.1. Stream States (Stream-Zustände)
Der Lebenszyklus eines Streams ist in Abbildung 2 dargestellt.
+--------+
send PP | | recv PP
,--------+ idle +--------.
/ | | \
v +--------+ v
+----------+ | +----------+
| | | send H / | |
,-----+ reserved | | recv H | reserved +-----.
| | (local) | | | (remote) | |
| +---+------+ v +------+---+ |
| | +--------+ | |
| | recv ES | | send ES | |
| send H | ,-------+ open +-------. | recv H |
| | / | | \ | |
| v v +---+----+ v v |
| +----------+ | +----------+ |
| | half- | | | half- | |
| | closed | | send R / | closed | |
| | (remote) | | recv R | (local) | |
| +----+-----+ | +-----+----+ |
| | | | |
| | send ES / | recv ES / | |
| | send R / v send R / | |
| | recv R +--------+ recv R | |
| send R / `----------->| |<-----------' send R / |
| recv R | closed | recv R |
`----------------------->| |<----------------------'
+--------+
send: Endpunkt sendet diesen Frame
recv: Endpunkt empfängt diesen Frame
H: HEADERS-Frame (mit implizierten CONTINUATION-Frames)
PP: PUSH_PROMISE-Frame (mit implizierten CONTINUATION-Frames)
ES: END_STREAM-Flag
R: RST_STREAM-Frame
Abbildung 2: Stream-Zustände
Beachten Sie, dass dieses Diagramm nur die Stream-Zustandsübergänge und die Frames und Flags zeigt, die diese Zustandsänderungen beeinflussen. In dieser Hinsicht führen CONTINUATION-Frames nicht zu Zustandsübergängen und sind effektiv Teil der HEADERS oder PUSH_PROMISE, denen sie folgen. Für Zustandsübergänge wird das END_STREAM-Flag als separates Ereignis vom Frame verarbeitet, das es trägt; ein HEADERS-Frame mit gesetztem END_STREAM-Flag kann zwei Zustandsübergänge verursachen.
Beide Endpunkte haben eine subjektive Sicht auf den Zustand eines Streams, die unterschiedlich sein kann, wenn Frames übertragen werden. Endpunkte koordinieren die Erstellung von Streams nicht; sie werden einseitig von einem der beiden Endpunkte erstellt. Die negativen Folgen einer Zustandsdiskrepanz sind auf den Zustand „closed" nach dem Senden von RST_STREAM beschränkt, wo Frames möglicherweise noch einige Zeit nach dem Schließen empfangen werden.
Streams haben die folgenden Zustände:
idle (untätig): Alle Streams beginnen im Zustand „idle".
Die folgenden Übergänge sind aus diesem Zustand gültig:
-
Das Senden oder Empfangen eines HEADERS-Frames bewirkt, dass der Stream „open" wird. Der Stream-Identifikator wird wie in Abschnitt 5.1.1 beschrieben ausgewählt. Derselbe HEADERS-Frame kann auch bewirken, dass ein Stream sofort „half-closed" wird.
-
Das Senden eines PUSH_PROMISE-Frames auf einem anderen Stream reserviert den untätigen Stream, der zur späteren Verwendung identifiziert wird. Der Stream-Zustand für den reservierten Stream wechselt zu „reserved (local)".
-
Das Empfangen eines PUSH_PROMISE-Frames auf einem anderen Stream reserviert einen untätigen Stream, der zur späteren Verwendung identifiziert wird. Der Stream-Zustand für den reservierten Stream wechselt zu „reserved (remote)".
-
Beachten Sie, dass der PUSH_PROMISE-Frame nicht auf dem untätigen Stream gesendet wird, sondern den neu reservierten Stream im Feld Promised Stream ID referenziert.
Das Empfangen eines anderen Frames als HEADERS oder PRIORITY auf einem Stream in diesem Zustand MUSS als Verbindungsfehler (Abschnitt 5.4.1) vom Typ PROTOCOL_ERROR behandelt werden.
reserved (local) (reserviert (lokal)): Ein Stream im Zustand „reserved (local)" ist einer, der durch Senden eines PUSH_PROMISE-Frames versprochen wurde. Ein PUSH_PROMISE-Frame reserviert einen untätigen Stream, indem er den Stream mit einem offenen Stream verknüpft, der vom entfernten Peer initiiert wurde (siehe Abschnitt 8.4).
In diesem Zustand sind nur die folgenden Übergänge möglich:
-
Der Endpunkt kann einen HEADERS-Frame senden. Dies bewirkt, dass der Stream im Zustand „half-closed (remote)" geöffnet wird.
-
Beide Endpunkte können einen RST_STREAM-Frame senden, um den Stream „closed" zu machen. Dies gibt die Stream-Reservierung frei.
Ein Endpunkt DARF in diesem Zustand keinen Frame eines anderen Typs als HEADERS, RST_STREAM oder PRIORITY senden.
Ein PRIORITY- oder WINDOW_UPDATE-Frame KANN in diesem Zustand empfangen werden. Das Empfangen eines anderen Frame-Typs als RST_STREAM, PRIORITY oder WINDOW_UPDATE auf einem Stream in diesem Zustand MUSS als Verbindungsfehler (Abschnitt 5.4.1) vom Typ PROTOCOL_ERROR behandelt werden.
reserved (remote) (reserviert (entfernt)): Ein Stream im Zustand „reserved (remote)" wurde von einem entfernten Peer reserviert.
In diesem Zustand sind nur die folgenden Übergänge möglich:
-
Das Empfangen eines HEADERS-Frames bewirkt, dass der Stream zu „half-closed (local)" übergeht.
-
Beide Endpunkte können einen RST_STREAM-Frame senden, um den Stream „closed" zu machen. Dies gibt die Stream-Reservierung frei.
Ein Endpunkt KANN in diesem Zustand einen PRIORITY-Frame senden, um den reservierten Stream neu zu priorisieren. Ein Endpunkt DARF in diesem Zustand keinen Frame eines anderen Typs als RST_STREAM, WINDOW_UPDATE oder PRIORITY senden.
Das Empfangen eines anderen Frame-Typs als HEADERS, RST_STREAM oder PRIORITY auf einem Stream in diesem Zustand MUSS als Verbindungsfehler (Abschnitt 5.4.1) vom Typ PROTOCOL_ERROR behandelt werden.
open (offen): Ein Stream im Zustand „open" kann von beiden Peers zum Senden von Frames beliebigen Typs verwendet werden. In diesem Zustand beachten sendende Peers die angekündigten Stream-Level-Flusskontrollgrenzen (Abschnitt 5.2).
Aus diesem Zustand kann jeder Endpunkt einen Frame mit gesetztem END_STREAM-Flag senden, wodurch der Stream in einen der „half-closed"-Zustände übergeht. Ein Endpunkt, der ein END_STREAM-Flag sendet, bewirkt, dass der Stream-Zustand zu „half-closed (local)" wird; ein Endpunkt, der ein END_STREAM-Flag empfängt, bewirkt, dass der Stream-Zustand zu „half-closed (remote)" wird.
Beide Endpunkte können aus diesem Zustand einen RST_STREAM-Frame senden, wodurch er sofort zu „closed" übergeht.
half-closed (local) (halb geschlossen (lokal)): Ein Stream im Zustand „half-closed (local)" kann nicht zum Senden von Frames außer WINDOW_UPDATE, PRIORITY und RST_STREAM verwendet werden.
Ein Stream wechselt von diesem Zustand zu „closed", wenn ein Frame mit gesetztem END_STREAM-Flag empfangen wird oder wenn einer der Peers einen RST_STREAM-Frame sendet.
Ein Endpunkt kann in diesem Zustand jeden Frame-Typ empfangen. Die Bereitstellung von Flusskontroll-Guthaben über WINDOW_UPDATE-Frames ist notwendig, um weiterhin flusskontrollierte Frames zu empfangen. In diesem Zustand kann ein Empfänger WINDOW_UPDATE-Frames ignorieren, die für kurze Zeit nach dem Senden eines Frames mit gesetztem END_STREAM-Flag eintreffen können.
In diesem Zustand empfangene PRIORITY-Frames werden verwendet, um Streams neu zu priorisieren, die vom identifizierten Stream abhängen.
half-closed (remote) (halb geschlossen (entfernt)): Ein Stream, der „half-closed (remote)" ist, wird vom Peer nicht mehr zum Senden von Frames verwendet. In diesem Zustand ist ein Endpunkt nicht mehr verpflichtet, ein Empfänger-Flusskontrollfenster aufrechtzuerhalten.
Wenn ein Endpunkt zusätzliche Frames außer WINDOW_UPDATE, PRIORITY oder RST_STREAM für einen Stream in diesem Zustand empfängt, MUSS es mit einem Stream-Fehler (Abschnitt 5.4.2) vom Typ STREAM_CLOSED antworten.
Ein Stream, der „half-closed (remote)" ist, kann vom Endpunkt zum Senden von Frames beliebigen Typs verwendet werden. In diesem Zustand beachtet der Endpunkt weiterhin die angekündigten Stream-Level-Flusskontrollgrenzen (Abschnitt 5.2).
Ein Stream kann von diesem Zustand zu „closed" übergehen, indem ein Frame mit gesetztem END_STREAM-Flag gesendet wird oder wenn einer der Peers einen RST_STREAM-Frame sendet.
closed (geschlossen): Der Zustand „closed" ist der Endzustand.
Ein Endpunkt DARF auf einem geschlossenen Stream keine anderen Frames als PRIORITY senden. Ein Endpunkt, das einen anderen Frame als PRIORITY nach dem Empfang eines RST_STREAM empfängt, MUSS dies als Stream-Fehler (Abschnitt 5.4.2) vom Typ STREAM_CLOSED behandeln. Ebenso MUSS ein Endpunkt, das Frames nach dem Empfang eines Frames mit gesetztem END_STREAM-Flag empfängt, dies als Verbindungsfehler (Abschnitt 5.4.1) vom Typ STREAM_CLOSED behandeln, es sei denn, der Frame ist wie unten beschrieben erlaubt.
WINDOW_UPDATE- oder RST_STREAM-Frames können in diesem Zustand für kurze Zeit nach dem Senden eines DATA- oder HEADERS-Frames mit einem END_STREAM-Flag empfangen werden. Bis der entfernte Peer RST_STREAM oder den Frame mit dem END_STREAM-Flag empfängt und verarbeitet, kann er Frames dieser Typen senden. Endpunkte MÜSSEN in diesem Zustand empfangene WINDOW_UPDATE- oder RST_STREAM-Frames ignorieren, obwohl Endpunkte Frames, die eine erhebliche Zeit nach dem Senden von END_STREAM eintreffen, als Verbindungsfehler (Abschnitt 5.4.1) vom Typ PROTOCOL_ERROR behandeln KÖNNEN.
PRIORITY-Frames können auf geschlossenen Streams gesendet werden, um Streams zu priorisieren, die vom geschlossenen Stream abhängig sind. Endpunkte SOLLTEN PRIORITY-Frames verarbeiten, obwohl sie ignoriert werden können, wenn der Stream aus dem Abhängigkeitsbaum entfernt wurde (siehe Abschnitt 5.3.3 von [RFC7540]).
Wenn dieser Zustand durch das Senden eines RST_STREAM-Frames erreicht wird, hat der Peer, der den RST_STREAM empfängt, möglicherweise bereits Frames auf dem Stream gesendet oder zur Sendung eingereiht, die nicht zurückgezogen werden können. Ein Endpunkt MUSS Frames ignorieren, die es auf geschlossenen Streams empfängt, nachdem es einen RST_STREAM-Frame gesendet hat. Ein Endpunkt KANN wählen, den Zeitraum zu begrenzen, über den es Frames ignoriert, und Frames, die nach dieser Zeit eintreffen, als fehlerhaft zu behandeln.
Flusskontrollierte Frames (d. h. DATA), die nach dem Senden von RST_STREAM empfangen werden, werden auf das Verbindungs-Flusskontrollfenster angerechnet. Obwohl diese Frames möglicherweise ignoriert werden, werden sie gesendet, bevor der Sender den RST_STREAM empfängt, sodass der Sender die Frames als gegen das Flusskontrollfenster angerechnet betrachtet.
Ein Endpunkt kann einen PUSH_PROMISE-Frame empfangen, nachdem es RST_STREAM gesendet hat. PUSH_PROMISE bewirkt, dass ein Stream „reserved" wird, auch wenn der zugehörige Stream zurückgesetzt wurde. Daher ist ein RST_STREAM erforderlich, um einen unerwünschten versprochenen Stream zu schließen.
In Ermangelung spezifischerer Regeln SOLLTEN Implementierungen den Empfang eines Frames, das in der Beschreibung eines Zustands nicht ausdrücklich erlaubt ist, als Verbindungsfehler (Abschnitt 5.4.1) vom Typ PROTOCOL_ERROR behandeln. Beachten Sie, dass PRIORITY in jedem Stream-Zustand gesendet und empfangen werden kann.
Die Regeln in diesem Abschnitt gelten nur für in diesem Dokument definierte Frames. Der Empfang von Frames, deren Semantik unbekannt ist, kann nicht als Fehler behandelt werden, da die Bedingungen zum Senden und Empfangen dieser Frames ebenfalls unbekannt sind; siehe Abschnitt 5.5.
Ein Beispiel für die Zustandsübergänge für einen HTTP-Anfrage-/Antwort-Austausch finden Sie in Abschnitt 8.8. Ein Beispiel für die Zustandsübergänge für Server-Push finden Sie in den Abschnitten 8.4.1 und 8.4.2.
5.1.1. Stream Identifiers (Stream-Identifikatoren)
Streams werden durch eine vorzeichenlose 31-Bit-Ganzzahl identifiziert. Von einem Client initiierte Streams MÜSSEN ungerade Stream-Identifikatoren verwenden; von einem Server initiierte MÜSSEN gerade Stream-Identifikatoren verwenden. Ein Stream-Identifikator von Null (0x00) wird für Verbindungssteuerungsnachrichten verwendet; der Stream-Identifikator Null kann nicht zum Einrichten eines neuen Streams verwendet werden.
Der Identifikator eines neu eingerichteten Streams MUSS numerisch größer sein als alle Streams, die der initiierende Endpunkt geöffnet oder reserviert hat. Dies regelt Streams, die mit einem HEADERS-Frame geöffnet werden, und Streams, die mit PUSH_PROMISE reserviert werden. Ein Endpunkt, das einen unerwarteten Stream-Identifikator empfängt, MUSS mit einem Verbindungsfehler (Abschnitt 5.4.1) vom Typ PROTOCOL_ERROR antworten.
Ein HEADERS-Frame lässt den vom Client initiierten Stream, der durch den Stream-Identifikator im Frame-Header identifiziert wird, von „idle" zu „open" übergehen. Ein PUSH_PROMISE-Frame lässt den vom Server initiierten Stream, der durch das Feld Promised Stream ID in der Frame-Nutzlast identifiziert wird, von „idle" zu „reserved (local)" oder „reserved (remote)" übergehen. Wenn ein Stream aus dem Zustand „idle" herausgeht, gehen alle Streams im Zustand „idle", die möglicherweise vom Peer mit einem niedrigeren Stream-Identifikator geöffnet wurden, sofort in den Zustand „closed" über. Das heißt, ein Endpunkt kann einen Stream-Identifikator überspringen, wobei der Effekt ist, dass der übersprungene Stream sofort geschlossen wird.
Stream-Identifikatoren können nicht wiederverwendet werden. Langlebige Verbindungen können dazu führen, dass ein Endpunkt den verfügbaren Bereich von Stream-Identifikatoren erschöpft. Ein Client, der keinen neuen Stream-Identifikator einrichten kann, kann eine neue Verbindung für neue Streams einrichten. Ein Server, der keinen neuen Stream-Identifikator einrichten kann, kann einen GOAWAY-Frame senden, sodass der Client gezwungen wird, eine neue Verbindung für neue Streams zu öffnen.
5.1.2. Stream Concurrency (Stream-Nebenläufigkeit)
Ein Peer kann die Anzahl gleichzeitig aktiver Streams mithilfe des Parameters SETTINGS_MAX_CONCURRENT_STREAMS (siehe Abschnitt 6.5.2) innerhalb eines SETTINGS-Frames begrenzen. Die maximale Einstellung für gleichzeitige Streams ist spezifisch für jeden Endpunkt und gilt nur für den Peer, der die Einstellung empfängt. Das heißt, Clients geben die maximale Anzahl gleichzeitiger Streams an, die der Server initiieren kann, und Server geben die maximale Anzahl gleichzeitiger Streams an, die der Client initiieren kann.
Streams im Zustand „open" oder in einem der „half-closed"-Zustände zählen zur maximalen Anzahl von Streams, die ein Endpunkt öffnen darf. Streams in einem dieser drei Zustände zählen zur Grenze, die in der Einstellung SETTINGS_MAX_CONCURRENT_STREAMS angekündigt wird. Streams in einem der „reserved"-Zustände zählen nicht zur Stream-Grenze.
Endpunkte DÜRFEN die von ihrem Peer festgelegte Grenze nicht überschreiten. Ein Endpunkt, das einen HEADERS-Frame empfängt, der dazu führt, dass seine angekündigte Grenze für gleichzeitige Streams überschritten wird, MUSS dies als Stream-Fehler (Abschnitt 5.4.2) vom Typ PROTOCOL_ERROR oder REFUSED_STREAM behandeln. Die Wahl des Fehlercodes bestimmt, ob der Endpunkt die automatische Wiederholung aktivieren möchte (siehe Abschnitt 8.7 für Details).
Ein Endpunkt, das den Wert von SETTINGS_MAX_CONCURRENT_STREAMS auf einen Wert reduzieren möchte, der unter der aktuellen Anzahl offener Streams liegt, kann entweder Streams schließen, die den neuen Wert überschreiten, oder Streams zum Abschluss zulassen.
5.2. Flow Control (Flusskontrolle)
Die Verwendung von Streams für Multiplexing führt zu Konflikten bei der Verwendung der TCP-Verbindung, was zu blockierten Streams führt. Ein Flusskontrollschema stellt sicher, dass Streams auf derselben Verbindung sich nicht destruktiv gegenseitig stören. Die Flusskontrolle wird sowohl für einzelne Streams als auch für die Verbindung als Ganzes verwendet.
HTTP/2 bietet Flusskontrolle durch die Verwendung des WINDOW_UPDATE-Frames (Abschnitt 6.9).
5.2.1. Flow-Control Principles (Flusskontroll-Prinzipien)
Die HTTP/2-Stream-Flusskontrolle zielt darauf ab, die Verwendung verschiedener Flusskontroll-Algorithmen ohne Protokolländerungen zu ermöglichen. Die Flusskontrolle in HTTP/2 hat die folgenden Eigenschaften:
-
Die Flusskontrolle ist spezifisch für eine Verbindung. Die HTTP/2-Flusskontrolle operiert zwischen den Endpunkten eines einzelnen Hops und nicht über den gesamten End-to-End-Pfad.
-
Die Flusskontrolle basiert auf WINDOW_UPDATE-Frames. Empfänger kündigen an, wie viele Oktette sie auf einem Stream und für die gesamte Verbindung zu empfangen bereit sind. Dies ist ein kreditbasiertes Schema.
-
Die Flusskontrolle ist gerichtet mit Gesamtkontrolle durch den Empfänger. Ein Empfänger KANN wählen, eine beliebige Fenstergröße für jeden Stream und für die gesamte Verbindung festzulegen. Ein Sender MUSS die vom Empfänger auferlegten Flusskontrollgrenzen respektieren. Clients, Server und Intermediäre kündigen alle unabhängig ihr Flusskontrollfenster als Empfänger an und halten sich beim Senden an die von ihrem Peer festgelegten Flusskontrollgrenzen.
-
Der Anfangswert für das Flusskontrollfenster beträgt 65.535 Oktette sowohl für neue Streams als auch für die gesamte Verbindung.
-
Der Frame-Typ bestimmt, ob die Flusskontrolle auf einen Frame anwendbar ist. Von den in diesem Dokument spezifizierten Frames unterliegen nur DATA-Frames der Flusskontrolle; alle anderen Frame-Typen verbrauchen keinen Platz im angekündigten Flusskontrollfenster. Dies stellt sicher, dass wichtige Kontrollframes nicht durch Flusskontrolle blockiert werden.
-
Ein Endpunkt kann wählen, seine eigene Flusskontrolle zu deaktivieren, aber ein Endpunkt kann Flusskontrollsignale von seinem Peer nicht ignorieren.
-
HTTP/2 definiert nur das Format und die Semantik des WINDOW_UPDATE-Frames (Abschnitt 6.9). Dieses Dokument legt nicht fest, wie ein Empfänger entscheidet, wann dieser Frame gesendet wird oder welchen Wert er sendet, noch legt es fest, wie ein Sender wählt, Pakete zu senden. Implementierungen können jeden Algorithmus auswählen, der ihren Anforderungen entspricht.
Implementierungen sind auch für die Priorisierung des Sendens von Anfragen und Antworten, die Wahl, wie Head-of-Line-Blocking für Anfragen vermieden wird, und die Verwaltung der Erstellung neuer Streams verantwortlich. Algorithmenwahlen für diese könnten mit jedem Flusskontroll-Algorithmus interagieren.
5.2.2. Appropriate Use of Flow Control (Angemessene Verwendung der Flusskontrolle)
Die Flusskontrolle ist definiert, um Endpunkte zu schützen, die unter Ressourcenbeschränkungen arbeiten. Zum Beispiel muss ein Proxy Speicher zwischen vielen Verbindungen teilen und kann auch eine langsame Upstream-Verbindung und eine schnelle Downstream-Verbindung haben. Die Flusskontrolle adressiert Fälle, in denen der Empfänger nicht in der Lage ist, Daten auf einem Stream zu verarbeiten, aber andere Streams auf derselben Verbindung weiter verarbeiten möchte.
Bereitstellungen, die diese Fähigkeit nicht benötigen, können ein Flusskontrollfenster der maximalen Größe (2^31-1) ankündigen und dieses Fenster aufrechterhalten, indem sie einen WINDOW_UPDATE-Frame senden, wenn Daten empfangen werden. Dies deaktiviert effektiv die Flusskontrolle für diesen Empfänger. Umgekehrt unterliegt ein Sender immer dem vom Empfänger angekündigten Flusskontrollfenster.
Bereitstellungen mit eingeschränkten Ressourcen (zum Beispiel Speicher) können Flusskontrolle einsetzen, um die Menge an Speicher zu begrenzen, die ein Peer verbrauchen kann. Beachten Sie jedoch, dass dies zu einer suboptimalen Nutzung verfügbarer Netzwerkressourcen führen kann, wenn die Flusskontrolle ohne Kenntnis des Bandbreite-×-Verzögerungsprodukts aktiviert wird (siehe [RFC7323]).
Selbst mit vollständigem Bewusstsein des aktuellen Bandbreite-×-Verzögerungsprodukts kann die Implementierung der Flusskontrolle schwierig sein. Endpunkte MÜSSEN HTTP/2-Frames aus dem TCP-Empfangspuffer lesen und verarbeiten, sobald Daten verfügbar sind. Das Versäumnis, zeitnah zu lesen, könnte zu einem Deadlock führen, wenn kritische Frames wie WINDOW_UPDATE nicht gelesen und darauf reagiert wird. Das zeitnahe Lesen von Frames setzt Endpunkte nicht Ressourcenerschöpfungsangriffen aus, da die HTTP/2-Flusskontrolle Ressourcenverpflichtungen begrenzt.
5.2.3. Flow-Control Performance (Flusskontroll-Leistung)
Wenn ein Endpunkt nicht sicherstellen kann, dass sein Peer immer verfügbaren Flusskontrollfensterplatz hat, der größer ist als das Bandbreite-×-Verzögerungsprodukt des Peers auf dieser Verbindung, wird sein Empfangsdurchsatz durch die HTTP/2-Flusskontrolle begrenzt. Dies führt zu verschlechterter Leistung.
Das rechtzeitige Senden von WINDOW_UPDATE-Frames kann die Leistung verbessern. Endpunkte werden das Bedürfnis ausbalancieren wollen, den Empfangsdurchsatz zu verbessern, mit dem Bedürfnis, Ressourcenerschöpfungsrisiken zu verwalten, und sollten Abschnitt 10.5 bei der Definition ihrer Strategie zur Verwaltung von Fenstergrößen sorgfältig beachten.
5.3. Prioritization (Priorisierung)
In einem gemultiplexten Protokoll wie HTTP/2 kann die Priorisierung der Zuweisung von Bandbreite und Rechenressourcen zu Streams entscheidend für die Erzielung guter Leistung sein. Ein schlechtes Priorisierungsschema kann dazu führen, dass HTTP/2 schlechte Leistung bietet. Ohne Parallelität auf der TCP-Schicht könnte die Leistung erheblich schlechter sein als bei HTTP/1.1.
Ein gutes Priorisierungsschema profitiert von der Anwendung kontextuellen Wissens wie dem Inhalt von Ressourcen, wie Ressourcen miteinander in Beziehung stehen und wie diese Ressourcen von einem Peer verwendet werden. Insbesondere können Clients Wissen über die Priorität von Anfragen besitzen, das für die Server-Priorisierung relevant ist. In diesen Fällen kann die Bereitstellung von Prioritätsinformationen durch Clients die Leistung verbessern.
5.3.1. Background on Priority in RFC 7540 (Hintergrund zur Priorität in RFC 7540)
RFC 7540 definierte ein umfangreiches System zur Signalisierung der Priorität von Anfragen. Dieses System erwies sich jedoch als komplex und wurde nicht einheitlich implementiert.
Das flexible Schema bedeutete, dass Clients Prioritäten auf sehr unterschiedliche Weise ausdrücken konnten, mit wenig Konsistenz in den angewandten Ansätzen. Für Server war die Implementierung generischer Unterstützung für das Schema komplex. Die Implementierung von Prioritäten war sowohl bei Clients als auch bei Servern ungleichmäßig. Viele Server-Bereitstellungen ignorierten Client-Signale bei der Priorisierung ihrer Verarbeitung von Anfragen.
Kurz gesagt, die Prioritätssignalisierung in RFC 7540 [RFC7540] war nicht erfolgreich.
5.3.2. Priority Signaling in This Document (Prioritätssignalisierung in diesem Dokument)
Dieses Update zu HTTP/2 macht die in RFC 7540 [RFC7540] definierte Prioritätssignalisierung veraltet. Der Großteil des Textes zu Prioritätssignalen ist in diesem Dokument nicht enthalten. Die Beschreibung von Frame-Feldern und ein Teil der obligatorischen Verarbeitung werden beibehalten, um sicherzustellen, dass Implementierungen dieses Dokuments mit Implementierungen interoperabel bleiben, die die in RFC 7540 beschriebene Prioritätssignalisierung verwenden.
Eine gründliche Beschreibung des RFC 7540-Prioritätsschemas bleibt in Abschnitt 5.3 von [RFC7540] erhalten.
Die Signalisierung von Prioritätsinformationen ist in vielen Fällen notwendig, um gute Leistung zu erzielen. Wenn die Signalisierung von Prioritätsinformationen wichtig ist, werden Endpunkte ermutigt, ein alternatives Schema zu verwenden, wie das in [HTTP-PRIORITY] beschriebene Schema.
Obwohl die Prioritätssignalisierung von RFC 7540 nicht weit verbreitet war, können die von ihr bereitgestellten Informationen in Ermangelung besserer Informationen immer noch nützlich sein. Endpunkte, die Prioritätssignale in HEADERS- oder PRIORITY-Frames empfangen, können von der Anwendung dieser Informationen profitieren. Insbesondere würden Implementierungen, die diese Signale verwenden, nicht davon profitieren, diese Prioritätssignale in Ermangelung von Alternativen zu verwerfen.
Server SOLLTEN in Ermangelung von Prioritätssignalen andere kontextuelle Informationen zur Bestimmung der Priorität von Anfragen verwenden. Server KÖNNEN das völlige Fehlen von Signalen als Hinweis darauf interpretieren, dass der Client die Funktion nicht implementiert hat. Die in Abschnitt 5.3.5 von [RFC7540] beschriebenen Standardwerte sind dafür bekannt, unter den meisten Bedingungen schlechte Leistung zu haben, und ihre Verwendung ist unwahrscheinlich absichtlich.
5.4. Error Handling (Fehlerbehandlung)
HTTP/2-Framing erlaubt zwei Klassen von Fehlern:
-
Eine Fehlerbedingung, die die gesamte Verbindung unbrauchbar macht, ist ein Verbindungsfehler (connection error).
-
Ein Fehler in einem einzelnen Stream ist ein Stream-Fehler (stream error).
Eine Liste von Fehlercodes ist in Abschnitt 7 enthalten.
Es ist möglich, dass ein Endpunkt auf Frames stößt, die mehrere Fehler verursachen würden. Implementierungen KÖNNEN während der Verarbeitung mehrere Fehler entdecken, aber sie SOLLTEN höchstens einen Stream-Fehler und einen Verbindungsfehler melden.
Der erste für einen bestimmten Stream gemeldete Stream-Fehler verhindert, dass andere Fehler auf diesem Stream gemeldet werden. Im Vergleich erlaubt das Protokoll mehrere GOAWAY-Frames, obwohl ein Endpunkt nur einen Typ von Verbindungsfehler melden SOLLTE, es sei denn, während des ordnungsgemäßen Herunterfahrens wird ein Fehler festgestellt. Wenn dies auftritt, KANN ein Endpunkt einen zusätzlichen GOAWAY-Frame mit dem neuen Fehlercode senden, zusätzlich zu jedem vorherigen GOAWAY, das NO_ERROR enthielt.
Wenn ein Endpunkt mehrere verschiedene Fehler erkennt, KANN es wählen, einen dieser Fehler zu melden. Wenn ein Frame einen Verbindungsfehler verursacht, MUSS dieser Fehler gemeldet werden. Zusätzlich KANN ein Endpunkt jeden anwendbaren Fehlercode verwenden, wenn es eine Fehlerbedingung erkennt; ein generischer Fehlercode (wie PROTOCOL_ERROR oder INTERNAL_ERROR) kann immer anstelle spezifischerer Fehlercodes verwendet werden.
5.4.1. Connection Error Handling (Behandlung von Verbindungsfehlern)
Ein Verbindungsfehler ist jeder Fehler, der die weitere Verarbeitung der Frame-Schicht verhindert oder einen Verbindungszustand beschädigt.
Ein Endpunkt, das auf einen Verbindungsfehler stößt, SOLLTE zuerst einen GOAWAY-Frame (Abschnitt 6.8) mit dem Stream-Identifikator des letzten Streams senden, den es erfolgreich von seinem Peer empfangen hat. Der GOAWAY-Frame enthält einen Fehlercode (Abschnitt 7), der angibt, warum die Verbindung beendet wird. Nach dem Senden des GOAWAY-Frames für eine Fehlerbedingung MUSS der Endpunkt die TCP-Verbindung schließen.
Es ist möglich, dass das GOAWAY vom empfangenden Endpunkt nicht zuverlässig empfangen wird. Im Falle eines Verbindungsfehlers bietet GOAWAY nur einen Best-Effort-Versuch, mit dem Peer darüber zu kommunizieren, warum die Verbindung beendet wird.
Ein Endpunkt kann eine Verbindung jederzeit beenden. Insbesondere KANN ein Endpunkt wählen, einen Stream-Fehler als Verbindungsfehler zu behandeln. Endpunkte SOLLTEN beim Beenden einer Verbindung einen GOAWAY-Frame senden, sofern die Umstände dies zulassen.
5.4.2. Stream Error Handling (Behandlung von Stream-Fehlern)
Ein Stream-Fehler ist ein Fehler im Zusammenhang mit einem bestimmten Stream, der die Verarbeitung anderer Streams nicht beeinträchtigt.
Ein Endpunkt, das einen Stream-Fehler erkennt, sendet einen RST_STREAM-Frame (Abschnitt 6.4), der den Stream-Identifikator des Streams enthält, in dem der Fehler aufgetreten ist. Der RST_STREAM-Frame enthält einen Fehlercode, der den Fehlertyp angibt.
Ein RST_STREAM ist der letzte Frame, den ein Endpunkt auf einem Stream senden kann. Der Peer, der den RST_STREAM-Frame sendet, MUSS darauf vorbereitet sein, alle Frames zu empfangen, die vom entfernten Peer gesendet wurden oder zur Sendung eingereiht wurden. Diese Frames können ignoriert werden, außer wenn sie den Verbindungszustand ändern (wie den für die Feldabschnittskompression (Abschnitt 4.3) oder Flusskontrolle aufrechterhaltenen Zustand).
Normalerweise SOLLTE ein Endpunkt nicht mehr als einen RST_STREAM-Frame für einen Stream senden. Ein Endpunkt KANN jedoch zusätzliche RST_STREAM-Frames senden, wenn es Frames auf einem geschlossenen Stream nach mehr als einer Rundlaufzeit empfängt. Dieses Verhalten ist erlaubt, um mit fehlerhaft funktionierenden Implementierungen umzugehen.
Um Schleifen zu vermeiden, DARF ein Endpunkt keinen RST_STREAM als Antwort auf einen RST_STREAM-Frame senden.
5.4.3. Connection Termination (Verbindungsbeendigung)
Wenn die TCP-Verbindung geschlossen oder zurückgesetzt wird, während Streams in den Zuständen „open" oder „half-closed" verbleiben, können die betroffenen Streams nicht automatisch wiederholt werden (siehe Abschnitt 8.7 für Details).
5.5. Extending HTTP/2 (Erweiterung von HTTP/2)
HTTP/2 erlaubt die Erweiterung des Protokolls. Innerhalb der in diesem Abschnitt beschriebenen Grenzen können Protokollerweiterungen verwendet werden, um zusätzliche Dienste bereitzustellen oder jeden Aspekt des Protokolls zu ändern. Erweiterungen sind nur im Rahmen einer einzelnen HTTP/2-Verbindung wirksam.
Dies gilt für die in diesem Dokument definierten Protokollelemente. Dies betrifft nicht die bestehenden Optionen zur Erweiterung von HTTP, wie die Definition neuer Methoden, Statuscodes oder Felder (siehe Abschnitt 16 von [HTTP]).
Erweiterungen dürfen neue Frame-Typen (Abschnitt 4.1), neue Einstellungen (Abschnitt 6.5) oder neue Fehlercodes (Abschnitt 7) verwenden. Register zur Verwaltung dieser Erweiterungspunkte sind in Abschnitt 11 von [RFC7540] definiert.
Implementierungen MÜSSEN unbekannte oder nicht unterstützte Werte in allen erweiterbaren Protokollelementen ignorieren. Implementierungen MÜSSEN Frames mit unbekannten oder nicht unterstützten Typen verwerfen. Dies bedeutet, dass jeder dieser Erweiterungspunkte von Erweiterungen sicher ohne vorherige Vereinbarung oder Verhandlung verwendet werden kann. Erweiterungsframes, die in der Mitte eines Feldblocks (Abschnitt 4.3) erscheinen, sind jedoch nicht erlaubt; diese MÜSSEN als Verbindungsfehler (Abschnitt 5.4.1) vom Typ PROTOCOL_ERROR behandelt werden.
Erweiterungen SOLLTEN vermeiden, in diesem Dokument definierte Protokollelemente oder Elemente zu ändern, für die kein Erweiterungsmechanismus definiert ist. Dies umfasst Änderungen am Layout von Frames, Ergänzungen oder Änderungen an der Art und Weise, wie Frames zu HTTP-Nachrichten zusammengesetzt werden (Abschnitt 8.1), die Definition von Pseudo-Header-Feldern oder Änderungen an jedem Protokollelement, das ein konformer Endpunkt als Verbindungsfehler (Abschnitt 5.4.1) behandeln könnte.
Eine Erweiterung, die bestehende Protokollelemente oder Zustände ändert, MUSS vor der Verwendung ausgehandelt werden. Zum Beispiel kann eine Erweiterung, die das Layout des HEADERS-Frames ändert, nicht verwendet werden, bis der Peer ein positives Signal gegeben hat, dass dies akzeptabel ist. In diesem Fall könnte es auch notwendig sein, zu koordinieren, wann das überarbeitete Layout in Kraft tritt. Zum Beispiel erfordert die Behandlung anderer Frames als DATA-Frames als flusskontrolliert eine Änderung der Semantik, die beide Endpunkte verstehen müssen, sodass dies nur durch Verhandlung erfolgen kann.
Dieses Dokument schreibt keine spezifische Methode zur Verhandlung der Verwendung einer Erweiterung vor, weist jedoch darauf hin, dass eine Einstellung (Abschnitt 6.5.2) für diesen Zweck verwendet werden könnte. Wenn beide Peers einen Wert festlegen, der die Bereitschaft zur Verwendung der Erweiterung anzeigt, kann die Erweiterung verwendet werden. Wenn eine Einstellung für die Erweiterungsverhandlung verwendet wird, MUSS der Anfangswert so definiert werden, dass die Erweiterung anfänglich deaktiviert ist.
Kapitel 5 abgeschlossen!