上一篇介绍了《菜鸟学习nginx之接收HTTP请求行》,本篇介绍Nginx接收HTTP Header处理流程。
一、HTTP Header处理流程
处理Header与上一篇处理HTTP请求行很类似,比较容易理解
1.1、流程图
1.2、代码
/**
* 处理HTTP Header
* @param rev 读事件
*/
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;
}
这部分代码是处理定时器超时事件,与解析HTTP请求行是相同的,不在多说。
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
rc = NGX_AGAIN;
for (;;)
{
if (rc == NGX_AGAIN)
{
if (r->header_in->pos == r->header_in->end)
{//表示分配的内存不足 需要申请更大内存
rv = ngx_http_alloc_large_header_buffer(r, 0);
if (rv == NGX_ERROR)
{
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (rv == NGX_DECLINED)
{
p = r->header_name_start;
r->lingering_close = 1;
if (p == NULL)
{
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent too large request");
ngx_http_finalize_request(r,
NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
return;
}
len = r->header_in->end - p;
if (len > NGX_MAX_ERROR_STR - 300)
{
len = NGX_MAX_ERROR_STR - 300;
}
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;
}
}
//接收 HTTP Header
n = ngx_http_read_request_header(r);
if (n == NGX_AGAIN || n == NGX_ERROR)
{
return;
}
}
这部分代码说明:
1、 判断内存是否足够,若不足则申请最大内存(默认8k)。由于HTTP报文是不定长报文,所以无法一次性申请足够内存。
2、调用ngx_http_read_request_header方法接收报文
/* the host header could change the server configuration context */
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
/**
* http协议解析
* 返回值 : NGX_OK: 解析完header中的一行时返回
* NGX_HTTP_PARSE_HEADER_DONE: 解析完整个header时返回;
* NGX_HTTP_PARSE_INVALID_HEADER: 解析出错时返回;
* NGX_AGAIN: 表示需要读取新的请求头内容
*/
rc = ngx_http_parse_header_line(r, r->header_in,
cscf->underscores_in_headers);
ngx_http_parse_header_line是纯粹解析HTTP报文的,将解析出的报文保存到header_in中。ngx_http_parse_header_line,返回值如下表:
返回值 | 含义 |
NGX_OK | 当前header line解析成功(一个http请求中可以有多个header line) |
NGX_HTTP_PARSE_HEADER_DONE | 表示所有HTTP Header都已经成功解析完毕 |
NGX_AGAIN | 表是当前header line没有接收完毕,需要再次接收 |
NGX_HTTP_PARSE_INVALID_HEADER | 解析失败,结束htt request |
根据上表,我们来看一下具体处理内容:
if (rc == NGX_OK)
{//返回 NGX_OK 处理解析后数据
r->request_length += r->header_in->pos - r->header_name_start;
if (r->invalid_header && cscf->ignore_invalid_headers)
{
/* there was error while a header line parsing */
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid header line: \"%*s\"",
r->header_end - r->header_name_start,
r->header_name_start);
continue;
}
/* a header line has been parsed successfully */
h = ngx_list_push(&r->headers_in.headers);
if (h == NULL)
{
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
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)
{
ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
}
else
{
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
}
hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh && hh->handler(r, h, hh->offset) != NGX_OK)
{
return;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http header: \"%V: %V\"",
&h->key, &h->value);
continue;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE)
{
/**
* a whole header has been parsed successfully
* 所有header行都解析成功 准备处理该http请求
* 1、没有body则对该http请求进行后续处理
* 2、如果有body则接收body
*/
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 header解析完毕 进行处理request header
rc = ngx_http_process_request_header(r);
if (rc != NGX_OK)
{
return;
}
/* 处理http请求 */
ngx_http_process_request(r);
return;
}
这段代码比较重要,该代码段主要处理request header请求以及处理http request。
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;
}
这段代码针对NGX_AGAIN和NGX_HTTP_PARSE_INVALID_HEADER处理流程,比较简单。
二、ngx_http_process_request_header函数
该函数处理相对简单,主要针对header中参数合法性校验以及request对象相关成员进行再次赋值,例如:content_length_n、keep_alive等
ngx_int_t
ngx_http_process_request_header(ngx_http_request_t *r)
{
if (r->headers_in.server.len == 0 && ngx_http_set_virtual_server(r, &r->headers_in.server) == NGX_ERROR)
{
return NGX_ERROR;
}
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;
}
if (r->headers_in.content_length)
{//如果body不空 则需要设置body长度
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;
}
}
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;
}
if (r->headers_in.transfer_encoding)
{
if (r->headers_in.transfer_encoding->value.len == 7 && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
(u_char *)"chunked", 7) == 0)
{
r->headers_in.content_length = NULL;
r->headers_in.content_length_n = -1;
r->headers_in.chunked = 1;
}
else if (r->headers_in.transfer_encoding->value.len != 8 || ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
(u_char *)"identity", 8) != 0)
{
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;
}
}
if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE)
{//处理长连接 设置超时时间
if (r->headers_in.keep_alive)
{
r->headers_in.keep_alive_n =
ngx_atotm(r->headers_in.keep_alive->value.data,
r->headers_in.keep_alive->value.len);
}
}
return NGX_OK;
}
三、ngx_http_read_request_header函数
本篇和上一篇都有该函数,该函数主要功能就是接收HTTP报文,功能也比较简单。
/**
* 获取request header
* @param r http请求
*/
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;
if (n > 0)
{/* 表示header_in中有数据 不需要从socket中获取数据 */
return n;
}
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;
}
四、总结
本篇介绍了Nginx如何接收HTTP Header以及处理Header,下一篇介绍如何处理当前HTTP Request请求,即ngx_http_process_request。