Skip to content

http phase handler module

paulran edited this page Apr 21, 2016 · 2 revisions

1. 首先是module,有定义:

ngx_module_t  ngx_http_xxx_module = { ..., &ngx_http_xxx_module_ctx, ... };
ngx_http_module_t  ngx_http_xxx_module_ctx = { ..., ngx_http_xxx_init, ... };

2. handler module 本身提供的接口:

static ngx_int_t ngx_http_xxx_handler(ngx_http_request_t *r);

3. ngx_http_phases:

typedef enum {
    NGX_HTTP_POST_READ_PHASE = 0,

    NGX_HTTP_SERVER_REWRITE_PHASE,	// ngx_http_core_rewrite_phase, ngx_http_rewrite_module and ngx_http_rewrite_handler

    NGX_HTTP_FIND_CONFIG_PHASE,		// ngx_http_core_find_config_phase
    NGX_HTTP_REWRITE_PHASE,         // ngx_http_core_rewrite_phase, ngx_http_rewrite_module and ngx_http_rewrite_handler
    NGX_HTTP_POST_REWRITE_PHASE,	// ngx_http_core_post_rewrite_phase

    NGX_HTTP_PREACCESS_PHASE,		// (ngx_http_limit_conn_module and ngx_http_limit_conn_handler) or (ngx_http_limit_req_module and ngx_http_limit_req_handler)

    NGX_HTTP_ACCESS_PHASE,		    // ngx_http_core_access_phase, (ngx_http_access_module and ngx_http_access_handler) or (ngx_http_auth_basic_module and ngx_http_auth_basic_handler)
    NGX_HTTP_POST_ACCESS_PHASE,		// ngx_http_core_post_access_phase

    NGX_HTTP_TRY_FILES_PHASE,		// ngx_http_core_try_files_phase
    NGX_HTTP_CONTENT_PHASE,		    // ngx_http_core_content_phase, (ngx_http_static_module and ngx_http_static_handler) or (ngx_http_index_module and ngx_http_index_handler) or (ngx_http_autoindex_module and ngx_http_autoindex_handler)

    NGX_HTTP_LOG_PHASE			    // ngx_http_log_module and ngx_http_log_handler
} ngx_http_phases;


/* 注意 ngx_http_handler_pt 和 ngx_http_phase_handler_pt 的区别:前者为 handler,后者为 checker。
 * 注意 ngx_http_phase_handler_t 和 ngx_http_phase_handler_pt 的区别:前者为 struct,后者为函数指针。
 */
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);

typedef struct ngx_http_phase_handler_s  ngx_http_phase_handler_t;

typedef ngx_int_t (*ngx_http_phase_handler_pt)(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph);

struct ngx_http_phase_handler_s {
    ngx_http_phase_handler_pt  checker;
    ngx_http_handler_pt        handler;
    ngx_uint_t                 next;
};


typedef struct {
    ngx_http_phase_handler_t  *handlers;
    ngx_uint_t                 server_rewrite_index;
    ngx_uint_t                 location_rewrite_index;
} ngx_http_phase_engine_t;


/* 每一个 phase 都对应有一个 ngx_http_phase_t,即表示一个 phase 可以有多个 handler。
 */
typedef struct {
    ngx_array_t                handlers; // 存储 ngx_http_handler_pt*
} ngx_http_phase_t;

4. http phase handler module 的 init 函数的设计:

static ngx_int_t ngx_http_xxx_init(ngx_conf_t *cf)
{
    ngx_http_handler_pt        *h;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    /* 将 phase 与 handler 绑定:某个 http phase handler 是固定与一个或多个 phase 绑定,在这个 init 函数中写死。 */
    h = ngx_array_push(&cmcf->phases[NGX_HTTP_XXX_PHASE].handlers);  // ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];
    if (h == NULL) {
        return NGX_ERROR;
    }

    *h = ngx_http_xxx_handler;

    return NGX_OK;
}

5. 对应的 checker 接口:

ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); // default phase
ngx_int_t ngx_http_core_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph);
ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph);
ngx_int_t ngx_http_core_post_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph);
ngx_int_t ngx_http_core_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph);
ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph);
ngx_int_t ngx_http_core_try_files_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph);
ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph);

6. init phase handlers

typedef struct {
    ... ...

    ngx_http_phase_engine_t    phase_engine;

    ... ...

    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];
} ngx_http_core_main_conf_t;

/*  init 调用顺序:先 http phase handler module 的 ngx_http_xxx_init,再调用 ngx_http_init_phase_handlers。
 *  ngx_http_core_main_conf_t *cmcf;
 *  cmcf->phase_engine.handlers, saving ngx_http_phase_handler_t *ph. 
 *  cmcf->phases[i].handlers, ngx_array_t, saving ngx_http_handler_pt *h[], ph->handler = h[j].
 */
