2009年5月31日 星期日

Request Processing in Apache 2.0請求的處理過程

Apache中HTTP Request的程序主要是request.c中的函數來處理,這個函數是ap_process_request_internal,所有的請求都由它處理,包含子請求(subrequest)和轉向(redirect)。

其程式碼如下所示:


/* This is the master logic for processing requests. Do NOT duplicate
* this logic elsewhere, or the security model will be broken by future
* API changes. Each phase must be individually optimized to pick up
* redundant/duplicate calls by subrequests, and redirects.
*/
AP_DECLARE(int) ap_process_request_internal(request_rec *r)
{
int file_req = (r->main && r->filename);
int access_status;

/* Ignore embedded %2F's in path for proxy requests */
if (!r->proxyreq && r->parsed_uri.path) {
core_dir_config *d;
d = ap_get_module_config(r->per_dir_config, &core_module);
if (d->allow_encoded_slashes) {
access_status = ap_unescape_url_keep2f(r->parsed_uri.path);
}
else {
access_status = ap_unescape_url(r->parsed_uri.path);
}
if (access_status) {
if (access_status == HTTP_NOT_FOUND) {
if (! d->allow_encoded_slashes) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
"found %%2f (encoded '/') in URI "
"(decoded='%s'), returning 404",
r->parsed_uri.path);
}
}
return access_status;
}
}

ap_getparents(r->uri); /* OK --- shrinking transformations... */

/* All file subrequests are a huge pain... they cannot bubble through the
* next several steps. Only file subrequests are allowed an empty uri,
* otherwise let translate_name kill the request.
*/
if (!file_req) {
if ((access_status = ap_location_walk(r))) {
return access_status;
}

if ((access_status = ap_run_translate_name(r))) {
return decl_die(access_status, "translate", r);
}
}

/* Reset to the server default config prior to running map_to_storage
*/
r->per_dir_config = r->server->lookup_defaults;

if ((access_status = ap_run_map_to_storage(r))) {
/* This request wasn't in storage (e.g. TRACE) */
return access_status;
}

/* Rerun the location walk, which overrides any map_to_storage config.
*/
if ((access_status = ap_location_walk(r))) {
return access_status;
}

/* Only on the main request! */
if (r->main == NULL) {
if ((access_status = ap_run_header_parser(r))) {
return access_status;
}
}

/* Skip authn/authz if the parent or prior request passed the authn/authz,
* and that configuration didn't change (this requires optimized _walk()
* functions in map_to_storage that use the same merge results given
* identical input.) If the config changes, we must re-auth.
*/
if (r->main && (r->main->per_dir_config == r->per_dir_config)) {
r->user = r->main->user;
r->ap_auth_type = r->main->ap_auth_type;
}
else if (r->prev && (r->prev->per_dir_config == r->per_dir_config)) {
r->user = r->prev->user;
r->ap_auth_type = r->prev->ap_auth_type;
}
else {
switch (ap_satisfies(r)) {
case SATISFY_ALL:
case SATISFY_NOSPEC:
if ((access_status = ap_run_access_checker(r)) != 0) {
return decl_die(access_status, "check access", r);
}

if (ap_some_auth_required(r)) {
if (((access_status = ap_run_check_user_id(r)) != 0)
|| !ap_auth_type(r)) {
return decl_die(access_status, ap_auth_type(r)
? "check user. No user file?"
: "perform authentication. AuthType not set!",
r);
}

if (((access_status = ap_run_auth_checker(r)) != 0)
|| !ap_auth_type(r)) {
return decl_die(access_status, ap_auth_type(r)
? "check access. No groups file?"
: "perform authentication. AuthType not set!",
r);
}
}
break;

case SATISFY_ANY:
if (((access_status = ap_run_access_checker(r)) != 0)) {
if (!ap_some_auth_required(r)) {
return decl_die(access_status, "check access", r);
}

if (((access_status = ap_run_check_user_id(r)) != 0)
|| !ap_auth_type(r)) {
return decl_die(access_status, ap_auth_type(r)
? "check user. No user file?"
: "perform authentication. AuthType not set!",
r);
}

if (((access_status = ap_run_auth_checker(r)) != 0)
|| !ap_auth_type(r)) {
return decl_die(access_status, ap_auth_type(r)
? "check access. No groups file?"
: "perform authentication. AuthType not set!",
r);
}
}
break;
}
}
/* XXX Must make certain the ap_run_type_checker short circuits mime
* in mod-proxy for r->proxyreq && r->parsed_uri.scheme
* && !strcmp(r->parsed_uri.scheme, "http")
*/
if ((access_status = ap_run_type_checker(r)) != 0) {
return decl_die(access_status, "find types", r);
}

