5. Übungblatt UNIX Praktikum

swebd: Prozesskommunikation, Sockets, Libraries, TCP/IP
Abgabe: 16. Juli 2006

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:

Folgende Kommandozeilenoptionen soll der Server beherrschen:
swebd [-h|-?] [-dv]
      [-i ] [-p ] [-a ]
      /lehre/unix/ss06/swebd.html| [/lehre/unix/ss06/swebd.html|] ...

HTTP-Protkoll

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: localhost
Ein 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.

Komprimierte Übertragung

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.

PUT-Feature

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.

HTML für Verzeichnisse

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).

Client

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.

Admin-Protokoll

Der Server soll auf einem frei wählbaren Admin-Port kurze Kommandos entgegennehmen. Dabei sollen folgende Features implementiert werden:

Ein Client schickt eines dieser Kommandos und schließt die Eingabe mit dem HTTP-Zeilenendezeichen CRLF ab. Der Server antwortet dann über die TCP-Verbindung mit den Daten (STATUS, LIST) oder einem OK (SHUTDOWN, ABORT, REREAD) und beendet danach die Verbindung.

Hinweise