Apache模块开发/用C语言扩展apache(1:简述)

Apache模块开发/用C语言扩展apache(1:简述)

from:http://www.loveopensource.com/?p=16

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 {
    /** The pool associated with the request */
    //内存管理池,后面讲apr时会讲到
    apr_pool_t *pool;
    /** The connection to the client */
    conn_rec *connection;
    /** The virtual host for this request */
    server_rec *server;

    /** Pointer to the redirected request if this is an external redirect */
    request_rec *next;
    /** Pointer to the previous request if this is an internal redirect */
    request_rec *prev;

    /** Pointer to the main request if this is a sub-request
     * (see http_request.h) */
    request_rec *main;

    /* Info about the request itself… we begin with stuff that only
     * protocol.c should ever touch…
     */
    /** First line of request */
    char *the_request;
    /** HTTP/0.9, “simple” request (e.g. GET /foo\n w/no headers) */
    int assbackwards;
    /** A proxy request (calculated during post_read_request/translate_name)
     *  possible values PROXYREQ_NONE, PROXYREQ_PROXY, PROXYREQ_REVERSE,
     *                  PROXYREQ_RESPONSE
     */
    int proxyreq;
    /** HEAD request, as opposed to GET */
    int header_only;
    /** Protocol string, as given to us, or HTTP/0.9 */
    char *protocol;
    /** Protocol version number of protocol; 1.1 = 1001 */
    int proto_num;
    /** Host, as set by full URI or Host: */
    const char *hostname;

    /** Time when the request started */
    apr_time_t request_time;

    /** Status line, if set by script */
    const char *status_line;
    /** Status line */
    int status;

    /* Request method, two ways; also, protocol, etc..  Outside of protocol.c,
     * look, but don’t touch.
     */

    /** Request method (eg. GET, HEAD, POST, etc.) */
    const char *method;
    /** M_GET, M_POST, etc. */
    int method_number;

    /**
     *  ‘allowed’ is a bitvector of the allowed methods.
     *
     *  A handler must ensure that the request method is one that
     *  it is capable of handling.  Generally modules should DECLINE
     *  any request methods they do not handle.  Prior to aborting the
     *  handler like this the handler should set r->allowed to the list
     *  of methods that it is willing to handle.  This bitvector is used
     *  to construct the “Allow:” header required for OPTIONS requests,
     *  and HTTP_METHOD_NOT_ALLOWED and HTTP_NOT_IMPLEMENTED status codes.
     *
     *  Since the default_handler deals with OPTIONS, all modules can
     *  usually decline to deal with OPTIONS.  TRACE is always allowed,
     *  modules don’t need to set it explicitly.
     *
     *  Since the default_handler will always handle a GET, a
     *  module which does *not* implement GET should probably return
     *  HTTP_METHOD_NOT_ALLOWED.  Unfortunately this means that a Script GET
     *  handler can’t be installed by mod_actions.
     */
    apr_int64_t allowed;
    /** Array of extension methods */
    apr_array_header_t *allowed_xmethods;
    /** List of allowed methods */
    ap_method_list_t *allowed_methods;

    /** byte count in stream is for body */
    apr_off_t sent_bodyct;
    /** body byte count, for easy access */
    apr_off_t bytes_sent;
    /** Last modified time of the requested resource */
    apr_time_t mtime;

    /* HTTP/1.1 connection-level features */

    /** sending chunked transfer-coding */
    int chunked;
    /** The Range: header */
    const char *range;
    /** The “real” content length */
    apr_off_t clength;

    /** Remaining bytes left to read from the request body */
    apr_off_t remaining;
    /** Number of bytes that have been read  from the request body */
    apr_off_t read_length;
    /** Method for reading the request body
     * (eg. REQUEST_CHUNKED_ERROR, REQUEST_NO_BODY,
     *  REQUEST_CHUNKED_DECHUNK, etc…) */
    int read_body;
    /** reading chunked transfer-coding */
    int read_chunked;
    /** is client waiting for a 100 response? */
    unsigned expecting_100;

    /* MIME header environments, in and out.  Also, an array containing
     * environment variables to be passed to subprocesses, so people can
     * write modules to add to that environment.
     *
     * The difference between headers_out and err_headers_out is that the
     * latter are printed even on error, and persist across internal redirects
     * (so the headers printed for ErrorDocument handlers will have them).
     *
     * The ‘notes’ apr_table_t is for notes from one module to another, with no
     * other set purpose in mind…
     */

    /** MIME header environment from the request */
    apr_table_t *headers_in;
    /** MIME header environment for the response */
    apr_table_t *headers_out;
    /** MIME header environment for the response, printed even on errors and
     * persist across internal redirects */
    apr_table_t *err_headers_out;
    /** Array of environment variables to be used for sub processes */
    apr_table_t *subprocess_env;
    /** Notes from one module to another */
    apr_table_t *notes;

    /* content_type, handler, content_encoding, and all content_languages
     * MUST be lowercased strings.  They may be pointers to static strings;
     * they should not be modified in place.
     */
    /** The content-type for the current request */
    const char *content_type; /* Break these out — we dispatch on ‘em */
    /** The handler string that we use to call a handler function */
    const char *handler; /* What we *really* dispatch on */

    /** How to encode the data */
    const char *content_encoding;
    /** Array of strings representing the content languages */
    apr_array_header_t *content_languages;

    /** variant list validator (if negotiated) */
    char *vlist_validator;
   
    /** If an authentication check was made, this gets set to the user name. */
    char *user;
    /** If an authentication check was made, this gets set to the auth type. */
    char *ap_auth_type;

    /** This response can not be cached */
    int no_cache;
    /** There is no local copy of this response */
    int no_local_copy;

    /* What object is being requested (either directly, or via include
     * or content-negotiation mapping).
     */

    /** The URI without any parsing performed */
    char *unparsed_uri;
    /** The path portion of the URI */
    char *uri;
    /** The filename on disk corresponding to this response */
    char *filename;
    /* XXX: What does this mean? Please define “canonicalize” -aaron */
    /** The true filename, we canonicalize r->filename if these don’t match */
    char *canonical_filename;
    /** The PATH_INFO extracted from this request */
    char *path_info;
    /** The QUERY_ARGS extracted from this request */
    char *args;
    /**  finfo.protection (st_mode) set to zero if no such file */
    apr_finfo_t finfo;
    /** A struct containing the components of URI */
    apr_uri_t parsed_uri;

    /**
     * Flag for the handler to accept or reject path_info on
     * the current request.  All modules should respect the
     * AP_REQ_ACCEPT_PATH_INFO and AP_REQ_REJECT_PATH_INFO
     * values, while AP_REQ_DEFAULT_PATH_INFO indicates they
     * may follow existing conventions.  This is set to the
     * user’s preference upon HOOK_VERY_FIRST of the fixups.
     */
    int used_path_info;

    /* Various other config info which may change with .htaccess files
     * These are config vectors, with one void* pointer for each module
     * (the thing pointed to being the module’s business).
     */

    /** Options set in config files, etc. */
    struct ap_conf_vector_t *per_dir_config;
    /** Notes on *this* request */
    struct ap_conf_vector_t *request_config;

    /**
     * A linked list of the .htaccess configuration directives
     * accessed by this request.
     * N.B. always add to the head of the list, _never_ to the end.
     * that way, a sub request’s list can (temporarily) point to a parent’s list
     */
    const struct htaccess_result *htaccess;

    /** A list of output filters to be used for this request */
    struct ap_filter_t *output_filters;
    /** A list of input filters to be used for this request */
    struct ap_filter_t *input_filters;

    /** A list of protocol level output filters to be used for this
     *  request */
    struct ap_filter_t *proto_output_filters;
    /** A list of protocol level input filters to be used for this
     *  request */
    struct ap_filter_t *proto_input_filters;

    /** A flag to determine if the eos bucket has been sent yet */
    int eos_sent;

/* Things placed at the end of the record to avoid breaking binary
* compatibility.  It would be nice to remember to reorder the entire
* record to improve 64bit alignment the next time we need to break
* binary compatibility for some other reason.
*/
};
   可以看到源码中有很多apr_开头的结构,这个是什么呢?下节介绍一下。
