Zum Hauptinhalt springen

9. Verbindungsverwaltung (Connection Management)

Das HTTP-Messaging ist unabhängig von den zugrunde liegenden Verbindungsprotokollen der Transport- oder Sitzungsschicht. HTTP setzt lediglich einen zuverlässigen Transport mit geordneter Zustellung von Anfragen und der entsprechenden geordneten Zustellung von Antworten voraus. Die Abbildung der HTTP-Anfrage- und -Antwortstrukturen auf die Dateneinheiten eines zugrunde liegenden Transportprotokolls liegt außerhalb des Geltungsbereichs dieser Spezifikation.

Wie in Abschnitt 7.3 von [HTTP] beschrieben, werden die für eine HTTP-Interaktion zu verwendenden Verbindungsprotokolle durch die Client-Konfiguration und den Ziel-URI bestimmt. Beispielsweise bezeichnet das URI-Schema "http" (Abschnitt 4.2.1 von [HTTP]) eine Standardverbindung von TCP über IP mit dem Standard-TCP-Port 80, aber der Client kann so konfiguriert sein, dass er einen Proxy über eine andere Verbindung, einen anderen Port oder ein anderes Protokoll verwendet.

Von HTTP-Implementierungen wird erwartet, dass sie Verbindungsverwaltung betreiben; dazu gehören die Pflege des Zustands aktueller Verbindungen, das Herstellen einer neuen Verbindung oder die Wiederverwendung einer bestehenden Verbindung, die Verarbeitung von auf einer Verbindung empfangenen Nachrichten, das Erkennen von Verbindungsfehlern und das Schließen jeder Verbindung. Die meisten Clients unterhalten mehrere Verbindungen parallel, darunter mehr als eine Verbindung pro Server-Endpunkt. Die meisten Server sind darauf ausgelegt, Tausende gleichzeitiger Verbindungen zu unterhalten und dabei Anfragewarteschlangen zu steuern, um eine faire Nutzung zu ermöglichen und Denial-of-Service-Angriffe zu erkennen.

9.1. Aufbau (Establishment)​

Es liegt außerhalb des Geltungsbereichs dieser Spezifikation zu beschreiben, wie Verbindungen über verschiedene Protokolle der Transport- oder Sitzungsschicht aufgebaut werden. Jede HTTP-Verbindung entspricht einer zugrunde liegenden Transportverbindung.

9.2. Zuordnung einer Antwort zu einer Anfrage (Associating a Response to a Request)​

HTTP/1.1 enthält keine Anfragekennung, um eine bestimmte Anfragenachricht ihren einer oder mehreren zugehörigen Antwortnachrichten zuzuordnen. Daher verlässt es sich darauf, dass die Reihenfolge des Eintreffens der Antworten genau der Reihenfolge entspricht, in der die Anfragen auf derselben Verbindung gestellt werden. Mehr als eine Antwortnachricht pro Anfrage tritt nur dann auf, wenn eine oder mehrere informative Antworten (1xx; siehe Abschnitt 15.2 von [HTTP]) einer endgültigen Antwort auf dieselbe Anfrage vorausgehen.

Ein Client, der mehr als eine offene Anfrage auf einer Verbindung hat, muss (MUST) eine Liste der offenen Anfragen in der Reihenfolge ihres Absendens führen und muss (MUST) jede auf dieser Verbindung empfangene Antwortnachricht der ersten offenen Anfrage zuordnen, die noch keine endgültige (Nicht-1xx-)Antwort erhalten hat.

Wenn ein Client Daten auf einer Verbindung empfängt, die keine offenen Anfragen hat, darf (MUST NOT) der Client diese Daten als gültige Antwort betrachten; der Client sollte (SHOULD) die Verbindung schließen, da die Nachrichtenabgrenzung nun mehrdeutig ist, es sei denn, die Daten bestehen nur aus einem oder mehreren CRLF (die gemäß Abschnitt 2.2 verworfen werden können).

9.3. Persistenz (Persistence)​

HTTP/1.1 verwendet standardmäßig "persistente Verbindungen", die es ermöglichen, mehrere Anfragen und Antworten über eine einzige Verbindung zu übertragen. HTTP-Implementierungen sollten (SHOULD) persistente Verbindungen unterstützen.