if ((access_status = ap_run_fixups(r)) != 0) {
return access_status;
}

return OK;
}

2009年5月25日 星期一

Apache Portable Runtime可移植執行環境

Apache HTTP Server運作時需要依賴另一個程式庫,也就是APR,全名為「Apache Portable Runtime可移植執行環境」,這其實是Apache Software Foundation(ASF)底下另一個專案計畫,請參考http://apr.apache.org/

APR中有一項重要的程式庫模組,這個模組是APR Pools(apr_pools.h),這是APR和Apchee中管理資源的重要模組!其資料型態是apr_poot_t。在APR當中則是Memory Pool Functions這個模組裡面,原始碼是memory\apr_pools.c,他主要功能其實也就是記憶體管理!

其他定義如下(ver 1.3)
Defines
  • #define APR_POOL_DECLARE_ACCESSOR(type)
  • #define APR_POOL_IMPLEMENT_ACCESSOR(type)
  • #define APR_POOL_DEBUG 0
  • #define APR_POOL__FILE_LINE__ __FILE__ ":" APR_STRINGIFY(__LINE__)

Typedefs
  • typedef struct apr_pool_t apr_pool_t
  • typedef int(* apr_abortfunc_t )(int retcode)

Functions
  • apr_status_t apr_pool_initialize (void)
  • void apr_pool_terminate (void)
  • apr_status_t apr_pool_create_ex (apr_pool_t **newpool, apr_pool_t *parent, apr_abortfunc_t abort_fn, apr_allocator_t *allocator)
  • apr_status_t apr_pool_create_core_ex (apr_pool_t **newpool, apr_abortfunc_t abort_fn, apr_allocator_t *allocator)
  • apr_status_t apr_pool_create_unmanaged_ex (apr_pool_t **newpool, apr_abortfunc_t abort_fn, apr_allocator_t *allocator)
  • apr_status_t apr_pool_create_ex_debug (apr_pool_t **newpool, apr_pool_t *parent, apr_abortfunc_t abort_fn, apr_allocator_t *allocator, const char *file_line)
  • apr_status_t apr_pool_create_core_ex_debug (apr_pool_t **newpool, apr_abortfunc_t abort_fn, apr_allocator_t *allocator, const char *file_line)
  • apr_status_t apr_pool_create_unmanaged_ex_debug (apr_pool_t **newpool, apr_abortfunc_t abort_fn, apr_allocator_t *allocator, const char *file_line)
  • apr_status_t apr_pool_create (apr_pool_t **newpool, apr_pool_t *parent)
  • apr_status_t apr_pool_create_core (apr_pool_t **newpool)
  • apr_status_t apr_pool_create_unmanaged (apr_pool_t **newpool)
  • apr_allocator_t * apr_pool_allocator_get (apr_pool_t *pool)
  • void apr_pool_clear (apr_pool_t *p)
  • void apr_pool_clear_debug (apr_pool_t *p, const char *file_line)
  • void apr_pool_destroy (apr_pool_t *p)
  • void apr_pool_destroy_debug (apr_pool_t *p, const char *file_line)
  • void * apr_palloc (apr_pool_t *p, apr_size_t size)
  • void * apr_palloc_debug (apr_pool_t *p, apr_size_t size, const char *file_line)
  • void * apr_pcalloc (apr_pool_t *p, apr_size_t size)
  • void * apr_pcalloc_debug (apr_pool_t *p, apr_size_t size, const char *file_line)
  • void apr_pool_abort_set (apr_abortfunc_t abortfunc, apr_pool_t *pool)
  • apr_abortfunc_t apr_pool_abort_get (apr_pool_t *pool)
  • apr_pool_t * apr_pool_parent_get (apr_pool_t *pool)
  • int apr_pool_is_ancestor (apr_pool_t *a, apr_pool_t *b)
  • void apr_pool_tag (apr_pool_t *pool, const char *tag)
  • apr_status_t apr_pool_userdata_set (const void *data, const char *key, apr_status_t(*cleanup)(void *), apr_pool_t *pool)
  • apr_status_t apr_pool_userdata_setn (const void *data, const char *key, apr_status_t(*cleanup)(void *), apr_pool_t *pool)
  • apr_status_t apr_pool_userdata_get (void **data, const char *key, apr_pool_t *pool)