from: http://www.loveopensource.com/?p=17

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;
/* create a memory pool. */
apr_pool_create(&mp, NULL);

/* allocate memory chunks from the memory pool */
char *buf1;
buf1 = apr_palloc(mp, MEM_ALLOC_SIZE);
/* sample code about apr_pool_clear() */
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"); /* string literals don't require duplication */
    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的函数:
/**
* Get the elements from a table
* @param t The table
* @return An array containing the contents of the table
*/
APR_DECLARE(const apr_array_header_t *) apr_table_elts(const apr_table_t *t);

/**
* Determine if the table is empty
* @param t The table to check
* @return True if empty, False otherwise
*/
APR_DECLARE(int) apr_is_empty_table(const apr_table_t *t);

/**
* Determine if the array is empty
* @param a The array to check
* @return True if empty, False otherwise
*/
APR_DECLARE(int) apr_is_empty_array(const apr_array_header_t *a);

/**
* Create an array
* @param p The pool to allocate the memory out of
* @param nelts the number of elements in the initial array
* @param elt_size The size of each element in the array.
* @return The new array
*/
APR_DECLARE(apr_array_header_t *) apr_array_make(apr_pool_t *p,
                                                 int nelts, int elt_size);

/**
* Add a new element to an array (as a first-in, last-out stack)
* @param arr The array to add an element to.
* @return Location for the new element in the array.
* @remark If there are no free spots in the array, then this function will
*         allocate new space for the new element.
*/
APR_DECLARE(void *) apr_array_push(apr_array_header_t *arr);

/**
* Remove an element from an array (as a first-in, last-out stack)
* @param arr The array to remove an element from.
* @return Location of the element in the array.
* @remark If there are no elements in the array, NULL is returned.
*/
APR_DECLARE(void *) apr_array_pop(apr_array_header_t *arr);

/**
* Concatenate two arrays together
* @param dst The destination array, and the one to go first in the combined
*            array
* @param src The source array to add to the destination array
*/
APR_DECLARE(void) apr_array_cat(apr_array_header_t *dst,
           const apr_array_header_t *src);

/**
* Copy the entire array
* @param p The pool to allocate the copy of the array out of
* @param arr The array to copy
* @return An exact copy of the array passed in
* @remark The alternate apr_array_copy_hdr copies only the header, and arranges
*         for the elements to be copied if (and only if) the code subsequently
*         does a push or arraycat.
*/
APR_DECLARE(apr_array_header_t *) apr_array_copy(apr_pool_t *p,
                                      const apr_array_header_t *arr);
/**
* Copy the headers of the array, and arrange for the elements to be copied if
* and only if the code subsequently does a push or arraycat.
* @param p The pool to allocate the copy of the array out of
* @param arr The array to copy
* @return An exact copy of the array passed in
* @remark The alternate apr_array_copy copies the *entire* array.
*/
APR_DECLARE(apr_array_header_t *) apr_array_copy_hdr(apr_pool_t *p,
                                      const apr_array_header_t *arr);

/**
* Append one array to the end of another, creating a new array in the process.
* @param p The pool to allocate the new array out of
* @param first The array to put first in the new array.
* @param second The array to put second in the new array.
* @return A new array containing the data from the two arrays passed in.
*/
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);

/**
* Generates a new string from the apr_pool_t containing the concatenated
* sequence of substrings referenced as elements within the array.  The string
* will be empty if all substrings are empty or null, or if there are no
* elements in the array.  If sep is non-NUL, it will be inserted between
* elements as a separator.
* @param p The pool to allocate the string out of
* @param arr The array to generate the string from
* @param sep The separator to use
* @return A string containing all of the data in the array.
*/
APR_DECLARE(char *) apr_array_pstrcat(apr_pool_t *p,
          const apr_array_header_t *arr,
          const char sep);

/**
* Make a new table
* @param p The pool to allocate the pool out of
* @param nelts The number of elements in the initial table.
* @return The new table.
* @warning This table can only store text data
*/
APR_DECLARE(apr_table_t *) apr_table_make(apr_pool_t *p, int nelts);

/**
* Create a new table and copy another table into it
* @param p The pool to allocate the new table out of
* @param t The table to copy
* @return A copy of the table passed in
*/
APR_DECLARE(apr_table_t *) apr_table_copy(apr_pool_t *p,
                                          const apr_table_t *t);

/**
* Delete all of the elements from a table
* @param t The table to clear
*/
APR_DECLARE(void) apr_table_clear(apr_table_t *t);

/**
* Get the value associated with a given key from the table.  After this call,
* The data is still in the table
* @param t The table to search for the key
* @param key The key to search for
* @return The value associated with the key, or NULL if the key does not exist.
*/
APR_DECLARE(const char *) apr_table_get(const apr_table_t *t, const char *key);

/**
* Add a key/value pair to a table, if another element already exists with the
* same key, this will over-write the old data.
* @param t The table to add the data to.
* @param key The key fo use
* @param val The value to add
* @remark When adding data, this function makes a copy of both the key and the
*         value.
*/
APR_DECLARE(void) apr_table_set(apr_table_t *t, const char *key,
                                const char *val);

/**
* Add a key/value pair to a table, if another element already exists with the
* same key, this will over-write the old data.
* @param t The table to add the data to.
* @param key The key to use
* @param val The value to add
* @warning When adding data, this function does not make a copy of the key or
*          the value, so care should be taken to ensure that the values will
*          not change after they have been added..
*/
APR_DECLARE(void) apr_table_setn(apr_table_t *t, const char *key,
                                 const char *val);

