歡迎您光臨本站 註冊首頁

Apache模塊開發/用C語言擴展apache(1:簡述)

←手機掃碼閱讀     火星人 @ 2014-03-04 , reply:0

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_開頭的結構,這個是什麼呢?下節介紹一下。
《解決方案》

(2:APR編程介紹) Apache模塊開發/用C語言擴展apache

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/apr/apr-tutorial/html/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_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_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);
《解決方案》

(3:一個非常簡單的apache module)Apache模塊開發/用C語言擴展apache

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進去了:
     # 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/
    提供了非常不錯的文檔,可以參考一下。
《解決方案》

(4:一個生產環境使用的apache module– viewvc許可權控制)

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

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 != start)
      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, dir;
    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 == 『#』) || (!l)) {
            continue;
        }
      
        if(start_with(l, 「」)) {
            flag = 1;
            continue;
        }
      
        if(l == 『[』) {
            flag = 2;
            w = apr_strtok(l, 「[]:\n」, &last);
           
            if(w && w == 『/』) {
                // the root directory
                snprintf(dir, sizeof(dir), 「%s」, conf->prefixPath);
                dir = 『\0′;
            }
            else if(w && w != 『/』)
            {
                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 == 『/』) dir = 『\0′;
                }
                else
                    dir = 『\0′;
            }
            else
            {
                dir = 『\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 == 『@』) {
                    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′) 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 == 『@』) {
                    w++;
                    if(w) {
                        arec->name = apr_pstrdup(r->pool, w);
                        arec->type = 0;
                    }
                    else continue;
                }
                else if(w == 『*』)
                {
                    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.key == NULL || elts.val == NULL) continue;
                  
        if(elts.val == 『1′ && strcmp(elts.key, user) == 0)
        {
            return 1;
        }
      
        if(elts.val == 『0′)
        {
            if(find_user_in_group(user, elts.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) return 0;
   
    short access = find_access(user, url, ugMap, accessMap);
    if(access < 2) return access;
   
    if(url == '/' && url == '\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;
   const size_t nmatch = 1;
   int res = 0;
short r = 0;
   char ebuf;
   
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.key == NULL || elts.val == NULL) continue;
                    if(elts.val == 『0′)
                    {
                        ap_rprintf(r, 「@」);
                    }
                    ap_rprintf(r, 「%s 「, elts.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 != '/' || name != '/')
        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.... ,安全性不外乎邏輯安全和編碼安全,再說自己編寫的起碼是一個黑箱,開源的反而容易被利用已知漏洞

[火星人 ] Apache模塊開發/用C語言擴展apache(1:簡述)已經有700次圍觀

http://coctec.com/docs/service/show-post-21696.html