3.5.1. pfi::network::cgi¶
3.5.1.1. 概要¶
cgi/fcgi/http serverを楽に書くためのライブラリ。
3.5.1.2. 使い方¶
3.5.1.2.1. rawテキストCGI¶
cgiクラスを継承してrunメソッドを実装して、 それをmainから呼び出す。
class my_cgi : public cgi{
public:
void run(ostream &os){
// implementation...
head["Content-Type"]="text/plain";
os<<"Hello, "<<query["name"]<<endl;
}
};
int main(){
run_cgi(my_cgi()).run();
}
run()はosに文字列を出力する。 headでHTTPヘッダを設定できる。 デフォルト状態では、text/plainのUTF8になっている。 HTTPのクエリはqueryに入っている。
3.5.1.2.2. XHTML CGI¶
XHTMLを生成したい時に便利なクラスである。 run()では、HTMLを生成するコードを書く。 このライブラリではHTMLは次のようなDSLで生成する。
class my_cgi : public xhtml_cgi{
public:
void run(){
html__{
head__{
title__{
text__("タイトル");
}
}
body__{
a__{ href__ = "http://kzk9.net/blog/";
text__("super blog!");
}
br__;
}
}
}
};
tag__{
...
}
これが、<tag>…</tag>というHTMLに相当する。 HTML4のタグは予め全部定義してある。 属性はタグのあとに書く。 文字列要素はtext__(“hoge”); あるいは、prim__(“hoge”); で注入できる。 textの文字列は自動的にHTMLエスケープされるが、 primはされない。
定義されていないタグ、属性が必要な場合は、
tag__(“tag-name”)
attr_(“attr-name”)
で任意の名前のタグ、属性が使える。
tag__の実体は例によってif文なので、 if文のブロックに使える構文は使える。
head__ title__ text__ ("hoge");
や、
hr__;
はvalidな構文です。
この様に書くと、裏でシコシコHTMLのツリーが組み立てられて、 run()の終了後それがXHTMLとして生成され、CGIの出力になる。
3.5.1.2.3. fcgiとして実行¶
fcgiとして実行するのに、my_cgiクラスを変更する必要はない。 単にrun_cgiの代わりにrun_fcgiを使うとfcgiとして実行されるようになる。
int main(){
run_fcgi(my_cgi()).run();
}
なお、run_fcgiによって実行した場合でも、 プログラム自体をfcgiとして実行しなかった場合は 通常のcgiとして動作する。
3.5.1.2.4. スタンドアロンHTTPサーバとして実行¶
run_cgi、run_fcgiの代わりにrun_serverを用いると、 スタンドアロンHTTPサーバとして実行される。
int main(){
run_server(my_cgi(), port_number /* = 8080 */, thread_num /* = 1 */, timeout /* = 10 */).run();
}
コンストラクタで、ポート番号、スレッド数、通信タイムアウト(秒)が指定できる。省略すると適当な値が使われる。
int main(int argc, char *argv[]){
run_server(my_cgi(), argc, argv).run();
}
argcとargvを渡すコンストラクタを用いると、コマンドライン引数でサーバの設定をすることができる。
$ ./a.out --help
usage: ./a.out [-p port] [-t thread-num] [-o timeout] [-h]
なお、スタンドアロンHTTPサーバを用いるときは、スレッドセーフのために、渡されたCGIオブジェクトをスレッド数だけクローンする。ゆえに、渡すCGIオブジェクトがclonableでなければならない。CGIオブジェクトは、デフォルトではclonableではない。
class my_cgi : public xhtml_cgi, cloner<my_cgi> {
public:
void run(){
html__{
head__{
...
clonableにするには、cgiクラスをclonerと多重継承にする。clonerには実装するクラスの名前を渡す。clonerは、渡されたクラスをコピーコンストラクタでコピーする。clone()動作を変えるにはコピーコンストラクタを実装すれば良い。
run_serverはコンストラクタでソケットをlistenする。小さい番号のポートのためにrootでポートをlistenしてそれから降格してような場合は、次のように書ける。
assert(getuid()==0); // rootである
my_cgi c;
run_server serv(c, 80); // 80番ポートlisten
setuid(...); // 降格
serv.run(); // サーバ処理開始
きめ細かい制御のために、listenするソケットを渡すこともできる。
run_server::socket_type ssock(new server_socket()); // ソケットを自分で作成
ssock->create(8080);
ssock->set_timeout(10);
run_server(my_cgi(), ssock).run(); // run_serverに渡す
run_server::socket_typeは、server_socketへのスマートポインタの型である(型名が長いのでtypedefしている)。クライアントとの接続のタイムアウトは、サーバーソケットの設定が継承される。
run_server::run()にfalseを渡すことにより、非同期実行が可能である。次の例は8080、8081の二つのポートでサーバを立てる。
int main(){
my_cgi m;
run_server serv1(m, 8080);
run_server serv2(m, 8081);
serv1.run(false); // サーバを立てた後帰ってくる
serv2.run(false); // サーバを立てた後帰ってくる
}
非同期にサーバを立てた後、run_server::join()を使って明示的に終了を待つことができる(サーバは終了しないので、永遠に待つことになる)。明示的にjoin()しない場合でも、デストラクタでjoin()する。なので、上のコードは正しく動作する。
なお、デストラクタでjoin()するので、
int main(){
run_server(my_cgi()).run(true);
}
と、
int main(){
run_server(my_cgi()).run(false);
}
は同じ動作になる。