Пример #1
0
    static ngx_int_t body_filter(ngx_http_request_t *r, ngx_chain_t *in)
    try
    {
        if(!in)
        {
            return NGX_OK;  //this_filter::next(r, in);
        }

        if( this_module::ctx().empty(r) ||
           !this_module::data(r).do_hook)
        {
            return this_filter::next(r, in);
        }

        // parent ctx data
        auto& pr_ctx_data = this_module::data(r->parent);

        // copy to parent ctx
        auto rc = ngx_chain_add_copy(r->pool, &pr_ctx_data.body, in);

        NgxException::require(rc);

        return NGX_OK;      // stop body filter chain
    }
    catch(const NgxException& e)
    {
        return e.code();
    }
//通过ngx_http_top_request_body_filter调用
ngx_int_t
ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
#if (NGX_DEBUG)
    ngx_chain_t               *cl;
#endif
    ngx_http_request_body_t   *rb;

    rb = r->request_body;

#if (NGX_DEBUG)

    for (cl = rb->bufs; cl; cl = cl->next) {
        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                       "http body old 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);
    }

    for (cl = in; cl; cl = cl->next) {
        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                       "http body new 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);
    }

#endif

    /* TODO: coalesce neighbouring buffers */

    if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    //当一个rb->buf填满后就会通过ngx_http_write_request_body把bufs链表中的所有ngx_chain_t->ngx_buf_t中指向的数据
    //写入到临时文件,并把ngx_buf_t结构加入poll->chain,通过poll统一释放他们
    if (rb->rest > 0
        && rb->buf && rb->buf->last == rb->buf->end
        && !r->request_body_no_buffering)
    {
        //需要缓存数据,并且rb->buf数据已经解析完毕,并且buf已经满了,但是包体还没有读完,那么就可以把buf中的数据写入临时文件,
        //这样改buf指向的内存空间在该函数退出后可以继续用来读取数据
        if (ngx_http_write_request_body(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    }

    return NGX_OK;
}
Пример #3
0
ngx_int_t
ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
#if (NGX_DEBUG)
    ngx_chain_t               *cl;
#endif
    ngx_http_request_body_t   *rb;

    rb = r->request_body;

#if (NGX_DEBUG)

    for (cl = rb->bufs; cl; cl = cl->next) {
        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                       "http body old 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);
    }

    for (cl = in; cl; cl = cl->next) {
        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                       "http body new 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);
    }

#endif

    /* TODO: coalesce neighbouring buffers */

    if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if (rb->rest > 0
        && rb->buf && rb->buf->last == rb->buf->end
        && !r->request_body_no_buffering)
    {
        if (ngx_http_write_request_body(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    }

    return NGX_OK;
}
static ngx_int_t
ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_http_postponed_request_t  *pr, **ppr;

	//判断postponed链最末尾(必定是在最末尾)的ngx_http_postponed_request_t结构存放的是否就是数据(即pr->request == NULL),
	//如果是则把数据直接添加进去(具体也就是ngx_http_postponed_request_t结构体对象的out链)即可,
	//否则的话,就需在postponed链表末尾新建一个对应的ngx_http_postponed_request_t结构来存放该数据。
    if (r->postponed)
	{
        for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }

        if (pr->request == NULL)
		{
            goto found;
        }

        ppr = &pr->next;

    }
	else 
	{
        ppr = &r->postponed;
    }

    pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
    if (pr == NULL)
	{
        return NGX_ERROR;
    }

    *ppr = pr;

    pr->request = NULL;
    pr->out = NULL;
    pr->next = NULL;

found:
	//最终复制in到pr->out,也就是保存request 需要发送的数据。  
    if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK)
	{
        return NGX_OK;
    }

    return NGX_ERROR;
}
Пример #5
0
static ngx_int_t
ngx_http_subs_body_filter_init_context(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_http_subs_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_subs_filter_module);

    r->connection->buffered |= NGX_HTTP_SUB_BUFFERED;

    ctx->in = NULL;

    if (in) {
        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
            return NGX_ERROR;
        }
    }

#if SUBS_DEBUG
    if (ngx_buf_size(ctx->line_in) > 0) {
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "subs line in buffer: %p, size:%uz",
                       ctx->line_in, ngx_buf_size(ctx->line_in));
    }
#endif

#if SUBS_DEBUG
    ngx_chain_t               *cl;

    for (cl = ctx->in; cl; cl = cl->next) {
        if (cl->buf) {
            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "subs in buffer:%p, size:%uz, "
                           "flush:%d, last_buf:%d",
                           cl->buf, ngx_buf_size(cl->buf),
                           cl->buf->flush, cl->buf->last_buf);
        }
    }
#endif

    ctx->last_out = &ctx->out;
    ctx->out_buf  = NULL;

    return NGX_OK;
}
static ngx_int_t
ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_http_postponed_request_t  *pr, **ppr;

    if (r->postponed) {
        for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }

        if (pr->request == NULL) {
            goto found;
        }

        ppr = &pr->next;

    } else {
        ppr = &r->postponed;
    }

	//	申请一个空的postponed_request_t节点用于存放数据,并挂载到 r->postponed 末尾
    pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
    if (pr == NULL) {
        return NGX_ERROR;
    }

    *ppr = pr;

    pr->request = NULL;
    pr->out = NULL;
    pr->next = NULL;

