はじめに lighttpd cgiのqueryとbodyの扱い
前回 python socketサーバーを作成して、マルチスレッドでcgiを利用するの続きです。queryとbodyの処理をするの忘れてた(-_-;)
lighttpdcgi仕様をもう少し説明します。
query⇒環境変数QUERY_STRINGに文字列を設定する。
body⇒stdinにデータを書き込む
例えばこんなpython処理で起動時にstdinと環境変数QUERY_STRINGを読み込み、その内容を表示します。
import sys, os
query_string=os.environ.get("QUERY_STRING")
body_data=sys.stdin.read()
if not query_string:
print('Empty query')
else:
queries=query_string.split("&")
for i, query in enumerate(queries):
print(i, '=>' , query)
if not body_data:
print('Empty body')
else:
print("body => ", str(body_data))
するとcgiの実行結果はこんな感じに
$ curl -X POST --data-ascii "request_body" "http://localhost/cgi-bin/show_query.py?test=1"
0 => test=1
body => request_body
stdinの読み込みはsys.stdin.read
で、環境変数の読み込みはos.environ.get("QUERY_STRING")
ですね。これをコマンドサーバー上で実現するには、
- 環境変数の設定
- stdinへの書き込み
この2つを実行コマンドに対して行う必要があります。今回はpython内で、実行コマンドへこれらを渡す方法の紹介です。
環境変数の設定
こちらは単純です。python内で実行したコマンド(子プロセス)には環境変数が受け継がれる仕様。
ということで以下のようにos.environ
に設定してあげればOK。
os.environ["QUERY_STRING"] = query
stdinへの書き込み
こちらはエラーが取れず苦労しました。
子プロセスの標準入力にデータを送りたい場合は、 Popen オブジェクトを stdin=PIPE と指定して作成しなければなりません。同じく、戻り値のタプルから None ではない値を取得するためには、 stdout=PIPE かつ/または stderr=PIPE を指定しなければなりません。
というわけで、Popen
のPIPEを使ってstdinを実行コマンドと紐づけてあげないといけません。
紐づけさえしてしまえば、PIPEしたstdinに対してwriteすればOK。
ただ普通にstdout=PIPEと書いてもエラーになってしまいました。
stdinの指定方法はこちらのように、stdin=subprocess.PIPE
とsubprocessを付けてあげないといけないみたい。
こんな感じ。args
の内容はsubprocess.check_output
と同じく["echo", "test"]みたいな指定の仕方です。
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
if len(body) != 0:
p.stdin.write (body.encode ('ascii'))
print(args)
return p.communicate()[0]
サンプル
上記を踏まえ、前回のサンプルを更新したので貼っておきます。
# for socket
import os, sys, socket
# for exec command
import subprocess
class CommandService:
#constructor, set socket path
def __init__(self, id):
self._socket_path="/var/run/command_service_"+ str(id) + ".sock"
#for server
def run_server(self):
if self._is_start():
print("Already start server")
return
else:
print("Not start server")
print("Start server")
waitsock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
try:
waitsock.bind(self._socket_path)
while True:
req_cmd_byte = waitsock.recv(8192)
if not req_cmd_byte: break
self._exec_request(req_cmd_byte.decode('utf-8').split(','))
except socket.error as msg:
print(msg)
except:
print("Execute")
finally:
waitsock.close()
os.unlink(self._socket_path)
print("Exit server")
def _is_start(self):
return os.path.exists(self._socket_path)
#parse request, call command and send response
def _exec_request(self, req_cmd):
command=req_cmd[0]
query=req_cmd[1]
body=req_cmd[2]
sockpath=req_cmd[3]
print("comamnd:" + command)
print("query:" + query)
print("body:" + body)
print("socket:" + sockpath)
os.environ["QUERY_STRING"] = query
callresp =self._call_cmd_with_env(command.split(" ") , body)
if len(callresp) != 0 :
self._send(callresp, sockpath)
def _call_cmd_with_env(self, args, body):
print("enter")
try:
print(args)
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
if len(body) != 0:
p.stdin.write (body.encode ('ascii'))
print(args)
return p.communicate()[0]
except OSError as msg:
print(msg)
#for client
def send_to_server(self, command, query, body_path, response_sockpath):
request=command + "," + query + "," + body_path + "," + response_sockpath
self._send(request.encode('utf-8'), self._socket_path)
def _send(self, request, sockpath):
sendsock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
try:
sendsock.sendto(request, sockpath)
#sendsock.sendto(b"test, command", self._socket_path)
except socket.error as msg:
print(msg)
finally:
sendsock.close()