Ein Empfänger bestimmt anhand der Protokollversion und des Connection-Header-Felds (Abschnitt 7.6.1 von [HTTP]) in der zuletzt empfangenen Nachricht, sofern vorhanden, ob eine Verbindung persistent ist oder nicht:

  • Wenn die Verbindungsoption "close" vorhanden ist (Abschnitt 9.6), bleibt die Verbindung nach der aktuellen Antwort nicht bestehen; andernfalls,
  • Wenn das empfangene Protokoll HTTP/1.1 (oder später) ist, bleibt die Verbindung nach der aktuellen Antwort bestehen; andernfalls,
  • Wenn das empfangene Protokoll HTTP/1.0 ist, die Verbindungsoption "keep-alive" vorhanden ist, entweder der Empfänger kein Proxy ist oder die Nachricht eine Antwort ist, und der Empfänger den "keep-alive"-Mechanismus von HTTP/1.0 befolgen möchte, bleibt die Verbindung nach der aktuellen Antwort bestehen; andernfalls,
  • Die Verbindung wird nach der aktuellen Antwort geschlossen.

Ein Client, der keine persistenten Verbindungen unterstützt, muss (MUST) in jeder Anfragenachricht die Verbindungsoption "close" senden.

Ein Server, der keine persistenten Verbindungen unterstützt, muss (MUST) in jeder Antwortnachricht, die keinen 1xx-Statuscode (Informational) hat, die Verbindungsoption "close" senden.

Ein Client darf (MAY) zusätzliche Anfragen auf einer persistenten Verbindung senden, bis er eine Verbindungsoption "close" sendet oder empfängt oder eine HTTP/1.0-Antwort ohne die Verbindungsoption "keep-alive" empfängt.

Um persistent zu bleiben, müssen alle Nachrichten auf einer Verbindung eine selbstdefinierte Nachrichtenlänge haben (d. h. eine, die nicht durch das Schließen der Verbindung definiert wird), wie in Abschnitt 6 beschrieben. Ein Server muss (MUST) den gesamten Anfragenachrichteninhalt lesen oder die Verbindung nach dem Senden seiner Antwort schließen; andernfalls würden die verbleibenden Daten auf einer persistenten Verbindung als die nächste Anfrage fehlinterpretiert. Ebenso muss (MUST) ein Client den gesamten Antwortnachrichteninhalt lesen, wenn er dieselbe Verbindung für eine nachfolgende Anfrage wiederverwenden möchte.

Ein Proxy-Server darf (MUST NOT) eine persistente Verbindung mit einem HTTP/1.0-Client unterhalten (Informationen und eine Diskussion der Probleme mit dem Keep-Alive-Header-Feld, das von vielen HTTP/1.0-Clients implementiert wird, finden Sie in Anhang C.2.2).

Weitere Informationen zur Abwärtskompatibilität mit HTTP/1.0-Clients finden Sie in Anhang C.2.2.

9.3.1. Erneutes Senden von Anfragen (Retrying Requests)​

Verbindungen können jederzeit geschlossen werden, absichtlich oder unabsichtlich. Implementierungen sollten die Notwendigkeit vorhersehen, sich von asynchronen Schließereignissen zu erholen. Die Bedingungen, unter denen ein Client eine Folge offener Anfragen automatisch erneut senden kann, sind in Abschnitt 9.2.2 von [HTTP] definiert.

9.3.2. Pipelining​

Ein Client, der persistente Verbindungen unterstützt, darf (MAY) seine Anfragen "pipelinen" (d. h. mehrere Anfragen senden, ohne auf die jeweilige Antwort zu warten). Ein Server darf (MAY) eine Folge gepipelinter Anfragen parallel verarbeiten, wenn sie alle sichere Methoden verwenden (Abschnitt 9.2.1 von [HTTP]), aber er muss (MUST) die entsprechenden Antworten in derselben Reihenfolge senden, in der die Anfragen empfangen wurden.

Ein Client, der Anfragen pipelinet, sollte (SHOULD) unbeantwortete Anfragen erneut senden, wenn die Verbindung geschlossen wird, bevor er alle zugehörigen Antworten empfangen hat. Beim erneuten Senden gepipelinter Anfragen nach einer fehlgeschlagenen Verbindung (einer Verbindung, die vom Server nicht in seiner letzten vollständigen Antwort ausdrücklich geschlossen wurde) darf (MUST NOT) ein Client unmittelbar nach dem Verbindungsaufbau pipelinen, da die erste verbleibende Anfrage in der vorherigen Pipeline eine Fehlerantwort verursacht haben könnte, die erneut verloren gehen kann, wenn mehrere Anfragen auf einer vorzeitig geschlossenen Verbindung gesendet werden (siehe das in Abschnitt 9.6 beschriebene TCP-Reset-Problem).

