将 Apache httpd 作为应用开发平台(2)

来源:developerworks 作者:developerworks
  

 

这个函数通过 Apache 提供的 API:ap_get_client_block 将请求中 POST 的数据读入到缓冲区,如果预分配的缓冲区不够,则重新分配内存存放,并同时修改缓冲区的实际长度。然后,我们在 handler 中调用此函数


清单 9. echo 模块的 handler 实现

				
 /* The sample content handler */ 
 static int echo_post_handler(request_rec *req) 
 { 
    if (strcmp(req->handler, "echo_post")) { 
        return DECLINED; 
    } 

    if((req->method_number != M_GET) && (req->method_number != M_POST)){ 
        return HTTP_METHOD_NOT_ALLOWED; 
    } 

    char *post = (char *)malloc(sizeof(char)*DFT_BUF_SIZE); 
    size_t post_size = DFT_BUF_SIZE; 

    if(post == NULL){ 
        return HTTP_INTERNAL_SERVER_ERROR; 
    } 

    memset(post, '\0', post_size); 

    int ret = read_post_data(req, &post, &post_size); 
    if(ret != OK){ 
        free(post); 
        post = NULL; 
        post_size = 0; 
        return ret; 
    } 

    ap_set_content_type(req, "text/html;charset=utf-8"); 
    ap_set_content_length(req, post_size); 

    if(post_size == 0){ 
        ap_rputs("no post data found", req); 
        return OK; 
    } 

    ap_rputs(post, req); 

    free(post); 
    post = NULL; 
    post_size = 0; 

    return OK; 
 } 

 

handler 读到客户端 POST 数据之后,将数据原封不动地回显 (echo) 给客户端。在调用 ap_rputs 将数据写回客户端之后,释放动态分配的内存。

运行 echo 模块

配置信息如第一小节中,拷贝模块到 apache_home/modules/ 下之后,重启 httpd,以便重新加载 echo_post 模块。我们仍旧使用 curl 程序来模拟客户端调用,发送一个文件的内容到 echo_post 模块:


清单 10. 测试

				
 $ curl -F "file=@Makefile" http://10.111.43.145:9527/echo_post 

 

curl 的 -F 选项指定一个本地文件,并将文件内容 POST 到指定的 URL,运行结果如下:


清单 11. 运行结果

				
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current 
                                 Dload  Upload   Total   Spent    Left  Speed 
 100  2334  100  1167  100  1167  28789  28789 --:--:-- --:--:-- --:--:-- 29175 
 ------------------------------4dba7c85f3d1 
 Content-Disposition: form-data; name="file"; filename="Makefile"
 Content-Type: application/octet-stream 

 ## 
 ##  Makefile -- Build procedure for sample echo_post Apache module 
 ##  Autogenerated via ``apxs -n echo_post -g''. 
 ## 

 builddir=. 
 top_srcdir=/etc/httpd 
 top_builddir=/etc/httpd 
 include /usr/lib/httpd/build/special.mk 

 #   the used tools 
 APXS=apxs 
 APACHECTL=apachectl 

 #   additional defines, includes and libraries 
 #DEFS=-Dmy_define=my_value 
 #INCLUDES=-Imy/include/dir 
 ...... 

 

可以看到,echo_post 会将 Makefile 的内容原封不动的返回。我们需要更进一步的改造这个模块,并引入模块的配置等新的内容。

可配置的 echo 模块

在这一小节,我们继续扩展上例中的 echo_post 模块,我们将 echo_post 扩展为可配置的模块,通过修改配置文件 httpd.conf 中设置 ConvertType 的值,可以使得模块在运行时的行为发生变化。

配置信息读取

首先定义一个配置信息的结构体:


清单 12. 配置定义结构体

				
 typedef struct{ 
    int convert_type; // 转换类型
 }cust_config_t; 

 

为了便于演示,这个结构体仅有一个成员,convert_type, 表示转换类型,如果在配置文件中该值被设置为 0,则将客户端 POST 的数据转换为大写,如果为 1,则转换为小写。这样即可通过配置信息修改模块运行时的行为。

加入函数声明:


清单 13. 函数声明

				
 static void *create_config(apr_pool_t *pool, server_rec *server); 

 static const char *set_mod_config(cmd_parms *params, 
		 void *config, const char *arg); 

 

 

create_config 函数用以创建一个用户自定义的结构体,告诉 Apache 如果创建这个结构体。set_mod_config 函数用以设置配置结构体中的成员,这个函数注册在 command_rec 数组中。而 command_rec 数组则保存在模块声明结构体中: 定义一个 command_rec 结构体类型的数组:


清单 14. command_rec 结构体

				
 static const command_rec cust_echo_cmds[] = { 
    AP_INIT_TAKE1("ConvertType", 
    		 set_mod_config, 
    		 NULL, 
    		 RSRC_CONF, 
    		"convert type of post data"), 
    {0} 
 }; 

 

