apache2.x filter编程入门介绍
linux_prog
|
1#
linux_prog 发表于 2008-04-08 13:48
apache2.x filter编程入门介绍
from: http://www.loveopensource.com/?p=35
Apache2.x filter编程(方便的控制apache的输入和输出) by linux_prog Apache2中引入了filter的概念,可以利用filter很方便的对接受到的数据和发出去的数据进行更改或者替换。 在Apache1中做这些事情比较的困难,利用filter,做这些事情变得非常的简单。 先分析一下apache2自带的一个非常简单的filter: #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” #include <ctype.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) { // 插入filter ap_hook_insert_filter(CaseFilterInsertFilter,NULL,NULL,APR_HOOK_MIDDLE); // 注册filter 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 //注册output filter }; 安装方法: ./bin/apxs -c ./mod_case_filter.c ./bin/apxs -a -i -n case_filter ./mod_case_filter.la 在httpd.conf中加入: CaseFilter on 这个时候访问启动apache,访问一个页面看看,查看页面源代码,发现页面中的每个字符都变成了大写。 这就是filter改变了输出的内容的结果。 这个是一个很好的例子,在这个例子上做一些改变可以很方便的实现自己对所有经过apache出去的内容做更改。 下面对filter编写中的一些难点做一些解释: (1) 常见filter的种类 output filter / input filter (2) filter类型 util_filter.h中有定义: typedef enum { /** These filters are used to alter the content that is passed through * them. Examples are SSI or PHP. */ AP_FTYPE_RESOURCE = 10, /** These filters are used to alter the content as a whole, but after all * AP_FTYPE_RESOURCE filters are executed. These filters should not * change the content-type. An example is deflate. */ AP_FTYPE_CONTENT_SET = 20, /** These filters are used to handle the protocol between server and * client. Examples are HTTP and POP. */ AP_FTYPE_PROTOCOL = 30, /** These filters implement transport encodings (e.g., chunking). */ AP_FTYPE_TRANSCODE = 40, /** These filters will alter the content, but in ways that are * more strongly associated with the connection. Examples are * splitting an HTTP connection into multiple requests and * buffering HTTP responses across multiple requests. * * It is important to note that these types of filters are not * allowed in a sub-request. A sub-request’s output can certainly * be filtered by ::AP_FTYPE_RESOURCE filters, but all of the “final * processing” is determined by the main request. */ AP_FTYPE_CONNECTION = 50, /** These filters don’t alter the content. They are responsible for * sending/receiving data to/from the client. */ AP_FTYPE_NETWORK = 60 } ap_filter_type; 可见,上面是一个AP_FTYPE_RESOURCE的filter,用来修改页面内容,类似于PHP。 AP_FTYPE_CONTENT_SET filter是把内容当作一个整体来处理,像mod_deflate将输出压缩。 一般应用中,这两种filter是我们常用到的。 (3) filter执行顺序 按照ap_filter_type从小到大的顺序执行,也就是说也执行AP_FTYPE_RESOURCE,再执行AP_FTYPE_CONTENT_SET。。。。。 当然,你也可以这样定义filter: ap_register_output_filter(s_szCaseFilterName,CaseFilterOutFilter,NULL, AP_FTYPE_RESOURCE-1); 这时filter将会在所有AP_FTYPE_RESOURCE filter前面执行。 如果两个filter都属于一个级别的filter,比如都属于AP_FTYPE_RESOURCE,那么按照httpd.conf中: LoadModule php5_module modules/libphp5.so LoadModule case_filter_module modules/mod_case_filter.so 的先后顺序依次执行各个filter. (4) 编写filter中用到的重要数据结构: apr_bucket_brigade *pbbOut; // 这个东西把所有的输入或者输出串起来,类似于一个链表 apr_bucket *pbktIn; // 这个东西就是链表中的一个元素 apache提供很多的宏对这两个结构做处理。 比如: // 遍历输出内容 for (pbktIn = APR_BRIGADE_FIRST(pbbIn); pbktIn != APR_BRIGADE_SENTINEL(pbbIn); pbktIn = APR_BUCKET_NEXT(pbktIn)) { } // 得到下一个bucket apr_bucket *newb = APR_BUCKET_NEXT(pbktIn); //从当前apr_bucket_brigade中删除apr_bucket,但不释放资源 APR_BUCKET_REMOVE(pbktIn); //从当前apr_bucket_brigade中删除apr_bucket,且释放资源 apr_bucket_delete(pbktIn); 对于一个apr_bucket,你可以做split,分割成多个apr_bucket,方便你修改数据。 比如: int index; apr_bucket_split(pbktIn, index); //从pbktIn的index位置开始分裂 filter是非常重要的一个功能,利用它我们可以开发出功能非常强大的apache module. 希望对大家有用。 |