Apache模块开发/用C语言扩展apache

jackxiang 2011-6-5 09:30 | |
Apache模块开发/用C语言扩展apache(1:简述)
by linux_prog
   Apache是一个非常稳定而且非常open的web server,它的很多功能都可以通过plugin的方式去扩展。
比如:mod_proxy使得apache可以作代理, mod_rewrite使得apache可以实现非常强大的url mapping和rewritting
功能,你是否也想自己来开发一个apache module呢?网上这方面的文章非常的少,而且全是E文,
希望我的这篇文章能够给你一些实质性的帮助。
   开发apache module之前,我们有必要先分析一下其源代码。
   $ cd httpd-2.2.4/
   $ ls
   其中:server/目录是apache核心程序的代码
         include/目录存放主要的头文件
         srclib/目录存放apr和apr-util代码(这两个是什么,后面介绍)
         modules/目录下存放目前已经有的各种module(可以看看这些代码先)
   $ cd include/
   先分析一下apache的头文件
   $ vi httpd.h
   第766行,这个结构非常的重要,后面编写模块时都要用到这个结构,所以分析一下。
   每个http request都会对应这个结构的一个实例。
   由于apache的源代码都有很详细的英文注释,所以我也不翻译了。
struct request_rec {
    //内存管理池,后面讲apr时会讲到
    apr_pool_t *pool;
    conn_rec *connection;
    server_rec *server;
    request_rec *next;
    request_rec *prev;
    request_rec *main;
    char *the_request;
    int assbackwards;
    int proxyreq;
    int header_only;
    char *protocol;
    int proto_num;
    const char *hostname;
    apr_time_t request_time;
    const char *status_line;
    int status;
    const char *method;
    int method_number;
    apr_int64_t allowed;
    apr_array_header_t *allowed_xmethods;
    ap_method_list_t *allowed_methods;
    apr_off_t sent_bodyct;
    apr_off_t bytes_sent;
    apr_time_t mtime;
    int chunked;
    const char *range;
    apr_off_t clength;
    apr_off_t remaining;
    apr_off_t read_length;
    int read_body;
    int read_chunked;
    unsigned expecting_100;
    apr_table_t *headers_in;
    apr_table_t *headers_out;
    apr_table_t *err_headers_out;
    apr_table_t *subprocess_env;
    apr_table_t *notes;
    const char *content_type;
    const char *handler;
    const char *content_encoding;
    apr_array_header_t *content_languages;
    char *vlist_validator;
    char *user;
    char *ap_auth_type;
    int no_cache;
    int no_local_copy;
    char *unparsed_uri;
    char *uri;
    char *filename;
    char *canonical_filename;
    char *path_info;
    char *args;
    apr_finfo_t finfo;
    apr_uri_t parsed_uri;
    int used_path_info;
    struct ap_conf_vector_t *per_dir_config;
    struct ap_conf_vector_t *request_config;
    const struct htaccess_result *htaccess;
    struct ap_filter_t *output_filters;
    struct ap_filter_t *input_filters;
    struct ap_filter_t *proto_output_filters;
    struct ap_filter_t *proto_input_filters;
    int eos_sent;
};
   可以看到源码中有很多apr_开头的结构,这个是什么呢?下节介绍一下。
Apache模块开发/用C语言扩展apache(2:APR编程介绍)
by linux_prog
    可以看到apache代码中使用了大量的以apr_开头的结构或者函数,这些其实是APR.
    什么是apr?
    我的理解是apache工作小组在编写apache等C程序过程中所积累的一套编程框架,里面提供比较先进的内存管理模式
和常用的数据结构,另外根据各种平台作了一些不同的宏定义,让代码做到平台无关性。由于做得不错,后来,就干脆把它从apache源代码中脱离出来,又搞了一个项目,apache官方站点上也有它的相关介绍:http://apr.apache.org/
    The mission of the Apache Portable Runtime (APR) project is to create and maintain software libraries that provide a predictable and consistent interface to underlying platform-specific implementations. The primary goal is to provide an API to which software developers may code and be assured of predictable if not identical behaviour regardless of the platform on which their software is built, relieving them of the need to code special-case conditions to work around or take advantage of platform-specific deficiencies or features.
     apr可以独立于apache安装,让我们编写应用程序时也使用。
     有个非常好的英文文档介绍了怎么样使用:http://dev.ariel-networks.com/ap ... l/apr-tutorial.html
     上面那个文档非常不错,大家看懂了,我写的这篇就不用看了。
     我这个把几个以后经常要用到的介绍一下。
    (1) apr_pool_t *pool;
    内存池,所有的内存操作可以基于这个池之上,当这个池不再需要时,一次性释放该池上的所有内存,这个模式非常适合基于
