BaseHTTPServer – web サーバを実装するベースクラス

目的:BaseHTTPServer は web サーバの基本的なクラスを提供する
利用できるバージョン:1.4 以上

BaseHTTPServer モジュールは、HTTP サーバを構築するベースクラスを作るために SocketServer モジュールのクラスを使用します。 HTTPServer は直接的に使用できますが、 BaseHTTPRequestHandler は各プロトコルメソッド(GET, POST 等)を扱うように拡張するためにあります。

HTTP GET

リクエストハンドラクラスの HTTP メソッドをサポートするために、引数を取らない do_METHOD() メソッドを実装してください。 METHOD の部分は実際の HTTP メソッドの名前に置き換えます。例えば、 do_GET(), do_POST() 等になります。一貫性をもたせるためにこれらのメソッドは引数を受け取りません。全てのリクエストのパラメータは BaseHTTPRequestHandler へ渡されて、リクエストインスタンスの属性として保持されます。

このサンプルのリクエストハンドラは、レスポンスを作成するために便利なローカル属性とクライアントへレスポンスを返す方法を説明します。

from BaseHTTPServer import BaseHTTPRequestHandler
import urlparse

class GetHandler(BaseHTTPRequestHandler):
    
    def do_GET(self):
        parsed_path = urlparse.urlparse(self.path)
        message_parts = [
                '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,
                '',
                'HEADERS RECEIVED:',
                ]
        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 'Starting server, use <Ctrl-C> to stop'
    server.serve_forever()

メッセージのテキストを組み立ててから、レスポンスのソケットをラップするファイルハンドラ wfile へ書き込みます。それぞれのレスポンスは、 send_response() を通してレスポンスコードをセットする必要があります。エラーコード(404, 501 等)を使用する場合、適切なデフォルトのエラーメッセージがヘッダに含まれるか、またはメッセージがエラーコードで渡されます。

サーバのリクエストハンドラを実行するには、サンプルスクリプトの __main__ の処理に HTTPServer のコンストラクタへそのハンドラクラスを引数として渡します。

さぁ、サーバを起動しましょう。

$ python BaseHTTPServer_GET.py
Starting server, use <Ctrl-C> to stop

別のターミナルでこのサーバへアクセスするために curl コマンドを使用してください。

$ 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

HTTP POST

POST リクエストをサポートするにはもう少し大変です。それはベースクラスがフォームデータを解析しないからです。 cgi モジュールは、与えられた入力が正しいときにフォームの解析方法を知っている FieldStorage クラスを提供します。

from BaseHTTPServer import BaseHTTPRequestHandler
import cgi

class PostHandler(BaseHTTPRequestHandler):
    
    def do_POST(self):
        # POST されたフォームデータを解析する
        form = cgi.FieldStorage(
            fp=self.rfile, 
            headers=self.headers,
            environ={'REQUEST_METHOD':'POST',
                     'CONTENT_TYPE':self.headers['Content-Type'],
                     })

        # レスポンス開始
        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('Form data:\n')

        # フォームに POST されたデータの情報を送り返す
        for field in form.keys():
            field_item = form[field]
            if field_item.filename:
                # field はアップロードされたファイルを含みます
                file_data = field_item.file.read()
                file_len = len(file_data)
                del file_data
                self.wfile.write('\tUploaded %s as "%s" (%d bytes)\n' % \
                        (field, field_item.filename, file_len))
            else:
                # 通常のフォーム値
                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 'Starting server, use <Ctrl-C> to stop'
    server.serve_forever()

curl コマンドでサーバへ POST するメッセージのフォームデータを作成します。最後の引数 -F datafile=@BaseHTTPServer_GET.py は、そのフォームからファイルデータを読み込むのを説明するために BaseHTTPServer_GET.py の内容を POST します。

$ curl http://localhost:8080/ -F name=dhellmann -F foo=bar -F  datafile=@BaseHTTPServer_GET.py
Client: ('127.0.0.1', 51128)
Path: /
Form data:
        name=dhellmann
        foo=bar
        Uploaded datafile (2222 bytes)

スレッドとフォーク

HTTPServerSocketServer.TCPServer のシンプルなサブクラスです。それはリクエストを扱う複数スレッドや複数プロセスを使用しません。スレッドやプロセスをフォークして扱うには、 SocketServer からどちらかのクラスを mix-in して新たなクラスを作成してください。

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):
    """Handle requests in a separate thread."""

if __name__ == '__main__':
    server = ThreadedHTTPServer(('localhost', 8080), Handler)
    print 'Starting server, use <Ctrl-C> to stop'
    server.serve_forever()

リクエストを受け付けると、新たなスレッドかプロセスがそのリクエストを扱うのに作成されます。

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

エラー制御

エラー制御は send_error() で簡単に行えます。適切なエラーコードと追加のエラーメッセージを単純に渡すと、(ヘッダ、ステータスコード、本文をもつ)完全なレスポンスが自動的に生成されます。

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 'Starting server, use <Ctrl-C> to stop'
    server.serve_forever()

このケースでは、404 エラーが必ず返されます。

$ 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>

ハンドラ設定

send_header メソッドは HTTP レスポンスへのヘッダデータを追加します。その関数はヘッダ名とその値の2つの引数を取ります。

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('Response body\n')
        return

if __name__ == '__main__':
    from BaseHTTPServer import HTTPServer
    server = HTTPServer(('localhost', 8080), GetHandler)
    print 'Starting server, use <Ctrl-C> to stop'
    server.serve_forever()

このサンプルは RFC 2822 に準拠したカレントのタイムスタンプに Last-Modified ヘッダをセットします。

$ curl -i http://localhost:8080/
HTTP/1.0 200 OK
Server: BaseHTTP/0.3 Python/2.7
Date: Sun, 10 Oct 2010 13:58:32 GMT
Last-Modified: Sun, 10 Oct 2010 13:58:32 -0000

Response body

See also

BaseHTTPServer
本モジュールの標準ライブラリドキュメント
SocketServer
raw ソケットコネクションを操作するベースクラスを提供する SocketServer モジュール
Bookmark and Share