Home >Backend Development >PHP Tutorial >Handling HTTP requests in Nginx
Overview
During the initial startup process of Nginx, the worker worker process will call the ngx_event_process_init method of the event module to allocate a ngx_connection_t connection for each listening socket ngx_listening_t, and Set up the connection Read event callback method
handler is ngx_event_accept, and mounts the read event to the epoll event mechanism to wait for the read event to occur on the listening socket connection. At this point,
Receive the HTTP request message
Before receiving the HTTP request, the successfully established connection will first be initialized; the function of the ngx_http_init_connection function is to set the callback method for reading and writing events, and the actual writing event The callback method does not perform any operation. The callback method of the read event is to initialize the
ngx_http_init_connection function execution flow:
function ngx_http_init_connection is defined in the file src/http/ngx_http_request.c as follows:
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 /* 分配http连接ngx_http_connection_t结构体空间 */ 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) ... #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) ... #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; c->log->handler = ngx_http_log_error; c->log->data = ctx; c->log->action = "waiting for request"; c->log_error = NGX_ERROR_INFO; /* 设置当前连接读、写事件的handler处理方法 */ rev = c->read; /* 设置当前连接读事件的处理方法handler为ngx_http_wait_request_handler */ rev->handler = ngx_http_wait_request_handler; /* * 设置当前连接写事件的处理方法handler为ngx_http_empty_handler, * 该方法不执行任何实际操作,只记录日志; * 因为处理请求的过程不需要write方法; */ c->write->handler = ngx_http_empty_handler; #if (NGX_HTTP_SPDY) ... #endif #if (NGX_HTTP_SSL) ... #endif if (hc->addr_conf->proxy_protocol) { hc->proxy_protocol = 1; c->log->action = "reading PROXY protocol"; } /* 若读事件准备就绪,则判断是否使用同步锁, * 根据同步锁情况判断决定是否立即处理该事件; */ if (rev->ready) { /* the deferred accept(), rtsig, aio, iocp */ /* * 若使用了同步锁ngx_use_accept_mutex, * 则将该读事件添加到待处理事件队列ngx_post_event中, * 直到退出锁时,才处理该读事件; */ if (ngx_use_accept_mutex) { ngx_post_event(rev, &ngx_posted_events); return; } /* 若没有使用同步锁,则直接处理该读事件; * 读事件的处理函数handler为ngx_http_wait_request_handler; */ rev->handler(rev); return; } /* * 若当前连接的读事件未准备就绪, * 则将其添加到定时器事件机制,并注册到epoll事件机制中; */ /* 将当前连接的读事件添加到定时器机制中 */ ngx_add_timer(rev, c->listening->post_accept_timeout); ngx_reusable_connection(c, 1); /* 将当前连接的读事件注册到epoll事件机制中 */ if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; } }
When connected When a readable event occurs for the first time, the ngx_http_wait_request_handler function will be called. The function of this function is to initialize the
ngx_http_wait_request_handler Function execution flow:
函数 ngx_http_wait_request_handler 在文件src/http/ngx_http_request.c 中定义如下:
/* 处理连接的可读事件 */ 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; /* 获取读事件所对应的连接ngx_connection_t 对象 */ 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; } /* 若当前读事件不超时,且其所对应的连接不设置close标志位,则继续指向以下语句 */ hc = c->data; /* 获取当前读事件请求的相关配置项结构 */ cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); size = cscf->client_header_buffer_size; /* 以下内容是接收缓冲区的操作 */ b = c->buffer; /* 若当前连接的接收缓冲区不存在,则创建该接收缓冲区 */ if (b == NULL) { 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; } /* 在当前连接上开始接收HTTP请求数据 */ 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; } /* 若接收HTTP请求数据成功,则调整接收缓冲区成员指针 */ b->last += n; if (hc->proxy_protocol) { hc->proxy_protocol = 0; p = ngx_proxy_protocol_parse(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); /* 为当前连接创建一个请求结构体ngx_http_request_t */ 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,接收HTTP请求行 */ ngx_http_process_request_line(rev); }
接收 HTTP 请求行
ngx_http_process_request_line 处理
函数 ngx_http_process_request_line 在文件src/http/ngx_http_request.c 中定义如下:
/* 处理HTTP请求行 */ 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; } /* 设置NGX_AGAIN标志,表示请求行还没解析完毕 */ rc = NGX_AGAIN; for ( ;; ) { /* 若请求行还没解析完毕,则继续解析 */ if (rc == NGX_AGAIN) { /* 读取当前请求未解析的数据 */ n = ngx_http_read_request_header(r); /* 若没有数据,或读取失败,则直接退出 */ if (n == NGX_AGAIN || n == NGX_ERROR) { return; } } /* 解析接收缓冲区header_in中的请求行 */ rc = ngx_http_parse_request_line(r, r->header_in); /* 若请求行解析完毕 */ if (rc == NGX_OK) { /* the request line has been parsed successfully */ /* 设置请求行的成员,请求行是ngx_str_t类型 */ 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; /* 设置HTTP请求协议 */ if (r->http_protocol.data) { r->http_protocol.len = r->request_end - r->http_protocol.data; } /* 处理请求中的URI */ if (ngx_http_process_request_uri(r) != NGX_OK) { return; } if (r->host_start && 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); return; } if (rc == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) { return; } 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) { return; } /* 若HTTP版本小于1.0版本,则表示不需要接收HTTP请求头部,则直接处理请求 */ ngx_http_process_request(r); return; } /* 初始化链表容器,为接收HTTP请求头部做准备 */ 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); return; } c->log->action = "reading client request headers"; /* 若请求行解析完毕,则接下来处理请求头部 */ /* 设置连接读事件的回调方法 */ rev->handler = ngx_http_process_request_headers; /* 开始处理HTTP请求头部 */ ngx_http_process_request_headers(rev); return; } /* 解析请求行出错 */ 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]); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } /* 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); return; } 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); return; } } } }
在接收并解析请求行的过程中会调用 ngx_http_read_request_header 读取请求数据,我们看下该函数是如何读取到请求数据的。
ngx_http_read_request_header 读取请求数据函数执行流程:
函数 ngx_http_read_request_header 在文件src/http/ngx_http_request.c 中定义如下:
static ssize_t ngx_http_read_request_header(ngx_http_request_t *r) { ssize_t n; ngx_event_t *rev; ngx_connection_t *c; ngx_http_core_srv_conf_t *cscf; /* 获取当前请求所对应的连接 */ c = r->connection; /* 获取当前连接的读事件 */ rev = c->read; /* 获取当前请求接收缓冲区的数据,header_in 是ngx_buf_t类型 */ n = r->header_in->last - r->header_in->pos; /* 若接收缓冲区有数据,则直接返回该数据 */ if (n > 0) { return n; } /* 若当前接收缓冲区没有数据,首先判断当前读事件是否准备就绪 */ if (rev->ready) { /* 若当前读事件已准备就绪,则从其所对应的连接套接字读取数据,并保存到接收缓冲区中 */ n = c->recv(c, r->header_in->last, r->header_in->end - r->header_in->last); } else { /* 若接收缓冲区没有数据,且读事件未准备就绪,则设置为NGX_AGAIN */ n = NGX_AGAIN; } /* 若接收缓冲区没有数据,且读事件未准备就绪,则设置为NGX_AGAIN */ /* 将当前读事件添加到定时器机制; * 将当前读事件注册到epoll事件机制; */ if (n == NGX_AGAIN) { if (!rev->timer_set) { cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); /* 将当前读事件添加到定时器机制中 */ ngx_add_timer(rev, cscf->client_header_timeout); } /* 将当前读事件注册到epoll事件机制中 */ if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } return NGX_AGAIN; } if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client prematurely closed connection"); } if (n == 0 || n == NGX_ERROR) { c->error = 1; c->log->action = "reading client request headers"; ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return NGX_ERROR; } r->header_in->last += n; return n; }
接收 HTTP 请求头部
前面已经成功接收并解析了
ngx_http_process_request_headers 处理
函数 ngx_http_process_request_headers 在文件src/http/ngx_http_request.c 中定义如下:
/* 处理HTTP请求头部 */ 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; /* 获取当前连接的HTTP请求 */ 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; } /* 获取ngx_http_core_module模块的main级别配置项结构 */ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); /* 表示当前请求头部未解析完毕 */ rc = NGX_AGAIN; for ( ;; ) { 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); return; } 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); return; } len = r->header_in->end - p; if (len > NGX_MAX_ERROR_STR - 300) { len = NGX_MAX_ERROR_STR - 300; p[len++] = '.'; p[len++] = '.'; p[len++] = '.'; } 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); return; } } /* 读取未解析请求数据 */ n = ngx_http_read_request_header(r); /* 若没有可读的数据,或读取失败,则直接退出 */ if (n == NGX_AGAIN || n == NGX_ERROR) { return; } } /* the host header could change the server configuration context */ /* 获取ngx_http_core_module模块的srv级别配置项结构 */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); /* 开始解析HTTP请求头部 */ rc = ngx_http_parse_header_line(r, r->header_in, cscf->underscores_in_headers); /* 解析出一行请求头部(注意:一行请求头部只是HTTP请求头部的一部分) */ if (rc == NGX_OK) { /* 设置当前请求的长度 */ r->request_length += r->header_in->pos - r->header_name_start; /* * 若当前解析出来的一行请求头部是非法的,或Nginx当前版本不支持, * 则记录错误日志,并继续解析下一行请求头部; */ 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 */ /* * 若当前解析出来的一行请求头部是合法的,表示成功解析出该行请求头部, * 将该行请求头部保存在当前请求的headers_in的headers链表中; * 接着继续解析下一行请求头部; */ h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } /* 设置请求头部名称的hash值 */ 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); return; } 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); if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { return; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header: \"%V: %V\"", &h->key, &h->value); continue; } /* 若成功解析所有请求头部,则接下来就开始处理该请求 */ 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; /* * 调用该函数主要目的有两个: * 1、根据HTTP头部的host字段,调用ngx_http_find_virtual_server查找虚拟主机的配置块; * 2、对HTTP请求头部协议版本进行检查,例如http1.1版本,host头部不能为空,否则会返回400 Bad Request错误; */ rc = ngx_http_process_request_header(r); if (rc != NGX_OK) { return; } /* 开始处理当前请求 */ ngx_http_process_request(r); return; } /* 表示当前行的请求头部未解析完毕,则继续读取请求数据进行解析 */ if (rc == NGX_AGAIN) { /* a header line parsing is still not complete */ continue; } /* rc == NGX_HTTP_PARSE_INVALID_HEADER: "\r" is not followed by "\n" */ /* 解析请求头部出错,则关闭该请求,并退出 */ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line: \"%*s\\r...\"", r->header_end - r->header_name_start, r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } }
处理 HTTP 请求
前面的步骤已经接收到完整的
ngx_http_process_request 处理
函数 ngx_http_process_request 在文件src/http/ngx_http_request.c 中定义如下:
/* 处理HTTP请求 */ void ngx_http_process_request(ngx_http_request_t *r) { ngx_connection_t *c; /* 获取当前请求所对应的连接 */ c = r->connection; #if (NGX_HTTP_SSL) ... #endif /* * 由于现在不需要再接收HTTP请求头部超时问题, * 则需要把当前连接的读事件从定时器机制中删除; * timer_set为1表示读事件已添加到定时器机制中, * 则将其从定时器机制中删除,0表示不在定时器机制中; */ if (c->read->timer_set) { ngx_del_timer(c->read); } #if (NGX_STAT_STUB) ... #endif /* 重新设置当前连接的读、写事件的回调方法 */ c->read->handler = ngx_http_request_handler; c->write->handler = ngx_http_request_handler; /* * 设置请求读事件的回调方法, * 其实ngx_http_block_reading函数实际对读事件不做任何处理; * 即在处理请求时,不会对读事件任何操作,除非有HTTP模块重新设置处理方法; */ r->read_event_handler = ngx_http_block_reading; /* 开始处理各个HTTP模块的handler方法,该函数定义于ngx_http_core_module.c中*/ ngx_http_handler(r); /* 处理post请求 */ ngx_http_run_posted_requests(c); }
ngx_http_handler 函数的执行流程:
函数 ngx_http_handler 在文件 src/http/ngx_http_core_module.c 中定义如下:
void ngx_http_handler(ngx_http_request_t *r) { ngx_http_core_main_conf_t *cmcf; r->connection->log->action = NULL; r->connection->unexpected_eof = 0; /* 若当前请求的internal标志位为0,表示不需要重定向 */ if (!r->internal) { /* 下面语句是决定是否使用keepalive机制 */ 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); /* * phase_handler序号设置为0,表示执行ngx_http_phase_engine_t结构体成员 * ngx_http_phase_handler_t *handlers数组中的第一个回调方法; */ r->phase_handler = 0; } else { /* 若当前请求的internal标志位为1,表示需要做内部跳转 */ /* 获取ngx_http_core_module模块的main级别的配置项结构 */ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); /* * 将phase_handler序号设为server_rewriter_index, * 该phase_handler序号是作为ngx_http_phase_engine_t结构中成员 * ngx_http_phase_handler_t *handlers回调方法数组的序号, * 即表示回调方法在该数组中所处的位置; * * server_rewrite_index则是handlers数组中NGX_HTTP_SERVER_REWRITE_PHASE阶段的 * 第一个ngx_http_phase_handler_t回调的方法; */ 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 /* 设置当前请求写事件的回调方法 */ r->write_event_handler = ngx_http_core_run_phases; /* * 执行该回调方法,将调用各个HTTP模块共同处理当前请求, * 各个HTTP模块按照11个HTTP阶段进行处理; */ ngx_http_core_run_phases(r); }
ngx_http_core_run_phases 函数的执行流程:
函数 ngx_http_core_run_phases 在文件src/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; /* 获取ngx_http_core_module模块的main级别的配置项结构体 */ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); /* 获取各个HTTP模块处理请求的回调方法数组 */ ph = cmcf->phase_engine.handlers; /* 若实现了checker方法 */ while (ph[r->phase_handler].checker) { /* 执行phase_handler序号在数组中指定的checker方法 */ rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); /* 成功执行checker方法,则退出,否则继续执行下一个HTTP模块的checker方法 */ if (rc == NGX_OK) { return; } } }
处理子请求
post 子请求是基于 subrequest 机制的,首先看下 post 子请求结构体类型:
/* 子请求的单链表结构 */ typedef struct ngx_http_posted_request_s ngx_http_posted_request_t; struct ngx_http_posted_request_s { /* 指向当前待处理子请求的ngx_http_request_t结构体 */ ngx_http_request_t *request; /* 指向下一个子请求 */ ngx_http_posted_request_t *next; };
ngx_http_run_posted_requests 函数执行流程:
函数 ngx_http_run_posted_requests 在文件src/http/ngx_http_request.c 中定义如下:
void ngx_http_run_posted_requests(ngx_connection_t *c) { ngx_http_request_t *r; ngx_http_log_ctx_t *ctx; ngx_http_posted_request_t *pr; for ( ;; ) { /* 若当前连接已被销毁,则直接退出 */ if (c->destroyed) { return; } /* 获取当前连接所对应的请求 */ r = c->data; /* 获取原始请求的子请求单链表 */ pr = r->main->posted_requests; /* 若子请求单链表为空,则直接退出 */ if (pr == NULL) { return; } /* 将原始请求的posted_requests指向单链表的下一个post请求 */ r->main->posted_requests = pr->next; /* 获取子请求链表中的第一个post请求 */ r = pr->request; ctx = c->log->data; ctx->current_request = r; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http posted request: \"%V?%V\"", &r->uri, &r->args); /* * 调用当前post请求写事件的回调方法write_event_handler; * 子请求不被网络事件驱动,因此不需要调用read_event_handler; */ r->write_event_handler(r); } }
处理 HTTP 请求包体
下面开始要分析
其中有一个很重要的成员就是请求结构体 ngx_http_request_t 中的引用计数count,引用计数是用来决定是否真正结束当前请求,若引用计数为0 时,表示没有其他动作在处理该请求,则可以终止该请求;若引用计数不为0 时,表示当前请求还有其他动作在操作,因此不能结束当前请求,以免发生错误;那怎么样控制这个引用计数呢?例如,当一个请求添加新事件,或是把一些原本从定时器、epoll 事件机制中移除的事件从新加入到其中等等,出现这些情况都是要对引用计数增加1;当要结束请求时,首先会把引用计数减 1,并判断该引用计数是否为 0,再进一步判断是否决定真的结束当前请求。
接收 HTTP 请求包体
/* 存储HTTP请求包体的结构体ngx_http_request_body_t */ typedef struct { /* 存放HTTP请求包体的临时文件 */ ngx_temp_file_t *temp_file; /* * 指向接收HTTP请求包体的缓冲区链表表头, * 因为当一个缓冲区ngx_buf_t无法容纳所有包体时,就需要多个缓冲区形成链表; */ ngx_chain_t *bufs; /* 指向当前保存HTTP请求包体的缓冲区 */ ngx_buf_t *buf; /* * 根据content-length头部和已接收包体长度,计算还需接收的包体长度; * 即当前剩余的请求包体大小; */ off_t rest; /* 接收HTTP请求包体缓冲区链表空闲缓冲区 */ ngx_chain_t *free; /* 接收HTTP请求包体缓冲区链表已使用的缓冲区 */ ngx_chain_t *busy; /* 保存chunked的解码状态,供ngx_http_parse_chunked方法使用 */ ngx_http_chunked_t *chunked; /* * HTTP请求包体接收完毕后执行的回调方法; * 即ngx_http_read_client_request_body方法传递的第 2 个参数; */ ngx_http_client_body_handler_pt post_handler; } ngx_http_request_body_t;
接收
as follows: /* Receive HTTP request body */ ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler) { size_t preread; ssize_t size; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out, *cl; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; /* * When there is logic to start the process, the reference count will increase by 1. When the process ends, the reference count will decrease by 1; * In the ngx_http_read_client_request_body method, first increase the original request reference count by 1, * When encountering an abnormal termination, the reference count will be decremented by 1 before the function returns; * If it ends normally, the reference count will continue to be maintained by the post_handler method; */ /* Increase the reference count of the original request by 1 */ r->main->count++; #if (NGX_HTTP_SPDY) if (r->spdy_stream && r == r->main) { rc = ngx_http_spdy_read_request_body(r, post_handler); goto done; } #endif /* When the HTTP request body is not processed, the request_body structure is not allocated and will only be allocated when it is processed */ /* * If the current HTTP request is not an original request, or the HTTP request body has been read or discarded; * Then directly execute the callback method post_handler of the HTTP module and return NGX_OK; */ if (r != r->main || r->request_body || r->discard_body) { post_handler(r); return NGX_OK; } /* * ngx_http_test_expect is used to check whether the client sends the Expect:100-continue header, * If the client has sent this header to indicate that it expects to send the request body data, the server will return