2009年5月24日 星期日

Request in Apache請求處理的流程

我們必須先知道一件事,使用者的瀏覽器(browser)向Apache伺服器請求資料時,必須先建立TPC連線,依照HTTP通訊協定的規範向Apache伺服器請求資料。也就是說,Aapche處理請求的流程可以分為:建立TPC連線之前、建立TPC連線之後(開始用HTTP),這兩個部分。

請求處理的流程
  1. 接收請求(Accept Request)
  2. 中繼資料處理(Metadata Processing),request會傳遞很多個掛勾(hook),呼叫所屬階段的程序(function)
  3. 產生回應內容(Content Generator)
  4. 記錄(Logging)
正常來說,大致上是依據這個流程,如果是錯誤(error)或是產生新請求,則有可能使得request導向其他程序,另外,每個程序也可以讓其他程序呼叫,簡言之,有點複雜!(應該要畫圖解釋...)

處理的流程當中,Apache提供掛勾(hook)這項功能,可讓我們開發應用模組(module)的時候,利用回呼(callback)的方式值型模組中的功能。掛勾的總類依照請求的先後順序為:
  1. ap_hook_post_read_request
  2. ap_hook_translate_name
  3. ap_hook_map_to_storage
  4. ap_hook_header_parser
  5. ap_hook_access_checker
  6. ap_hook_check_user_id
  7. ap_hook_auth_checker
  8. ap_hook_type_checker
  9. ap_hook_fixups
  10. ap_hook_handler
  11. ap_hook_log_transaction
最後,Apache預設處理請求的程式是掛在ap_hook_handler之中,其函數原形為static int default_handler(request_rec *r),位於server\core.c檔案當中。

2009年5月23日 星期六

Apache Core Objects主要物件

Apache程式碼定義了一些結構可以使用,其中有四個結構相當重要,會在開發應用程式模組中使用到,主要是定義在httpd.h這個標頭檔中,其結構名稱有:
  • request_rec每個請求的相關資訊
  • server_rec每個主機的相關資訊
  • conn_rec每次連線的相關資訊
  • process_rec每個程序的相關資訊
詳細程式碼如下:

struct request_rec {
    /** The pool associated with the request */
    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.
 */
};

struct server_rec {
    /** The process this server is running in */這個伺服器正在執行的行程,也許是虛擬伺服器
    process_rec *process;
    /** The next server in the list */位於清單中,下一個伺服器組態
    server_rec *next;

    /** The name of the server */這個伺服器的名子
    const char *defn_name;
    /** The line of the config file that the server was defined on */組態檔httpd.conf中,定義伺服器的行號
    unsigned defn_line_number;

