nginx&http 第三章 ngx http ngx_http_process_request_headers
HTTP 请求行正确处理完成后,针对 HTTP/1.0 及以上版本紧接着要做的就是请求 HEADER 的处理与解析了
/**
* 用于处理http的header数据
* 请求头:
* Host: localhost
* User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
* Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
* Accept-Language: zh-cn,zh;q=0.5
* Accept-Encoding: gzip, deflate
*
copy from https://techlog.cn/article/list
/*
用于处理http的header数据
GET /sample.jsp HTTP/1.1
* 请求头:
* Host: localhost
* User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
* Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
* Accept-Language: zh-cn,zh;q=0.5
* Accept-Encoding: gzip, deflate
*/ //解析上面的GET /sample.jsp HTTP/1.1以外的配置
ngx_http_parse_request_line解析请求行, ngx_http_process_request_headers(ngx_http_parse_header_line)解析头部行(请求头部)
接收包体ngx_http_read_client_request_body
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) {//如果tcp连接建立后,等了client_header_timeout秒一直没有收到客户端的数据包过来,则关闭连接
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; for ( ;; ) { if (rc == NGX_AGAIN) { if (r->header_in->pos == r->header_in->end) {
//说明header_in空间已经用完了,无法继续存储recv的数据,则重新分配大空间-------buf太小,则扩大buf */ 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;
}
} //这里至少读一次----------读取请求数据 调用方法:os/ngx_recv.c文件夹中的 ngx_unix_recv */
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);
/* 处理HTTP请求头数据/* 处理HTTP请求头数据 **/
rc = ngx_http_parse_header_line(r, r->header_in,
cscf->underscores_in_headers); /* 处理成功,则继续解析下一个http header数据 */
if (rc == NGX_OK) { r->request_length += r->header_in->pos - r->header_name_start; //request_length增加一行字符长度 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 */
/* 解析的头不会KEY:VALUE存入到headers_in中 */
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;
//拷贝name:value中的name到key中,name后面的冒号被用\0替换了
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'; //拷贝name:value中的value到value中,value后的换行符被用\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;
} //拷贝name:value中的name到key中,包括name后面的冒号
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);
} //见ngx_http_headers_in //检查解析到的头部key:value中的对应ngx_http_header_t结构,即ngx_http_headers_in中的成员
hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len); //从ngx_http_headers_in找到与name匹配的字符串,然后只需对应的handler,一般就是把解析出的name:value中的value存放到headers_in成员的headers链表中 //参考ngx_http_headers_in,通过该数组中的回调hander来存储解析到的请求行name:value中的value到headers_in的响应成员中,见ngx_http_process_request_headers
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); //如果打开调试开关,则name:value可以存到error.log中 continue; //继续解析下一行
}
如果HTTP的header全部处理完*
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {//如果HTTP的header全部处理 /* a whole header has been parsed successfully */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http header done");
/*
请求行
头部行
空行(这里是\r\n)
内容
*/
r->request_length += r->header_in->pos - r->header_name_start;//把空行的\r\n加上 r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
/* 检查解析后的request header是否符合要求,如果不符合要求,返回NGX_ERROR */
rc = ngx_http_process_request_header(r); if (rc != NGX_OK) {
return;
}
/* 请求行和请求头处理完成后,调用ngx_http_process_request,处理HTTP请求body以及响应 */
ngx_http_process_request(r); return;
} if (rc == NGX_AGAIN) {//返回NGX_AGAIN时,表示还需要接收到更多的字符流才能继续解析 /* a header line parsing is still not complete */ continue;
} /* rc == NGX_HTTP_PARSE_INVALID_HEADER: "\r" is not followed by "\n" */
/*
当调用ngx_http_parse_header_line方法解析字符串构成的HTTP时,是有可能遇到非法的或者Nginx当前版本不支持的HTTP头部的,
这时该方法会返回错误,于是调用ngx_http_finalize_request方法,向客户端发送NGX_ HTTP BAD_REQUEST宏对应的400错误码响应。
*/
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid header line: \"%*s\\r...\"",
r->header_end - r->header_name_start,
r->header_name_start);
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return;
}
}
当 ngx_http_parse_header_line 函数返回 NGX_HTTP_PARSE_HEADER_DONE 意味着所有的 HEADER 指令已经全部解析完成随后就要进入正式的请求处理阶段 ngx_http_process_request 了然而,在进入 ngx_http_process_request 之前,nginx 首先调用 ngx_http_process_request_header 对 HEADER 进行了校验,比如对那些使用 http/1.1 协议但是却没有发送Host头的请求,nginx 给这些请求返回 400 错误。还有 nginx 现在的版本并不支持 chunked 格式的输入,如果某些请求申明自己使用了 chunked 格式的输入(请求带有值为 chunked 的 transfer_encoding 头部),ngx给这些请求返回 错误等等code 411
nginx&http 第三章 ngx http ngx_http_process_request_headers的更多相关文章
- nginx&http 第三章 ngx http ngx_http_process_request_line读取和处理HTTP头部的行
在 ngx_http_wait_request_handler 的最后调用了 ngx_http_process_request_line 函数用来处理和解析这次请求的全文 在读事件被触发时,内核套接字 ...
- nginx&http 第三章 ngx 1-http ngx_http_wait_request_handler
对于活跃的 HTTP 连接,在执行连接建立回调函数 ngx_http_init_connection 的过程中会执行 ngx_http_wait_request_handler 回调函数, 负责 HT ...
- nginx&http 第三章 ngx http 框架处理流程
1. nginx 连接结构 ngx_connection_t 这个连接表示是客户端主动发起的.Nginx服务器被动接受的TCP连接,我们可以简单称其为被动连接.同时,在有些请求的处理过程中,Nginx ...
- nginx&http 第三章 ngx 请求处理的 11 个阶段 --ngx_http_process_request& ngx_http_handler
ngx_http_process_request如果设置了定时器则删除,既然所有的请求已经接收完毕,就不会再发生超时了 重设连接的读写回调函数 重设请求读事件回调函数 调用 ngx_http_hand ...
- nginx&http 第三章 ngx HTTP 请求的 11 个处理阶段
nginx 将一个 HTTP 请求分为 11 个处理阶段,这样做让每一个 HTTP 模块可以仅仅专注于完成一个独立.简单的功能,而一个请求的完整处理过程可以由多个 HTTP 模块共同合作完成将一次 H ...
- nginx&http 第三章 ngx 事件http 初始化1
在 http 配置块中,我们配置了 http 连接相关的信息,HTTP 框架也正是从这里启动的 在 nginx 初始化的过程中,执行了 ngx_init_cycle 函数,其中进行了配置文件解析,调用 ...
- nginx&http 第三章 ngx 事件event accept epoll /init
tcp 三次握手成功后,listen fd 可读,在process_event_timer 中调用rev->handler(rev)处理: 其回调函数为: ngx_event_accept / ...
- nginx&http 第三章 ngx 事件event epoll 处理
1. epoll模块命令集 ngx_epoll_commands epoll模块上下文 ngx_epoll_module_ctx epoll模块配置 ngx_epoll_module static ...
- nginx&http 第四章 ngx http代理 && 转载
Nginx访问上游服务器的流程大致分以下几个阶段:启动upstream.连接上游服务器.向上游发送请求.接收上游响应(包头/包体).结束请求. upstream相关的两个重要数据结构ngx_http_ ...
随机推荐
- Python数据类型--集合(set)
Python的集合是无序.可迭代的容器对象,所有元素放在一对大括号中{},元素之间使用逗号隔开,同一集合内的元素具有唯一性,不允许重复. 集合中只能包含数字.字符串.元组等不可变类型的数据,不能包含列 ...
- CSS字体属性与文本属性
CSS字体属性与文本属性 1. 字体属性 1.1 字体系列font-family p { font-family: "Microsoft Yahei";/*微软雅黑*/ } /*当 ...
- 多测师讲解jmeter _图片详解_(全)高级讲师肖sir
- Flask实现websocket
from flask import Flask,request user_socket_dict = {} app = Flask(__name__) @app.route("/conn_w ...
- C语言从1打印到100再打印到1该如何编写?我只服最后一种写法!
我觉得这是一个送分题,奈何人才太多了,给出了各种古怪的写法,如果是做项目的话,我比骄建议一些正常的写法,就是大家都能看得懂的,不要搞什么花里胡哨,不过你要是交流的话,既然是交流,我不觉得要多正规,即使 ...
- 【纯水题】POJ 1852 Ants
题目大意 有一根长\(L\)厘米米的水平木棍上有\(n\)个蚂蚁,它们以每秒1cm/s的爬(fei)行(ben)到木棍的一端,之后掉下去. 给出每个蚂蚁的起始位置,但是不知道它们爬行的方向.相向而行的 ...
- xuexi
1.内存的编址方法就是内存地址与内存单元格一一对应且永久绑定.计算机的cpu只认识内存地址,不关心内存单元格的位置和内容.通过硬件的设计来达到通过内存地址找到内存单元格. 2.内存的编址是以字节为单位 ...
- Ambari仓库安装教程
Ambari仓库安装教程 如果用户需要后续使用Ambari server进行安装Hadoop则必须搭建一个内部的yum源,否则直接下载速度将会很慢,当然该服务仅要搭建一个即可,可以一直使用. 一.Ce ...
- 一些免费API接口
转载自:https://www.cnblogs.com/haimishasha/p/6351403.html 天气接口 聚合数据: http://op.juhe.cn/onebox/weather/q ...
- socket 参考文档
socket.io 中文手册,socket.io 中文文档转载于:http://www.cnblogs.com/xiezhengcai/p/3956401.html 服务端 io.on('connec ...