found:

	//	拷贝in到pr->out中
    if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) {
        return NGX_OK;
    }

    return NGX_ERROR;
}
static ngx_int_t
ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    int                     rc;
    ngx_uint_t              flush;
    ngx_chain_t            *cl;
    ngx_http_gunzip_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_gunzip_filter_module);

    if (ctx == NULL || ctx->done) {
        return ngx_http_next_body_filter(r, in);
    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http gunzip filter");

    if (!ctx->started) {
        if (ngx_http_gunzip_filter_inflate_start(r, ctx) != NGX_OK) {
            goto failed;
        }
    }

    if (in) {
        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
            goto failed;
        }
    }

    if (ctx->nomem) {

        /* flush busy buffers */

        if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
            goto failed;
        }

        cl = NULL;

        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
                                (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
        ctx->nomem = 0;
        flush = 0;

    } else {
        flush = ctx->busy ? 1 : 0;
    }

    for ( ;; ) {

        /* cycle while we can write to a client */

        for ( ;; ) {

            /* cycle while there is data to feed zlib and ... */

            rc = ngx_http_gunzip_filter_add_data(r, ctx);

            if (rc == NGX_DECLINED) {
                break;
            }

            if (rc == NGX_AGAIN) {
                continue;
            }


            /* ... there are buffers to write zlib output */

            rc = ngx_http_gunzip_filter_get_buf(r, ctx);

            if (rc == NGX_DECLINED) {
                break;
            }

            if (rc == NGX_ERROR) {
                goto failed;
            }

            rc = ngx_http_gunzip_filter_inflate(r, ctx);

            if (rc == NGX_OK) {
                break;
            }

            if (rc == NGX_ERROR) {
                goto failed;
            }

            /* rc == NGX_AGAIN */
        }

        if (ctx->out == NULL && !flush) {
            return ctx->busy ? NGX_AGAIN : NGX_OK;
        }

        rc = ngx_http_next_body_filter(r, ctx->out);

        if (rc == NGX_ERROR) {
            goto failed;
        }

        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
                                (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
        ctx->last_out = &ctx->out;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "gunzip out: %p", ctx->out);

        ctx->nomem = 0;
        flush = 0;

        if (ctx->done) {
            return rc;
        }
    }

    /* unreachable */

failed:

    ctx->done = 1;

    return NGX_ERROR;
}
/* The brotli body is almost identical to gzip body */
static ngx_int_t
ngx_http_brotli_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    int                    rc;
    ngx_uint_t             flush;
    ngx_chain_t           *cl;
    ngx_http_brotli_ctx_t *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_brotli_filter_module);

    if (ctx == NULL || ctx->done || r->header_only) {
        return ngx_http_next_body_filter(r, in);
    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http brotli filter");

    if (ctx->bro == NULL) {
        if (ngx_http_brotli_filter_start(r, ctx) != NGX_OK) {
            goto failed;
        }
    }

    if (in) {
        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
            goto failed;
        }
        r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
    }

    if (ctx->nomem) {
        /* flush busy buffers */
        if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
            goto failed;
        }

        cl = NULL;

        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
                                (ngx_buf_tag_t) &ngx_http_brotli_filter_module);
        ctx->nomem = 0;
        flush = 0;

    } else {
        flush = ctx->busy ? 1 : 0;
    }

    for ( ;; ) {

        /* cycle while we can write to a client */

        for ( ;; ) {

            /* cycle while there is data to feed botli and ... */

            rc = ngx_http_brotli_filter_add_data(r, ctx);

            if (rc == NGX_DECLINED) {
                break;
            }

            if (rc == NGX_AGAIN) {
                continue;
            }


            /* ... there are buffers to write brotli output */

            rc = ngx_http_brotli_filter_get_buf(r, ctx);

            if (rc == NGX_DECLINED) {
                break;
            }

            if (rc == NGX_ERROR) {
                goto failed;
            }


            rc = ngx_http_brotli_filter_compress(r, ctx);

            if (rc == NGX_OK) {
                break;
            }

            if (rc == NGX_ERROR) {
                goto failed;
            }

            /* rc == NGX_AGAIN */
        }

        if (ctx->out == NULL && !flush) {
            ngx_http_brotli_filter_free_copy_buf(r, ctx);
            return ctx->busy ? NGX_AGAIN : NGX_OK;
        }

        rc = ngx_http_next_body_filter(r, ctx->out);

        if (rc == NGX_ERROR) {
            goto failed;
        }

        ngx_http_brotli_filter_free_copy_buf(r, ctx);

        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
                                (ngx_buf_tag_t) &ngx_http_brotli_filter_module);
        ctx->last_out = &ctx->out;

        ctx->nomem = 0;
        flush = 0;

        if (ctx->done) {
            return rc;
        }
    }

    /* unreachable */

failed:

    ctx->done = 1;

    ngx_http_brotli_filter_free_copy_buf(r, ctx);
    BrotliEncoderDestroyInstance(ctx->bro);
    ctx->bro = NULL;

    return NGX_ERROR;
}
// the implementation is based on ngx_http_sub_body_filter
static ngx_int_t
ngx_http_secure_token_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
	ngx_http_secure_token_processor_output_t output;
	ngx_http_secure_token_loc_conf_t *conf;
	ngx_http_secure_token_ctx_t* ctx;
	ngx_chain_t* in_copy = NULL;
	ngx_chain_t* cl;
	ngx_buf_t* buf;
	ngx_buf_t* b;
	ngx_int_t rc;
	u_char* copy_start;
	u_char* pos = NULL;

	ctx = ngx_http_get_module_ctx(r, ngx_http_secure_token_filter_module);

	if (ctx == NULL) {
		return ngx_http_next_body_filter(r, in);
	}

	if ((in == NULL
		&& ctx->busy == NULL))
	{
		return ngx_http_next_body_filter(r, in);
	}

	conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_token_filter_module);

	/* add the incoming chain to the chain in_copy */

	if (in) {
		if (ngx_chain_add_copy(r->pool, &in_copy, in) != NGX_OK) {
			return NGX_ERROR;
		}
	}

	ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
		"http secure token filter \"%V\"", &r->uri);

	buf = NULL;

	while (in_copy || buf) {

		if (buf == NULL) {
			buf = in_copy->buf;
			in_copy = in_copy->next;
			pos = buf->pos;
		}

		b = NULL;

		while (pos < buf->last) {

			copy_start = pos;

			output.copy_input = 1;
			output.output_buffer.len = 0;
			output.token_index = -1;

			rc = ctx->process(
				r,
				&conf->processor_conf,
				ctx->processor_params,
				&pos, 
				buf->last,
				(u_char*)ctx + ctx->processor_context_offset, 
				&output);

			ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
				"process: %d, %p-%p",
				rc, copy_start, pos);

			if (rc == NGX_ERROR) {
				return rc;
			}

			if (output.copy_input && copy_start != pos) {

				cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
				if (cl == NULL) {
					return NGX_ERROR;
				}

				b = cl->buf;

				ngx_memcpy(b, buf, sizeof(ngx_buf_t));

				b->pos = copy_start;
				b->last = pos;
				b->shadow = NULL;
				b->last_buf = 0;
				b->last_in_chain = 0;
				b->recycled = 0;

				if (b->in_file) {
					b->file_last = b->file_pos + (b->last - buf->pos);
					b->file_pos += b->pos - buf->pos;
				}

				*ctx->last_out = cl;
				ctx->last_out = &cl->next;
			}

			if (output.output_buffer.len != 0)
			{
				cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
				if (cl == NULL) {
					return NGX_ERROR;
				}

				b = cl->buf;

				ngx_memzero(b, sizeof(ngx_buf_t));

				b->memory = 1;
				b->pos = output.output_buffer.data;
				b->last = output.output_buffer.data + output.output_buffer.len;

				*ctx->last_out = cl;
				ctx->last_out = &cl->next;
			}

			if (output.token_index >= 0 && ctx->token.len != 0)
			{
				cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
				if (cl == NULL) {
					return NGX_ERROR;
				}

				b = cl->buf;

				rc = ngx_http_secure_token_get_token_buffer(r, ctx, b, output.token_index);
				if (rc != NGX_OK)
				{
					return rc;
				}

				*ctx->last_out = cl;
				ctx->last_out = &cl->next;
			}

			continue;
		}

		if (buf->last_buf || buf->flush || buf->sync
			|| ngx_buf_in_memory(buf))
		{
			if (b == NULL) {
				cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
				if (cl == NULL) {
					return NGX_ERROR;
				}

				b = cl->buf;

				ngx_memzero(b, sizeof(ngx_buf_t));

				b->sync = 1;

				*ctx->last_out = cl;
				ctx->last_out = &cl->next;
			}

			b->last_buf = buf->last_buf;
			b->last_in_chain = buf->last_in_chain;
			b->flush = buf->flush;
			b->shadow = buf;

			b->recycled = buf->recycled;
		}

		buf = NULL;
	}

	if (ctx->out == NULL && ctx->busy == NULL) {
		return NGX_OK;
	}

	return ngx_http_secure_token_output(r, ctx);
}
Пример #10
0
static ngx_int_t
ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_int_t                  rc;
    ngx_buf_t                 *b;
    ngx_chain_t               *cl;
    ngx_http_sub_ctx_t        *ctx;
    ngx_http_sub_loc_conf_t   *slcf;

    ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module);

    if (ctx == NULL) {
        return ngx_http_next_body_filter(r, in);
    }

    if ((in == NULL
         && ctx->buf == NULL
         && ctx->in == NULL
         && ctx->busy == NULL))
    {
        return ngx_http_next_body_filter(r, in);
    }

    if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) {

        if (ctx->busy) {
            if (ngx_http_sub_output(r, ctx) == NGX_ERROR) {
                return NGX_ERROR;
            }
        }

        return ngx_http_next_body_filter(r, in);
    }

    /* add the incoming chain to the chain ctx->in */

    if (in) {
        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
            return NGX_ERROR;
        }
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http sub filter \"%V\"", &r->uri);

    while (ctx->in || ctx->buf) {

        if (ctx->buf == NULL) {
            ctx->buf = ctx->in->buf;
            ctx->in = ctx->in->next;
            ctx->pos = ctx->buf->pos;
        }

        if (ctx->state == sub_start_state) {
            ctx->copy_start = ctx->pos;
            ctx->copy_end = ctx->pos;
        }

        b = NULL;

        while (ctx->pos < ctx->buf->last) {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "saved: \"%V\" state: %d", &ctx->saved, ctx->state);

            rc = ngx_http_sub_parse(r, ctx);

            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "parse: %d, looked: \"%V\" %p-%p",
                           rc, &ctx->looked, ctx->copy_start, ctx->copy_end);

            if (rc == NGX_ERROR) {
                return rc;
            }

            if (ctx->saved.len) {

                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                               "saved: \"%V\"", &ctx->saved);

                cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
                if (cl == NULL) {
                    return NGX_ERROR;
                }

                b = cl->buf;

                ngx_memzero(b, sizeof(ngx_buf_t));

                b->pos = ngx_pnalloc(r->pool, ctx->saved.len);
                if (b->pos == NULL) {
                    return NGX_ERROR;
                }

                ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len);
                b->last = b->pos + ctx->saved.len;
                b->memory = 1;

                *ctx->last_out = cl;
                ctx->last_out = &cl->next;

                ctx->saved.len = 0;
            }

            if (ctx->copy_start != ctx->copy_end) {

                cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
                if (cl == NULL) {
                    return NGX_ERROR;
                }

                b = cl->buf;

                ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));

                b->pos = ctx->copy_start;
                b->last = ctx->copy_end;
                b->shadow = NULL;
                b->last_buf = 0;
                b->last_in_chain = 0;
                b->recycled = 0;

                if (b->in_file) {
                    b->file_last = b->file_pos + (b->last - ctx->buf->pos);
                    b->file_pos += b->pos - ctx->buf->pos;
                }

                *ctx->last_out = cl;
                ctx->last_out = &cl->next;
            }

            if (ctx->state == sub_start_state) {
                ctx->copy_start = ctx->pos;
                ctx->copy_end = ctx->pos;

            } else {
                ctx->copy_start = NULL;
                ctx->copy_end = NULL;
            }

            if (ctx->looked.len > (size_t) (ctx->pos - ctx->buf->pos)) {
                ctx->saved.len = ctx->looked.len - (ctx->pos - ctx->buf->pos);
                ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len);
            }

            if (rc == NGX_AGAIN) {
                continue;
            }


            /* rc == NGX_OK */

            cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
            if (cl == NULL) {
                return NGX_ERROR;
            }

            b = cl->buf;

            ngx_memzero(b, sizeof(ngx_buf_t));

            slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);

            if (ctx->sub.data == NULL) {

                if (ngx_http_complex_value(r, &slcf->value, &ctx->sub)
                    != NGX_OK)
                {
                    return NGX_ERROR;
                }
            }

            if (ctx->sub.len) {
                b->memory = 1;
                b->pos = ctx->sub.data;
                b->last = ctx->sub.data + ctx->sub.len;

            } else {
                b->sync = 1;
            }

            *ctx->last_out = cl;
            ctx->last_out = &cl->next;

            ctx->once = slcf->once;

            continue;
        }

        if (ctx->looked.len
            && (ctx->buf->last_buf || ctx->buf->last_in_chain))
        {
            cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
            if (cl == NULL) {
                return NGX_ERROR;
            }

            b = cl->buf;

            ngx_memzero(b, sizeof(ngx_buf_t));

            b->pos = ctx->looked.data;
            b->last = b->pos + ctx->looked.len;
            b->memory = 1;

            *ctx->last_out = cl;
            ctx->last_out = &cl->next;

            ctx->looked.len = 0;
        }

        if (ctx->buf->last_buf || ctx->buf->flush || ctx->buf->sync
            || ngx_buf_in_memory(ctx->buf))
        {
            if (b == NULL) {
                cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
                if (cl == NULL) {
                    return NGX_ERROR;
                }

                b = cl->buf;

                ngx_memzero(b, sizeof(ngx_buf_t));

                b->sync = 1;

                *ctx->last_out = cl;
                ctx->last_out = &cl->next;
            }

            b->last_buf = ctx->buf->last_buf;
            b->last_in_chain = ctx->buf->last_in_chain;
            b->flush = ctx->buf->flush;
            b->shadow = ctx->buf;

            b->recycled = ctx->buf->recycled;
        }

        ctx->buf = NULL;

        ctx->saved.len = ctx->looked.len;
        ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->looked.len);
    }

    if (ctx->out == NULL && ctx->busy == NULL) {
        return NGX_OK;
    }

    return ngx_http_sub_output(r, ctx);
}
static ngx_int_t
ngx_http_replace_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_int_t                  rc;
    ngx_buf_t                 *b;
    ngx_str_t                 *sub;
    ngx_chain_t               *cl, *cur = NULL, *rematch = NULL;

    ngx_http_replace_ctx_t        *ctx;
    ngx_http_replace_loc_conf_t   *rlcf;

    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_replace_filter_module);

    ctx = ngx_http_get_module_ctx(r, ngx_http_replace_filter_module);

    if (ctx == NULL) {
        return ngx_http_next_body_filter(r, in);
    }

    if ((in == NULL
         && ctx->buf == NULL
         && ctx->in == NULL
         && ctx->busy == NULL))
    {
        return ngx_http_next_body_filter(r, in);
    }

    if ((ctx->once || ctx->vm_done) && (ctx->buf == NULL || ctx->in == NULL)) {

        if (ctx->busy) {
            if (ngx_http_replace_output(r, ctx) == NGX_ERROR) {
                return NGX_ERROR;
            }
        }

        return ngx_http_next_body_filter(r, in);
    }

    /* add the incoming chain to the chain ctx->in */

    if (in) {
        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
            return NGX_ERROR;
        }
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http sub filter \"%V\"", &r->uri);

    while (ctx->in || ctx->buf) {

        if (ctx->buf == NULL) {
            cur = ctx->in;
            ctx->buf = cur->buf;
            ctx->in = cur->next;

            ctx->pos = ctx->buf->pos;
            ctx->special_buf = ngx_buf_special(ctx->buf);
            ctx->last_buf = (ctx->buf->last_buf || ctx->buf->last_in_chain);

            dd("=== new incoming buf: size=%d, special=%u, last=%u",
               (int) ngx_buf_size(ctx->buf), ctx->special_buf,
               ctx->last_buf);
        }

        b = NULL;

        while (ctx->pos < ctx->buf->last
               || (ctx->special_buf && ctx->last_buf))
        {
            rc = rlcf->parse_buf(r, ctx, rematch);

            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "replace filter parse: %d, %p-%p",
                           rc, ctx->copy_start, ctx->copy_end);

            if (rc == NGX_ERROR) {
                return rc;
            }

            if (rc == NGX_DECLINED) {

                if (ctx->pending) {
                    *ctx->last_out = ctx->pending;
                    ctx->last_out = ctx->last_pending;

                    ctx->pending = NULL;
                    ctx->last_pending = &ctx->pending;
                }

                if (!ctx->special_buf) {
                    ctx->copy_start = ctx->pos;
                    ctx->copy_end = ctx->buf->last;
                    ctx->pos = ctx->buf->last;

                } else {
                    ctx->copy_start = NULL;
                    ctx->copy_end = NULL;
                }

                sre_reset_pool(ctx->vm_pool);
                ctx->vm_done = 1;
            }

            dd("copy_end - copy_start: %d, special: %u",
               (int) (ctx->copy_end - ctx->copy_start), ctx->special_buf);

            if (ctx->copy_start != ctx->copy_end && !ctx->special_buf) {
                dd("copy: %.*s", (int) (ctx->copy_end - ctx->copy_start),
                   ctx->copy_start);

                cl = ngx_http_replace_get_free_buf(r->pool, &ctx->free);
                if (cl == NULL) {
                    return NGX_ERROR;
                }

                b = cl->buf;

                b->memory = 1;
                b->pos = ctx->copy_start;
                b->last = ctx->copy_end;

                *ctx->last_out = cl;
                ctx->last_out = &cl->next;
            }

            if (rc == NGX_AGAIN) {
                if (ctx->special_buf && ctx->last_buf) {
                    break;
                }

                continue;
            }

            if (rc == NGX_DECLINED) {
                break;
            }

            /* rc == NGX_OK || rc == NGX_BUSY */

            cl = ngx_http_replace_get_free_buf(r->pool, &ctx->free);
            if (cl == NULL) {
                return NGX_ERROR;
            }

            b = cl->buf;

            dd("free data buf: %p", b);

            sub = &ctx->sub[ctx->regex_id];

            if (sub->data == NULL
                || rlcf->parse_buf == ngx_http_replace_capturing_parse)
            {
                ngx_http_replace_complex_value_t            *cv;

                if (ngx_http_replace_regex_is_disabled(ctx)) {
                    cv = &rlcf->verbatim;

                } else {
                    cv = rlcf->multi_replace.elts;
                    cv = &cv[ctx->regex_id];
                }

                if (ngx_http_replace_complex_value(r, ctx->captured,
                                                   rlcf->ncaps,
                                                   ctx->ovector,
                                                   cv, sub)
                    != NGX_OK)
                {
                    return NGX_ERROR;
                }

                /* release ctx->captured */
                if (ctx->captured) {
                    dd("release ctx captured: %p", ctx->captured);
                    *ctx->last_captured = ctx->free;
                    ctx->free = ctx->captured;

                    ctx->captured = NULL;
                    ctx->last_captured = &ctx->captured;
                }
            }

            dd("emit replaced value: \"%.*s\"", (int) sub->len, sub->data);

            if (sub->len) {
                b->memory = 1;
                b->pos = sub->data;
                b->last = sub->data + sub->len;

            } else {
                b->sync = 1;
            }

            cl->buf = b;
            cl->next = NULL;

            *ctx->last_out = cl;
            ctx->last_out = &cl->next;

            if (!ctx->once && !ngx_http_replace_regex_is_disabled(ctx)) {
                uint8_t    *once;

                once = rlcf->multi_once.elts;

                if (rlcf->regexes.nelts == 1) {
                    ctx->once = once[0];

                } else {
                    if (once[ctx->regex_id]) {
                        ngx_http_replace_regex_set_disabled(ctx);
                        if (!rlcf->seen_global
                            && ++ctx->disabled_count == rlcf->regexes.nelts)
                        {
                            ctx->once = 1;
                        }
                    }
                }
            }

            if (rc == NGX_BUSY) {
                dd("goto rematch");
                goto rematch;
            }

            if (ctx->special_buf) {
                break;
            }

            continue;
        }

        if ((ctx->buf->flush || ctx->last_buf || ngx_buf_in_memory(ctx->buf))
            && cur)
        {
            if (b == NULL) {
                cl = ngx_http_replace_get_free_buf(r->pool, &ctx->free);
                if (cl == NULL) {
                    return NGX_ERROR;
                }

                b = cl->buf;
                b->sync = 1;

                *ctx->last_out = cl;
                ctx->last_out = &cl->next;
            }

            dd("setting shadow and last buf: %d", (int) ctx->buf->last_buf);
            b->last_buf = ctx->buf->last_buf;
            b->last_in_chain = ctx->buf->last_in_chain;
            b->flush = ctx->buf->flush;
            b->shadow = ctx->buf;
            b->recycled = ctx->buf->recycled;
        }

        if (!ctx->special_buf) {
            ctx->stream_pos += ctx->buf->last - ctx->buf->pos;
        }

        if (rematch) {
            rematch->next = ctx->free;
            ctx->free = rematch;
            rematch = NULL;
        }

