Scopo | BaseHTTPServer include le classi che possono formare le basi per un server web. |
Versione Python | 1.4 e superiore |
A partire dal 1 gennaio 2021 le versioni 2.x di Python non sono piu' supportate. Ti invito a consultare la corrispondente versione 3.x dell'articolo per le versioni 3.x di Python
Il modulo BaseHTTPServer usa classi da SocketServer per creare le classi base per costruire dei server HTTP. HTTPServer può essere usato direttamente, mentre BaseHTTPRequestHandler è concepito per essere esteso per la gestione di ciascun metodo di protocollo (GET, POST, ecc.).
Per aggiungere supporto ad un metodo HTTP nella propria classe per la gestione della richiesta, si implementa il metodo
do_METHOD()
, rimpiazzando
METHOD
con il nome del metodo HTTP. Ad esempio
do_GET()
,
do_POST()
, ecc. Per consistenza, il metodo non riceve parametri. Tutti i parametri per le richieste sono analizzati da
BaseHTTPRequestHandler
e conservati come attributi di istanza dell'istanza di richiesta.
Questo esempio di gestore di richiesta illustra come restituire una risposta al client ed alcuni degli attributi locali che possono essere utili nella costruzione della risposta:
from BaseHTTPServer import BaseHTTPRequestHandler
import urlparse
class GetHandler(BaseHTTPRequestHandler):
def do_GET(self):
parsed_path = urlparse.urlparse(self.path)
message_parts = [
'VALORI DEL CLIENT:',
'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,
'',
'VALORI DEL SERVER:',
'server_version=%s' % self.server_version,
'sys_version=%s' % self.sys_version,
'protocol_version=%s' % self.protocol_version,
'', '',
'INTESTAZIONI RICEVUTE:',
]
for name, value in sorted(self.headers.items()):
message_parts.append('%s=%s' % (name, value.rstrip()))
message_parts.append('')
message = '\r\n'.join(message_parts)
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 'Server in esecuzione, usare <Ctrl-C> per interrompere'
server.serve_forever()
Il testo del messaggio viene assemblato e scritto in
wfile
, l'
handle
del file che incapsula la risposta del socket. Ogni risposta necessita di un codice di risposta, impostato tramite
send_response()
. Se viene usato un codice di errore (404, 501 ecc.), nell'intestazione viene incluso l'appropriato messaggio di errore predefinito. oppure può essere passato un messaggio con il codice di errore.
Per eseguire il gestore di richiesta in un server, lo si passa al costruttore di HTTPServer, come si vede nella porzione __main__ dello script di esempio.
Quindi si fa partire il server:
$ python BaseHTTPServer_GET.py Server in esecuzione, usareper interrompere
In un terminale separato si usa curl per accedervi:
$ curl -i http://localhost:8080/?foo=barHTTP/1.0 HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.6.6 Date: Tue, 07 Dec 2010 20:52:01 GMT VALORI DEL CLIENT: client_address=('127.0.0.1', 37029) (localhost.localdomain) command=GET path=/?foo=barHTTP/1.0 real path=/ query=foo=barHTTP/1.0 request_version=HTTP/1.1 VALORI DEL SERVER: server_version=BaseHTTP/0.3 sys_version=Python/2.6.6 protocol_version=HTTP/1.0 INTESTAZIONI RICEVUTE: accept=*/* host=localhost:8080 user-agent=curl/7.21.0 (i686-pc-linux-gnu) libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18
Il supporto per POST richiede un poco più di lavoro visto che la classe base non analizza i dati form. Il modulo cgi fornisce la classe FieldStorage che sa come analizzare il form, se viene fornita del corretto input.
from BaseHTTPServer import BaseHTTPRequestHandler
import cgi
class PostHandler(BaseHTTPRequestHandler):
def do_POST(self):
# Elabora i dati ricevuti nel form
form = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={'REQUEST_METHOD':'POST',
'CONTENT_TYPE':self.headers['Content-Type'],
})
# Begin the response
self.send_response(200)
self.end_headers()
self.wfile.write('Client: %s\n' % str(self.client_address))
self.wfile.write('User-agent: %s\n' % str(self.headers['user-agent']))
self.wfile.write('Path: %s\n' % self.path)
self.wfile.write('Dati form:\n')
# Rimanda le informazioni su ciò che era stato passato nel form
for field in form.keys():
field_item = form[field]
if field_item.filename:
# Il campo contiene un file che è stato inviato
file_data = field_item.file.read()
file_len = len(file_data)
del file_data
self.wfile.write('\tInviato %s as "%s" (%d bytes)\n' % \
(field, field_item.filename, file_len))
else:
# Valori normali nel form
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 'Server in esecuzione, usare <Ctrl-C> per interrompere'
server.serve_forever()
curl può includere i dati form nel messaggio che invia al server. L'ultimo parametro, -F datafile@BaseHTTPServer_POST.py invia il contenuto del file BaseHTTPServer_POST.py per illustrare la lettura dei dati del file dal form.
$ curl http://localhost:8080/ -F name=robbpy -F foo=bar -F datafile=@BaseHTTPServer_POST.py Client: ('127.0.0.1', 50904) User-agent: curl/7.21.0 (i686-pc-linux-gnu) libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18 Path: / Dati form: Inviato datafile as "BaseHTTPServer_POST.py" (1667 bytes) foo=bar name=robbpy
HTTPServer è una semplice classe derivata da SocketServer.TCPServer , e non usa thread o processi per gestire le richieste. Per aggiungere il threading od il forking, si crea una nuova classe che utilizza l'appropriato mix-in da SocketServer .
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):
"""Gestisce le richieste in un thread separato."""
if __name__ == '__main__':
server = ThreadedHTTPServer(('localhost', 8080), Handler)
print 'Server in esecuzione, usare <Ctrl-C> per interrompere'
server.serve_forever()
Ogni volta che perviene una richiesta, viene creato un nuovo thread o processo per gestirla.
$ curl http://localhost:8080/ Thread-1 $ curl http://localhost:8080/ Thread-2 $ curl http://localhost:8080/ Thread-3
La gestione degli errori è facilitata da
send_error()
. Basta passare l'appropriato codice di errore ed un messaggio di errore (opzionale), e l'intera risposta (compresi intestazioni, codici di stato e corpo) viene generata automaticamente.
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 'Server in esecuzione, usare <Ctrl-C> per interrompere'
server.serve_forever()
In questo caso viene sempre ritornato un errore 404.
$ curl -i http://localhost:8080/ HTTP/1.0 404 Not Found Server: BaseHTTP/0.3 Python/2.6.6 Date: Tue, 07 Dec 2010 20:12:42 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.</p> </body>
Il metodo
send_header()
aggiunge dati di intestazione alla risposta HTTP. Riceve due parametri, il nome dell'intestazione ed il valore.
from BaseHTTPServer import BaseHTTPRequestHandler
import urlparse
import time
class GetHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Last-Modified', self.date_time_string(time.time()))
self.end_headers()
self.wfile.write('Corpo della risposta\n')
return
if __name__ == '__main__':
from BaseHTTPServer import HTTPServer
server = HTTPServer(('localhost', 8080), GetHandler)
print 'Server in esecuzione, usare <Ctrl-C> per interrompere'
server.serve_forever()
Questo esempio imposta l'intestazione Last-modified alla data/ora corrente formattata secondo le specifiche RFC 2822 .
$ curl -i http://localhost:8080/ HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.6.6 Date: Tue, 07 Dec 2010 20:19:32 GMT Last-Modified: Tue, 07 Dec 2010 20:19:32 GMT Corpo della risposta
Vedere anche: