Posted at

1回だけレスポンスを返すだけのHTTPサーバー(俺得)

More than 1 year has passed since last update.

タイトルの通り、1回だけリクエストに対してレスポンスを返した後にそのまま終了するHTTPサーバーのスクリプトです。

他にもうちょっとスマートな実装があっても良さそうなのですが、

https://gist.github.com/attakei/c09232241c5a24b4206c7f4758fe8b75


主な構成要素


  • 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)

OnceHttpServerHTTPServerを継承しているのですが、1個のメソッド追加と1個のメソッド拡張を持っています。


process_request()

拡張したほう。

基本的にリクエストを受け付けた時点でその処理をするメソッドなのですが、大元の処理終了後にひたすらSIGALRMを投げ続けさせる命令を追加します。

これで、最初のリクエストを処理以降、何処かのタイミングでSIGALRMに対応された処理を実行しようとプロセスが躍起になります。


shutdown_by_signal()

追加したほう。

サーバーのクリーンアップを行なったのちに、sys.exit(0)を実行してプロセスをまるっと停止させます。

このメソッドは、run()内でSIGALRMに対する処理として登録します。

こうすることで、


  1. SIGALRMにshutdown_by_signal()を登録してからサーバー待ち受け開始

  2. 最初のリクエストを処理すると当時にSIGALRMを定期的に送信(死ぬまで)

  3. シグナルを受け付けたshutdown_by_signalが自ら死を選ぶ

こんな感じの挙動すします。


誰得?

極めて俺得です。

他の人向けの使い道としては、特定のステータスコードを返すようにしてHTTPリクエストをするクラスのモックには使えるんじゃないでしょうか。

本来、自分がやりたかったことのために作ってみたコードなのですが、

それについてはまた書きます(例によってErrBot関係です)





  1. http.serverで動かした際も基本的にはKeyboardInterupt待ちです