rematch:
        dd("ctx->rematch: %p", ctx->rematch);

        if (ctx->rematch == NULL) {
            ctx->buf = NULL;
            cur = NULL;

        } else {

            if (cur) {
                ctx->in = cur;
                cur = NULL;
            }

            ctx->buf = ctx->rematch->buf;

            dd("ctx->buf set to rematch buf %p, len=%d, next=%p",
               ctx->buf, (int) ngx_buf_size(ctx->buf), ctx->rematch->next);

            rematch = ctx->rematch;
            ctx->rematch = rematch->next;

            ctx->pos = ctx->buf->pos;
            ctx->special_buf = ngx_buf_special(ctx->buf);
            ctx->last_buf = (ctx->buf->last_buf || ctx->buf->last_in_chain);
            ctx->stream_pos = ctx->buf->file_pos;
        }

#if (DDEBUG)
        /*
        ngx_http_replace_dump_chain("ctx->pending", &ctx->pending,
                                    ctx->last_pending);
        ngx_http_replace_dump_chain("ctx->pending2", &ctx->pending2,
                                    ctx->last_pending2);
        */
#endif
    } /* while */

    if (ctx->out == NULL && ctx->busy == NULL) {
        return NGX_OK;
    }

    return ngx_http_replace_output(r, ctx);
}
Пример #12
0
static ngx_int_t
ngx_http_trim_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_int_t             rc;
    ngx_chain_t          *cl, *ln, *out, **ll;
    ngx_http_trim_ctx_t  *ctx;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http trim filter");

    ctx = ngx_http_get_module_ctx(r, ngx_http_trim_filter_module);
    if (ctx == NULL) {
        return ngx_http_next_body_filter(r, in);
    }

    if (in == NULL) {
        return ngx_http_next_body_filter(r, in);
    }

    ctx->in = NULL;
    if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
        return NGX_ERROR;
    }

    out = NULL;
    ll = &out;

    for (ln = ctx->in; ln; ln = ln->next) {
        ngx_http_trim_parse(r, ln->buf, ctx);

        if (ctx->saved) {
            cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
            if (cl == NULL) {
                return NGX_ERROR;
            }

            cl->buf->tag = (ngx_buf_tag_t) &ngx_http_trim_filter_module;
            cl->buf->memory = 1;

            if (ctx->saved > 0) {
                cl->buf->pos = ngx_http_trim_saved_html.data;
                cl->buf->last = cl->buf->pos + ctx->saved;

            } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_SLASH) {
                cl->buf->pos = ngx_http_trim_saved_jscss.data;
                cl->buf->last = cl->buf->pos + 1;

            } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_JSCSS) {
                cl->buf->pos = ngx_http_trim_saved_jscss.data;
                cl->buf->last = cl->buf->pos + ngx_http_trim_saved_jscss.len;

            } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_HACKCSS) {
                cl->buf->pos = ngx_http_trim_saved_css_hack.data;
                cl->buf->last = cl->buf->pos + ngx_http_trim_saved_css_hack.len;
            }

            *ll = cl;
            ll = &cl->next;

            ctx->saved = 0;
        }

        if(ln->buf->in_file
           && (ln->buf->file_last - ln->buf->file_pos)
               != (off_t) (ln->buf->last - ln->buf->pos))
        {
            ln->buf->in_file = 0;
        }

        if (ngx_buf_size(ln->buf) == 0) {
            if (ln->buf->last_buf) {
                cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
                if (cl == NULL) {
                    return NGX_ERROR;
                }

                ngx_memzero(cl->buf, sizeof(ngx_buf_t));
                cl->buf->tag = (ngx_buf_tag_t) &ngx_http_trim_filter_module;
                cl->buf->last_buf = 1;

                *ll = cl;
                ll = &cl->next;

            }  else {
                if (ln->next == NULL) {
                    *ll = NULL;
                }
            }

        } else {
            *ll = ln;
            ll = &ln->next;
        }

    }

    if (out == NULL) {
        return NGX_OK;
    }

    rc = ngx_http_next_body_filter(r, out);

    ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,
                           (ngx_buf_tag_t) &ngx_http_trim_filter_module);

    return rc;
}
Пример #13
0
static ngx_int_t
ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_int_t                  rc;
    ngx_chain_t               *cl;
    ngx_http_request_body_t   *rb;

    rb = r->request_body;