session服务的编程模式,比如:每个http request都分配一个apr_pool_t,当该http request end时,一次性释放该request运行时申请的所有内存。可以看到struct request_rec中就有这个东东。
    用法:
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
char *buf1;
buf1 = apr_palloc(mp, MEM_ALLOC_SIZE);
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
for (i = 0; i < n; ++i) {
    do_operation(..., mp);
    apr_pool_clear(mp);
}
apr_pool_destroy(mp);
    (2)数据结构--容器
    动态数组---apr_array_header_t
    类似于hashmap的apr_table_t:
    apr_table_t *tab;
    tab = apr_table_make(mp, 5);
    apr_table_setn(tab, "foo", "bar");
    apr_table_setn(tab, apr_pstrdup(mp, "foo"), apr_pstrdup(mp, "bar"));
    const char *v = apr_table_get(tab, "mykey");
    可以看到apr_table_t存储key-value pairs类型的数据非常方便,难怪apache所有的http header都用它存储。
    其他操作apr_table_t的函数:
APR_DECLARE(const apr_array_header_t *) apr_table_elts(const apr_table_t *t);
APR_DECLARE(int) apr_is_empty_table(const apr_table_t *t);
APR_DECLARE(int) apr_is_empty_array(const apr_array_header_t *a);
APR_DECLARE(apr_array_header_t *) apr_array_make(apr_pool_t *p,
                                                 int nelts, int elt_size);
APR_DECLARE(void *) apr_array_push(apr_array_header_t *arr);
APR_DECLARE(void *) apr_array_pop(apr_array_header_t *arr);
APR_DECLARE(void) apr_array_cat(apr_array_header_t *dst,
           const apr_array_header_t *src);
APR_DECLARE(apr_array_header_t *) apr_array_copy(apr_pool_t *p,
                                      const apr_array_header_t *arr);
APR_DECLARE(apr_array_header_t *) apr_array_copy_hdr(apr_pool_t *p,
                                      const apr_array_header_t *arr);
APR_DECLARE(apr_array_header_t *) apr_array_append(apr_pool_t *p,
                                      const apr_array_header_t *first,
                                      const apr_array_header_t *second);
APR_DECLARE(char *) apr_array_pstrcat(apr_pool_t *p,
          const apr_array_header_t *arr,
          const char sep);
APR_DECLARE(apr_table_t *) apr_table_make(apr_pool_t *p, int nelts);
APR_DECLARE(apr_table_t *) apr_table_copy(apr_pool_t *p,
                                          const apr_table_t *t);
APR_DECLARE(void) apr_table_clear(apr_table_t *t);
APR_DECLARE(const char *) apr_table_get(const apr_table_t *t, const char *key);
APR_DECLARE(void) apr_table_set(apr_table_t *t, const char *key,
                                const char *val);
APR_DECLARE(void) apr_table_setn(apr_table_t *t, const char *key,
                                 const char *val);
APR_DECLARE(void) apr_table_unset(apr_table_t *t, const char *key);
APR_DECLARE(void) apr_table_merge(apr_table_t *t, const char *key,
                                  const char *val);
APR_DECLARE(void) apr_table_mergen(apr_table_t *t, const char *key,
                                   const char *val);
APR_DECLARE(void) apr_table_add(apr_table_t *t, const char *key,
                                const char *val);
APR_DECLARE(void) apr_table_addn(apr_table_t *t, const char *key,
                                 const char *val);
APR_DECLARE(apr_table_t *) apr_table_overlay(apr_pool_t *p,
                                             const apr_table_t *overlay,
                                             const apr_table_t *base);