这个模块的模块声明部分较前面小节中的例子更复杂一些,我们启用了配置信息表 cust_echo_cmds,并注册了创建配置结构的函数 create_config。


清单 15. 注册模块回调函数

				
 /* Dispatch list for API hooks */ 
 module AP_MODULE_DECLARE_DATA cust_echo_post_module = { 
    STANDARD20_MODULE_STUFF, 
    NULL,                  /* create per-dir    config structures */ 
    NULL,                  /* merge  per-dir    config structures */ 
    create_config,       /* create per-server config structures */ 
    NULL,                  /* merge  per-server config structures */ 
    cust_echo_cmds,      /* table of config file commands       */ 
    cust_echo_post_register_hooks  /* register hooks                      */ 
 }; 

 

运行可配置 echo 模块

在 httpd.conf 配置文件中,加入 LoadModule 指令加载此模块,并设置配置项 ConvertType 为 0


清单 16. httpd.conf 配置文件

				
 LoadModule cust_echo_post_module modules/mod_cust_echo_post.so 
 <Location /cust_echo_post> 
 SetHandler cust_echo_post 
 </Location> 

 #configure for cust_echo_post 
 ConvertType 0 

 

这样,我们通过 curl 测试可以得到如下结果:


清单 17. 测试 1

				
 $ curl -d "hello darkness my old friend" \ 
 http://10.111.43.145:9527/cust_echo_post 
 HELLO DARKNESS MY OLD FRIEND 

 

修改 httpd.conf 中的 ConvertType 为 1,重启 Apache httpd,重新测试:


清单 18. 测试 2

				
 $ curl -d "HELLO DARKNESS MY OLD FRIEND" \ 
 http://10.111.43.145:9527/cust_echo_post 
 hello darkness my old friend 

 

过滤器

过滤器事实上是另一种形式的模块,其生命周期及调用时机请参看第一小节。Apache 对通用的数据结构都做过一些封装,并以库的方式提供 ( 即 APR(Apache Portable Runtime))。在过滤器中,有两个比较重要的数据结构:apr_bucket 和 apr_bucket_brigade。apr_bucket_birgade 相当于一个环状队列,而 apr_bucket 是队列中的元素。这两个数据结构的名字可能来源于救火队。救火人员站成一个长队,一个队头临近水源,另一头用水灭火,然后每个队列中的人员将水桶从上一个人手中接过,然后传递给下一个人。过滤器的工作方式与此类似,所有的过滤器形成一个长链,数据从上一个过滤器流入,进行过滤,然后将加工过的数据流入下一个过滤器。

我们的过滤器非常简单,从上一个过滤器中读到数据,将数据中的字符串转换为大写,然后将桶 (apr_bucket) 传递给下一个过滤器。Apache 提供了丰富的 API 来完成这一系列的操作。

大小写转换过滤器


清单 19. 过滤器实现

				
 static apr_status_t case_filter(ap_filter_t *filter, 
        apr_bucket_brigade *bbin){ 
    request_rec *req = filter->r; 
    conn_rec *con = req->connection; 

    apr_bucket *bucket; 
    apr_bucket_brigade *bbout; 

    //create brigade 
    bbout = apr_brigade_create(req->pool, con->bucket_alloc); 

    //iterate the full brigade 
    APR_BRIGADE_FOREACH(bucket, bbin){ 
        if(APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)){ 
            APR_BUCKET_REMOVE(bucket); 
            APR_BRIGADE_INSERT_TAIL(bbout, bucket); 
            return ap_pass_brigade(filter->next, bbout); 
        } 
        char *data, *buffer; 
        apr_size_t data_len; 

        //read content of current bucket in brigade 
        apr_bucket_read(bucket, &data, &data_len, APR_NONBLOCK_READ); 
        buffer = apr_bucket_alloc(data_len, con->bucket_alloc); 
        int i; 
        for(i = 0; i < data_len; i++){ 
        	 //convert 
            buffer[i] = apr_toupper(data[i]); 
        } 

        apr_bucket *temp_bucket; 
        temp_bucket = apr_bucket_heap_create( 
                buffer, data_len, apr_bucket_free, con->bucket_alloc); 

        APR_BRIGADE_INSERT_TAIL(bbout, temp_bucket); 
    } 

    return APR_SUCCESS; 
 } 

 

注册这个过滤器:


清单 20. 注册过滤器

				
 static void filter_echo_post_register_hooks(apr_pool_t *p) 
 { 
    ap_register_output_filter(filter_name, 
    		 case_filter, NULL, AP_FTYPE_RESOURCE); 
 } 

时间:2012-02-25 12:31 来源:developerworks 作者:developerworks 原文链接

好文,顶一下
(1)
100%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量