撰写一个HTTPD Module
(这是一篇旧文,整理到这里)
在这篇文章里,为大家介绍,如何为Apache HTTPD
撰写一个module。我使用的是Fedora Linux
,因此可以把Fedora Linux里面给提供的httpd
和httpd-devel
两个包安装好。
安装httpd-devel
的原因是因为我们给HTTPD
写module时,需要调用相关的header文件。接下来我们写一个简单的模块:
// module_foo.c
#include <stdio.h>
#include "apr_hash.h"
#include "ap_config.h"
#include "ap_provider.h"
#include "httpd.h"
#include "http_core.h"
#include "http_config.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h"
static int foo_handler(request_rec *r) {
if (!r->handler || strcmp(r->handler, "foo_handler")) return (DECLINED);
ap_set_content_type(r, "text/html");
ap_rprintf(r, "Hello, martian!");
return OK;
}
static void foo_hooks(apr_pool_t *pool) {
ap_hook_handler(foo_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
module AP_MODULE_DECLARE_DATA foo_module = {
STANDARD20_MODULE_STUFF,
NULL,
NULL,
NULL,
NULL,
NULL,
foo_hooks
};
这个模块通过AP_MODULE_DECLARE_DATA
来注册一个foo_module
:
module AP_MODULE_DECLARE_DATA foo_module = {
并会在运行时通过foo_hooks
中调用ap_hook_handler
将我们的逻辑函数foo_handler
注册进httpd:
static void foo_hooks(apr_pool_t *pool) {
ap_hook_handler(foo_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
我们的foo_handler
功能非常简单,并不处理用户请求request_rec
,只是先判断在httpd.conf
中模块是否设置为{foo_handler}
。判断完成后,这个模块会直接返回HTML
数据:
ap_set_content_type(r, "text/html");
ap_rprintf(r, "Hello, martian!");
理解了这个module的作用以后,接下来是编译这个module。
HTTPD
自己提供了一个很方便的module compiler叫做apxs
,我们可以用它来编译所写的foo_module
:
$ apxs -a -c foo_module.c
然后编译过程输出如下:
/usr/lib64/apr-1/build/libtool --silent --mode=compile gcc -prefer-pic -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic -DLINUX -D_REENTRANT -D_GNU_SOURCE -pthread -I/usr/include/httpd -I/usr/include/apr-1 -I/usr/include/apr-1 -c -o foo_module.lo foo_module.c && touch foo_module.slo
/usr/lib64/apr-1/build/libtool --silent --mode=link gcc -Wl,-z,relro,-z,now -o foo_module.la -rpath /usr/lib64/httpd/modules -module -avoid-version foo_module.lo
可以看到apxs
展开成了复杂的libtool
编译命令,编译后生成了很多library文件:
$ ls
foo_module.c foo_module.la foo_module.lo foo_module.o foo_module.slo
此外还有很多文件在隐藏目录.libs
里面:
$ ls -l ./.libs/
total 104
-rw-rw-r--. 1 weli weli 35580 Jan 27 02:55 foo_module.a
lrwxrwxrwx. 1 weli weli 16 Jan 27 02:55 foo_module.la -> ../foo_module.la
-rw-rw-r--. 1 weli weli 938 Jan 27 02:55 foo_module.lai
-rw-rw-r--. 1 weli weli 35432 Jan 27 02:55 foo_module.o
-rwxrwxr-x. 1 weli weli 25560 Jan 27 02:55 foo_module.so
接下来我们可以把这个编译好的module安装进httpd
的标准位置,我们可以使用apxs -i
命令来完成:
$ sudo apxs -i -a foo_module.la
此外还帮我们在httpd中加载好了这个module:
[weli@localhost httpd]$ grep 'foo' conf/httpd.conf
LoadModule foo_module modules/module_foo.so
加载了这个模块后,我们来使用它。
还记得我们在module_foo.c
中写的:
static int foo_handler(request_rec *r) {
if (!r->handler || strcmp(r->handler, "foo_handler")) return (DECLINED);
...
因此,在httpd.conf的最后添加一行配置:
<Location /foo>
SetHandler foo_handler
</Location>
这样,当用户请求/foo这个位置时,由foo_handler
负责处理请求并返回。可以重启一下httpd服务然后访问这个url来验证:
$ curl http://localhost/foo
Hello, martian!
可以看到我们的module已经工作了。
小结
在本文中,简单介绍了httpd module的开发流程,以及module的加载及配置方式。