C++でApache Moduleを書きたい人へのTutorial
C++ Apache Module Tutorial
あどさーばー作っています@yutakikuchi_です。
広告配信等の処理高速化の実現手段としてCを使ってApache/NginxのModuleレイヤーで処理を書く事があります。Apache/NginxのModuleはCを基本としているんですが、char*の処理は面倒でstringにしたい、連想配列でデータを管理し易くしたい、その他C++にしか無いライブラリを使いたいといったC++への欲求が出てきてしまいます。ぐぐってもApache ModuleをC++で実現している人って結構少なく、おそらく一般的にはApache Moduleにそんな複雑な処理を書く要件や期待なんて無いんだろうなと想像はしていますが、このエントリーではC++で書いてみます(笑)。
Apache API C++ Cookbook
Apache 2.x Modules In C++ (Part 1) - CodeProject
1つ目のサイトの「A Basic C++ Apache Module Example」にExampleが載っているんですが内容が素晴らしく古くて使い物にならなかったり、2つ目のサイトはMakefileの説明がよく分からないんで、僕がTutorial作ってみます。
C++ Source & Makefile
Tutorial File
CPlus/apache_module/tutorial at master · yutakikuchi/CPlus
下で使うTutorialの内容を置いておきました。Package Install
今回のC++ ApacheModule作成はCentOS6.4 x86_64でやっています。
先に開発に必要なパッケージをInstallしておきましょう。$ sudo yum install httpd httpd-devel make gcc gcc-c++ -y $ tree . ├── Makefile └── mod_cpphello.cpp 0 directories, 2 filesC++ Source
先にC++のSourceを書きます。拡張子はmod_cpphello.cppのように.cppとします。下のSourceは単純にHTMLの出力に「こんにちは!」というString型を埋め込むだけのものです。C++記述によりstd::stringが使えるようになっているのが分かると思います。ポイントはmoduleの宣言をCでやるよとextern "C"で宣言することで使えるようになります。mod_cpphello.cpp ここのSampleがかなり奇麗にまとまったと思います。
#include "ap_config.h" #include "apr.h" #include "apr_lib.h" #include "apr_strings.h" #include "apr_network_io.h" #include "apr_want.h" #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_request.h" #include "http_protocol.h" #include <string> extern "C" module AP_MODULE_DECLARE_DATA cpphello_module; typedef struct { char *hellomessage; } cpphello_dir_config; static void *cpphello_create_dir_config(apr_pool_t *p, char *path) { cpphello_dir_config *cfg = (cpphello_dir_config *)apr_pcalloc(p, sizeof(cpphello_dir_config)); cfg->hellomessage = (char *)"こんにちは!"; return cfg; } static int cpphello_handler(request_rec *r) { cpphello_dir_config *cfg = (cpphello_dir_config *) ap_get_module_config(r->per_dir_config, &cpphello_module); std::string messagetosend = std::string("<html><p>") + std::string(cfg->hellomessage) + std::string("</p></html>\n"); r->content_type = "text/html"; if (!r->header_only) { ap_rputs(messagetosend.c_str(), r); } return OK; } static void register_hooks(apr_pool_t *p) { ap_hook_fixups(cpphello_handler,NULL,NULL,APR_HOOK_MIDDLE); } extern "C" { module AP_MODULE_DECLARE_DATA cpphello_module = { STANDARD20_MODULE_STUFF, cpphello_create_dir_config, NULL, NULL, NULL, NULL, register_hooks }; };Makefile
Makefileのポイントはg++を使う事と、ap系のheaderファイルを読み込む為の-I/usr/include/apr-1/を記述する事です。下の記述を書いたらsudo gmake reloadと打ち込むと上のmod_cpphello.cppのcompile、install、httpd.confへの書き込み、apache restartの全てをやってくれます。httpd.confへの書き込みは最初の1回だけで問題ありませんが、手動でやると忘れてしまう事が多いので(apxs -aのオプションを忘れる)注意してください。
## ## Makefile -- Build procedure for fast3lpoad Apache module ## ## This is a C++ module so things have to be handled a little differently. # the used tools APXS=apxs APACHECTL=apachectl # Get all of apxs's internal values. APXS_CC=`$(APXS) -q CC` APXS_TARGET=`$(APXS) -q TARGET` APXS_CFLAGS=`$(APXS) -q CFLAGS` APXS_SBINDIR=`$(APXS) -q SBINDIR` APXS_CFLAGS_SHLIB=`$(APXS) -q CFLAGS_SHLIB` APXS_INCLUDEDIR=`$(APXS) -q INCLUDEDIR` APXS_LD_SHLIB=`$(APXS) -q LD_SHLIB` APXS_LIBEXECDIR=`$(APXS) -q LIBEXECDIR` APXS_LDFLAGS_SHLIB=`$(APXS) -q LDFLAGS_SHLIB` APXS_SYSCONFDIR=`$(APXS) -q SYSCONFDIR` APXS_LIBS_SHLIB=`$(APXS) -q LIBS_SHLIB` # the default target all: mod_cpphello.so # compile the shared object file. use g++ instead of letting apxs call # ld so we end up with the right c++ stuff. We do this in two steps, # compile and link. # compile mod_cpphello.o: mod_cpphello.cpp g++ -c -fPIC -I$(APXS_INCLUDEDIR) -I/usr/include/apr-1/ $(APXS_CFLAGS) $(APXS_CFLAGS_SHLIB) -Wall -o $@ $< # link mod_cpphello.so: mod_cpphello.o g++ -fPIC -shared -o $@ $< $(APXS_LIBS_SHLIB) # install the shared object file into Apache install: all $(APXS) -i -a -n 'cpphello' mod_cpphello.so # display the apxs variables check_apxs_vars: @echo APXS_CC $(APXS_CC);\ echo APXS_TARGET $(APXS_TARGET);\ echo APXS_CFLAGS $(APXS_CFLAGS);\ echo APXS_SBINDIR $(APXS_SBINDIR);\ echo APXS_CFLAGS_SHLIB $(APXS_CFLAGS_SHLIB);\ echo APXS_INCLUDEDIR $(APXS_INCLUDEDIR);\ echo APXS_LD_SHLIB $(APXS_LD_SHLIB);\ echo APXS_LIBEXECDIR $(APXS_LIBEXECDIR);\ echo APXS_LDFLAGS_SHLIB $(APXS_LDFLAGS_SHLIB);\ echo APXS_SYSCONFDIR $(APXS_SYSCONFDIR);\ echo APXS_LIBS_SHLIB $(APXS_LIBS_SHLIB) # cleanup clean: -rm -f *.so *.o *~ # install and activate shared object by reloading Apache to # force a reload of the shared object file reload: install restart # the general Apache start/restart/stop # procedures start: $(APACHECTL) start restart: $(APACHECTL) restart stop: $(APACHECTL) stop最後にApacheの設定ファイルの確認とlocalhostのPathにアクセスしてみて、「こんにちは!」が表示されている事を見てみます。httpd.confについてはLoadModule cpphello_moduleが自動追加、curlを実行してみると「こんにちは!」のHTMLが確かに取得できました。これでstd::stringがちゃんと使えている事が分かりましたね。
これを参考にC++でもApache Moduleを作ってみてください。
$ sudo gmake reload /usr/lib64/httpd/build/instdso.sh SH_LIBTOOL='/usr/lib64/apr-1/build/libtool' mod_cpphello.so /usr/lib64/httpd/modules /usr/lib64/apr-1/build/libtool --mode=install cp mod_cpphello.so /usr/lib64/httpd/modules/ libtool: install: cp mod_cpphello.so /usr/lib64/httpd/modules/mod_cpphello.so Warning! dlname not found in /usr/lib64/httpd/modules/mod_cpphello.so. Assuming installing a .so rather than a libtool archive. chmod 755 /usr/lib64/httpd/modules/mod_cpphello.so [activating module `cpphello' in /etc/httpd/conf/httpd.conf] apachectl restart $ grep "cpphello" /etc/httpd/conf/httpd.conf LoadModule cpphello_module /usr/lib64/httpd/modules/mod_cpphello.so $ sudo touch /var/www/html/hello_world $ curl -i "http://localhost/hello_world" HTTP/1.1 200 OK Date: Thu, 31 Oct 2013 18:17:14 GMT Server: Apache/2.2.15 (CentOS) Last-Modified: Thu, 31 Oct 2013 18:03:36 GMT ETag: "2601eb-0-4ea0d44d23aa2" Accept-Ranges: bytes Content-Length: 39 Connection: close Content-Type: text/html; charset=UTF-8 <html><p>こんにちは!</p></html>