#if (NGX_DEBUG)

    for (cl = rb->bufs; cl; cl = cl->next) {
        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                       "http body old buf t:%d f:%d %p, pos %p, size: %z "
                       "file: %O, size: %z",
                       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);
    }

    for (cl = in; cl; cl = cl->next) {
        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                       "http body new buf t:%d f:%d %p, pos %p, size: %z "
                       "file: %O, size: %z",
                       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);
    }

#endif

    /* TODO: coalesce neighbouring buffers */

    if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    for (cl = in; cl; cl = cl->next) {
        rc = ngx_http_top_input_body_filter(r, cl->buf);
        if (rc != NGX_OK) {
            if (rc > NGX_OK && rc < NGX_HTTP_SPECIAL_RESPONSE) {
                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                              "input filter: return code 1xx or 2xx "
                              "will cause trouble and is converted to 500");
            }

            /**
             * NGX_OK: success and continue;
             * NGX_ERROR: failed and exit;
             * NGX_AGAIN: not ready and retry later.
             */

            if (rc < NGX_HTTP_SPECIAL_RESPONSE && rc != NGX_AGAIN) {
                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            return rc;
        }
    }

    return NGX_OK;
}
// 参数in实际上是ngx_http_request_body_length_filter里的out,即读取到的数据
// 从内存池里分配节点
// 拷贝in链表里的buf到rb->bufs里,不是直接连接
// 同样是指针操作,没有内存拷贝
// 如果要求写磁盘文件,那么调用ngx_http_write_request_body
ngx_int_t
ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_buf_t                 *b;
    ngx_chain_t               *cl;
    ngx_http_request_body_t   *rb;

    // 请求体数据的结构体
    rb = r->request_body;