    /* Contact information */

    /** The admin's contact information */管理員的聯絡資訊(電子信箱)
    char *server_admin;
    /** The server hostname */這個伺服器的主機名稱
    char *server_hostname;
    /** for redirects, etc. */用於重新導向,埠號
    apr_port_t port;

    /* Log files --- note that transfer log is now in the modules... */

    /** The name of the error log */錯誤記錄檔的名稱
    char *error_fname;
    /** A file descriptor that references the error log */指標,指向錯誤記錄檔
    apr_file_t *error_log;
    /** The log level for this server */記錄檔記錄的層次
    int loglevel;

    /* Module-specific configuration for server, and defaults... */

    /** true if this is the virtual server */是否是虛擬伺服器
    int is_virtual;
    /** Config vector containing pointers to modules' per-server config 
     *  structures. */指向伺服器組態的向量
    struct ap_conf_vector_t *module_config; 
    /** MIME type info, etc., before we start checking per-directory info */MIME型態資訊
    struct ap_conf_vector_t *lookup_defaults;

    /* Transaction handling */

    /** I haven't got a clue */還不知道這是什麼!
    server_addr_rec *addrs;
    /** Timeout, as an apr interval, before we give up */逾時時間
    apr_interval_time_t timeout;
    /** The apr interval we will wait for another request */持續連線的逾時時間
    apr_interval_time_t keep_alive_timeout;
    /** Maximum requests per connection */每個連線的最大請求數目
    int keep_alive_max;
    /** Use persistent connections? */是否使用http的持續連線
    int keep_alive;

    /** Pathname for ServerPath */伺服器路徑的名稱
    const char *path;
    /** Length of path */路徑長度
    int pathlen;

    /** Normal names for ServerAlias servers */別名伺服器的正常名稱
    apr_array_header_t *names;
    /** Wildcarded names for ServerAlias servers */別名伺服器的萬用名稱
    apr_array_header_t *wild_names;

    /** limit on size of the HTTP request line    */請求的行數限制
    int limit_req_line;
    /** limit on size of any request header field */請求標頭欄位大小的限制
    int limit_req_fieldsize;
    /** limit on number of request header fields  */請求標頭欄位數目的限制
    int limit_req_fields; 

    /** The server request scheme for redirect responses */重新導向的請求方法
    const char *server_scheme;
};

struct conn_rec {
    /** Pool associated with this connection */
    apr_pool_t *pool;
    /** Physical vhost this conn came in on */
    server_rec *base_server;
    /** used by http_vhost.c */
    void *vhost_lookup_data;

    /* Information about the connection itself */
    /** local address */
    apr_sockaddr_t *local_addr;
    /** remote address */
    apr_sockaddr_t *remote_addr;

    /** Client's IP address */
    char *remote_ip;
    /** Client's DNS name, if known.  NULL if DNS hasn't been checked,
     *  "" if it has and no address was found.  N.B. Only access this though
     * get_remote_host() */
    char *remote_host;
    /** Only ever set if doing rfc1413 lookups.  N.B. Only access this through
     *  get_remote_logname() */
    char *remote_logname;

    /** Are we still talking? */
    unsigned aborted:1;

    /** Are we going to keep the connection alive for another request?
     * @see ap_conn_keepalive_e */
    ap_conn_keepalive_e keepalive;

    /** have we done double-reverse DNS? -1 yes/failure, 0 not yet, 
     *  1 yes/success */
    signed int double_reverse:2;

    /** How many times have we used it? */
    int keepalives;
    /** server IP address */
    char *local_ip;
    /** used for ap_get_server_name when UseCanonicalName is set to DNS
     *  (ignores setting of HostnameLookups) */
    char *local_host;