Idempotente Methoden (Abschnitt 9.2.2 von [HTTP]) sind für das Pipelining von Bedeutung, weil sie nach einem Verbindungsfehler automatisch erneut gesendet werden können. Ein Benutzeragent sollte (SHOULD NOT) nach einer nicht idempotenten Methode Anfragen pipelinen, bis der endgültige Antwortstatuscode für diese Methode empfangen wurde, es sei denn, der Benutzeragent verfügt über Mittel, um Teilausfallbedingungen im Zusammenhang mit der gepipelinten Folge zu erkennen und sich davon zu erholen.

Ein Vermittler, der gepipelinte Anfragen empfängt, darf (MAY) diese Anfragen beim Weiterleiten inbound pipelinen, da er sich auf die outbound-Benutzeragenten verlassen kann, um zu bestimmen, welche Anfragen sicher gepipelint werden können. Wenn die inbound-Verbindung vor dem Empfang einer Antwort fehlschlägt, darf (MAY) der pipelinende Vermittler versuchen, eine Folge von Anfragen erneut zu senden, die noch keine Antwort erhalten haben, sofern die Anfragen alle idempotente Methoden verwenden; andernfalls sollte (SHOULD) der pipelinende Vermittler alle empfangenen Antworten weiterleiten und dann die entsprechenden outbound-Verbindungen schließen, damit sich die outbound-Benutzeragenten entsprechend erholen können.

9.4. Nebenläufigkeit (Concurrency)​

Ein Client sollte die Anzahl gleichzeitig offener Verbindungen, die er zu einem bestimmten Server unterhält, begrenzen.

Frühere Revisionen von HTTP gaben eine bestimmte Anzahl von Verbindungen als Obergrenze an, aber dies erwies sich für viele Anwendungen als unpraktisch. Infolgedessen schreibt diese Spezifikation keine bestimmte maximale Anzahl von Verbindungen vor, sondern ermutigt Clients vielmehr, beim Öffnen mehrerer Verbindungen konservativ zu sein.

Mehrere Verbindungen werden typischerweise verwendet, um das Problem des "Head-of-Line-Blocking" zu vermeiden, bei dem eine Anfrage, die eine erhebliche serverseitige Verarbeitung erfordert und/oder sehr große Inhalte überträgt, nachfolgende Anfragen auf derselben Verbindung blockieren würde. Jede Verbindung verbraucht jedoch Serverressourcen.

Darüber hinaus kann die Verwendung mehrerer Verbindungen in überlasteten Netzwerken unerwünschte Nebenwirkungen verursachen. Die Verwendung einer größeren Anzahl von Verbindungen kann auch in ansonsten nicht überlasteten Netzwerken Nebenwirkungen verursachen, weil ihr aggregiertes und anfangs synchronisiertes Sendeverhalten eine Überlastung verursachen kann, die bei Verwendung weniger paralleler Verbindungen nicht aufgetreten wäre.

Beachten Sie, dass ein Server Datenverkehr ablehnen kann, den er als missbräuchlich oder als charakteristisch für einen Denial-of-Service-Angriff ansieht, etwa eine übermäßige Anzahl offener Verbindungen von einem einzelnen Client.

9.5. Fehler und Zeitüberschreitungen (Failures and Timeouts)​

Server haben üblicherweise einen Timeout-Wert, über den hinaus sie eine inaktive Verbindung nicht mehr unterhalten. Proxy-Server setzen diesen Wert möglicherweise höher, da der Client wahrscheinlich mehr Verbindungen über denselben Proxy-Server herstellen wird. Die Verwendung persistenter Verbindungen stellt weder für den Client noch für den Server Anforderungen an die Länge (oder das Vorhandensein) dieses Timeouts.

Ein Client oder Server, der eine Zeitüberschreitung wünscht, sollte (SHOULD) eine geordnete Schließung der Verbindung vornehmen. Implementierungen sollten (SHOULD) offene Verbindungen ständig auf ein empfangenes Schließsignal überwachen und entsprechend darauf reagieren, da das umgehende Schließen beider Seiten einer Verbindung die Freigabe zugewiesener Systemressourcen ermöglicht.

Ein Client, Server oder Proxy darf (MAY) die Transportverbindung jederzeit schließen. Beispielsweise kann ein Client gerade begonnen haben, eine neue Anfrage zu senden, während der Server beschlossen hat, die "inaktive" Verbindung zu schließen. Aus Sicht des Servers wird die Verbindung geschlossen, während sie inaktiv war, aus Sicht des Clients ist jedoch eine Anfrage im Gange.