/**
* Remove data from the table
* @param t The table to remove data from
* @param key The key of the data being removed
*/
APR_DECLARE(void) apr_table_unset(apr_table_t *t, const char *key);

/**
* Add data to a table by merging the value with data that has already been
* stored
* @param t The table to search for the data
* @param key The key to merge data for
* @param val The data to add
* @remark If the key is not found, then this function acts like apr_table_add
*/
APR_DECLARE(void) apr_table_merge(apr_table_t *t, const char *key,
                                  const char *val);

/**
* Add data to a table by merging the value with data that has already been
* stored
* @param t The table to search for the data
* @param key The key to merge data for
* @param val The data to add
* @remark If the key is not found, then this function acts like apr_table_addn
*/
APR_DECLARE(void) apr_table_mergen(apr_table_t *t, const char *key,
                                   const char *val);

/**
* Add data to a table, regardless of whether there is another element with the
* same key.
* @param t The table to add to
* @param key The key to use
* @param val The value to add.
* @remark When adding data, this function makes a copy of both the key and the
*         value.
*/
APR_DECLARE(void) apr_table_add(apr_table_t *t, const char *key,
                                const char *val);

/**
* Add data to a table, regardless of whether there is another element with the
* same key.
* @param t The table to add to
* @param key The key to use
* @param val The value to add.
* @remark When adding data, this function does not make a copy of the key or the
*         value, so care should be taken to ensure that the values will not
*         change after they have been added..
*/
APR_DECLARE(void) apr_table_addn(apr_table_t *t, const char *key,
                                 const char *val);

/**
* Merge two tables into one new table
* @param p The pool to use for the new table
* @param overlay The first table to put in the new table
* @param base The table to add at the end of the new table
* @return A new table containing all of the data from the two passed in
*/
APR_DECLARE(apr_table_t *) apr_table_overlay(apr_pool_t *p,
                                             const apr_table_t *overlay,
                                             const apr_table_t *base);

/**
* Declaration prototype for the iterator callback function of apr_table_do()
* and apr_table_vdo().
* @param rec The data passed as the first argument to apr_table_[v]do()
* @param key The key from this iteration of the table
* @param value The value from this iteration of the table
* @remark Iteration continues while this callback function returns non-zero.
* To export the callback function for apr_table_[v]do() it must be declared
* in the _NONSTD convention.
*/
typedef int (apr_table_do_callback_fn_t)(void *rec, const char *key,
                                                    const char *value);

/**
* Iterate over a table running the provided function once for every
* element in the table.  If there is data passed in as a vararg, then the
* function is only run on those elements whose key matches something in
* the vararg.  If the vararg is NULL, then every element is run through the
* function.  Iteration continues while the function returns non-zero.
* @param comp The function to run
* @param rec The data to pass as the first argument to the function
* @param t The table to iterate over
* @param ... The vararg.  If this is NULL, then all elements in the table are
*            run through the function, otherwise only those whose key matches
*            are run.
* @return FALSE if one of the comp() iterations returned zero; TRUE if all
*            iterations returned non-zero
* @see apr_table_do_callback_fn_t
*/
APR_DECLARE_NONSTD(int) apr_table_do(apr_table_do_callback_fn_t *comp,
                                     void *rec, const apr_table_t *t, ...);

/**
* Iterate over a table running the provided function once for every
* element in the table.  If there is data passed in as a vararg, then the
* function is only run on those element's whose key matches something in
* the vararg.  If the vararg is NULL, then every element is run through the
* function.  Iteration continues while the function returns non-zero.
* @param comp The function to run
* @param rec The data to pass as the first argument to the function
* @param t The table to iterate over
* @param vp The vararg table.  If this is NULL, then all elements in the
*                table are run through the function, otherwise only those
*                whose key matches are run.
* @return FALSE if one of the comp() iterations returned zero; TRUE if all
*            iterations returned non-zero
* @see apr_table_do_callback_fn_t
*/
APR_DECLARE(int) apr_table_vdo(apr_table_do_callback_fn_t *comp,
                               void *rec, const apr_table_t *t, va_list vp);

