簡単Webプログラミング!

2008年5月30日(金)
吉田 裕美

Webサーバを作る!

 Gaucheは軽く起動時間が早い処理系なので、CGIでWebアプリケーションを作るのにも使えます。その際に便利なwww.cgiモジュールなどもあります。しかし、ここではよりGaucheらしさを出すために、Gaucheで簡単なWebサーバを書いてみることにします。

 このWebサーバはGETリクエストのみを受け付けます。GETリクエストを受け取るとリクエストのURIに含まれる、パス、パラメータを表示するHTMLを戻します(リスト1)。

 今までのプログラムに比べるとだいぶ大きくなっていますね。しかし、関数単位で読んでいけば理解できると思います。最初の3つのuseはnet(ソケット)、URIの解析、CGI用モジュールを読み込んでいます。

 Webサーバを動かし、アクセスした画像は図2となります。

関数について

 次に関数について解説していきます。web-serverは、サーバのメインの関数です。ソケットを作成し、ブラウザからのリクエストを受け取り、コンテンツ生成関数を呼び出し、その結果をブラウザに戻す処理を繰り返しています。get-requestは、ブラウザからのリクエストを受け取り、パスとパラメータを返します。parse-uriは、URIからパスとパラメータを取り出します。put-responseは、ブラウザにコンテンツを返します。renderは、コンテンツを生成します。

 これらについて、ソースコードを見ながら、解説していきます。まず、web-serverの関数についてです。

 最初の行はmake-server-socketで3030ポートにソケットを作成しlet関数でserver-socket変数に代入します。whileは第1引数が真の間中、第2引数以降を実行します、ここでは第1引数が真なので永遠に繰り返します。

 (socket-accept server-socket)は、3030ポートのソケットにブラウザからのリクエストが来るのを待ち、通信用のソケットを戻します。そのソケットをlet関数でclient-socketに代入します。(socket-input-port client-socket)は、ソケットからデータを読みだす入力ポート(抽象化された入力のインタフェース)を返します。

 通常の関数は1の値しか戻しませんが、get-request関数はパスとパラメータの2つの値を返します。多値(多数の値)を戻す関数の値を受け取るには、receive関数を使います。

 次に、receive関数について解説していきましょう。receive関数は、第1引数で与えられる変数のリストに第2引数にある多値を分配し代入し、第3引数以降を実行します。

 (put-response ...)はコンテンツ生成関数renderにパスとパラメータを渡し、render関数の結果をブラウザに戻します。

 また、注意点として、本プログラムはエラー処理などを省略しています。実際のシステムに適用する場合は、guardなどで例外処理を追加してください。

 次に、get-uriについて解説します。#/.../は正規表現です。正規表現の詳細はリファレンスマニュアルを参照ください。

 (rxmatch #/.../ 式)は、正規表現にマッチするかチェックします。マッチした場合はマッチング結果を、しなかった場合は#fを戻します。(read-line in-port)は、入力ソケットin-portから1行分の文字列を読み込みます。

 (cond (条件式 => (lambda(x) 式)))は、条件式の値が#fでなければ、条件式の値をxに受取、式を評価します。

 ここでは、mに正規表現マッチ結果を受け取り、(rxmatch-substring m 1)で正規表現の最初のグループにマッチした値を取り出します。例えば、(read-line)の戻りが"GET /test?a=1 HTTP/1.0"の場合、(rxmatch-substring m 1)は"/test?a=1"になります。

 parse-uriを呼び出しURIから、パス、パラメータに変換し関数値としています。また、GETリクエスト以外が来た場合は、空のパス、パラメータを関数値としています。rxmatch,xmatch-substringは、適用可能なオブジェクトとして省略可能です。詳しくはリファレンスマニュアルを参照ください。

 次に、parse-uriについて解説します。uri-decompose-hierarchical関数は、"//host/test?a=1#abc"のようなURIを受け取り、サーバ名"host"、パス"/test"、パラメータ"a=1"、フラグメント"abc"を多値で戻します。

 cgi-parse-parameters関数は、"a=1&b=2&b=3"のようなパラメータ文字列を受け取り、分解した結果(("a" "1") ("b" "2" "3"))を戻します。(or query "")は、or関数でqueryの値が#fでなければその値、#fなら""を戻します。

 次に、put-responseについて解説します。contentの内容をブラウザに送れるようにステータスやMIMEタイプ、長さのヘッダ情報を付けて出力します。

 #`"..."は、文字列ですが、文字列中の,の直後のS式の値を文字列に取り込みます。例えば、#`"a=,(+ 1 2)"は、"a=3"になります。

 次に、renderについて解説します。render関数では、引数で与えられたパスとパラメータの内容を表示するHTML文字列を作ります。

 Gaucheで大量の文字列を組み立てるには、結果が文字列になるポート(抽象化された入出力のインターフェース)を作成し、そのポートにdisplayなどで出力するのが定石です。

 (for-each ...)は、パラメータが(("a" "1") ("b" "2" "3"))のような値になっているので、その要素("a" "1")をcar(最初の値)"a"、cdr(最初以外の値)("1")に分け、パラメータ名、値として表示しています。

 open-output-string関数が文字列ポートの作成、get-output-string関数はポートに書かれた文字列を戻します。

CADのベンチャー企業を経て、独立し有限会社EY-Officeを設立。Java、Ruby、PerlなどでWebアプリを中心に開発を行ってきたが、現在は、教育に注力し「問題解決型の教育」に奮闘中。「Gauche-Trac(http://www.ey-office.com/gauche)」に今回の記事のサポートページを作りましたのでご活用下さい。HP:http://www.ey-office.com。Blog:http://d.hatena.ne.jp/yuum3

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています