nginx将请求的处理过程划分为11个phase(阶段),相当于是对请求处理的一种抽象,便于定制处理过程。这个11个phase,分别是(定义在http/ngx_http_core_module.h):
typedef enum { NGX_HTTP_POST_READ_PHASE = 0, /* 读取请求 */ NGX_HTTP_SERVER_REWRITE_PHASE, /* server级别的rewrite */ NGX_HTTP_FIND_CONFIG_PHASE, /* 根据uri查找location */ NGX_HTTP_REWRITE_PHASE, /* localtion级别的rewrite */ NGX_HTTP_POST_REWRITE_PHASE, /* server、location级别的rewrite都是在这个phase进行收尾工作的 */ NGX_HTTP_PREACCESS_PHASE, /* 粗粒度的access */ NGX_HTTP_ACCESS_PHASE, /* 细粒度的access,比如权限验证、存取控制 */ NGX_HTTP_POST_ACCESS_PHASE, /* 根据上述两个phase得到access code进行操作 */ NGX_HTTP_TRY_FILES_PHASE, /* 实现try_files指令 */ NGX_HTTP_CONTENT_PHASE, /* 生成http响应 */ NGX_HTTP_LOG_PHASE /* log模块 */ } ngx_http_phases;这些phase按照先后顺序执行,只有在rewrite之后流程会重新跳转到NGX_HTTP_FIND_CONFIG_PHASE。其中,只有7个phase可以注册handler以定制处理过程,其他的只有一个固定的handler:
NGX_HTTP_POST_READ_PHASE NGX_HTTP_SERVER_REWRITE_PHASE, NGX_HTTP_REWRITE_PHASE, NGX_HTTP_PREACCESS_PHASE, NGX_HTTP_ACCESS_PHASE, NGX_HTTP_CONTENT_PHASE, NGX_HTTP_LOG_PHASE一般地,phase handler的注册都是在http模块的postconfiguration回调函数中,后面会看到为什么要在这个时间点注册。
typedef struct { ngx_array_t handlers; } ngx_http_phase_t;
ngx_http_core_main_conf_t的phases数组存放了所有的phase,其中每个元素是ngx_http_phase_t类型的,表示的就是对应的phase handler的数组。ngx_http_core_main_conf_t->phases数组主要用于handler的注册。
typedef struct { /** * 所有phase handler的数组。 */ ngx_http_phase_handler_t *handlers; /** * server rewrite阶段的handler在ngx_http_core_main_conf_t->phase_engine.handlers数组中的下标 */ ngx_uint_t server_rewrite_index; /** * rewrite阶段的handler在ngx_http_core_main_conf_t->phase_engine.handlers数组中的下标 */ ngx_uint_t location_rewrite_index; } ngx_http_phase_engine_t;ngx_http_core_main_conf_t的phase_engine字段表示phase的执行引擎,它会把所有的phase handler组织成数组,元素是ngx_http_phase_handler_t。phase_engine会根据phases数组中注册的handler进行初始化。
struct ngx_http_phase_handler_s { /* 执行校验,并调用handler函数,同一个phase的handler的checker相同 */ ngx_http_phase_handler_pt checker; /* handler函数指针 */ ngx_http_handler_pt handler; /* * 指向下一个phase的第一个handler在ngx_http_core_main_conf_t->phase_engine.handlers数组中的下标 * */ ngx_uint_t next; };
/** * 初始化每个phase对应的handlers数组,以便在执行postconfiguration回调函数时, * 注册phase handler。 * ngx_http_core_main_conf_t->phases数组存放所有的phase。 */ if (ngx_http_init_phases(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[m]->ctx; if (module->postconfiguration) { if (module->postconfiguration(cf) != NGX_OK) { return NGX_CONF_ERROR; } } } if (ngx_http_variables_init_vars(cf) != NGX_OK) { return NGX_CONF_ERROR; } /* * http{}'s cf->ctx was needed while the configuration merging * and in postconfiguration process */ *cf = pcf; /** * 根据各个phase的handlers数组初始化ngx_http_core_main_conf_t->phase_engine。 * 在phase_engine中所有的handler存放在一个数组中。 */ if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; }先调用ngx_http_init_phases为ngx_http_core_main_conf_t->phases分配空间,然后会调用所有http module的postconfiguration回调函数,最后再调用ngx_http_init_phase_handlers初始化ngx_http_core_main_conf_t->phase_engine。这就是为什么phase handler的注册只能在postconfiguration回调函数中,因为只有在它调用前phases才会分配空间。ngx_http_init_phases函数很简单,就是一堆ngx_array_t的初始化,下面看一下ngx_http_init_phase_handlers。
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; /* ngx_http_rewrite在http module的postconfiguration回调函数中添加REWRITE阶段的处理函数 */ use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0; /* ngx_http_access在http module的postconfiguration回调函数中添加ACCESS阶段的处理函数 */ use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0; /* 计算handler数组的大小 */ n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */; /* 对所有handlers计数 */ for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { n += cmcf->phases[i].handlers.nelts; } /* 为handler数组分配内存 */ 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; /* 下一个phase的第一个handler的索引 */ n = 0; /* * 初始化phase handler,保存checker和next字段 * continue的都是不能添加handler的phase */ 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) { /* 设置server rewrite对应的handler的开始下标 */ cmcf->phase_engine.server_rewrite_index = n; } checker = ngx_http_core_rewrite_phase; break; case NGX_HTTP_FIND_CONFIG_PHASE: /* find config对应的handler的下标,后面post rewrite的handler设置需要用到 */ 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) { /* 设置location rewrite的handler的开始下标 */ 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; /** * 这里将post rewrite的next设置为find config的handler对应下标。 * 因为在location rewrite之后,需要重新匹配location,所以需要再次 * 进入这个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; } /* 跳过本phase所有handler,也就指向了下一个phase的第一个handler */ n += cmcf->phases[i].handlers.nelts; /* 遍历初始化同一个phase上的handler */ 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; }
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; /* 遍历phase上注册的所有handler,这里是以r->phase_handler为索引组成的链表 */ while (ph[r->phase_handler].checker) { rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); /* 如果一个checker返回ok,则后面的handler不会被调用 */ if (rc == NGX_OK) { return; } } }ngx_http_core_run_phases会遍历所有的phase,然后调用它的checker进行处理,phase处理过程中的错误处理,校验等都是在checker中完成的,不同phase的checker的逻辑是不同,但是返回值的意义是相同的,如果checker的返回值时NGX_OK表示请求处理完毕,否则会进入下一个handler继续处理。接下来看一下phase的checker。
ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { ngx_int_t rc; /* * generic phase checker, * used by the post read and pre-access phases */ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "generic phase: %ui", r->phase_handler); rc = ph->handler(r); /* phase处理完毕,将r->phase_handler指向下一个phase的第一个handler */ if (rc == NGX_OK) { r->phase_handler = ph->next; return NGX_AGAIN; } /* 继续处理本phase。handler因为某种原因没有执行,继续执行其他的handler */ if (rc == NGX_DECLINED) { r->phase_handler++; return NGX_AGAIN; } /* 整个请求处理完毕 */ if (rc == NGX_AGAIN || rc == NGX_DONE) { return NGX_OK; } /* rc == NGX_ERROR || rc == NGX_HTTP_... */ ngx_http_finalize_request(r, rc); return NGX_OK; }
ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { u_char *p; size_t len; ngx_int_t rc; ngx_http_core_loc_conf_t *clcf; r->content_handler = NULL; r->uri_changed = 0; /* 根据uri查找location,先静态查找,再正则匹配 */ rc = ngx_http_core_find_location(r); if (rc == NGX_ERROR) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); /* 非内部请求访问内部location是非法的,所有与error处理类似 */ if (!r->internal && clcf->internal) { ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); return NGX_OK; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "using configuration \"%s%V\"", (clcf->noname ? "*" : (clcf->exact_match ? "=" : "")), &clcf->name); /* 根据匹配的location设置request的属性 */ ngx_http_update_location_config(r); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http cl:%O max:%O", r->headers_in.content_length_n, clcf->client_max_body_size); /* 判断请求内容大小是否超过限制 */ if (r->headers_in.content_length_n != -1 && !r->discard_body && clcf->client_max_body_size && clcf->client_max_body_size < r->headers_in.content_length_n) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client intended to send too large body: %O bytes", r->headers_in.content_length_n); (void) ngx_http_discard_request_body(r); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE); return NGX_OK; } /* 处理重定向 */ if (rc == NGX_DONE) { r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; } /* * we do not need to set the r->headers_out.location->hash and * r->headers_out.location->key fields */ if (r->args.len == 0) { r->headers_out.location->value = clcf->name; } else { len = clcf->name.len + 1 + r->args.len; p = ngx_pnalloc(r->pool, len); if (p == NULL) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; } r->headers_out.location->value.len = len; r->headers_out.location->value.data = p; p = ngx_cpymem(p, clcf->name.data, clcf->name.len); *p++ = '?'; ngx_memcpy(p, r->args.data, r->args.len); } ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY); return NGX_OK; } /* 执行rewrite phase handler */ r->phase_handler++; return NGX_AGAIN; }
ngx_int_t ngx_http_core_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { ngx_int_t rc; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "rewrite phase: %ui", r->phase_handler); rc = ph->handler(r); /* 继续处理本phase的handler */ if (rc == NGX_DECLINED) { r->phase_handler++; return NGX_AGAIN; } /* 请求处理完毕 */ if (rc == NGX_DONE) { return NGX_OK; } /* NGX_OK, NGX_AGAIN, NGX_ERROR, NGX_HTTP_... */ ngx_http_finalize_request(r, rc); return NGX_OK; }
ngx_int_t ngx_http_core_post_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { ngx_http_core_srv_conf_t *cscf; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "post rewrite phase: %ui", r->phase_handler); /** * uri_changed标志位表示uri是否有被重写。 * 如果没有的话,这里累加r->phase_handler,由于post rewrite只有一个handler, * 所以就会跳到下一个phase继续执行。 */ if (!r->uri_changed) { r->phase_handler++; return NGX_AGAIN; } /* uri被重写 */ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "uri changes: %d", r->uri_changes); /* * gcc before 3.3 compiles the broken code for * if (r->uri_changes-- == 0) * if the r->uri_changes is defined as * unsigned uri_changes:4 */ r->uri_changes--; /* 校验是否被重写了多次,uri_changes初始值为11,所以最多可以重写10次 */ if (r->uri_changes == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "rewrite or internal redirection cycle " "while processing \"%V\"", &r->uri); ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; } /* * 在ngx_http_init_phase_handlers中,如果use_rewrite不为0,那么 * post rewrite phase handler的next指向find config的phase handler。 * 接下来会进入find config phase */ r->phase_handler = ph->next; /* server rewrite有可能改变了server config,所以要对r->loc_conf重新赋值 */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); r->loc_conf = cscf->ctx->loc_conf; return NGX_AGAIN; }
ngx_int_t ngx_http_core_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { ngx_int_t rc; ngx_http_core_loc_conf_t *clcf; /* 只针对主请求处理 */ if (r != r->main) { r->phase_handler = ph->next; return NGX_AGAIN; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "access phase: %ui", r->phase_handler); rc = ph->handler(r); /* 跳到本phase的下一个handler */ if (rc == NGX_DECLINED) { r->phase_handler++; return NGX_AGAIN; } /* 请求处理完毕 */ if (rc == NGX_AGAIN || rc == NGX_DONE) { return NGX_OK; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); /* 相当于satisfy all,就是必须满足所有条件,所以继续执行access handler */ if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) { if (rc == NGX_OK) { r->phase_handler++; return NGX_AGAIN; } /* 否则只要满足任意一个条件即可,所以执行下一个phase的第一个handler */ } else { if (rc == NGX_OK) { /* 对access_code清零,后面post access phase根据这个属性处理 */ r->access_code = 0; if (r->headers_out.www_authenticate) { r->headers_out.www_authenticate->hash = 0; } r->phase_handler = ph->next; return NGX_AGAIN; } if (rc == NGX_HTTP_FORBIDDEN || rc == NGX_HTTP_UNAUTHORIZED) { /* 设置access_code,如果多个handler校验不通过,则只记录最后一个 */ r->access_code = rc; r->phase_handler++; return NGX_AGAIN; } } /* rc == NGX_ERROR || rc == NGX_HTTP_... */ ngx_http_finalize_request(r, rc); return NGX_OK; }
ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { ngx_int_t access_code; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "post access phase: %ui", r->phase_handler); access_code = r->access_code; /* 设置了access_code,说明没有权限,则终结请求 */ if (access_code) { if (access_code == NGX_HTTP_FORBIDDEN) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "access forbidden by rule"); } r->access_code = 0; ngx_http_finalize_request(r, access_code); return NGX_OK; } /* 否则,跳到下一个handler */ r->phase_handler++; return NGX_AGAIN; }
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; /* * 在find config phase中如果匹配到的location具有handler,则会赋值给r->content_handler。 * 而这里可以看到,如果r->content_handler存在则只会执行这一个handler,然后返回。 * 也就是说如果location设置了handler,则只会执行这一个content handler,不会执行其他的。 */ 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); if (rc != NGX_DECLINED) { ngx_http_finalize_request(r, rc); return NGX_OK; } /* handler返回NGX_DECLINED会由接下来的content handler继续处理 */ /* rc == NGX_DECLINED */ ph++; /* 如果下一个handler的checker存在,则返回NGX_AGAIN,继续调用下一个handler */ 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; }这一篇介绍请求处理,下一篇我们来看看响应内容是如何输出的。