    /** ID of this connection; unique at any point in time */
    long id; 
    /** Config vector containing pointers to connections per-server
     *  config structures. */
    struct ap_conf_vector_t *conn_config;
    /** Notes on *this* connection: send note from one module to
     *  another. must remain valid for all requests on this conn */
    apr_table_t *notes;
    /** A list of input filters to be used for this connection */
    struct ap_filter_t *input_filters;
    /** A list of output filters to be used for this connection */
    struct ap_filter_t *output_filters;
    /** handle to scoreboard information for this connection */
    void *sbh;
    /** The bucket allocator to use for all bucket/brigade creations */
    struct apr_bucket_alloc_t *bucket_alloc;
    /** The current state of this connection */
    conn_state_t *cs;
    /** Is there data pending in the input filters? */ 
    int data_in_input_filters;
    
    /** Are there any filters that clogg/buffer the input stream, breaking
     *  the event mpm.
     */
    int clogging_input_filters;
};

struct process_rec {
    /** Global pool. Cleared upon normal exit */
    apr_pool_t *pool;
    /** Configuration pool. Cleared upon restart */
    apr_pool_t *pconf;
    /** Number of command line arguments passed to the program */
    int argc;
    /** The command line arguments */
    const char * const *argv;
    /** The program name used to execute the program */
    const char *short_name;
};

Apache Multi-Processing Modules多行程模組

MPM是Apache中的一個模組,全名為Multi-Processing Module多行程模組,這個模組是介於APR和底層的作業系統(Operation System, OS)的一個模組,其目的是用來最佳化,使得Apache在各式的OS上能有傑出的效能表現。

MPM本身是系統層級(system-level)的模組,意思就是說,開發上和其他模組會不一樣,哪裡不一樣?目前還在研究當中。不過,我們大多數的應用只會按照一般模組的樣子來開發,絕大多數都是這種開發方式(即非系統層級)。

早期Apache發展在UNIX之類的作業系統上,這類系統多為多處理器的伺服器,而作業系統會依照請求(request)分岔(fork)產生其他行程(process)來處理,一般來說,Apache啟動的時候,會產生一個集散區(pool,也有稱為"池")來管理這些行程。之後2.0版本,為了能夠跨平台(cross-platform)的高效率、最佳化運作,所以才設計出MPM這個模組。

實務應用上來說,只有UNIX之類的作業系統有較多的設定可供選擇使用。我目前是以windows開發,真的選項就只有兩個可以設定:MaxRequestsPerChildThreadsPerChild兩個指令。其他指令請參考http://httpd.apache.org/docs/2.2/mod/mpm_common.html

在MPM這個部分當中,因為跟底層OS有密切關係,其實MPM有好幾個類別,有些是給UNIX用的,有些是給Windows(WIN32)用的,有些則給其他OS用的,所以再看這些指令的時候,必須注意它所依賴的模組,否則社定會失敗。另一個問題是,我們可以選擇使用哪個MPM嗎?應該是不行,除非你自己重新complie一次,MPM的設定在編譯Apache時就已經決定了!

以UNIX這群的MPM來說,有Prefork MPM、Worker MPM以及正在試驗的Event MPM。
  • Prefork MPM是非執行緒的架構,和以往1版的類似。
  • Worker MPM是多執行緒的架構,其執行緒和每個請求連線有關。
  • Event MPM是正在試驗當中,將執行緒與請求連線的關係解耦合,可以避免請求連線超出執行緒的問題。
結論:開發Apache的模組、應用程式來說,不需要理會MPM,請忽略MPM這個模組!

我目前以windows開發,所以主要是專注於mpm_winnt,其指令Directives有
  • CoreDumpDirectory
  • Listen
  • ListenBacklog
  • MaxMemFree
  • MaxRequestsPerChild
  • PidFile
  • ReceiveBufferSize
  • ScoreBoardFile
  • SendBufferSize
  • ThreadLimit
  • ThreadsPerChild
  • ThreadStackSize
  • Win32DisableAcceptEx

2009年5月22日 星期五

