zoukankan      html  css  js  c++  java
  • http 请求体数据处理2--ngx

    • HTTP 处理数据包, 有的业务不需要,此时只需要将数据包文读取后丢弃, 但是ngx 为什么还要提供一个丢弃接口呢???解决了什么问题??
    • ------对于HTTP模块而言,放弃接收包体就是简单地不处理包体了,可是对于HTTP框架而言,并不是不接收包体就可以的。因为对于客户端而言,通常
      会调用一些阻塞的发送方法来发送包体,如果HTTP框架一直不接收包体,会导致实现上不够健壮的客户端认为服务器超时无响应,因而简单地关
      闭连接,可这时Nginx模块可能还在处理这个连接。因此,HTTP模块中的放弃接收包体,对HTTP框架而言就是接收包体,但是接收后不做保存,直接丢弃。

      HTTP模块调用的ngx_http_discard_request_body方法用于第一次启动丢弃包体动作,而ngx_http_discarded_request_body_handler是作为请
      求的read_event_handler方法的,在有新的可读事件时会调用它处理包体。ngx_http_read discarded_request_body方法则是根据上述两个方法
      通用部分提取出的公共方法,用来读取包体且不做任何处理。
    ngx_int_t
    ngx_http_discard_request_body(ngx_http_request_t *r)
    {
        ssize_t       size;
        ngx_int_t     rc;
        ngx_event_t  *rev;
    
    #if (NGX_HTTP_V2)
        if (r->stream && r == r->main) {
            r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD;
            return NGX_OK;
        }
    #endif
    
        /*
         首先检查当前请求是一个子请求还是原始请求。为什么要检查这个呢?因为对于子请求而言,它不是来自客户端的请求,所以不存在处理HTTP
         请求包体的概念。如果当前请求是原始请求,则继续执行;如果它是子请求,则直接返回NGX_OK表示丢弃包体成功。检查ngx_http_request_t结构
         体的request_body成员,如果它已经被赋值过且不再为NULL空指针,则说明已经接收过包体了,这时也需要返回NGX_OK表示成功。
         */
        if (r != r->main || r->discard_body || r->request_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");
    
        /*
        检查请求连接上的读事件是否在定时器中,这是因为丢弃包体不用考虑超时问题(linger_timer例外,本章不考虑此情况)。如果读事件
        的timer set标志位为1,则从定时器中移除此事件。还要检查content-length头部,如果它的值小于或等于0,同样意味着可以直接返回
        NGX一OK,表示成功丢弃了全部包体。
         */
        if (rev->timer_set) {
            ngx_del_timer(rev, NGX_FUNC_LINE);
        }
        if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
            return NGX_OK;
        }
        
    
        size = r->header_in->last - r->header_in->pos;
    
        if (size || r->headers_in.chunked) {
            rc = ngx_http_discard_request_body_filter(r, r->header_in);
    
            if (rc != NGX_OK) {
                return rc;
            }
    
            if (r->headers_in.content_length_n == 0) {
                return NGX_OK;
            }
        }
    
        /*
            在接收HTTP头部时,还是要检查是否凑巧已经接收到完整的包体(如果包体很小,那么这是非常可能发生的事),如果已经接收到完整的包
        体,则直接返回NGX OK,表示丢弃包体成功,否则,说明需要多次的调度才能完成丢弃包体这一动作,此时把请求的read_event_handler
        成员设置为ngx_http_discarded_request_body_handler方法。
          */
        rc = ngx_http_read_discarded_request_body(r);
    
        if (rc == NGX_OK) {
            /* 返回NGX一OK表示已经接收到完整的包体了,这时将请求的lingering_close延时关闭标志位设为0,表示不需要为了包体的接收而
            延时关闭了,同时返回NGX—OK表示丢弃包体成功。 */
            r->lingering_close = 0;
            return NGX_OK;
        }
    
        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
            return rc;
        }
    
        //返回非NGX_OK表示Nginx的事件框架触发事件需要多次调度才能完成丢弃包体这一动作
    
        /* rc == NGX_AGAIN */
        
        r->read_event_handler = ngx_http_discarded_request_body_handler; //下次读事件到来时通过ngx_http_request_handler来调用
        /* 有可能执行了ngx_http_block_reading->ngx_http_block_reading,所以如果需要继续读取客户端请求,需要add event */
        if (ngx_handle_read_event(rev, 0, NGX_FUNC_LINE) != NGX_OK) { //调用ngx_handle_read_event方法把读事件添加到epoll中handle为ngx_http_request_handler
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        /*
        返回非NGX_OK表示Nginx的事件框架触发事件需要多次调度才能完成丢弃包体这一动作,于是先把引用计数加1,防止这边还在丢弃包体,
        而其他事件却已让请求意外销毁,引发严重错误。同时把ngx_http_request_t结构体的discard_body标志位置为1,表示正在丢弃包体,并
        返回NGX_OK,当然,达时的NGX_OK绝不表示已经成功地接收完包体,只是说明ngx_http_discard_request_body执行完毕而已。
         */
        r->count++;
        r->discard_body = 1;
    
        return NGX_OK;
    }

      HTTP模块调用的ngx_http_discard_request_body方法用于第一次启动丢弃包体动作,而ngx_http_discarded_request_body_handler是作为请
    求的read_event_handler方法的,在有新的可读事件时会调用它处理包体。ngx_http_read_discarded_request_body方法则是根据上述两个方法
    通用部分提取出的公共方法,用来读取包体且不做任何处理。

    void
    ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
    {
        ngx_int_t                  rc;
        ngx_msec_t                 timer;
        ngx_event_t               *rev;
        ngx_connection_t          *c;
        ngx_http_core_loc_conf_t  *clcf;
    
        c = r->connection;
        rev = c->read;
    
        //首先检查TCP连接上的读事件的timedout标志位,为1时表示已经超时,这时调用ngx_http_finalize_request方法结束请求,传递的参数是NGX_ERROR,流程结束
        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_msec_t) ngx_time();
    
            if ((ngx_msec_int_t) timer <= 0) {
                r->discard_body = 0;
                r->lingering_close = 0;
                ngx_http_finalize_request(r, NGX_ERROR);
                return;
            }
    
        } else {
            timer = 0;
        }
    
        //调用ngx_http_read_discarded_request_body方法接收包体,检测其返回值。
        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;
        }
    
        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
            c->error = 1;
            ngx_http_finalize_request(r, NGX_ERROR);
            return;
        }
    
        /* rc == NGX_AGAIN */
    
        if (ngx_handle_read_event(rev, 0, NGX_FUNC_LINE) != 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, NGX_FUNC_LINE);
        }
    }
    */ //ngx_http_read_discarded_request_body方法与ngx_http_do_read_client_request_body方法很类似
    static ngx_int_t
    ngx_http_read_discarded_request_body(ngx_http_request_t *r)
    {
        size_t     size;
        ssize_t    n;
        ngx_int_t  rc;
        ngx_buf_t  b;
        u_char     buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
    
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http read discarded body");
    
        ngx_memzero(&b, sizeof(ngx_buf_t));
    
        b.temporary = 1;
    
        for ( ;; ) {
    /*
        丢弃包体时请求的request_body成员实际上是NULL室指针,那么用什么变量来表示已经丢弃的包体有多大呢?实际上这时使用
    了请求ngx_http_request_t结构体headers_in成员里的content_length_n,最初它等于content-length头部,而每丢弃一部分包体,就会在
    content_length_n变量中减去相应的大小。因此,content_length_n表示还需要丢弃的包体长度,这里首先检查请求的content_length_n成员,
    如果它已经等于0,则表示已经接收到完整的包体,这时要把read event_handler重置为ngx_http_block_reading方法,表示如果再有可读
    事件被触发时,不做任何处理。同时返回NGX_OK,告诉上层的方法已经丢弃了所有包体。
      */
            if (r->headers_in.content_length_n == 0) {
                r->read_event_handler = ngx_http_block_reading;
                return NGX_OK;
            }
    
            /* 如果连接套接字的缓冲区上没有可读内容,则直接返回NGX_AGAIN,告诉上层方法需要等待读事件的触发,等待Nginx框架的再次调度。 */
            if (!r->connection->read->ready) {
                return NGX_AGAIN;
            }
    
            size = (size_t) ngx_min(r->headers_in.content_length_n,
                                    NGX_HTTP_DISCARD_BUFFER_SIZE);
    
            n = r->connection->recv(r->connection, buffer, size);
    
            if (n == NGX_ERROR) {
                r->connection->error = 1;
                return NGX_OK;
            }
    
            if (n == NGX_AGAIN) { //如果套接字缓冲区中没有读取到内容
                return NGX_AGAIN;
            }
    
            if (n == 0) { //如果客户端主动关闭了连接
                return NGX_OK;
            }
    
            b.pos = buffer;
            b.last = buffer + n;
    
            //接收到包体后,要更新请求的content_length_n成员,从而判断是否读取完毕,如果为0表示读取完毕,同时继续循环
            rc = ngx_http_discard_request_body_filter(r, &b);
    
            if (rc != NGX_OK) {
                return rc;
            }
        }
    }

  • 相关阅读:
    jquery.datatables中文使用说明
    jquery.datatables中文语言设置
    .net c# 视频剪切抓取缩略图
    Firefox os初体验
    maven 管理项目实践指南
    HTML5本地存储之Database Storage篇
    推荐开源软件
    HTML5实现网页元素的拖放操作
    分布式环境下基于redis解决在线客服坐席动态分配的问题
    HTML5本地存储之Web Storage篇
  • 原文地址:https://www.cnblogs.com/codestack/p/13513145.html
Copyright © 2011-2022 走看看