typedef int (apr_table_do_callback_fn_t)(void *rec, const char *key,
                                                    const char *value);
APR_DECLARE_NONSTD(int) apr_table_do(apr_table_do_callback_fn_t *comp,
                                     void *rec, const apr_table_t *t, ...);
APR_DECLARE(int) apr_table_vdo(apr_table_do_callback_fn_t *comp,
                               void *rec, const apr_table_t *t, va_list vp);
#define APR_OVERLAP_TABLES_SET   (0)
#define APR_OVERLAP_TABLES_MERGE (1)
APR_DECLARE(void) apr_table_overlap(apr_table_t *a, const apr_table_t *b,
                                     unsigned flags);
APR_DECLARE(void) apr_table_compress(apr_table_t *t, unsigned flags);
    其他apr提供的数据结构还有:hash和list,这里不再详述。
    (3) 字符串操作
    写过C的人都知道,处理字符串是非常头痛的问题,搞不好就内存溢出,apr也提供一些字符串函数,都是基于apr_pool_t,
使用时不用担心内存溢出的问题。
     把apr_strings.h贴出来大家一起看看,注释也比较详细,不多说:
APR_DECLARE(int) apr_strnatcmp(char const *a, char const *b);
APR_DECLARE(int) apr_strnatcasecmp(char const *a, char const *b);
APR_DECLARE(char *) apr_pstrdup(apr_pool_t *p, const char *s);
APR_DECLARE(char *) apr_pstrmemdup(apr_pool_t *p, const char *s, apr_size_t n);
APR_DECLARE(char *) apr_pstrndup(apr_pool_t *p, const char *s, apr_size_t n);
APR_DECLARE(void *) apr_pmemdup(apr_pool_t *p, const void *m, apr_size_t n);
Apache模块开发/用C语言扩展apache(3:一个非常简单的apache module)
by linux_prog
    有了上面几篇文章的基础,大家自己再下点功夫,应该可以去写一些简单的模块了,
下面贴出一个很简单的apache module,大家一起分析一下。
    $ cd /usr/local/apache2.2.4
    $ vi mod_c.c