Deconstruction of Apache Code解構阿帕契程式碼

太久,真的是一個月沒有更新部落格。忙著,也跟著茫了。

目前正在使用Apache Http Server的程式撰寫。我想之後會寫有關於Apache的程式碼內容,希望有這個能力和空閒時間。(忙著活下去,還有時間嗎!好問題~)

先講參考書籍,討論Apache的書籍很少(手指頭的數目之內),中文可以參考下面這本:
Ben Laurie & Peter Laurie原著,郭文生編譯,Apache技術手冊(第三版),台北:歐萊禮,2003。

怎麼開始呢?Deconstruction,解構。應該不稱之為解構主義,一方面我現在不了解何謂解構主義,只是想將Apache的Code猜(拆)解開來看看,這套軟體的運作方式?我們有沒有能力做出這樣的軟體呢?需要哪些能力和人力?如何設計更是大問題!(我的疑問)

Apache的進入點是main.c,在httpd這個專案(Project)裡面,程式碼架構如下:

用到的標頭檔,Apache底層是Apache Portability Runtime(APR),因此會引入許多apr_的header file,而基於APR建置的程式庫則有APR-util,簡稱APU,以及實現iconv()的APR-iconv。另外,http_是關於http protocol的一些處理,也需要因引入。

#include "apr.h" //平台定義
#include "apr_strings.h" //字串處理程式庫
#include "apr_getopt.h" //命令參數
#include "apr_general.h" //混雜的程式庫,未歸類的
#include "apr_lib.h" //通用程式庫
#include "apr_md5.h" //md5的程式庫,屬於APU
#include "apr_time.h" //時間程式庫
#include "apr_version.h" //APR的版本介面
#include "apu_version.h" //APU的版本介面

#define APR_WANT_STDIO
#define APR_WANT_STRFUNC
#include "apr_want.h" //APR對於標準標頭檔支援

#define CORE_PRIVATE
#include "ap_config.h" //定義掛勾hook的標頭檔
#include "httpd.h" //主要基本的定義
#include "http_main.h" //main程式的定義
#include "http_log.h" //記錄檔的定義
#include "http_config.h" //一些設定的定義
#include "http_core.h" //核心模組的定義
#include "http_vhost.h" //虛擬主機的定義
#include "apr_uri.h" //uri的程式庫,屬於APU
#include "util_ebcdic.h" //定義Extended Binary Coded Decimal Interchange Code(EBCDIC)的轉換功能
#include "ap_mpm.h" //定義Multi-Processing Module(MPM),多處理模組
#include "mpm_common.h" //MPM用的程式庫

定義到的函數(function):
static void show_mpm_settings(void)
static void show_compile_settings(void)
static void destroy_and_exit_process(process_rec *process, int process_exit_value)
static process_rec *init_process(int *argc, const char * const * *argv)
static void usage(process_rec *process)

主程式碼:


int main(int argc, const char * const argv[])
{
char c;
int configtestonly = 0;
const char *confname = SERVER_CONFIG_FILE;
const char *def_server_root = HTTPD_ROOT;
const char *temp_error_log = NULL;
const char *error;
process_rec *process;
server_rec *server_conf;
apr_pool_t *pglobal;
apr_pool_t *pconf;
apr_pool_t *plog; /* Pool of log streams, reset _after_ each read of conf */
apr_pool_t *ptemp; /* Pool for temporary config stuff, reset often */
apr_pool_t *pcommands; /* Pool for -D, -C and -c switches */
apr_getopt_t *opt;
apr_status_t rv;
module **mod;
const char *optarg;
APR_OPTIONAL_FN_TYPE(ap_signal_server) *signal_server;

AP_MONCONTROL(0); /* turn off profiling of startup */

process = init_process(&argc, &argv);
pglobal = process->pool;
pconf = process->pconf;
ap_server_argv0 = process->short_name;

#if APR_CHARSET_EBCDIC
if (ap_init_ebcdic(pglobal) != APR_SUCCESS) {
destroy_and_exit_process(process, 1);
}
#endif

apr_pool_create(&pcommands, pglobal);
apr_pool_tag(pcommands, "pcommands");
ap_server_pre_read_config = apr_array_make(pcommands, 1, sizeof(char *));
ap_server_post_read_config = apr_array_make(pcommands, 1, sizeof(char *));
ap_server_config_defines = apr_array_make(pcommands, 1, sizeof(char *));

error = ap_setup_prelinked_modules(process);
if (error) {
ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_EMERG, 0, NULL, "%s: %s",
ap_server_argv0, error);
destroy_and_exit_process(process, 1);
}

