Das HTTP37 ist
verantwortlich für das Web, was ja schon an URLs38 (zum Beispiel http://linide.sf.net/
) erkennbar ist.
Wie SMTP () ist es ein ASCII-basiertes Protokoll, das heißt man kann
telnet
benutzen, um im Internet zu surfen...
Heute werden HTTP 1.0 (RFC 1945) und 1.1 (RFC 2616) benutzt.
HTTP/1.1 hat einige Verbesserungen gegenüber HTTP/1.0 erhalten, zum Beispiel Keep-Alive39und virtuelle Hosts. Beiden dieser Features ist ein eigener Abschnitt gewidmet.
Der Standard-TCP-Port für HTTP (1.0 und 1.1) ist 80
.
Außerdem gibt es noch HTTP-Proxies40. Dort wird, je nach Zielhost,
sowohl HTTP/1.0 als auch HTTP/1.1 verwendet. Oft benutzte Ports sind 3128
,
8080
und 79
; Einen Standardport gibt es nicht.
Um HTTP zu verstehen, muss man wissen, wie Internetseiten eigentlich addressiert werden.
Da gibt es zum einen die
www.foobar.de
. Schlecht: Hier wird keine Adresse
beschrieben, sondern lediglich einen Hostnamen.
http://linide.sf.net/
. Gut. Alle für
den Browser wichtigen Bestandteile sind vorhanden: Protokoll (HTTP oder
FTP), Host, und Pfad (/
).
protokoll://host:portpfadDer Pfad ist wie bei URLs Unix-like, also keine Backslashes (
\
)!
Wenn man nun allerdings einem normalen HTTP-Server eine URL vorsetzt, kann er
damit nichts anfangen: Schließlich soll er nicht als Proxy (siehe ) fungieren, sonden nur seine eigenen Seiten ausliefern.
Deswegen benutzt man bei HTTP nur den Pfad (samt dem ersten /
).
Ein einfacher GET
-Request ("`zeig mir den Inhalt (Quelltext) einer Seite
an!"') für die URL http://linide.sf.net/
sieht dann so aus:
linide.sf.net
auf Port 80.
>GET / HTTP/1.0Damit ist unser Befehl abgeschlossen42und muss noch mit einer Leerzeile terminiert werden:
>
<HTTP/1.1 200 OKDiese Zeile übermittelt den Statuscode. Hier ist er
200
, was einem
OK
(direkt nachfolgend) entspricht.
Dann kommen einige Header-Felder, in der gleichen Syntax wie wir sie schon
von den Mails her kennen (siehe ):
< <<!DOCTYPE...
Als Minimalanforderung wird bei HTTP/1.1 ein zusätzliches Header-Feld vom
Client erwartet, Host
:
>GET / HTTP/1.1 >Host: linide.sf.net >
Neu dazugekommen ist also nur der Host
-Header. Dieser wird von vielen
Hosting-Angeboten genutzt, die ihren Kunden nur Webspace, aber keinen eigenen
Server. So kann also ein Kunde eine eigene Domain haben, ohne dass ein
weiterer Server benötigt wird.
Der Webserver unterscheidet sein Webangebot dann einfach anhand jenes Headerfeldes.
Zu beachten ist auch, dass die Verbindung bei HTTP/1.1 nicht nach erfolgtem
Reply automatisch geschlossen wird. Dies liegt am bei HTTP/1.1 standardmäßig
aktiviertem Keep-Alive (siehe dazu ).
HTTP-Proxies sind wirklich cool, wie später im Kapitel
über die Automatisierung mit Hilfe von Skripten () noch
näher erläutert wird.
Proxies übernehmen das Auflösen des (Ziel-)Hostnamens zu einer IP-Adresse
(siehe dazu ) und verbinden sich dann, stellvertretend für den
eigenen Computer, mit dem eigenen Computer.
Die Antwort liefert der Proxy dann an den eigenen Computer zurück.
Auch hier unterscheidet sich das praktische Vorgehen wenig von HTTP/1.0 oder HTTP/1.1, nur dass
3128
, 8080
und 79
.
Um zum Beispiel über die Proxy proxy.as-netz.de:8080
43 die Seite
http://linide.sf.net/uebersicht.html
abzurufen, tippt man nach Verbinden
mit der Proxy ein:
>GET http://linide.sf.net/uebersicht.html HTTP/1.1 >Host: linide.sf.net >Die Antwort (samt Statuscodes) ist dann dieselbe wie bei normalem HTTP.
Bis jetzt sollte also folgendes klar sein:
Typ | Standard-Port(s) | Adressenübergabe | Host -Angabe |
HTTP/1.0 | 80 | Nur Pfad | nein |
HTTP/1.1 | 80 | Nur Pfad | ja |
HTTP-Proxy | 3128, 8080 | Volle URL | je nach Zielprotokoll |
Aber jetzt wird es erst richtig spanned: Oft will man aus irgendwelchen vollkommen unbekannten Gründen44 eine Website abrufen. Im Browser funktioniert das absolut unproblematisch, in automatisierten Skripten jedoch nicht45.
Die Magie liegt im sogenannten Referer. Der Referer ist ein spezielles Header-Feld, was die zuletzt besuchte Website enthält.
So kann ein Hoster zum Beispiel sehen, von welchen Ländern (Top-Level-Domains), von welchen Unterseiten, usw. kommt. Und er kann so auch prüfen, ob man erst die Homepage "`angeklickt"' hat, und nicht einen direkten Link benutzt.
Man kann den Referer aber natürlich auch faken. Angenommen, wir wollen via
Telnet die Seite http://www.microsoft.com/unterseite.asp
abrufen, der
Seitenbetreiber zwingt uns aber, zuvor auf http://www.microsoft.com/
gewesen zu sein, verwendet man
>GET /unterseite.asp HTTP/1.1 >Host: www.microsoft.com >Referer: http://www.microsoft.com/ >Dem
Referer
-Feld folgt also eine URL (auch bei Proxies).
Im Kapitel über die Automatisierung von HTTP () werden dazu
noch einige Beispiele folgen.
Manchmal möchte man wissen, welche Daten ein Server nach passieren aller
eventuellen Proxies tatsächlich erhält. Dazu benutzt man den nru bei HTTP/1.1
vorhandenen TRACE
-Request:
>TRACE http://www.linux.org/ HTTP/1.1 >Test-Header: Test-Inhalt > <HTTP/1.1 200 OK <Date: Wed, 06 Aug 2003 16:46:47 GMT <Server: Apache/1.3.28 (Linux) mod_perl/1.28 mod_ssl/2.8.15 OpenSSL/0.9.7a <Content-Type: message/http <X-Cache: MISS from true <Proxy-Connection: close < <TRACE / HTTP/1.1 <Cache-Control: max-age=259200 <Connection: keep-alive <Host: www.linux.org <Test-Header: Test-Inhalt <Via: 1.0 true:3128 (Squid/2.4.STABLE1) <X-Forwarded-For: 10.0.0.3 <Im Beispiel geht der Request also über eine Proxy (Squid), der ursprüngliche Absender war
10.0.0.3
46.
Manchmal bekommt man beim Surfen auch die Meldung "`Tut uns wirklich sehr sehr
leid, aber wir optimieren unsere Seiten auf den Microsoft (R)(TM)(C)
Internet (R)(TM)(C) Explorer (R)(TM)(C), Sie werden jedoch Mozilla. Klicken Sie
hier, um sich die aktuelle Version des IE herunterzuladen..."'. Die Antwort auf
die Frage "`Woher wissen die eigentlich, welchen Browser ich benutze?"' liegt
wieder in einem zusätzlichen Header-Feld, das der Browser dem Server sendet:
User-Agent
:
>GET / HTTP/1.1 >Host: microsoft.com >User-Agent: Mozilla/5.0 (compatible; Konqueror/3.1; Linux) > <HTTP/1.1 200 OK <Server: Microsoft IIS Bug-free(TM) <Content-Type: text/plain < <Sorry, aber diese Seite...
Ein Seitenbetrieber kann die vom Client mitgeschickten Header aber auch für
gute Zwecke nutzen: Verlangt man zum Beispiel http://debian.org/
, so
bekommt man (je nach Browser und Einstellungen) automatisch die deutsche
Version angezeigt. Diese Information erhält der Server über das
Accept-Language
-Headerfeld:
>GET / HTTP/1.1 >Host: debian.org >Accept-Language: de,en > <HTTP/1.1 200 OK <Content-Type: text/plain < <Deutsche Version
Üblicherweise werden mehrere von der ISO normierten Sprachen-Kürzel mit einem
,
(Komma) getrennt.
Mit Keep-Alive wird die Verbindung zum Server auch nach einem Request (samt Reply) offen gehalten. Dies ist besonders bei HTML-Seiten mit vielen Bildern sehr nützlich, da das Verbinden mit dem Server oftmals der längste Teil einer HTTP-Transaktion ist. Bei einer Seite mit 20 Bildern wären 21 Verbindungen erforderlich!
Bei HTTP/1.1 ist Keep-Alive standardmäßig aktiviert. Möchte man die Verbindung
nach einem Request schließen, so muss man bei dem letzten Request explizit den
Connection
-Header benutzen:
>Connection: closeEin letzter Request könnte also zum Beispiel so aussehen:
>GET /letztes-bild.png HTTP/1.1 >Host: der-server.org >Connection: Close >
Möchte man bei HTTP/1.0 Keep-Alive aktivieren, so kann man
>Connection: Keep-Alivebenutzen. Allerdings ist Keep-Alive bei HTTP/1.0 im Zusammenhang mit Proxies nicht zu empfehlen: Wenn die Proxy nicht aktiviertes Keep-Alive erkennt, so kann sie auch nicht das Ende einer Übertragung erkennen. Also würde der Proxy unbegrenzt lange (=bis der Timeout eintritt) auf die Antwort des Servers warten...
Jetzt gibt es allerdings en großes Problem: Wenn die Verbindung nicht
geschlossen wird, woher weiß dann ein Client, dass die Seite fertig übertragen
wurde? Eine einfache Antwort bietet der Content-Length
-Header, der die
Anzahl der Bytes der Übertragung angibt47.:
>HTTP/1.1 200 OK >Content-Length: 12 > >Hallo, Welt!
Bei CGI-Skripten kann der Server aber nicht wissen, wir groß die Seite ist, sie
wird ja dynamisch generiert. Aber auch dazu gibt es eine Lösung:
Chunked-Encoding. Bei diesem Verfahren wird eine Antwort in viele kleinere
Teile zerlegt, wobei bei jedem kleineren Teil die Content-Length
bekannt
ist:
>GET / HTTP/1.1 >Host: somehost > >HTTP/1.1 200 OK >Date: Thu, 07 Aug 2003 08:31:29 GMT >Transfer-Encoding: chunked >Content-Type: text/html > >ea1 >[Jetzt folgen ea1 (=3745 dezimal) Bytes] >f7 >[Jetzt folgen nochmal f7 (=247 dezimal) Bytes] >0 >
Auch kann ein Client, zum Beispiel via POST
, Chunked-Encoding verwenden,
mehr dazu .
Laut RFC 2616 muss jeder HTTP/1.1 Client und Server Chunked-Encoding unterstützen.
Manchmal möchte man nur einen bestimmten Bereich einer Datei herunterladen, zum
Beispiel wenn ein Download abgebrochen wurde und dann später erneut aufgenommen
werden soll. Dazu gibt es bei HTTP/1.1 den Range
-Header:
>GET /proxy.log HTTP/1.1 >Host: mars > <HTTP/1.1 200 OK <Content-Type: text/plain < <Server listening at port 8017 <Date ........................ Wed Aug 6 14:09:10 2003 <URL ......................... http://www.google.de:80/search < >GET /proxy.log HTTP/1.1 >Host: mars >Range: bytes=17-42 > <HTTP/1.1 206 Partial Content <Accept-Ranges: bytes <Content-Length: 26 <Content-Range: bytes 17-42/1158 <Content-Type: text/plain < <at port 8017 <Date ........
Cookies, definiert in RFC 2109, können kleinere Informationen über den Benutzer speichern, zum Beispiel welche Waren sich in seinem E-Warenkorb seines E-Shops befinden.
Auch können sie jedem Benutzer eine von der IP unabhängige ID zuweisen, um jeden Benutzer bei einem späteren Besuch wiedererkennen zu können.
Cookies werden48 vom Server durch ein Headerfeld gesetzt (im Beispiel ein verbessertes Google):
>GET / HTTP/1.1 >Host: www.google.de > <HTTP/1.1 200 OK <Date: Fri, 01 Aug 2003 09:17:00 GMT <Content-Type: text/html <Set-Cookie: PREF="ID=450b1d25610"; \ < expires="Sun, 17-Jan-2038 19:14:07 GMT"; Path="/"; \ < Domain=".google.de"; Version="1" < <<html><head>...Durch den
Set-Cookie
-Header haben wir ein Cookie erhalten. Die durch das
Cookie gesetzten Variablen sind:
PREF=...
Dadurch kann Google uns wieder erkennen und unsere Einstellungen (Sprache, etc.) speichern.
expires=...
Mit diesem Parameter legt Google fest, wann das Cookie verfällt, das heißt wann die gesetzten Einstellungen nicht mehr gültig sind49.
path=...
, domain=...
Diese Variable bestimmt den Zugriff auf den Cookie: Nur alle Dateien, ab
/
(in dem Fall also alle), der Domain irgendwas.google.de
haben
Zugriff auf das Cookie. www.altavista.de
darf also nicht den Inhalt des
Cookies erhalten.
Bei jedem weiteren Seitenabruf müssen alle für die jeweilige Seite relevanten
(also nicht verfallene Cookies) Cookies mit dem Cookie
-Headerfeld
übertragen werden, und zwar genau so, wie man sie erhalten hat. Auch muss
natürlich auf die Zugriffsbestimmungen geachtet werden.
Ein zweiter Request auf http://www.google.de/
, muss dann also so aus sehen:
>GET / HTTP/1.1 >Host: www.google.de >Cookie: PREF="ID=450b1d25610"; \ > expires="Sun, 17-Jan-2038 19:14:07 GMT"; $Path="/"; \ > $Domain=".google.de"; $Version="1" > <HTTP/1.1 200 OK <Date: Fri, 01 Aug 2003 09:23:13 GMT <Content-Type: text/html < <<html><head>...Besonderen Variablen, wie
Path
, Domain
und Version
, muss dabei ein
$
(Dollarzeichen) voranstehen. Die "`normalen"' Werte (PREF
, expires
)
werden "`roh"' gespeichert.
Hier fällt auch auf, dass der Server nicht ein neues Cookie setzen will
(Set-Cookie
-Header fehlt). Das bedeutet also, dass Google uns nun "`kennt"'.
Würden wir eine Einstellung verändern (Sprache, etc.) würden wir ein neues
Cookie erhalten. Wenn wir dieses veränderte Cookie nicht mitschicken würden, so
kämen wieder Googles Default-Einstellungen zum Einsatz.
Oft sind auf einer Website Formulare zu finden, um zum Beispiel bei einer Umfrage abzustimmen oder dem Webmaster eine Nachricht zukommen zulassen. In (X)HTML realisiert sieht das zum Beispiel so aus:
<form action="http://host/umfrage.php" method="get"> Name: <input type="text" name="name" /> <br /> Alter: <input type="text" name="alter" /> <br /> OS: <input type="text" name="os" /> <br /> <input type="submit" value="Abschicken!" /> </form>
Die Übertragung solcher Formulare ist über zwei HTTP-Requests möglich: Den
einfachen GET
-Request50 und den etwas schwieriger verwendbaren POST
-Request.
Der GET
-Request ist ja schon vom "`normalen"' Abrufen von
Websites bekannt, das heißt: Nichts neues Lernen!
Bei GET
werden die Feldnamen von den Feldinhalten mit einem =
getrennt, und dann an die URL, getrennt mit einem ?
, angehängt. Die
einzelnen Parameter unter sich werden mit einem &
getrennt (siehe Grafik
Zusammenfassend wird
?
,
&
und
=
Auch hierzu mehr im Kapitel über die Automatisierung der ganzen Vorgänge .
Beim POST
-Request werden die Felder nicht in der URL
gespeichert, sondern im HTTP-Request selbst. Dies ermöglicht Übertragungen
größer als einem Kilobyte.
Unser Beispielrequest sieht bei Verwendung von POST so aus:
>POST /umfrage.php HTTP/1.1 >Host: host >Content-Type: application/x-www-form-urlencoded >Content-Length: 29 > >name=Ingo&alter=15&os=Gentoo > <HTTP/1.1 200 OK <Date: Fri, 01 Aug 2003 10:17:48 GMT <Content-Type: text/html < <<html>>...Der Request ist also verlängert, nach der Leerzeile kommt nicht (wie etwa bei
GET
und HEAD
) die Antwort, sondern der Inhalt des Formulars.
Wichtig ist auch der Content-Length
-Header. Die Länge muss exakt mit der
Anzahl der Zeichen der Übertragung übereinstimmen. Kennt man die Länge nicht,
kann man auch hier Chunked-Encoding verwenden:
>POST /umfrage.php HTTP/1.1 >Host: host >Content-Type: application/x-www-form-urlencoded >Transfer-Encoding: chunked > >a >name=Ingo& >12 >alter=15&os=Gentoo >0 >
Oft verweist eine Adresse auf eine andere, zum Beispiel verweist
http://sf.net/
auf http://sourceforge.net/
. Während dies zum einen mit
Hilfe eines Meta-Feldes von (X)HTML möglich ist, ist die HTTP-Methode
(verfügbar sowohl bei HTTP/1.0 als auch bei HTTP/1.1) wesentlich eleganter, da
sie bei Dateien jedes Formats funktioniert, nicht nur bei HTML.
Erkennbar ist so eine Weiterleitung daran, dass statt des Statuscodes 200
302
zurückgeliefert wird. Im Location
-Headerfeld wird dann die
Zieladresse übermittelt.
Um also auf das Beispiel mit Sourceforge zurückzukommen:
80
von sf.net
, und verlangen
ganz normal /
:
>GET / HTTP/1.1 >
302 Found
:
<HTTP/1.1 302 Found
<Date: Fri, 18 Jul 2003 17:37:25 GMT <Server: Apache/1.3.27 (Unix) PHP/4.1.2 <X-Powered-By: PHP/4.1.2 <X-Accelerated-By: PHPA/1.3.3r1
<Location: http://sourceforge.net/
<Connection: close <Transfer-Encoding: chunked <Content-Type: text/html <
Wenn der Inhalt einer Seite vollkommen uninteressant, nur der Header von
Bedeutung ist, dann sehe ich keinen Zufall - dann erkenne ich die
Bestimmung51 und verwende den HEAD
-Request:
>HEAD / HTTP/1.1 >Host: hostname > <HTTP/1.1 200 OK <Date: Fri, 18 Jul 2003 19:00:37 GMT <Server: Apache/21.3.19 (GNU Gentoo/Hurd) mod_fastcgi/2.2.2 mod_dtcl <Content-Type: text/html <
Befehl | Wirkung |
GET /pfad |
Holt angegebene Datei |
GET /pfad?feld1=name1&feld2=name2 |
Sendet Parameter via GET |
HEAD /pfad |
Zeigt den Server-Header (Content-Type, etc.) einer Seite an |
Typische Header-Felder sind
Header-Feld | Bedeutung | Wird gesendet vom |
Host |
Hostname des Servers | Client |
Connection |
Keep-Alive benutzen? | Client |
Referer |
Zuletzt besuchte Seite | Client |
User-Agent |
Verwendeter Browser | Client |
Cookie |
Gesetzte Cookies | Client |
Accept-Language |
Gewünschte Sprachen | Client |
Server |
Benutzte Server-Software | Server |
Location |
Weiterleitung | Server |
Set-Cookie |
Cookie(s) setzen | Server |
Content-Type |
MIME-Typ der angeforderten Seite | Server |
Eine Beispielsitzung unter Verwendung der meisten Befehle und Header-Felder ist
GET
, auch noch weitere Headerfelder, wie zum Beispiel bei HTTP/1.1 das
Host
-Feld oder den Referer (siehe dazu proxy.as-netz.de:8080
4310.0.0.3
46