/** flag for overlap to use apr_table_setn */
#define APR_OVERLAP_TABLES_SET   (0)
/** flag for overlap to use apr_table_mergen */
#define APR_OVERLAP_TABLES_MERGE (1)
/**
* For each element in table b, either use setn or mergen to add the data
* to table a.  Which method is used is determined by the flags passed in.
* @param a The table to add the data to.
* @param b The table to iterate over, adding its data to table a
* @param flags How to add the table to table a.  One of:
*          APR_OVERLAP_TABLES_SET        Use apr_table_setn
*          APR_OVERLAP_TABLES_MERGE      Use apr_table_mergen
* @remark  This function is highly optimized, and uses less memory and CPU cycles
*          than a function that just loops through table b calling other functions.
*/
/**
*

* Conceptually, apr_table_overlap does this:
*
*  apr_array_header_t *barr = apr_table_elts(b);
*  apr_table_entry_t *belt = (apr_table_entry_t *)barr->elts;
*  int i;
*
*  for (i = 0; i < barr->nelts; ++i) {
*      if (flags & APR_OVERLAP_TABLES_MERGE) {
*          apr_table_mergen(a, belt.key, belt.val);
*      }
*      else {
*          apr_table_setn(a, belt.key, belt.val);
*      }
*  }
*
*  Except that it is more efficient (less space and cpu-time) especially
*  when b has many elements.
*
*  Notice the assumptions on the keys and values in b — they must be
*  in an ancestor of a’s pool.  In practice b and a are usually from
*  the same pool.
*
*/

APR_DECLARE(void) apr_table_overlap(apr_table_t *a, const apr_table_t *b,
                                     unsigned flags);

/**
* Eliminate redundant entries in a table by either overwriting
* or merging duplicates
*
* @param t Table.
* @param flags APR_OVERLAP_TABLES_MERGE to merge, or
*              APR_OVERLAP_TABLES_SET to overwrite
*/
APR_DECLARE(void) apr_table_compress(apr_table_t *t, unsigned flags);

   
    其他apr提供的数据结构还有:hash和list,这里不再详述。
   
    (3) 字符串操作
    写过C的人都知道,处理字符串是非常头痛的问题,搞不好就内存溢出,apr也提供一些字符串函数,都是基于apr_pool_t,
使用时不用担心内存溢出的问题。
     把apr_strings.h贴出来大家一起看看,注释也比较详细,不多说:
/**
* Do a natural order comparison of two strings.
* @param a The first string to compare
* @param b The second string to compare
* @return Either <0, 0, or >0.  If the first string is less than the second
*          this returns <0, if they are equivalent it returns 0, and if the
*          first string is greater than second string it retuns >0.
*/
APR_DECLARE(int) apr_strnatcmp(char const *a, char const *b);

/**
* Do a natural order comparison of two strings ignoring the case of the
* strings.
* @param a The first string to compare
* @param b The second string to compare
* @return Either <0, 0, or >0.  If the first string is less than the second
*         this returns <0, if they are equivalent it returns 0, and if the
*         first string is greater than second string it retuns >0.
*/
APR_DECLARE(int) apr_strnatcasecmp(char const *a, char const *b);

/**
* duplicate a string into memory allocated out of a pool
* @param p The pool to allocate out of
* @param s The string to duplicate
* @return The new string
*/
APR_DECLARE(char *) apr_pstrdup(apr_pool_t *p, const char *s);

/**
* Create a null-terminated string by making a copy of a sequence
* of characters and appending a null byte
* @param p The pool to allocate out of
* @param s The block of characters to duplicate
* @param n The number of characters to duplicate
* @return The new string
* @remark This is a faster alternative to apr_pstrndup, for use
*         when you know that the string being duplicated really
*         has ‘n’ or more characters.  If the string might contain
*         fewer characters, use apr_pstrndup.
*/
APR_DECLARE(char *) apr_pstrmemdup(apr_pool_t *p, const char *s, apr_size_t n);

/**
* Duplicate at most n characters of a string into memory allocated
* out of a pool; the new string will be NUL-terminated
* @param p The pool to allocate out of
* @param s The string to duplicate
* @param n The maximum number of characters to duplicate
* @return The new string
* @remark The amount of memory allocated from the pool is the length
*         of the returned string including the NUL terminator
*/
APR_DECLARE(char *) apr_pstrndup(apr_pool_t *p, const char *s, apr_size_t n);