static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    ngx_int_t                   j;
    ngx_uint_t                  i, n;
    ngx_uint_t                  find_config_index, use_rewrite, use_access;
    ngx_http_handler_pt        *h;
    ngx_http_phase_handler_t   *ph;
    ngx_http_phase_handler_pt   checker;

    cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
    cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
    find_config_index = 0;
    use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
    use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;

    n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        n += cmcf->phases[i].handlers.nelts;
    }

    ph = ngx_pcalloc(cf->pool,
                     n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
    if (ph == NULL) {
        return NGX_ERROR;
    }

    cmcf->phase_engine.handlers = ph;
    n = 0;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        h = cmcf->phases[i].handlers.elts;

        switch (i) {

        case NGX_HTTP_SERVER_REWRITE_PHASE:
            if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.server_rewrite_index = n;
            }
            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_FIND_CONFIG_PHASE:
            find_config_index = n;

            ph->checker = ngx_http_core_find_config_phase;
            n++;
            ph++;

            continue;

        case NGX_HTTP_REWRITE_PHASE:
            if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.location_rewrite_index = n;
            }
            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_POST_REWRITE_PHASE:
            if (use_rewrite) {
                ph->checker = ngx_http_core_post_rewrite_phase;
                ph->next = find_config_index;
                n++;
                ph++;
            }

            continue;

        case NGX_HTTP_ACCESS_PHASE:
            checker = ngx_http_core_access_phase;
            n++;
            break;

        case NGX_HTTP_POST_ACCESS_PHASE:
            if (use_access) {
                ph->checker = ngx_http_core_post_access_phase;
                ph->next = n;
                ph++;
            }

            continue;

        case NGX_HTTP_TRY_FILES_PHASE:
            if (cmcf->try_files) {
                ph->checker = ngx_http_core_try_files_phase;
                n++;
                ph++;
            }

            continue;

        case NGX_HTTP_CONTENT_PHASE:
            checker = ngx_http_core_content_phase;
            break;

        default:
            checker = ngx_http_core_generic_phase;
        }

        n += cmcf->phases[i].handlers.nelts;

        /* 同一个 phase,cmcf->phases[i].handlers 可能有多个 ngx_http_handler_pt 。*/
        for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) {
            ph->checker = checker;
            ph->handler = h[j];
            ph->next = n;
            ph++;
        }
    }

    return NGX_OK;
}

初始化后的结果示例:

cmcf->phase_engine.handlers = {
	/* ngx_http_phase_handler_pt checker, ngx_http_handler_pt handler, ngx_uint_t next */
	/* 0 */ { ngx_http_core_rewrite_phase, ngx_http_rewrite_handler, 1 },
	/* 1 */ { ngx_http_core_find_config_phase, 0, 0 },
	/* 2 */ { ngx_http_core_rewrite_phase, ngx_http_rewrite_handler, 3 },
	/* 3 */ { ngx_http_core_post_rewrite_phase, 0, 1 },
	/* 4 */ { ngx_http_core_generic_phase, ngx_http_limit_req_handler, 6 },
	/* 5 */ { ngx_http_core_generic_phase, ngx_http_limit_conn_handler, 6 },
	/* 6 */ { ngx_http_core_access_phase, ngx_http_access_handler, 9 },
	/* 7 */ { ngx_http_core_access_phase, ngx_http_auth_basic_handler, 9 },
	/* 8 */ { ngx_http_core_post_access_phase, 0, 9 }
	/* 9 */ { ngx_http_core_content_phase, ngx_http_index_handler, 12 },
	/* 10 */ { ngx_http_core_content_phase, ngx_http_autoindex_handler, 12 },
	/* 11 */ { ngx_http_core_content_phase, ngx_http_static_handler, 12 }
}

cmcf->phase_engine.server_rewrite_index = 0;

cmcf->phase_engine.location_rewrite_index = 2; // 在设置 ngx_http_request_t 的 phase_handler 的起始值时被使用

7. 运行所有的 phases

void ngx_http_core_run_phases(ngx_http_request_t *r)
{
    ngx_int_t                   rc;
    ngx_http_phase_handler_t   *ph;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    ph = cmcf->phase_engine.handlers;

    while (ph[r->phase_handler].checker) {

        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

        if (rc == NGX_OK) {
            return;
        }
    }
}

/*****************************************************************************************/
// notes 1.
r->phase_handler,  ngx_int_t 型变量值为 C 类型数组 cmcf->phase_engine.handlers 的下标索引// notes 2.
checker,  ngx_http_core_xxxx_phase() :
{
	      ph++; r->phase_handler++; return NGX_AGAIN;  => xxx_phase()从小到大一步一步执行or
  	      r->phase_handler = ph->next; return NGX_AGAIN;
  or
  	      return NGX_OK;      
}

// notes 3.
&ph[r->phase_handler],  ngx_http_phase_handler_t *ph为结构体指针

8. phase checker 示例:

ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph)
{
    size_t     root;
    ngx_int_t  rc;
    ngx_str_t  path;

    if (r->content_handler) {
        r->write_event_handler = ngx_http_request_empty_handler;
        ngx_http_finalize_request(r, r->content_handler(r));
        return NGX_OK;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "content phase: %ui", r->phase_handler);

    rc = ph->handler(r); // call the phase handler

    if (rc != NGX_DECLINED) {
        ngx_http_finalize_request(r, rc);
        return NGX_OK;
    }

    /* rc == NGX_DECLINED */

    ph++;

    if (ph->checker) {
        r->phase_handler++;
        return NGX_AGAIN;
    }

    /* no content handler was found */

    if (r->uri.data[r->uri.len - 1] == '/') {

        if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "directory index of \"%s\" is forbidden", path.data);
        }

        ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
        return NGX_OK;
    }

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");

    ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
    return NGX_OK;
}
Clone this wiki locally