Ein Server sollte (SHOULD) persistente Verbindungen aufrechterhalten, wenn möglich, und die Flusskontrollmechanismen des zugrunde liegenden Transports vorübergehende Überlastungen auflösen lassen, anstatt Verbindungen mit der Erwartung zu beenden, dass Clients es erneut versuchen. Letzteres Verfahren kann die Netzwerküberlastung oder die Serverlast verschärfen.

Ein Client, der einen Nachrichteninhalt sendet, sollte (SHOULD) die Netzwerkverbindung während der Übertragung der Anfrage auf eine Fehlerantwort überwachen. Sieht der Client eine Antwort, die anzeigt, dass der Server den Nachrichteninhalt nicht empfangen möchte und die Verbindung schließt, sollte (SHOULD) der Client die Übertragung des Inhalts sofort einstellen und seine Seite der Verbindung schließen.

9.6. Abbau (Tear-down)​

Die Verbindungsoption "close" ist als ein Signal definiert, dass der Absender diese Verbindung nach Abschluss der Antwort schließen wird. Ein Absender sollte (SHOULD) ein Connection-Header-Feld (Abschnitt 7.6.1 von [HTTP]) senden, das die Verbindungsoption "close" enthält, wenn er beabsichtigt, eine Verbindung zu schließen. Zum Beispiel

Connection: close

als Anfrage-Header-Feld zeigt an, dass dies die letzte Anfrage ist, die der Client auf dieser Verbindung senden wird, während dasselbe Feld in einer Antwort anzeigt, dass der Server diese Verbindung schließen wird, nachdem die Antwortnachricht vollständig ist.

Beachten Sie, dass der Feldname "Close" reserviert ist, da die Verwendung dieses Namens als Header-Feld mit der Verbindungsoption "close" in Konflikt geraten könnte.

Ein Client, der eine Verbindungsoption "close" sendet, darf (MUST NOT) weitere Anfragen auf dieser Verbindung senden (nach derjenigen, die "close" enthält) und muss (MUST) die Verbindung schließen, nachdem er die endgültige Antwortnachricht zu dieser Anfrage gelesen hat.

Ein Server, der eine Verbindungsoption "close" empfängt, muss (MUST) die Schließung der Verbindung einleiten (siehe unten), nachdem er die endgültige Antwort auf die Anfrage gesendet hat, die die Verbindungsoption "close" enthielt. Der Server sollte (SHOULD) in seiner endgültigen Antwort auf dieser Verbindung eine Verbindungsoption "close" senden. Der Server darf (MUST NOT) weitere auf dieser Verbindung empfangene Anfragen verarbeiten.

Ein Server, der eine Verbindungsoption "close" sendet, muss (MUST) die Schließung der Verbindung einleiten (siehe unten), nachdem er die Antwort gesendet hat, die die Verbindungsoption "close" enthält. Der Server darf (MUST NOT) weitere auf dieser Verbindung empfangene Anfragen verarbeiten.

Ein Client, der eine Verbindungsoption "close" empfängt, muss (MUST) das Senden von Anfragen auf dieser Verbindung einstellen und die Verbindung schließen, nachdem er die Antwortnachricht gelesen hat, die die Verbindungsoption "close" enthält; wenn zusätzliche gepipelinte Anfragen auf der Verbindung gesendet wurden, sollte (SHOULD NOT) der Client annehmen, dass sie vom Server verarbeitet werden.

Wenn ein Server eine sofortige Schließung einer TCP-Verbindung vornimmt, besteht ein erhebliches Risiko, dass der Client die letzte HTTP-Antwort nicht lesen kann. Empfängt der Server zusätzliche Daten vom Client auf einer vollständig geschlossenen Verbindung, etwa eine weitere Anfrage, die der Client vor dem Empfang der Antwort des Servers gesendet hat, sendet der TCP-Stack des Servers ein Reset-Paket an den Client; unglücklicherweise kann das Reset-Paket die unbestätigten Eingabepuffer des Clients löschen, bevor sie vom HTTP-Parser des Clients gelesen und interpretiert werden können.

Um das TCP-Reset-Problem zu vermeiden, schließen Server eine Verbindung in der Regel in Stufen. Zunächst führt der Server einen Halb-Abschluss durch, indem er nur die Schreibseite der Lese-/Schreibverbindung schließt. Der Server liest dann weiter von der Verbindung, bis er ein entsprechendes Schließen durch den Client empfängt oder bis der Server mit hinreichender Sicherheit davon ausgehen kann, dass sein eigener TCP-Stack die Bestätigung des Clients für die Pakete empfangen hat, die die letzte Antwort des Servers enthielten. Schließlich schließt der Server die Verbindung vollständig.

