Es soll ein einfacher Web-Server implementiert werden, der ein beim Aufruf übergebenes Verzeichnis auf einen frei wählbaren Port per HTTP-Protokoll exportiert. Folgende Features soll der Server beherrschen:
STATUS, SHUTDOWN, ABORT, REREAD
oder LIST ohne weitere Parameter mit einem
Zeilenendezeichen (CR oder LF) an den Serverport, der Server soll
dann entsprechend reagieren. Details dazu siehe Beschreibung
Admin-Protokoll.
swebd [-h|-?] [-dv]
[-i ] [-p ] [-a ]
/lehre/unix/ss06/swebd.html| [/lehre/unix/ss06/swebd.html|] ...
Das HTTP-Protokoll ist im RFC 2068 beschrieben. Der Server soll eine Untermenge dieses Protokolls verstehen und bearbeiten, allerdings ist dabei auf vollständige Kompatiblität zu Clients zu achten, d.h. nicht implementierte Operationen sind protokollgemäß abzulehnen. Obligatorische Felder sind in jedem Fall zu implementieren. Im folgenden folgt hier eine einfache Zusammenfassung des Protokolls:
Der Client baut die Verbindung per TCP zum Server auf und schickt seine
Anfrage (Request) auf mehrere Zeilen verteilt zum Server. Die erste Zeile
hat dabei folgendes Format: <REQ> (Leerzeichen) <URI>
(Leerzeichen) <Version> (CRLF). Dabei bezeichnet REQ den
Request-Typ (GET, HEAD oder PUT), URI ist die gewünschte Adresse und
Version bezeichnet die verwendete HTTP-Version. Browser tragen hier
üblicherweise HTTP/1.0 oder HTTP/1.1 ein, der Server soll die
Protokoll-Version 1.1 verstehen können. Nach diesen Zeilen folgen
Message-Header, die weitere Parameter oder Optionen zu dieser Anfrage
beinhalten. Diese Header-Felder sind durch ihren Namen gefolgt von
einem Doppelpunkt und den nachfolgenden Inhalt gekennzeichnet
(ähnlich dem E-Mail-Format). Ein Message-Header wird durch die
Zeilenende-Zeichen CRLF=\r\n abgeschlossen.
In dieser Weise folgen mehrere Message-Header, deren Ende dann durch
eine leere Zeile angezeigt wird. Die einfachste Anfrage nach
HTTP/1.1-Protokoll kann so aussehen:
GET /index.html HTTP/1.1 Host: localhostEin typische Anfrage eines richtigen Browsers sieht z.B. so aus:
GET /index.html HTTP/1.1 User-Agent: Opera/8.51 (X11; Linux i686; U; de) Host: localhost:4711 Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, ... Accept-Language: de,en;q=0.9,ru;q=0.8 Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1 Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Connection: Keep-Alive
Header-Felder, die der Server bei einer Response
liefern muß, sind: Content-Type, Content-Encoding und
Content-Length. Andere Felder können optional
implementiert werden. Insbesondere Sinn macht hier die Unterstützung
von abgebrochenen Downloads, in dem die Header-Felder Range,
Content-Range und Accept-Range ausgewertet und
bearbeitet werden. Eine Implementation dieses Features wirkt sich
positiv auf die Rücksprache aus!
Bei Unklarheiten kann man sich einfach mit dem Tool netcat
(aufzurufen mit nc, installiert in /home/pub/bin) die Anfrage
eines Browsers oder die Reaktion eines Servers anzeigen lassen.
Ein Browser kann eine on-the-fly Komprimierung der übertragenden Daten
anforden. Dazu sendet er im Accept-Encoding Header-Feld die
Namen der von ihm unterstützten Verfahren. Der Server zeigt dann die
Verwendung eines dieser Verfahren durch die Nennung im Feld
Content-Encoding an.
Der Server soll in unserem Fall die Typen deflate, gzip
und x-gzip unterstützen. Die letzten beiden Typen sind dabei
effektiv dasselbe, als würde man die Datei vorher mit gzip
komprimieren. Zum Testen könnt ihr daher für gzip das externe
Kommando gzip in die Ausgabe einschleifen.
Das deflate-Format ist die Basis von gzip, wird allerdings von
gzip nicht direkt als Ausgabeformat unterstützt. Zur Unterstützung dieses
Formats ist es daher nötig, die zlib-Bibliothek zu verwenden.
Die nötige Dokumentation dazu findet ihr in der Header-Datei zlib.h.
Es ist sinnvoll, die Kompression in einen Kind-Prozeß auszulagern, so
kann man den Code stark vereinfachen. Zum Durchschleifen der Ausgabe
könnt ihr dann die Pipeline-Mechanismen aus der Shell-Aufgabe verwenden.
Wenn die Komprimierung benutzt wird, braucht das Header-Feld
Content-Length nicht gesetzt zu sein.
In der HTTP/1.1-Version findet sich ein Verb für das einfache Hochladen von Daten zu einer URL. Dabei wird die Methode PUT benutzt, ansonsten ist die Syntax identisch mit der GET-Methode. Nach dem Ende des Headers sendet der Client die eigentlichen Daten ohne weitere Umkodierung. Um Sicherheitsproblemen vorzubeugen, ist von der URI nur der Dateiname zu benutzen, d.h. der Teil nach dem letzten Slash. Die gesendeten Daten sollen dann in einer Datei mit diesen Namen unter dem beim Start angegebenen Verzeichnis angelegt werden.
Wenn dem Server der Name eines gültigen Verzeichnisses übergeben wird, dann soll der Server den Inhalt dieses Verzeichnisses im HTML-Format an den Browser übermitteln. Ihr könnt dabei folgendes Grundgerüst verwenden:
<html><head><title>Directory content - Name des Verzeichnisses</title></head>
<body>
<table border="0">
<tr><th>Name</th><th>Type</th><th>Size</th><th>Time</th></tr>
<tr><td>foobar</td><td>directory</td><td> </td>
<td>2006-05-29 12:35:26</td></tr>
<tr><td>baz.bar</td><td>BAR file</td><td>27304 Bytes</td>
<td>2006-05-29 12:37:10</td></tr>
<tr><td>vl1.pdf</td><td>application/pdf</td><td>185618 Bytes</td>
<td>2006-04-21 23:56:14</td></tr>
</table>
</body>
</html>
Der Name des Verzeichnisses ist dabei im title-Tag einzusetzen. Gerne
dürft ihr das Ausgabeformat verändern, solange mindestens die oben
angegebenen vier Felder vorhanden sind. Im Feld ''Type'' soll entweder
(sofern vorhanden) der MIME-Typ der Datei eingetragen werden oder die
Endung des Dateinames mit dem Zusatz '' file'' (siehe Beispiel).
Um die Funktionalität des Hochladens nutzen zu können, soll ein
spezieller Client entwickelt werden. Dieser Client kann eine oder
mehrere Dateien per HTTP-PUT (Version 1.1) an den Server übermitteln.
Dabei sollen
sowohl die Server-Adresse als auch die Dateien als Parameter übergeben
werden. Dabei ist die Server-Adresse stets der erste Parameter:
$ putclient localhost:8080 foo.bar *.txt
Es reicht aus, wenn der Client mit dem Server zusammenarbeit.
Der Server soll auf einem frei wählbaren Admin-Port kurze Kommandos entgegennehmen. Dabei sollen folgende Features implementiert werden:
OK
(SHUTDOWN, ABORT, REREAD) und beendet danach die Verbindung.
select(3c), poll(3c), listen(3c), bind(3c), accept(3c),
connect(3c), readdir(2), stat(2), mmap(2), fwrite(3),
netcat(/home/pub/man,1)
fprintf, fgets, fread, fwrite) an.
Ihr könnt einen Socket-Deskriptor mit fdopen(3) in eine
FILE-Struktur überführen.