HTTP Keep-alive

HTTP Keep-alive

Der Inhalt der Seite war erst nur ein Kapitel der Seite TCP Session Timeout aber sehr schnell hat sich gezeigt, dass das Kapitel zu umfangreich wurde. Ich beschreibe die Hintergründe von HTTP-Verbindungen, welche über die Optionen Keep-Alive auch länger bestand haben können.

Der Anfang von Keep-alive

In den Anfangszeiten von HTTP bestand ein HTTP-Request aus dem Aufbau der TCP-Verbindung, der Anfrage und Antwort und kurz darauf dem Abbau der Verbindung. Das hat kostbare "Connections" auf dem WebServer gespart aber zu mehr Datenvolumen und längerer Antwortzeit geführt. Schon sehr früh wurde daher überlegt, wie man mehrere HTTP-Requests über eine einmal aufgebaute TCP-Verbindung führen kann. Mit der Nutzung von HTTPS ist es noch wichtiger, dass eine einmal etablierte "kostbare" TLS-Verbindung samt Zertifikatsübertragung und Prüfung nicht gleich wieder verworfen wird.

So wurde im HTTP-Header das Feld "Connection: keepalive" definiert, mit dem der Client dem Server sagen kann, dass die Verbindung nach der Antwort nicht abgebaut werden soll und weitere Anfragen sequentiell folgen können. Mit HTTP 1.1 wurde festgelegt, dass alle Verbindungen per Default "Persistent" sind, es sei denn ein Headereintrag fordert etwas anderes. Die Verbindung bleibt solange bestehen, bis eine der beiden Seiten die TCP-Verbindung abbaut. Es gibt im HTTP-Protokoll keinen Mechanismus, um per HTTP ein Platzhalter-Paket zu senden, um die Verbindung aufrecht zu erhalten. Das ist wieder Aufgabe des TCP-Stacks und das können auch mal 2 Stunden bis zum nächsten Keep-alive sein. Allerdings kann ein Client natürlich einfach einen HTTP-Request selbst absetzen um irgend etwas zu übertragen.

Allerdings sollte ein WebServer mit einem "Connection: keep-alive" antworten, damit der Client dies weiß und die Verbindung beibehält.

Hinweis:
In HTTP 1.1, all connections are considered persistent unless declared otherwise. The HTTP persistent connections do not use separate keepalive messages, they just allow multiple requests to use a single connection. However, the default connection timeout of Apache httpd 1.3 and 2.0 is as little as 15 seconds and just 5 seconds for Apache httpd 2.2 and above.
Quelle: https://en.wikipedia.org/wiki/HTTP_persistent_connection

Keep-Alive im Trace

Hier eine Anfrage per "Invoke-WebRequest" und die Antwort des WebServers, der trotz HTTP 1.1 auch noch einen "Connection" und "Keep-Alive"-Header mit sendet.

Der WebServer schlägt hier also einen Timeout von 15 Sekunden vor. Wenn ich die Anfrage immer wieder mache, dann sehe ich in meinem WireShark die entsprechenden Pakete und Source Ports. Wenn ich nach mehr als 15 Sekunden warte, dann startet der Client eine neue Verbindung.

Schaue ich mir die TCP-Handshake-Meldungen zu der ersten Verbindung an, dann sehe ich den Handshake, die Zwischenquittungen auf die Anfragen und dass ca. 69 Sekunden nach der letzten Quittung der Webserver die Verbindung abbaut.

Er hält sich also selbst nicht an seine Vorgabe von 15 Sekunden. Diese Information brauchen wir aber später noch für eine Analyse. Zuerst möchte ich aber einfach ein paar Gegenstellen analysieren, Wie sie auf einen HTTP-Request reagieren. Liefern Sie einen "Connection: Keep-alive", welcher Timeout wird vom Server vorgegeben und wer baut wann die Verbindung ab.

Outlook und Keep-alive

Wenn Sie sich Outlook gegen Office 365 anschauen, dann sehen Sie keinen "Connection: Keep-alive" in der Antwort. Hier kommt dann "chunked" zum Einsatz. Mehr Details finden Sie dazu auf HTTP Chunked:

Damit meldet der Server, dass noch Daten nachkommen können. Quasi "Streaming". Auch Fiddler zeigt das entsprechend an. Es gibt zwar schon einem 200 OK aber da die Transaktion noch nicht abgeschlossen ist, wird der Code nicht angezeigt und das Symbol für einen offenen Download vorangestellt.

Auch in dem Fall ist es natürlich interessant, wie der darunterliegende TCP-Stack damit umgeht. Sowohl der Client als auch der Server könnte ja die Verbindung abbauen. In der Regel wird sie aber eben nicht durch den Endpunkte abgebaut. Damit sind diese Verbindungen dem Risiko ausgesetzt, dass jemand anderes den TCP-Kanal kappt.

Nicht alle WebServer unterstützen Keep-Alive oder melden es nicht. Teilweise kann ich es verstehen, wenn die Seite sowieso direkt eine Umleitung auf ein Anmeldeseite unter einer anderen URL oder eine Wechsel auf HTTPS mit einem anderen Port forciert. Dann ist es sicher besser die "falsche" Verbindung nicht aufrecht zu halten.

Test mit Gegenstellen

Ich habe die Aufrufe in der PowerShell mit "(Invoke-webrequest <url> -UseBasicParsing).headers" ausgeführt, um gleich die Header zu sehen und mit BasicParsing weitere Umleitungen, JavaScript u.a. Dinge zu vermeiden. Parallel habe ich mit WireShark die TCP-Verbindung aufgezeichnet und gewartet, bis Sie abgebaut wird.

Gegenstelle Connection: Keep-alive Keep-alive Transfer-encoding: chunked FIN-Zeit Ergebnis

Synology DS216J-NAS (lokal)

Ja

20 Sek

Nein

65 Sek

Die Webverwaltung des NAS unterstützt Keep-alive mit 20 Sekunden und beendet die Verbindung nach ca. 65 Sekunden

https://www.msxfaq.de

Ja

15 Sek

Nein

15 Sek

Die Apache Webserver von 1und1 nehmen es sehr genau und beenden die Verbindung direkt nach 15 Sekunden

https://outlook.office365.com

Nein
Nein

Nein
Nein

Nein
Nein

0 Sek

Microsoft liefert hier einen "302 Moved Permanently" und beendet die TCP-Verbindung sofort aktiv

https://www.google.com

Nein

Nein

Ja

100 Sek

Eine TCP-Session wird von Google nach 100 Sekunden abgebaut

https://login.microsoftonline.com

Nein

Nein

Nein

100 Sek

Diesmal ist es hier aber mein Client, der die Verbindung abbaut. Interessant, da Invoke-WebRequest vom Server nicht über einen Timeout-Header dahingehend angewiesen wurde.

Ich fand es schon überraschend, dass die Office 365 Dienste weder Keep-Alive noch Chunked nutzen. Allerdings ist diese Auswertung aus irreführen, da die Funktion durch den WebService auf der anderen Seite individuell pro Request gesteuert werden kann. Wenn ich mit Fiddler nachschaue, wie mein Outlook oder Smartphone mit Office 365 spricht, dann kommt dort natürlich "Chunked" zum Einsatz. Insofern sind solche einfachen Tests nur bedingt aussagekräftig.

Rauswurf mit "Close"

Aber es gibt auch das andere Extrem, dass eine Gegenstelle ein "Connection: close" vorgibt. Das passiert bei einem ADFS-Server z.B. im Fehlerfall:

So kann sich ein WebServer ganz schnell von Verbindungen befreien, bei denen wohl keine weiteren Zugriffe zu erwarten sind.

Ein Grund könnte aber auch sein, dass der Client zu viele Zugriffe parallel startet. Der meist genutzte Client dürfte ein Browser sein und hier haben die verschiedenen Hersteller eigene Werte hinterlegt. So nutzt der IE8 und höher bis zu 6 parallele persistente Verbindungen, die aber bei Inaktivität nach 60 Sekunden abgebaut werden. Hier sollten Sie also nie in das Problem laufen, dass eine Verbindung zu früh durch Transfersysteme gekappt wird. Firefox baut seine Verbindungen nach 115 Sekunden wieder ab. Die Zahl ist knapp unter den 120 Sekunden, die Microsoft für Office 365 Verbindungen empfiehlt.

Weitere Links