/**
* Duplicate a block of memory.
*
* @param p The pool to allocate from
* @param m The memory to duplicate
* @param n The number of bytes to duplicate
* @return The new block of memory
*/
APR_DECLARE(void *) apr_pmemdup(apr_pool_t *p, const void *m, apr_size_t n);
from: http://www.loveopensource.com/?p=18

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 structure */
module AP_MODULE_DECLARE_DATA c_module = {
    STANDARD20_MODULE_STUFF,
    NULL,      /* dir config creater */
    NULL,                       /* dir merger — default is to override */
    NULL,      /* server config */
    NULL,                       /* merge server configs */
    NULL,      /* command apr_table_t */
    register_hooks    /* 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 structure */
module AP_MODULE_DECLARE_DATA c_module = {
    STANDARD20_MODULE_STUFF,
    NULL,      /* dir config creater */
    NULL,                       /* dir merger — default is to override */
    NULL,      /* server config */
    NULL,                       /* merge server configs */
    //上面4项都是定义httpd.conf中命令的作用的
    NULL,      /* command apr_table_t */ //定义在httpd.conf中添加的命令,和各命令的处理函数
    register_hooks    /* 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: [url]http://www.loveopensource.com/?p=19[/url]

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”            /* for apr_password_validate */
#include “apr_lib.h”            /* for apr_isspace */
#include “apr_base64.h”         /* for apr_base64_decode et al */
#define APR_WANT_STRFUNC        /* for strcasecmp */
#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

/* data need by our module */
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;
}
/* init per dir */
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;
}

/* hex to int */
static char hex2int(char c)
{
if( c>=’0′ && c<='9' ) return (c - '0');
return (c - 'A' + 10);
}

/* url decode */
static void url_decode(char *url)
{
char *p  = url;
char *p1 = NULL;

while(*p)
{
  if(*p == '+') *p = ' ';
  
  /* %AB */
  if(*p=='%' && *(p+1) && *(p+2))
  {
   *p = hex2int(toupper(*(p+1))) * 16 + hex2int(toupper(*(p+ 2)));
   strcpy(p + 1, p + 3);
  }
  /* \xAB */
  if(*p=='\\' && *(p+1) && *(p+2) && *(p+3))
       {
              p1 = p + 1;
              if(*p1 && *p1=='x')
              {
                   *p = hex2int(toupper(*(p+2))) * 16 + hex2int(toupper(*(p+3)));
                   strcpy(p+1, p+4);
              }
        }
  p++;
}

return;
}

static void parent_path(char *url)
{
    char *p = url + strlen(url) - 1;
   
    while(p != url && *p != '/') { *p = '\0'; p--; }
    if(p != url && *p=='/') *p = '\0';
   
    return;
}

// return
// 0: the user don't belong to this group
// 1: the user belong to this group
static short find_user_in_group(const char* user, const char *group, apr_hash_t* ugMap)
{
    apr_table_t *apt = (apr_table_t *)apr_hash_get(ugMap,
                                                 (const void *)group,
                                                 APR_HASH_KEY_STRING);
    if(apt == NULL) return 0;
    apr_array_header_t *arr;
    apr_table_entry_t  *elts;
    int i;
    arr  = (apr_array_header_t *)apr_table_elts(apt);
    elts = (apr_table_entry_t *)arr->elts;
   
    for(i=0; inelts; i++)
    {
        if(elts[i].key == NULL || elts[i].val == NULL) continue;
                  
        if(elts[i].val[0] == ‘1′ && strcmp(elts[i].key, user) == 0)
        {
            return 1;
        }
      
        if(elts[i].val[0] == ‘0′)
        {
            if(find_user_in_group(user, elts[i].key, ugMap))
                return 1;
        }
    }
   
    return 0;
}

// return
//  0:don’t have access
//  1:have read access
//  2:access not found
static short find_access(const char* user, const char* url,
                         apr_hash_t* ugMap, apr_hash_t* accessMap)
{
    struct access_rec *arec= (struct access_rec *)apr_hash_get(accessMap,
                             (const void *)url, APR_HASH_KEY_STRING);
   
    short access = 2;
   
    while(arec != NULL)
    {
        if(strcmp(arec->name, “*”) == 0)
        {
            // specified access to all users and groups on this url
            access = arec->access;
        }
      
        if(arec->type == 1 && strcmp(arec->name, user) == 0)
        {
            // specified user access on this url
            access = arec->access;
        }
      
        if(arec->type == 0)
        {
            // this is group access
            if(find_user_in_group(user, arec->name, ugMap))
                access = arec->access;
        }
      
        // if this user have access, we return
        if(access == 1) return access;  
      
        arec = arec->next;
    }
   
    return access;
}
static short estimate_access( request_rec *r, const authSVN_rec* conf,
                              char* url, apr_hash_t* ugMap,
                              apr_hash_t* accessMap )
{
    const char* user = r->user;
    // unauthorized
    if(!user || !user[0]) return 0;
   
    short access = find_access(user, url, ugMap, accessMap);
    if(access < 2) return access;
   
    if(url[0] == '/' && url[1] == '\0') return 0;
   
    parent_path(url);
   
    return estimate_access(r, conf, url, ugMap, accessMap);
}

