static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) { size_t size; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *cl, *out, *tl, **ll; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; rb = r->request_body; if (rb->rest == -1) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http request body chunked filter"); rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t)); if (rb->chunked == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->headers_in.content_length_n = 0; rb->rest = 3; } out = NULL; ll = &out; for (cl = in; cl; cl = cl->next) { for ( ;; ) { ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, "http body chunked buf " "t:%d f:%d %p, pos %p, size: %z file: %O, size: %O", cl->buf->temporary, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked); if (rc == NGX_OK) { /* a chunk has been parsed successfully */ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (clcf->client_max_body_size && clcf->client_max_body_size - r->headers_in.content_length_n < rb->chunked->size) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client intended to send too large chunked " "body: %O+%O bytes", r->headers_in.content_length_n, rb->chunked->size); r->lingering_close = 1; return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; } tl = ngx_chain_get_free_buf(r->pool, &rb->free); if (tl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b = tl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->temporary = 1; b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; b->start = cl->buf->pos; b->pos = cl->buf->pos; b->last = cl->buf->last; b->end = cl->buf->end; b->flush = r->request_body_no_buffering; *ll = tl; ll = &tl->next; size = cl->buf->last - cl->buf->pos; if ((off_t) size > rb->chunked->size) { cl->buf->pos += (size_t) rb->chunked->size; r->headers_in.content_length_n += rb->chunked->size; rb->chunked->size = 0; } else { rb->chunked->size -= size; r->headers_in.content_length_n += size; cl->buf->pos = cl->buf->last; } b->last = cl->buf->pos; continue; } if (rc == NGX_DONE) { /* a whole response has been parsed successfully */ rb->rest = 0; tl = ngx_chain_get_free_buf(r->pool, &rb->free); if (tl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b = tl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->last_buf = 1; *ll = tl; ll = &tl->next; break; } if (rc == NGX_AGAIN) { /* set rb->rest, amount of data we want to see next time */ rb->rest = rb->chunked->length; break; } /* invalid */ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client sent invalid chunked body"); return NGX_HTTP_BAD_REQUEST; } } rc = ngx_http_top_request_body_filter(r, out); ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, (ngx_buf_tag_t) &ngx_http_read_client_request_body); return rc; }
// 处理确定长度的请求体数据,参数in是已经读取的数据链表 // 先看free里是否有空闲节点,有则直接使用 // 如果没有,就从内存池的空闲链表里获取 // 创建新的链表节点,加入到out链表里 // 这里只是指针操作,没有内存拷贝 // 调用请求体过滤链表,对数据进行过滤处理 // 实际上是ngx_http_request_body_save_filter // 拷贝in链表里的buf到rb->bufs里,不是直接连接 // 最后把用完的ngx_chaint_t挂到free里供复用,提高效率 static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) { size_t size; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *cl, *tl, *out, **ll; ngx_http_request_body_t *rb; // 请求体数据的结构体 rb = r->request_body; // -1表示无效,即还没有开始读取 // 那么剩余字节数就是content_length_n if (rb->rest == -1) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http request body content length filter"); rb->rest = r->headers_in.content_length_n; } out = NULL; ll = &out; // 遍历已经读取的数据链表 // 创建新的链表节点,加入到out链表里 // 这里只是指针操作,没有内存拷贝 for (cl = in; cl; cl = cl->next) { // 已经读完,无剩余字节,那么就结束循环 if (rb->rest == 0) { break; } // 先看free里是否有空闲节点,有则直接使用 // 如果没有,就从内存池的空闲链表里获取 // 最开始rb->free是空的,所以要从内存池里获取 tl = ngx_chain_get_free_buf(r->pool, &rb->free); if (tl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } // 这个缓冲区对象并不持有实际的内存块 b = tl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); // 调整缓冲区的指针,指向链表节点里的地址 b->temporary = 1; b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; b->start = cl->buf->pos; b->pos = cl->buf->pos; b->last = cl->buf->last; b->end = cl->buf->end; b->flush = r->request_body_no_buffering; // 计算此缓冲区里的数据长度 size = cl->buf->last - cl->buf->pos; // 剩余的数据还很多 // 这里处理的是in链表,不是out // 消费的是in链表里的缓冲区 if ((off_t) size < rb->rest) { // 消费缓冲区里的数据 cl->buf->pos = cl->buf->last; // 剩余字节数减少 rb->rest -= size; } else { // 这里rest字节已经读取足够了 // 消费缓冲区里的数据 cl->buf->pos += (size_t) rb->rest; // 剩余数据全部读取完毕,rest=0 rb->rest = 0; // 如果还有多余的数据也不再考虑,调整last b->last = cl->buf->pos; // 标记为最后一块数据,重要 b->last_buf = 1; } // 加入链表 *ll = tl; ll = &tl->next; } // 这里调用请求体过滤链表,对数据进行过滤处理 // 实际上是ngx_http_request_body_save_filter // 从内存池里分配节点 // 拷贝in链表里的buf到rb->bufs里,不是直接连接 // 同样是指针操作,没有内存拷贝 // 如果要求写磁盘文件,那么调用ngx_http_write_request_body rc = ngx_http_top_request_body_filter(r, out); // 用于处理请求体数据,更新free/busy几个链表指针 // 先把out链表挂到busy指针上 // 遍历busy链表 // 缓冲区为空,说明可以复用,应该挂到free链表里 // 把缓冲区复位,都指向start,即完全可用 // 此节点不应该在busy里,从busy链表摘除 // 加入到free链表里,供以后复用 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, (ngx_buf_tag_t) &ngx_http_read_client_request_body); return rc; }
static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) { size_t size; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *cl, *tl, *out, **ll; ngx_http_request_body_t *rb; rb = r->request_body; if (rb->rest == -1) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http request body content length filter"); rb->rest = r->headers_in.content_length_n; } out = NULL; ll = &out; for (cl = in; cl; cl = cl->next) { if (rb->rest == 0) { break; } tl = ngx_chain_get_free_buf(r->pool, &rb->free); if (tl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b = tl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->temporary = 1; b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; b->start = cl->buf->pos; b->pos = cl->buf->pos; b->last = cl->buf->last; b->end = cl->buf->end; b->flush = r->request_body_no_buffering; size = cl->buf->last - cl->buf->pos; if ((off_t) size < rb->rest) { cl->buf->pos = cl->buf->last; rb->rest -= size; } else { cl->buf->pos += (size_t) rb->rest; rb->rest = 0; b->last = cl->buf->pos; b->last_buf = 1; } *ll = tl; ll = &tl->next; } rc = ngx_http_top_request_body_filter(r, out); ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, (ngx_buf_tag_t) &ngx_http_read_client_request_body); return rc; }
static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) { //in其实也是从r->request_body->buf中来的 size_t size; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *cl, *tl, *out, **ll; ngx_http_request_body_t *rb; rb = r->request_body; if (rb->rest == -1) {//第一次执行该函数 rest 设置为请求头的 content-length ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http request body content length filter"); rb->rest = r->headers_in.content_length_n; } out = NULL; ll = &out; //把in中的所有数据节点数据连接在一起添加到out头中 for (cl = in; cl; cl = cl->next) {//遍历r->request_body中的所有buf if (rb->rest == 0) {//表示包体数据已经处理完毕 break; } tl = ngx_chain_get_free_buf(r->pool, &rb->free); //从free链表中poll中获取ngx_chain_t空间,如果free中为空,则直接创建 if (tl == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b = tl->buf; //获取tl中的buf ngx_memzero(b, sizeof(ngx_buf_t)); //b的相关成员指针指向in中的各个节点里面的对于成员,即新的b指向获取到的包体数据中的各个ngx_buf_t //b指向的数据和cl->buf指向的数据时一致的,最后实际读取到的数据的头尾用b指向 b->temporary = 1; b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; b->start = cl->buf->pos; b->pos = cl->buf->pos; b->last = cl->buf->last; b->end = cl->buf->end; b->flush = r->request_body_no_buffering; size = cl->buf->last - cl->buf->pos; if ((off_t) size < rb->rest) { //说明数据还不够r->headers_in.content_length_n; cl->buf->pos = cl->buf->last; rb->rest -= size; //已经获取到size了,还差rb->rest才到content_length_n } else { //说明已经获取到了content_length_n这么多包体,也就是包体已经够了 cl->buf->pos += (size_t) rb->rest; //注意这时候的last没有移动,如果头部行content-length:len中的len小于实际携带的包体数据,就会造成pos小于last rb->rest = 0;//表示包体有这么多了 b->last = cl->buf->pos; //实际读到的数据比我们期望的rest数据多,因此我们截取实际需要的数据即可 b->last_buf = 1; //标记该buf是组成包体数据的buf数组中的最后一个ngx_buf_t } *ll = tl; ll = &tl->next; //前面创建的所有tl(ngx_chain_t)通过next连接在一起,所有这些节点的头部是前面的out } //把in表中的数据(通过从新创建ngx_buf_t指向in表中各个数据的成员)连接到r->request_body->bufs中,这样所有的out数据都会添加到r->request_body->bufs数组中 //通过新创建的ngx_chain_t(之前out中的ngx_chain_t就通过下面的ngx_chain_update_chains进行回收)中的各个指针来执行新读取到的out数据,ngx_http_request_body_save_filter,通过该函数后所有的out数据都连接到rb->bufs中缓存了 rc = ngx_http_top_request_body_filter(r, out); //ngx_http_request_body_save_filter //rb中的bufs链表中的成员中的各个指针指向读取到的原始数据位置(如pos指向读取到数据的头,last指向读取到数据的尾部) //rb->busy最初是直接从out中拷贝的,指向的数据空间最初与bufs是一样的,但是一旦http模块从网络读取到的数据中解析出一部分数据,那么free中成员的各个指针就会移动,例如pos会 //向last方向移动,直到pos=list,这时候busy中的这个ngx_buf_t节点成员就可以从busy链表中取出,然后添加到free链表中 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, (ngx_buf_tag_t) &ngx_http_read_client_request_body); return rc; }