- 论坛徽章:
- 0
|
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.
希望对大家有用。 |
|