// do regexp matching
static short regexp_match(char *str, char *pattern)
{
    regex_t      reg;
   regmatch_t   pm[1];
   const size_t nmatch = 1;
   int res = 0;
short r = 0;
   char ebuf[MAX_STRING_LEN];
   
res = regcomp(&reg, pattern, REG_EXTENDED);
   
if(res != 0)
   {
     regfree(&reg);
    return 0;
   }
   
   res = regexec(&reg, str, nmatch, pm, 0);
   
   if(res == REG_NOMATCH)
       r = 0;
   else
       r = 1;
   
regfree(&reg);

return r;
}

/* all pages need to pass from this handler */
static int authSVN_handler(request_rec *r)
{
        authSVN_rec *conf = ap_get_module_config(r->per_dir_config,
                                                 &authSVN_module);
        if(!conf || !conf->enabled)
             return DECLINED;
      
        if(conf->prefixPath == NULL || !start_with(r->uri, conf->prefixPath))
            return DECLINED;
      
        if(conf->stopPattern !=NULL && regexp_match(r->uri, conf->stopPattern))
            return DECLINED;
      
        apr_hash_t* ugMap     = apr_hash_make(r->pool);
        apr_hash_t* accessMap = apr_hash_make(r->pool);
      
        if(!parse_access_file(r, conf->accessFile, conf, ugMap, accessMap))
            return 403;
      
        if(conf->debug)
        {
            // run in debug mode
            // print all users/groups and access information
            apr_hash_index_t* hi;
            char *key;
            apr_table_t *val;
            struct access_rec *arec;
            apr_array_header_t *arr;
            apr_table_entry_t  *elts;
            int i;
           
            r->content_type=”text/plain”;
            ap_rprintf(r, “Parsed Users and Groups:\n”);
           
            hi = apr_hash_first(r->pool, ugMap);
            while(hi != NULL)
            {
                apr_hash_this(hi, (void *)&key, NULL, (void *)&val);
               
                ap_rprintf(r, “%s: “, key);
                arr  = (apr_array_header_t *)apr_table_elts(val);
                elts = (apr_table_entry_t *)arr->elts;
                for(i=0; inelts; i++)
                {
                    if(elts[i].key == NULL || elts[i].val == NULL) continue;
                    if(elts[i].val[0] == ‘0′)
                    {
                        ap_rprintf(r, “@”);
                    }
                    ap_rprintf(r, “%s “, elts[i].key);
                }
               
                ap_rprintf(r, “\n”);
               
                hi = apr_hash_next(hi);
            }
           
            ap_rprintf(r, “Parsed Path Access:\n”);
            hi = apr_hash_first(r->pool, accessMap);
            while(hi != NULL)
            {
                apr_hash_this(hi, (void *)&key, NULL, (void *)&arec);
                ap_rprintf(r, “%s:\n”, key);
                while(arec != NULL)
                {
                    if(arec->type == 0)
                        ap_rprintf(r, “group:%s “, arec->name);
                    else if(arec->type == 1)
                        ap_rprintf(r, “user:%s “, arec->name);
                    else
                        ap_rprintf(r, “all “);
                  
                    ap_rprintf(r, “access:%d “, arec->access);
                    ap_rprintf(r, “\n”);
                    arec = arec->next;
                }
               
                ap_rprintf(r, “\n”);
                hi = apr_hash_next(hi);
            }
        }
      
        char *url = apr_pstrdup(r->pool, r->uri);
        // decode the url for some chinese characters
        url_decode(url);
      
        // analyze the access
        if(estimate_access(r, conf, url, ugMap, accessMap))
        {
            if(conf->debug)
            {
                ap_rprintf(r, “%s have access on:%s\n”, r->user, r->uri);
                return OK;
            }
            return DECLINED;
        }
        else
        {
            if(conf->debug)
            {
                ap_rprintf(r, “%s don’t have access on:%s\n”, r->user, r->uri);
                return OK;
            }
        }
      
        return 403;
}

/* enable this module or not */
static const char *set_authSVN_enable(cmd_parms *cmd,
                                      void *mconfig,
                                      int arg)
{
    authSVN_rec *conf = (authSVN_rec *) mconfig;
    conf->enabled = arg;
    return NULL;
}

/* debug this module or not */
static const char *set_authSVN_debug( cmd_parms *cmd,
                                      void *mconfig,
                                      int arg)
{
    authSVN_rec *conf = (authSVN_rec *) mconfig;
    conf->debug = arg;
    return NULL;
}