ap_run_rewrite_args(process);

/* Maintain AP_SERVER_BASEARGS list in http_main.h to allow the MPM
* to safely pass on our args from its rewrite_args() handler.
*/
apr_getopt_init(&opt, pcommands, process->argc, process->argv);

while ((rv = apr_getopt(opt, AP_SERVER_BASEARGS, &c, &optarg))
== APR_SUCCESS) {
char **new;

switch (c) {
case 'c':
new = (char **)apr_array_push(ap_server_post_read_config);
*new = apr_pstrdup(pcommands, optarg);
break;

case 'C':
new = (char **)apr_array_push(ap_server_pre_read_config);
*new = apr_pstrdup(pcommands, optarg);
break;

case 'd':
def_server_root = optarg;
break;

case 'D':
new = (char **)apr_array_push(ap_server_config_defines);
*new = apr_pstrdup(pcommands, optarg);
/* Setting -D DUMP_VHOSTS is equivalent to setting -S */
if (strcmp(optarg, "DUMP_VHOSTS") == 0)
configtestonly = 1;
/* Setting -D DUMP_MODULES is equivalent to setting -M */
if (strcmp(optarg, "DUMP_MODULES") == 0)
configtestonly = 1;
break;

case 'e':
if (strcasecmp(optarg, "emerg") == 0) {
ap_default_loglevel = APLOG_EMERG;
}
else if (strcasecmp(optarg, "alert") == 0) {
ap_default_loglevel = APLOG_ALERT;
}
else if (strcasecmp(optarg, "crit") == 0) {
ap_default_loglevel = APLOG_CRIT;
}
else if (strncasecmp(optarg, "err", 3) == 0) {
ap_default_loglevel = APLOG_ERR;
}
else if (strncasecmp(optarg, "warn", 4) == 0) {
ap_default_loglevel = APLOG_WARNING;
}
else if (strcasecmp(optarg, "notice") == 0) {
ap_default_loglevel = APLOG_NOTICE;
}
else if (strcasecmp(optarg, "info") == 0) {
ap_default_loglevel = APLOG_INFO;
}
else if (strcasecmp(optarg, "debug") == 0) {
ap_default_loglevel = APLOG_DEBUG;
}
else {
usage(process);
}
break;

case 'E':
temp_error_log = apr_pstrdup(process->pool, optarg);
break;

case 'X':
new = (char **)apr_array_push(ap_server_config_defines);
*new = "DEBUG";
break;

case 'f':
confname = optarg;
break;

case 'v':
printf("Server version: %s\n", ap_get_server_description());
printf("Server built: %s\n", ap_get_server_built());
destroy_and_exit_process(process, 0);

case 'V':
show_compile_settings();
destroy_and_exit_process(process, 0);

case 'l':
ap_show_modules();
destroy_and_exit_process(process, 0);

case 'L':
ap_show_directives();
destroy_and_exit_process(process, 0);

case 't':
configtestonly = 1;
break;

case 'S':
configtestonly = 1;
new = (char **)apr_array_push(ap_server_config_defines);
*new = "DUMP_VHOSTS";
break;

case 'M':
configtestonly = 1;
new = (char **)apr_array_push(ap_server_config_defines);
*new = "DUMP_MODULES";
break;

case 'h':
case '?':
usage(process);
}
}

