HTTP PUT でファイルアクセス
HTTP のGETとPUTを使った簡単なファイルサーバがテスト用に必要になったのでPythonで書いてみた.Python標準のBasicHTTPServerのハンドラにはPUTもPOSTも実装されていないのだけど,拡張は可能になっている.実際,CGIHTTPServerではPOSTをサポートしている.
mname = 'do_' + self.command if not hasattr(self, mname): ...
みたいに書かれていて,ハンドラオブジェクトでdo_XXXXメソッドを実装すると,XXXXメソッドがディスパッチされる.
SimpleHTTPRequestHandlerを拡張して実装してみた.do_POSTとdo_PUTを同じものとして実装してある.SimpleHTTPRequestHandlerを拡張しているので,GETはそのまま使える.実行すると実行パス以下にファイルを書く.'..'とか使うと実行パスよりも上にもかけちゃうのはまずいので,さすがにそれはチェックするようにしてみた.
テストには,QuickPutというツールが使える.
$ python QuickPut.py FILENAME URL
cURLを使ってもできる.
$ curl -O -T FILENAME URL
'-O'を付けているのはHTTP/1.0を使うようにという指定.これがないとデフォルトの1.1になり,Expect: 100-continueが出てしまい,ちょっと待つようになって遅くなる.100 を出すようにもしてみたのだけど,その場合にもちょっとブロックしているように見える.curlが悪いとは思えないので私のプロトコル解釈がどこか間違っているのだろうけど,当面放置.
ソース
"""Puttable HTTP Server. This module builds on SimpleHTTPServer implementing PUT and POST to put files. Note this is really dangerous!! Just for test. """ __version__ = "0.1" __all__ = ["SimpleHTTPRequestHandler"] import os import sys import BaseHTTPServer import SimpleHTTPServer class PuttableHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): server_version = "PuttableHTTP/" + __version__ def do_PUT(self): """Serve a PUT request.""" self.do_POST() def do_POST(self): """Serve a POST request.""" self._writeOut(os.path.join(os.getcwd(), self.path[1:]), self.rfile) def _copyCont(self, src, dest, length): buflen = 10000 while length > 0: b = src.read(min(buflen, length)) dest.write(b) length = length - len(b) def _writeOut(self, path, f): if path.find('/../') >= 0: self.send_response(403, "forbidden") return length = self.headers.getheader('content-length') if not length: self.send_response(411, "length required") return; try: existFlag = os.path.exists(path) dest = file(path, 'w') self._copyCont(f, dest, int(length)) dest.close() if existFlag: self.send_response(204, "post succeeded, modified") return else: self.send_response(201, "post succeeded") return except IOError, msg: self.send_response(401, "faield to writefile," + msg) return def test(HandlerClass = PuttableHTTPRequestHandler, ServerClass = BaseHTTPServer.HTTPServer): BaseHTTPServer.test(HandlerClass, ServerClass) if __name__ == '__main__': test()