nginx源码分析(8)——phase handler处理
来源:互联网 发布:在淘宝买电脑可靠吗 编辑:程序博客网 时间:2024/05/23 15:06
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回调函数中,后面会看到为什么要在这个时间点注册。
1. 重要的数据结构
1. ngx_http_phase_t
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的注册。
2. ngx_http_phase_engine_t
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进行初始化。
3. ngx_http_phase_handler_t
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;};
2. phase handler初始化
phase handler初始化是在http模块初始化函数ngx_http_block中完成的,摘录关键代码:
/** * 初始化每个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。
1. ngx_http_init_phase_handlers
static ngx_int_tngx_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;}
具体可以参见注释。大致原理就是将所有的phase handler以ngx_http_phase_handler_t->next组织成handler链表,处理请求时遍历这个链表。handler处理函数ngx_http_phase_handler_t->handler的调用是在checker中完成的,不同的phase具有不同的checker,但是同一个phase的checker相同。调用完这个函数,phase handler的初始化就完成了,下面看一下handler的调用。
3. phase handler调用
在请求处理一文中介绍了在处理的最后一步就是调用ngx_http_core_run_phases跑一遍所有的phase handler,下面看一下这个函数。
voidngx_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。
4. phase handler的checker
由于checker的返回值决定了是否执行下一个handler,而且handler的返回值又决定了checker的返回值,所以在编写phase handler时,要特别注意返回值。
1. ngx_http_core_generic_phase
这个checker用于处理post read和preaccess两个phase。它的作用很简单,就是根据handler的返回值决定继续这个phase的handler的处理,还是调用下一个phase的handler。它的返回值分为四种:
NGX_OK:本phase处理完毕,可以继续执行下一个phase的handler,返回NGX_AGAIN。
NGX_DECLINED:handler由于某种原因执行失败,但不影响本phase其他handler的执行,所以继续执行本phase的其他handler,返回NGX_AGAIN。
NGX_AGAIN或者NGX_DONE:请求处理完毕,返回NGX_OK。
NGX_ERROR或其他错误:终结请求的处理,释放相关资源。
ngx_int_tngx_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;}
2. ngx_http_core_find_config_phase
这是find config phase的checker,用于根据uri查找对应的location,nginx中location的处理是相当复杂的,这里关注于phase handler的处理流程。find config phase只有一个phase handler,并且它没有相应的handler回调函数,完成的只是根据uri匹配到location之后,将location的loc_conf赋值给request,并且根据loc_conf对request进行一些处理。所有的由静态字符串标识的location被称作static location,由正则表达式表示的location成为regex location。所有的static location被组织成二叉树,以便于查找。在static location匹配失败后,会进行regex location匹配。这个过程正好描述了nginx中location的匹配规则。
find config的checker可能会被执行多次,当rewrite成功后,会修改uri参数,需要重新匹配location。这就是find_config_index存在的必要,它用于初始化post rewrite phase handler的next字段,当uri被修改后会重新跳至find config phase执行。
ngx_int_tngx_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;}
3. ngx_http_core_rewrite_phase
用于处理server rewrite phase和rewrite phase。逻辑很简单就是执行响应的handler,因为phase handler执行流程的跳转是在post rewrite中完成的,所以这里只需要将r->phase_handler++顺序遍历其后的handler即可。
ngx_int_tngx_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;}
4. ngx_http_core_post_rewrite_phase
这是post rewrite phase的checker,用于对rewrite和server rewrite phase进行收尾工作。request中有两个字段与重写相关:
uri_changed:uri是否被重写。
uri_changes:uri被重写的次数,初始值为11,所以只能重写10次。
server rewrite和rewrite的handler会修改这两个变量,实现重写。这个checker就是根据uri_changed判断是否进入find config phase,然后再根据uri_changes做一些校验。
ngx_int_tngx_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;}
5. ngx_http_core_access_phase
这是access phase的checker,逻辑和之前的checker差不多,但需要注意这个checker有自己特有的逻辑:
satisfy:对应于satisfy指令,‘satisfy all’要满足所有的access handler,‘satisfy any’只需要满足任意一个access handler。
access_code:access phase中只判断是否有权限,而处理是在post access phase中完成的。access handler中设置具体的access code,然后传递给post access handler,由它处理最终的响应结果。
ngx_int_tngx_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;}
6. ngx_http_core_post_access_phase
这个是post access phase的checker,用于对access phase做收尾处理。在ngx_http_init_phase_handlers中只有当use_access为1时这个phase才会有handler,也就是说只有在access phase注册了handler时这个phase才会添加到请求处理流程里。post access handler完成的工作很简单,就是根据access_code做些出。
ngx_int_tngx_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;}
7. ngx_http_core_content_phase
这个checker处理content phase,也就是用于生成响应内容的,我们编写的模块大部分是在这个phase执行的。这个需要注意一点,如果location设置了handler(就是content handler),那么就只会执行这一个handler,而不会执行其他的。大部分content handler中会调用output filter产生输出。content handler的返回值只有等于NGX_DECLINED时才会执行接下来的handler,而如果是其他值则返回NGX_OK,结束请求的处理。
ngx_int_tngx_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;}这一篇介绍请求处理,下一篇我们来看看响应内容是如何输出的。
- nginx源码分析(8)——phase handler处理
- Nginx源码分析——日志处理
- nginx源码分析(7)——请求处理
- [nginx源码分析]nginx handler 模块解析
- (转)android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- 基于platform的ok6410按键中断实验
- Typical C++ interview questions rised by foreign companies
- android Ant 批量多渠道打包 总结!
- 接口和抽象类的理解
- 怎样写Linux下的USB驱动
- nginx源码分析(8)——phase handler处理
- Android 4.0新增WiFiDirect功能
- python中_get_getattr_getattribute_的差别
- C实现的五子棋
- 动画震动效果,,,嘿嘿。
- WordPress建站教程之安装和基本设置
- csdn我来了
- linux 基本操作命令
- 修复错误的Infopath表单xsn文件