/*    setting prefix path   */
static const char *set_prefix_path(cmd_parms *cmd,
                                   void *mconfig,
                                   const char *name)
{
    authSVN_rec *conf = (authSVN_rec *) mconfig;
   
    if(strlen(name) <= 0)
        return "AuthSVNPrefixPath can not be null.";
   
    if(name[0] != '/' || name[strlen(name) - 1] != '/')
        return "AuthSVNPrefixPath must start and end with '/'.";
   
    conf->prefixPath = apr_pstrdup(cmd->pool, name);
   
    return NULL;
}

/*    setting stop url pattern   */
static const char *set_stop_pattern(cmd_parms *cmd,
                                    void *mconfig,
                                   const char *name)
{
    authSVN_rec *conf = (authSVN_rec *) mconfig;
   
    if(strlen(name) <= 0)
        return "AuthSVNStopPattern can not be null.";
   
    conf->stopPattern = apr_pstrdup(cmd->pool, name);
   
    return NULL;
}

/* setting SVN access file  */
static const char *set_authSVN_accessFile(cmd_parms *cmd,
                                          void *mconfig,
                                          const char *name)
{
    authSVN_rec *conf = (authSVN_rec *) mconfig;
    ap_configfile_t *f = NULL;
    apr_status_t status;
   
    if(strlen(name) <= 0)
        return "SVNAccessFile can not be null.";
   
    status = ap_pcfg_openfile(&f, cmd->pool, name);

    if (status != APR_SUCCESS) {
        return “Can not open given SVN access file.”;
    }
    ap_cfg_closefile(f);
   
    conf->accessFile = apr_pstrdup(cmd->pool, name);
   
    return NULL;
}
static const command_rec auth_cmds[] =
{
    AP_INIT_FLAG(”EnableAuthSVN”, set_authSVN_enable, NULL, OR_FILEINFO,
     “enable authSVN or not.”),
    AP_INIT_FLAG(”DebugAuthSVN”,  set_authSVN_debug, NULL, OR_FILEINFO,
     “debug authSVN or not.”),
    AP_INIT_TAKE1(”AuthSVNPrefixPath”,   set_prefix_path,   NULL, OR_FILEINFO,
     “set prefix path.”),
    AP_INIT_TAKE1(”AuthSVNStopPattern”,  set_stop_pattern,   NULL, OR_FILEINFO,
     “the url pattern we do not do the access checking.”),
    AP_INIT_TAKE1(”SVNAccessFile”, set_authSVN_accessFile, NULL, OR_FILEINFO,
     “set SVN access file.”),
    { NULL }
};

static void register_hooks(apr_pool_t *p)
{
    ap_hook_handler(authSVN_handler, NULL, NULL, APR_HOOK_FIRST);
}

module AP_MODULE_DECLARE_DATA authSVN_module =
{
    STANDARD20_MODULE_STUFF,
    create_authSVN_dir_config,     /* dir config creater */
    NULL,                          /* dir merger — default is to override */
    NULL,                          /* server config */
    NULL,                          /* merge server config */
    auth_cmds,                     /* command apr_table_t */
    register_hooks                 /* register hooks */
};

安装方法:
install the module:
./bin/apachectl stop
./bin/apxs -c mod_authSVN.c
./bin/apxs -a -i -n authSVN mod_authSVN.la
./bin/apachectl start

add the following to httpd.conf(insite the viewvc location directory):
EnableAuthSVN            on
DebugAuthSVN             off
SVNAccessFile             /usr/local/apache2.2.4/access
AuthSVNPrefixPath     /viewvc/
AuthSVNStopPattern   ^\/viewvc\/\*docroot\*\/
有点不明白,Apache的模块开发老是觉得有点麻烦,好像自己写Web服务器然后加功能还简单一些
自己写一个成熟的WEB SERVER怎么说也要好几年吧?
不明白为什么说apache module programming复杂。
需要几年??还好几年? 要怎么个成熟法?
如果自己写的话,只要能支持静态文件,动态内容通过提供Web应用开发接口实现也就行了,我实现了一个这样的,也没用多长时间。
楼主,你媳妇的照片挺漂亮
我是说成熟的WEB SERVER,能够用于大并发,而且相对安全的,而且支持动态内容的。
简单一点的当然不在话下。
大并发就用多路转发 epoll IOCP.... ,安全性不外乎逻辑安全和编码安全,再说自己编写的起码是一个黑箱,开源的反而容易被利用已知漏洞