Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.19.2 ]​[ nginx-1.18.0 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 #include <zlib.h>
0013 
0014 
0015 typedef struct {
0016     ngx_flag_t           enable;
0017     ngx_flag_t           no_buffer;
0018 
0019     ngx_hash_t           types;
0020 
0021     ngx_bufs_t           bufs;
0022 
0023     size_t               postpone_gzipping;
0024     ngx_int_t            level;
0025     size_t               wbits;
0026     size_t               memlevel;
0027     ssize_t              min_length;
0028 
0029     ngx_array_t         *types_keys;
0030 } ngx_http_gzip_conf_t;
0031 
0032 
0033 typedef struct {
0034     ngx_chain_t         *in;
0035     ngx_chain_t         *free;
0036     ngx_chain_t         *busy;
0037     ngx_chain_t         *out;
0038     ngx_chain_t        **last_out;
0039 
0040     ngx_chain_t         *copied;
0041     ngx_chain_t         *copy_buf;
0042 
0043     ngx_buf_t           *in_buf;
0044     ngx_buf_t           *out_buf;
0045     ngx_int_t            bufs;
0046 
0047     void                *preallocated;
0048     char                *free_mem;
0049     ngx_uint_t           allocated;
0050 
0051     int                  wbits;
0052     int                  memlevel;
0053 
0054     unsigned             flush:4;
0055     unsigned             redo:1;
0056     unsigned             done:1;
0057     unsigned             nomem:1;
0058     unsigned             buffering:1;
0059     unsigned             intel:1;
0060 
0061     size_t               zin;
0062     size_t               zout;
0063 
0064     z_stream             zstream;
0065     ngx_http_request_t  *request;
0066 } ngx_http_gzip_ctx_t;
0067 
0068 
0069 static void ngx_http_gzip_filter_memory(ngx_http_request_t *r,
0070     ngx_http_gzip_ctx_t *ctx);
0071 static ngx_int_t ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx,
0072     ngx_chain_t *in);
0073 static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
0074     ngx_http_gzip_ctx_t *ctx);
0075 static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r,
0076     ngx_http_gzip_ctx_t *ctx);
0077 static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r,
0078     ngx_http_gzip_ctx_t *ctx);
0079 static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r,
0080     ngx_http_gzip_ctx_t *ctx);
0081 static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
0082     ngx_http_gzip_ctx_t *ctx);
0083 
0084 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
0085     u_int size);
0086 static void ngx_http_gzip_filter_free(void *opaque, void *address);
0087 static void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
0088     ngx_http_gzip_ctx_t *ctx);
0089 
0090 static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf);
0091 static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
0092     ngx_http_variable_value_t *v, uintptr_t data);
0093 
0094 static ngx_int_t ngx_http_gzip_filter_init(ngx_conf_t *cf);
0095 static void *ngx_http_gzip_create_conf(ngx_conf_t *cf);
0096 static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
0097     void *parent, void *child);
0098 static char *ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data);
0099 static char *ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data);
0100 
0101 
0102 static ngx_conf_num_bounds_t  ngx_http_gzip_comp_level_bounds = {
0103     ngx_conf_check_num_bounds, 1, 9
0104 };
0105 
0106 static ngx_conf_post_handler_pt  ngx_http_gzip_window_p = ngx_http_gzip_window;
0107 static ngx_conf_post_handler_pt  ngx_http_gzip_hash_p = ngx_http_gzip_hash;
0108 
0109 
0110 static ngx_command_t  ngx_http_gzip_filter_commands[] = {
0111 
0112     { ngx_string("gzip"),
0113       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
0114                         |NGX_CONF_FLAG,
0115       ngx_conf_set_flag_slot,
0116       NGX_HTTP_LOC_CONF_OFFSET,
0117       offsetof(ngx_http_gzip_conf_t, enable),
0118       NULL },
0119 
0120     { ngx_string("gzip_buffers"),
0121       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
0122       ngx_conf_set_bufs_slot,
0123       NGX_HTTP_LOC_CONF_OFFSET,
0124       offsetof(ngx_http_gzip_conf_t, bufs),
0125       NULL },
0126 
0127     { ngx_string("gzip_types"),
0128       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
0129       ngx_http_types_slot,
0130       NGX_HTTP_LOC_CONF_OFFSET,
0131       offsetof(ngx_http_gzip_conf_t, types_keys),
0132       &ngx_http_html_default_types[0] },
0133 
0134     { ngx_string("gzip_comp_level"),
0135       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0136       ngx_conf_set_num_slot,
0137       NGX_HTTP_LOC_CONF_OFFSET,
0138       offsetof(ngx_http_gzip_conf_t, level),
0139       &ngx_http_gzip_comp_level_bounds },
0140 
0141     { ngx_string("gzip_window"),
0142       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0143       ngx_conf_set_size_slot,
0144       NGX_HTTP_LOC_CONF_OFFSET,
0145       offsetof(ngx_http_gzip_conf_t, wbits),
0146       &ngx_http_gzip_window_p },
0147 
0148     { ngx_string("gzip_hash"),
0149       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0150       ngx_conf_set_size_slot,
0151       NGX_HTTP_LOC_CONF_OFFSET,
0152       offsetof(ngx_http_gzip_conf_t, memlevel),
0153       &ngx_http_gzip_hash_p },
0154 
0155     { ngx_string("postpone_gzipping"),
0156       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0157       ngx_conf_set_size_slot,
0158       NGX_HTTP_LOC_CONF_OFFSET,
0159       offsetof(ngx_http_gzip_conf_t, postpone_gzipping),
0160       NULL },
0161 
0162     { ngx_string("gzip_no_buffer"),
0163       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
0164       ngx_conf_set_flag_slot,
0165       NGX_HTTP_LOC_CONF_OFFSET,
0166       offsetof(ngx_http_gzip_conf_t, no_buffer),
0167       NULL },
0168 
0169     { ngx_string("gzip_min_length"),
0170       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0171       ngx_conf_set_size_slot,
0172       NGX_HTTP_LOC_CONF_OFFSET,
0173       offsetof(ngx_http_gzip_conf_t, min_length),
0174       NULL },
0175 
0176       ngx_null_command
0177 };
0178 
0179 
0180 static ngx_http_module_t  ngx_http_gzip_filter_module_ctx = {
0181     ngx_http_gzip_add_variables,           /* preconfiguration */
0182     ngx_http_gzip_filter_init,             /* postconfiguration */
0183 
0184     NULL,                                  /* create main configuration */
0185     NULL,                                  /* init main configuration */
0186 
0187     NULL,                                  /* create server configuration */
0188     NULL,                                  /* merge server configuration */
0189 
0190     ngx_http_gzip_create_conf,             /* create location configuration */
0191     ngx_http_gzip_merge_conf               /* merge location configuration */
0192 };
0193 
0194 
0195 ngx_module_t  ngx_http_gzip_filter_module = {
0196     NGX_MODULE_V1,
0197     &ngx_http_gzip_filter_module_ctx,      /* module context */
0198     ngx_http_gzip_filter_commands,         /* module directives */
0199     NGX_HTTP_MODULE,                       /* module type */
0200     NULL,                                  /* init master */
0201     NULL,                                  /* init module */
0202     NULL,                                  /* init process */
0203     NULL,                                  /* init thread */
0204     NULL,                                  /* exit thread */
0205     NULL,                                  /* exit process */
0206     NULL,                                  /* exit master */
0207     NGX_MODULE_V1_PADDING
0208 };
0209 
0210 
0211 static ngx_str_t  ngx_http_gzip_ratio = ngx_string("gzip_ratio");
0212 
0213 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
0214 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
0215 
0216 static ngx_uint_t  ngx_http_gzip_assume_intel;
0217 
0218 
0219 static ngx_int_t
0220 ngx_http_gzip_header_filter(ngx_http_request_t *r)
0221 {
0222     ngx_table_elt_t       *h;
0223     ngx_http_gzip_ctx_t   *ctx;
0224     ngx_http_gzip_conf_t  *conf;
0225 
0226     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
0227 
0228     if (!conf->enable
0229         || (r->headers_out.status != NGX_HTTP_OK
0230             && r->headers_out.status != NGX_HTTP_FORBIDDEN
0231             && r->headers_out.status != NGX_HTTP_NOT_FOUND)
0232         || (r->headers_out.content_encoding
0233             && r->headers_out.content_encoding->value.len)
0234         || (r->headers_out.content_length_n != -1
0235             && r->headers_out.content_length_n < conf->min_length)
0236         || ngx_http_test_content_type(r, &conf->types) == NULL
0237         || r->header_only)
0238     {
0239         return ngx_http_next_header_filter(r);
0240     }
0241 
0242     r->gzip_vary = 1;
0243 
0244 #if (NGX_HTTP_DEGRADATION)
0245     {
0246     ngx_http_core_loc_conf_t  *clcf;
0247 
0248     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
0249 
0250     if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) {
0251         return ngx_http_next_header_filter(r);
0252     }
0253     }
0254 #endif
0255 
0256     if (!r->gzip_tested) {
0257         if (ngx_http_gzip_ok(r) != NGX_OK) {
0258             return ngx_http_next_header_filter(r);
0259         }
0260 
0261     } else if (!r->gzip_ok) {
0262         return ngx_http_next_header_filter(r);
0263     }
0264 
0265     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t));
0266     if (ctx == NULL) {
0267         return NGX_ERROR;
0268     }
0269 
0270     ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
0271 
0272     ctx->request = r;
0273     ctx->buffering = (conf->postpone_gzipping != 0);
0274 
0275     ngx_http_gzip_filter_memory(r, ctx);
0276 
0277     h = ngx_list_push(&r->headers_out.headers);
0278     if (h == NULL) {
0279         return NGX_ERROR;
0280     }
0281 
0282     h->hash = 1;
0283     ngx_str_set(&h->key, "Content-Encoding");
0284     ngx_str_set(&h->value, "gzip");
0285     r->headers_out.content_encoding = h;
0286 
0287     r->main_filter_need_in_memory = 1;
0288 
0289     ngx_http_clear_content_length(r);
0290     ngx_http_clear_accept_ranges(r);
0291     ngx_http_weak_etag(r);
0292 
0293     return ngx_http_next_header_filter(r);
0294 }
0295 
0296 
0297 static ngx_int_t
0298 ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
0299 {
0300     int                   rc;
0301     ngx_uint_t            flush;
0302     ngx_chain_t          *cl;
0303     ngx_http_gzip_ctx_t  *ctx;
0304 
0305     ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
0306 
0307     if (ctx == NULL || ctx->done || r->header_only) {
0308         return ngx_http_next_body_filter(r, in);
0309     }
0310 
0311     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0312                    "http gzip filter");
0313 
0314     if (ctx->buffering) {
0315 
0316         /*
0317          * With default memory settings zlib starts to output gzipped data
0318          * only after it has got about 90K, so it makes sense to allocate
0319          * zlib memory (200-400K) only after we have enough data to compress.
0320          * Although we copy buffers, nevertheless for not big responses
0321          * this allows to allocate zlib memory, to compress and to output
0322          * the response in one step using hot CPU cache.
0323          */
0324 
0325         if (in) {
0326             switch (ngx_http_gzip_filter_buffer(ctx, in)) {
0327 
0328             case NGX_OK:
0329                 return NGX_OK;
0330 
0331             case NGX_DONE:
0332                 in = NULL;
0333                 break;
0334 
0335             default:  /* NGX_ERROR */
0336                 goto failed;
0337             }
0338 
0339         } else {
0340             ctx->buffering = 0;
0341         }
0342     }
0343 
0344     if (ctx->preallocated == NULL) {
0345         if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) {
0346             goto failed;
0347         }
0348     }
0349 
0350     if (in) {
0351         if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
0352             goto failed;
0353         }
0354 
0355         r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
0356     }
0357 
0358     if (ctx->nomem) {
0359 
0360         /* flush busy buffers */
0361 
0362         if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
0363             goto failed;
0364         }
0365 
0366         cl = NULL;
0367 
0368         ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
0369                                 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
0370         ctx->nomem = 0;
0371         flush = 0;
0372 
0373     } else {
0374         flush = ctx->busy ? 1 : 0;
0375     }
0376 
0377     for ( ;; ) {
0378 
0379         /* cycle while we can write to a client */
0380 
0381         for ( ;; ) {
0382 
0383             /* cycle while there is data to feed zlib and ... */
0384 
0385             rc = ngx_http_gzip_filter_add_data(r, ctx);
0386 
0387             if (rc == NGX_DECLINED) {
0388                 break;
0389             }
0390 
0391             if (rc == NGX_AGAIN) {
0392                 continue;
0393             }
0394 
0395 
0396             /* ... there are buffers to write zlib output */
0397 
0398             rc = ngx_http_gzip_filter_get_buf(r, ctx);
0399 
0400             if (rc == NGX_DECLINED) {
0401                 break;
0402             }
0403 
0404             if (rc == NGX_ERROR) {
0405                 goto failed;
0406             }
0407 
0408 
0409             rc = ngx_http_gzip_filter_deflate(r, ctx);
0410 
0411             if (rc == NGX_OK) {
0412                 break;
0413             }
0414 
0415             if (rc == NGX_ERROR) {
0416                 goto failed;
0417             }
0418 
0419             /* rc == NGX_AGAIN */
0420         }
0421 
0422         if (ctx->out == NULL && !flush) {
0423             ngx_http_gzip_filter_free_copy_buf(r, ctx);
0424 
0425             return ctx->busy ? NGX_AGAIN : NGX_OK;
0426         }
0427 
0428         rc = ngx_http_next_body_filter(r, ctx->out);
0429 
0430         if (rc == NGX_ERROR) {
0431             goto failed;
0432         }
0433 
0434         ngx_http_gzip_filter_free_copy_buf(r, ctx);
0435 
0436         ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
0437                                 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
0438         ctx->last_out = &ctx->out;
0439 
0440         ctx->nomem = 0;
0441         flush = 0;
0442 
0443         if (ctx->done) {
0444             return rc;
0445         }
0446     }
0447 
0448     /* unreachable */
0449 
0450 failed:
0451 
0452     ctx->done = 1;
0453 
0454     if (ctx->preallocated) {
0455         deflateEnd(&ctx->zstream);
0456 
0457         ngx_pfree(r->pool, ctx->preallocated);
0458     }
0459 
0460     ngx_http_gzip_filter_free_copy_buf(r, ctx);
0461 
0462     return NGX_ERROR;
0463 }
0464 
0465 
0466 static void
0467 ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
0468 {
0469     int                    wbits, memlevel;
0470     ngx_http_gzip_conf_t  *conf;
0471 
0472     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
0473 
0474     wbits = conf->wbits;
0475     memlevel = conf->memlevel;
0476 
0477     if (r->headers_out.content_length_n > 0) {
0478 
0479         /* the actual zlib window size is smaller by 262 bytes */
0480 
0481         while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) {
0482             wbits--;
0483             memlevel--;
0484         }
0485 
0486         if (memlevel < 1) {
0487             memlevel = 1;
0488         }
0489     }
0490 
0491     ctx->wbits = wbits;
0492     ctx->memlevel = memlevel;
0493 
0494     /*
0495      * We preallocate a memory for zlib in one buffer (200K-400K), this
0496      * decreases a number of malloc() and free() calls and also probably
0497      * decreases a number of syscalls (sbrk()/mmap() and so on).
0498      * Besides we free the memory as soon as a gzipping will complete
0499      * and do not wait while a whole response will be sent to a client.
0500      *
0501      * 8K is for zlib deflate_state, it takes
0502      *  *) 5816 bytes on i386 and sparc64 (32-bit mode)
0503      *  *) 5920 bytes on amd64 and sparc64
0504      */
0505 
0506     if (!ngx_http_gzip_assume_intel) {
0507         ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
0508 
0509     } else {
0510         /*
0511          * A zlib variant from Intel, https://github.com/jtkukunas/zlib.
0512          * It can force window bits to 13 for fast compression level,
0513          * on processors with SSE 4.2 it uses 64K hash instead of scaling
0514          * it from the specified memory level, and also introduces
0515          * 16-byte padding in one out of the two window-sized buffers.
0516          */
0517 
0518         if (conf->level == 1) {
0519             wbits = ngx_max(wbits, 13);
0520         }
0521 
0522         ctx->allocated = 8192 + 16 + (1 << (wbits + 2))
0523                          + (1 << (ngx_max(memlevel, 8) + 8))
0524                          + (1 << (memlevel + 8));
0525         ctx->intel = 1;
0526     }
0527 }
0528 
0529 
0530 static ngx_int_t
0531 ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in)
0532 {
0533     size_t                 size, buffered;
0534     ngx_buf_t             *b, *buf;
0535     ngx_chain_t           *cl, **ll;
0536     ngx_http_request_t    *r;
0537     ngx_http_gzip_conf_t  *conf;
0538 
0539     r = ctx->request;
0540 
0541     r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
0542 
0543     buffered = 0;
0544     ll = &ctx->in;
0545 
0546     for (cl = ctx->in; cl; cl = cl->next) {
0547         buffered += cl->buf->last - cl->buf->pos;
0548         ll = &cl->next;
0549     }
0550 
0551     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
0552 
0553     while (in) {
0554         cl = ngx_alloc_chain_link(r->pool);
0555         if (cl == NULL) {
0556             return NGX_ERROR;
0557         }
0558 
0559         b = in->buf;
0560 
0561         size = b->last - b->pos;
0562         buffered += size;
0563 
0564         if (b->flush || b->last_buf || buffered > conf->postpone_gzipping) {
0565             ctx->buffering = 0;
0566         }
0567 
0568         if (ctx->buffering && size) {
0569 
0570             buf = ngx_create_temp_buf(r->pool, size);
0571             if (buf == NULL) {
0572                 return NGX_ERROR;
0573             }
0574 
0575             buf->last = ngx_cpymem(buf->pos, b->pos, size);
0576             b->pos = b->last;
0577 
0578             buf->last_buf = b->last_buf;
0579             buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
0580 
0581             cl->buf = buf;
0582 
0583         } else {
0584             cl->buf = b;
0585         }
0586 
0587         *ll = cl;
0588         ll = &cl->next;
0589         in = in->next;
0590     }
0591 
0592     *ll = NULL;
0593 
0594     return ctx->buffering ? NGX_OK : NGX_DONE;
0595 }
0596 
0597 
0598 static ngx_int_t
0599 ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
0600     ngx_http_gzip_ctx_t *ctx)
0601 {
0602     int                    rc;
0603     ngx_http_gzip_conf_t  *conf;
0604 
0605     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
0606 
0607     ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
0608     if (ctx->preallocated == NULL) {
0609         return NGX_ERROR;
0610     }
0611 
0612     ctx->free_mem = ctx->preallocated;
0613 
0614     ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
0615     ctx->zstream.zfree = ngx_http_gzip_filter_free;
0616     ctx->zstream.opaque = ctx;
0617 
0618     rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
0619                       ctx->wbits + 16, ctx->memlevel, Z_DEFAULT_STRATEGY);
0620 
0621     if (rc != Z_OK) {
0622         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
0623                       "deflateInit2() failed: %d", rc);
0624         return NGX_ERROR;
0625     }
0626 
0627     ctx->last_out = &ctx->out;
0628     ctx->flush = Z_NO_FLUSH;
0629 
0630     return NGX_OK;
0631 }
0632 
0633 
0634 static ngx_int_t
0635 ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
0636 {
0637     ngx_chain_t  *cl;
0638 
0639     if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
0640         return NGX_OK;
0641     }
0642 
0643     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0644                    "gzip in: %p", ctx->in);
0645 
0646     if (ctx->in == NULL) {
0647         return NGX_DECLINED;
0648     }
0649 
0650     if (ctx->copy_buf) {
0651 
0652         /*
0653          * to avoid CPU cache trashing we do not free() just quit buf,
0654          * but postpone free()ing after zlib compressing and data output
0655          */
0656 
0657         ctx->copy_buf->next = ctx->copied;
0658         ctx->copied = ctx->copy_buf;
0659         ctx->copy_buf = NULL;
0660     }
0661 
0662     cl = ctx->in;
0663     ctx->in_buf = cl->buf;
0664     ctx->in = cl->next;
0665 
0666     if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) {
0667         ctx->copy_buf = cl;
0668 
0669     } else {
0670         ngx_free_chain(r->pool, cl);
0671     }
0672 
0673     ctx->zstream.next_in = ctx->in_buf->pos;
0674     ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
0675 
0676     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0677                    "gzip in_buf:%p ni:%p ai:%ud",
0678                    ctx->in_buf,
0679                    ctx->zstream.next_in, ctx->zstream.avail_in);
0680 
0681     if (ctx->in_buf->last_buf) {
0682         ctx->flush = Z_FINISH;
0683 
0684     } else if (ctx->in_buf->flush) {
0685         ctx->flush = Z_SYNC_FLUSH;
0686 
0687     } else if (ctx->zstream.avail_in == 0) {
0688         /* ctx->flush == Z_NO_FLUSH */
0689         return NGX_AGAIN;
0690     }
0691 
0692     return NGX_OK;
0693 }
0694 
0695 
0696 static ngx_int_t
0697 ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
0698 {
0699     ngx_chain_t           *cl;
0700     ngx_http_gzip_conf_t  *conf;
0701 
0702     if (ctx->zstream.avail_out) {
0703         return NGX_OK;
0704     }
0705 
0706     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
0707 
0708     if (ctx->free) {
0709 
0710         cl = ctx->free;
0711         ctx->out_buf = cl->buf;
0712         ctx->free = cl->next;
0713 
0714         ngx_free_chain(r->pool, cl);
0715 
0716     } else if (ctx->bufs < conf->bufs.num) {
0717 
0718         ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
0719         if (ctx->out_buf == NULL) {
0720             return NGX_ERROR;
0721         }
0722 
0723         ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
0724         ctx->out_buf->recycled = 1;
0725         ctx->bufs++;
0726 
0727     } else {
0728         ctx->nomem = 1;
0729         return NGX_DECLINED;
0730     }
0731 
0732     ctx->zstream.next_out = ctx->out_buf->pos;
0733     ctx->zstream.avail_out = conf->bufs.size;
0734 
0735     return NGX_OK;
0736 }
0737 
0738 
0739 static ngx_int_t
0740 ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
0741 {
0742     int                    rc;
0743     ngx_buf_t             *b;
0744     ngx_chain_t           *cl;
0745     ngx_http_gzip_conf_t  *conf;
0746 
0747     ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0748                  "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
0749                  ctx->zstream.next_in, ctx->zstream.next_out,
0750                  ctx->zstream.avail_in, ctx->zstream.avail_out,
0751                  ctx->flush, ctx->redo);
0752 
0753     rc = deflate(&ctx->zstream, ctx->flush);
0754 
0755     if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {
0756         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
0757                       "deflate() failed: %d, %d", ctx->flush, rc);
0758         return NGX_ERROR;
0759     }
0760 
0761     ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0762                    "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
0763                    ctx->zstream.next_in, ctx->zstream.next_out,
0764                    ctx->zstream.avail_in, ctx->zstream.avail_out,
0765                    rc);
0766 
0767     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0768                    "gzip in_buf:%p pos:%p",
0769                    ctx->in_buf, ctx->in_buf->pos);
0770 
0771     if (ctx->zstream.next_in) {
0772         ctx->in_buf->pos = ctx->zstream.next_in;
0773 
0774         if (ctx->zstream.avail_in == 0) {
0775             ctx->zstream.next_in = NULL;
0776         }
0777     }
0778 
0779     ctx->out_buf->last = ctx->zstream.next_out;
0780 
0781     if (ctx->zstream.avail_out == 0 && rc != Z_STREAM_END) {
0782 
0783         /* zlib wants to output some more gzipped data */
0784 
0785         cl = ngx_alloc_chain_link(r->pool);
0786         if (cl == NULL) {
0787             return NGX_ERROR;
0788         }
0789 
0790         cl->buf = ctx->out_buf;
0791         cl->next = NULL;
0792         *ctx->last_out = cl;
0793         ctx->last_out = &cl->next;
0794 
0795         ctx->redo = 1;
0796 
0797         return NGX_AGAIN;
0798     }
0799 
0800     ctx->redo = 0;
0801 
0802     if (ctx->flush == Z_SYNC_FLUSH) {
0803 
0804         ctx->flush = Z_NO_FLUSH;
0805 
0806         cl = ngx_alloc_chain_link(r->pool);
0807         if (cl == NULL) {
0808             return NGX_ERROR;
0809         }
0810 
0811         b = ctx->out_buf;
0812 
0813         if (ngx_buf_size(b) == 0) {
0814 
0815             b = ngx_calloc_buf(ctx->request->pool);
0816             if (b == NULL) {
0817                 return NGX_ERROR;
0818             }
0819 
0820         } else {
0821             ctx->zstream.avail_out = 0;
0822         }
0823 
0824         b->flush = 1;
0825 
0826         cl->buf = b;
0827         cl->next = NULL;
0828         *ctx->last_out = cl;
0829         ctx->last_out = &cl->next;
0830 
0831         r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
0832 
0833         return NGX_OK;
0834     }
0835 
0836     if (rc == Z_STREAM_END) {
0837 
0838         if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) {
0839             return NGX_ERROR;
0840         }
0841 
0842         return NGX_OK;
0843     }
0844 
0845     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
0846 
0847     if (conf->no_buffer && ctx->in == NULL) {
0848 
0849         cl = ngx_alloc_chain_link(r->pool);
0850         if (cl == NULL) {
0851             return NGX_ERROR;
0852         }
0853 
0854         cl->buf = ctx->out_buf;
0855         cl->next = NULL;
0856         *ctx->last_out = cl;
0857         ctx->last_out = &cl->next;
0858 
0859         return NGX_OK;
0860     }
0861 
0862     return NGX_AGAIN;
0863 }
0864 
0865 
0866 static ngx_int_t
0867 ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
0868     ngx_http_gzip_ctx_t *ctx)
0869 {
0870     int           rc;
0871     ngx_buf_t    *b;
0872     ngx_chain_t  *cl;
0873 
0874     ctx->zin = ctx->zstream.total_in;
0875     ctx->zout = ctx->zstream.total_out;
0876 
0877     rc = deflateEnd(&ctx->zstream);
0878 
0879     if (rc != Z_OK) {
0880         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
0881                       "deflateEnd() failed: %d", rc);
0882         return NGX_ERROR;
0883     }
0884 
0885     ngx_pfree(r->pool, ctx->preallocated);
0886 
0887     cl = ngx_alloc_chain_link(r->pool);
0888     if (cl == NULL) {
0889         return NGX_ERROR;
0890     }
0891 
0892     b = ctx->out_buf;
0893 
0894     if (ngx_buf_size(b) == 0) {
0895         b->temporary = 0;
0896     }
0897 
0898     b->last_buf = 1;
0899 
0900     cl->buf = b;
0901     cl->next = NULL;
0902     *ctx->last_out = cl;
0903     ctx->last_out = &cl->next;
0904 
0905     ctx->zstream.avail_in = 0;
0906     ctx->zstream.avail_out = 0;
0907 
0908     ctx->done = 1;
0909 
0910     r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
0911 
0912     return NGX_OK;
0913 }
0914 
0915 
0916 static void *
0917 ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size)
0918 {
0919     ngx_http_gzip_ctx_t *ctx = opaque;
0920 
0921     void        *p;
0922     ngx_uint_t   alloc;
0923 
0924     alloc = items * size;
0925 
0926     if (items == 1 && alloc % 512 != 0 && alloc < 8192) {
0927 
0928         /*
0929          * The zlib deflate_state allocation, it takes about 6K,
0930          * we allocate 8K.  Other allocations are divisible by 512.
0931          */
0932 
0933         alloc = 8192;
0934     }
0935 
0936     if (alloc <= ctx->allocated) {
0937         p = ctx->free_mem;
0938         ctx->free_mem += alloc;
0939         ctx->allocated -= alloc;
0940 
0941         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
0942                        "gzip alloc: n:%ud s:%ud a:%ui p:%p",
0943                        items, size, alloc, p);
0944 
0945         return p;
0946     }
0947 
0948     if (ctx->intel) {
0949         ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0,
0950                       "gzip filter failed to use preallocated memory: "
0951                       "%ud of %ui", items * size, ctx->allocated);
0952 
0953     } else {
0954         ngx_http_gzip_assume_intel = 1;
0955     }
0956 
0957     p = ngx_palloc(ctx->request->pool, items * size);
0958 
0959     return p;
0960 }
0961 
0962 
0963 static void
0964 ngx_http_gzip_filter_free(void *opaque, void *address)
0965 {
0966 #if 0
0967     ngx_http_gzip_ctx_t *ctx = opaque;
0968 
0969     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
0970                    "gzip free: %p", address);
0971 #endif
0972 }
0973 
0974 
0975 static void
0976 ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
0977     ngx_http_gzip_ctx_t *ctx)
0978 {
0979     ngx_chain_t  *cl;
0980 
0981     for (cl = ctx->copied; cl; cl = cl->next) {
0982         ngx_pfree(r->pool, cl->buf->start);
0983     }
0984 
0985     ctx->copied = NULL;
0986 }
0987 
0988 
0989 static ngx_int_t
0990 ngx_http_gzip_add_variables(ngx_conf_t *cf)
0991 {
0992     ngx_http_variable_t  *var;
0993 
0994     var = ngx_http_add_variable(cf, &ngx_http_gzip_ratio, NGX_HTTP_VAR_NOHASH);
0995     if (var == NULL) {
0996         return NGX_ERROR;
0997     }
0998 
0999     var->get_handler = ngx_http_gzip_ratio_variable;
1000 
1001     return NGX_OK;
1002 }
1003 
1004 
1005 static ngx_int_t
1006 ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
1007     ngx_http_variable_value_t *v, uintptr_t data)
1008 {
1009     ngx_uint_t            zint, zfrac;
1010     ngx_http_gzip_ctx_t  *ctx;
1011 
1012     ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
1013 
1014     if (ctx == NULL || ctx->zout == 0) {
1015         v->not_found = 1;
1016         return NGX_OK;
1017     }
1018 
1019     v->valid = 1;
1020     v->no_cacheable = 0;
1021     v->not_found = 0;
1022 
1023     v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN + 3);
1024     if (v->data == NULL) {
1025         return NGX_ERROR;
1026     }
1027 
1028     zint = (ngx_uint_t) (ctx->zin / ctx->zout);
1029     zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);
1030 
1031     if ((ctx->zin * 1000 / ctx->zout) % 10 > 4) {
1032 
1033         /* the rounding, e.g., 2.125 to 2.13 */
1034 
1035         zfrac++;
1036 
1037         if (zfrac > 99) {
1038             zint++;
1039             zfrac = 0;
1040         }
1041     }
1042 
1043     v->len = ngx_sprintf(v->data, "%ui.%02ui", zint, zfrac) - v->data;
1044 
1045     return NGX_OK;
1046 }
1047 
1048 
1049 static void *
1050 ngx_http_gzip_create_conf(ngx_conf_t *cf)
1051 {
1052     ngx_http_gzip_conf_t  *conf;
1053 
1054     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t));
1055     if (conf == NULL) {
1056         return NULL;
1057     }
1058 
1059     /*
1060      * set by ngx_pcalloc():
1061      *
1062      *     conf->bufs.num = 0;
1063      *     conf->types = { NULL };
1064      *     conf->types_keys = NULL;
1065      */
1066 
1067     conf->enable = NGX_CONF_UNSET;
1068     conf->no_buffer = NGX_CONF_UNSET;
1069 
1070     conf->postpone_gzipping = NGX_CONF_UNSET_SIZE;
1071     conf->level = NGX_CONF_UNSET;
1072     conf->wbits = NGX_CONF_UNSET_SIZE;
1073     conf->memlevel = NGX_CONF_UNSET_SIZE;
1074     conf->min_length = NGX_CONF_UNSET;
1075 
1076     return conf;
1077 }
1078 
1079 
1080 static char *
1081 ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1082 {
1083     ngx_http_gzip_conf_t *prev = parent;
1084     ngx_http_gzip_conf_t *conf = child;
1085 
1086     ngx_conf_merge_value(conf->enable, prev->enable, 0);
1087     ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);
1088 
1089     ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
1090                               (128 * 1024) / ngx_pagesize, ngx_pagesize);
1091 
1092     ngx_conf_merge_size_value(conf->postpone_gzipping, prev->postpone_gzipping,
1093                               0);
1094     ngx_conf_merge_value(conf->level, prev->level, 1);
1095     ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
1096     ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,
1097                               MAX_MEM_LEVEL - 1);
1098     ngx_conf_merge_value(conf->min_length, prev->min_length, 20);
1099 
1100     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
1101                              &prev->types_keys, &prev->types,
1102                              ngx_http_html_default_types)
1103         != NGX_OK)
1104     {
1105         return NGX_CONF_ERROR;
1106     }
1107 
1108     return NGX_CONF_OK;
1109 }
1110 
1111 
1112 static ngx_int_t
1113 ngx_http_gzip_filter_init(ngx_conf_t *cf)
1114 {
1115     ngx_http_next_header_filter = ngx_http_top_header_filter;
1116     ngx_http_top_header_filter = ngx_http_gzip_header_filter;
1117 
1118     ngx_http_next_body_filter = ngx_http_top_body_filter;
1119     ngx_http_top_body_filter = ngx_http_gzip_body_filter;
1120 
1121     return NGX_OK;
1122 }
1123 
1124 
1125 static char *
1126 ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data)
1127 {
1128     size_t *np = data;
1129 
1130     size_t  wbits, wsize;
1131 
1132     wbits = 15;
1133 
1134     for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {
1135 
1136         if (wsize == *np) {
1137             *np = wbits;
1138 
1139             return NGX_CONF_OK;
1140         }
1141 
1142         wbits--;
1143     }
1144 
1145     return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k";
1146 }
1147 
1148 
1149 static char *
1150 ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data)
1151 {
1152     size_t *np = data;
1153 
1154     size_t  memlevel, hsize;
1155 
1156     memlevel = 9;
1157 
1158     for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) {
1159 
1160         if (hsize == *np) {
1161             *np = memlevel;
1162 
1163             return NGX_CONF_OK;
1164         }
1165 
1166         memlevel--;
1167     }
1168 
1169     return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k";
1170 }