无疑,在nginx的核心服务中,http服务占据了相当大的份量。那么,要想多了解nginx多一点,则必须要了解其http模块的工作机制。
而在上一篇文章中,我们已完全了解了nginx的worker工作机制,以及它是如何接入http服务的,但很明显那很粗,我们需要更深入点理解http模块的工作原理。
而本身nginx对模块的支持又是复杂的,至少我们认为有两个大方向,正向代理和反向代理。正向代理实际上就是一个http服务器,明显简单些,所以,我们本篇就来说说nginx的正向代理实现吧。
0. 整体时序图
如果你对nginx的http模块工作原理有过深入理解,相信只需要这一张时序图就够。为了节省大家宝贵时,可以先一览宏图。
1. 异步io事件的交接
我们知道,nginx的核心是事件io机制的使用,当外部网络io就绪时,内核会回应nginx, 而nginx则会通过accept(), receive(), fd_set 等方法,将事件接入进来,从而转交到http服务模块。其中select模块我们上一篇中已经讲过,此时再简单回顾下:(需要的话)
// event/modules/ngx_select_module.c // io 事件监听 static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) { int ready, nready; ngx_err_t err; ngx_uint_t i, found; ngx_event_t *ev; ngx_queue_t *queue; struct timeval tv, *tp; ngx_connection_t *c; // 获取 max_fd, 系统传值需要 if (max_fd == -1) { for (i = 0; i < nevents; i++) { c = event_index[i]->data; if (max_fd < c->fd) { max_fd = c->fd; } } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "change max_fd: %i", max_fd); } #if (NGX_DEBUG) if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) { for (i = 0; i < nevents; i++) { ev = event_index[i]; c = ev->data; ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select event: fd:%d wr:%d", c->fd, ev->write); } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "max_fd: %i", max_fd); } #endif if (timer == NGX_TIMER_INFINITE) { tp = NULL; } else { tv.tv_sec = (long) (timer / 1000); tv.tv_usec = (long) ((timer % 1000) * 1000); tp = &tv; } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select timer: %M", timer); work_read_fd_set = master_read_fd_set; work_write_fd_set = master_write_fd_set; // 在此处交由内核进行处理网络事件,epoll 机制,至少有一个事件到来时返回 // tp 代表是否要超时退出 ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp); err = (ready == -1) ? ngx_errno : 0; if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { // 事件结束后,先尝试更新gmtTime 时间信息 ngx_time_update(); } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select ready %d", ready); if (err) { ngx_uint_t level; if (err == NGX_EINTR) { if (ngx_event_timer_alarm) { ngx_event_timer_alarm = 0; return NGX_OK; } level = NGX_LOG_INFO; } else { level = NGX_LOG_ALERT; } ngx_log_error(level, cycle->log, err, "select() failed"); if (err == NGX_EBADF) { ngx_select_repair_fd_sets(cycle); } return NGX_ERROR; } if (ready == 0) { if (timer != NGX_TIMER_INFINITE) { return NGX_OK; } ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "select() returned no events without timeout"); return NGX_ERROR; } nready = 0; // 遍历所有事件 for (i = 0; i < nevents; i++) { ev = event_index[i]; c = ev->data; found = 0; // 写事件处理 if (ev->write) { if (FD_ISSET(c->fd, &work_write_fd_set)) { found = 1; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select write %d", c->fd); } } // 读或accept事件 else { if (FD_ISSET(c->fd, &work_read_fd_set)) { found = 1; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select read %d", c->fd); } } // 读写就绪事件 found 都为1 if (found) { ev->ready = 1; ev->available = -1; // 如果是 accept 事件则取 ngx_posted_accept_events 队列 // 否则取 ngx_posted_events 队列 queue = ev->accept ? &ngx_posted_accept_events : &ngx_posted_events; // 将事件插入到相应队列尾部 ngx_post_event(ev, queue); // 有效就绪事件+1 nready++; } } // 如果两个值不相等,则需要修正下 if (ready != nready) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "select ready != events: %d:%d", ready, nready); ngx_select_repair_fd_sets(cycle); } return NGX_OK; }View Code
反正大概意思就是会调用内核级别的select/poll或epoll等io机制,等待io事件的发生,然后返回到用户态。当然,为了保证系统例外情况,都会进行超时设置,避免系统事件检测的偶发异常,可以在超时机制帮助下正常工作。
此处接收到的事件可能写入两个队列: 即是否是 accept 队列之分。最开始建立连接时,自然是放入accept队列的,后续则一般放ngx_posted_events队列中。这两个队列,后续将被分开处理。accept 队列会将socket接入,并注册read监听。而 posted_events 则是需要进行正式处理的队列,将会读取数据,写入客户端等更多工作。
其中,ngx_http_init_connection 的初始化过程,如需要请点击查看.
// http/ngx_http_request.c // 初始化socket连接, 接入 http模块 void ngx_http_init_connection(ngx_connection_t *c) { ngx_uint_t i; ngx_event_t *rev; struct sockaddr_in *sin; ngx_http_port_t *port; ngx_http_in_addr_t *addr; ngx_http_log_ctx_t *ctx; ngx_http_connection_t *hc; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; ngx_http_in6_addr_t *addr6; #endif // 分配数据内存 hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); if (hc == NULL) { ngx_http_close_connection(c); return; } c->data = hc; /* find the server configuration for the address:port */ port = c->listening->servers; if (port->naddrs > 1) { /* * there are several addresses on this port and one of them * is an "*:port" wildcard so getsockname() in ngx_http_server_addr() * is required to determine a server address */ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { ngx_http_close_connection(c); return; } // 根据网络类型处理 switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) c->local_sockaddr; addr6 = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { break; } } hc->addr_conf = &addr6[i].conf; break; #endif default: /* AF_INET */ sin = (struct sockaddr_in *) c->local_sockaddr; addr = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) { if (addr[i].addr == sin->sin_addr.s_addr) { break; } } hc->addr_conf = &addr[i].conf; break; } } else { switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: addr6 = port->addrs; hc->addr_conf = &addr6[0].conf; break; #endif default: /* AF_INET */ addr = port->addrs; hc->addr_conf = &addr[0].conf; break; } } /* the default server configuration for the address:port */ hc->conf_ctx = hc->addr_conf->default_server->ctx; ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); if (ctx == NULL) { ngx_http_close_connection(c); return; } ctx->connection = c; ctx->request = NULL; ctx->current_request = NULL; c->log->connection = c->number; // 每个http server 都有自己的日志记录控制 c->log->handler = ngx_http_log_error; c->log->data = ctx; c->log->action = "waiting for request"; c->log_error = NGX_ERROR_INFO; rev = c->read; // 设置接收数据处理器为 ngx_http_wait_request_handler rev->handler = ngx_http_wait_request_handler; c->write->handler = ngx_http_empty_handler; #if (NGX_HTTP_V2) if (hc->addr_conf->http2) { rev->handler = ngx_http_v2_init; } #endif #if (NGX_HTTP_SSL) { ngx_http_ssl_srv_conf_t *sscf; sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); if (sscf->enable || hc->addr_conf->ssl) { hc->ssl = 1; c->log->action = "SSL handshaking"; rev->handler = ngx_http_ssl_handshake; } } #endif if (hc->addr_conf->proxy_protocol) { hc->proxy_protocol = 1; c->log->action = "reading PROXY protocol"; } if (rev->ready) { /* the deferred accept(), iocp */ if (ngx_use_accept_mutex) { ngx_post_event(rev, &ngx_posted_events); return; } rev->handler(rev); return; } // 将rev 放入到 ngx_event_timer_rbtree 队列中, 红黑树实现 ngx_add_timer(rev, c->listening->post_accept_timeout); // 重用 connection ngx_reusable_connection(c, 1); // 处理 读就绪事件,注册 read 监听 if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; } }View Code
经过 ngx_http_init_connection 之后,就注册了read事件, 该事件基本已就绪, 所以将会在下一次进行select操作时返回该事件, 即下一次worker巡检时触发 read. 而此时的handler则被设置为 ngx_http_wait_request_handler.
// http/ngx_http_request.c // 处理socket读事件 static void ngx_http_wait_request_handler(ngx_event_t *rev) { u_char *p; size_t size; ssize_t n; ngx_buf_t *b; ngx_connection_t *c; ngx_http_connection_t *hc; ngx_http_core_srv_conf_t *cscf; c = rev->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler"); if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); ngx_http_close_connection(c); return; } if (c->close) { ngx_http_close_connection(c); return; } hc = c->data; cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); // 默认1024 缓冲大小 size = cscf->client_header_buffer_size; b = c->buffer; // 首次接入时,创建初始空间 if (b == NULL) { // 创建缓冲区接收http传过来的数据 b = ngx_create_temp_buf(c->pool, size); if (b == NULL) { ngx_http_close_connection(c); return; } c->buffer = b; } else if (b->start == NULL) { // 缓冲冲填满,需要另外增加空间? b->start = ngx_palloc(c->pool, size); if (b->start == NULL) { ngx_http_close_connection(c); return; } b->pos = b->start; b->last = b->start; b->end = b->last + size; } // 接收数据 n = c->recv(c, b->last, size); if (n == NGX_AGAIN) { if (!rev->timer_set) { ngx_add_timer(rev, c->listening->post_accept_timeout); ngx_reusable_connection(c, 1); } if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; } /* * We are trying to not hold c->buffer's memory for an idle connection. */ // 如果还要等待更多数据,释放占有空间 if (ngx_pfree(c->pool, b->start) == NGX_OK) { b->start = NULL; } return; } if (n == NGX_ERROR) { ngx_http_close_connection(c); return; } if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client closed connection"); ngx_http_close_connection(c); return; } b->last += n; // 如果配置了 proxy_pass (且匹配了模式), 则走代理逻辑 if (hc->proxy_protocol) { hc->proxy_protocol = 0; p = ngx_proxy_protocol_read(c, b->pos, b->last); if (p == NULL) { ngx_http_close_connection(c); return; } b->pos = p; if (b->pos == b->last) { c->log->action = "waiting for request"; b->pos = b->start; b->last = b->start; ngx_post_event(rev, &ngx_posted_events); return; } } c->log->action = "reading client request line"; // 设置不可重用连接 ngx_reusable_connection(c, 0); // 创建 http 连接请求, 分配内存空, 设置下一个 handler 等等 c->data = ngx_http_create_request(c); if (c->data == NULL) { ngx_http_close_connection(c); return; } // 设置读取数据的处理器为 ngx_http_process_request_line, 以便下次使用 rev->handler = ngx_http_process_request_line; ngx_http_process_request_line(rev); }View Code
ngx_http_wait_request_handler 非常重要的一个任务就是接收客户端的传送数据,即调用 recv 方法处理数据, 使用缓冲区的方式进行读取, 默认缓冲区大小为 1024, 即实际是处理不了太多数据的. 最多读取1024字节, 然后正常情况下就进入到 ngx_http_process_request_line() 逻辑了. 也就是说, 剩下的数据会在接下再被读取, 而非一次性被读取完成. 其中, ngx_http_create_request 主要是为 body体分配内存空间.
// http/ngx_http_request.c ngx_http_request_t * ngx_http_create_request(ngx_connection_t *c) { ngx_http_request_t *r; ngx_http_log_ctx_t *ctx; ngx_http_core_loc_conf_t *clcf; // 重要: 分配请求的上下文信息 r = ngx_http_alloc_request(c); if (r == NULL) { return NULL; } c->requests++; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_set_connection_log(c, clcf->error_log); ctx = c->log->data; ctx->request = r; ctx->current_request = r; #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); r->stat_reading = 1; (void) ngx_atomic_fetch_add(ngx_stat_requests, 1); #endif return r; }View Code
// http/ngx_http_request.c static ngx_http_request_t * ngx_http_alloc_request(ngx_connection_t *c) { ngx_pool_t *pool; ngx_time_t *tp; ngx_http_request_t *r; ngx_http_connection_t *hc; ngx_http_core_srv_conf_t *cscf; ngx_http_core_main_conf_t *cmcf; hc = c->data; cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); pool = ngx_create_pool(cscf->request_pool_size, c->log); if (pool == NULL) { return NULL; } r = ngx_pcalloc(pool, sizeof(ngx_http_request_t)); if (r == NULL) { ngx_destroy_pool(pool); return NULL; } r->pool = pool; r->http_connection = hc; r->signature = NGX_HTTP_MODULE; r->connection = c; // 设置配置信息备用 r->main_conf = hc->conf_ctx->main_conf; r->srv_conf = hc->conf_ctx->srv_conf; r->loc_conf = hc->conf_ctx->loc_conf; // 可能使用读取方式为 blocking, 如果是异步读取, 则无需blocking r->read_event_handler = ngx_http_block_reading; // 将刚刚读取出的数据引用给到header指针, 以便重新读取 r->header_in = hc->busy ? hc->busy->buf : c->buffer; if (ngx_list_init(&r->headers_out.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { ngx_destroy_pool(r->pool); return NULL; } if (ngx_list_init(&r->headers_out.trailers, r->pool, 4, sizeof(ngx_table_elt_t)) != NGX_OK) { ngx_destroy_pool(r->pool); return NULL; } r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { ngx_destroy_pool(r->pool); return NULL; } cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t)); if (r->variables == NULL) { ngx_destroy_pool(r->pool); return NULL; } #if (NGX_HTTP_SSL) if (c->ssl) { r->main_filter_need_in_memory = 1; } #endif r->main = r; r->count = 1; // 分配系统默认值 tp = ngx_timeofday(); r->start_sec = tp->sec; r->start_msec = tp->msec; r->method = NGX_HTTP_UNKNOWN; r->http_version = NGX_HTTP_VERSION_10; r->headers_in.content_length_n = -1; r->headers_in.keep_alive_n = -1; r->headers_out.content_length_n = -1; r->headers_out.last_modified_time = -1; r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1; r->http_state = NGX_HTTP_READING_REQUEST_STATE; r->log_handler = ngx_http_log_error_handler; return r; }
而接下来的handler被设置为了 ngx_http_process_request_line , 则会进一步读取数据, 处理事件, 也是处理的核心任务.
2. 核心数据读取解析
前面我们看到, nginx 通过调用系统级recv() 接收部分客户端数据过来, 但那里仅有一个缓冲区的大小, 有可能取到的数据是不完整的. 那么, 自然需要进一步处理, 即: ngx_http_process_request_line . 它会在第一次接到数据时就进行调用, 但如果存在多数据段, 则会反复进行该 handler 的调用(这是一个核心的异步io的处理实现, 类似断点续传):
// http/ngx_http_request.c // 读取body数据,并响应客户端 static void ngx_http_process_request_line(ngx_event_t *rev) { ssize_t n; ngx_int_t rc, rv; ngx_str_t host; ngx_connection_t *c; ngx_http_request_t *r; c = rev->data; r = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http process request line"); if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); c->timedout = 1; ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; } rc = NGX_AGAIN; for ( ;; ) { if (rc == NGX_AGAIN) { // 读取header, 因为前面已经读取了部分buff, 可能就直接返回 n = ngx_http_read_request_header(r); if (n == NGX_AGAIN || n == NGX_ERROR) { break; } } // 读取body 数据, 按照http协议解析,非常长 // 如解析出 GET HTTP1.1... rc = ngx_http_parse_request_line(r, r->header_in); if (rc == NGX_OK) { /* the request line has been parsed successfully */ // 解析成功部分信息, 进行记录处理 r->request_line.len = r->request_end - r->request_start; r->request_line.data = r->request_start; r->request_length = r->header_in->pos - r->request_start; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http request line: \"%V\"", &r->request_line); r->method_name.len = r->method_end - r->request_start + 1; r->method_name.data = r->request_line.data; if (r->http_protocol.data) { r->http_protocol.len = r->request_end - r->http_protocol.data; } // 处理 uri, 解析路径, 放入 r->uri.data if (ngx_http_process_request_uri(r) != NGX_OK) { break; } if (r->schema_end) { r->schema.len = r->schema_end - r->schema_start; r->schema.data = r->schema_start; } if (r->host_end) { host.len = r->host_end - r->host_start; host.data = r->host_start; rc = ngx_http_validate_host(&host, r->pool, 0); if (rc == NGX_DECLINED) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid host in request line"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); break; } if (rc == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); break; } if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) { break; } r->headers_in.server = host; } if (r->http_version < NGX_HTTP_VERSION_10) { if (r->headers_in.server.len == 0 && ngx_http_set_virtual_server(r, &r->headers_in.server) == NGX_ERROR) { break; } ngx_http_process_request(r); break; } if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); break; } c->log->action = "reading client request headers"; // 如果运行到此处, 则意味着header数据未读取完成, 需要下一次io事件继续 // handler 变更为 ngx_http_process_request_headers rev->handler = ngx_http_process_request_headers; ngx_http_process_request_headers(rev); break; } if (rc != NGX_AGAIN) { /* there was error while a request line parsing */ ngx_log_error(NGX_LOG_INFO, c->log, 0, ngx_http_client_errors[rc - NGX_HTTP_CLIENT_ERROR]); if (rc == NGX_HTTP_PARSE_INVALID_VERSION) { ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED); } else { ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); } break; } /* NGX_AGAIN: a request line parsing is still incomplete */ if (r->header_in->pos == r->header_in->end) { rv = ngx_http_alloc_large_header_buffer(r, 1); if (rv == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); break; } if (rv == NGX_DECLINED) { r->request_line.len = r->header_in->end - r->request_start; r->request_line.data = r->request_start; ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long URI"); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE); break; } } } // 处理请求, 响应客户端 ngx_http_run_posted_requests(c); } // http/ngx_http_request.c // 进一步处理 header 信息 static void ngx_http_process_request_headers(ngx_event_t *rev) { u_char *p; size_t len; ssize_t n; ngx_int_t rc, rv; ngx_table_elt_t *h; ngx_connection_t *c; ngx_http_header_t *hh; ngx_http_request_t *r; ngx_http_core_srv_conf_t *cscf; ngx_http_core_main_conf_t *cmcf; c = rev->data; r = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http process request header line"); if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); c->timedout = 1; ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; } cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); rc = NGX_AGAIN; for ( ;; ) { // 依次解析每一行 header 信息 if (rc == NGX_AGAIN) { // 如果解析到最后还没完, 说明后续还需要读取数据 if (r->header_in->pos == r->header_in->end) { rv = ngx_http_alloc_large_header_buffer(r, 0); if (rv == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); break; } if (rv == NGX_DECLINED) { p = r->header_name_start; r->lingering_close = 1; if (p == NULL) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too large request"); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); break; } len = r->header_in->end - p; if (len > NGX_MAX_ERROR_STR - 300) { len = NGX_MAX_ERROR_STR - 300; } ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long header line: \"%*s...\"", len, r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); break; } } // 将会尝试读取更多的 header 数据 n = ngx_http_read_request_header(r); if (n == NGX_AGAIN || n == NGX_ERROR) { break; } } /* the host header could change the server configuration context */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); // 重新解析 header 信息 rc = ngx_http_parse_header_line(r, r->header_in, cscf->underscores_in_headers); if (rc == NGX_OK) { r->request_length += r->header_in->pos - r->header_name_start; if (r->invalid_header && cscf->ignore_invalid_headers) { /* there was error while a header line parsing */ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line: \"%*s\"", r->header_end - r->header_name_start, r->header_name_start); continue; } /* a header line has been parsed successfully */ h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); break; } h->hash = r->header_hash; h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; h->key.data[h->key.len] = '\0'; h->value.len = r->header_end - r->header_start; h->value.data = r->header_start; h->value.data[h->value.len] = '\0'; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); break; } if (h->key.len == r->lowcase_index) { ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { ngx_strlow(h->lowcase_key, h->key.data, h->key.len); } hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, h->lowcase_key, h->key.len); // ngx_http_process_host, 处理 host 验证 // ngx_http_process_connection, 处理 keepalive if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { break; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header: \"%V: %V\"", &h->key, &h->value); continue; } // header 解析完成后, 处理body 数据了 if (rc == NGX_HTTP_PARSE_HEADER_DONE) { /* a whole header has been parsed successfully */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header done"); r->request_length += r->header_in->pos - r->header_name_start; r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; rc = ngx_http_process_request_header(r); if (rc != NGX_OK) { break; } ngx_http_process_request(r); break; } if (rc == NGX_AGAIN) { /* a header line parsing is still not complete */ continue; } /* rc == NGX_HTTP_PARSE_INVALID_HEADER */ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); break; } ngx_http_run_posted_requests(c); }
以上是整个http的主要流程之一, 主要分为读取 header 处理header, 读取body处理body, 最后 ngx_http_run_posted_requests, 做后置处理. 其中重要的实现方式是, 依次读取每个字节, 进行http协议解析, 按行划分 header, 以及读取部分buffer就处理部分header等优化.
接下来, 我们看看其对 body 部分的处理:
// http/ngx_http_request.c // 处理 body 部分处理, 并处理响应对应请求 void ngx_http_process_request(ngx_http_request_t *r) { ngx_connection_t *c; c = r->connection; #if (NGX_HTTP_SSL) // https 处理... #endif // 删除 timer if (c->read->timer_set) { ngx_del_timer(c->read); } #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); r->stat_reading = 0; (void) ngx_atomic_fetch_add(ngx_stat_writing, 1); r->stat_writing = 1; #endif // 设置读写 handler 为 ngx_http_request_handler, 以便在必要的时候使用 c->read->handler = ngx_http_request_handler; c->write->handler = ngx_http_request_handler; r->read_event_handler = ngx_http_block_reading; // 由 ngx_http_handler 处理细节, 下节再看 ngx_http_handler(r); } // http/ngx_http_request.c // 读写处理器 static void ngx_http_request_handler(ngx_event_t *ev) { ngx_connection_t *c; ngx_http_request_t *r; c = ev->data; r = c->data; ngx_http_set_log_request(c->log, r); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http run request: \"%V?%V\"", &r->uri, &r->args); if (c->close) { r->main->count++; ngx_http_terminate_request(r, 0); ngx_http_run_posted_requests(c); return; } if (ev->delayed && ev->timedout) { ev->delayed = 0; ev->timedout = 0; } if (ev->write) { r->write_event_handler(r); } else { r->read_event_handler(r); } ngx_http_run_posted_requests(c); }
大体就是如何使用 recv() 读取数据的过程, 看着流程多, 但实际上其时间复杂度基本为 O(1), 所以效率蛮高的.
3. http 请求的处理
经过数据准备, 数据解析后, 就可以进行逻辑处理了. ngx 中支持许多的功能操作, 如配置内容跳转, 反向代理, 负载均衡 等等. 这些都统一归为一类操作.
void ngx_http_handler(ngx_http_request_t *r) { ngx_http_core_main_conf_t *cmcf; r->connection->log->action = NULL; // 设置 keepalive 标识 if (!r->internal) { switch (r->headers_in.connection_type) { case 0: r->keepalive = (r->http_version > NGX_HTTP_VERSION_10); break; case NGX_HTTP_CONNECTION_CLOSE: r->keepalive = 0; break; case NGX_HTTP_CONNECTION_KEEP_ALIVE: r->keepalive = 1; break; } r->lingering_close = (r->headers_in.content_length_n > 0 || r->headers_in.chunked); r->phase_handler = 0; } else { cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); r->phase_handler = cmcf->phase_engine.server_rewrite_index; } r->valid_location = 1; #if (NGX_HTTP_GZIP) r->gzip_tested = 0; r->gzip_ok = 0; r->gzip_vary = 0; #endif // 设置写处理器为 ngx_http_core_run_phases, 即开始响应客户端流程 r->write_event_handler = ngx_http_core_run_phases; ngx_http_core_run_phases(r); } // http/ngx_http_core_module.c // 响应客户端操作, 多阶段式操作 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; // 依次调用各 checker, 直到有响应 OK 的checker为止 while (ph[r->phase_handler].checker) { // 每次调用 checker 之后, 内部都会将 r->phase_handler++, 即迭代下一个 // 此处的 checker 非常之多, 是在各模块启动时, 自动向 ngx_http_core_module.main_conf 中进行注册的 /** * 定义如下: typedef enum { NGX_HTTP_POST_READ_PHASE = 0, NGX_HTTP_SERVER_REWRITE_PHASE, NGX_HTTP_FIND_CONFIG_PHASE, NGX_HTTP_REWRITE_PHASE, NGX_HTTP_POST_REWRITE_PHASE, NGX_HTTP_PREACCESS_PHASE, NGX_HTTP_ACCESS_PHASE, NGX_HTTP_POST_ACCESS_PHASE, NGX_HTTP_PRECONTENT_PHASE, NGX_HTTP_CONTENT_PHASE, NGX_HTTP_LOG_PHASE } ngx_http_phases; // 注册方式 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); *h = ngx_http_access_handler; */ // 将请求信息和 handler 本身传入调用(不是面向, 只能这么做了) rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); // 只要有一个处理成功, 则后续不再调用 if (rc == NGX_OK) { return; } } } // http/modules/ngx_http_rewrite_module.c // 路径重写模块 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); 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; } static ngx_int_t ngx_http_rewrite_handler(ngx_http_request_t *r) { ngx_int_t index; ngx_http_script_code_pt code; ngx_http_script_engine_t *e; ngx_http_core_srv_conf_t *cscf; ngx_http_core_main_conf_t *cmcf; ngx_http_rewrite_loc_conf_t *rlcf; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); index = cmcf->phase_engine.location_rewrite_index; if (r->phase_handler == index && r->loc_conf == cscf->ctx->loc_conf) { /* skipping location rewrite phase for server null location */ return NGX_DECLINED; } rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module); if (rlcf->codes == NULL) { return NGX_DECLINED; } e = ngx_pcalloc(r->pool, sizeof(ngx_http_script_engine_t)); if (e == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } e->sp = ngx_pcalloc(r->pool, rlcf->stack_size * sizeof(ngx_http_variable_value_t)); if (e->sp == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } e->ip = rlcf->codes->elts; e->request = r; e->quote = 1; e->log = rlcf->log; e->status = NGX_DECLINED; while (*(uintptr_t *) e->ip) { code = *(ngx_http_script_code_pt *) e->ip; code(e); } return e->status; } // http/modules/ngx_http_core_module.c 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; 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); 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); 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); r->expect_tested = 1; (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) { ngx_http_clear_location(r); 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; } r->headers_out.location->hash = 1; ngx_str_set(&r->headers_out.location->key, "Location"); 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_clear_location(r); 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; } r->phase_handler++; return NGX_AGAIN; } // http/ngx_http_core_module.c 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); if (rc == NGX_OK) { r->phase_handler = ph->next; return NGX_AGAIN; } 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; } static ngx_int_t ngx_http_index_handler(ngx_http_request_t *r) { u_char *p, *name; size_t len, root, reserve, allocated; ngx_int_t rc; ngx_str_t path, uri; ngx_uint_t i, dir_tested; ngx_http_index_t *index; ngx_open_file_info_t of; ngx_http_script_code_pt code; ngx_http_script_engine_t e; ngx_http_core_loc_conf_t *clcf; ngx_http_index_loc_conf_t *ilcf; ngx_http_script_len_code_pt lcode; if (r->uri.data[r->uri.len - 1] != '/') { return NGX_DECLINED; } if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) { return NGX_DECLINED; } ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); allocated = 0; root = 0; dir_tested = 0; name = NULL; /* suppress MSVC warning */ path.data = NULL; index = ilcf->indices->elts; for (i = 0; i < ilcf->indices->nelts; i++) { if (index[i].lengths == NULL) { if (index[i].name.data[0] == '/') { return ngx_http_internal_redirect(r, &index[i].name, &r->args); } reserve = ilcf->max_index_len; len = index[i].name.len; } else { ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); e.ip = index[i].lengths->elts; e.request = r; e.flushed = 1; /* 1 is for terminating '\0' as in static names */ len = 1; while (*(uintptr_t *) e.ip) { lcode = *(ngx_http_script_len_code_pt *) e.ip; len += lcode(&e); } /* 16 bytes are preallocation */ reserve = len + 16; } if (reserve > allocated) { name = ngx_http_map_uri_to_path(r, &path, &root, reserve); if (name == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } allocated = path.data + path.len - name; } if (index[i].values == NULL) { /* index[i].name.len includes the terminating '\0' */ ngx_memcpy(name, index[i].name.data, index[i].name.len); path.len = (name + index[i].name.len - 1) - path.data; } else { e.ip = index[i].values->elts; e.pos = name; while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); } if (*name == '/') { uri.len = len - 1; uri.data = name; return ngx_http_internal_redirect(r, &uri, &r->args); } path.len = e.pos - path.data; *e.pos = '\0'; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "open index \"%V\"", &path); ngx_memzero(&of, sizeof(ngx_open_file_info_t)); of.read_ahead = clcf->read_ahead; of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.test_only = 1; of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { if (of.err == 0) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err, "%s \"%s\" failed", of.failed, path.data); #if (NGX_HAVE_OPENAT) if (of.err == NGX_EMLINK || of.err == NGX_ELOOP) { return NGX_HTTP_FORBIDDEN; } #endif if (of.err == NGX_ENOTDIR || of.err == NGX_ENAMETOOLONG || of.err == NGX_EACCES) { return ngx_http_index_error(r, clcf, path.data, of.err); } if (!dir_tested) { rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1); if (rc != NGX_OK) { return rc; } dir_tested = 1; } if (of.err == NGX_ENOENT) { continue; } ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, "%s \"%s\" failed", of.failed, path.data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } uri.len = r->uri.len + len - 1; if (!clcf->alias) { uri.data = path.data + root; } else { uri.data = ngx_pnalloc(r->pool, uri.len); if (uri.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } p = ngx_copy(uri.data, r->uri.data, r->uri.len); ngx_memcpy(p, name, len - 1); } return ngx_http_internal_redirect(r, &uri, &r->args); } return NGX_DECLINED; } static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r) { u_char *last, *location; size_t root, len; ngx_str_t path; ngx_int_t rc; ngx_uint_t level; ngx_log_t *log; ngx_buf_t *b; ngx_chain_t out; ngx_open_file_info_t of; ngx_http_core_loc_conf_t *clcf; if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) { return NGX_HTTP_NOT_ALLOWED; } if (r->uri.data[r->uri.len - 1] == '/') { return NGX_DECLINED; } log = r->connection->log; /* * ngx_http_map_uri_to_path() allocates memory for terminating '\0' * so we do not need to reserve memory for '/' for possible redirect */ last = ngx_http_map_uri_to_path(r, &path, &root, 0); if (last == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } path.len = last - path.data; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http filename: \"%s\"", path.data); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ngx_memzero(&of, sizeof(ngx_open_file_info_t)); of.read_ahead = clcf->read_ahead; of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events; if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { switch (of.err) { case 0: return NGX_HTTP_INTERNAL_SERVER_ERROR; case NGX_ENOENT: case NGX_ENOTDIR: case NGX_ENAMETOOLONG: level = NGX_LOG_ERR; rc = NGX_HTTP_NOT_FOUND; break; case NGX_EACCES: #if (NGX_HAVE_OPENAT) case NGX_EMLINK: case NGX_ELOOP: #endif level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; break; default: level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; break; } if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) { ngx_log_error(level, log, of.err, "%s \"%s\" failed", of.failed, path.data); } return rc; } r->root_tested = !r->error_page; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd); if (of.is_dir) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir"); ngx_http_clear_location(r); r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } len = r->uri.len + 1; if (!clcf->alias && r->args.len == 0) { location = path.data + root; *last = '/'; } else { if (r->args.len) { len += r->args.len + 1; } location = ngx_pnalloc(r->pool, len); if (location == NULL) { ngx_http_clear_location(r); return NGX_HTTP_INTERNAL_SERVER_ERROR; } last = ngx_copy(location, r->uri.data, r->uri.len); *last = '/'; if (r->args.len) { *++last = '?'; ngx_memcpy(++last, r->args.data, r->args.len); } } r->headers_out.location->hash = 1; ngx_str_set(&r->headers_out.location->key, "Location"); r->headers_out.location->value.len = len; r->headers_out.location->value.data = location; return NGX_HTTP_MOVED_PERMANENTLY; } #if !(NGX_WIN32) /* the not regular files are probably Unix specific */ if (!of.is_file) { ngx_log_error(NGX_LOG_CRIT, log, 0, "\"%s\" is not a regular file", path.data); return NGX_HTTP_NOT_FOUND; } #endif if (r->method == NGX_HTTP_POST) { return NGX_HTTP_NOT_ALLOWED; } rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } log->action = "sending response to client"; r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = of.size; r->headers_out.last_modified_time = of.mtime; if (ngx_http_set_etag(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (r != r->main && of.size == 0) { return ngx_http_send_header(r); } r->allow_ranges = 1; /* we need to allocate all before the header would be sent */ b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); if (b->file == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } b->file_pos = 0; b->file_last = of.size; b->in_file = b->file_last ? 1: 0; b->last_buf = (r == r->main) ? 1: 0; b->last_in_chain = 1; b->file->fd = of.fd; b->file->name = path; b->file->log = log; b->file->directio = of.is_directio; out.buf = b; out.next = NULL; return ngx_http_output_filter(r, &out); } // http/ngx_http_request.c void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_connection_t *c; ngx_http_request_t *pr; ngx_http_core_loc_conf_t *clcf; c = r->connection; ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, "http finalize request: %i, \"%V?%V\" a:%d, c:%d", rc, &r->uri, &r->args, r == c->data, r->main->count); if (rc == NGX_DONE) { ngx_http_finalize_connection(r); return; } if (rc == NGX_OK && r->filter_finalize) { c->error = 1; } if (rc == NGX_DECLINED) { r->content_handler = NULL; r->write_event_handler = ngx_http_core_run_phases; ngx_http_core_run_phases(r); return; } if (r != r->main && r->post_subrequest) { rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc); } if (rc == NGX_ERROR || rc == NGX_HTTP_REQUEST_TIME_OUT || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST || c->error) { if (ngx_http_post_action(r) == NGX_OK) { return; } ngx_http_terminate_request(r, rc); return; } if (rc >= NGX_HTTP_SPECIAL_RESPONSE || rc == NGX_HTTP_CREATED || rc == NGX_HTTP_NO_CONTENT) { if (rc == NGX_HTTP_CLOSE) { c->timedout = 1; ngx_http_terminate_request(r, rc); return; } if (r == r->main) { if (c->read->timer_set) { ngx_del_timer(c->read); } if (c->write->timer_set) { ngx_del_timer(c->write); } } c->read->handler = ngx_http_request_handler; c->write->handler = ngx_http_request_handler; ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc)); return; } if (r != r->main) { if (r->buffered || r->postponed) { if (ngx_http_set_write_handler(r) != NGX_OK) { ngx_http_terminate_request(r, 0); } return; } pr = r->parent; if (r == c->data || r->background) { if (!r->logged) { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (clcf->log_subrequest) { ngx_http_log_request(r); } r->logged = 1; } else { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "subrequest: \"%V?%V\" logged again", &r->uri, &r->args); } r->done = 1; if (r->background) { ngx_http_finalize_connection(r); return; } r->main->count--; if (pr->postponed && pr->postponed->request == r) { pr->postponed = pr->postponed->next; } c->data = pr; } else { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http finalize non-active request: \"%V?%V\"", &r->uri, &r->args); r->write_event_handler = ngx_http_request_finalizer; if (r->waited) { r->done = 1; } } if (ngx_http_post_request(pr, NULL) != NGX_OK) { r->main->count++; ngx_http_terminate_request(r, 0); return; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wake parent request: \"%V?%V\"", &pr->uri, &pr->args); return; } if (r->buffered || c->buffered || r->postponed) { if (ngx_http_set_write_handler(r) != NGX_OK) { ngx_http_terminate_request(r, 0); } return; } if (r != c->data) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "http finalize non-active request: \"%V?%V\"", &r->uri, &r->args); return; } r->done = 1; r->read_event_handler = ngx_http_block_reading; r->write_event_handler = ngx_http_request_empty_handler; if (!r->post_action) { r->request_complete = 1; } if (ngx_http_post_action(r) == NGX_OK) { return; } if (c->read->timer_set) { ngx_del_timer(c->read); } if (c->write->timer_set) { c->write->delayed = 0; ngx_del_timer(c->write); } if (c->read->eof) { ngx_http_close_request(r, 0); return; } ngx_http_finalize_connection(r); }
然后就输出内容到客户端了。