#if (NGX_DEBUG)

    for (cl = rb->bufs; cl; cl = cl->next) {
        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                       "http body old 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);
    }

    for (cl = in; cl; cl = cl->next) {
        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                       "http body new 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);
    }

#endif

    /* TODO: coalesce neighbouring buffers */

    // 从内存池里分配节点
    // 拷贝in链表里的buf到rb->bufs里,不是直接连接
    // 同样是指针操作,没有内存拷贝
    if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if (r->request_body_no_buffering) {
        return NGX_OK;
    }

    if (rb->rest > 0) {

        if (rb->buf && rb->buf->last == rb->buf->end
            && ngx_http_write_request_body(r) != NGX_OK)
        {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        return NGX_OK;
    }

    /* rb->rest == 0 */

    // 如果要求写磁盘文件,那么调用ngx_http_write_request_body
    if (rb->temp_file || r->request_body_in_file_only) {

        if (ngx_http_write_request_body(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        if (rb->temp_file->file.offset != 0) {

            cl = ngx_chain_get_free_buf(r->pool, &rb->free);
            if (cl == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            b = cl->buf;

            ngx_memzero(b, sizeof(ngx_buf_t));

            b->in_file = 1;
            b->file_last = rb->temp_file->file.offset;
            b->file = &rb->temp_file->file;

            rb->bufs = cl;
        }
    }

    return NGX_OK;
}
Пример #15
0
static ngx_int_t
ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_int_t                  rc;
    ngx_buf_t                 *b;
    ngx_chain_t               *cl;
    ngx_http_sub_ctx_t        *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module);

    if (ctx == NULL) {
        return ngx_http_next_body_filter(r, in);
    }

    if ((in == NULL
         && ctx->buf == NULL
         && ctx->in == NULL
         && ctx->busy == NULL))
    {
        return ngx_http_next_body_filter(r, in);
    }

    if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) {

        if (ctx->busy) {
            if (ngx_http_sub_output(r, ctx) == NGX_ERROR) {
                return NGX_ERROR;
            }
        }

        return ngx_http_next_body_filter(r, in);
    }

    /* add the incoming chain to the chain ctx->in */

    if (in) {
        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
            return NGX_ERROR;
        }
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http sub filter \"%V\"", &r->uri);

    while (ctx->in || (ctx->buf && ctx->buf->last_buf)) {

        if (ctx->buf == NULL) {
            ctx->buf = ctx->in->buf;
            ctx->in = ctx->in->next;
            ctx->pos = ctx->buf->pos;
        }
        else
        {
            if(ctx->buf->last_buf)
            {
                ngx_memzero(ctx->buf->last, ctx->tmp.len/2);
                ctx->buf->last += ctx->tmp.len/2;
            }
            else
            {
                ngx_uint_t next_buf_size = ctx->in->buf->last - ctx->in->buf->pos;
                ngx_uint_t tmp_buf_size = ctx->buf->last - ctx->buf->pos;
                ngx_uint_t rem_part_size = ctx->tmp.len/2 - (tmp_buf_size - ctx->first_part_size);
                ngx_uint_t next_part_size = next_buf_size < rem_part_size ? next_buf_size : rem_part_size;

                ngx_memcpy(ctx->buf->last, ctx->in->buf->pos, next_part_size);
                ctx->buf->last += next_part_size;

                if(ctx->in->buf->last_buf) {
                    rem_part_size -= next_part_size;
                    ngx_memzero(ctx->buf->last, rem_part_size);
                    ctx->buf->last += rem_part_size;
                }
                else if(next_part_size != rem_part_size) {
                    ctx->in = ctx->in->next;
                    continue;
                }
            }
            ctx->pos = ctx->buf->pos;
        }    
        
        ctx->pos += ctx->next_buf_offset;
        ctx->copy_start = ctx->pos;
        ctx->copy_end = ctx->pos;

        b = NULL;

        while (ctx->pos < ctx->buf->last) {
/*
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "saved: \"%V\" state: %d", &ctx->saved, ctx->state);
*/
            rc = ngx_http_sub_parse(r, ctx);

            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "parse: %d, tmp: \"%V\" %p-%p",
                           rc, &ctx->tmp, ctx->copy_start, ctx->copy_end);

            if (rc == NGX_ERROR) {
                return rc;
            }

            if (ctx->copy_start != ctx->copy_end) {
                /*
                   ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "saved: \"%V\"", &ctx->saved);
                 */
                if (ctx->free) {
                    cl = ctx->free;
                    ctx->free = ctx->free->next;
                    b = cl->buf;

                } else {
                    b = ngx_alloc_buf(r->pool);
                    if (b == NULL) {
                        return NGX_ERROR;
                    }

                    cl = ngx_alloc_chain_link(r->pool);
                    if (cl == NULL) {
                        return NGX_ERROR;
                    }

                    cl->buf = b;
                }

                if(ctx->state == sub_main_state)
                {
                    ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));

                    b->pos = ctx->copy_start;
                    b->last = ctx->copy_end;
                    b->shadow = NULL;
                    b->last_buf = 0;
                    b->recycled = 0;

                    if (b->in_file) {
                        b->file_last = b->file_pos + (b->last - ctx->buf->pos);
                        b->file_pos += b->pos - ctx->buf->pos;
                    }
                }
                else
                {
                    ngx_uint_t buf_size = ctx->copy_end - ctx->copy_start;
                    ngx_memzero(b, sizeof(ngx_buf_t));
                    b->pos = ngx_pnalloc(r->pool, buf_size);
                    if (b->pos == NULL) {
                        return NGX_ERROR;
                    }
                    ngx_memcpy(b->pos, ctx->copy_start, buf_size);
                    b->last = b->pos + buf_size;
                    b->memory = 1;
                }

                cl->next = NULL;
                *ctx->last_out = cl;
                ctx->last_out = &cl->next;
            }

            if (rc == NGX_AGAIN) {
                continue;
            }


            /* rc == NGX_OK */

            b = ngx_calloc_buf(r->pool);
            if (b == NULL) {
                return NGX_ERROR;
            }

            cl = ngx_alloc_chain_link(r->pool);
            if (cl == NULL) {
                return NGX_ERROR;
            }

            if(!ctx->repl[ctx->index].data) {
                if (ngx_http_complex_value(r, &ctx->values[ctx->index], &ctx->repl[ctx->index])
                    != NGX_OK)
                {
                    return NGX_ERROR;
                }
            }

            if (ctx->repl[ctx->index].len) {
                b->memory = 1;
                b->pos = ctx->repl[ctx->index].data;
                b->last = ctx->repl[ctx->index].data + ctx->repl[ctx->index].len;

            } else {
                b->sync = 1;
            }

            cl->buf = b;
            cl->next = NULL;
            *ctx->last_out = cl;
            ctx->last_out = &cl->next;