Es ist unbekannt, ob das Reset-Problem ausschließlich TCP betrifft oder auch bei anderen Transportverbindungsprotokollen auftreten kann.

Beachten Sie, dass eine vom Client halbgeschlossene TCP-Verbindung keine Anfragenachricht abgrenzt und auch nicht impliziert, dass der Client nicht mehr an einer Antwort interessiert ist. Im Allgemeinen kann man sich nicht auf Transportsignale verlassen, um Sonderfälle zu signalisieren, da HTTP/1.1 transportunabhängig ist.

9.7. TLS-Verbindungsinitiierung (TLS Connection Initiation)​

Konzeptionell besteht HTTP/TLS einfach darin, HTTP-Nachrichten über eine durch TLS [TLS13] gesicherte Verbindung zu senden.

Der HTTP-Client fungiert zugleich als TLS-Client. Er initiiert eine Verbindung zum Server am geeigneten Port und sendet das TLS-ClientHello, um den TLS-Handshake zu beginnen. Nach Abschluss des TLS-Handshakes kann der Client dann die erste HTTP-Anfrage initiieren. Alle HTTP-Daten müssen (MUST) als TLS-"application data" gesendet werden, werden aber ansonsten wie eine normale Verbindung für HTTP behandelt (einschließlich einer möglichen Wiederverwendung als persistente Verbindung).

9.8. TLS-Verbindungsabschluss (TLS Connection Closure)​

TLS verwendet einen Austausch von Schließungs-Warnungen vor dem (fehlerfreien) Verbindungsabschluss, um einen sicheren Verbindungsabschluss bereitzustellen; siehe Abschnitt 6.1 von [TLS13]. Wenn eine gültige Schließungs-Warnung empfangen wird, kann eine Implementierung sicher sein, dass auf dieser Verbindung keine weiteren Daten mehr empfangen werden.

Wenn eine Implementierung weiß, dass sie alle Nachrichtendaten, an denen sie interessiert ist, gesendet oder empfangen hat, typischerweise durch Erkennen der HTTP-Nachrichtengrenzen, kann sie einen "unvollständigen Abschluss" erzeugen, indem sie eine Schließungs-Warnung sendet und dann die Verbindung schließt, ohne auf den Empfang der entsprechenden Schließungs-Warnung von ihrem Peer zu warten.

Ein unvollständiger Abschluss stellt die Sicherheit der bereits empfangenen Daten nicht infrage, könnte aber darauf hindeuten, dass nachfolgende Daten abgeschnitten wurden. Da TLS sich der HTTP-Nachrichtenrahmung nicht direkt bewusst ist, ist es notwendig, die HTTP-Daten selbst zu untersuchen, um festzustellen, ob Nachrichten vollständig sind. Die Behandlung unvollständiger Nachrichten ist in Abschnitt 8 definiert.

Bei einem unvollständigen Abschluss sollte (SHOULD) ein Client alle Anfragen als abgeschlossen betrachten, für die er eines der Folgenden empfangen hat:

  1. so viele Daten, wie im Content-Length-Header-Feld angegeben, oder
  2. den abschließenden Chunk der Länge null (wenn Transfer-Encoding mit dem Wert chunked verwendet wird).

Eine Antwort, die weder über eine Chunked-Übertragungskodierung noch über Content-Length verfügt, ist nur dann vollständig, wenn eine gültige Schließungs-Warnung empfangen wurde. Das Behandeln einer unvollständigen Nachricht als vollständig könnte Implementierungen Angriffen aussetzen.

Ein Client, der einen unvollständigen Abschluss erkennt, sollte (SHOULD) sich ordnungsgemäß erholen.

Clients müssen (MUST) eine Schließungs-Warnung senden, bevor sie die Verbindung schließen. Clients, die nicht erwarten, weitere Daten zu empfangen, können (MAY) darauf verzichten, auf die Schließungs-Warnung des Servers zu warten, und die Verbindung einfach schließen, wodurch auf der Serverseite ein unvollständiger Abschluss erzeugt wird.

Server sollten (SHOULD) darauf vorbereitet sein, einen unvollständigen Abschluss vom Client zu empfangen, da der Client oft das Ende der Serverdaten lokalisieren kann.

Server müssen (MUST) versuchen, vor dem Schließen der Verbindung einen Austausch von Schließungs-Warnungen mit dem Client zu initiieren. Server dürfen (MAY) die Verbindung nach dem Senden der Schließungs-Warnung schließen, wodurch auf der Clientseite ein unvollständiger Abschluss erzeugt wird.