static ngx_int_t
ngx_http_not_modified_header_filter(ngx_http_request_t *r)
{
    if (r->headers_out.status != NGX_HTTP_OK
        || r != r->main
        || r->disable_not_modified)
    {
        return ngx_http_next_header_filter(r);
    }

    if (r->headers_in.if_unmodified_since
        && !ngx_http_test_if_unmodified(r))
    {
        return ngx_http_filter_finalize_request(r, NULL,
                                                NGX_HTTP_PRECONDITION_FAILED);
    }

    if (r->headers_in.if_match
        && !ngx_http_test_if_match(r, r->headers_in.if_match, 0))
    {
        return ngx_http_filter_finalize_request(r, NULL,
                                                NGX_HTTP_PRECONDITION_FAILED);
    }

    if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {

        if (r->headers_in.if_modified_since
            && ngx_http_test_if_modified(r))
        {
            return ngx_http_next_header_filter(r);
        }

        if (r->headers_in.if_none_match
            && !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1))
        {
            return ngx_http_next_header_filter(r);
        }

        /* not modified */

        r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
        r->headers_out.status_line.len = 0;
        r->headers_out.content_type.len = 0;
        ngx_http_clear_content_length(r);
        ngx_http_clear_accept_ranges(r);

        if (r->headers_out.content_encoding) {
            r->headers_out.content_encoding->hash = 0;
            r->headers_out.content_encoding = NULL;
        }

        return ngx_http_next_header_filter(r);
    }

    return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
    ngx_buf_t *b)
{
    ngx_int_t                         rc;
    ngx_chain_t                       out;
    ngx_pool_cleanup_t               *cln;
    ngx_http_xslt_filter_loc_conf_t  *conf;

    ctx->done = 1;

    if (b == NULL) {
        return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
    }

    cln = ngx_pool_cleanup_add(r->pool, 0);

    if (cln == NULL) {
        ngx_free(b->pos);
        return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
    }

    if (r == r->main) {
        r->headers_out.content_length_n = b->last - b->pos;

        if (r->headers_out.content_length) {
            r->headers_out.content_length->hash = 0;
            r->headers_out.content_length = NULL;
        }

        ngx_http_clear_etag(r);

        conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);

        if (!conf->last_modified) {
            ngx_http_clear_last_modified(r);
        }
    }

    rc = ngx_http_next_header_filter(r);

    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        ngx_free(b->pos);
        return rc;
    }

    cln->handler = ngx_http_xslt_cleanup;
    cln->data = b->pos;

    out.buf = b;
    out.next = NULL;

    return ngx_http_next_body_filter(r, &out);
}
static ngx_int_t
ngx_http_waf_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_int_t                      rc;
    ngx_chain_t                    out;
    ngx_http_waf_filter_ctx_t   *ctx;


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

    ctx = ngx_http_get_module_ctx(r, ngx_http_waf_filter_module);

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

    if (ctx->complete) {
        return ngx_http_next_body_filter(r, in);
    }

    rc = ngx_http_waf_read(r, in);

    if (rc == NGX_AGAIN) {
        return NGX_OK;
    }

    if (rc == NGX_ERROR) {
        return ngx_http_filter_finalize_request(r,
               &ngx_http_waf_filter_module,
               NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
    }

    out.next = NULL;
    out.buf = ngx_http_waf_process(r);

    if (out.buf == NULL) {
        return ngx_http_filter_finalize_request(r,
                                              &ngx_http_waf_filter_module,
                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
    }

    return ngx_http_waf_send(r, ctx, &out);
}
static ngx_int_t
ngx_http_test_precondition(ngx_http_request_t *r)
{
    time_t  iums;

    iums = ngx_http_parse_time(r->headers_in.if_unmodified_since->value.data,
                               r->headers_in.if_unmodified_since->value.len);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                 "http iums:%d lm:%d", iums, r->headers_out.last_modified_time);

    if (iums >= r->headers_out.last_modified_time) {
        return ngx_http_next_header_filter(r);
    }

    return ngx_http_filter_finalize_request(r, NULL,
                                            NGX_HTTP_PRECONDITION_FAILED);
}
ngx_int_t
ngx_http_lua_header_filter_by_chunk(lua_State *L, ngx_http_request_t *r)
{
    int              old_exit_code = 0;
    ngx_int_t        rc;
    u_char          *err_msg;
    size_t           len;
#if (NGX_PCRE)
    ngx_pool_t      *old_pool;
#endif
    ngx_http_lua_ctx_t          *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
    if (ctx->exited) {
        old_exit_code = ctx->exit_code;
    }

    /*  initialize nginx context in Lua VM, code chunk at stack top    sp = 1 */
    ngx_http_lua_header_filter_by_lua_env(L, r);

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    old_pool = ngx_http_lua_pcre_malloc_init(r->pool);
#endif

    lua_pushcfunction(L, ngx_http_lua_traceback);
    lua_insert(L, 1);  /* put it under chunk and args */

    /*  protected call user code */
    rc = lua_pcall(L, 0, 1, 1);

    lua_remove(L, 1);  /* remove traceback function */

#if (NGX_PCRE)
    /* XXX: work-around to nginx regex subsystem */
    ngx_http_lua_pcre_malloc_done(old_pool);
#endif

    dd("rc == %d", (int) rc);

    if (rc != 0) {
        /*  error occured when running loaded code */
        err_msg = (u_char *) lua_tolstring(L, -1, &len);

        if (err_msg == NULL) {
            err_msg = (u_char *) "unknown reason";
            len = sizeof("unknown reason") - 1;
        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to run header_filter_by_lua*: %*s", len, err_msg);

        lua_settop(L, 0); /*  clear remaining elems on stack */

        return NGX_ERROR;
    }

    dd("exited: %d, exit code: %d, old exit code: %d",
       (int) ctx->exited, (int) ctx->exit_code, (int) old_exit_code);

    if (ctx->exited && ctx->exit_code != old_exit_code) {
        if (ctx->exit_code == NGX_ERROR) {
            return NGX_ERROR;
        }

        dd("finalize request with %d", (int) ctx->exit_code);

        rc = ngx_http_filter_finalize_request(r, &ngx_http_lua_module,
                                              ctx->exit_code);
        if (rc == NGX_ERROR || rc == NGX_AGAIN) {
            return rc;
        }

        return NGX_DECLINED;
    }

    /*  clear Lua stack */
    lua_settop(L, 0);

    return NGX_OK;
}
static ngx_int_t
ngx_http_advertise_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_int_t                   rc;
    ngx_chain_t                 out;
    ngx_buf_t                   *b;
    ngx_http_advertise_ctx_t    *ctx;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "advertisement inject");
    if (in == NULL) {
        return ngx_http_next_body_filter(r, in);
    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_advertise_module);

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

    case NGX_HTTP_ADVERTISE_START:

        ctx->phase = NGX_HTTP_ADVERTISE_READ;

        /* fall through */

    case NGX_HTTP_ADVERTISE_READ:
        
        rc = ngx_http_advertise_read(r, in);

        if (rc == NGX_AGAIN) {
            return NGX_OK;
        }
        r->connection->buffered &= ~NGX_HTTP_ADVERTISE_BUFFERED; 
        if (rc == NGX_ERROR) {
            ctx->status = 5;
            return ngx_http_filter_finalize_request(r,
                        &ngx_http_advertise_module,
                      NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
        }

        /* fall through */

    case NGX_HTTP_ADVERTISE_PROCESS:
        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
        if (b == NULL) {
            ctx->status = 6;
            return ngx_http_filter_finalize_request(r,
                        &ngx_http_advertise_module,
                        NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
        }
        if (ngx_http_advertise_process(r) == NGX_OK) {
            if (r->headers_out.content_length_n > 0)
                r->headers_out.content_length_n += ctx->target->len;
            ngx_http_advertise_set_status(r, 1);
        }
        
        b->pos = ctx->html;
        b->last = ctx->last;
        b->memory = 1;
        b->last_buf = 1;
        out.buf = b;
        out.next = NULL;
        ctx->phase = NGX_HTTP_ADVERTISE_PASS;
        return ngx_http_advertise_send(r, ctx, &out);
       
    case NGX_HTTP_ADVERTISE_PASS:

        return ngx_http_next_body_filter(r, in);

    default: /* NGX_HTTP_IMAGE_DONE */

        rc = ngx_http_next_body_filter(r, NULL);

        /* NGX_ERROR resets any pending data */
        return (rc == NGX_OK) ? NGX_ERROR : rc;
    }
}
static ngx_int_t ngx_http_small_light_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_http_small_light_conf_t *loc_conf;
    ngx_http_small_light_ctx_t  *ctx;
    ngx_buf_t                   *b;
    ngx_pool_cleanup_t          *cln;
    ngx_chain_t                  out;
    ngx_int_t                    rc;
    size_t                       content_type_len;

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

    loc_conf = ngx_http_get_module_loc_conf(r, ngx_http_small_light_module);

    if (!loc_conf->enable) {
        return ngx_http_next_body_filter(r, in);
    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_small_light_module);

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

    if((rc = ngx_http_small_light_image_read(r, in, loc_conf->buffer_size, ctx)) != NGX_OK) {
        if (rc == NGX_AGAIN) {
            return NGX_OK;
        }
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to read image %s:%d",
                      __FUNCTION__,
                      __LINE__);
        return ngx_http_filter_finalize_request(r,
                                                &ngx_http_small_light_module,
                                                NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
    }

    r->connection->buffered &= ~NGX_HTTP_SMALL_LIGHT_IMAGE_BUFFERED;

    rc = ctx->converter.init(r, ctx);
    if (rc != NGX_OK) {

        ngx_pfree(r->pool, ctx->content_orig);

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to process image %s:%d",
                      __FUNCTION__,
                      __LINE__);
        return ngx_http_filter_finalize_request(r,
                                                &ngx_http_small_light_module,
                                                NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
    }

    rc = ctx->converter.process(r, ctx);

    if (rc != NGX_OK) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to process image %s:%d",
                      __FUNCTION__,
                      __LINE__);
        ctx->converter.term(ctx);
        return ngx_http_filter_finalize_request(r,
                                                &ngx_http_small_light_module,
                                                NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
    }

    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    if (b == NULL) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to allocate memory from r->pool %s:%d",
                      __FUNCTION__,
                      __LINE__);
        ctx->converter.term(ctx);
        return NGX_ERROR;
    }
    b->pos      = ctx->content;
    b->last     = ctx->content + ctx->content_length;
    b->memory   = 1;
    b->last_buf = 1;

    out.buf  = b;
    out.next = NULL;

    r->headers_out.content_length_n = b->last - b->pos;
    if (r->headers_out.content_length) {
        r->headers_out.content_length->hash = 0;
    }
    content_type_len = ngx_strlen(ctx->of);
    r->headers_out.content_length       = NULL;
    r->headers_out.content_type_len     = content_type_len;
    r->headers_out.content_type.len     = content_type_len;
    r->headers_out.content_type.data    = (u_char *)ctx->of;
    r->headers_out.content_type_lowcase = NULL;

    cln = ngx_pool_cleanup_add(r->pool, 0);
    if (cln == NULL) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "failed to allocate memory from r->pool %s:%d",
                      __FUNCTION__,
                      __LINE__);
        ctx->converter.term(ctx);
        return NGX_ERROR;
    }
    cln->handler = ctx->converter.term;
    cln->data    = ctx;

    return ngx_http_small_light_finish(r, &out);
}
static ngx_int_t
ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_int_t                      rc;
    ngx_str_t                     *ct;
    ngx_chain_t                    out;
    ngx_http_image_filter_ctx_t   *ctx;
    ngx_http_image_filter_conf_t  *conf;

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

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

    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);

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

    switch (ctx->phase) {

    case NGX_HTTP_IMAGE_START:

        ctx->type = ngx_http_image_test(r, in);

        conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);

        if (ctx->type == NGX_HTTP_IMAGE_NONE) {

            if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
                out.buf = ngx_http_image_json(r, NULL);

                if (out.buf) {
                    out.next = NULL;
                    ctx->phase = NGX_HTTP_IMAGE_DONE;

                    return ngx_http_image_send(r, ctx, &out);
                }
            }

            return ngx_http_filter_finalize_request(r,
                                              &ngx_http_image_filter_module,
                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
        }

        /* override content type */

        ct = &ngx_http_image_types[ctx->type - 1];
        r->headers_out.content_type_len = ct->len;
        r->headers_out.content_type = *ct;
        r->headers_out.content_type_lowcase = NULL;

        if (conf->filter == NGX_HTTP_IMAGE_TEST) {
            ctx->phase = NGX_HTTP_IMAGE_PASS;

            return ngx_http_image_send(r, ctx, in);
        }

        ctx->phase = NGX_HTTP_IMAGE_READ;

        /* fall through */

    case NGX_HTTP_IMAGE_READ:

        rc = ngx_http_image_read(r, in);

        if (rc == NGX_AGAIN) {
            return NGX_OK;
        }

        if (rc == NGX_ERROR) {
            return ngx_http_filter_finalize_request(r,
                                              &ngx_http_image_filter_module,
                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
        }

        /* fall through */

    case NGX_HTTP_IMAGE_PROCESS:

        out.buf = ngx_http_image_process(r);

        if (out.buf == NULL) {
            return ngx_http_filter_finalize_request(r,
                                              &ngx_http_image_filter_module,
                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
        }

        out.next = NULL;
        ctx->phase = NGX_HTTP_IMAGE_PASS;

        return ngx_http_image_send(r, ctx, &out);

    case NGX_HTTP_IMAGE_PASS:

        return ngx_http_next_body_filter(r, in);

    default: /* NGX_HTTP_IMAGE_DONE */

        rc = ngx_http_next_body_filter(r, NULL);

        /* NGX_ERROR resets any pending data */
        return (rc == NGX_OK) ? NGX_ERROR : rc;
    }
}
/*
{If-None-Match和ETag , If-Modified-Since和Last-Modified
    If-Modified-Since(浏览器) = Last-Modified(服务器)
    作用:浏览器端第一次访问获得服务器的Last-Modified,第2次访问把浏览器端缓存页面的最后修改时间发送到服务器去,服务器会把这
    个时间与服务器上实际文件的最后修改时间进行对比。如果时间一致,那么返回304,客户端就直接使用本地缓存文件。如果时间不一致,就
    会返回200和新的文件内容。客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示在浏览器中.


    If-None-Match(浏览器) = ETag(服务器)
    作用: If-None-Match和ETag一起工作,工作原理是在HTTP Response中添加ETag信息。 当用户再次请求该资源时,将在HTTP Request 中加入If-None-Match
    信息(ETag的值)。如果服务器验证资源的ETag没有改变(该资源没有更新),将返回一个304状态告诉客户端使用本地缓存文件。否则将返回200状态和新的资源和Etag. 
}


{
    ETags和If-None-Match是一种常用的判断资源是否改变的方法。类似于Last-Modified和HTTP-If-Modified-Since。但是有所不同的是Last-Modified和HTTP-If-Modified-Since只判断资源的最后修改时间,而ETags和If-None-Match可以是资源任何的任何属性。
    ETags和If-None-Match的工作原理是在HTTPResponse中添加ETags信息。当客户端再次请求该资源时,将在HTTPRequest中加入If-None-Match信息(ETags的值)。如果服务器验证资源的ETags没有改变(该资源没有改变),将返回一个304状态;否则,服务器将返回200状态,并返回该资源和新的ETags。
}


{  
http响应Last-Modified和ETag

  基础知识
1) 什么是”Last-Modified”?
      在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记此文件在服务期端
    最后被修改的时间,格式类似这样:Last-Modified: Fri, 12 May 2006 18:53:33 GMT
  客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since 报头,询问该时间之后文件是否有被修改过:
  If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT
  如果服务器端的资源没有变化,则自动返回 HTTP 304 (Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改
    变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。
2) 什么是”Etag”?
  HTTP 协议规格说明定义ETag为“被请求变量的实体值” (参见 ―― 章节 14.19)。 另一种说法是,ETag是一个可以与Web资源关联的记号(token)。典型的Web资源可以一个Web页,但也可能是JSON或XML文档。服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:
  ETag: "50b1c1d4f775c61:df3"
  客户端的查询更新格式是这样的:
  If-None-Match: W/"50b1c1d4f775c61:df3"
  如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。本人测试Etag主要在断点下载时比较有用。
  
Last-Modified和Etags如何帮助提高性能?
  聪明的开发者会把Last-Modified 和ETags请求的http报头一起使用,这样可利用客户端(例如浏览器)的缓存。因为服务器首先产生 
Last-Modified/Etag标记,服务器可在稍后使用它来判断页面是否已经被修改。本质上,客户端通过将该记号传回服务器要求服务器验证其(客户端)缓存。过程如下:
1.客户端请求一个页面(A)。
2.服务器返回页面A,并在给A加上一个Last-Modified/ETag。
3.客户端展现该页面,并将页面连同Last-Modified/ETag一起缓存。
4.客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/ETag一起传递给服务器。
5.服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。
}
*/
static ngx_int_t
ngx_http_not_modified_header_filter(ngx_http_request_t *r)
{
    if (r->headers_out.status != NGX_HTTP_OK  //只有返回类型为OK的才进行304 not modified判断
        || r != r->main
        || r->disable_not_modified)
    {
        return ngx_http_next_header_filter(r);
    }
    

    /*
If-Unmodified-Since: 从字面上看, 意思是: 如果从某个时间点算起, 文件没有被修改.....
    1. 如果没有被修改: 则开始`继续'传送文件: 服务器返回: 200 OK
    2. 如果文件被修改: 则不传输, 服务器返回: 412 Precondition failed (预处理错误)
用途:断点续传(一般会指定Range参数). 要想断点续传, 那么文件就一定不能被修改, 否则就不是同一个文件了
*/
    if (r->headers_in.if_unmodified_since
        && !ngx_http_test_if_unmodified(r)) 
    /*
    如果从某个时间点算起, 文件被修改了,客户端请求行中带有if_unmodified_since,表示如果文件如果从某个时间段起没有被修改,
    则继续200 OK传送给客户端包体。但是现在文件却被修改了,因此返回错误
    */
    {
        return ngx_http_filter_finalize_request(r, NULL,
                                                NGX_HTTP_PRECONDITION_FAILED);
    }

    if (r->headers_in.if_match
        && !ngx_http_test_if_match(r, r->headers_in.if_match, 0))
    {
        return ngx_http_filter_finalize_request(r, NULL,
                                                NGX_HTTP_PRECONDITION_FAILED);
    }

    if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {

        if (r->headers_in.if_modified_since
            && ngx_http_test_if_modified(r)) //文件有发生了修改
        {
            return ngx_http_next_header_filter(r);
        }

        if (r->headers_in.if_none_match
            && !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1)) //etag不匹配,说明文件也发生了修改
        {
            return ngx_http_next_header_filter(r);
        }

        /* not modified */
        //回送304表示文件没有修改
        r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
        r->headers_out.status_line.len = 0;
        r->headers_out.content_type.len = 0;
        ngx_http_clear_content_length(r);
        ngx_http_clear_accept_ranges(r);

        if (r->headers_out.content_encoding) {
            r->headers_out.content_encoding->hash = 0;
            r->headers_out.content_encoding = NULL;
        }

        return ngx_http_next_header_filter(r);
    }

    return ngx_http_next_header_filter(r);
}
Пример #10
0
static ngx_int_t
ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_http_modsecurity_loc_conf_t *cf;
    ngx_http_modsecurity_ctx_t      *ctx;
    ngx_int_t                        rc;
    apr_off_t                        content_length;
    ngx_chain_t                     *cl, *out;
    ngx_int_t                        last_buf = 0;

    cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity);
    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);

    if (r != r->main || !cf->enable || ctx == NULL || ctx->complete) {
        return ngx_http_next_body_filter(r, in);
    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: body filter");

    for (cl = in; cl; cl = cl->next) {
        apr_bucket  *e;
        ngx_buf_t   *buf = cl->buf;
        apr_bucket_brigade  *bb = ctx->brigade;
        off_t size = ngx_buf_size(buf);
        if (size) {
            char *data = apr_pmemdup(bb->p, buf->pos, size);
            if (data == NULL) {
                return ngx_http_filter_finalize_request(r, 
                         &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR);
            }
            e = apr_bucket_pool_create(data , size, bb->p, bb->bucket_alloc);
            if (e == NULL) {
                return ngx_http_filter_finalize_request(r, 
                         &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR);
            }
            APR_BRIGADE_INSERT_TAIL(bb, e);
        }

        if (buf->last_buf) {
            last_buf = 1;
            buf->last_buf = 0;
            e = apr_bucket_eos_create(bb->bucket_alloc);
            if (e == NULL) {
                return ngx_http_filter_finalize_request(r, 
                         &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR);
            }
            APR_BRIGADE_INSERT_TAIL(bb, e);
            break;
        }

        buf->pos = buf->last;
    }

    if (!last_buf) {
        return NGX_AGAIN;
    }

    /* last buf has been saved */
    ctx->complete = 1;
    modsecSetResponseBrigade(ctx->req, ctx->brigade);

    if (ngx_http_modsecurity_load_headers_in(r) != NGX_OK
            || ngx_http_modsecurity_load_headers_out(r) != NGX_OK) {

        return ngx_http_filter_finalize_request(r, 
                 &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR);
    }

    rc = ngx_http_modsecurity_status(r, modsecProcessResponse(ctx->req));

    if (rc != NGX_DECLINED) {
        return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, rc);
    }

    apr_brigade_length(ctx->brigade, 0, &content_length);

    rc = move_brigade_to_chain(ctx->brigade, &out, r->pool);
    if (rc == NGX_ERROR) {
        return ngx_http_filter_finalize_request(r, 
                 &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR);
    }

    if (ngx_http_modsecurity_save_headers_in(r) != NGX_OK
            ||ngx_http_modsecurity_save_headers_out(r) != NGX_OK) {

        return ngx_http_filter_finalize_request(r, 
                 &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR);
    }

    if (r->headers_out.content_length_n != -1) {

        r->headers_out.content_length_n = content_length;
        r->headers_out.content_length = NULL; /* header filter will set this */
    }

    r->header_sent = 0;
    rc = ngx_http_next_header_filter(r);

    if (rc == NGX_ERROR || rc > NGX_OK) {
        return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, rc);
    }

    return ngx_http_next_body_filter(r, out);
}
static ngx_int_t
ngx_http_pngquant_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_int_t                  rc;
    ngx_str_t                 *ct;
    ngx_chain_t                out;
    ngx_http_pngquant_ctx_t   *ctx;

    if (in == NULL) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "pngquant_body_filter (!in)");

        return ngx_http_next_body_filter(r, in);
    }

    ctx = ngx_http_get_module_ctx(r, ngx_http_pngquant_module);

    if (ctx == NULL) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "pngquant_body_filter (!ctx)");

        return ngx_http_next_body_filter(r, in);
    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "pngquant_body_filter");

    switch (ctx->phase) {

    case NGX_HTTP_PNGQUANT_START:

        if (NGX_OK != ngx_http_pngquant_is_png(r, in)) {

            return ngx_http_filter_finalize_request(r,
                &ngx_http_pngquant_module,
                NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
        }

        /* override content type */

        ct = &ngx_http_pngquant_content_type[0];
        r->headers_out.content_type_len = ct->len;
        r->headers_out.content_type = *ct;
        r->headers_out.content_type_lowcase = NULL;

        ctx->phase = NGX_HTTP_PNGQUANT_READ;

        /* fall through */

    case NGX_HTTP_PNGQUANT_READ:

        rc = ngx_http_pngquant_read(r, in);

        if (rc == NGX_AGAIN) {

            return NGX_OK;
        }

        if (rc == NGX_ERROR) {

            return ngx_http_filter_finalize_request(r,
                &ngx_http_pngquant_module,
                NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
        }

        /* fall through */

    case NGX_HTTP_PNGQUANT_PROCESS:

        out.buf = ngx_http_pngquant_process(r);

        if (out.buf == NULL) {

            return ngx_http_filter_finalize_request(r,
                &ngx_http_pngquant_module,
                NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
        }

        out.next = NULL;
        ctx->phase = NGX_HTTP_PNGQUANT_PASS;

        return ngx_http_pngquant_send(r, ctx, &out);

    case NGX_HTTP_PNGQUANT_PASS:

        return ngx_http_next_body_filter(r, in);

    case NGX_HTTP_PNGQUANT_DONE:
    default:

        rc = ngx_http_next_body_filter(r, NULL);

        /* NGX_ERROR resets any pending data */
        return (rc == NGX_OK) ? NGX_ERROR : rc;
    }
}
ngx_int_t
ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    int buffer_fully_loadead = 0;
    ngx_chain_t *chain = in;
    ngx_http_modsecurity_ctx_t *ctx = NULL;
#ifdef MODSECURITY_SANITY_CHECKS
    ngx_list_part_t *part = &r->headers_out.headers.part;
    ngx_table_elt_t *data = part->elts;
    ngx_uint_t i = 0;
#endif

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

    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);

    dd("body filter, recovering ctx: %p", ctx);

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

#ifdef MODSECURITY_SANITY_CHECKS
#if 0
    dd("dumping stored ctx headers");
    for (i = 0; i < ctx->sanity_headers_out->nelts; i++)
    {
	ngx_http_modsecurity_header_t *vals = ctx->sanity_headers_out->elts;
	ngx_str_t *s2 = &vals[i].name, *s3 = &vals[i].value;
	dd(" dump[%d]: name = '%.*s', value = '%.*s'", (int)i,
		(int)s2->len, (char*)s2->data,
		(int)s3->len, (char*)s3->data);
    }
#endif
    /*
     * Identify if there is a header that was not inspected by ModSecurity.
     */
    int worth_to_fail = 0;

    for (i = 0; ; i++)
    {
        int found = 0;
        ngx_uint_t j = 0;
        ngx_table_elt_t *s1;
        ngx_http_modsecurity_header_t *vals;

        if (i >= part->nelts)
        {
            if (part->next == NULL) {
                break;
            }

            part = part->next;
            data = part->elts;
            i = 0;
        }

        vals = ctx->sanity_headers_out->elts;
        s1 = &data[i];

        /*
         * Headers that were inspected by ModSecurity.
         */
        while (j < ctx->sanity_headers_out->nelts)
        {
            ngx_str_t *s2 = &vals[j].name;
            ngx_str_t *s3 = &vals[j].value;

            if (s1->key.len == s2->len && ngx_strncmp(s1->key.data, s2->data, s1->key.len) == 0)
            {
                if (s1->value.len == s3->len && ngx_strncmp(s1->value.data, s3->data, s1->value.len) == 0)
                {
                    found = 1;
                    break;
                }
            }
            j++;
        }
        if (!found) {
            dd("header: `%.*s' with value: `%.*s' was not inspected by ModSecurity",
                (int) s1->key.len,
                (const char *) s1->key.data,
                (int) s1->value.len,
                (const char *) s1->value.data);
	    worth_to_fail++;
        }
    }

    if (worth_to_fail)
    {
        dd("%d header(s) were not inspected by ModSecurity, so exiting", worth_to_fail);
        return ngx_http_filter_finalize_request(r,
            &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR);
    }
#endif

    for (; chain != NULL; chain = chain->next)
    {
/* XXX: chain->buf->last_buf || chain->buf->last_in_chain */
        if (chain->buf->last_buf) {
            buffer_fully_loadead = 1;
        }
    }

    if (buffer_fully_loadead == 1)
    {
        int ret;

        for (chain = in; chain != NULL; chain = chain->next)
        {
            u_char *data = chain->buf->start;

            msc_append_response_body(ctx->modsec_transaction, data, chain->buf->end - data);
            ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
            if (ret > 0) {
                return ngx_http_filter_finalize_request(r,
                    &ngx_http_modsecurity, ret);
            }
        }

        msc_process_response_body(ctx->modsec_transaction);
/* XXX: I don't get how body from modsec being transferred to nginx's buffer.  If so - after adjusting of nginx's
   XXX: body we can proceed to adjust body size (content-length).  see xslt_body_filter() for example */
        ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
        if (ret > 0) {
            return ret;
        }
	else if (ret < 0) {
            return ngx_http_filter_finalize_request(r,
                &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR);
        }
    }
    else
    {
        dd("buffer was not fully loaded! ctx: %p", ctx);
    }

/* XXX: xflt_filter() -- return NGX_OK here */
    return ngx_http_next_body_filter(r, in);
}