これみると
gs = TCPserver.open() while TRUE Thead.start(gs.accept) do |s| print(s, " is acepted\n") while s.gets s.write($_) end print(s " is gone\n") s.close end end
telnet とかでつないでみると 1行1行 echo されている。 この構造が基本か。
複数のスレッドが出来たら同期はどうするの?というのがまず頭に 浮かぶが ...
このあたり?
Thread, Mutex, Queue .. そのへんからはじめないと ...
ちょっとだけ考えてみました。
esehttpd のcgiruby.c みると、環境変数をセットアップして、0番にQUERY_STRINGを書き込んで、rb_eval_string_protectを呼んで1番デスクリプタから読み出せば結果が返ってくるらしい。とおもったら単にテンポラリファイルを作ってるだけだった。これならむっちゃ簡単にできる。
というようなプログラムを書くのがてっとりばやい気がする。
ひとりで使う分にはThreadは後回しでいいでしょ? --ksr
うちだとwwwsrv含め他のものも全然動かないので途中ですが、 wwwsrv-0.14.1 のcgi.rb をいじるのがてっとりばやい
class RubyCGI def runcgi(cgi) print "Content-Type: text/html\r\n"; print "\r\n"; print "test\r\n"; end def prepare_rubycgi(cgi) @cgi = cgi; begin require "tempfile"; @in = Tempfile.new("IN"); @in.binmode; @out = Tempfile.new("OUT"); @out.binmode; @stdin_org = $stdin; @stdout_org = $stdout; $stdin = @in; $stdout = @out; return [@in, @out]; rescue end return [nil, nil]; end def exec_rubycgi(cgi) begin @in.seek(0, 0); runcgi(@cgi); out.seek(0, 0); $stdin = @stdin_org; $stdout = @stdout_org; rescue end end end
以下のような感じに書き換えて
cgi_ioをcgi_in, cgi_out に適宜書き換えればいきおう起動できるように見えるはず。
cgi_out, cgi_in = (rcgi = RubyCGI.new).prepare_rubycgi(cgi_exec);とか、cgi_outをclose_writeした後
LOCK.synchronize{ protect_environ(cgi_var) { protect_chdir(File::dirname(cgi_path)) { rcgi.exec_rubycgi; } } }とかを適宜挿入書き換える。
やっぱCベースのhttpd をいじるのがてっとりばやいかなあ。
シングルスレッドのみを考えれば
ということができれば CGI が使えるだろうということですね。
この線で考えてみたいです。
ruby について良くしらないんだけども ruby 内で簡単にできそうな気も。 > おしえて ruby 識者
上にも書いたけど、wwwsrvを http://www.ruby-lang.org/ からとってきて cgi.rbをいじるのが
てっとりばやいとは思うんですが、ただwwwsrvはかなり本格的なのでそれなりにでかくて難しいんですよ。
もっと小さいのがあればいいんだろうけど。ということで、mhttpd からフォークしないでlibruby.a を
呼び出してもらえるように改造して、とCakeさんに頼んでみるというのはあるかも。
ところで、stdin/out と env を仮想化ってのは server を作るとは別次元の 話だと思います --- ちゃんと説明すると
調べてみたら、出力については、$defout というのがある ということが わかりました。
def $stdout.write(arg1) xout = Thread.current['stdout']; return super(arg1) if (xout == nil); return xout.write(arg1); end
ただし、入力については ... そういう仕組みになっていない。
static VALUE filename, current_file; static int gets_lineno; という context を static に持っていて、 EOF に到達すると 次の入力に切り替わる。 ファイルがなければ、stdin に切り替わる。
結論としては、入力をリダイレクトするのは、あきらめた方がよさそう。
そもそもリダイレクトという考えが変?
cgi.rb を使うとCGIまわりについては詳細を知らなくてもいいから具体的に何をやればThread的なソリューションになるのかはわかりません。で、わかっているのは、CGI的なインタフェース、環境変数の設定とファイルディスクリプタ0, 1の設定だということで議論を進めてました。
Thread動作にこだわるなら、WebBrick には HttpServlet 的な方法もあるので、Tiki側をそれに合わせるつうのはあるとは思います。
esehttpdもファイルディスクプタをそのまま即値で使っているから ruby処理はいっぺんには一個しかできないんだろうなあ。
Rubyだけでやるほうが簡単なような気がするので、あやしいhttp待ち受けスクリプト なぞ貼り付けてみるテスト。
とりあえず SHIMADA さんの server.rb と lib/cgi.rb と tiki.cgi をくっつけて みたいんですが、うまくいかんです。
server while true s = server.accept xxx = readRequest(s) cgi.new(yyy); tiki = Tiki.new; tiki.process(cgi); if (tiki.result == []) then tiki.internalerror('no content'); end res = tiki.httpheader + tiki.result + tiki.footer; sendResponse(s, res) end
def env_table ENV end def stdinput $stdin end def stdoutput $DEFAULT_OUTPUT end
というところがあるから、たぶん stdinput とか ENV は オーバーロード できると思うんですが ...
そっか、そうなってるならいけるかも。
class LOCALCGI < CGI def initialize(in, out, env) @in = in; @out = out; @env = env; def env_table @env end def stdinput @in end def stdoutput @out end end # in, out はIOクラスを継承してオンメモリででっちあげる。 .... readhttprequest(req) env = buildcgienv(req); cgi = LOCALCGI.new(req.in, res.out, env); tiki = TIki.new; tiki.process(cgi); .....っていう感じでいけそうな気がします。
まとまっていないけど、こんな感じにしたいです。
server は、header だけ読んで pat = method + ':' + パス とする。 (RE,handler) の配列 を サーチしていって、 最初に RE =~ pat が真になった ものに対して handler(cgi,socket) を呼び出す。 あとは勝手にやってねということで、Thread.start する。 Thread が それぞれの handler で いくつ動いているか 一応数えていて、 ある一定数になったら、待ち合わせる。(Tiki が 1 つしか動かないのなら 1 個しか動かさない ) あと Thread は、ちゃんと timeout をかける。
header 読み込み -> CGI のデータを作る ->header 書き込み (socketを渡す) (ローカルな Hash (socketを渡す) を使う )こういう風に使えば良いわけですね。
CGI を作る -> Tiki などハンドラを呼び出す -> socket に書き込む (Server側) (header を CGI に読ませてから 結果を reader として socketを reader として渡す) 受け取る。 header を CGI に書かせる 自分で reader から read して socket に書き込むこういう感じで良いわけですね。
やっぱり できない。なんか考え違いをしているのかな?
ところで話はかわりますが ... キャッシュサーバってのも作りたいです。
プロキシに接続した場合。 GET http://suz.s7.xrea.com:80/cgi-bin/tiki.cgi HTTP/1.0^M User-Agent: Wget/1.5.3^M Host: suz.s7.xrea.com:80^M Accept: */*^M 直接接続した場合 GET /cgi-bin/tiki.cgi HTTP/1.0^M User-Agent: Wget/1.5.3^M Host: suz.s7.xrea.com:80^M Accept: */*^Mこの程度の違いなら、1 つのポートで両方サポートできるような気が する。
組み込みTiki --- いちおう動いたので 次は ... 環境を整備していきたい。
/etc システムの設定ファイル と スタートアップ /etc/init.rb -- メインスレッド。(= スタートアップ) /etc/resolv.conf -- network につながらないと何もできない ので重要。 /lib ruby のライブラリ(必要なものだけ)と tiki 本体。 /tiki tiki のリポジトリ => 実際のファイル名を EUC 化。 /icons icons