• 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()来读取并丢弃数据。

  • 相关阅读:
    [置顶] 强力开刷图论目录(不定期更新)
    强力开刷图论目录(图论再临)
    JQuery实现树的功能doc
    JQuery学习网站
    从相学来看眼神的奥秘
    asp之Repeater
    jquery上传文件
    眼神的心理秘密你了解多少
    jquery表单验证控件,下载地址和使用列子
    保护自己的生存法则:识人看眼神
  • 原文地址:https://www.cnblogs.com/lidabo/p/4177108.html
Copyright © 2020-2023  润新知