HTTPリクエストハンドラを書く
PythonのSimpleHTTPServerはクライアントからのreqestを受け取るとそれをRequestHandlerClassに渡してインスタンス化します。これはSocketServerモジュール中のBaseServerを見ると分かります。
def finish_request(self, request, client_address): """Finish one request by instantiating RequestHandlerClass.""" self.RequestHandlerClass(request, client_address, self)
意外かもしれませんが、ServerがRequestHandlerを扱うのはここだけです。つまり、このような引数をとるコンストラクタさえ実装してしまえば、どんなクラスでもRequestHandlerClassになれます。が、clientに文字列を送ることを考えれば
class HTTPRequestHandler(object): responses = BaseHTTPServer.BaseHTTPRequestHandler.responses def __init__(self, request, client_address, server): self.request = request self.setup() self.handle() self.finish() def setup(self): self.wfile = self.request.makefile('wb', 0) def finish(self): if not self.wfile.closed: self.wfile.flush() self.wfile.close() def handle(self): self.wfile.write("Hello World\r\n")
みたいな処理をするClassがRequestHandlerになります。HTTPプロトコルのことを考慮すればヘッダーに情報を送らないといけないでしょうし、リクエストを受信してparseする必要もあります。そのようなことをできるように最低限書くと次のようになります。
#!/usr/bin/env python2.6 #myserver.py import shutil import BaseHTTPServer import os class HTTPRequestHandler(object): responses = BaseHTTPServer.BaseHTTPRequestHandler.responses def __init__(self, request, client_address, server): self.request = request self.setup() self.handle() self.finish() def setup(self): self.rfile = self.request.makefile('rb', -1) self.wfile = self.request.makefile('wb', 0) def finish(self): if not self.wfile.closed: self.wfile.flush() self.wfile.close() self.rfile.close() def handle(self): self.raw_requestline = self.rfile.readline() if not self.raw_requestline: return self.parse_request() mname = 'do_' + self.command if not hasattr(self, mname): return method = getattr(self, mname) method() def parse_request(self): assert(self.raw_requestline[-2:] == '\r\n') words = self.raw_requestline[:-2].split() assert(3 == len(words)) self.command, self.path, self.request_version = words return True def send_response(self, code, message=None): if message is None: if code in self.responses: message = self.responses[code][0] else: message = '' if self.request_version != "HTTP/0.9": self.wfile.write("HTTP/1.0 %d %s\r\n" % (code, message)) self.send_header("Server", self.version_string()) self.send_header("Date", self.date_time_string()) def version_string(self): return "SimpleHTTP/0.6 Python/2.6.5" def date_time_string(self): return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( "Sun", 28, "Nov", 2011, 3, 18, 14) def send_header(self, key, value): if self.request_version != "HTTP/0.9": self.wfile.write("%s: %s\r\n" % (key, value)) def end_headers(self): if self.request_version != "HTTP/0.9": self.wfile.write("\r\n") def do_GET(self): f = self.send_head() if f is not None: shutil.copyfileobj(f, self.wfile) f.close() def do_HEAD(self): f = self.send_head() if f is not None: f.close() def send_head(self): f = open(self.path[1:], "rb") self.send_response(200) self.send_header("Content-type", "text/plain") fs = os.fstat(f.fileno()) self.send_header("Content-Length", fs[6]) self.send_header("Last-Modified", self.date_time_string()) self.end_headers() return f if __name__ == '__main__': import SimpleHTTPServer SimpleHTTPServer.test(HandlerClass=HTTPRequestHandler)
エラー処理をほとんどしていませんし、ヘッダーへ送っている情報も嘘っぱちですが"./myserver.py 1128"などとしてwebブラウザで"http://localhost:1128/myserver.py"を開けばmyserver.pyの中身が見れます。テストは前回のものを参考に書きました。