nginx请求体读取(二)
2,丢弃请求体
一个模块想要主动的丢弃客户端发过的请求体,可以调用nginx核心提供的ngx_http_discard_request_body()接口,主动丢弃的原因可能有很多种,如模块的业务逻辑压根不需要请求体 ,客户端发送了过大的请求体,另外为了兼容http1.1协议的pipeline请求,模块有义务主动丢弃不需要的请求体。总之为了保持良好的客户端兼容性,nginx必须主动丢弃无用的请求体。下面开始分析ngx_http_discard_request_body()函数:
- <span style="font-size:18px;">ngx_int_t
- ngx_http_discard_request_body(ngx_http_request_t *r)
- {
- ssize_t size;
- ngx_event_t *rev;
- if (r != r->main || r->discard_body) {
- return NGX_OK;
- }
- if (ngx_http_test_expect(r) != NGX_OK) {
- return NGX_HTTP_INTERNAL_SERVER_ERROR;
- }
- rev = r->connection->read;
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");
- if (rev->timer_set) {
- ngx_del_timer(rev);
- }
- if (r->headers_in.content_length_n <= 0 || r->request_body) {
- return NGX_OK;
- }
- size = r->header_in->last - r->header_in->pos;
- if (size) {
- if (r->headers_in.content_length_n > size) {
- r->header_in->pos += size;
- r->headers_in.content_length_n -= size;
- } else {
- r->header_in->pos += (size_t) r->headers_in.content_length_n;
- r->headers_in.content_length_n = 0;
- return NGX_OK;
- }
- }
- r->read_event_handler = ngx_http_discarded_request_body_handler;
- if (ngx_handle_read_event(rev, 0) != NGX_OK) {
- return NGX_HTTP_INTERNAL_SERVER_ERROR;
- }
- if (ngx_http_read_discarded_request_body(r) == NGX_OK) {
- r->lingering_close = 0;
- } else {
- r->count++;
- r->discard_body = 1;
- }
- return NGX_OK;
- }
- </span>
由于函数不长,这里把它完整的列出来了,函数的开始同样先判断了不需要再做处理的情况:子请求不需要处理,已经调用过此函数的也不需要再处理。接着调用ngx_http_test_expect() 处理http1.1 expect的情况,根据http1.1的expect机制,如果客户端发送了expect头,而服务端不希望接收请求体时,必须返回417(Expectation Failed)错误。nginx并没有这样做,它只是简单的让客户端把请求体发送过来,然后丢弃掉。接下来,函数删掉了读事件上的定时器,因为这时本身就不需要请求体,所以也无所谓客户端发送的快还是慢了,当然后面还会将到,当nginx已经处理完该请求但客户端还没有发送完无用的请求体时,nginx会在读事件上再挂上定时器。
函数同样还会检查请求头中的content-length头,客户端如果打算发送请求体,就必须发送content-length头,同时还会查看其他地方是不是已经读取了请求体。如果确实有待处理的请求体,函数接着检查请求头buffer中预读的数据,预读的数据会直接被丢掉,当然如果请求体已经被全部预读,函数就直接返回了。
接下来,如果还有剩余的请求体未处理,该函数调用ngx_handle_read_event()在事件处理机制中挂载好读事件,并把读事件的处理函数设置为ngx_http_discarded_request_body_handler。做好这些准备之后,该函数最后调用ngx_http_read_discarded_request_body()接口读取客户端过来的请求体并丢弃。如果客户端并没有一次将请求体发过来,函数会返回,剩余的数据等到下一次读事件过来时,交给ngx_http_discarded_request_body_handler()来处理,这时,请求的discard_body将被设置为1用来标识这种情况。另外请求的引用数(count)也被加1,这样做的目的是客户端可能在nginx处理完请求之后仍未完整发送待发送的请求体,增加引用是防止nginx核心在处理完请求后直接释放了请求的相关资源。
ngx_http_read_discarded_request_body()函数非常简单,它循环的从链接中读取数据并丢弃,直到读完接收缓冲区的所有数据,如果请求体已经被读完了,该函数会设置读事件的处理函数为ngx_http_block_reading,这个函数仅仅删除水平触发的读事件,防止同一事件不断被触发。
再来看一下读事件的处理函数ngx_http_discarded_request_body_handler,这个函数每次读事件来时会被调用,先看一下它的源码:
- <span style="font-size:18px;">void
- ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
- {
- ...
- c = r->connection;
- rev = c->read;
- if (rev->timedout) {
- c->timedout = 1;
- c->error = 1;
- ngx_http_finalize_request(r, NGX_ERROR);
- return;
- }
- if (r->lingering_time) {
- timer = (ngx_msec_t) (r->lingering_time - ngx_time());
- if (timer <= 0) {
- r->discard_body = 0;
- r->lingering_close = 0;
- ngx_http_finalize_request(r, NGX_ERROR);
- return;
- }
- } else {
- timer = 0;
- }
- rc = ngx_http_read_discarded_request_body(r);
- if (rc == NGX_OK) {
- r->discard_body = 0;
- r->lingering_close = 0;
- ngx_http_finalize_request(r, NGX_DONE);
- return;
- }
- /* rc == NGX_AGAIN */
- if (ngx_handle_read_event(rev, 0) != NGX_OK) {
- c->error = 1;
- ngx_http_finalize_request(r, NGX_ERROR);
- return;
- }
- if (timer) {
- clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
- timer *= 1000;
- if (timer > clcf->lingering_timeout) {
- timer = clcf->lingering_timeout;
- }
- ngx_add_timer(rev, timer);
- }
- }
- </span>
函数一开始就处理了读事件超时的情况,之前说到在ngx_http_discard_request_body()函数中已经删除了读事件的定时器,那么什么时候会设置定时器呢?答案就是在nginx已经处理完该请求,但是又没有完全将该请求的请求体丢弃的时候(客户端可能还没有发送过来),在ngx_http_finalize_connection()函数中,如果检查到还有未丢弃的请求体时,nginx会添加一个读事件定时器,它的时长为lingering_timeout指令所指定,默认为5秒,不过这个时间仅仅两次读事件之间的超时时间,等待请求体的总时长为lingering_time指令所指定,默认为30秒。这种情况中,该函数如果检测到超时事件则直接返回并断开连接。同样,还需要控制整个丢弃请求体的时长不能超过lingering_time设置的时间,如果超过了最大时长,也会直接返回并断开连接。
如果读事件发生在请求处理完之前,则不用处理超时事件,也不用设置定时器,函数只是简单的调用ngx_http_read_discarded_request_body()来读取并丢弃数据。
nginx请求体读取(二)的更多相关文章
- nginx请求体读取
上节说到nginx核心本身不会主动读取请求体,这个工作是交给请求处理阶段的模块来做,但是nginx核心提供了ngx_http_read_client_request_body()接口来读取请求体,另外 ...
- 使用Enablebuffering多次读取Asp Net Core 3.0 请求体 读取Request.Body流
原文:使用Enablebuffering多次读取Asp Net Core 请求体 使用Enablebuffering多次读取Asp Net Core 请求体 1 .Net Core 2.X时代 使用E ...
- nginx读取请求体
请求体的读取一般发生在nginx的content handler中,一些nginx内置的模块,比如proxy模块,fastcgi模块,uwsgi模块等,这些模块的行为必须将客户端过来的请求体(如果有的 ...
- http 请求体数据--ngx
HTTP包体的长度有可能非常大,不同业务可能对包体读取 处理不相同, 比如waf, 也许会读取body内容或者只是读取很少的前几十字节.所以根据不同业务特性,对http body 数据包处理方式不同, ...
- springboot请求体中的流只能读取一次的问题
场景交代 在springboot中添加拦截器进行权限拦截时,需要获取请求参数进行验证.当参数在url后面时(queryString)获取参数进行验证之后程序正常运行.但是,当请求参数在请求体中的时候, ...
- 使用Enablebuffering多次读取Asp Net Core 请求体
使用Enablebuffering多次读取Asp Net Core 请求体 1 .Net Core 2.X时代 使用EnableRewind倒带 public IActionResult Index( ...
- Nginx学习笔记(二) Nginx--connection&request
Nginx--connection&request 在Nginx中,主要包括了连接与处理两部分. connection 在src/core文件夹下包含有connection的源文件,Ngx_c ...
- 获取【请求体】数据的3种方式(精)(文末代码) request.getInputStream() request.getInputStream() request.getReader()
application/x- www-form-urlencoded是Post请求默认的请求体内容类型,也是form表单默认的类型.Servlet API规范中对该类型的请求内容提供了request. ...
- post请求体过大导致ngx.req.get_post_args()取不到参数体的问题
http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size 该地址对于client_body_buf ...
随机推荐
- grep;egrep;fgrep
-1 使用场景:搜索定位内容并输出(所在行) -2 三者区别: --1 grep 默认支持普通正则 --2 egrep 默认支持扩展正则 等同于 grep -E --3 fgrep 速度最快,不支持 ...
- python3.4+pyspider爬58同城(二)
之前使用python3.4+selenium实现了爬58同城的详细信息,这次用pyspider实现,网上搜了下,目前比较流行的爬虫框架就是pyspider和scrapy,但是scrapy不支持pyth ...
- 小希的迷宫(HDU 1272 并查集判断生成树)
小希的迷宫 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submi ...
- webservice 技术改进
Webservice 技术改进 1.不同系统不同语言之间的交互 基于http协议进行传输,使用REST服务实现WS 2.不同系统相同语言之间的交互 使用RPC(romate process call) ...
- android Unable to resolve target 'android-XX'错误和conversion to dalvik format failed with error 1错误
当用eclipse 导入一个已经存在的项目时,经常会遇见:Unable to resolve target 'android-XX' 类似的错误.这是因为导入的项目代码中project.propert ...
- SQL Server 2008空间数据应用系列十:使用存储过程生成GeoRSS聚合空间信息
原文:SQL Server 2008空间数据应用系列十:使用存储过程生成GeoRSS聚合空间信息 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Server 2 ...
- SmartBusinessDevFramework架构设计-3:考虑开源?
掖着藏着,终归不是好的办法.说的跟花一样,究竟里子是什么东西.一个好的被子,里料是羽绒还是棉花还是丝绵还是黑心棉?有时候,真的是看过之后,才能体验其中的奥秘. 这个架构的设计初衷,总体是为了方便.ne ...
- 【转】如何定制android源码的编译选项 & 后期安装? ---- 不错
原文网址:http://blog.sina.com.cn/s/blog_3e3fcadd0100z3o9.html Android编译过程比较长,配置起来也很麻烦.现仅就工作遇到的问题做个总结.所用硬 ...
- Eclipse/IDEA中使用Maven创建Web项目报错
Eclipse中的错误:Could not resolve archetype org.apache.maven.archetypes:maven-archetype-webapp-1.0.jar:R ...
- Android,使用Json发送数据中,使用的Java转义字符 KanKan原创
kankan原创 与php后台发送数据的时候.要求用到这样的格式. private void sendJson(){ //初始化自己定义的handler CashHandler handler = n ...