#include
#include
#include “apr.h”
#include “apr_lib.h”
#include “apr_strings.h”
#define APR_WANT_STRFUNC
#include “apr_want.h”
#include “httpd.h”
#include “http_config.h”
#include “http_core.h”
#include “http_request.h”
module AP_MODULE_DECLARE_DATA c_module;
static int c_handler(request_rec *r)
{
r->content_type=”text/plain”;
ap_rprintf(r,”handler:%s\n”,r->handler);
ap_rprintf(r,”query string:%s\n”,r->args);
ap_rprintf(r,”filename:%s\n”,r->filename);
return OK;
}
static void register_hooks(apr_pool_t *p)
{
ap_hook_handler(c_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
module AP_MODULE_DECLARE_DATA c_module = {
    STANDARD20_MODULE_STUFF,
    NULL,      
    NULL,                      
    NULL,      
    NULL,                      
    NULL,      
    register_hooks  
};
编译并安装这个模块(apache提供的apxs非常好):
     $ ./bin/apxs -c ./mod_c.c
     $ ./bin/apxs -a -i -n c mod_c.la
     这时apxs会自动帮我们把编译好的mod_c.so安装到modules/目录中,而且httpd.conf中已经把这个module load进去了:
     [root@cn-weblog apache2.2.4]# grep mod_c conf/httpd.conf    
      LoadModule c_module           modules/mod_c.so
     测试这个模块:
     $ ./bin/apachectl stop
     $ ./bin/apachectl start
     在IE中访问http://myhostname/index.html?query=yy
     IE中会出现:
handler:text/html
query string:query=yy
filename:/usr/local/apache2.2.4/htdocs/index.html
     说明该module运行成功。
     把上面的module简单解释一下。
     所有的apache module都必须是这个结构体,里面要定义各个内容。
module AP_MODULE_DECLARE_DATA c_module = {
    STANDARD20_MODULE_STUFF,
    NULL,      
    NULL,                      
    NULL,      
    NULL,                      
    //上面4项都是定义httpd.conf中命令的作用的
    NULL,       //定义在httpd.conf中添加的命令,和各命令的处理函数
    register_hooks          //hooks,定义什么时候执行我们这个module的相关函数
};
    ap_hook_handler(c_handler, NULL, NULL, APR_HOOK_MIDDLE);
    表示在处理内容请求时调用我们函数–c_handler
    http://httpd.apache.org/docs/2.2/developer/
    提供了非常不错的文档,可以参考一下。

from:http://www.loveopensource.com/?p=16
======================================================================
Apache模块开发/用C语言扩展apache(4:一个生产环境使用的apache module– viewvc权限控制)
by linux_prog
     下面公布一个目前在我们公司使用的apache module的源代码。
     我们公司开发人员很多,使用了SVN和viewvc来进行版本控制和查看,通过web界面,SVN能够根据每个用户的权限来控制能够
浏览某个项目下的代码,但是viewvc只要你在SVN中有用户,你就可以看当前SVN中所有项目的代码。这个风险比较大,因此,我们
开发了一个apache module,用来读取SVN的权限配置文件,把相应的权限集成到VIEWVC中。
源代码:

#include “apr_strings.h”
#include “apr_hash.h”
#include “apr_tables.h”
#include “apr_md5.h”            
#include “apr_lib.h”            
#include “apr_base64.h”        
#define APR_WANT_STRFUNC        
#include “apr_want.h”
#include “ap_config.h”
#include “httpd.h”
#include “http_config.h”
#include “http_core.h”
#include “http_log.h”
#include “http_protocol.h”
#include “http_request.h”
#include “ap_provider.h”
#include
#define ENABLED        1
#define DISABLED       0
typedef struct
{
    short    enabled;
    short    debug;
    char     *dir;
    // the starting path
    char     *prefixPath;
    // the stop url pattern
    char     *stopPattern;
    // the svn access file
    char     *accessFile;
} authSVN_rec;
struct access_rec
{
   // 0: group
   // 1: user
   // 2: all
   short              type;
   // the group or user name
   char              *name;
   // 0: don’t have read access
   // 1: have read access
   short             access;
   // the next access record
   struct access_rec *next;
};
module AP_MODULE_DECLARE_DATA authSVN_module;
// src starts with start
static short start_with(const char *src, const char *start)
{
int i = 0;
if(strlen(src) < strlen(start))
     return 0;
i = strlen(start) - 1;
while(i >= 0)
{
  if(src[i] != start[i])
      return 0;
  i–;
}
return 1;
}
// parse the SVN access file
static short parse_access_file(request_rec *r, const char* file,
                                     const authSVN_rec *conf,
                                     apr_hash_t* ugMap,
                                     apr_hash_t* accessMap)
{
    ap_configfile_t *f = NULL;
    apr_status_t status;
    char l[MAX_STRING_LEN], dir[256];
    status = ap_pcfg_openfile(&f, r->pool, file);
    short flag = 0;
    if (status != APR_SUCCESS)
        return 0;
    while(!(ap_cfg_getline(l, MAX_STRING_LEN, f)))
    {
        const char *w = NULL;
        char *last = NULL;
        apr_table_t *apt  = NULL;
        struct access_rec  *arec = NULL, *arecp = NULL;
        if ((l[0] == ‘#’) || (!l[0])) {
            continue;
        }
        if(start_with(l, “[groups]”)) {
            flag = 1;
            continue;
        }
        if(l[0] == ‘[’) {
            flag = 2;
            w = apr_strtok(l, “[]:\n”, &last);
            if(w && w[0] == ‘/’) {
                // the root directory
                snprintf(dir, sizeof(dir), “%s”, conf->prefixPath);
                dir[strlen(dir) - 1] = ‘\0′;
            }
            else if(w && w[0] != ‘/’)
            {
                const char *project = w;
                w = apr_strtok(NULL, “[]:\n”, &last);
                if(w)
                {
                    snprintf(dir, sizeof(dir), “%s%s%s”, conf->prefixPath, project, w);
                    // make sure the dir is not end with /
                    int len = strlen(dir);
                    if(dir[len - 1] == ‘/’) dir[len - 1] = ‘\0′;
                }
                else
                    dir[0] = ‘\0′;
            }
            else
            {
                dir[0] = ‘\0′;
            }
            continue;
        }
        if(flag == 1) {
            // this is the groups and users definition
            w = apr_strtok(l, “=, \n”, &last);
            if(w == NULL)
                // group name not found
                continue;
            apt = (apr_table_t *)apr_hash_get(ugMap, (const void *)w, APR_HASH_KEY_STRING);
            if(apt == NULL) {
                apt = apr_table_make(r->pool, 10);
                apr_hash_set(ugMap, (const void *)apr_pstrdup(r->pool, w),
                             APR_HASH_KEY_STRING, (const void *)apt);
            }
            while((w = apr_strtok(NULL, “=, \n”, &last)) != NULL) {
                // this is group name or user name
                if(w[0] == ‘@’) {
                    w++;
                if(w) {
                    apr_table_setn(apt, apr_pstrdup(r->pool, w), “0″);
                }
                }
                else
                {
                    // this is user name
                    apr_table_setn(apt, apr_pstrdup(r->pool, w), “1″);
                }
            }
        }
        if(flag == 2) {
            if(dir[0] == ‘\0′) continue;
            w = apr_strtok(l, “= \n”, &last);
            if(w)
            {
                arec = (struct access_rec *)apr_pcalloc(r->pool, sizeof(struct access_rec));
                arec->access = 0;
                if(w[0] == ‘@’) {
                    w++;
                    if(w) {
                        arec->name = apr_pstrdup(r->pool, w);
                        arec->type = 0;
                    }
                    else continue;
                }
                else if(w[0] == ‘*’)
                {
                    arec->name = apr_pstrdup(r->pool, “*”);
                    // this is all
                    arec->type = 2;
                }
                else
                {
                    arec->name = apr_pstrdup(r->pool, w);
                    // this is user name
                    arec->type = 1;
                }
            }
            else continue;
            w = apr_strtok(NULL, “= \n”, &last);
            if(!w)
            {
                arec->access = 0;
            }
            else
            {
                arec->access = 1;
            }
            arecp = (struct access_rec *)apr_hash_get(accessMap, (const void *)dir, APR_HASH_KEY_STRING);
            if(arecp == NULL) {
                arec->next = NULL;
                apr_hash_set(accessMap, (const void *)apr_pstrdup(r->pool, dir),
                             APR_HASH_KEY_STRING, (const void *)arec);
            }
            else
            {
                while(arecp->next != NULL) arecp = arecp->next;
                arecp->next = arec;
            }
        }
    }
    ap_cfg_closefile(f);
    return 1;
}
static void *create_authSVN_dir_config(apr_pool_t *p, char *d)
{
    authSVN_rec *conf = (authSVN_rec *)apr_pcalloc(p, sizeof(*conf));
    if(conf == NULL) return NULL;
    conf->enabled     = DISABLED;
    conf->debug       = DISABLED;
    conf->dir         = d;
    conf->prefixPath  = NULL;
    conf->stopPattern = NULL;
    conf->accessFile  = NULL;
    return conf;
}
static char hex2int(char c)
{
if( c>=’0′ && c<=&apos;9&apos; ) return (c - &apos;0&apos;);
return (c - &apos;A&apos; + 10);
}
static void url_decode(char *url)
{
char *p  = url;
char *p1 = NULL;
while(*p)
{
  if(*p == &apos;+&apos;) *p = &apos; &apos;;
  if(*p==&apos;%&apos; && *(p+1) && *(p+2))
  {
   *p = hex2int(toupper(*(p+1))) * 16 + hex2int(toupper(*(p+ 2)));
   strcpy(p + 1, p + 3);
  }
  if(*p==&apos;\\&apos; && *(p+1) && *(p+2) && *(p+3))
       {
              p1 = p + 1;
              if(*p1 && *p1==&apos;x&apos;)
              {
                   *p = hex2int(toupper(*(p+2))) * 16 + hex2int(toupper(*(p+3)));
                   strcpy(p+1, p+4);
              }
        }
  p++;
}
return;
}


多多关注:http://blog.sina.com.cn/yantaolucky

作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/4341/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2011-6-5 09:35
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]