static ngx_int_t
ngx_http_gunzip_header_filter(ngx_http_request_t *r)
{
    ngx_http_gunzip_ctx_t   *ctx;
    ngx_http_gunzip_conf_t  *conf;

    conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);

    /* TODO support multiple content-codings */
    /* TODO ignore content encoding? */

    if (conf->enable == NGX_HTTP_GUNZIP_OFF
        || r->headers_out.content_encoding == NULL
        || r->headers_out.content_encoding->value.len != 4
        || ngx_strncasecmp(r->headers_out.content_encoding->value.data,
                           (u_char *) "gzip", 4) != 0)
    {
        return ngx_http_next_header_filter(r);
    }

    r->gzip_vary = 1;

    if (conf->enable == NGX_HTTP_GUNZIP_ON) {
        if (!r->gzip_tested) {
            if (ngx_http_gzip_ok(r) == NGX_OK) {
                return ngx_http_next_header_filter(r);
            }

        } else if (r->gzip_ok) {
                   return ngx_http_next_header_filter(r);
        }

    } else if (conf->enable == NGX_HTTP_GUNZIP_ALWAYS
               && ngx_http_test_content_type(r, &conf->types) == NULL)
    {
               return ngx_http_next_header_filter(r);
    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gunzip_ctx_t));
    if (ctx == NULL) {
        return NGX_ERROR;
    }

    ngx_http_set_ctx(r, ctx, ngx_http_gunzip_filter_module);

    ctx->request = r;

    r->filter_need_in_memory = 1;

    r->headers_out.content_encoding->hash = 0;
    r->headers_out.content_encoding = NULL;

    ngx_http_clear_content_length(r);
    ngx_http_clear_accept_ranges(r);
    ngx_http_weak_etag(r);

    return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_gzip_ok_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data)
{
    char ok = '0';

    v->valid = 1;
    v->no_cacheable = 0;
    v->not_found = 0;

    if (!r->gzip_tested) {
        ngx_http_gzip_ok(r);
    }
    if (r->gzip_ok) {
        ok = '1';
    }

    v->data = ngx_pnalloc(r->pool, 2);
    v->data[0] = ok;
    v->data[1] = '\0';
    v->len = 1;
    return NGX_OK;
}
static ngx_int_t
ngx_http_gzip_static_handler(ngx_http_request_t *r)
{
    u_char                       *p;
    size_t                        root;
    ngx_str_t                     path;
    ngx_int_t                     rc;
    ngx_uint_t                    level;
    ngx_log_t                    *log;
    ngx_buf_t                    *b;
    ngx_chain_t                   out;
    ngx_table_elt_t              *h;
    ngx_open_file_info_t          of;
    ngx_http_core_loc_conf_t     *clcf;
    ngx_http_gzip_static_conf_t  *gzcf;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
        return NGX_DECLINED;
    }

    if (r->uri.data[r->uri.len - 1] == '/') {
        return NGX_DECLINED;
    }

    gzcf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_static_module);

    if (gzcf->enable == NGX_HTTP_GZIP_STATIC_OFF) {
        return NGX_DECLINED;
    }

    if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {
        rc = ngx_http_gzip_ok(r);

    } else {
        /* always */
        rc = NGX_OK;
    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!clcf->gzip_vary && rc != NGX_OK) {
        return NGX_DECLINED;
    }

    log = r->connection->log;

    p = ngx_http_map_uri_to_path(r, &path, &root, sizeof(".gz") - 1);
    if (p == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    *p++ = '.';
    *p++ = 'g';
    *p++ = 'z';
    *p = '\0';

    path.len = p - path.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                   "http filename: \"%s\"", path.data);

    ngx_memzero(&of, sizeof(ngx_open_file_info_t));

    of.read_ahead = clcf->read_ahead;
    of.directio = clcf->directio;
    of.valid = clcf->open_file_cache_valid;
    of.min_uses = clcf->open_file_cache_min_uses;
    of.errors = clcf->open_file_cache_errors;
    of.events = clcf->open_file_cache_events;

    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
        != NGX_OK)
    {
        switch (of.err) {

        case 0:
            return NGX_HTTP_INTERNAL_SERVER_ERROR;

        case NGX_ENOENT:
        case NGX_ENOTDIR:
        case NGX_ENAMETOOLONG:

            return NGX_DECLINED;

        case NGX_EACCES:
#if (NGX_HAVE_OPENAT)
        case NGX_EMLINK:
        case NGX_ELOOP:
#endif

            level = NGX_LOG_ERR;
            break;

        default:

            level = NGX_LOG_CRIT;
            break;
        }

        ngx_log_error(level, log, of.err,
                      "%s \"%s\" failed", of.failed, path.data);

        return NGX_DECLINED;
    }

    if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {
        r->gzip_vary = 1;

        if (rc != NGX_OK) {
            return NGX_DECLINED;
        }
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);

    if (of.is_dir) {
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
        return NGX_DECLINED;
    }

