zoukankan      html  css  js  c++  java
  • nginx请求体读取(二)

    2,丢弃请求体

    一个模块想要主动的丢弃客户端发过的请求体,可以调用nginx核心提供的ngx_http_discard_request_body()接口,主动丢弃的原因可能有很多种,如模块的业务逻辑压根不需要请求体 ,客户端发送了过大的请求体,另外为了兼容http1.1协议的pipeline请求,模块有义务主动丢弃不需要的请求体。总之为了保持良好的客户端兼容性,nginx必须主动丢弃无用的请求体。下面开始分析ngx_http_discard_request_body()函数:

    [cpp] view plaincopy
     
    1. <span style="font-size:18px;">ngx_int_t  
    2. ngx_http_discard_request_body(ngx_http_request_t *r)  
    3. {  
    4.     ssize_t       size;  
    5.     ngx_event_t  *rev;  
    6.   
    7.     if (r != r->main || r->discard_body) {  
    8.         return NGX_OK;  
    9.     }  
    10.   
    11.     if (ngx_http_test_expect(r) != NGX_OK) {  
    12.         return NGX_HTTP_INTERNAL_SERVER_ERROR;  
    13.     }  
    14.   
    15.     rev = r->connection->read;  
    16.   
    17.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");  
    18.   
    19.     if (rev->timer_set) {  
    20.         ngx_del_timer(rev);  
    21.     }  
    22.   
    23.     if (r->headers_in.content_length_n <= 0 || r->request_body) {  
    24.         return NGX_OK;  
    25.     }  
    26.   
    27.     size = r->header_in->last - r->header_in->pos;  
    28.   
    29.     if (size) {  
    30.         if (r->headers_in.content_length_n > size) {  
    31.             r->header_in->pos += size;  
    32.             r->headers_in.content_length_n -= size;  
    33.   
    34.         } else {  
    35.             r->header_in->pos += (size_t) r->headers_in.content_length_n;  
    36.             r->headers_in.content_length_n = 0;  
    37.             return NGX_OK;  
    38.         }  
    39.     }  
    40.   
    41.     r->read_event_handler = ngx_http_discarded_request_body_handler;  
    42.   
    43.     if (ngx_handle_read_event(rev, 0) != NGX_OK) {  
    44.         return NGX_HTTP_INTERNAL_SERVER_ERROR;  
    45.     }  
    46.   
    47.     if (ngx_http_read_discarded_request_body(r) == NGX_OK) {  
    48.         r->lingering_close = 0;  
    49.   
    50.     } else {  
    51.         r->count++;  
    52.         r->discard_body = 1;  
    53.     }  
    54.   
    55.     return NGX_OK;  
    56. }  
    57. </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,这个函数每次读事件来时会被调用,先看一下它的源码:

    [cpp] view plaincopy
     
    1. <span style="font-size:18px;">void  
    2. ngx_http_discarded_request_body_handler(ngx_http_request_t *r)  
    3. {  
    4.     ...  
    5.   
    6.     c = r->connection;  
    7.     rev = c->read;  
    8.   
    9.     if (rev->timedout) {  
    10.         c->timedout = 1;  
    11.         c->error = 1;  
    12.         ngx_http_finalize_request(r, NGX_ERROR);  
    13.         return;  
    14.     }  
    15.   
    16.     if (r->lingering_time) {  
    17.         timer = (ngx_msec_t) (r->lingering_time - ngx_time());  
    18.   
    19.         if (timer <= 0) {  
    20.             r->discard_body = 0;  
    21.             r->lingering_close = 0;  
    22.             ngx_http_finalize_request(r, NGX_ERROR);  
    23.             return;  
    24.         }  
    25.   
    26.     } else {  
    27.         timer = 0;  
    28.     }  
    29.   
    30.     rc = ngx_http_read_discarded_request_body(r);  
    31.   
    32.     if (rc == NGX_OK) {  
    33.         r->discard_body = 0;  
    34.         r->lingering_close = 0;  
    35.         ngx_http_finalize_request(r, NGX_DONE);  
    36.         return;  
    37.     }  
    38.   
    39.     /* rc == NGX_AGAIN */  
    40.   
    41.     if (ngx_handle_read_event(rev, 0) != NGX_OK) {  
    42.         c->error = 1;  
    43.         ngx_http_finalize_request(r, NGX_ERROR);  
    44.         return;  
    45.     }  
    46.   
    47.     if (timer) {  
    48.   
    49.         clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);  
    50.   
    51.         timer *= 1000;  
    52.   
    53.         if (timer > clcf->lingering_timeout) {  
    54.             timer = clcf->lingering_timeout;  
    55.         }  
    56.   
    57.         ngx_add_timer(rev, timer);  
    58.     }  
    59. }  
    60. </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()来读取并丢弃数据。

  • 相关阅读:
    取得窗口大小和窗口位置兼容所有浏览器的js代码
    一个简单易用的导出Excel类
    如何快速启动chrome插件
    网页表单设计案例
    Ubuntu下的打包解包
    The source file is different from when the module was built. Would you like the debugger to use it anyway?
    FFisher分布
    kalman filter
    Group delay Matlab simulate
    24位位图格式解析
  • 原文地址:https://www.cnblogs.com/lidabo/p/4177108.html
Copyright © 2011-2022 走看看