apache 模块开发之输出过滤器

xiaoxiao2026-08-13  46

前面写了apache模块开发之hellloworld ,那个例子是一个简单的内容生成器。现在我们来讲一个过滤器的实现。过滤器是apache中一个非常精妙的设计,apache自带的很多模块都是通过过滤器来实现。 对于过滤器,有输入过滤器与输出过滤器两种。 对于输入过滤器,就是在内容生成器之前执行,而输出过滤器则在内容生成器之后。可以有下面的顺序: http请求-》输入过滤器-》内容生成-》输出过滤器-》用户 所有的请求都会经过我们的过滤器,所以我们可以对这些进行操作,比如统计流量,压缩等等。 下面我们讲一个例子,这个例子是apache自带的一个demo,它的功能是把页面中所有的小写字母变成大写字母。 从上面的过滤器流程中我们可以找到,只要我们在输出过滤器中挂一个钩子,然后将所有的字符变成大写就OK了。 代码如下: #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); } apr_brigade_cleanup(pbbIn); 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}; 首先所有程序的入口还是case_filter_module这个变量,它和之前的helloworld多了几个选项,其中CaseFilterCreateServerConfig这是一个服务器初始化函数,这个初始化会在apache启动的时候执行。 我们来看下具体的实现: static void *CaseFilterCreateServerConfig(apr_pool_t *p,server_rec *s){ CaseFilterConfig *pConfig=apr_pcalloc(p,sizeof *pConfig); pConfig->bEnabled=0; return pConfig;} 这里我们需要讲一下在apache中申请内存空间和普通的c语言的malloc有所区别,apache的申请空间都是向池申请,然后在这个池消失的时候就自动的释放我们的空间。其效率也要高于我们普通c语言malloc的堆空间分配的。 具体语法: CaseFilterConfig *pConfig=apr_pcalloc(p,sizeof *pConfig); 这里就是向p这个池申请了空间,这个变量初始化以后,它的生命周期是等同于整个httpd进程的。所以在我们后面的程序中都可以调用。 case_filter_module这个变量还有CaseFilterCmds这个变量,这是一个配置命令,也就是说当我们想把一些参数通过配置httpd.conf来传入apache的时候,我们就通过这个函数指针。 它的结构为: static const command_rec CaseFilterCmds[] ={ AP_INIT_FLAG("CaseFilter", CaseFilterEnable, NULL, RSRC_CONF, "Run a case filter on this host"), { NULL }}; 变量第一个参数为我们配置的选项,也就是我们的key,第二个参数是这个配置命令将执行的函数,我们就是通过这个函数来传递我们的配置的。例如本例子我们需要在httpd.conf中加入CaseFilter on这个命令,我们也可以看到CaseFilterEnable的实现为: 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;} case_filter_module最后一个参数就是我们的注册函数,具体实现如下: 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);} ap_hook_insert_filter 就是通过CaseFilterInsertFilter来讲我们的过滤器加入到apache过滤链中,然后就是ap_register_output_filter这个函数将我们的过滤器模块注册起来。 当我们需要输出一个页面的时候,就会触发ap_register_output_filter中低二个参数,这个参数为我们的处理函数指针,也就是我们所有的操作都是可以在这里搞定。对于这里,我们把所有的小写字母转化为大写字母,当然我们可以做更多的事情。 最后我们在我们的httpd.conf中加入 Loadmodule******//加载我们的模块 再加上我们的配置命令 CaseFilter on //开启我们的过滤模块,我们也可以加入很多类似的配置。 编译我们的模块,重新启动apache,在看看我们的apache中所有的页面中的小写变成了大写。 最后还需要讲一点的时候,上面用的很多函数和标准C是有区别的,都是调用的apr的库。所以我们还要去熟悉下这里的函数,总体来说和标准C是差不多的,只是做了一些优化。
转载请注明原文地址: https://www.6miu.com/read-5051126.html

最新回复(0)