详解nginx请求头数据读取流程_nginx

来源:脚本之家  责任编辑:小易  

http { keepalive_timeout 65; client_head

在上一篇文章中,我们讲解了nginx是如何读取请求行的数据,并且解析请求行的。本文我们则主要讲解nginx是如何读取客户端发送来的请求头的数据,并且解析这些数据的。本质上来讲,请求行的数据和请求头的数据读取流程是基本一致的,因为其都面临着如何从间断的数据流中读取到数据,也面临着如何对数据进行处理的问题。

  一、反向代理:Web服务器的“经纪人”  1.1 反向代理初印象  反向代理(Rever

1. 请求头读取主流程

nginx模块的处理流程:a.客户端发送http请求道nginx服务器b.nginx

在介绍请求头的读取流程之前,我们首先展示一个http请求报文的示例:

Nginx 结合FastCGI 程序可以搭建高性能web service程序。(a)Nginx不支持

  在 ngx_lua 中访问 NginX 内置变量 ngx.var.arg_PARAMETER 即

POST /web/book/read HTTP/1.1 Host: localhost Connection: keep-alive Content-Length: 365 Accept: application/json, text/plain, */*

住在农村的小伙伴们在夏天的时候,都会碰到过一些小动物进房,夜里野猫会在屋顶上走过,昆虫更是到处都有了,说来都不稀奇。但是有一样相信大家都有所害怕,就是家蛇。在有些农村地区抓到家蛇时,是不可以打死的,如果你要打死,会遭到老一辈的训斥的,这是为何呢?在不同的农村,有不同的说法,一般南方叫地龙,北方叫大仙,说法大致分为这三类。第一:村里的老一辈说家蛇是十分灵性的一种动物,一般称之为守家仙,据说是来保你家平安的;第二:蛇又被称为“小龙”,向来都具有吉祥富贵之意,而农村习俗认为青龙入宅预示着吉利,可保家宅平安。家里进蛇说明即将会财源广进,富贵发达;第三:还有的农村认为蛇是已故亲人的化身,回来探望家里人,

示例中的第一行数据就是请求行,而后面的几行都是请求头。每一个请求头都是以name: value的格式组装的,并且每一个请求头都占用一行。         在上一篇介绍请求行读取流程的文章中,我们讲到,一旦请求行读取完成,nginx就会将当前读取事件的回调函数修改为ngx_http_process_request_headers()方法,并且直接调用该方法尝试读取请求头数据。这个方法就是读取请求行数据的主流程,如下是该方法的源码:

学围棋最恐怖的事情是越往后,越觉得自己什么都不会。在与职业高手下棋时,那种无力感、绝望感真是让人对围棋失去信心和兴趣。最早接触围棋的时候觉得围棋很简单,四颗子吃一个子,就这么下着下着,也玩得挺高兴;后来发现棋谱上的围棋不是这么挨着下的,然后才学会了定式、布局和基本的对杀技巧,这样总算踏入了围棋的大门。然后和许多棋迷一样,花费了大量的时间来学棋、下棋,经过了无数次头破血流的打击后,围棋水平一点点地涨了起来。但围棋水平到了一定瓶颈阶段后很难突破,必须有高手指点或者在棋上的顿悟,这样到了业余5段的水平后,在大学里或者当地已俨然是一大高手了,自我感觉颇佳。打高手的棋谱时,自认为也能看懂,并且在对弈过程

  在 ngx_lua 中访问 NginX 内置变量 ngx.var.arg_PARAMETER 即

/** * 解析客户端发送来的header数据 */ 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; 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; for (;;) { if (rc == NGX_AGAIN) { // 如果当前header缓冲区中没有剩余空间,则申请新的空间 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; } // 客户端发送的header太长,超出了large_client_header_buffers指定的最大大小 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_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); return; } } // 尝试读取连接上客户端新发送来的数据 n = ngx_http_read_request_header(r); if (n == NGX_AGAIN || n == NGX_ERROR) { return; } } 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); // NGX_OK表示成功解析得到了一个header数据 if (rc == NGX_OK) { r->request_length += r->header_in->pos - r->header_name_start; // 过滤无效的header if (r->invalid_header && cscf->ignore_invalid_headers) { continue; } // 创建一个存储header的结构体 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; // 把header的name作为hash表的key 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'; // 把header的value作为hash表的value 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); } // headers_in_hash中存储了所有的header,这里是查找当前客户端传的header是否为有效的header hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, h->lowcase_key, h->key.len); // 这里的handler是在ngx_http_headers_in中为每一个header定义的处理方法,经过各个header的 // handler()方法处理后,客户端传来的header就都转换到r->headers_in结构体中的各个属性中了 if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { return; } continue; } // NGX_HTTP_PARSE_HEADER_DONE表示已经将所有的header都处理完成了 if (rc == NGX_HTTP_PARSE_HEADER_DONE) { r->request_length += r->header_in->pos - r->header_name_start; r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; // 检查客户端发送来的header数据的合法性 rc = ngx_http_process_request_header(r); if (rc != NGX_OK) { return; } ngx_http_process_request(r); return; } // NGX_AGAIN表示读取到的header行数据不完全,还需要继续读取 if (rc == NGX_AGAIN) { continue; } ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } }

谢邀。根据最近公布的2K18中小前锋能力值,目前联盟中能力最强的前五个小前锋分别是勒布朗-詹姆斯(97),凯文-杜兰特(96),伦纳德(95),保罗-乔治(91)和阿德托昆博(91)。如果这五个人组一队恐怕将是目前为止NBA历史上罕见的强队了。他们不仅拥有出色的运动能力,全面的技巧,而且内外线防守都非常出色,甚至连身高都不会吃亏(杜兰特和阿德托昆博都超过2.10米)。他们一同上场,完全可以打起全攻全守的快速攻防,而且无论怎样的轮转换位都会让所有的对手无可奈何。进攻端主要可由詹姆斯和阿德托昆博轮流组织,而其余的三人都拥有出色的无球能力。防守端阿德托昆博坐镇中路是最后的防守屏障,其余几人皆可以防守

这里请求头的读取主要分为如下几个步骤: 首先检查当前读取事件是否已经超时,如果超时了,则直接关闭当前连接; 判断是否r->header_in->pos == r->header_in->end成立,这个主要是检查当前读取缓冲区中是否有可以存储新读取的数据的内存空间,如果没有,则从内存池中新申请一块内存空间; 调用ngx_http_read_request_header()方法读取当前连接句柄上的数据,其返回值如果大于0,则表示读取到的数据长度,如果等于0,则表示客户端断开了连接,如果为NGX_ERROR,则表示读取发生了异常,如果为NGX_AGAIN,则此次没有读取到数据,需要继续读取新的数据。可以看到,这里首先判断返回值是否为NGX_AGAIN,是则直接返回,而没有做任何其他的处理,这主要是因为当前的读事件的回调函数还是ngx_http_process_request_headers(),当有新的读事件触发时,其还是会调用到ngx_http_read_request_header()以再次读取到数据。另一方面,在ngx_http_read_request_header()方法中,如果发现返回值为NGX_AGAIN,其会将当前读事件再次添加到事件队列中,并且为当前连接在epoll句柄上注册读事件; 调用ngx_http_parse_header_line()方法解析读取到的请求头数据,需要注意的是,每次调用该方法都只会解析得到一个请求头,不过经过无限for循环和不停的事件触发机制,最终所有的请求头数据都会读取到。 根据ngx_http_parse_header_line()方法的返回值,如果为NGX_OK,则将新读取到的header存储到r->headers_in.headers链表中; 如果ngx_http_parse_header_line()方法的返回值为NGX_HTTP_PARSE_HEADER_DONE,则表示读取所有的header都成功了,此时首先会调用ngx_http_process_request_header()方法检查读取到的header的合法性,然后调用ngx_http_process_request()方法以启动nginx中http模块的11个阶段,这个方法的实现原理我们将会在后面的文章进行讲解。

2. 请求头数据的读取

可以看到,对请求头的读取主要有两个方法:ngx_http_read_request_header()和ngx_http_parse_header_line()。这里的第二个方法比较长,但是其逻辑非常的简单,主要就是解析读取到的数据是否能组成一个完整的请求头(name: value的形式,并且占用一行),如果是,则返回NGX_OK,否则返回NGX_AGAIN以期待继续读取数据。对于这个方法,我们这里不进行讲解,读者可自行阅读源码,我们主要讲解ngx_http_read_request_header()方法是如何读取客户端发送来的请求头数据的:

  在 ngx_lua 中访问 NginX 内置变量 ngx.var.arg_PARAMETER 即

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; // 如果n大于0,说明还有读取到的数据未处理,则直接返回n if (n > 0) { return n; } // 走到这里,说明当前读取到的数据都已经处理完了,因而这里会进行判断,如果当前事件的ready参数为1, // 则表示当前连接的句柄上存储还未读取的数据,因而调用c->recv()方法读取数据,否则继续将当前事件添加到 // 事件队列中,并且继续监听当前连接句柄的读事件 if (rev->ready) { // 在连接文件描述符上读取数据 n = c->recv(c, r->header_in->last, r->header_in->end - r->header_in->last); } else { n = NGX_AGAIN; } // 如果n为NGX_AGAIN,则将当前事件添加到事件监听器中,并且继续监听当前epoll句柄的读事件 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; } // 如果n为0,说明客户端关闭了连接 if (n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client prematurely closed connection"); } // 如果客户端关闭了连接或者读取异常,则回收当前的request结构体 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; }

这里请求头数据的读取主要分为如下几个步骤: 判断当前缓冲区中是否存在还未处理的数据,如果存在,则直接返回。存在还未读取的数据的原因主要是在前面读取请求行数据的过程中,可能会读取一部分或者全部的请求头数据,因而这里会做检查; 判断当前读事件是否已经准备就绪,如果已经就绪,则调用c->recv()方法读取当前连接句柄上的数据; 如果当前读事件还未就绪,则将当前的读事件再次添加到事件队列中,并且为当前连接在epoll句柄上注册读事件; 对第二步的返回值进行判断,如果为0,则表示客户端已经断开了连接,如果为NGX_ERROR,则表示读取数据异常了,这两种情况时都会关闭当前连接,并且向客户端返回400状态码。如果返回值NGX_AGAIN,则从事第三步中的步骤,以继续监听读事件。如果返回值大于0,则表示读取成功,而这个大于0的值就表示读取到的数据长度; 更新存储读取数据的缓冲区的指针数据。

3. 小结

本文主要对nginx是如何读取并且解析请求头的流程进行了讲解,并且着重讲解了读取数据的主流程代码和读取的详细步骤。

到此这篇关于详解nginx请求头数据读取流程的文章就介绍到这了,更多相关nginx请求头数据读取内容请搜索真格学网以前的文章或继续浏览下面的相关文章希望大家以后多多支持真格学网!

扩展阅读,根据您访问的内容系统为您准备了以下内容,希望对您有帮助。

nginx 支持start end吗

首先介绍一下rfc2616中定义的http请求基本格式:

Request = Request-Line

* (( general-header

| request-header

| entity-header ) CRLF)

CRLF

[ message-body ]

第一行是请求行(request line),用来说明请求方法,要访问的资源以及所使用的HTTP版本:

Request-Line = Method SP Request-URI SP HTTP-Version CRLF

请求方法(Method)的定义如下,其中最常用的是GET,POST方法:

Method = "OPTIONS"

| "GET"

| "HEAD"

| "POST"

| "PUT"

| "DELETE"

| "TRACE"

| "CONNECT"

| extension-method

extension-method = token

要访问的资源由统一资源地位符URI(Uniform Resource Identifier)确定,它的一个比较通用的组成格式(rfc2396)如下:

<scheme>://<authority><path>?<query>

一般来说根据请求方法(Method)的不同,请求URI的格式会有所不同,通常只需写出path和query部分。

http版本(version)定义如下,现在用的一般为1.0和1.1版本:

HTTP/<major>.<minor>

请求行的下一行则是请求头,rfc2616中定义了3种不同类型的请求头,分别为general-header,request-header和entity-header,每种类型rfc中都定义了一些通用的头,其中entity-header类型可以包含自定义的头。

请求头读取 (99%)¶

这一节介绍nginx中请求头的解析,nginx的请求处理流程中,会涉及到2个非常重要的数据结构,ngx_connection_t和ngx_http_request_t,分别用来表示连接和请求,这2个数据结构在本书的前篇中已经做了比较详细的介绍,没有印象的读者可以翻回去复习一下,整个请求处理流程从头到尾,对应着这2个数据结构的分配,初始化,使用,重用和销毁。

nginx在初始化阶段,具体是在init process阶段的ngx_event_process_init函数中会为每一个监听套接字分配一个连接结构(ngx_connection_t),并将该连接结构的读事件成员(read)的事件处理函数设置为ngx_event_accept,并且如果没有使用accept互斥锁的话,在这个函数中会将该读事件挂载到nginx的事件处理模型上(poll或者epoll等),反之则会等到init process阶段结束,在工作进程的事件处理循环中,某个进程抢到了accept锁才能挂载该读事件。

php使用nginx如何获取请求头?

Nginx的http模块在处理HTTP请求时对环境变量的封装与Apache有所不同。除了支持一些与HTTP协议相关的通用的变量之外,还支持一系列Nginx自有的变量,如Nginx配置目录下fastcgi_params.default文件里的$server_protocol、$nginx_version等。正如这个文件中的示例的用途,这些变量可以在配置fastcgi时传递给cgi程序,使其可以作为cgi程序的环境变量来使用。然而,即便是Nginx有了这些自有的变量也无法完全满足所有的需求。

了解Jquery的朋友会发现,Jquery在实现Ajax时会通过setRequestHeader(‘X-Requested-With’, ‘xmlhttprequest’)方法自动添加一个值为“xmlhttprequest”自定义的请求头”X-Requested-With”来标识这是一个Ajax请求,以期处理这个请求的后端能够通过判断这个标识来识别请求类型。那么这个时候PHP是如何来获取这个自定义参数的值的呢?

熟悉Apache和PHP的人一定会第一时间想到$_SERVER["HTTP_X_REQUESTED_WITH"],不错,这对黄金搭配早就把这个问题给完美解决了,但Nginx却不然,这是由Nginx对其自身工作的定位决定的——Nginx只负责HTTP。在Nginx眼里,PHP只是它的一个后端,形象点来说,它只管分发请求,而不管发给谁。这就意味着,我们无法期待Nginx像Apache一样给我们自动完成一些自定义参数到PHP的传递,只有自力更生。简单点说就是,想要直接像$_SERVER["HTTP_X_REQUESTED_WITH"]这样来调用自定义请求头参数的值的话,你就必须手工将其添加到fastcgi_params的配置中,明确告知cgi程序接收,否则Nginx会将其遗弃。

配置环境变量的方法可参照fastcgi_params.default这个文件,在前面的博客“Nginx下虚拟主机环境变量的配置方法”中也提到过。针对上述例子,只需在fastcgi_params文件中增加一行即可:

?12 # for Ajax fastcgi_param HTTP_X_REQUESTED_WITH $http_x_requested_with;

这样,重载Nginx配置后就可以之间在PHP中调用$_SERVER["HTTP_X_REQUESTED_WITH"]来判断请求类型了。其中需要注意以下两点:

一、自定义请求头部的名称不应该包括空白、冒号、换行和下划线。

Nginx在处理客户端请求header头时,会将名称中的中横线”-”替换为下划线”_”,并将所有字母小写再加上”$http_”来作为该名称对应的变量名。例如上述Jquery的例子中setRequestHeader(‘X-Requested-With’, ‘xmlhttprequest’),在HTTP请求头中为一行字符串:”X-Requested-With: xmlhttprequest”,经Nginx处理后将自动生成一个名为$http_x_requested_with的变量,其值为”xmlhttprequest”。尤其注意中横线”-”替换为下划线”_”这个规则,这意味着请求参数名称中如果含有下划线,Nginx将无法正确识别。

二、$_SERVER["HTTP_X_REQUESTED_WITH"]中的索引,也即“fastcgi_param HTTP_X_REQUESTED_WITH $http_x_requested_with;”中加红部分,是可以自由命名的,当前这种命名格式是为了保持和Apache保持一致。

..

nginx中proxy_set_header Host $host;的作用!~请详解!~

用户认证接口:根据客户端IP和port,进行IP反查和端口范围确认,如符合则用户认证通过。

proxy_set_header 就是可设置请求头-并将头信息传递到服务器端。

1、Nginx proxy_set_header

允许重新定义或添加字段传递给代理服务器的请求头。该值可以包含文本、变量和它们的组合。在没有定义proxy_set_header时会继承之前定义的值。默认情况下,只有两个字段被重定义:

2、如果启用缓存,来自之前请求的头字段“If-Modified-Since”, “If-Unmodified-Since”, “If-None-Match”, “If-Match”, “Range”, 和 “If-Range”  将不会被代理服务器传递。

一个不会变化的“Host”头请求字段可通过如下方式被传递:

3、然后,当字段不在请求头中就无法传递啦。在这种情况下,可通过设置Host变量,将需传递值赋给Host变量。

4、此外,服务器名称和端口一起通过代理服务器传递。

5、如果请求头的存在空的字段将不会通过代理服务器传递出去。

在nginx模块中如何获得请求消息头全部信息?

都没人知道吗?自已顶顶 查看原帖>>

  • 本文相关:
  • nginx和geoip模块读取ip所在的地域信息方法
  • nodejs读取分析nginx错误日志的方法
  • 详解nginx的配置函数对于请求体的读取
  • nginx配置ssl双向验证的方法
  • nginx 作为反向代理实现负载均衡的例子
  • nginx禁止某个ip访问站点的设置方法
  • nginx 多域名配置的方法
  • 分享一份nginx重启脚本
  • 详解nginx 和 php 的两种部署方式的对比
  • nginx服务器nginx.com配置文件详解
  • nginx中if语句实现数学比较功能
  • 详解nginx惊群问题的解决方式
  • nginx安装与使用教程详解
  • nginx 支持start end吗
  • php使用nginx如何获取请求头?
  • nginx中proxy_set_header Host $host;的作用!~请详解!~
  • 在nginx模块中如何获得请求消息头全部信息?
  • 集群是用什么方案实现nginx均衡配置
  • nginx链接时长设置多少合适
  • 请教问题nginx反向代理proxy
  • 怎么用nginx输出helloworld
  • nginx 支持 iocp 吗
  • nginx+lua怎样实现http请求的响应
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全星外虚拟主机华众虚拟主机linuxwin服务器ftp服务器dns服务器tomcat nginxzabbix云和虚拟化服务器其它首页服务器nginxnginx和geoip模块读取ip所在的地域信息方法nodejs读取分析nginx错误日志的方法详解nginx的配置函数对于请求体的读取nginx配置ssl双向验证的方法nginx 作为反向代理实现负载均衡的例子nginx禁止某个ip访问站点的设置方法nginx 多域名配置的方法分享一份nginx重启脚本详解nginx 和 php 的两种部署方式的对比nginx服务器nginx.com配置文件详解nginx中if语句实现数学比较功能详解nginx惊群问题的解决方式nginx安装与使用教程详解nginx提示:500 internal servernginx 多站点配置方法集合权限问题导致nginx 403 forbiddenginx fastcgi错误primary scripnginx服务器的反向代理proxy_pasnginx 403 forbidden的解决办法nginx伪静态配置和常用rewrite伪nginx下301重定向域名的方法小结详解nginx服务器中配置超时时间的windows下nginx+php5的安装与配置nginx地址重定向的方法nginx+uwsgi启动django项目的详细步骤详解nginx服务器中http headers相关的模块centos 7下安装nginx服务器采用ngxtop实现nginx实时访问数据统计nginx大并发优化实战centos6.6 安装 tengine 笔记ubuntu16.04下彻底卸载nginx的相关命令详解用nginx+wordpress搭建个人博客全流程nginx配置文件nginx.conf详细说明
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved