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 ( ;; ) { if (r->headers_in.content_length_n == 0) { r->read_event_handler = ngx_http_block_reading; return NGX_OK; } 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; rc = ngx_http_discard_request_body_filter(r, &b); if (rc != NGX_OK) { return rc; } } }
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 (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"); if (rev->timer_set) { ngx_del_timer(rev); } 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; } } rc = ngx_http_read_discarded_request_body(r); if (rc == NGX_OK) { r->lingering_close = 0; return NGX_OK; } if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } /* rc == NGX_AGAIN */ 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; } r->count++; r->discard_body = 1; return NGX_OK; }
// 读取请求体数据并丢弃 // 使用固定的4k缓冲区接受丢弃的数据 // 一直读数据并解析,检查content_length_n,如果无数据可读就返回NGX_AGAIN // 因为使用的是et模式,所以必须把数据读完 // 需要使用回调ngx_http_discarded_request_body_handler读取数据 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; // 使用固定的4k缓冲区接受丢弃的数据 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; // 一直读数据并解析,检查content_length_n // 如果无数据可读就返回NGX_AGAIN // 需要使用回调ngx_http_discarded_request_body_handler读取数据 for ( ;; ) { // 判断content_length_n,为0就是已经读取完请求体 // 就不需要再读了,读事件设置为block,返回成功 if (r->headers_in.content_length_n == 0) { r->read_event_handler = ngx_http_block_reading; return NGX_OK; } // content_length_n大于0,表示还有数据需要读取 // 看读事件是否ready,即是否有数据可读 // 如果没数据那么就返回again // 需要使用回调ngx_http_discarded_request_body_handler读取数据 if (!r->connection->read->ready) { return NGX_AGAIN; } // 决定要读取的数据长度,不能超过4k // #define NGX_HTTP_DISCARD_BUFFER_SIZE 4096 size = (size_t) ngx_min(r->headers_in.content_length_n, NGX_HTTP_DISCARD_BUFFER_SIZE); // 调用底层recv读取数据 // 每次都从buffer的0位置放置数据,也就是丢弃之前读取的全部数据 n = r->connection->recv(r->connection, buffer, size); // 出错也允许,因为丢弃数据不需要关心 // 但需要置error标记 if (n == NGX_ERROR) { r->connection->error = 1; return NGX_OK; } // again表示无数据可读 // 需要使用回调ngx_http_discarded_request_body_handler读取数据 if (n == NGX_AGAIN) { return NGX_AGAIN; } // 读到了0字节,即连接被客户端关闭,client abort // 也是ok if (n == 0) { return NGX_OK; } // 读取了n字节的数据,但不使用 // 交给ngx_http_discard_request_body_filter来检查 b.pos = buffer; b.last = buffer + n; // 检查请求结构体里的缓冲区数据,丢弃 // 有content_length_n指定确切长度,那么只接收,不处理,移动缓冲区指针 // chunked数据需要解析数据 // content_length_n==0表示数据已经全部读完 // 就已经完成了丢弃任务,否则就要加入epoll读事件继续读 rc = ngx_http_discard_request_body_filter(r, &b); if (rc != NGX_OK) { return rc; } // 如果是ok,那么在for开始的地方检查content_length_n } }
// 要求nginx丢弃请求体数据 // 子请求不与客户端直接通信,不会有请求体的读取 // 已经设置了discard_body标志,表示已经调用了此函数 // request_body指针不空,表示已经调用了此函数 // 这三种情况就无需再启动读取handler,故直接返回成功 // 因为要丢弃数据,所以不需要检查超时,也就是说即使超时也不算是错误 // 如果头里的长度是0且不是chunked // 说明没有请求体数据,那么就无需再读,直接返回成功 // *一直*读数据并解析,检查content_length_n,如果无数据可读就返回NGX_AGAIN // 因为使用的是et模式,所以必须把数据读完 // 调用ngx_http_discard_request_body_filter检查收到的数据 // 使用回调ngx_http_discarded_request_body_handler读取数据 ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r) { ssize_t size; ngx_int_t rc; ngx_event_t *rev; // 子请求不与客户端直接通信,不会有请求体的读取 // 已经设置了discard_body标志,表示已经调用了此函数 // request_body指针不空,表示已经调用了此函数 // 这三种情况就无需再启动读取handler,故直接返回成功 // discard_body在本函数最末尾设置,防止重入 if (r != r->main || r->discard_body || r->request_body) { return NGX_OK; } #if (NGX_HTTP_V2) if (r->stream) { r->stream->skip_data = 1; return NGX_OK; } #endif 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); } // 如果头里的长度未设置、或者是0且不是chunked // 说明没有请求体数据,那么就无需再读,直接返回成功 if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) { return NGX_OK; } // 检查缓冲区里在解析完头后是否还有数据 // 也就是说之前可能读取了部分请求体数据 size = r->header_in->last - r->header_in->pos; // 有数据,或者是chunked数据 // 有可能已经读取了一些请求体数据,所以先检查一下 if (size || r->headers_in.chunked) { // 检查请求结构体里的缓冲区数据,丢弃 // 有content_length_n指定确切长度,那么只接收,不处理,移动缓冲区指针 // chunked数据需要解析数据 rc = ngx_http_discard_request_body_filter(r, r->header_in); // 不是ok表示出错,不能再读取数据 if (rc != NGX_OK) { return rc; } // content_length_n==0表示数据已经全部读完 // 就已经完成了丢弃任务,否则就要加入epoll读事件继续读 if (r->headers_in.content_length_n == 0) { return NGX_OK; } } // 走到这里,表明content_length_n>=0,还有数据要读取 // 接下来就读取请求体数据并丢弃 // 使用固定的4k缓冲区接受丢弃的数据 // 一直读数据并解析,检查content_length_n,如果无数据可读就返回NGX_AGAIN // 因为使用的是et模式,所以必须把数据读完 // 需要使用回调ngx_http_discarded_request_body_handler读取数据 rc = ngx_http_read_discarded_request_body(r); if (rc == NGX_OK) { r->lingering_close = 0; return NGX_OK; } // 出错 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } /* rc == NGX_AGAIN */ // 读事件not ready,无数据可读,那么就要在epoll里加入读事件和handler // 注意,不再需要加入定时器 // 之后再有数据来均由ngx_http_discarded_request_body_handler处理 // 里面还是调用ngx_http_read_discarded_request_body读数据 r->read_event_handler = ngx_http_discarded_request_body_handler; // 注意,读事件的handler实际上是ngx_http_request_handler // 但最终会调用r->read_event_handler if (ngx_handle_read_event(rev, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } // 引用计数器增加,表示此请求还有关联的操作,不能直接销毁 r->count++; // 设置丢弃标志,防止再次进入本函数 r->discard_body = 1; return NGX_OK; }
//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; } } }
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_SPDY) if (r->spdy_stream && r == r->main) { r->spdy_stream->skip_data = NGX_SPDY_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来调用 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; }