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