Apache模块开发学习笔记(一)
先祝大家2009年心想事成,万事如意,工作顺利,心情愉快啊!
最近开始研究Apache模块的开发,在博客里记录一下学习的过程吧,
本来没什么头绪,现在研究下来,
主要学习Apache的基础对象和 API头文件,例如http_头文件里的函数,
APR(Apache Portable Runtime)函数库,请求处理流程,过滤器架构,页面内容生成等等
我是在Fedora10上开发的,现介绍一下环境的搭建:
# yum install httpd-devel# yum install automake autoconf# yum install libtool# yum install ImageMagick# yum install ImageMagick-devel# yum install pcre-devel
现在看一个apache2自带的一个非常简单的filter:
首先创建一个Module工程
cd /usr/local/srcmkdir modtestcd modtestapxs -g -n case_filter
自动声称如下文件:
ls -a ./test. .. .deps Makefile case_filter.c modules.mk
修改case_filter.c,代码如下:
#include "httpd.h"#include "http_config.h"#include "apr_buckets.h"#include "apr_general.h"#include "apr_lib.h"#include "util_filter.h"#include "http_request.h"static const char s_szCaseFilterName[] = "CaseFilter";module AP_MODULE_DECLARE_DATA case_filter_module;typedef struct { int bEnabled;} CaseFilterConfig;static void *CaseFilterCreateServerConfig(apr_pool_t *p, server_rec *s) { CaseFilterConfig *pConfig = apr_pcalloc(p, sizeof *pConfig); pConfig->bEnabled = 0; return pConfig;}static void CaseFilterInsertFilter(request_rec *r) { CaseFilterConfig *pConfig = ap_get_module_config(r->server->module_config, &case_filter_module); if (!pConfig->bEnabled) return; ap_add_output_filter(s_szCaseFilterName, NULL, r, r->connection);}static apr_status_t CaseFilterOutFilter(ap_filter_t *f, apr_bucket_brigade *pbbIn) { request_rec *r = f->r; conn_rec *c = r->connection; apr_bucket *pbktIn; apr_bucket_brigade *pbbOut; pbbOut = apr_brigade_create(r->pool, c->bucket_alloc); for (pbktIn = APR_BRIGADE_FIRST(pbbIn); pbktIn != APR_BRIGADE_SENTINEL(pbbIn); pbktIn = APR_BUCKET_NEXT(pbktIn)) { const char *data; apr_size_t len; char *buf; apr_size_t n; apr_bucket *pbktOut; if (APR_BUCKET_IS_EOS(pbktIn)) { apr_bucket *pbktEOS = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(pbbOut, pbktEOS); continue; } /* read */ apr_bucket_read(pbktIn, &data, &len, APR_BLOCK_READ); /* write */ buf = apr_bucket_alloc(len, c->bucket_alloc); for (n = 0; n < len; ++n) buf[n] = apr_toupper(data[n]); pbktOut = apr_bucket_heap_create(buf, len, apr_bucket_free, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut); } /* XXX: is there any advantage to passing a brigade for each bucket? */ return ap_pass_brigade(f->next, pbbOut);}static const char *CaseFilterEnable(cmd_parms *cmd, void *dummy, int arg) { CaseFilterConfig *pConfig = ap_get_module_config(cmd->server->module_config, &case_filter_module); pConfig->bEnabled = arg; return NULL;}static const command_rec CaseFilterCmds[] ={ AP_INIT_FLAG("CaseFilter", CaseFilterEnable, NULL, RSRC_CONF,"Run a case filter on this host"), { NULL}};static void CaseFilterRegisterHooks(apr_pool_t *p) { ap_hook_insert_filter(CaseFilterInsertFilter, NULL, NULL, APR_HOOK_MIDDLE); ap_register_output_filter(s_szCaseFilterName, CaseFilterOutFilter, NULL, AP_FTYPE_RESOURCE);}module AP_MODULE_DECLARE_DATA case_filter_module ={ STANDARD20_MODULE_STUFF, NULL, NULL, CaseFilterCreateServerConfig, NULL, CaseFilterCmds, CaseFilterRegisterHooks};然后编译:
apxs -c -i mod_case_filter
确认模块安装完毕:
cd /etc/httpd/modulesls | grep mod_case_filter
在httpd.conf中加入:
LoadModule case_filter_module modules/mod_case_filter.so<Location /mod_case_filter> SetHandler mod_case_filter </Location>CaseFilter on
这个时候访问启动apache,
apachectl restart
访问一个页面看看,发现页面中的每个字符都变成了大写。
这就是filter改变了输出的内容的结果。
这个是一个很好的例子,在这个例子上做一些改变可以很方便的实现自己对所有经过apache出去的内容做更改。
效果图:

简单的介绍一下代码,首先实现几个Apache的钩子,比如:ap_hook_insert_filter和ap_register_output_filter
具体Apache钩子函数的介绍可以参考:
http://httpd.apache.org/docs/2.0/developer/modules.html
然后写filter模块的话 ,有一个非常重要的数据结构apr_bucket
简单的说bucket是流数据的抽象,数据源包括了文件,管道,网络,内存等,抽象化了之后,不管是什么数据源,都统一了操作,
主要可以进行read、split、copy三个操作,
apr_bucket* b ; //当前元素
apr_bucket_brigade* bbin ; //链表
遍历客户端输入的内容内容
for ( b = APR_BRIGADE_FIRST(bbin) ;
b != APR_BRIGADE_SENTINEL(bbin) ;
b = APR_BUCKET_NEXT(b) ) {
{ }
//从当前apr_bucket_brigade中删除apr_bucket,但不释放资源
APR_BUCKET_REMOVE(b);
//从当前apr_bucket_brigade中删除apr_bucket,且释放资源
apr_bucket_delete(b);
输出结果的话有这几个函数apr_brigade_puts(),apr_brigade_printf(),apr_brigade_write(),apr_brigade_putc()
filter模块可以在请求数据可以在到达内容生成器之前被输入过滤器
进行处理,回复数据可以在发送到客户端之前被输出过滤器进行处理。过滤器使得数据处
理的实现比以往的版本更加简洁有效,同时将内容生成器从它的变换(transformation)和
集合(aggregation)中分离出来。
说了这么多,我想 每个真正的Web程序员,有机会都应该看看Apache的源代码,都会有可能要修改甚至新作一个Apache模块的,
不光是Apache,lighttpd和nginx等服务器也是一样的道理,祝大家新年快乐啊!