Gauche:HTTPD
インターネットエイジのこの時代、HTTP サーバの実装の一つや二つないと モダーンなプログラミング言語とはいえないんじゃなかろうか...
(長くなってきたので、トピック毎にまとめてみました。 コメント毎に時間を入れておいて頂くと良いかも。--Shiro (2002/11/22 22:04:59 PST))
参考資料
- http://www.jaist.ac.jp/~kiyoshiy/software/kumonoito/spec.txt
大学生のプログラミングの課題。ミニマルな仕様。
- http://www.w3.org/Protocols/
HTTP
- http://www.w3.org/Protocols/rfc2616/rfc2616.html
HTTP1.1
- http://www.pugo.org:8080/
Postscript による実装。 skimu 版の参考にした。
nekoie による実装
- すいません、宣伝させてください…‥。
skimu による実装
- 上記のミニマル仕様すらちゃんと実装してないけど、サーブレットで Gauche のマニュアルが検索できるので遊んでみて下さい
- Shiro: をを、すごい。こんなに短く書けるのか。んー、もじらだと 何も出ないが… w3mだと見られました。なんでだろ。(2002/11/15 23:40:10 PST)
- skimu: あれれ Chimera では 問題ありませんでした。もちろん IE でもオッケイ。スクリーンショット
- skimu: しばらく、HTTP と html の勉強で更新してませんでしたが、なんとなく方針が見えてきたので、改造に着手しました。(2002/12/01 19:42:27 PST)
SIGPIPE問題
- sakae です。お願いすると出てきますね。ひょっとして、scheme-undergroundな人? 私の所で試したら、こんなエラーがでました。
cons:sakae$ /www/bin/ab -c 5 -n 100 http://localhost:8081/ : ("GET" "/" "HTTP/1.0") ("GET" "/" "HTTP/1.0") *** ERROR: unhandled signal 13 (SIGPIPE) Stack Trace: _______________________________________ 0 (zap (mime-type->Content-type mime-type)) At line 134 of "./httpd.scm" 1 (let ((in (socket-input-port s)) (out (socket-output-port s))) (le ... At line 146 of "./httpd.scm" 2 (do-http cnl) At line 174 of "./httpd.scm" 環境は cons:sakae$ gosh -V Gauche scheme interpreter, version 0.6.4 [euc-jp] cons:sakae$ uname -a FreeBSD 4.7-STABLE FreeBSD 4.7-STABLE #0: Mon Oct 14 20:15:12 JST
- skimu: うぅ、エラーハンドリングはいい加減です (_o_) つーか, ちゃんとしたのができたら gauche-develで宣伝します:-) しかし、こんなところで SIGPIPE はいやっぽいですね... だけど、僕の NetBSD 1.6 では再現しませんでした。 ところで、scheme-underground って聞くとまっさきに ここ を思い浮かべてしまうのですが、ほかにもあるのでしょうか?(面白そうだから案内ください。)
- Shiro: SIGPIPE問題ですが、クライアントがリプライを全部受け取る前に 接続を切ってしまうと発生すると思います。これはシグナルハンドラで救済して ソケットを閉じるようにするしかないかな。
("GET" "/" "HTTP/1.0")
(("user-agent" "ApacheBench/1.3d") ("host" "localhost:8080") ("accept" "*/
"One reqest processed"
"Waiting for a client"
"A Client accepted"
"My handler: bad type of argument for s: #<eof>"
"Waiting for a client"
MacOSX10.2.2で落ちる
skimu MacOSX10.2.2 だとなんとバスエラーで落ちました。
Program received signal EXC_BAD_ACCESS, Could not access memory. Scm_ClassOf (obj=0x0) at class.c:398 398 return SCM_CLASS_OF(obj); (gdb) bt #0 Scm_ClassOf (obj=0x0) at class.c:398 #1 0x00020124 in Scm_ComputeApplicableMethods (gf=0x0, args=0x2e41b0, nargs=2) at class.c:1301 #2 0x000084b8 in run_loop () at vm.c:585 #3 0x0000b2bc in user_eval_inner (program=0x4700d0) at vm.c:1707 #4 0x0000318c in main (argc=3, argv=0xbffffdc0) at main.c:275 #5 0x000024f4 in _start (argc=3, argv=0xbffffdc0, envp=0xbffffdd0) at /SourceCache/Csu/Csu-45/crt.c:267 #6 0x00002374 in start ()
- ひさしぶりに gosh を落とすのに成功してしまった。
(let loop ((cs #f))
(with-error-handler
(lambda (e)
(print-log LOG_ERR #`"My handler: ,(slot-ref e 'message)")
(report-error e))
...
(loop #f)
- と書き替えると MacOSX でも動く様になりました。ちなみに loop () というように変数をいれないと、やはり Bus error でおちました。機種によってバグがでたり出なかったりする問題だったのかしら。(2002/11/22 21:36:05 PST)
- Shiro: 貴重な情報をありがとうございます。これでだいたい見当がついた、 という気がします。(2002/11/22 21:57:32 PST)
- Shiro: 解決しました。MacOS固有の問題ではなく、他のプラットフォームで たまたま症状が出ない問題でした。次のパッチで直ります。(2002/11/30 12:05:59 PST)
*** ext/net/addr.c 6 Jul 2002 23:01:17 -0000 1.12
--- ext/net/addr.c 30 Nov 2002 09:19:03 -0000
***************
*** 77,83 ****
/* creates sockaddr from struct sockaddr. */
ScmObj Scm_MakeSockAddr(ScmClass *klass, struct sockaddr *saddr, int len)
{
! ScmSockAddr *addr = SCM_NEW2(ScmSockAddr*, sizeof(ScmObj)+len);
SCM_SET_CLASS(addr, klass);
addr->addrlen = len;
memset(&addr->addr, len, 0);
--- 77,85 ----
/* creates sockaddr from struct sockaddr. */
ScmObj Scm_MakeSockAddr(ScmClass *klass, struct sockaddr *saddr, int len)
{
! ScmSockAddr *addr;
! addr = SCM_NEW2(ScmSockAddr*,
! sizeof(ScmSockAddr) - sizeof(struct sockaddr) + len);
SCM_SET_CLASS(addr, klass);
addr->addrlen = len;
memset(&addr->addr, len, 0);
もじらで見られない問題
- Shiro: もじらで見られない問題ですが、リクエストヘッダを全部読むように
したら見られるようになりました。
- (use rfc.822)を最初に追加
- do-httpの本体で、リクエストが"GET"の場合のブランチを次のようにする
(begin
(rfc822-header->list in)
(receive (text type len) (gen-text path)
(reply text type len out)))
- 反映して頂きました。
エラーハンドラ
- skimu: エラーハンドラ付けてみたのでいじめてあげて下さい。ところで、当初
(define (httpd-main nport)
(let ((ss (make-server-socket 'inet nport :reuse-addr? #t))
(cs #f))
(set-signal-handler! SIGPIPE (lambda (n) (error #`"Catched signal ,|n|")))
(do () (#f)
(with-error-handler (lambda (e) (print-log #`"My handler: ,(slot-ref e 'message)"))
(lambda ()
(dynamic-wind
(lambda () #f)
(lambda ()
(print-log "Waiting for a client")
(set! cs (socket-accept ss))
(print-log "A Client accepted")
(do-http cs)
(print-log "One reqest processed")
)
(lambda ()
(socket-close cs))))))))
- というように signal-handler のなかで error を出して My handler で受け取ろうとしたのですが、どうもデフォルトのエラーハンドラが呼ばれてしまいます。最新版のソースでは単にメッセージを印字してそのまま戻って、閉じたポートに対する書き込みエラーを捕まえてます。それと、set-signal-handler する前は SIGINT のハンドラーの中のエラー呼び出しが My handler で捕まえられなかったのが、set-signal-handler SIGPIPE を入れただけで、SIGINT の方も My handler で捕まるようになりました。
ログの方法
- Shiro: print-logはgauche.loggerを使っておくと後で何かと便利なのでは… と思ったのですがファイルロック絡みで動かないプラットフォームがあるんでしたっけ。 回避コードを入れた方がいいなあ。(2002/11/18 22:06:34 PST)
- skimu: 何れにしても現バージョンは MacOSX では Bus error で動かないので gauche.logger を使う様にしてみました。なんか、本物のサーバみたいになてきた。今後は www.cgi をつかってヘッダを吐かせてたり、サーブレットでヘッダを指定できる様に do-http をリストラ予定。
- Shiro: MacOSXではgauche.loggerがfcntlではなくロックファイルによる ロックを使うようにしてみました (logger.scm, v1.5, file/util.scm, v1.18も必要)。(2002/12/01 18:21:27 PST)
- skimu: 動作確認しました。ばっちりです。 main を次のように変えてみました。 (2002/12/01 19:39:00 PST)
(define (main args)
(case (length args)
((2)
;; ログファイルを指定しなければ、デバッグモードで stderr に詳しいログ
(set! log-level 4)
(httpd-main (string->number (cadr args)) #t))
((3)
;; 二番目の引数はログファイル。 syslog を指定すれば syslog へログ。
(close-input-port (current-input-port))
(close-output-port (current-output-port))
(close-output-port (current-error-port))
(if (string=? (caddr args) "syslog")
(httpd-main (string->number (cadr args)) 'syslog)
(httpd-main (string->number (cadr args)) (caddr args))))
(else
(display "Usage: httpd port [logfile]\n" (current-error-port)))))
kawによる実装
子プロセス起動による並行サーバ(の雛型)を書いてみたのですが、Memory faultで落ちてしまいます。
LinuxとOpenBSDでやってみてどちらでも落ちる。なにが悪いんだろう?
... というわけで、詳細は kaw へ。
Shiroによる実装
https://github.com/shirok/Gauche-makiki
仕事で使ってて、必要に応じて機能を少しづつ足してます。