假设访问静态网页的配置如下:
worker_processes 1;
error_log stderr debug;
daemon off;
master_process on;events {worker_connections 1024;
}http {include pes;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;}}
}
假设在浏览器输入如下指令:serverip:8080/index.html
,Nginx 服务器即会返回所请求的页面。大致流程如下。
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
};
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;
#endifdefault: /* 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;
#endifdefault: /* 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-& == 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;}}
#endifif (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;}
}
添加一个定时器事件,超时时间为 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;
}
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}
}
/** 将读事件添加到事件驱动模块中,这样该事件对应的 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 回调函数。
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);
}
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;
#endifngx_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;
#endifsize_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;
#endifunsigned 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;
};
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->ailers, 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-<s* 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->t_length_n = -1;r->headers_in.keep_alive_n = -1;r->t_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);
#endifreturn r;
}
初始化一个链表.
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-<s = 0;/* 设置下一个链表为 NULL */list-& = NULL;list->last = &list->part;/* 该链表中的每个数组元素的大小 */list->size = size;/* 链表的数组元素一旦分配后是不可更改的。nalloc 表示每个 * ngx_list_part_t 数组的容量,即最多可以存储多少个数据 */list->nalloc = n;/* 链表中管理内存分配的内存池对象。 */list->pool = pool;return NGX_OK;
}
该函数时
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方法* 分配更多的接收缓冲区。到底是分配多大?这有f文件中的* 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;}}}
}
读取请求头,将数据保存到 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;
}
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 "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;
#endifcase '?':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 '