//            ctx->once = slcf->once;

            continue;
        }

        if (ctx->buf->last_buf || ngx_buf_in_memory(ctx->buf)) {
            if (b == NULL) {
                if (ctx->free) {
                    cl = ctx->free;
                    ctx->free = ctx->free->next;
                    b = cl->buf;
                    ngx_memzero(b, sizeof(ngx_buf_t));

                } else {
                    b = ngx_calloc_buf(r->pool);
                    if (b == NULL) {
                        return NGX_ERROR;
                    }

                    cl = ngx_alloc_chain_link(r->pool);
                    if (cl == NULL) {
                        return NGX_ERROR;
                    }

                    cl->buf = b;
                }

                b->sync = 1;

                cl->next = NULL;
                *ctx->last_out = cl;
                ctx->last_out = &cl->next;
            }

            b->last_buf = ctx->buf->last_buf;
            b->shadow = ctx->buf;

            b->recycled = ctx->buf->recycled;
        }

        if(ctx->state == sub_main_state && ctx->first_part_size)
        {
            b = ngx_calloc_buf(r->pool);
            if (b == NULL) {
                return NGX_ERROR;
            }
            b->last_buf = ctx->buf->last_buf;
            b->pos = ctx->tmp.data;
            b->last = b->pos + ctx->first_part_size;
            b->memory = 1;
            ctx->buf = b;
            ctx->state = sub_tmp_state;
        }
        else
        {
            ctx->buf = NULL;
            ctx->state = sub_main_state;
        }
    }

    if (ctx->out == NULL && ctx->busy == NULL) {
        return NGX_OK;
    }
    return ngx_http_sub_output(r, ctx);
}
static ngx_int_t
ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    int                   rc;
    ngx_uint_t            flush;
    ngx_chain_t          *cl;
    ngx_http_gzip_ctx_t  *ctx;
    ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
    if (ctx == NULL || ctx->done || r->header_only)
    {
        return ngx_http_next_body_filter(r, in);
    }
    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http gzip filter");
    if (ctx->buffering)
    {
        /*
         * With default memory settings zlib starts to output gzipped data
         * only after it has got about 90K, so it makes sense to allocate
         * zlib memory (200-400K) only after we have enough data to compress.
         * Although we copy buffers, nevertheless for not big responses
         * this allows to allocate zlib memory, to compress and to output
         * the response in one step using hot CPU cache.
         */
        if (in)
        {
            switch (ngx_http_gzip_filter_buffer(ctx, in))
            {
            case NGX_OK:
                return NGX_OK;
            case NGX_DONE:
                in = NULL;
                break;
            default:  /* NGX_ERROR */
                goto failed;
            }
        }
        else
        {
            ctx->buffering = 0;
        }
    }
    if (ctx->preallocated == NULL)
    {
        if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK)
        {
            goto failed;
        }
    }
    if (in)
    {
        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK)
        {
            goto failed;
        }
        r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
    }
    if (ctx->nomem)
    {
        /* flush busy buffers */
        if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR)
        {
            goto failed;
        }
        cl = NULL;
        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
                                (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
        ctx->nomem = 0;
        flush = 0;
    }
    else
    {
        flush = ctx->busy ? 1 : 0;
    }
    for (;;)
    {
        /* cycle while we can write to a client */
        for (;;)
        {
            /* cycle while there is data to feed zlib and ... */
            rc = ngx_http_gzip_filter_add_data(r, ctx);
            if (rc == NGX_DECLINED)
            {
                break;
            }
            if (rc == NGX_AGAIN)
            {
                continue;
            }
            /* ... there are buffers to write zlib output */
            rc = ngx_http_gzip_filter_get_buf(r, ctx);
            if (rc == NGX_DECLINED)
            {
                break;
            }
            if (rc == NGX_ERROR)
            {
                goto failed;
            }
            rc = ngx_http_gzip_filter_deflate(r, ctx);
            if (rc == NGX_OK)
            {
                break;
            }
            if (rc == NGX_ERROR)
            {
                goto failed;
            }
            /* rc == NGX_AGAIN */
        }
        if (ctx->out == NULL && !flush)
        {
            ngx_http_gzip_filter_free_copy_buf(r, ctx);
            return ctx->busy ? NGX_AGAIN : NGX_OK;
        }
        if (!ctx->gzheader)
        {
            if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK)
            {
                goto failed;
            }
        }
        rc = ngx_http_next_body_filter(r, ctx->out);
        if (rc == NGX_ERROR)
        {
            goto failed;
        }
        ngx_http_gzip_filter_free_copy_buf(r, ctx);
        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
                                (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
        ctx->last_out = &ctx->out;
        ctx->nomem = 0;
        flush = 0;
        if (ctx->done)
        {
            return rc;
        }
    }
    /* unreachable */
failed:
    ctx->done = 1;
    if (ctx->preallocated)
    {
        deflateEnd(&ctx->zstream);
        ngx_pfree(r->pool, ctx->preallocated);
    }
    ngx_http_gzip_filter_free_copy_buf(r, ctx);
    return NGX_ERROR;
}