python, cgi

https://coreblog.org/ats/stuff/minpy_web/04/index.html を参考に簡単なwebサーバーを作っていたが、どうもサイトのソースコードではうまくいかなかった。以下のように書き換えると、urlのqueryからparameterを取得できた。

#!/usr/local/bin/env python

html_body = """
<html>
   <body>
       foo = %s
   </body>
</html>
"""
import cgi
form=cgi.FieldStorage()
print("Content-type: text/html\n")
print(html_body % form['foo'].value)

最後をform['foo'].valueにすることで、http://127.0.0.1:8000/cgi-bin/test.py?foo=barとしたときのparameterを取得できた。というより良く本文を読めば、そのように記述していた。サンプルコードが違うだけだったのか。

form=cgi.baFieldStorage()をすることはどういうことなのか。formの内容をprint(form)で確認すると、"FieldStorage(None, None, [MiniFieldStorage('foo', 'baba')])"という内容が返ってきた。
'foo'というkeyに対して、'baba'というvalueが紐づけられているようである。実際にリファレンスにも、


"FieldStorage のインスタンスは Python の辞書型のように添え字アクセスが可能です。"
(https://docs.python.org/ja/3.5/library/cgi.html)

と書かれている。Request Headerも確認してみるとquery stringparametersにも"foo: baba"と値が格納されている。ではどのようにCGIサーバー上で受け取っているのか気になってきた。cgi.pyの実装(https://github.com/python/cpython/blob/3.8/Lib/cgi.py)をみると、

        if method == 'GET' or method == 'HEAD':
           if 'QUERY_STRING' in environ:
               qs = environ['QUERY_STRING']
           elif sys.argv[1:]:
               qs = sys.argv[1]
           else:
               qs = ""
           qs = qs.encode(locale.getpreferredencoding(), 'surrogateescape')
           fp = BytesIO(qs)
           if headers is None:
               headers = {'content-type':
                          "application/x-www-form-urlencoded"}

このあたりでquery stringを解析しているようだ。environは、

       environ         : environment dictionary; default: os.environ

と記されているように、環境変数が格納されている。pythonでprint(os.environ)により環境変数を表示すると、

'SERVER_PROTOCOL': 'HTTP/1.0', 'SERVER_PORT': '8000', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '', 'PATH_TRANSLATED': 'C:\\Users\\mtmoc\\python', 'SCRIPT_NAME': '/cgi-bin/test.py', 'QUERY_STRING': 'foo=baba', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_ACCEPT': '', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'HTTP_COOKIE': '', 'HTTP_REFERER': ''}

のように各パラメータのkeyがenvironの中に格納されているわかる。'QUERY_STRING'を見てみると、'foo=baba'がvalueに割り当てられていて、それをCGIサーバーが取得してtest.pyのソースコードにおいて変数formにその他もろもろの(k,v)と共に格納している。それを、fooをkeyとするvalueとして取得し、文字列を置換することでブラウザ上に表示している。

短い記事だけど調べるのにかなり時間がかかった。urlのparameterの値がどのようにして取得されているのかが気になったのだった

追記
CGIについての仕様はrfc3875に定められているようだ。それによると、httpサーバーとCGIスクリプトはクライアントからのリクエストに対する責務を共有すると記されている。その分担は
・httpサーバー:クライアントのリクエストに係るconnection management, data transfer, transport and network issues
・CGIスクリプト:data access, document processing などのアプリケーション側のisuueを扱う。
と定められている。
クライアントのリクエストを受け取ったサーバーはURIから実行すべきスクリプトを探し出す。そして、CGIリクエストをスクリプトへ渡す。そのリクエストにはthe request meta-variablesとrequest message dataが入っていて、具体的な内容はここを参照すること。

参考記事
https://github.com/python/cpython/blob/3.8/Lib/cgi.py
https://www.ipa.go.jp/security/awareness/vendor/programmingv1/a01_04.html
https://tools.ietf.org/html/rfc3986#section-3.4
https://www.futomi.com/lecture/form/index.html
https://qiita.com/Taro_man/items/ef1cbbf8991e3a5921ff




この記事が気に入ったらサポートをしてみませんか?