BaseHTTPServer - Basisklassen zur Implementierung von Webservern

Link zu Doug Hellmanns Original Artikel

Zweck: Das BaseHTTPServer Modul beinhaltet Klassen, die die Grundlage eines Webservers bilden können.
Python: ab 1.4

Der BaseHTTPServer benutzt Klassen des SocketServer zur Bereitstellung der Basisklassen eines Webservers. HTTPServer kann direkt genutzt werden, aber der BaseHTTPRequestHandler ist dazu gedacht, so erweitert zu werden, dass er jede Protokoll-Methode behandelt (GET, POST, etc).

Ein einfaches GET Beispiel

Um eine HTTP Methode in Deiner, eine Anfrage (request), verarbeitenden Klasse zu unterstützen, implementiere eine Methode do_METHOD(), wobei METHOD durch den Namen der HTTP Methode ersetzt werden muss. Zum Beispiel: do_GET(), do_POST(), etc. Aus Konsistenzgründen erwartet die Methode keine Argumente. Alle Parameter des Requests werden durch den BaseHTTPRequestHandler geparst und als Instanzattribute gespeichert, so dass sie leicht abgefragt werden können.

Dieser Beispiel Request Verarbeiter illustriert, wie man eine Antwort (response) dem Client zurückliefert und zeigt einige lokale Attribute, die hilfreich bei der Erstellung der response sein können.

from BaseHTTPServer import BaseHTTPRequestHandler
import urlparse

class GetHandler(BaseHTTPRequestHandler):
    
    def do_GET(self):
        parsed_path = urlparse.urlparse(self.path)
        message = '\n'.join([
                'CLIENT VALUES:',
                'client_address=%s (%s)' % (self.client_address,
                                            self.address_string()),
                'command=%s' % self.command,
                'path=%s' % self.path,
                'real path=%s' % parsed_path.path,
                'query=%s' % parsed_path.query,
                'request_version=%s' % self.request_version,
                '',
                'SERVER VALUES:',
                'server_version=%s' % self.server_version,
                'sys_version=%s' % self.sys_version,
                'protocol_version=%s' % self.protocol_version,
                '',
                ])
        self.send_response(200)
        self.end_headers()
        self.wfile.write(message)
        return

if __name__ == '__main__':
    from BaseHTTPServer import HTTPServer
    server = HTTPServer(('localhost', 8080), GetHandler)
    print 'Starte server, drücke <Ctrl-C> zum Beenden'
    server.serve_forever()

Die Nachricht wird zusammengesetzt und nach self.wfile geschrieben, dem file handle für den Antwort - Socket. Jede Antwort benötigt einen Antwortcode, dieser wird durch self.send_response() gesetzt. Wenn ein Fehlercode benutzt wird (404, 501, etc) wird, sofern keine Nachricht mit dem Fehlercode übergeben wurde, eine passende Fehlernachricht in den Header eingebunden.

Um den request handler eines Servers auszuführen, wird er einfach dem Konstruktor des HTTPServer übergeben, so wie im __main__ Abschnitt des Beispielprogramms.

$ python BaseHTTPServer_GET.py
Starte server, drücke  zum Beenden

Benutze in einem separaten Terminalfenster curl für den Aufruf:

$ curl -i http://localhost:8080/?foo=barHTTP/1.0 200 OK
Server: BaseHTTP/0.3 Python/2.5.1
Date: Sun, 09 Dec 2007 16:00:34 GMT

CLIENT VALUES:
client_address=('127.0.0.1', 51275) (localhost)
command=GET
path=/?foo=bar
real path=/
query=foo=bar
request_version=HTTP/1.1

SERVER VALUES:
server_version=BaseHTTP/0.3
sys_version=Python/2.5.1
protocol_version=HTTP/1.0

Threading und Forking

Der HTTPServer ist eine einfache Unterklasse des SocketServer.TCPServer und benutzt keine Threads um mehrfache Anfrage abzuarbeiten. Um threading oder forking hinzuzufügen müssen sie eine neue Klasse erzeugen, die die entsprechende mix-in Klasse des SocketServer benutzt.

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from SocketServer import ThreadingMixIn
import threading

class Handler(BaseHTTPRequestHandler):
    
    def do_GET(self):
        self.send_response(200)
        self.end_headers()
        message =  threading.currentThread().getName()
        self.wfile.write(message)
        self.wfile.write('\n')
        return

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """Verarbeite Anfragen in einem separaten thread."""