/* bad cmdline option? then we die */
if (rv != APR_EOF || opt->ind < opt->argc) {
usage(process);
}

apr_pool_create(&plog, pglobal);
apr_pool_tag(plog, "plog");
apr_pool_create(&ptemp, pconf);
apr_pool_tag(ptemp, "ptemp");

/* Note that we preflight the config file once
* before reading it _again_ in the main loop.
* This allows things, log files configuration
* for example, to settle down.
*/

ap_server_root = def_server_root;
if (temp_error_log) {
ap_replace_stderr_log(process->pool, temp_error_log);
}
server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);
if (!server_conf) {
destroy_and_exit_process(process, 1);
}

if (ap_run_pre_config(pconf, plog, ptemp) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,
NULL, "Pre-configuration failed");
destroy_and_exit_process(process, 1);
}

rv = ap_process_config_tree(server_conf, ap_conftree,
process->pconf, ptemp);
if (rv == OK) {
ap_fixup_virtual_hosts(pconf, server_conf);
ap_fini_vhost_config(pconf, server_conf);
apr_hook_sort_all();

if (configtestonly) {
ap_run_test_config(pconf, server_conf);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "Syntax OK");
destroy_and_exit_process(process, 0);
}
}

signal_server = APR_RETRIEVE_OPTIONAL_FN(ap_signal_server);
if (signal_server) {
int exit_status;

if (signal_server(&exit_status, pconf) != 0) {
destroy_and_exit_process(process, exit_status);
}
}

/* If our config failed, deal with that here. */
if (rv != OK) {
destroy_and_exit_process(process, 1);
}

apr_pool_clear(plog);

if ( ap_run_open_logs(pconf, plog, ptemp, server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
0, NULL, "Unable to open logs");
destroy_and_exit_process(process, 1);
}

if ( ap_run_post_config(pconf, plog, ptemp, server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,
NULL, "Configuration Failed");
destroy_and_exit_process(process, 1);
}

apr_pool_destroy(ptemp);

for (;;) {
apr_hook_deregister_all();
apr_pool_clear(pconf);

for (mod = ap_prelinked_modules; *mod != NULL; mod++) {
ap_register_hooks(*mod, pconf);
}

/* This is a hack until we finish the code so that it only reads
* the config file once and just operates on the tree already in
* memory. rbb
*/
ap_conftree = NULL;
apr_pool_create(&ptemp, pconf);
apr_pool_tag(ptemp, "ptemp");
ap_server_root = def_server_root;
server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);
if (!server_conf) {
destroy_and_exit_process(process, 1);
}

if (ap_run_pre_config(pconf, plog, ptemp) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
0, NULL, "Pre-configuration failed");
destroy_and_exit_process(process, 1);
}

if (ap_process_config_tree(server_conf, ap_conftree, process->pconf,
ptemp) != OK) {
destroy_and_exit_process(process, 1);
}
ap_fixup_virtual_hosts(pconf, server_conf);
ap_fini_vhost_config(pconf, server_conf);
apr_hook_sort_all();
apr_pool_clear(plog);
if (ap_run_open_logs(pconf, plog, ptemp, server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
0, NULL, "Unable to open logs");
destroy_and_exit_process(process, 1);
}

if (ap_run_post_config(pconf, plog, ptemp, server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
0, NULL, "Configuration Failed");
destroy_and_exit_process(process, 1);
}

apr_pool_destroy(ptemp);
apr_pool_lock(pconf, 1);

ap_run_optional_fn_retrieve();

if (ap_mpm_run(pconf, plog, server_conf))
break;

apr_pool_lock(pconf, 0);
}

apr_pool_lock(pconf, 0);
destroy_and_exit_process(process, 0);

return 0; /* Termination 'ok' */
}