タイトルの通り、1回だけリクエストに対してレスポンスを返した後にそのまま終了するHTTPサーバーのスクリプトです。
他にもうちょっとスマートな実装があっても良さそうなのですが、
主な構成要素
- http.serverモジュールによる、シンプルなサーバー実装
- signalモジュールによる、やっつけな終了処理
http.serverを軽く取り扱う
いわゆる「ワンライナーでHTTPサーバー」がLL系のプログラミング言語には当たり前のように備わっています。
Python3ではpython -m http.server
がそれに当たりますが、これはリクエストを処理するhandler_classにSimpleHTTPRequestHandler
を用いたものです。(--cgi
を引数に与えると、CGIHTTPRequestHandler
が使われます)
SimpleHTTPRequestHandler
は実行時のカレントディレクトリをルートにする静的ファイル限定のHTTPサーバーとみなしてリクエストを処理します。
閑話休題
http.server
がサーバとして動しているインスタンスはHTTPServer
なのですが、serve_forever()
というメソッドしかありません。1
そのため、1回だけしかリクエストを受け付けないようにするためには、どうにかリクエストの処理の時点でサーバーには死んでもらう必要があります。
で、こんなコードを書きました。
class OnceHttpServer(HTTPServer):
def shutdown_by_signal(self, sig_num, frame):
self.server_close()
sys.exit(0)
def process_request(self, request, client_address):
super().process_request(request, client_address)
signal.setitimer(signal.ITIMER_REAL, 0.5, 0.5)
def run(server_class=OnceHttpServer, handler_class=SimpleHTTPRequestHandler):
server_address = ('0.0.0.0', int(os.environ.get('PORT', 8000)))
httpd = server_class(server_address, SimpleHTTPRequestHandler)
signal.signal(signal.SIGALRM, httpd.shutdown_by_signal)
httpd.serve_forever(2)
OnceHttpServer
はHTTPServer
を継承しているのですが、1個のメソッド追加と1個のメソッド拡張を持っています。
process_request()
拡張したほう。
基本的にリクエストを受け付けた時点でその処理をするメソッドなのですが、大元の処理終了後にひたすらSIGALRMを投げ続けさせる命令を追加します。
これで、最初のリクエストを処理以降、何処かのタイミングでSIGALRMに対応された処理を実行しようとプロセスが躍起になります。
shutdown_by_signal()
追加したほう。
サーバーのクリーンアップを行なったのちに、sys.exit(0)
を実行してプロセスをまるっと停止させます。
このメソッドは、run()内でSIGALRMに対する処理として登録します。
こうすることで、
- SIGALRMにshutdown_by_signal()を登録してからサーバー待ち受け開始
- 最初のリクエストを処理すると当時にSIGALRMを定期的に送信(死ぬまで)
- シグナルを受け付けたshutdown_by_signalが自ら死を選ぶ
こんな感じの挙動すします。
誰得?
極めて俺得です。
他の人向けの使い道としては、特定のステータスコードを返すようにしてHTTPリクエストをするクラスのモックには使えるんじゃないでしょうか。
本来、自分がやりたかったことのために作ってみたコードなのですが、
それについてはまた書きます(例によってErrBot関係です)
-
http.serverで動かした際も基本的にはKeyboardInterupt待ちです ↩