if __name__ == '__main__':
    server = ThreadedHTTPServer(('localhost', 8080), Handler)
    print 'Starte Server, drücke <Ctrl-C> zum Beenden'
    server.serve_forever()

Jedes mal, wenn eine Anfrage hereinkommt, wird ein neuer Thread erzeugt.

$ curl http://localhost:8080/
Thread-1
$ curl http://localhost:8080/
Thread-2
$ curl http://localhost:8080/
Thread-3

Ein Wechsel vom ThreadingMixIn Server zum ForkingMixIn Server würde unter Verwendung von Prozessen statt Threads zu ähnlichen Ergebnissen führen

POST (Senden)

Post Anfragen zu Unterstützung erfordert etwas mehr Arbeit, da die Basisklasse die Formulardaten nicht für uns parst. Das cgi Modul stellt die FieldStorage Klasse zur Verfügung, mit der ein Formular geparst werden kann.

from BaseHTTPServer import BaseHTTPRequestHandler
import cgi

class PostHandler(BaseHTTPRequestHandler):
    
    def do_POST(self):
        # Parse die gesendetet Formulardaten
        form = cgi.FieldStorage(
            fp=self.rfile,
            headers=self.headers,
            environ={'REQUEST_METHOD':'POST',
                     'CONTENT_TYPE':self.headers['Content-Type'],
                     })

        # Beginne die Antwort
        self.send_response(200)
        self.end_headers()
        self.wfile.write('Client: %s\n' % str(self.client_address))
        self.wfile.write('Pfad: %s\n' % self.path)
        self.wfile.write('Formulardaten:\n')

        # Melde den Inhalt des gesendeten Formulars zurück.
        for field in form.keys():
            field_item = form[field]
            if field_item.filename:
                # Das Feld enthält eine hochgeladene Datei
                file_data = field_item.file.read()
                file_len = len(file_data)
                del file_data
                self.wfile.write('\Hochgeladen %s (%d Bytes)\n' % (field,
                                                                 file_len))
            else:
                # Regulärer Formularwert
                self.wfile.write('\t%s=%s\n' % (field, form[field].value))
        return

if __name__ == '__main__':
    from BaseHTTPServer import HTTPServer
    server = HTTPServer(('localhost', 8080), PostHandler)
    print 'Starte Server, benutze <Ctrl-C> zum Beenden.'
    server.serve_forever()

Wenn wir wieder curl benutzen, können wir Formulardaten einbinden, welches automatisch POST setzt. Das letzte Argument -F datafile=@BaseHTTPServer_GET.py, schickt den Inhalt der Datei BaseHTTPServer_GET.py um zu demonstrieren wie Dateidaten von einem Formular gelesen werden.

$ curl http://localhost:8080/ -F name=dhellmann -F foo=bar -F  datafile=@BaseHTTPServer_GET.py
Client: ('127.0.0.1', 51128)
Pfad: /
Formulardaten:
        name=dhellmann
        foo=bar
        Hochgeladen datafile (2222 Bytes)

Fehler

Die Fehlerverarbeitung wird einem mit send_error() einfach gemacht. Übergebe einfach den passenden Fehlercode und eine optionale Fehlermeldung und die vollständige Antwort (mit Header, Statuscode und body) wird für dich erzeugt.

from BaseHTTPServer import BaseHTTPRequestHandler

class ErrorHandler(BaseHTTPRequestHandler):
    
    def do_GET(self):
        self.send_error(404)
        return

if __name__ == '__main__':
    from BaseHTTPServer import HTTPServer
    server = HTTPServer(('localhost', 8080), ErrorHandler)
    print 'Starte Server, drücke <Ctrl-C> zum Beenden'
    server.serve_forever()

In diesem Fall wird der Fehlercode 404 zurückgeliefert.

$ curl -i http://localhost:8080/
HTTP/1.0 404 Not Found
Server: BaseHTTP/0.3 Python/2.5.1
Date: Sun, 09 Dec 2007 15:49:44 GMT
Content-Type: text/html
Connection: close

<head>
<title>Error response</title>
</head>
<body>
<h1>Error response</h1>
<p>Error code 404.

<p>Message: Not Found.
<p>Error code explanation: 404 = Nothing matches the given URI.
</body>

Siehe auch

BaseHTTPServer

Die Dokumenation der Standardbibliothek für dieses Modul.