Request-Header, Response-Header und ggf. der Message-Body sind Bestandteiler eines jeden HTTP-Request
Da es über HTTP ungezählte Artikel gibt, wird sich dieser hier mit ein paar Besonderheiten begnügen. Die erste Besonderheit ist die etwas irreführende Bezeichnung Hypertext Transfer Protocol
, denn HTTP überträgt nicht nur Text sondern auch reine Binaries, also Bytesequenzen mit Bytes beliebiger Wertigkeiten von 0x0
bis 0xFF
.
Einfacher gehts nicht: Ein HTTP-Client, z.B. ein gewöhnlicher Browser, auch User-Agent genannt, sendet einen Request zum Webserver. Nachdem beim Webserver alles angekommen ist, sendet dieser eine Response zurück zum Client. Für den Datentransport wird ein sogenanntes Socket erstellt, darunter liegt das Protokoll TCP/IP
.
Das Socket können wir uns wie eine Röhre (Pipe) vorstellen: Beim Request werden ein paar Bytes in die Pipe geschoben und als Response werden Bytes aus der Röhre herausgelesen. Für Client und Server ist diese Pipe transparent. Etwas genauer ausgedrückt, besteht jeder Request aus einem oder mehreren Request-Header
und dem Message-Body
, nach diesem Schema:
GET /index.html HTTP/1.0 Host: example.com
PUT /index.html HTTP/1.0 Host: example.com Content-Length: 10 Hallo Welt
Die Unterschiede fallen sofort ins Auge, zunächst ist mit GET
oder PUT
die Request-Methode qualifiziert. Mit Request-Method GET
wird i.d.R. kein Message-Body gesendet und damit gibt es auch keinen Header Content-Length
.
Grundsätzlich folgt dem Request-Header eine Leerzeile und falls vorhanden, kommt danach der Message-Body. Dies erfordert dann die Längen-Angabe im Request-Header Content-Length: 10 (= Anzahl der Bytes des Message-Body).
HTTP/1.1 200 OK Date: Sun, 20 Oct 2001 13:30:08 GMT Server: Apache Content-Language: de Cache-Control: no-cache, must-revalidate, proxy-revalidate, no-x-cache Expires: Thu, 28 Nov 2001 01:01:01 GMT Set-Cookie: FRAMEWORK=c47506d360f8370806018bf92a166f70 Vary: Accept-Encoding Connection: close Content-Type: text/html; charset=UTF-8 <!DOCTYPE HTML> ...
Anmerkung: Der Server antwortet mit HTTP/1.1
, obwohl 1.0
angefordert wurde. Wesentlich ist der Response-Header Connection: Close
, das heißt, dass der Server nach dem Senden der Response die Verbindung zum Client schließt. Und das macht ein Webserver nach jedem Request-Response-Zyklus, wenn der Client entweder HTTP/1.0
angefordert oder den Header Connection: Close
gesendet hat.
Wir können mehrere Requests in einer einzigen Verbindung zum Server senden, nach folgendem Schema:
GET /index.html?x=y HTTP/1.1 Host: example.com Connection: Keep-Alive
Der Server wird die Verbindung offenhalten, solange, bis entweder ein konfiguriertes Timeout erreicht wurde, oder bis ihn ein Request erreicht mit dem Header Connection: Close
. Bis dahin bewegen sich alle Daten hinein in das Pipe in Richtung Webserver. Jeder einzelne Request zu einer persistent Connection enthält vollständige Header, ggf. verschiedene Request-Methoden und Message-Bodies.
Wenn nichts mehr gesendet wurde, kann der Client daran gehen, das Socket auszulesen, diese Response enthält alle Responses zusammengefasst in einer Response. Es kommt nun auf den Client an, wie er mit einer solchen Response umgeht. Beispielsweise wird ein Browser eine HTML-Seite per HTTP/1.1
und Connection: Keep-Alive
requesten, weil außer text/html
i.d.R. auch noch Grafiken und andere Inhalte gesondert anzufordern sind. Gegenüber HTTP/1.0
ergibt sich somit ein erheblicher Zeitgewinn.
Anmerkung: Wenn der Server die Verbindung offenhält, kann er währenddessen auf den Gateway nach CGI/1.1
zugreifen, verschiedene Scripts oder andere Ressourcen laden. Persistent Connection heißt jedoch nicht, dass dem Webserver nachgelagerte CGI-Scripts weiterlaufen.
Wenn mehrere Requests in einer persistent Connection an den Server gesendet wurden, ist die Response eine Sequenz in welcher mehrere Responses aufeinanderfolgen. Ein Deserializer hat nun die Aufgabe, die einzelnen Serverantworten wieder voneinander zu trennen. Damit dies fehlerfrei möglich ist, muss ein jeder Webserver die Standards konsequent einhalten. Maßgeblich hierzu ist:
\r\n\r\n (CRLFCRLF)
erzeugt,Content-Length
angegeben ODERTransfer-Encoding: chunked
hat jeder Chunk eine eigene Längenangabe. Zum Deserialisieren ergeben sich somit genau drei Fälle, die, nachdem das Ende der Header (Leerzeile) festgestellt wurde, zu untersuchen sind (Pseudocode):
if Content-Length: 123 { lese 123 bytes } elsif Transfer-Encoding: chunked { lese jeden Chunk mit der für ihn angegbenen Länge } else { Lese einfach solange bis keine Daten mehr kommen }
Das Lesen selbst erfolg dafür direkt aus dem zwischen Client und Server bestehenden Socket.
GET http://rolfrost.de:80/index.html HTTP/1.0 Connection: Close Host: www-proxy.t-online.de
So sehen die Header aus, das Beispiel erklärt sich von selbst: Gut zu sehen, wo der Proxy-Server einzutragen ist und an welcher Stelle zu notieren ist, welche Webressource angefordert werden soll.
Grundsätzlich können Sie sich unendlich viele Namen für Request-Methoden ausdenken und Daten sowohl im URI
als angehängten QUERY_STRING
als auch in Message-Body
übertragen. Es ist jedoch so, dass je nach Webserver und -Konfiguration die Response nicht unbedingt Ihren Erwartungen entsprechen muss. Bei einem HEAD
-Request wird ein normal konfigurierter Apache-Webserver
niemals Daten im Message-Body
senden, auch dann nicht, wenn der Response-Header Content-Length
gesendet wird.
Hier beschrieben und zum Download bereitgestellt ist es ein zweckmäßiger Ersatz für LWP::UserAgent
weil es einfacher in der Handhabe ist. Es eignet sich hervorragend zur Erstellung von HTTP-Clients
unbeschränkter Aufgabenstellungen. Mein Perlmodul HTTPRequest
kommt vor Allem mit Persistent Connections
bestens zurecht.
Datenschutzerklärung: Diese Seite dient rein privaten Zwecken. Auf den für diese Domäne installierten Seiten werden grundsätzlich keine personenbezogenen Daten erhoben. Das Loggen der Zugriffe mit Ihrer Remote Adresse erfolgt beim Provider soweit das technisch erforderlich ist. sos@rolfrost.de.