CentOS下Apache模块开发helloworld无错版(4)

Apache模块开发学习笔记

最近开始研究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/src
mkdir modtest
cd modtest
apxs 
--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 
*= f->r;
    conn_rec 
*= 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 --i mod_case_filter

确认模块安装完毕:

cd /etc/httpd/modules
ls 
| 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出去的内容做更改。

效果图:

CentOS下Apache模块开发helloworld无错版


 

简单的介绍一下代码,首先实现几个Apache的钩子,比如:ap_hook_insert_filter和ap_register_output_filter

具体Apache钩子函数的介绍可以参考:这页的最后

然后写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等服务器也是一样的道理。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://127.0.0.1/wyysxy.html