#if !(NGX_WIN32) /* the not regular files are probably Unix specific */

    if (!of.is_file) {
        ngx_log_error(NGX_LOG_CRIT, log, 0,
                      "\"%s\" is not a regular file", path.data);

        return NGX_HTTP_NOT_FOUND;
    }

#endif

    r->root_tested = !r->error_page;

    rc = ngx_http_discard_request_body(r);

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

    log->action = "sending response to client";

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = of.size;
    r->headers_out.last_modified_time = of.mtime;

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

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

    h = ngx_list_push(&r->headers_out.headers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    h->hash = 1;
    ngx_str_set(&h->key, "Content-Encoding");
    ngx_str_set(&h->value, "gzip");
    r->headers_out.content_encoding = h;

    /* we need to allocate all before the header would be sent */

    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    if (b == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
    if (b->file == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    rc = ngx_http_send_header(r);

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

    b->file_pos = 0;
    b->file_last = of.size;

    b->in_file = b->file_last ? 1 : 0;
    b->last_buf = (r == r->main) ? 1 : 0;
    b->last_in_chain = 1;

    b->file->fd = of.fd;
    b->file->name = path;
    b->file->log = log;
    b->file->directio = of.is_directio;

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

    return ngx_http_output_filter(r, &out);
}
static ngx_int_t
ngx_http_gunzip_header_filter(ngx_http_request_t *r)
{
    ngx_http_gunzip_ctx_t   *ctx;
    ngx_http_gunzip_conf_t  *conf;

    conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);

    /* TODO support multiple content-codings */
    /* TODO always gunzip - due to configuration or module request */
    /* TODO ignore content encoding? */

    if (!conf->enable
        || r->headers_out.content_encoding == NULL
        || r->headers_out.content_encoding->value.len != 4
        || ngx_strncasecmp(r->headers_out.content_encoding->value.data,
                           (u_char *) "gzip", 4) != 0)
    {
        return ngx_http_next_header_filter(r);
    }

#if (nginx_version >= 8025 || (nginx_version >= 7065 && nginx_version < 8000))

    r->gzip_vary = 1;

    if (!r->gzip_tested) {
        if (ngx_http_gzip_ok(r) == NGX_OK) {
            return ngx_http_next_header_filter(r);
        }

    } else if (!r->gzip_ok) {
        return ngx_http_next_header_filter(r);
    }

#else

    if (ngx_http_gzip_ok(r) == NGX_OK) {
        return ngx_http_next_header_filter(r);
    }

#endif

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gunzip_ctx_t));
    if (ctx == NULL) {
        return NGX_ERROR;
    }

    ngx_http_set_ctx(r, ctx, ngx_http_gunzip_filter_module);

    ctx->request = r;

    r->filter_need_in_memory = 1;

    r->headers_out.content_encoding->hash = 0;
    r->headers_out.content_encoding = NULL;

    ngx_http_clear_content_length(r);
    ngx_http_clear_accept_ranges(r);

    return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_gzip_header_filter(ngx_http_request_t *r)
{
    ngx_table_elt_t       *h;
    ngx_http_gzip_ctx_t   *ctx;
    ngx_http_gzip_conf_t  *conf;
    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
    if (!conf->enable
            || (r->headers_out.status != NGX_HTTP_OK
                && r->headers_out.status != NGX_HTTP_FORBIDDEN
                && r->headers_out.status != NGX_HTTP_NOT_FOUND)
            || (r->headers_out.content_encoding
                && r->headers_out.content_encoding->value.len)
            || (r->headers_out.content_length_n != -1
                && r->headers_out.content_length_n < conf->min_length)
            || ngx_http_test_content_type(r, &conf->types) == NULL
            || r->header_only)
    {
        return ngx_http_next_header_filter(r);
    }
    r->gzip_vary = 1;
#if (NGX_HTTP_DEGRADATION)
    {
        ngx_http_core_loc_conf_t  *clcf;
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
        if (clcf->gzip_disable_degradation && ngx_http_degraded(r))
        {
            return ngx_http_next_header_filter(r);
        }
    }
#endif
    if (!r->gzip_tested)
    {
        if (ngx_http_gzip_ok(r) != NGX_OK)
        {
            return ngx_http_next_header_filter(r);
        }
    }
    else if (!r->gzip_ok)
    {
        return ngx_http_next_header_filter(r);
    }
    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t));
    if (ctx == NULL)
    {
        return NGX_ERROR;
    }
    ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
    ctx->request = r;
    ctx->buffering = (conf->postpone_gzipping != 0);
    ngx_http_gzip_filter_memory(r, ctx);
    h = ngx_list_push(&r->headers_out.headers);
    if (h == NULL)
    {
        return NGX_ERROR;
    }
    h->hash = 1;
    ngx_str_set(&h->key, "Content-Encoding");
    ngx_str_set(&h->value, "gzip");
    r->headers_out.content_encoding = h;
    r->main_filter_need_in_memory = 1;
    ngx_http_clear_content_length(r);
    ngx_http_clear_accept_ranges(r);
    ngx_http_weak_etag(r);
    return ngx_http_next_header_filter(r);
}