Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

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