1.模块依赖
2. 模块的初始化
2.1 location的定义
location的定义包含以下几种
location [ = | ~ | ~* | ^~ ] uri { ... }
location @name { ... }
=:表示精确匹配,只有请求的url路径与后面的字符串完全相等时,才会命中,不支持location嵌套
~:表示使用正则定义的,区分大小写
~*:表示是使用正则定义的,不区分大小写
^~:表示该符号后面的字符是最佳匹配,采用该规则,不再进行后续的查找
@name:用于定义一个内部 Location 块,该块不能被外部 Client 所访问,只能被 NGINX 内部配置指令所访问,比如 try_files 或者error_page。其修饰的location不能嵌套到其它location,也不能再嵌套其它location,即只能是server这一层的
2.2 分配ngx_http_conf_ctx_t
2.2.1 ngx_http_block
其是在解析配置文件中的http分配
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
ctx->main_conf = ngx_pcalloc(cf->pool,
sizeof(void *) * ngx_http_max_module);
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
2.2.2 ngx_http_core_location
其是在解析配置文件中的http块内location时分配
其中main_conf,srv_conf是延用上一层级的,loc_conf会再一次分配内存(每一层级location会再次分配)
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
pctx = cf->ctx;
ctx->main_conf = pctx->main_conf;
ctx->srv_conf = pctx->srv_conf;
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
同时也会遍历模块调用create_loc_conf创建location的配置
for (i = 0; cf->cycle->modules[i]; i++) {
if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = cf->cycle->modules[i]->ctx;
if (module->create_loc_conf) {
ctx->loc_conf[cf->cycle->modules[i]->ctx_index] =
module->create_loc_conf(cf);
if (ctx->loc_conf[cf->cycle->modules[i]->ctx_index] == NULL) {
return NGX_CONF_ERROR;
}
}
}
设置http_core_module配置的loc_conf来源
clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
clcf->loc_conf = ctx->loc_conf;
2.3 ngx_http_add_location
构造ngx_http_location_queue_t,将当前ngx_http_core_loc_conf_t添加到上一层级ngx_http_core_loc_conf_t中的location队列中。如果是精确匹配,正则,有名或者是无名,构造的ngx_http_location_queue_t的exact来存放ngx_http_core_loc_conf_t配置,否则使用ngx_http_location_queue_t的inclusive来存放ngx_http_core_loc_conf_t配置
if (clcf->exact_match
#if (NGX_PCRE)
|| clcf->regex
#endif
|| clcf->named || clcf->noname)
{
lq->exact = clcf;
lq->inclusive = NULL;
} else {
lq->exact = NULL;
lq->inclusive = clcf;
}
将构造的队列添加到上一层级的队列中
ngx_queue_insert_tail(*locations, &lq->queue);
2.4 主配置中的server
在ngx_http_block中会分配main_conf
ctx->main_conf = ngx_pcalloc(cf->pool,
sizeof(void *) * ngx_http_max_module);
在处理server时(ngx_http_core_server),当前层的ctx中的main_conf会延用上一层的main_conf
http_ctx = cf->ctx;
ctx->main_conf = http_ctx->main_conf;
将server放入cmcf->server中
cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
cscf->ctx = ctx;
cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
cscfp = ngx_array_push(&cmcf->servers);
if (cscfp == NULL) {
return NGX_CONF_ERROR;
}
*cscfp = cscf;
2.5 初始化location(ngx_http_init_locations)
处理ngx_http_core_srv_conf_t中的有名location(named_locations)以及ngx_http_core_loc_conf_t中的正则location(regex_locations),在作割裂之前,会先对ngx_http_core_loc_conf_t中的locations排序,使用的排序规则为ngx_http_cmp_locations,即按照exact(sorted) -> inclusive(sorted) -> regex -> named -> noname的原则进行排序,经过处理后,原先的location队列就只剩下经过排序后的exact以及inclusive类型的location了。这两类location对应配置文件中的定义,就是不含修饰符的location,带有=和^~前缀的location。
2.6 将queue转为list(ngx_http_create_locations_list)
将locations queue变成locations list
2.7 创建location的二叉查找树(ngx_http_create_locations_tree)
创建精确匹配location的二叉查找树,使用ngx_queue_middle(其时间度为O(n))得到location_list中的中间位置,如果location_list的元素个数为奇数,则是中间的一个,否则是后半部分的第一个。
ngx_queue_t *
ngx_queue_middle(ngx_queue_t *queue)
{
ngx_queue_t *middle, *next;
middle = ngx_queue_head(queue);
if (middle == ngx_queue_last(queue)) {
return middle;
}
next = ngx_queue_head(queue);
for ( ;; ) {
middle = ngx_queue_next(middle);
next = ngx_queue_next(next);
if (next == ngx_queue_last(queue)) {
return middle;
}
next = ngx_queue_next(next);
if (next == ngx_queue_last(queue)) {
return middle;
}
}
}
使用递归来构建二叉查找树
/*
* to keep cache locality for left leaf nodes, allocate nodes in following
* order: node, left subtree, right subtree, inclusive subtree
*/
static ngx_http_location_tree_node_t *
ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations,
size_t prefix)
{
size_t len;
ngx_queue_t *q, tail;
ngx_http_location_queue_t *lq;
ngx_http_location_tree_node_t *node;
q = ngx_queue_middle(locations);
lq = (ngx_http_location_queue_t *) q;
len = lq->name->len - prefix;
node = ngx_palloc(cf->pool,
offsetof(ngx_http_location_tree_node_t, name) + len);
if (node == NULL) {
return NULL;
}
node->left = NULL;
node->right = NULL;
node->tree = NULL;
node->exact = lq->exact;
node->inclusive = lq->inclusive;
node->auto_redirect = (u_char) ((lq->exact && lq->exact->auto_redirect)
|| (lq->inclusive && lq->inclusive->auto_redirect));
node->len = (u_short) len;
ngx_memcpy(node->name, &lq->name->data[prefix], len);
ngx_queue_split(locations, q, &tail);
if (ngx_queue_empty(locations)) {
/*
* ngx_queue_split() insures that if left part is empty,
* then right one is empty too
*/
goto inclusive;
}
node->left = ngx_http_create_locations_tree(cf, locations, prefix);
if (node->left == NULL) {
return NULL;
}
ngx_queue_remove(q);
if (ngx_queue_empty(&tail)) {
goto inclusive;
}
node->right = ngx_http_create_locations_tree(cf, &tail, prefix);
if (node->right == NULL) {
return NULL;
}
inclusive:
if (ngx_queue_empty(&lq->list)) {
return node;
}
node->tree = ngx_http_create_locations_tree(cf, &lq->list, prefix + len);
if (node->tree == NULL) {
return NULL;
}
return node;
}
2.8 http的处理阶段
包含11个阶段
枚举 |
名称 |
checker方法 |
NGX_HTTP_POST_READ_PHASE |
在接收到完整的HTTP头部后处理的HTTP阶段 |
ngx_http_core_generic_phase |
NGX_HTTP_SERVER_REWRITE_PHASE |
在将请求的URI与location表达式匹配前, 修改请求的URI(所谓的重定向) 是一个独立的HTTP阶段 |
ngx_http_core_rewrite_phase |
NGX_HTTP_FIND_CONFIG_PHASE |
根据请求的URI寻找匹配的location表达式, 这个阶段只能由ngx_http_core_module模块实现, 不建议其他HTTP模块重新定义这一阶段的行为 |
ngx_http_core_find_config_phase |
NGX_HTTP_REWRITE_PHASE |
在NGX_HTTP_FIND_CONFIG_PHASE阶段寻找到匹配的location之后再修改请求的URI |
ngx_http_core_rewrite_phase |
NGX_HTTP_POST_REWRITE_PHASE |
这一阶段是用于在rewrite重写URL后, 防止错误的 nginx.conf配置导致死循环(递归地修改URI) , 因此, 这一阶段仅由ngx_http_core_module模块处理。 目前, 控制死循环的方式很简单, 首先检查 rewrite的次数, 如果一个请求超过10次重定向 ,就认为进入了rewrite死循环, 这时在 NGX_HTTP_POST_REWRITE_PHASE阶段就会向用户返回500, 表示服务器内部错误 |
ngx_http_core_post_rewrite_phase |
NGX_HTTP_PREACCESS_PHASE |
表示在处理NGX_HTTP_ACCESS_PHASE阶段决定请求的访问权限前HTTP模块可以介入的处理阶段 |
ngx_http_core_generic_phase |
NGX_HTTP_ACCESS_PHASE |
这个阶段用于让HTTP模块判断是否允许这个请求访问 Nginx服务器 |
ngx_http_core_access_phase |
NGX_HTTP_POST_ACCESS_PHASE |
在NGX_HTTP_ACCESS_PHASE阶段中, 当 HTTP模块的handler处理函数返回不允许访问的错误码时(实际就是NGX_HTTP_FORBIDDEN或者 NGX_HTTP_UNAUTHORIZED) , 这里将负责向用户发送拒绝服务的错误响应。 因此, 这个阶段实际上用于给NGX_HTTP_ACCESS_PHASE阶段收尾 |
ngx_http_core_post_access_phase |
NGX_HTTP_PRECONTENT_PHASE |
http请求内容前置处理 |
ngx_http_core_generic_phase |
NGX_HTTP_CONTENT_PHASE |
用于处理HTTP请求内容的阶段, 这是大部分 HTTP模块最愿意介入的阶段 |
ngx_http_core_content_phase |
NGX_HTTP_LOG_PHASE |
处理完请求后记录日志的阶段 |
ngx_http_core_generic_phase |
2.9 阶段处理器的初始化
ngx_http_init_phases初始化以下阶段的handlers
NGX_HTTP_POST_READ_PHASE
NGX_HTTP_SERVER_REWRITE_PHASE
NGX_HTTP_REWRITE_PHASE
NGX_HTTP_PREACCESS_PHASE
NGX_HTTP_ACCESS_PHASE
NGX_HTTP_PRECONTENT_PHASE
NGX_HTTP_CONTENT_PHASE
NGX_HTTP_LOG_PHASE
static ngx_int_t
ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
cf->pool, 2, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_PRECONTENT_PHASE].handlers,
cf->pool, 2, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,
cf->pool, 4, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
return NGX_OK;
}
2.10 配置后置处理(postconfiguration)
遍历调用http模块的postconfiguration,用来注册阶段的handler
for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = cf->cycle->modules[m]->ctx;
if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
2.11 阶段引擎handler的初始化
将各个不同阶段的handler汇聚成一个处理链表
static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
ngx_int_t j;
ngx_uint_t i, n;
ngx_uint_t find_config_index, use_rewrite, use_access;
ngx_http_handler_pt *h;
ngx_http_phase_handler_t *ph;
ngx_http_phase_handler_pt checker;
cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
find_config_index = 0;
use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;
n = 1 /* find config phase */
+ use_rewrite /* post rewrite phase */
+ use_access; /* post access phase */
for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
n += cmcf->phases[i].handlers.nelts;
}
ph = ngx_pcalloc(cf->pool,
n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
if (ph == NULL) {
return NGX_ERROR;
}
cmcf->phase_engine.handlers = ph;
n = 0;
for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
h = cmcf->phases[i].handlers.elts;
switch (i) {
case NGX_HTTP_SERVER_REWRITE_PHASE:
if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
cmcf->phase_engine.server_rewrite_index = n;
}
checker = ngx_http_core_rewrite_phase;
break;
case NGX_HTTP_FIND_CONFIG_PHASE:
find_config_index = n;
ph->checker = ngx_http_core_find_config_phase;
n++;
ph++;
continue;
case NGX_HTTP_REWRITE_PHASE:
if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
cmcf->phase_engine.location_rewrite_index = n;
}
checker = ngx_http_core_rewrite_phase;
break;
case NGX_HTTP_POST_REWRITE_PHASE:
if (use_rewrite) {
ph->checker = ngx_http_core_post_rewrite_phase;
ph->next = find_config_index;
n++;
ph++;
}
continue;
case NGX_HTTP_ACCESS_PHASE:
checker = ngx_http_core_access_phase;
n++;
break;
case NGX_HTTP_POST_ACCESS_PHASE:
if (use_access) {
ph->checker = ngx_http_core_post_access_phase;
ph->next = n;
ph++;
}
continue;
case NGX_HTTP_CONTENT_PHASE:
checker = ngx_http_core_content_phase;
break;
default:
checker = ngx_http_core_generic_phase;
}
n += cmcf->phases[i].handlers.nelts;
for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) {
ph->checker = checker;
ph->handler = h[j];
ph->next = n;
ph++;
}
}
return NGX_OK;
}
2.12 初始监听端口, 服务以及监听回调
ngx_http_optimize_servers中的ngx_http_add_listening会设置端口的回调
ls->handler = ngx_http_init_connection;
3. 运行时的处理
连接上的读写回调
状态 |
读handler |
写handler |
连接建立后 |
ngx_http_wait_request_handler |
ngx_http_empty_handler |
读取请求行 |
ngx_http_process_request_line |
ngx_http_empty_handler |
读取请求头 |
ngx_http_process_request_headers |
ngx_http_empty_handler |
处理请求 |
ngx_http_request_handler |
ngx_http_request_handler |
http请求的读写回调
状态 |
读handler |
写handler |
业务处理开始 |
ngx_http_block_reading |
ngx_http_core_run_phases |
3.1 accept事件处理
在处理accept连接事件时,会调用ngx_listening_t的回调handler函数ngx_http_init_connection
对于新分配的连接,如果读事件的ready为1,即iocp或者延时的accept事件,在有使用accept锁情况 下,将事件放入posted_events队列中,否则直接调用事件的回调handler
if (rev->ready) {
/* the deferred accept(), iocp */
if (ngx_use_accept_mutex) {
ngx_post_event(rev, &ngx_posted_events);
return;
}
rev->handler(rev);
return;
}
如果读事件的ready不为1,则将事件加入定时器的红黑树中。定时器超时后,就会调用它的 handler ngx_http_wait_request_handler 函数。
ngx_add_timer(rev, cscf->client_header_timeout);
将连接设置为可重用,因为该连接上还没有请求到来,所以当连接池中的连接不够用时,就可以重用这个连接。将当前connection添加可重用的连接队列中,同时可重用连接数加1
ngx_reusable_connection(c, 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);
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 */
ngx_queue_insert_head(
(ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);
ngx_cycle->reusable_connections_n++;
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_waiting, 1);
#endif
}
}
ngx_handle_read_event将分配连接的事件添加到事件驱动模块中
ngx_int_t
ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)
{
if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
/* kqueue, 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;
}
3.2 首次可读事件处理
是通过ngx_http_wait_request_handler来处理
首先从网络上读取数据到连接中的buffer
ngx_connection_t *c;
ngx_buf_t *b;
c = rev->data;
b = c->buffer;
if (b == NULL) {
b = ngx_create_temp_buf(c->pool, size);
if (b == NULL) {
ngx_http_close_connection(c);
return;
}
c->buffer = b;
} else if (b->start == NULL) {
b->start = ngx_palloc(c->pool, size);
if (b->start == NULL) {
ngx_http_close_connection(c);
return;
}
b->pos = b->start;
b->last = b->start;
b->end = b->last + size;
}
n = c->recv(c, b->last, size);
b->last += n;
在可重用连接中删除当前连接
ngx_reusable_connection(c, 0);
创建http_request,在创建请求中,会将上面读取的缓冲区放在ngx_http_request_t中的header_in用于处理请求头
c->data = ngx_http_create_request(c);
处理请求头,同时将当前连接读事件的回调函数设置为ngx_http_process_request_line,用于处理单次接收的数据不完整
rev->handler = ngx_http_process_request_line;
ngx_http_process_request_line(rev);
3.3 请求行的处理
是通过ngx_http_process_request_line来处理的
先解析请求行
rc = ngx_http_parse_request_line(r, r->header_in);
在解析请求行中,状态变换为
在解析请求行中,会有状态sw_http_09表示http的0.9版本
HTTP 0.9 请求行格式: [请求方法][空格..空格][URL](空格..空格)(回车符)[换行符]
HTTP >= 1.0 请求行格式: [请求方法][空格..空格][URL][空格..空格][协议版本][回车符][换行符]
在状态机处理中,会将处理状态放在结构体ngx_http_request_s 的state中
返回值有以下三种情况
NGX_OK:表示成功解析到完整的http请求行
NGX_AGAIN:表示接收到字符流不能构成完整的请求行
NGX_HTTP_PARSE_INVALID_09_METHOD和NGX_HTTP_PARSE_INVALID_REQUEST:表示接收到非法的请求行
当返回NGX_AGAIN时,并且没有可用的内存继续接收字符流时,会调用
rv = ngx_http_alloc_large_header_buffer(r, 1);
分配的大小由large_client_header_buffers来决定
解析完成后,会设置ngx_http_request_t中的request_line, method_name, http_protocol, uri, unparsed_uri, exten, args,schema等
对于http版本号小于1.0的,如果请求头时没有server,会调用ngx_http_set_virtual_server,内部ngx_http_find_virtual_server来找到虚拟主机,后面调用ngx_http_process_request来处理请求。
对于http版本号大于等于1.0的,会初始化请求头列表
if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
break;
}
调整连接读事件的回调
rev->handler = ngx_http_process_request_headers;
调用ngx_http_process_request_headers开始处理请求头
ngx_http_process_request_headers(rev);
3.4 请求头的处理
首先检查当前的读事件是否已经超时,检查事件的timeout标志位,如果为1,表示已经超时,调用ngx_http_close_request关闭连接。
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;
}
检查接收http请求头部的header_in缓冲区是否用尽,当pos成员指向了end成员时,表示已经用完,需要分配更大的缓冲区
if (r->header_in->pos == r->header_in->end) {
rv = ngx_http_alloc_large_header_buffer(r, 0);
if (rv == NGX_ERROR) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
break;
}
if (rv == NGX_DECLINED) {
p = r->header_name_start;
r->lingering_close = 1;
if (p == NULL) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent too large request");
ngx_http_finalize_request(r,
NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
break;
}
len = r->header_in->end - p;
if (len > NGX_MAX_ERROR_STR - 300) {
len = NGX_MAX_ERROR_STR - 300;
}
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent too long header line: \"%*s...\"",
len, r->header_name_start);
ngx_http_finalize_request(r,
NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
break;
}
}
调用ngx_http_parse_header_line来解析请求头,当中也使用了状态机,将解析得到的请求头放到ngx_http_headers_in_t中的headers列表中。
当所有请求头处理完后,会调用ngx_http_process_request_header对请求头信息作一些验证
http版本大于1.0时,请求头中的host不能为空
if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent HTTP/1.1 request without \"Host\" header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
请求头中的content_length必须是合法的数字
if (r->headers_in.content_length) {
r->headers_in.content_length_n =
ngx_atoof(r->headers_in.content_length->value.data,
r->headers_in.content_length->value.len);
if (r->headers_in.content_length_n == NGX_ERROR) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid \"Content-Length\" header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
}
当使用传输编码时,要求http版本为大于等于1.1, 编码值为chunked时,不能传content_length
if (r->headers_in.transfer_encoding) {
if (r->http_version < NGX_HTTP_VERSION_11) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent HTTP/1.0 request with "
"\"Transfer-Encoding\" header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
if (r->headers_in.transfer_encoding->value.len == 7
&& ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
(u_char *) "chunked", 7) == 0)
{
if (r->headers_in.content_length) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent \"Content-Length\" and "
"\"Transfer-Encoding\" headers "
"at the same time");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
r->headers_in.chunked = 1;
} else {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent unknown \"Transfer-Encoding\": \"%V\"",
&r->headers_in.transfer_encoding->value);
ngx_http_finalize_request(r, NGX_HTTP_NOT_IMPLEMENTED);
return NGX_ERROR;
}
}
方法名不能为CONNECT和TRACE
if (r->method == NGX_HTTP_CONNECT) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent CONNECT method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
return NGX_ERROR;
}
if (r->method == NGX_HTTP_TRACE) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent TRACE method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
return NGX_ERROR;
}
最后调用ngx_http_process_request来处理请求
3.5 请求处理
由于开始准备调用各Http模块处理请求,不再存在接收http请求头部超时的问题,需要从定时器中将当前连接的读事件移除。检查读事件对应的timer_set标识位,为1表示读事件已经添加到定时器中,需要删除
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
设置连接的读写事件,以及http请求的读事件
c->read->handler = ngx_http_request_handler;
c->write->handler = ngx_http_request_handler;
r->read_event_handler = ngx_http_block_reading;
如果internal标志位为1,则表示请求当前需要做内部跳转,将结构体中的phase_handler序号置为server_rewrite_index,即从NGX_HTTP_SERVER_REWRITE_PHASE阶段开始,否则将phase_handler序号置为0
if (!r->internal) {
switch (r->headers_in.connection_type) {
case 0:
r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);
break;
case NGX_HTTP_CONNECTION_CLOSE:
r->keepalive = 0;
break;
case NGX_HTTP_CONNECTION_KEEP_ALIVE:
r->keepalive = 1;
break;
}
r->lingering_close = (r->headers_in.content_length_n > 0
|| r->headers_in.chunked);
r->phase_handler = 0;
} else {
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
r->phase_handler = cmcf->phase_engine.server_rewrite_index;
}
设置http请求的写回调为ngx_http_core_run_phases,并且执行ngx_http_core_run_phases
r->write_event_handler = ngx_http_core_run_phases;
ngx_http_core_run_phases(r);
ngx_http_core_run_phases就是执行http处理阶段中设置的phanse_handler
void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_phase_handler_t *ph;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
ph = cmcf->phase_engine.handlers;
while (ph[r->phase_handler].checker) {
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
if (rc == NGX_OK) {
return;
}
}
}
在阶段的cheker方法内会调用handler
checker方法返回 NGX_OK时,会将控制权交给nginx的事件模块,当返回非NGX_OK时,向下执行phase_engine中的各处理方法
3.6 子请求和post请求
ngx_http_request_t结构中与子请求有关的成员
post_subrequest:用于子请求的后置处理,即子请求处理结束时的上下文,包含处理回调,其定义为
typedef struct {
ngx_http_post_subrequest_pt handler;//子请求的完成回调
void *data;
} ngx_http_post_subrequest_t;
posted_requests:用来表示主请求包含的子请求链表
postponed:用于表示有嵌套层级的子请求,其结构定义为
struct ngx_http_postponed_request_s {
ngx_http_request_t *request;//第一个子请求
ngx_chain_t *out;
ngx_http_postponed_request_t *next;//用来表示后继子请求
};
subrequest是通过将一个请求拆分成多个子请求来完成整个请求过程
post请求是为了实现subrequest
子请求的设计是通过ngx_http_request_t结构体中的三个成员posted_requests, main和parent来完成。posted_requests的数据类型为ngx_http_posted_request_t,其结构定义为
struct ngx_http_posted_request_s {
ngx_http_request_t *request;//子请求
ngx_http_posted_request_t *next;
};
在创建subrequest时,会设置一些参数,将原始请求即main指向请求的引用计数+1
//ngx_http_subrequest
ngx_http_request_t *sr;
sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t));
if (sr == NULL) {
return NGX_ERROR;
}
sr->signature = NGX_HTTP_MODULE;
c = r->connection;
sr->connection = c;
sr->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
if (sr->ctx == NULL) {
return NGX_ERROR;
}
if (ngx_list_init(&sr->headers_out.headers, r->pool, 20,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_list_init(&sr->headers_out.trailers, r->pool, 4,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
return NGX_ERROR;
}
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
sr->main_conf = cscf->ctx->main_conf;
sr->srv_conf = cscf->ctx->srv_conf;
sr->loc_conf = cscf->ctx->loc_conf;
sr->pool = r->pool;
sr->headers_in = r->headers_in;
ngx_http_clear_content_length(sr);
ngx_http_clear_accept_ranges(sr);
ngx_http_clear_last_modified(sr);
sr->request_body = r->request_body;
#if (NGX_HTTP_V2)
sr->stream = r->stream;
#endif
sr->method = NGX_HTTP_GET;
sr->http_version = r->http_version;
sr->request_line = r->request_line;
sr->uri = *uri;
if (args) {
sr->args = *args;
sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;
sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0;
sr->background = (flags & NGX_HTTP_SUBREQUEST_BACKGROUND) != 0;
sr->unparsed_uri = r->unparsed_uri;
sr->method_name = ngx_http_core_get_method;
sr->http_protocol = r->http_protocol;
sr->schema = r->schema;
ngx_http_set_exten(sr);
sr->main = r->main;
sr->parent = r;
sr->post_subrequest = ps;
sr->read_event_handler = ngx_http_request_empty_handler;
sr->write_event_handler = ngx_http_handler;
sr->variables = r->variables;
sr->log_handler = r->log_handler;
if (sr->subrequest_in_memory) {
sr->filter_need_in_memory = 1;
}
sr->internal = 1;
sr->discard_body = r->discard_body;
sr->expect_tested = 1;
sr->main_filter_need_in_memory = r->main_filter_need_in_memory;
sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
sr->subrequests = r->subrequests - 1;
tp = ngx_timeofday();
sr->start_sec = tp->sec;
sr->start_msec = tp->msec;
r->main->count++;
*psr = sr;
对于需要延时的子请求,是放在请求结构体ngx_http_request_s中的postponed中
if (!sr->background) {
if (c->data == r && r->postponed == NULL) {
c->data = sr;
}
pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
if (pr == NULL) {
return NGX_ERROR;
}
pr->request = sr;
pr->out = NULL;
pr->next = NULL;
if (r->postponed) {
for (p = r->postponed; p->next; p = p->next) { /* void */ }
p->next = pr;
} else {
r->postponed = pr;
}
}
创建posted_request,放在main中的posted_requests链表尾
ngx_int_t
ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr)
{
ngx_http_posted_request_t **p;
if (pr == NULL) {
pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
if (pr == NULL) {
return NGX_ERROR;
}
}
pr->request = r;
pr->next = NULL;
for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }
*p = pr;
return NGX_OK;
}
什么时候执行subrequest的回调呢?
是在ngx_http_run_posted_requests中,会遍历主请求中的posted_requests链表,执行对应的write_event_handler回调
void
ngx_http_run_posted_requests(ngx_connection_t *c)
{
ngx_http_request_t *r;
ngx_http_posted_request_t *pr;
for ( ;; ) {
if (c->destroyed) {
return;
}
r = c->data;
pr = r->main->posted_requests;
if (pr == NULL) {
return;
}
r->main->posted_requests = pr->next;
r = pr->request;
ngx_http_set_log_request(c->log, r);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http posted request: \"%V?%V\"", &r->uri, &r->args);
r->write_event_handler(r);
}
}
子请求的类型有
NGX_HTTP_SUBREQUEST_IN_MEMORY:不支持嵌套,也是upstream的处理方式
NGX_HTTP_SUBREQUEST_WAITED:如果子请求提前完成,会将子请求的done标识设置为1
NGX_HTTP_SUBREQUEST_BACKGROUND:创建的「后台子请求」不参与响应生产过程,所以并不 需要加入「子请求关系树」。
3.7 消息体处理
主要使用数据结构有
ngx_http_request_body_t:请求消息体结构
ngx_chain_t:用于存放消息体的内容,是一个由ngx_buf_t组合的链表
其结构定义为
typedef struct {
ngx_temp_file_t *temp_file;
ngx_chain_t *bufs;
ngx_buf_t *buf;
off_t rest;//消息体剩余未处理的长度
off_t received;
ngx_chain_t *free;//空闲缓冲区链表
ngx_chain_t *busy;//繁忙缓冲区链表
ngx_http_chunked_t *chunked;
ngx_http_client_body_handler_pt post_handler;
unsigned filter_need_buffering:1;
unsigned last_sent:1;
unsigned last_saved:1;//表示是否需要写入文件以及是否已经写入文件
} ngx_http_request_body_t;
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
消息体的处理入口函数为ngx_http_read_client_request_body
请求消息体的初始化,设置rest为-1,以及读取完成后的后置处理handler
rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
if (rb == NULL) {
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
goto done;
}
rb->rest = -1;
rb->post_handler = post_handler;
如果之前的http请求中读取的数据还没有处理完,及读取的内容中包含部分消息体的内容,则进入下面逻辑
preread = r->header_in->last - r->header_in->pos;
if (preread) {
/* there is the pre-read part of the request body */
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http client request body preread %uz", preread);
out.buf = r->header_in;
out.next = NULL;
rc = ngx_http_request_body_filter(r, &out);
if (rc != NGX_OK) {
goto done;
}
r->request_length += preread - (r->header_in->last - r->header_in->pos);
if (!r->headers_in.chunked
&& rb->rest > 0
&& rb->rest <= (off_t) (r->header_in->end - r->header_in->last))
{
/* the whole request body may be placed in r->header_in */
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
goto done;
}
b->temporary = 1;
b->start = r->header_in->pos;
b->pos = r->header_in->pos;
b->last = r->header_in->last;
b->end = r->header_in->end;
rb->buf = b;
r->read_event_handler = ngx_http_read_client_request_body_handler;
r->write_event_handler = ngx_http_request_empty_handler;
rc = ngx_http_do_read_client_request_body(r);
goto done;
}
}
ngx_http_request_body_filter中会区分http流类型,分为两种
chunked类型(ngx_http_request_body_chunked_filter)
请求头中有content-length(ngx_http_request_body_length_filter)
static ngx_int_t
ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
if (r->headers_in.chunked) {
return ngx_http_request_body_chunked_filter(r, in);
} else {
return ngx_http_request_body_length_filter(r, in);
}
}
ngx_http_request_body_length_filter中,会在rest为-1时,重新设置rest
rb->rest = r->headers_in.content_length_n;
然后根据读取的请求体内容,构建ngx_chain_t缓冲区链表out,其中in是根据r->head_in构造的ngx_chain_t,实际只有一个。通过二级指针实现了链表的插入
ngx_chain_t *out,**ll;
out = NULL;
ll = &out;
for (cl = in; cl; cl = cl->next) {
if (rb->rest == 0) {
break;
}
tl = ngx_chain_get_free_buf(r->pool, &rb->free);
if (tl == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b = tl->buf;
ngx_memzero(b, sizeof(ngx_buf_t));
b->temporary = 1;
b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
b->start = cl->buf->pos;
b->pos = cl->buf->pos;
b->last = cl->buf->last;
b->end = cl->buf->end;
b->flush = r->request_body_no_buffering;
size = cl->buf->last - cl->buf->pos;
if ((off_t) size < rb->rest) {
cl->buf->pos = cl->buf->last;
rb->rest -= size;
} else {
cl->buf->pos += (size_t) rb->rest;
rb->rest = 0;
b->last = cl->buf->pos;
b->last_buf = 1;
}
*ll = tl;
ll = &tl->next;
}
调用ngx_http_top_request_body_filter来将消息体内容放到临时文件中,ngx_http_top_request_body_filter是在ngx_http_core_module模块的postconfiguration方法中来初始化的,对应的是ngx_http_request_body_save_filter
static ngx_int_t
ngx_http_core_postconfiguration(ngx_conf_t *cf)
{
ngx_http_top_request_body_filter = ngx_http_request_body_save_filter;
return NGX_OK;
}
将上面生成的out链表放入到消息请求体中的bufs中
ngx_chain_t *cl, *tl, **ll;
ll = &rb->bufs;
//指向最后一个
for (cl = rb->bufs; cl; cl = cl->next) {
ll = &cl->next;
}
for (cl = in; cl; cl = cl->next) {
if (cl->buf->last_buf) {
if (rb->last_saved) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"duplicate last buf in save filter");
*ll = NULL;
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
rb->last_saved = 1;
}
tl = ngx_alloc_chain_link(r->pool);
if (tl == NULL) {
*ll = NULL;
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
tl->buf = cl->buf;
*ll = tl;
ll = &tl->next;
}
*ll = NULL;
通过ngx_http_write_request_body将缓冲区链表中的数据写入到临时文件中,当写完文件后,会创建一个ngx_chain_t结构的数据,将其中buf填充,设置in_file标记,file名以及文件最后个位置的偏移
,最后赋值给消息请求体中的bufs
if (rb->temp_file || r->request_body_in_file_only) {
if (rb->bufs && rb->bufs->buf->in_file) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"body already in file");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_http_write_request_body(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (rb->temp_file->file.offset != 0) {
cl = ngx_chain_get_free_buf(r->pool, &rb->free);
if (cl == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b = cl->buf;
ngx_memzero(b, sizeof(ngx_buf_t));
b->in_file = 1;
b->file_last = rb->temp_file->file.offset;
b->file = &rb->temp_file->file;
rb->bufs = cl;
}
}
另外一种处理消息体的方式是丢弃即ngx_http_discard_request_body
3.8 发送响应
发送消息体主要 涉及到消息头和消息体的处理,nginx抽象出了两个函数接口ngx_http_output_header_filter_pt和ngx_http_output_body_filter_pt,其定义为
typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);
typedef ngx_int_t (*ngx_http_output_body_filter_pt)(ngx_http_request_t *r, ngx_chain_t *chain);
对于消息头和消息体的处理,是通过两个链表来处理
其中ngx_http_top_header_filter指向处理消息头链表的头,ngx_http_top_body_filter指向处理消息体链表的头。在每个http子模块中会定义两个静态变量文章来源:https://www.toymoban.com/news/detail-404786.html
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
通过每个模块的postconfiguration来将当前模块的请求头和请求体处理分别添加到两个处理链表中文章来源地址https://www.toymoban.com/news/detail-404786.html
到了这里,关于nginx http模块的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!