Nginx-HTTP之静态网页访问流程分析一
假设访问静态网页的配置如下:
worker_processes 1;
error_log stderr debug;
daemon off;
master_process on;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8080;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 504 /50x.html;
location = /50x.html {
root html;
}
}
}
假设在浏览器输入如下指令:http://serverip:8080/index.html
,Nginx 服务器即会返回所请求的页面。大致流程如下。
1. ngx_connection_t
typedef struct ngx_connection_s ngx_connection_t;
/*
* 这个连接表示是客户端主动发起的,Nginx服务器被动接受
* 的 TCP 连接,简称之为被动连接
*/
struct ngx_connection_s {
void *data;
/*
* 连接对应的读事件
*/
ngx_event_t *read;
/*
* 连接对应的写事件
*/
ngx_event_t *write;
/*
* 该连接对应已连接套接字
*/
ngx_socket_t fd;
/*
* 直接接收网络字节流的方法
*/
ngx_recv_pt recv;
/*
* 直接发送网络字节流的方法
*/
ngx_send_pt send;
/*
* 以 ngx_chain_t 链表为参数来接收网络字节流的方法
*/
ngx_recv_chain_pt recv_chain;
/*
* 以 ngx_chain_t 链表为参数来发送网络字节流的方法
*/
ngx_send_chain_pt send_chain;
/*
* 这个连接对应的 ngx_listening_t 监听对象,此连接由 listening
* 监听端口的事件建立.
*/
ngx_listening_t *listening;
/*
* 这个连接上已经发送出去的字节数
*/
off_t sent;
/*
* 记录日志的 ngx_log_t 对象
*/
ngx_log_t *log;
/*
* 内存池。一般在 accept 一个新连接时,会创建一个内存池,而在这个连接
* 结束时会销毁内存池。注意,这里所说的连接是指成功建立的 TCP 连接,
* 所有的 ngx_connection_t 结构体都是预分配的。这个内存池的大小将由
* listening 监听对象中的 pool_size 成功决定.
*/
ngx_pool_t *pool;
int type;
/*
* 连接客户端的 sockaddr 结构体
*/
struct sockaddr *sockaddr;
socklen_t socklen;
/*
* 连接客户端字符串形式的 IP 地址
*/
ngx_str_t addr_text;
ngx_str_t proxy_protocol_addr;
in_port_t proxy_protocol_port;
#if (NGX_SSL || NGX_COMPAT)
ngx_ssl_connection_t *ssl;
#endif
/*
* 本机的监听端口对应的 sockaddr 结构体,也就是 listening 监听
* 对象中的 sockaddr 成员.
*/
struct sockaddr *local_sockaddr;
socklen_t local_socklen;
/*
* 用于接收、缓存客户端发来的字符流,每个事件消费模块可自由决定从
* 连接池中分配多大的空间给 buffer 这个接收缓存字段。例如,在 HTTP
* 模块中,它的大小决定于 client_header_buffer_size 配置项.
*/
ngx_buf_t *buffer;
/*
* 该字段用来将当前连接以双向链表元素的形式添加到 ngx_cycle_t 核心结构体
* 的 reuseable_connections_queue 双向链表中,表示可重用的连接.
*/
ngx_queue_t queue;
/*
* 连接使用次数。ngx_connection_t 结构体每次建立一条来自客户端的连接,
* 或者用于主动向后端服务器发起连接时(ngx_peer_connection_t 也是用该字
* 段),number 都会加 1
*
*/
ngx_atomic_uint_t number;
/*
* 处理的请求次数
*/
ngx_uint_t requests;
/*
* 缓存中的业务类型。任何事件消费模块都可以自定义需要的标志位。这个
* buffered 字段有 8 位,最多可以同时表示 8 个不同的业务。第三方模块
* 在自定义buffered标志位时注意不要与可能使用的模块定义的标志位冲突。
* 目前 openssl 模块定义了一个标志位:
* - #define NGX_SSL_BUFFERED 0x01
* HTTP官方模块定义了以下标志位:
* - #define NGX_HTTP_LOWLEVEL_BUFFERED 0xf0
* - #define NGX_HTTP_WRITE_BUFFERED 0x10
* - #define NGX_HTTP_GZIP_BUFFERED 0x20
* - #define NGX_HTTP_SSI_BUFFERED 0x01
* - #define NGX_HTTP_SUB_BUFFERED 0x02
* - #define NGX_HTTP_COPY_BUFFERED 0x04
* - #define NGX_HTTP_IMAGE_BUFFERED 0x08
* 同时,对于 HTTP 模块而言,buffered 的低4位要慎用,在实际发送响应的
* ngx_http_write_filter_module 过滤模块中,低 4 位标志位为 1 则意味着
* Nginx 会一直认为有 HTTP 模块还需要处理这个请求,必须等待 HTTP 模块
* 将低 4 位全置为 0 才会正常结束请求。检查低 4 位的宏如下:
* - #define NGX_LOWLEVEL_BUFFERED 0xf0
*/
unsigned buffered:8;
/*
* 本连接记录日志的级别
*/
unsigned log_error:3; /* ngx_connection_log_error_e */
/*
* 标志位,为 1 时表示连接已超时
*/
unsigned timedout:1;
/*
* 标志位,为 1 时表示连接处理过程中出现错误
*/
unsigned error:1;
/*
* 标志位,为 1 时表示连接已经销毁。这里的连接指的是 TCP 连接,而不是
* ngx_connection_t 结构体。当 destroyed 为 1 时,ngx_connection_t 结
* 构体仍然存在,但其对应的套接字、内存池等已经不可用
*/
unsigned destroyed:1;
/*
* 标志位,为 1 时表示连接处于空闲状态,如 keepalive 请求中两次请求之间的状态
*/
unsigned idle:1;
/*
* 标志位,为 1 时表示连接可重用,它与上面的 queue 字段是对应使用的
*/
unsigned reusable:1;
/*
* 标志位,为1时表示连接关闭
*/
unsigned close:1;
unsigned shared:1;
/*
* 标志位,为 1 时表示正在将文件中的数据发往连接的另一端
*/
unsigned sendfile:1;
/*
* 标志位,如果为 1,则表示只有在连接套接字对应的发送缓冲区必须满足
* 最低设置的大小阈值时,事件驱动模块才会分发该事件。这与
* ngx_handle_write_event 方法中的 lowat 参数是对应的
*/
unsigned sndlowat:1;
/*
* 标志位,表示如何使用 TCP 的 nodelay 特性。它的取值范围是下面这个枚举
* 类型 ngx_connection_tcp_nodelay_e:
* typedef enum {
* NGX_TCP_NODELAY_UNSET = 0,
* NGX_TCP_NODELAY_SET,
* NGX_TCP_NODELAY_DISABLED
* }ngx_connection_tcp_nodelay_e;
*/
unsigned tcp_nodelay:2; /* ngx_connection_tcp_nodelay_e */
/*
* 标志位,表示如何使用 TCP 的 nopush 特性。它的取值范围是下面这个枚举类型
* ngx_connection_tcp_nopush_e:
* typedef enum {
* NGX_TCP_NOPUSH_UNSET = 0,
* NGX_TCP_NOPUSH_SET,
* NGX_TCP_NOPUSH_DISABLED
* }ngx_connection_tcp_nopush_e;
*/
unsigned tcp_nopush:2; /* ngx_connection_tcp_nopush_e */
unsigned need_last_buf:1;
#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
unsigned busy_count:2;
#endif
#if (NGX_THREADS || NGX_COMPAT)
ngx_thread_task_t *sendfile_task;
#endif
};
2. ngx_http_init_connection
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 结构体,该结构体
* 代表当前的 HTTP 连接 */
hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
if (hc == NULL) {
ngx_http_close_connection(c);
return;
}
/* 将 data 指针指向表示当前 HTTP 连接的 ngx_http_connection_t */
c->data = hc;
/* find the server configuration for the address:port */
/* listening:这个连接对应的 ngx_listening_t 监听对象,此连接由 listening
* 监听端口的事件建立.
* servers: 对于 HTTP 模块,该指针指向 ngx_http_port_t 结构体,该结构体
* 实际保存着当前监听端口的地址信息.
*/
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 {
/* 本机的监听端口对应的 sockaddr 结构体 */
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;
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;
/* 为该连接的读事件设置回调处理函数 */
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) {
c->log->action = "SSL handshaking";
if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"no \"ssl_certificate\" is defined "
"in server listening on SSL port");
ngx_http_close_connection(c);
return;
}
hc->ssl = 1;
rev->handler = ngx_http_ssl_handshake;
}
}
#endif
if (hc->addr_conf->proxy_protocol) {
hc->proxy_protocol = 1;
c->log->action = "reading PROXY protocol";
}
/*
* 标志位,为1时表示当前事件已经准备就绪,也就是说,允许这个事件的消费者模块
* 处理这个事件。在HTTP框架中,经常会检查事件的ready标志位以确定是否可以接收
* 请求或者发送响应 */
if (rev->ready) {
/* the deferred accept(), iocp */
/* 为 1,表示开启了负载均衡机制,此时不会立刻执行该读事件,而是将当前的
* 读事件添加到 ngx_posted_events 延迟执行队列中 */
if (ngx_use_accept_mutex) {
ngx_post_event(rev, &ngx_posted_events);
return;
}
/* 若没有开启负载均衡机制,则直接处理该读事件 */
rev->handler(rev);
return;
}
/* 将读事件添加到定时器中,超时时间为 post_accept_timeout 毫秒
* post_accept_timeout 在配置文件中没有配置的话,默认为 60000
* 毫秒 */
ngx_add_timer(rev, c->listening->post_accept_timeout);
/* 将该连接添加到可重用双向链表的头部 */
ngx_reusable_connection(c, 1);
/* 将该读事件添加到事件驱动模块中,这样当该事件对应的 TCP 连接上
* 一旦出现可读事件(如接收到 TCP 连接的另一端发送来的字节流)就会
* 回调该事件的 handler 方法 */
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
}
3. ngx_add_timer
添加一个定时器事件,超时时间为 timer 毫秒.
/*
* @ev: 需要操作的事件
* @timer: 单位是毫秒,它告诉定时器事件 ev 希望 timer 毫秒后超时,
* 同时需要回调 ev 的handler 方法.
*/
static ngx_inline void
ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
{
ngx_msec_t key;
ngx_msec_int_t diff;
key = ngx_current_msec + timer;
/* 若该事件已经添加到定时器中(即红黑树) */
if (ev->timer_set) {
/*
* Use a previous timer value if difference between it and a new
* value is less than NGX_TIMER_LAZY_DELAY milliseconds: this allows
* to minimize the rbtree operations for fast connections.
*/
/* 若该事件之前已经添加到定时器中,则计算此时两者的超时时间之差 */
diff = (ngx_msec_int_t) (key - ev->timer.key);
/* 若两者之差小于 300 毫秒,则直接返回 */
if (ngx_abs(diff) < NGX_TIMER_LAZY_DELAY) {
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"event timer: %d, old: %M, new: %M",
ngx_event_ident(ev->data), ev->timer.key, key);
return;
}
/* 否则,则先删除该定时器事件,下面再以新的超时时间添加到定时器中 */
ngx_del_timer(ev);
}
/* 超时时间 */
ev->timer.key = key;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"event timer add: %d: %M:%M",
ngx_event_ident(ev->data), timer, ev->timer.key);
/*
* 将事件添加到红黑树中
* 这种添加是间接性的,每个事件对象封装结构体中都有一个timer字段,
* 其为ngx_rbtree_node_t 类型变量,加入到红黑树中就是该字段,
* 而非事件对象结构体本身。后面要获取该事件结构体时可以通过利用
* offsetof宏来根据该timer字段方便找到其所在的对应事件对象结构体.
*/
ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);
/* 置位该变量,表示添加到红黑树中 */
ev->timer_set = 1;
}
4. ngx_reusable_connection
void
ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)
{
ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
"reusable connection: %ui", reusable);
/* 标志位,为 1 表示该连接可重用 */
if (c->reusable) {
ngx_queue_remove(&c->queue);
ngx_cycle->reusable_connections_n--;
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_waiting, -1);
#endif
}
/* 用参数值重新设置该标志位 */
c->reusable = reusable;
/* 若调用者指定该连接可重用 */
if (reusable) {
/* need cast as ngx_cycle is volatile */
/* 则将该连接添加到 reusable_connections_queue 双向链表头中 */
ngx_queue_insert_head(
(ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);
/* 可重用连接的个数加 1 */
ngx_cycle->reusable_connections_n++;
#if (NGX_STAT_STUB)
/* 将原子变量 ngx_stat_waiting 的值加 1 */
(void) ngx_atomic_fetch_add(ngx_stat_waiting, 1);
#endif
}
}
5. ngx_handle_read_event
/*
* 将读事件添加到事件驱动模块中,这样该事件对应的 TCP 连接上一旦出现
* 可读事件(如接收到 TCP 连接另一端发送来的字符流)就会回调该事件的
* handler 方法
*/
ngx_int_t
ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)
{
/* 在使用 epoll 的情况下,NGX_USE_CLEAR_EVENT 宏表示为该 epoll 使用
* ET(即边缘)模式。实际上,在 Nginx 中,epoll 是默认使用边缘模式的,
* 该模式仅支持非阻塞方式。所谓边缘模式,即为当一个新的事件到来时,
* ET 模式从 epoll_wait 调用获取这个事件,可是如果这次没有把这个事件
* 对应的套接字缓存区处理完,在这个套接字没有新的事件再次到来时,在 ET
* 模式是无法再次从 epoll_wait 调用中获取这个事件的;而 LT(即水平)模式
* 相反,只要有一个事件对应的套接字缓冲区还有数据,就总能从 epoll_wait 中
* 获取这个事件。因此,LT 模式相对简单,而在 ET 模式下事件发生时,若没有
* 彻底将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。 */
if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
/* kqueue, epoll */
/* 若当前读事件不是活跃的且该读事件还未准备就绪,则将该读事件以
* 边缘模式添加到 epoll 中 */
if (!rev->active && !rev->ready) {
if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
}
}
return NGX_OK;
} else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {
/* select, poll, /dev/poll */
if (!rev->active && !rev->ready) {
if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
}
return NGX_OK;
}
if (rev->active && (rev->ready || (flags & NGX_CLOSE_EVENT))) {
if (ngx_del_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT | flags)
== NGX_ERROR)
{
return NGX_ERROR;
}
return NGX_OK;
}
} else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
/* event ports */
if (!rev->active && !rev->ready) {
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
return NGX_OK;
}
if (rev->oneshot && !rev->ready) {
if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
return NGX_OK;
}
}
/* iocp */
return NGX_OK;
}
ngx_http_init_connection 函数主要是为当前的 HTTP 连接创建并初始化一个 ngx_http_connection_t 结构体,并使 ngx_connection_t 结构体的 data 指针指向该结构体,然后将当前连接的读事件添加到定时器和 epoll 事件监控机制中,等待客户端发送数据来触发该读事件.
当监听到客户端发送数据过来时,会调用该读事件的 ngx_http_wait_request_handler 回调函数。
7. ngx_http_wait_request_handler
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;
/* 事件相关的对象。通常 data 都是指向 ngx_connection_t 连接对象。
* 开启文件异步 I/O 时,它可能会指向 ngx_event_aio_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;
}
/* 标志位,为 1 时表示连接关闭 */
if (c->close) {
ngx_http_close_connection(c);
return;
}
/* 由 ngx_http_init_connection 函数知,此时该 data 指针指向
* ngx_http_connection_t 结构体 */
hc = c->data;
/* 获取该 server{} 对应的配置项结构体 */
cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
size = cscf->client_header_buffer_size;
/* 用于接收、缓存客户端发来的字节流,每个事件消费模块可自由决定从连接池中
* 分配多大的空间给 buffer 这个接收缓存字段。例如,在 HTTP 模块中,它的大小
* 决定于 client_header_buffer_size 配置项 */
b = c->buffer;
/* 若没有为当前连接的接收/发送缓存分配内存 */
if (b == NULL) {
/* 分配一个 size 大小的临时缓存(表示该缓存中的数据在内存中且
* 该缓存中的数据可以被修改) */
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;
}
/* 调用接收字节流的回调函数 ngx_unix_recv 接收客户端发送的数据 */
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;
}
/* last 指向缓存中有效数据的末尾 */
b->last += n;
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";
/* 将该连接从 reusable_connections_queue 可重用双向链表中删除 */
ngx_reusable_connection(c, 0);
/* 为当前客户端连接创建并初始化一个 ngx_http_request_t 结构体
* 并将 c->data 指向该结构体 */
c->data = ngx_http_create_request(c);
if (c->data == NULL) {
ngx_http_close_connection(c);
return;
}
/* 设置该读事件的回调处理函数 */
rev->handler = ngx_http_process_request_line;
ngx_http_process_request_line(rev);
}
8. ngx_http_request_t
typedef struct ngx_http_request_s ngx_http_request_t;
struct ngx_http_request_s {
uint32_t signature; /* "HTTP" */
/*
* 这个请求对应的客户端连接
*/
ngx_connection_t *connection;
/*
* 存放指向所有的 HTTP 模块的上下文结构体的指针数组
*/
void **ctx;
/*
* 存放请求对应的存放 main 级别配置结构体的指针数组
*/
void **main_conf;
/*
* 存放请求对应的存放srv级别配置结构体的指针数组
*/
void **srv_conf;
/*
* 存放请求对应的存放loc级别配置结构体的指针数组
*/
void **loc_conf;
/*
* 在接收完 HTTP 头部,第一次在业务上处理 HTTP 请求时,HTTP 框架提供
* 的处理方法是 ngx_http_process_request。但如果该方法无法一次处理完
* 该请求的全部业务,在归还控制权到 epoll 事件模块后,该请求回调时,
* 将通过 ngx_http_request_handler 方法来处理,而这个方法中对于可读事
* 件的处理就是调用 read_event_handler 处理请求,也就是说,HTTP 模块希望
* 在底层处理请求的读事件时,重新实现 read_event_handler 方法
*/
ngx_http_event_handler_pt read_event_handler;
ngx_http_event_handler_pt write_event_handler;
#if (NGX_HTTP_CACHE)
ngx_http_cache_t *cache;
#endif
ngx_http_upstream_t *upstream;
ngx_array_t *upstream_states;
/*
* 表示这个请求的内存池,在 ngx_http_free_request 方法中销毁。
* 它与 ngx_connection_t 中的内存池意义不同,当请求释放时,TCP
* 连接可能并没有关闭,这时请求的内存池会销毁,但 ngx_connection_t
* 的内存池并不会销毁
*/
ngx_pool_t *pool;
/*
* 存储读取到的HTTP头部数据
*/
ngx_buf_t *header_in;
/*
* ngx_http_process_request_headers方法在接收、解析完HTTP请求的头部后,会把解析
* 完的每一个HTTP头部加入到headers_in的headers链表中,同时会构造headers_in中的
* 其他成员
*/
ngx_http_headers_in_t headers_in;
/*
* HTTP模块会把想要发送的HTTP响应信息放到headers_out中,期望HTTP框架将
* headers_out中的成员序列化为HTTP响应包发送给用户
*/
ngx_http_headers_out_t headers_out;
/* 接收HTTP请求中包体的数据结构 */
ngx_http_request_body_t *request_body;
/* 延迟关闭连接的时间 */
time_t lingering_time;
/*
* 当前请求初始化时的时间。start_sec是格林威治时间1970年1月1日0:0:0到
* 当前时间的秒数。如果这个请求是子请求,则该时间是子请求的生成时间;
* 如果这个请求是用户发来的请求,则是在建立起TCP连接后,第一次接收到
* 可读事件时的时间
*/
time_t start_sec;
/*
* 与start_sec配合使用,表示相对于start_sec秒的毫秒偏移量
*/
ngx_msec_t start_msec;
/*
* 以下 9 个成员都是 ngx_http_process_request_lint 方法在接收、解析
* HTTP 请求行时解析出的信息
*/
ngx_uint_t method;
ngx_uint_t http_version;
ngx_str_t request_line;
ngx_str_t uri;
ngx_str_t args;
ngx_str_t exten;
ngx_str_t unparsed_uri;
ngx_str_t method_name;
ngx_str_t http_protocol;
/*
* 表示需要发送给客户端的HTTP响应。out中保存着由headers_out中序列化后的表示HTTP头部
* 的TCP流。在调用ngx_http_output_filter方法后,out中还会保存着待发送的HTTP包体,它是
* 实现异步发送HTTP响应的关键
*/
ngx_chain_t *out;
/*
* 当前请求即可能是用户发来的请求,也可能是派生出的子请求,而main则标识一系列相关的
* 派生子请求的原始请求,我们一般可通过main和当前请求的地址是否相等来判断当前请求是
* 否为用户发来的原始请求
*/
ngx_http_request_t *main;
/*
* 当前请求的父请求。注意,父请求未必是原始请求
*/
ngx_http_request_t *parent;
ngx_http_postponed_request_t *postponed;
ngx_http_post_subrequest_t *post_subrequest;
/*
* 所有的子请求都是通过posted_requests这个单链表来链接起来的,执行post子请求时调用
* 的ngx_http_run_posted_requests方法就是通过遍历该链表来执行子请求的
*/
ngx_http_posted_request_t *posted_requests;
/*
* 全局的ngx_http_phase_engine_t结构体中定义了一个ngx_http_phase_handler_t回调方法
* 组成的数组,而phase_handler成员则与该数组配合使用,表示请求下次应当执行以
* phase_handler 作为序号指定的数组中的回调方法。HTTP框架正是以这种方式把各个HTTP
* 模块集成起来处理请求的
*/
ngx_int_t phase_handler;
/*
* 表示NGX_HTTP_CONTENT_PHASE阶段提供给HTTP模块处理请求的一种方式,content_handler指向
* HTTP模块实现的请求处理方法
*/
ngx_http_handler_pt content_handler;
/*
* 在NGX_HTTP_ACCESS_PHASE阶段需要判断请求是否具有访问权限时,通过access_code来传递HTTP
* 模块的handler回调方法的返回值,如果access_code为0,则表示请求具备访问权限,反之则
* 说明请求不具备访问权限
*/
ngx_uint_t access_code;
ngx_http_variable_value_t *variables;
#if (NGX_PCRE)
ngx_uint_t ncaptures;
int *captures;
u_char *captures_data;
#endif
size_t limit_rate;
size_t limit_rate_after;
/* used to learn the Apache compatible response length without a header */
size_t header_size;
/*
* HTTP请求的全部长度,包括HTTP包体
*/
off_t request_length;
ngx_uint_t err_status;
ngx_http_connection_t *http_connection;
ngx_http_v2_stream_t *stream;
ngx_http_log_handler_pt log_handler;
/*
* 在这个请求中如果打开了某些资源,并需要在请求结束时释放,那么都需要在把定义
* 的释放资源方法添加到cleanup成员中
*/
ngx_http_cleanup_t *cleanup;
/*
* 表示当前请求的引用次数。例如,在使用subrequest功能时,依附在这个请求上的子请求数目会
* 返回到count上,每增加一个子请求,count数就要加1.其中任何一个子请求派生出新的子请求时,
* 对应的原始请求(main指针指向的请求)的count值都要加1.又如,当我们接收HTTP包体时,由于
* 这也是一个异步调用,所有count上也需要加1,这样在结束请求时,就不会在count引用计数未
* 清零时销毁请求。
* 在HTTP模块中每进行一类新的操作,包括为一个请求添加新的事件,或者把一些已经由定时器、
* epoll中移除的事件重新加入其中,都需要把这个请求的引用计数加1,这是因为需要让HTTP
* 框架知道,HTTP模块对于该请求有独立的异步处理机制,将由该HTTP模块决定这个操作什么时候
* 结束,防止在这个操作还未结束时HTTP框架却把这个请求销毁了
*/
unsigned count:16;
unsigned subrequests:8;
unsigned blocked:8;
/* 标志位,为1时表示当前请求正在使用异步文件I/O */
unsigned aio:1;
unsigned http_state:4;
/* URI with "/." and on Win32 with "//" */
unsigned complex_uri:1;
/* URI with "%" */
unsigned quoted_uri:1;
/* URI with "+" */
unsigned plus_in_uri:1;
/* URI with " " */
unsigned space_in_uri:1;
unsigned invalid_header:1;
unsigned add_uri_to_alias:1;
unsigned valid_location:1;
unsigned valid_unparsed_uri:1;
/* 标志位,为1时表示URL发生过rewrite重写 */
unsigned uri_changed:1;
/* 表示使用rewrite重写URL的次数。因为目前最多可以更改10次,所以uri_changes初始化
* 为11,而每重写URL一次就把uri_changes减1,一旦uri_changes等于0,则向用户返回失败*/
unsigned uri_changes:4;
unsigned request_body_in_single_buf:1;
unsigned request_body_in_file_only:1;
unsigned request_body_in_persistent_file:1;
unsigned request_body_in_clean_file:1;
unsigned request_body_file_group_access:1;
unsigned request_body_file_log_level:3;
unsigned request_body_no_buffering:1;
unsigned subrequest_in_memory:1;
unsigned waited:1;
#if (NGX_HTTP_CACHE)
unsigned cached:1;
#endif
#if (NGX_HTTP_GZIP)
unsigned gzip_tested:1;
unsigned gzip_ok:1;
unsigned gzip_vary:1;
#endif
unsigned proxy:1;
unsigned bypass_cache:1;
unsigned no_cache:1;
/*
* instead of using the request context data in
* ngx_http_limit_conn_module and ngx_http_limit_req_module
* we use the single bits in the request structure
*/
unsigned limit_conn_set:1;
unsigned limit_req_set:1;
unsigned pipeline:1;
unsigned chunked:1;
unsigned header_only:1;
unsigned expect_trailers:1;
/* 标志位,为1时表示当前请求是keepalive请求 */
unsigned keepalive:1;
/* 延迟关闭标志位,为1时表示需要延迟关闭。例如,在接收完HTTP头部时如果发现包体
* 存在,该标志位会设为1,而放弃接收包体时会设为0 */
unsigned lingering_close:1;
/* 标志位,为1时表示正在丢弃HTTP请求中的包体 */
unsigned discard_body:1;
unsigned reading_body:1;
/* 标志位,为1时表示请求的当前状态是在做内部跳转 */
unsigned internal:1;
unsigned error_page:1;
unsigned filter_finalize:1;
unsigned post_action:1;
unsigned request_complete:1;
unsigned request_output:1;
/* 标志位,为1时表示发送给客户端的HTTP响应头部已经发送。在调用ngx_http_send_header方法
* 后,若已经成功地启动响应头部发送流程,该标志位就会置为1,用来防止反复地发送头部 */
unsigned header_sent:1;
unsigned expect_tested:1;
unsigned root_tested:1;
unsigned done:1;
unsigned logged:1;
/* 表示缓冲中是否有待发送内容的标志位 */
unsigned buffered:4;
unsigned main_filter_need_in_memory:1;
unsigned filter_need_in_memory:1;
unsigned filter_need_temporary:1;
unsigned allow_ranges:1;
unsigned subrequest_ranges:1;
unsigned single_range:1;
unsigned disable_not_modified:1;
unsigned stat_reading:1;
unsigned stat_writing:1;
unsigned stat_processing:1;
unsigned background:1;
unsigned health_check:1;
/* used to parse HTTP headers */
/* 状态机解析HTTP时使用state来表示当前的解析状态 */
ngx_uint_t state;
ngx_uint_t header_hash;
ngx_uint_t lowcase_index;
u_char lowcase_header[NGX_HTTP_LC_HEADER_LEN];
u_char *header_name_start;
u_char *header_name_end;
u_char *header_start;
u_char *header_end;
/*
* a memory that can be reused after parsing a request line
* via ngx_http_ephmeral_t
*/
u_char *uri_start;
u_char *uri_end;
u_char *uri_ext;
u_char *args_start;
u_char *request_start;
u_char *request_end;
u_char *method_end;
u_char *schema_start;
u_char *schema_end;
u_char *host_start;
u_char *host_end;
u_char *port_start;
u_char *port_end;
unsigned http_minor:16;
unsigned http_major:16;
};
9. ngx_http_create_request
ngx_http_request_t *
ngx_http_create_request(ngx_connection_t *c)
{
ngx_pool_t *pool;
ngx_time_t *tp;
ngx_http_request_t *r;
ngx_http_log_ctx_t *ctx;
ngx_http_connection_t *hc;
ngx_http_core_srv_conf_t *cscf;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_main_conf_t *cmcf;
/* 处理请求的次数加 1 */
c->requests++;
/* 在该函数返回前,data 还是指向 ngx_http_connection_t 结构体 */
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;
}
/* 该请求的内存池,在 ngx_http_free_request 方法中销毁。
* 它与 ngx_connection_t 中的内存池意义不同,当请求释放时,TCP 连接
* 可能并没有关闭,这时请求的内存池会销毁,但 ngx_connection_t 的
* 内存池并不会销毁. */
r->pool = pool;
/* 代表当前 HTTP 连接 */
r->http_connection = hc;
r->signature = NGX_HTTP_MODULE;
/* 指向这个请求对应的客户端连接 */
r->connection = c;
/* 存放请求对应的存放 main 级别配置结构体的指针数组 */
r->main_conf = hc->conf_ctx->main_conf;
/* 存放请求对应的存放 srv 级别配置结构体的指针数组 */
r->srv_conf = hc->conf_ctx->srv_conf;
/* 存放请求对应的存放 loc 级别配置结构体的指针数组 */
r->loc_conf = hc->conf_ctx->loc_conf;
/* 在接收完 HTTP 头部,第一次在业务上处理 HTTP 请求时,HTTP 框架提供的
* 处理方法是 ngx_http_process_request。但如果该方法无法一次处理完该
* 请求的全部业务,在归还控制权到 epoll 事件模块后,该请求回调时,
* 将通过 ngx_http_request_handler 方法来处理,而这个方法中对于可读
* 事件的处理就是调用 read_event_handler 处理请求,也就是说,HTTP 模块
* 希望在底层处理请求的读事件时,重新实现 read_event_handler 方法 */
r->read_event_handler = ngx_http_block_reading;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_set_connection_log(r->connection, clcf->error_log);
/* header_in: 存储读取到的 HTTP 头部数据 */
r->header_in = hc->busy ? hc->busy->buf : c->buffer;
/* headers_out: HTTP 模块会把想要发送的 HTTP 响应信息放到 headers_out 中,
* 期望 HTTP 框架将 headers_out 中的成员序列化为 HTTP 响应包发送给用户 */
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;
}
/* 存放指向所有的 HTTP 模块的上下文结构体的指针数组 */
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
/* 当前请求既可能是用户发来的请求,也可能是派生出的子请求,而 main
* 则标识一系列相关的派生子请求的原始请求,一般可通过 main 和当前
* 请求的地址是否相等来判断当前请求是否为用户发来的原始请求 */
r->main = r;
/* 表示当前请求的引用次数。例如,在使用 subrequest 功能时,依附在
* 这个请求上的子请求数目会返回到 count 上,每增加一个子请求,count
* 数就要加 1. 其中任何一个子请求派生出新的子请求时,对应的原始请求
*(main 指针指向的请求)的 count 值都要加 1。又如,当我们接收 HTTP
* 包体时,由于这也是一个异步调用,所有 count 上也需要加 1,这样在结束
* 请求时,就不会在 count 引用计数未清零时销毁请求。
*
* 在 HTTP 模块中每进行一类新的操作,包括为一个请求添加新的事件,或者把
* 一些已经由定时器、epoll 中移除的事件重新加入其中,都需要把这个请求的
* 引用计数加 1,这是因为需要让 HTTP 框架知道,HTTP 模块对于该请求有
* 独立的异步处理机制,将由该 HTTP 模块决定这个操作什么时候结束,防止
* 在这个操作还未结束时 HTTP 框架却把这个请求销毁了 */
r->count = 1;
tp = ngx_timeofday();
/* 当前请求初始化时的时间。start_sec是格林威治时间1970年1月1日0:0:0到当前时间的秒数。
* 如果这个请求是子请求,则该时间是子请求的生成时间;如果这个请求是用户发来的请求,
* 则是在建立起TCP连接后,第一次接收到可读事件时的时间 */
r->start_sec = tp->sec;
/* 与start_sec配合使用,表示相对于start_sec秒的毫秒偏移量 */
r->start_msec = tp->msec;
r->method = NGX_HTTP_UNKNOWN;
r->http_version = NGX_HTTP_VERSION_10;
/* ngx_http_process_request_headers 方法在接收、解析完 HTTP 请求的
* 头部后,会把解析完的每一个HTTP头部加入到 headers_in 的 headers 链表中,
* 同时会构造 headers_in 中的其他成员 */
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;
/* 表示使用 rewrite 重写 URL 的次数。因为目前最多可以更改 10 次,
* 所以 uri_changes 初始化为 11,而每重写 URL 一次就把 uri_changes
* 减 1,一旦 uri_changes 等于 0,则向用户返回失败 */
r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
/* 表示允许派生子请求的个数,当前最多可为 50,因此该值初始化为 51 */
r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;
/* 设置当前请求的状态为正在读取请求的状态 */
r->http_state = NGX_HTTP_READING_REQUEST_STATE;
ctx = c->log->data;
ctx->request = r;
ctx->current_request = r;
r->log_handler = ngx_http_log_error_handler;
#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;
}
9.1 ngx_list_init
初始化一个链表.
typedef struct ngx_list_part_s ngx_list_part_t;
/* 该结构体抽象为一个 数组,拥有连续的内存 */
struct ngx_list_part_s {
/*
* 指向数组的首地址
*/
void *elts;
/*
* 当前数组中元素个数
*/
ngx_uint_t nelts;
/*
* 下一个链表元素 ngx_list_part_t 的地址
*/
ngx_list_part_t *next;
};
/* 该结构体 ngx_list_t 描述整个链表 */
typedef struct {
/*
* 指向链表中最后一个数组元素
*/
ngx_list_part_t *last;
/*
* 链表的首个数组元素
*/
ngx_list_part_t part;
/*
* ngx_list_part_t 数组中每个元素占用的空间大小,也就是用户要
* 存储的一个数据所占用的字节数必须小于或等于 size
*/
size_t size;
/*
* 链表的数组元素一旦分配后是不可更改的。nalloc 表示每个 ngx_list_part_t
* 数组的容量,即最多可以存储多少个数据
*/
ngx_uint_t nalloc;
/*
* 链表中管理内存分配的内存池对象.
*/
ngx_pool_t *pool;
} ngx_list_t;
static ngx_inline ngx_int_t
ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
{
/* 为该数组 elts 分配 n 个元素大小为 size 的内存 */
list->part.elts = ngx_palloc(pool, n * size);
if (list->part.elts == NULL) {
return NGX_ERROR;
}
/* 刚创建时,该数组中元素个数为 0 */
list->part.nelts = 0;
/* 设置下一个链表为 NULL */
list->part.next = NULL;
list->last = &list->part;
/* 该链表中的每个数组元素的大小 */
list->size = size;
/* 链表的数组元素一旦分配后是不可更改的。nalloc 表示每个
* ngx_list_part_t 数组的容量,即最多可以存储多少个数据 */
list->nalloc = n;
/* 链表中管理内存分配的内存池对象。 */
list->pool = pool;
return NGX_OK;
}
10. ngx_http_process_request_line
该函数时
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;
/* rev->data 指向当前客户端连接对象 ngx_connection_t */
c = rev->data;
/* 由前面知,当接收到客户端的请求数据并为该请求创建一个
* ngx_http_request_t 结构体后,c->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_in 指向的缓存中,若该缓存中
* 已有数据,则直接返回该缓存中数据的大小 */
n = ngx_http_read_request_header(r);
if (n == NGX_AGAIN || n == NGX_ERROR) {
return;
}
}
/* 解析请求行 */
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);
/* 该 HTTP 的方法名 */
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 */
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;
}
ngx_http_process_request(r);
return;
}
/* 初始化该 header_in.headers 链表 */
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;
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]);
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);
}
return;
}
/* NGX_AGAIN: a request line parsing is still incomplete */
/* ngx_http_parse_reqeust_line 方法返回NGX_AGAIN,则表示需要接收更多的字符流,
* 这时需要对header_in缓冲区做判断,检查是否还有空闲的内存,如果还有未使用的
* 内存可以继续接收字符流,否则调用ngx_http_alloc_large_header_buffer方法
* 分配更多的接收缓冲区。到底是分配多大?这有nginx.conf文件中的
* large_client_header_buffers 配置项指定 */
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;
}
}
}
}
10.1 ngx_http_read_request_header
读取请求头,将数据保存到 header_in 指向的缓存中。
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;
n = r->header_in->last - r->header_in->pos;
/* 下面是检查header_in接收缓冲区中是否有未解析的字符流,若有则直接返回,
* 否则调用封装的recv方法把Linux内核套接字缓冲区中的TCP流复制到header_in
* 缓冲区中. */
if (n > 0) {
/* 若 header_in 缓存中已经有数据了,则直接返回 */
return n;
}
/* 若 header_in 缓存中还没有从该连接的socket套接字接收到
* 数据,则下面开始接收数据到 header_in 中 */
/* 若该读事件已经准备好允许消费者模块处理这个事件 */
if (rev->ready) {
n = c->recv(c, r->header_in->last,
r->header_in->end - r->header_in->last);
} else {
n = NGX_AGAIN;
}
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);
}
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;
}
10.2 ngx_http_parse_request_line
ngx_int_t
ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
{
u_char c, ch, *p, *m;
enum {
sw_start = 0,
sw_method,
sw_spaces_before_uri,
sw_schema,
sw_schema_slash,
sw_schema_slash_slash,
sw_host_start,
sw_host,
sw_host_end,
sw_host_ip_literal,
sw_port,
sw_host_http_09,
sw_after_slash_in_uri,
sw_check_uri,
sw_check_uri_http_09,
sw_uri,
sw_http_09,
sw_http_H,
sw_http_HT,
sw_http_HTT,
sw_http_HTTP,
sw_first_major_digit,
sw_major_digit,
sw_first_minor_digit,
sw_minor_digit,
sw_spaces_after_digit,
sw_almost_done
} state;
/* 最开始时,state 为 0 */
state = r->state;
/* 一个个字符的读取 */
for (p = b->pos; p < b->last; p++) {
ch = *p;
switch (state) {
/* HTTP methods: GET, HEAD, POST */
case sw_start:
r->request_start = p;
if (ch == CR || ch == LF) {
break;
}
if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
return NGX_HTTP_PARSE_INVALID_METHOD;
}
state = sw_method;
break;
case sw_method:
/* 循环读,直到遇到空格 */
if (ch == ' ') {
/* 这里读取到 HTTP 请求的方法,GET、POST 等 */
r->method_end = p - 1;
m = r->request_start;
/* 通过读取到的 HTTP 方法的长度值,确定该客户端请求的方法 */
switch (p - m) {
case 3:
if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
/* GET 方法 */
r->method = NGX_HTTP_GET;
break;
}
if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
/* PUT 方法 */
r->method = NGX_HTTP_PUT;
break;
}
break;
case 4:
if (m[1] == 'O') {
if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
r->method = NGX_HTTP_POST;
break;
}
if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
r->method = NGX_HTTP_COPY;
break;
}
if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
r->method = NGX_HTTP_MOVE;
break;
}
if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
r->method = NGX_HTTP_LOCK;
break;
}
} else {
if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
r->method = NGX_HTTP_HEAD;
break;
}
}
break;
case 5:
if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
r->method = NGX_HTTP_MKCOL;
break;
}
if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
r->method = NGX_HTTP_PATCH;
break;
}
if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
r->method = NGX_HTTP_TRACE;
break;
}
break;
case 6:
if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
r->method = NGX_HTTP_DELETE;
break;
}
if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
r->method = NGX_HTTP_UNLOCK;
break;
}
break;
case 7:
if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
{
r->method = NGX_HTTP_OPTIONS;
}
break;
case 8:
if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
{
r->method = NGX_HTTP_PROPFIND;
}
break;
case 9:
if (ngx_str9cmp(m,
'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
{
r->method = NGX_HTTP_PROPPATCH;
}
break;
}
/* 确定该 HTTP 请求的方法后,进入下一个阶段 */
state = sw_spaces_before_uri;
break;
}
if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
return NGX_HTTP_PARSE_INVALID_METHOD;
}
break;
/* space* before URI */
case sw_spaces_before_uri:
if (ch == '/') {
/* 开始解析 uri */
r->uri_start = p;
state = sw_after_slash_in_uri;
break;
}
c = (u_char) (ch | 0x20);
if (c >= 'a' && c <= 'z') {
r->schema_start = p;
state = sw_schema;
break;
}
switch (ch) {
case ' ':
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
case sw_schema:
c = (u_char) (ch | 0x20);
if (c >= 'a' && c <= 'z') {
break;
}
switch (ch) {
case ':':
r->schema_end = p;
state = sw_schema_slash;
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
case sw_schema_slash:
switch (ch) {
case '/':
state = sw_schema_slash_slash;
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
case sw_schema_slash_slash:
switch (ch) {
case '/':
state = sw_host_start;
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
case sw_host_start:
r->host_start = p;
if (ch == '[') {
state = sw_host_ip_literal;
break;
}
state = sw_host;
/* fall through */
case sw_host:
c = (u_char) (ch | 0x20);
if (c >= 'a' && c <= 'z') {
break;
}
if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
break;
}
/* fall through */
case sw_host_end:
r->host_end = p;
switch (ch) {
case ':':
state = sw_port;
break;
case '/':
r->uri_start = p;
state = sw_after_slash_in_uri;
break;
case ' ':
/*
* use single "/" from request line to preserve pointers,
* if request line will be copied to large client buffer
*/
r->uri_start = r->schema_end + 1;
r->uri_end = r->schema_end + 2;
state = sw_host_http_09;
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
case sw_host_ip_literal:
if (ch >= '0' && ch <= '9') {
break;
}
c = (u_char) (ch | 0x20);
if (c >= 'a' && c <= 'z') {
break;
}
switch (ch) {
case ':':
break;
case ']':
state = sw_host_end;
break;
case '-':
case '.':
case '_':
case '~':
/* unreserved */
break;
case '!':
case '$':
case '&':
case '\'':
case '(':
case ')':
case '*':
case '+':
case ',':
case ';':
case '=':
/* sub-delims */
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
case sw_port:
if (ch >= '0' && ch <= '9') {
break;
}
switch (ch) {
case '/':
r->port_end = p;
r->uri_start = p;
state = sw_after_slash_in_uri;
break;
case ' ':
r->port_end = p;
/*
* use single "/" from request line to preserve pointers,
* if request line will be copied to large client buffer
*/
r->uri_start = r->schema_end + 1;
r->uri_end = r->schema_end + 2;
state = sw_host_http_09;
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
/* space+ after "http://host[:port] " */
case sw_host_http_09:
switch (ch) {
case ' ':
break;
case CR:
r->http_minor = 9;
state = sw_almost_done;
break;
case LF:
r->http_minor = 9;
goto done;
case 'H':
r->http_protocol.data = p;
state = sw_http_H;
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
/* check "/.", "//", "%", and "\" (Win32) in URI */
case sw_after_slash_in_uri:
if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
state = sw_check_uri;
break;
}
switch (ch) {
case ' ':
r->uri_end = p;
state = sw_check_uri_http_09;
break;
case CR:
r->uri_end = p;
r->http_minor = 9;
state = sw_almost_done;
break;
case LF:
r->uri_end = p;
r->http_minor = 9;
goto done;
case '.':
r->complex_uri = 1;
state = sw_uri;
break;
case '%':
r->quoted_uri = 1;
state = sw_uri;
break;
case '/':
r->complex_uri = 1;
state = sw_uri;
break;
#if (NGX_WIN32)
case '\\':
r->complex_uri = 1;
state = sw_uri;
break;
#endif
case '?':
r->args_start = p + 1;
state = sw_uri;
break;
case '#':
r->complex_uri = 1;
state = sw_uri;
break;
case '+':
r->plus_in_uri = 1;
break;
case '\0':
return NGX_HTTP_PARSE_INVALID_REQUEST;
default:
state = sw_check_uri;
break;
}
break;
/* check "/", "%" and "\" (Win32) in URI */
case sw_check_uri:
if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
break;
}
switch (ch) {
case '/':
#if (NGX_WIN32)
if (r->uri_ext == p) {
r->complex_uri = 1;
state = sw_uri;
break;
}
#endif
r->uri_ext = NULL;
state = sw_after_slash_in_uri;
break;
case '.':
r->uri_ext = p + 1;
break;
case ' ':
r->uri_end = p;
state = sw_check_uri_http_09;
break;
case CR:
r->uri_end = p;
r->http_minor = 9;
state = sw_almost_done;
break;
case LF:
r->uri_end = p;
r->http_minor = 9;
goto done;
#if (NGX_WIN32)
case '\\':
r->complex_uri = 1;
state = sw_after_slash_in_uri;
break;
#endif
case '%':
r->quoted_uri = 1;
state = sw_uri;
break;
case '?':
r->args_start = p + 1;
state = sw_uri;
break;
case '#':
r->complex_uri = 1;
state = sw_uri;
break;
case '+':
r->plus_in_uri = 1;
break;
case '\0':
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
/* space+ after URI */
case sw_check_uri_http_09:
switch (ch) {
case ' ':
break;
case CR:
r->http_minor = 9;
state = sw_almost_done;
break;
case LF:
r->http_minor = 9;
goto done;
case 'H':
r->http_protocol.data = p;
state = sw_http_H;
break;
default:
r->space_in_uri = 1;
state = sw_check_uri;
p--;
break;
}
break;
/* URI */
case sw_uri:
if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
break;
}
switch (ch) {
case ' ':
r->uri_end = p;
state = sw_http_09;
break;
case CR:
r->uri_end = p;
r->http_minor = 9;
state = sw_almost_done;
break;
case LF:
r->uri_end = p;
r->http_minor = 9;
goto done;
case '#':
r->complex_uri = 1;
break;
case '\0':
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
/* space+ after URI */
case sw_http_09:
switch (ch) {
case ' ':
break;
case CR:
r->http_minor = 9;
state = sw_almost_done;
break;
case LF:
r->http_minor = 9;
goto done;
case 'H':
r->http_protocol.data = p;
state = sw_http_H;
break;
default:
r->space_in_uri = 1;
state = sw_uri;
p--;
break;
}
break;
case sw_http_H:
switch (ch) {
case 'T':
state = sw_http_HT;
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
case sw_http_HT:
switch (ch) {
case 'T':
state = sw_http_HTT;
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
case sw_http_HTT:
switch (ch) {
case 'P':
state = sw_http_HTTP;
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
case sw_http_HTTP:
switch (ch) {
case '/':
state = sw_first_major_digit;
break;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
/* first digit of major HTTP version */
case sw_first_major_digit:
if (ch < '1' || ch > '9') {
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
r->http_major = ch - '0';
if (r->http_major > 1) {
return NGX_HTTP_PARSE_INVALID_VERSION;
}
state = sw_major_digit;
break;
/* major HTTP version or dot */
case sw_major_digit:
if (ch == '.') {
state = sw_first_minor_digit;
break;
}
if (ch < '0' || ch > '9') {
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
r->http_major = r->http_major * 10 + ch - '0';
if (r->http_major > 1) {
return NGX_HTTP_PARSE_INVALID_VERSION;
}
break;
/* first digit of minor HTTP version */
case sw_first_minor_digit:
if (ch < '0' || ch > '9') {
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
r->http_minor = ch - '0';
state = sw_minor_digit;
break;
/* minor HTTP version or end of request line */
case sw_minor_digit:
if (ch == CR) {
state = sw_almost_done;
break;
}
if (ch == LF) {
goto done;
}
if (ch == ' ') {
state = sw_spaces_after_digit;
break;
}
if (ch < '0' || ch > '9') {
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
if (r->http_minor > 99) {
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
r->http_minor = r->http_minor * 10 + ch - '0';
break;
case sw_spaces_after_digit:
switch (ch) {
case ' ':
break;
case CR:
state = sw_almost_done;
break;
case LF:
goto done;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
break;
/* end of request line */
case sw_almost_done:
r->request_end = p - 1;
switch (ch) {
case LF:
goto done;
default:
return NGX_HTTP_PARSE_INVALID_REQUEST;
}
}
}
b->pos = p;
r->state = state;
return NGX_AGAIN;
done:
/* 解析请求行结束 */
b->pos = p + 1;
if (r->request_end == NULL) {
r->request_end = p;
}
r->http_version = r->http_major * 1000 + r->http_minor;
r->state = sw_start;
if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
return NGX_HTTP_PARSE_INVALID_09_METHOD;
}
return NGX_OK;
}
10.3 ngx_http_process_request_uri
解析请求的 uri。
ngx_int_t
ngx_http_process_request_uri(ngx_http_request_t *r)
{
ngx_http_core_srv_conf_t *cscf;
if (r->args_start) {
r->uri.len = r->args_start - 1 - r->uri_start;
} else {
/* 该请求 uri 中没有参数时的长度 */
r->uri.len = r->uri_end - r->uri_start;
}
/* 若该 uri 是复杂,则需要进行解析 */
if (r->complex_uri || r->quoted_uri) {
r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1);
if (r->uri.data == NULL) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
if (ngx_http_parse_complex_uri(r, cscf->merge_slashes) != NGX_OK) {
r->uri.len = 0;
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid request");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
} else {
r->uri.data = r->uri_start;
}
r->unparsed_uri.len = r->uri_end - r->uri_start;
r->unparsed_uri.data = r->uri_start;
r->valid_unparsed_uri = r->space_in_uri ? 0 : 1;
if (r->uri_ext) {
if (r->args_start) {
r->exten.len = r->args_start - 1 - r->uri_ext;
} else {
r->exten.len = r->uri_end - r->uri_ext;
}
r->exten.data = r->uri_ext;
}
if (r->args_start && r->uri_end > r->args_start) {
r->args.len = r->uri_end - r->args_start;
r->args.data = r->args_start;
}
#if (NGX_WIN32)
{
u_char *p, *last;
p = r->uri.data;
last = r->uri.data + r->uri.len;
while (p < last) {
if (*p++ == ':') {
/*
* this check covers "::$data", "::$index_allocation" and
* ":$i30:$index_allocation"
*/
if (p < last && *p == '$') {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent unsafe win32 URI");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
}
}
p = r->uri.data + r->uri.len - 1;
while (p > r->uri.data) {
if (*p == ' ') {
p--;
continue;
}
if (*p == '.') {
p--;
continue;
}
break;
}
if (p != r->uri.data + r->uri.len - 1) {
r->uri.len = p + 1 - r->uri.data;
ngx_http_set_exten(r);
}
}
#endif
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http uri: \"%V\"", &r->uri);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http args: \"%V\"", &r->args);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http exten: \"%V\"", &r->exten);
return NGX_OK;
}
10.4 ngx_http_process_request_headers
在解析完请求行后,开始处理请求的头部数据。
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;
/* 在该循环中,将 HTTP 请求头一个个的解析出来,并添加到
* headers_in.header 链表中 */
for ( ;; ) {
if (rc == NGX_AGAIN) {
/* 若当前 heder_in 指向的缓存已全部使用完,则需要分配更多的内存 */
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;
}
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;
}
}
/* 读取数据,若 header_in 指向的缓存中仍然有未处理的数据,则
* 直接返回,否则需要从 socket 中读取数据 */
n = ngx_http_read_request_header(r);
if (n == NGX_AGAIN || n == NGX_ERROR) {
return;
}
}
/* the host header could change the server configuration context */
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
/* 该函数是将头部数据解析出一行 */
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 */
/* 在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;
}
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) {
/* r->lowcase_header 存放的上面解析出来的 h->key.data
* 的小写字符串 */
ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
} else {
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
}
/* 在 headers_in_hash 指向的 hash 表中寻找是否与该 lowcase_key
* 相同的项*/
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;
}
/* 若解析HTTP的头部结束 */
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;
/* 对解析后的 HTTP 头部字段的一些处理 */
rc = ngx_http_process_request_header(r);
if (rc != NGX_OK) {
return;
}
/* 在解析并处理HTTP的头部数据后,开始处理该 HTTP 请求 */
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 */
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid header line");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return;
}
}
10.5 ngx_http_process_request
void
ngx_http_process_request(ngx_http_request_t *r)
{
ngx_connection_t *c;
c = r->connection;
#if (NGX_HTTP_SSL)
if (r->http_connection->ssl) {
long rc;
X509 *cert;
ngx_http_ssl_srv_conf_t *sscf;
if (c->ssl == NULL) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent plain HTTP request to HTTPS port");
ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
return;
}
sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
if (sscf->verify) {
rc = SSL_get_verify_result(c->ssl->connection);
if (rc != X509_V_OK
&& (sscf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))
{
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client SSL certificate verify error: (%l:%s)",
rc, X509_verify_cert_error_string(rc));
ngx_ssl_remove_cached_session(sscf->ssl.ctx,
(SSL_get0_session(c->ssl->connection)));
ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);
return;
}
if (sscf->verify == 1) {
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent no required SSL certificate");
ngx_ssl_remove_cached_session(sscf->ssl.ctx,
(SSL_get0_session(c->ssl->connection)));
ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);
return;
}
X509_free(cert);
}
}
}
#endif
/* 由于现在已经开始准备调用各 HTTP 模块处理请求了,不再存在
* 接收 HTTP 请求头部超时的问题,因此需要从定时器中将当前
* 连接的读事件移除 */
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
/* 设置该读、写事件的回调函数 */
c->read->handler = ngx_http_request_handler;
c->write->handler = ngx_http_request_handler;
/* 设置 ngx_http_request_t 结构体的 read_event_handler 方法为
* ngx_http_block_reading。当再次有读事件到来时,这个方法可以
* 认为不做任何事,它的意义在于,目前已经开始处理 HTTP 请求,
* 除非某个 HTTP 模块重新设置了 read_event_handler 方法,否则
* 任何读事件都将得不到处理,也可以认为读事件被阻塞了 */
r->read_event_handler = ngx_http_block_reading;
ngx_http_handler(r);
ngx_http_run_posted_requests(c);
}
10.6 ngx_http_handler
void
ngx_http_handler(ngx_http_request_t *r)
{
ngx_http_core_main_conf_t *cmcf;
r->connection->log->action = NULL;
/* 如果 internal 标志位为 1,则表示当前需要做内部跳转,将要把
* 结构体中的 phase_handler 序号置为 server_rewrite_index. */
if (!r->internal) {
/* 当 internal 标志位为 0 时,表示不需要重定向(如刚开始处理请求时),
* 将 phase_handler 序号置为 0,意味着从 ngx_http_phase_engine_t 指定
* 数组的第一个回调方法开始执行 */
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:
/* 标志位,为 1 表示当前请求是 keepalive 请求,即长连接 */
r->keepalive = 1;
break;
}
/* 延迟关闭标志位,为 1 时表示需要延迟关闭。例如,在接收完
* HTTP 头部时如果发现包体存在,该标志位为设为 1,而放弃接收
* 包体时会设为 0 */
r->lingering_close = (r->headers_in.content_length_n > 0
|| r->headers_in.chunked);
/* 置为 0,表示从 ngx_http_phase_engine_t 指定数组的第一个回调
* 方法开始执行 */
r->phase_handler = 0;
} else {
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
/* 这里,把phase_handler序号设为server_rewrite_index,这意味着
* 无论之前执行到哪一个阶段,马上都要重新从NGX_HTTP_SERVER_REWRITE_PHASE
* 阶段开始再次执行,这是Nginx的请求可以反复rewrite重定向的基础 */
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 请求的各个阶段 */
ngx_http_core_run_phases(r);
}
10.7 ngx_http_core_run_phases
/* ngx_http_phase_engine_t结构体就是所有的ngx_http_phase_handler_t组成的数组 */
typedef struct {
/* handlers是由ngx_http_phase_handler_t构成的数组首地址,它表示一个请求可能
* 经历的所有ngx_http_handler_pt处理方法 */
ngx_http_phase_handler_t *handlers;
/* 表示NGX_HTTP_SERVER_REWRITE_PHASE阶段第1个ngx_http_phase_handler_t处理方法
* 在handlers数组中的序号,用于在执行HTTP请求的任何阶段中快速跳转到
* NGX_HTTP_SERVER_REWRITE_PHASE阶段处理请求 */
ngx_uint_t server_rewrite_index;
/* 表示NGX_HTTP_REWRITE_PHASE阶段第1个ngx_http_phase_handler_t处理方法
* 在handlers数组中的序号,用于在执行HTTP请求的任何阶段中快速跳转到
* NGX_HTTP_REWRITE_PHASE阶段处理请求 */
ngx_uint_t location_rewrite_index;
}ngx_http_phase_engine_t;
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);
/* handlers 是由 ngx_http_phase_handler_t 构成的数组首地址,它表示
* 一个请求可能经历的所有 ngx_http_handler_pt 处理方法 */
ph = cmcf->phase_engine.handlers;
/*
* 在处理到某一个 HTTP 阶段时,HTTP 框架将会在 checker 方法已实现的前提下
* 首先调用 checket 方法来处理请求,而不会直接调用任何阶段中的handler方法,
* 只有在checket方法中才会去调用handler方法。因此,事实上所有的checker方法
* 都是由框架中的 ngx_http_core_module 模块实现的,且普通的 HTTP 模块无法
* 重定义 checket 方法 */
while (ph[r->phase_handler].checker) {
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
if (rc == NGX_OK) {
return;
}
}
}
下面开始执行 HTTP 的各个阶段,此时会调用到各个 HTTP 模块介入到该阶段的回调函数。
首先,基于当前配置以及访问静态网页的请求,Nginx 会依次执行以下 HTTP 的阶段(虽然划分了 11 个阶段,但不会每个阶段都执行到).
注意,有一个原则,就是若该阶段实现有 checker 函数的话,会首先调用 checker 函数来处理请求,而不会直接调用任何阶段中的 handler 方法,只有在 checker 函数中才会去调用 handler 方法。
Nginx-HTTP之静态网页访问流程分析一的更多相关文章
- Nginx-HTTP之静态网页访问流程分析二
11. HTTP 阶段执行 下面会依次执行以下阶段: NGX_HTTP_SERVER_REWRITE_PHASE: 在将请求的 URI 与 location 表达式匹配前,修改请求的 URI (所谓重 ...
- Nginx 多进程连接请求/事件分发流程分析
Nginx使用多进程的方法进行任务处理,每个worker进程只有一个线程,单线程循环处理全部监听的事件.本文重点分析一下多进程间的负载均衡问题以及Nginx多进程事件处理流程,方便大家自己写程序的时候 ...
- TI IPNC Web网页之流程分析
流程 Appro IPNC使用的web服务器是boa. 请仔细理解下面这段话. boa这个web服务器是GUI界面和IPNC应用程序之间的通信的桥梁.它的责任是从web GUI中接收HTTP请求,并且 ...
- 【TCP/IP】【网络基础】网页访问流程
引用自 <鸟哥的linux私房菜> http://cn.linux.vbird.org/linux_server/0110network_basic_1.php#ps7 那 TCP/IP ...
- 通过http、https域名访问静态网页、nginx配置负载均衡(nginx配置)
很多场景下需要可以通过浏览器访问静态网页,不想把服务器ip地址直接暴露出来,通过nginx可以解决这个问题. 实现http域名访问静态网页 1.域名解析配置(本文都是以阿里云为例,其他平台,操作步骤类 ...
- web理论知识--网页访问过程(附有Django的web项目访问流程)
当我们闲暇之余想上网看看新闻,或者看个电影,通常的操作是:打开电脑.打开浏览器.输入网址.浏览页面信息.点击自己感兴趣的连接......那么有没有想过,这些网页从哪里来的?过程中计算机又做了什么事情了 ...
- 用户对动态PHP网页访问过程,以及nginx解析php步骤
www.example.com | Nginx | 路由到www.example.com/index.php | 加载nginx的fast-cgi模块 | fast-cgi监听127.0.0.1:90 ...
- 关于linux下部署JavaWeb项目,nginx负责静态资源访问,tomcat负责处理动态请求的nginx配置
1.项目的运行环境 linux版本 [root@localhost ~]# cat /proc/version Linux version -.el6.x86_64 (mockbuild@x86-.b ...
- Nginx 优化静态文件访问
简介 Web 开发中需要的静态文件有:CSS.JS.字体.图片,可以通过web框架进行访问,但是效率不是最优的. Nginx 对于处理静态文件的效率要远高于 Web 框架,因为可以使用 gzip 压缩 ...
随机推荐
- EF的导航属性
在EF中,外键被称为导航属性. 在EF core中,查询的时候默认是只查自身而不会去查询外键表的.如果想要让查询结果包含外键实体,则需要使用include方法来让查询结果包含外键实体.如 db.Stu ...
- 如何确定asp.net请求生命周期的当前处理事件
1 首先在全局应用程序里面添加如下代码 using System; using System.Collections.Generic; using System.Linq; using System. ...
- Redis的最常见面试问题
Redis的那些最常见面试问题[转] 1.什么是redis? Redis 是一个基于内存的高性能key-value数据库. 2.Reids的特点 Redis本质上是一个Key-Value类型的内存数据 ...
- 解释mysql 语句
一.在我们创建mysql数据库的时候我们经常会用到这句SQL: CREATE DATABASE TEST DEFAULT CHARACTER SET utf8 COLLATE utf8_general ...
- shell脚本——字符串
printf printf "%-10s %-10s %-10s\n" NO Name Height printf "%-10s %-10s %-10d\n&quo ...
- java动态代理框架
java动态代理是一个挺有意思的东西,他有时候可以被使用的很灵活.像rpc的调用,调用方只是定义的一个接口,动态代理让他匹配上对应的不同接口:mybatis内部的实现,编码时,只是实 ...
- docker 查看系统进程pid
docker inspect -f '{{.State.Pid}} {{.Id}}' $(docker ps -a -q)
- C# 内存管理和指针 (13)
本章要点 运行库在栈和堆上分配空间 垃圾回收 使用析构函数 和 SYstem.IDisposable 接口来释放非托管的资源 C#中使用指针的语法 使用指针实现基于栈的高性能数组 值类型数据 程序第一 ...
- 完整开发vue后台管理系统小结
最近业余帮朋友做两个vue项目,一个是面向用户纯展示系列的(后统称A项目),一个是后端管理系统类的(后统称B项目).两者在技术上都没难度,这里对开发过程遇到的问题.取舍等做一个小节. 关于项目搭建 目 ...
- Java笔记(基础第二篇)
声明数组 数组元素类型 数组名字[]; 数组元素类型[] 数组名字; 分配内存空间 数组名字 = new 数组元素类型[数组元素的个数] 其中使用new关键字为数组分配内存时,数组中各个元素的初始化值 ...