トップ «前の日記(2006-05-16) 最新 次の日記(2006-05-25)» 編集

Route 477



2006-05-22

[scheme] GaucheでHTTPサーバを書いてみる

  • GaucheでCGIを書いてみたい。
  • けどこのマシンはWindowsだなぁ。
  • 適当にHTTPサーバを拾ってくるって手もあるけど、せっかくだからGaucheで書いてみるか。
  • (GaucheでHTTPサーバを書くためのライブラリ*1を探したんだけど、見つからず)

というわけで。

  • とりあえずリファレンスを眺める。www.cgiはCGIを書くためのものだから違うな。rfc.httpはHTTP「クライアント」って書いてあるから違うな。うーん、gauche.net使って頑張るのか。
  • 高レベルインターフェイスにHTTP「クライアント」の例はあるな。サーバは…そうか、bindとかlistenとかするのか。実習でやったなぁ。
  • socket(),bind(),listen(),accept(),read/write/send/recv(),close()らしい
  • 今回は、socket,bind,listenまでmake-server-socketがやってくれそう。

というわけでhttpserv.scmできたよー。

(use gauche.net)

(define sock #f)

(define (server-start)
  (make-server-socket 10800))

(define (server-accept sock)
  (let ((new-sock (socket-accept sock)))
    (display (socket-status new-sock))
    (display (socket-recv new-sock 100000))
    (socket-send new-sock "HTTP/1.1 200 OK\ncontent-type: text/html;\n\nasdf,fdsa")
    (socket-close new-sock)))

(define (main arg)
  (let ((server (server-start)))
    (server-accept server)))

gosh httpserv.scmとやって、ブラウザでhttp://localhost:10800/を開くとasdf,fdsaと表示されます。

*1 Rubyのwebrickのような

[scheme] GaucheでHTTP

次はCGIか。

…あれ、GaucheでRubyの「$0==__FILE__」(そのファイルをインタプリタで実行されたときは真に、requireされたときは偽に なる)ってどう書くんだろう。

まあいいや、CGIのリクエスト文字列を引数に取るようなmain関数をCGIスクリプト側で書くようにしよう。 サーバからはそのmainを呼ぶ、と。

cgitest.scm:

(use www.cgi)
(define (main arg)
  ...

httpserv.scm:

(use gauche.net)
(load "./cgitest.scm")
...
   (socket-send new-sock (main req_str))

[scheme] GaucheでHTTP(3)

いろんなデータを文字列に直すのはどうするのかな?(Rubyのto_sやinspectみたいな)

(x->string hoge) を使えばいいのか。

[scheme] GaucheでHTTP(4)

さて、じゃあHTTPのリクエストのパースをするか。

文字列処理だから6.10(文字列)と10.9(srfi-13)あたりを読んどけば大丈夫かな。正規表現使うなら6.11と9.16(gauche.regexp)か。 *1

うあ、caseの同値判定はeqv?か!文字列には使えないじゃん。cond使うか…。

こんな感じでいいか。

(use gauche.net)
(use srfi-1)
(use srfi-13)
(load "./cgitest.scm")

(define (parse-request request)
  (rxmatch-let (rxmatch #/^(GET|POST)/ (car request)) (method)
	       (cond
		 ((string= "GET"  method)
		  ((#/^GET \/?([^\?]+\?)?(\S+) HTTP/ (car request)) 2))
		 ((string= "POST" method)
		  (last request))
		 (else
		   "error: unknown method"))))

...
    (socket-send new-sock (main (parse-request request)))

*1 「あれ、この処理標準ライブラリにあるじゃん!」という経験はRubyで何回かやってるので、リファレンスはちゃんと読むことにしています(^^;