函数原型 static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t r, ngx_chain_t in);
此函数是nginx body解析中重要的函数,只要nginx读取到数据,就会调用此函数
函数定义如下
static ngx_int_t
ngx_http_request_body_filter(ngx_http_request_t r, ngx_chain_t in)
{
if (r->headers_in.chunked) {
return ngx_http_request_body_chunked_filter(r, in);
} else {
return ngx_http_request_body_length_filter(r, in);
}
}
以下只分析非 chunked 模式,也就是 ngx_http_request_body_length_filter 函数
函数定义 删除了不影响功能的行
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) { // rest 设置为请求头的 content-length
rb->rest = r->headers_in.content_length_n;
}
out = NULL;
ll = &out;
// 遍历输入的chain in
// 循环的结果是 out 指向的 chain,chain 中的 buf 对象是新创建的,
// 但是 buf 指向的内存仍和输入的 in 中的 buf 指向的是一致的,
// out 指向的 chain,最后一个 buf 中字段 last_buf 为 1
// 当然也可能读入的数据不足 content length 长度,此时 rest 不为 0
// 关键,out 指向的 chain 和 buf 对象都是新创建的,buf 指向的内存是 复用的,数据还在里面
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;
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_request_body_save_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;
}
看后面的函数,里面只调用了一个函数,ngx_chain_add_copy
static ngx_int_t
ngx_http_request_body_save_filter(ngx_http_request_t r, ngx_chain_t in)
{
ngx_http_request_body_t *rb;
rb = r->request_body;
// 合并相邻的 buf
if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return NGX_OK;
}
继续
// 把要处理的 chain 追加到 request body 的 chain 列表中
// 注意,只复制了 chain 对象本身,buf 对象是复用的,更不用说 buf 指向的内存了
ngx_int_t
ngx_chain_add_copy(ngx_pool_t pool, ngx_chain_t chain, ngx_chain_t in)
{
// 输入的 chain 是 request body 中的 bufs 的地址
// 输入的 in 是 要过滤的当前请求的 body
ngx_chain_t *cl, **ll;
ll = chain;
// 循环结束后,ll 指向最后一个 chain 的 next,next 又是指针,所以 ll 是二级指针
for (cl = *chain; cl; cl = cl->next) {
ll = &cl->next;
}
// 遍历 in
// 生成 chain 列表追加到 request body 的 bufs 后面,注意此处并没有生成 buf 对象,因此 buf 对象本身是复用的
while (in) {
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = in->buf;
*ll = cl;
ll = &cl->next;
in = in->next;
}
*ll = NULL;
return NGX_OK;
}
还有一个很重要的函数 ngx_chain_update_chains
调用参数为:r->pool, &rb->free, &rb->busy, &out, (ngx_buf_tag_t) &ngx_http_read_client_request_body
void
ngx_chain_update_chains(ngx_pool_t p, ngx_chain_t free, ngx_chain_t *busy,
ngx_chain_t **out, ngx_buf_tag_t tag)
{
// free, busy, out 都是二级指针
ngx_chain_t *cl;
if (*busy == NULL) { // busy 指向 out 指向的地方
*busy = *out;
} else {
for (cl = *busy; cl->next; cl = cl->next) { /* void */ } // cl 指向 busy chain 链条的最后一个
cl->next = *out;
}
// 上面的结果就是 out 挂载到了 busy 上
*out = NULL;
while (*busy) {
cl = *busy;
// buf 大小不是 0,说明还没有输出;request body 中的 bufs 是输出用的,如上所述,bufs 中指向的 buf 和 busy 指向的 buf 对象是一模一样的
if (ngx_buf_size(cl->buf) != 0) {
break;
}
if (cl->buf->tag != tag) { // tag 中存储的是 函数指针
*busy = cl->next;
ngx_free_chain(p, cl);
continue;
}
cl->buf->pos = cl->buf->start; // 复位
cl->buf->last = cl->buf->start;
*busy = cl->next;
cl->next = *free;
*free = cl; // 这个 chain 放到 free 列表的最前面
}
}
综上所述,ngx_http_request_body_filter 函数的目的就是要解析读取到的数据 in,追加到 request body 里的 bufs 列表中,busy 也指向要解析到的 chain 和 buf,同时 函数会更新 request body 中 rest 的值,此值表示当前请求还有多少字节没有读取。