src/http/modules/ngx_http_gzip_filter_module.c - nginx-1.7.10

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>

  8. #include <zlib.h>


  9. typedef struct {
  10.     ngx_flag_t           enable;
  11.     ngx_flag_t           no_buffer;

  12.     ngx_hash_t           types;

  13.     ngx_bufs_t           bufs;

  14.     size_t               postpone_gzipping;
  15.     ngx_int_t            level;
  16.     size_t               wbits;
  17.     size_t               memlevel;
  18.     ssize_t              min_length;

  19.     ngx_array_t         *types_keys;
  20. } ngx_http_gzip_conf_t;


  21. typedef struct {
  22.     ngx_chain_t         *in;
  23.     ngx_chain_t         *free;
  24.     ngx_chain_t         *busy;
  25.     ngx_chain_t         *out;
  26.     ngx_chain_t        **last_out;

  27.     ngx_chain_t         *copied;
  28.     ngx_chain_t         *copy_buf;

  29.     ngx_buf_t           *in_buf;
  30.     ngx_buf_t           *out_buf;
  31.     ngx_int_t            bufs;

  32.     void                *preallocated;
  33.     char                *free_mem;
  34.     ngx_uint_t           allocated;

  35.     int                  wbits;
  36.     int                  memlevel;

  37.     unsigned             flush:4;
  38.     unsigned             redo:1;
  39.     unsigned             done:1;
  40.     unsigned             nomem:1;
  41.     unsigned             gzheader:1;
  42.     unsigned             buffering:1;

  43.     size_t               zin;
  44.     size_t               zout;

  45.     uint32_t             crc32;
  46.     z_stream             zstream;
  47.     ngx_http_request_t  *request;
  48. } ngx_http_gzip_ctx_t;


  49. #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)

  50. struct gztrailer {
  51.     uint32_t  crc32;
  52.     uint32_t  zlen;
  53. };

  54. #else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */

  55. struct gztrailer {
  56.     u_char  crc32[4];
  57.     u_char  zlen[4];
  58. };

  59. #endif


  60. static void ngx_http_gzip_filter_memory(ngx_http_request_t *r,
  61.     ngx_http_gzip_ctx_t *ctx);
  62. static ngx_int_t ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx,
  63.     ngx_chain_t *in);
  64. static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
  65.     ngx_http_gzip_ctx_t *ctx);
  66. static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r,
  67.     ngx_http_gzip_ctx_t *ctx);
  68. static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r,
  69.     ngx_http_gzip_ctx_t *ctx);
  70. static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r,
  71.     ngx_http_gzip_ctx_t *ctx);
  72. static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r,
  73.     ngx_http_gzip_ctx_t *ctx);
  74. static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
  75.     ngx_http_gzip_ctx_t *ctx);

  76. static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
  77.     u_int size);
  78. static void ngx_http_gzip_filter_free(void *opaque, void *address);
  79. static void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
  80.     ngx_http_gzip_ctx_t *ctx);

  81. static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf);
  82. static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
  83.     ngx_http_variable_value_t *v, uintptr_t data);

  84. static ngx_int_t ngx_http_gzip_filter_init(ngx_conf_t *cf);
  85. static void *ngx_http_gzip_create_conf(ngx_conf_t *cf);
  86. static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
  87.     void *parent, void *child);
  88. static char *ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data);
  89. static char *ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data);


  90. static ngx_conf_num_bounds_t  ngx_http_gzip_comp_level_bounds = {
  91.     ngx_conf_check_num_bounds, 1, 9
  92. };

  93. static ngx_conf_post_handler_pt  ngx_http_gzip_window_p = ngx_http_gzip_window;
  94. static ngx_conf_post_handler_pt  ngx_http_gzip_hash_p = ngx_http_gzip_hash;


  95. static ngx_command_t  ngx_http_gzip_filter_commands[] = {

  96.     { ngx_string("gzip"),
  97.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
  98.                         |NGX_CONF_FLAG,
  99.       ngx_conf_set_flag_slot,
  100.       NGX_HTTP_LOC_CONF_OFFSET,
  101.       offsetof(ngx_http_gzip_conf_t, enable),
  102.       NULL },

  103.     { ngx_string("gzip_buffers"),
  104.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
  105.       ngx_conf_set_bufs_slot,
  106.       NGX_HTTP_LOC_CONF_OFFSET,
  107.       offsetof(ngx_http_gzip_conf_t, bufs),
  108.       NULL },

  109.     { ngx_string("gzip_types"),
  110.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  111.       ngx_http_types_slot,
  112.       NGX_HTTP_LOC_CONF_OFFSET,
  113.       offsetof(ngx_http_gzip_conf_t, types_keys),
  114.       &ngx_http_html_default_types[0] },

  115.     { ngx_string("gzip_comp_level"),
  116.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  117.       ngx_conf_set_num_slot,
  118.       NGX_HTTP_LOC_CONF_OFFSET,
  119.       offsetof(ngx_http_gzip_conf_t, level),
  120.       &ngx_http_gzip_comp_level_bounds },

  121.     { ngx_string("gzip_window"),
  122.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  123.       ngx_conf_set_size_slot,
  124.       NGX_HTTP_LOC_CONF_OFFSET,
  125.       offsetof(ngx_http_gzip_conf_t, wbits),
  126.       &ngx_http_gzip_window_p },

  127.     { ngx_string("gzip_hash"),
  128.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  129.       ngx_conf_set_size_slot,
  130.       NGX_HTTP_LOC_CONF_OFFSET,
  131.       offsetof(ngx_http_gzip_conf_t, memlevel),
  132.       &ngx_http_gzip_hash_p },

  133.     { ngx_string("postpone_gzipping"),
  134.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  135.       ngx_conf_set_size_slot,
  136.       NGX_HTTP_LOC_CONF_OFFSET,
  137.       offsetof(ngx_http_gzip_conf_t, postpone_gzipping),
  138.       NULL },

  139.     { ngx_string("gzip_no_buffer"),
  140.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  141.       ngx_conf_set_flag_slot,
  142.       NGX_HTTP_LOC_CONF_OFFSET,
  143.       offsetof(ngx_http_gzip_conf_t, no_buffer),
  144.       NULL },

  145.     { ngx_string("gzip_min_length"),
  146.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  147.       ngx_conf_set_size_slot,
  148.       NGX_HTTP_LOC_CONF_OFFSET,
  149.       offsetof(ngx_http_gzip_conf_t, min_length),
  150.       NULL },

  151.       ngx_null_command
  152. };


  153. static ngx_http_module_t  ngx_http_gzip_filter_module_ctx = {
  154.     ngx_http_gzip_add_variables,           /* preconfiguration */
  155.     ngx_http_gzip_filter_init,             /* postconfiguration */

  156.     NULL,                                  /* create main configuration */
  157.     NULL,                                  /* init main configuration */

  158.     NULL,                                  /* create server configuration */
  159.     NULL,                                  /* merge server configuration */

  160.     ngx_http_gzip_create_conf,             /* create location configuration */
  161.     ngx_http_gzip_merge_conf               /* merge location configuration */
  162. };


  163. ngx_module_t  ngx_http_gzip_filter_module = {
  164.     NGX_MODULE_V1,
  165.     &ngx_http_gzip_filter_module_ctx,      /* module context */
  166.     ngx_http_gzip_filter_commands,         /* module directives */
  167.     NGX_HTTP_MODULE,                       /* module type */
  168.     NULL,                                  /* init master */
  169.     NULL,                                  /* init module */
  170.     NULL,                                  /* init process */
  171.     NULL,                                  /* init thread */
  172.     NULL,                                  /* exit thread */
  173.     NULL,                                  /* exit process */
  174.     NULL,                                  /* exit master */
  175.     NGX_MODULE_V1_PADDING
  176. };


  177. static ngx_str_t  ngx_http_gzip_ratio = ngx_string("gzip_ratio");

  178. static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
  179. static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;


  180. static ngx_int_t
  181. ngx_http_gzip_header_filter(ngx_http_request_t *r)
  182. {
  183.     ngx_table_elt_t       *h;
  184.     ngx_http_gzip_ctx_t   *ctx;
  185.     ngx_http_gzip_conf_t  *conf;

  186.     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

  187.     if (!conf->enable
  188.         || (r->headers_out.status != NGX_HTTP_OK
  189.             && r->headers_out.status != NGX_HTTP_FORBIDDEN
  190.             && r->headers_out.status != NGX_HTTP_NOT_FOUND)
  191.         || (r->headers_out.content_encoding
  192.             && r->headers_out.content_encoding->value.len)
  193.         || (r->headers_out.content_length_n != -1
  194.             && r->headers_out.content_length_n < conf->min_length)
  195.         || ngx_http_test_content_type(r, &conf->types) == NULL
  196.         || r->header_only)
  197.     {
  198.         return ngx_http_next_header_filter(r);
  199.     }

  200.     r->gzip_vary = 1;

  201. #if (NGX_HTTP_DEGRADATION)
  202.     {
  203.     ngx_http_core_loc_conf_t  *clcf;

  204.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

  205.     if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) {
  206.         return ngx_http_next_header_filter(r);
  207.     }
  208.     }
  209. #endif

  210.     if (!r->gzip_tested) {
  211.         if (ngx_http_gzip_ok(r) != NGX_OK) {
  212.             return ngx_http_next_header_filter(r);
  213.         }

  214.     } else if (!r->gzip_ok) {
  215.         return ngx_http_next_header_filter(r);
  216.     }

  217.     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t));
  218.     if (ctx == NULL) {
  219.         return NGX_ERROR;
  220.     }

  221.     ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);

  222.     ctx->request = r;
  223.     ctx->buffering = (conf->postpone_gzipping != 0);

  224.     ngx_http_gzip_filter_memory(r, ctx);

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

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

  233.     r->main_filter_need_in_memory = 1;

  234.     ngx_http_clear_content_length(r);
  235.     ngx_http_clear_accept_ranges(r);
  236.     ngx_http_weak_etag(r);

  237.     return ngx_http_next_header_filter(r);
  238. }


  239. static ngx_int_t
  240. ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
  241. {
  242.     int                   rc;
  243.     ngx_uint_t            flush;
  244.     ngx_chain_t          *cl;
  245.     ngx_http_gzip_ctx_t  *ctx;

  246.     ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);

  247.     if (ctx == NULL || ctx->done || r->header_only) {
  248.         return ngx_http_next_body_filter(r, in);
  249.     }

  250.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  251.                    "http gzip filter");

  252.     if (ctx->buffering) {

  253.         /*
  254.          * With default memory settings zlib starts to output gzipped data
  255.          * only after it has got about 90K, so it makes sense to allocate
  256.          * zlib memory (200-400K) only after we have enough data to compress.
  257.          * Although we copy buffers, nevertheless for not big responses
  258.          * this allows to allocate zlib memory, to compress and to output
  259.          * the response in one step using hot CPU cache.
  260.          */

  261.         if (in) {
  262.             switch (ngx_http_gzip_filter_buffer(ctx, in)) {

  263.             case NGX_OK:
  264.                 return NGX_OK;

  265.             case NGX_DONE:
  266.                 in = NULL;
  267.                 break;

  268.             default:  /* NGX_ERROR */
  269.                 goto failed;
  270.             }

  271.         } else {
  272.             ctx->buffering = 0;
  273.         }
  274.     }

  275.     if (ctx->preallocated == NULL) {
  276.         if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) {
  277.             goto failed;
  278.         }
  279.     }

  280.     if (in) {
  281.         if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
  282.             goto failed;
  283.         }

  284.         r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
  285.     }

  286.     if (ctx->nomem) {

  287.         /* flush busy buffers */

  288.         if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
  289.             goto failed;
  290.         }

  291.         cl = NULL;

  292.         ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
  293.                                 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
  294.         ctx->nomem = 0;
  295.         flush = 0;

  296.     } else {
  297.         flush = ctx->busy ? 1 : 0;
  298.     }

  299.     for ( ;; ) {

  300.         /* cycle while we can write to a client */

  301.         for ( ;; ) {

  302.             /* cycle while there is data to feed zlib and ... */

  303.             rc = ngx_http_gzip_filter_add_data(r, ctx);

  304.             if (rc == NGX_DECLINED) {
  305.                 break;
  306.             }

  307.             if (rc == NGX_AGAIN) {
  308.                 continue;
  309.             }


  310.             /* ... there are buffers to write zlib output */

  311.             rc = ngx_http_gzip_filter_get_buf(r, ctx);

  312.             if (rc == NGX_DECLINED) {
  313.                 break;
  314.             }

  315.             if (rc == NGX_ERROR) {
  316.                 goto failed;
  317.             }


  318.             rc = ngx_http_gzip_filter_deflate(r, ctx);

  319.             if (rc == NGX_OK) {
  320.                 break;
  321.             }

  322.             if (rc == NGX_ERROR) {
  323.                 goto failed;
  324.             }

  325.             /* rc == NGX_AGAIN */
  326.         }

  327.         if (ctx->out == NULL && !flush) {
  328.             ngx_http_gzip_filter_free_copy_buf(r, ctx);

  329.             return ctx->busy ? NGX_AGAIN : NGX_OK;
  330.         }

  331.         if (!ctx->gzheader) {
  332.             if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) {
  333.                 goto failed;
  334.             }
  335.         }

  336.         rc = ngx_http_next_body_filter(r, ctx->out);

  337.         if (rc == NGX_ERROR) {
  338.             goto failed;
  339.         }

  340.         ngx_http_gzip_filter_free_copy_buf(r, ctx);

  341.         ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
  342.                                 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
  343.         ctx->last_out = &ctx->out;

  344.         ctx->nomem = 0;
  345.         flush = 0;

  346.         if (ctx->done) {
  347.             return rc;
  348.         }
  349.     }

  350.     /* unreachable */

  351. failed:

  352.     ctx->done = 1;

  353.     if (ctx->preallocated) {
  354.         deflateEnd(&ctx->zstream);

  355.         ngx_pfree(r->pool, ctx->preallocated);
  356.     }

  357.     ngx_http_gzip_filter_free_copy_buf(r, ctx);

  358.     return NGX_ERROR;
  359. }


  360. static void
  361. ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
  362. {
  363.     int                    wbits, memlevel;
  364.     ngx_http_gzip_conf_t  *conf;

  365.     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

  366.     wbits = conf->wbits;
  367.     memlevel = conf->memlevel;

  368.     if (r->headers_out.content_length_n > 0) {

  369.         /* the actual zlib window size is smaller by 262 bytes */

  370.         while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) {
  371.             wbits--;
  372.             memlevel--;
  373.         }

  374.         if (memlevel < 1) {
  375.             memlevel = 1;
  376.         }
  377.     }

  378.     ctx->wbits = wbits;
  379.     ctx->memlevel = memlevel;

  380.     /*
  381.      * We preallocate a memory for zlib in one buffer (200K-400K), this
  382.      * decreases a number of malloc() and free() calls and also probably
  383.      * decreases a number of syscalls (sbrk()/mmap() and so on).
  384.      * Besides we free the memory as soon as a gzipping will complete
  385.      * and do not wait while a whole response will be sent to a client.
  386.      *
  387.      * 8K is for zlib deflate_state, it takes
  388.      *  *) 5816 bytes on i386 and sparc64 (32-bit mode)
  389.      *  *) 5920 bytes on amd64 and sparc64
  390.      */

  391.     ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
  392. }


  393. static ngx_int_t
  394. ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in)
  395. {
  396.     size_t                 size, buffered;
  397.     ngx_buf_t             *b, *buf;
  398.     ngx_chain_t           *cl, **ll;
  399.     ngx_http_request_t    *r;
  400.     ngx_http_gzip_conf_t  *conf;

  401.     r = ctx->request;

  402.     r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;

  403.     buffered = 0;
  404.     ll = &ctx->in;

  405.     for (cl = ctx->in; cl; cl = cl->next) {
  406.         buffered += cl->buf->last - cl->buf->pos;
  407.         ll = &cl->next;
  408.     }

  409.     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

  410.     while (in) {
  411.         cl = ngx_alloc_chain_link(r->pool);
  412.         if (cl == NULL) {
  413.             return NGX_ERROR;
  414.         }

  415.         b = in->buf;

  416.         size = b->last - b->pos;
  417.         buffered += size;

  418.         if (b->flush || b->last_buf || buffered > conf->postpone_gzipping) {
  419.             ctx->buffering = 0;
  420.         }

  421.         if (ctx->buffering && size) {

  422.             buf = ngx_create_temp_buf(r->pool, size);
  423.             if (buf == NULL) {
  424.                 return NGX_ERROR;
  425.             }

  426.             buf->last = ngx_cpymem(buf->pos, b->pos, size);
  427.             b->pos = b->last;

  428.             buf->last_buf = b->last_buf;
  429.             buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;

  430.             cl->buf = buf;

  431.         } else {
  432.             cl->buf = b;
  433.         }

  434.         *ll = cl;
  435.         ll = &cl->next;
  436.         in = in->next;
  437.     }

  438.     *ll = NULL;

  439.     return ctx->buffering ? NGX_OK : NGX_DONE;
  440. }


  441. static ngx_int_t
  442. ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
  443.     ngx_http_gzip_ctx_t *ctx)
  444. {
  445.     int                    rc;
  446.     ngx_http_gzip_conf_t  *conf;

  447.     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

  448.     ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
  449.     if (ctx->preallocated == NULL) {
  450.         return NGX_ERROR;
  451.     }

  452.     ctx->free_mem = ctx->preallocated;

  453.     ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
  454.     ctx->zstream.zfree = ngx_http_gzip_filter_free;
  455.     ctx->zstream.opaque = ctx;

  456.     rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
  457.                       - ctx->wbits, ctx->memlevel, Z_DEFAULT_STRATEGY);

  458.     if (rc != Z_OK) {
  459.         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
  460.                       "deflateInit2() failed: %d", rc);
  461.         return NGX_ERROR;
  462.     }

  463.     ctx->last_out = &ctx->out;
  464.     ctx->crc32 = crc32(0L, Z_NULL, 0);
  465.     ctx->flush = Z_NO_FLUSH;

  466.     return NGX_OK;
  467. }


  468. static ngx_int_t
  469. ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
  470. {
  471.     ngx_buf_t      *b;
  472.     ngx_chain_t    *cl;
  473.     static u_char  gzheader[10] =
  474.                                { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };

  475.     b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
  476.     if (b == NULL) {
  477.         return NGX_ERROR;
  478.     }

  479.     b->memory = 1;
  480.     b->pos = gzheader;
  481.     b->last = b->pos + 10;

  482.     cl = ngx_alloc_chain_link(r->pool);
  483.     if (cl == NULL) {
  484.         return NGX_ERROR;
  485.     }

  486.     cl->buf = b;
  487.     cl->next = ctx->out;
  488.     ctx->out = cl;

  489.     ctx->gzheader = 1;

  490.     return NGX_OK;
  491. }


  492. static ngx_int_t
  493. ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
  494. {
  495.     if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
  496.         return NGX_OK;
  497.     }

  498.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  499.                    "gzip in: %p", ctx->in);

  500.     if (ctx->in == NULL) {
  501.         return NGX_DECLINED;
  502.     }

  503.     if (ctx->copy_buf) {

  504.         /*
  505.          * to avoid CPU cache trashing we do not free() just quit buf,
  506.          * but postpone free()ing after zlib compressing and data output
  507.          */

  508.         ctx->copy_buf->next = ctx->copied;
  509.         ctx->copied = ctx->copy_buf;
  510.         ctx->copy_buf = NULL;
  511.     }

  512.     ctx->in_buf = ctx->in->buf;

  513.     if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) {
  514.         ctx->copy_buf = ctx->in;
  515.     }

  516.     ctx->in = ctx->in->next;

  517.     ctx->zstream.next_in = ctx->in_buf->pos;
  518.     ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;

  519.     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  520.                    "gzip in_buf:%p ni:%p ai:%ud",
  521.                    ctx->in_buf,
  522.                    ctx->zstream.next_in, ctx->zstream.avail_in);

  523.     if (ctx->in_buf->last_buf) {
  524.         ctx->flush = Z_FINISH;

  525.     } else if (ctx->in_buf->flush) {
  526.         ctx->flush = Z_SYNC_FLUSH;
  527.     }

  528.     if (ctx->zstream.avail_in) {

  529.         ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
  530.                            ctx->zstream.avail_in);

  531.     } else if (ctx->flush == Z_NO_FLUSH) {
  532.         return NGX_AGAIN;
  533.     }

  534.     return NGX_OK;
  535. }


  536. static ngx_int_t
  537. ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
  538. {
  539.     ngx_http_gzip_conf_t  *conf;

  540.     if (ctx->zstream.avail_out) {
  541.         return NGX_OK;
  542.     }

  543.     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

  544.     if (ctx->free) {
  545.         ctx->out_buf = ctx->free->buf;
  546.         ctx->free = ctx->free->next;

  547.     } else if (ctx->bufs < conf->bufs.num) {

  548.         ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
  549.         if (ctx->out_buf == NULL) {
  550.             return NGX_ERROR;
  551.         }

  552.         ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
  553.         ctx->out_buf->recycled = 1;
  554.         ctx->bufs++;

  555.     } else {
  556.         ctx->nomem = 1;
  557.         return NGX_DECLINED;
  558.     }

  559.     ctx->zstream.next_out = ctx->out_buf->pos;
  560.     ctx->zstream.avail_out = conf->bufs.size;

  561.     return NGX_OK;
  562. }


  563. static ngx_int_t
  564. ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
  565. {
  566.     int                    rc;
  567.     ngx_buf_t             *b;
  568.     ngx_chain_t           *cl;
  569.     ngx_http_gzip_conf_t  *conf;

  570.     ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  571.                  "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
  572.                  ctx->zstream.next_in, ctx->zstream.next_out,
  573.                  ctx->zstream.avail_in, ctx->zstream.avail_out,
  574.                  ctx->flush, ctx->redo);

  575.     rc = deflate(&ctx->zstream, ctx->flush);

  576.     if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {
  577.         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
  578.                       "deflate() failed: %d, %d", ctx->flush, rc);
  579.         return NGX_ERROR;
  580.     }

  581.     ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  582.                    "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
  583.                    ctx->zstream.next_in, ctx->zstream.next_out,
  584.                    ctx->zstream.avail_in, ctx->zstream.avail_out,
  585.                    rc);

  586.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  587.                    "gzip in_buf:%p pos:%p",
  588.                    ctx->in_buf, ctx->in_buf->pos);

  589.     if (ctx->zstream.next_in) {
  590.         ctx->in_buf->pos = ctx->zstream.next_in;

  591.         if (ctx->zstream.avail_in == 0) {
  592.             ctx->zstream.next_in = NULL;
  593.         }
  594.     }

  595.     ctx->out_buf->last = ctx->zstream.next_out;

  596.     if (ctx->zstream.avail_out == 0) {

  597.         /* zlib wants to output some more gzipped data */

  598.         cl = ngx_alloc_chain_link(r->pool);
  599.         if (cl == NULL) {
  600.             return NGX_ERROR;
  601.         }

  602.         cl->buf = ctx->out_buf;
  603.         cl->next = NULL;
  604.         *ctx->last_out = cl;
  605.         ctx->last_out = &cl->next;

  606.         ctx->redo = 1;

  607.         return NGX_AGAIN;
  608.     }

  609.     ctx->redo = 0;

  610.     if (ctx->flush == Z_SYNC_FLUSH) {

  611.         ctx->flush = Z_NO_FLUSH;

  612.         cl = ngx_alloc_chain_link(r->pool);
  613.         if (cl == NULL) {
  614.             return NGX_ERROR;
  615.         }

  616.         b = ctx->out_buf;

  617.         if (ngx_buf_size(b) == 0) {

  618.             b = ngx_calloc_buf(ctx->request->pool);
  619.             if (b == NULL) {
  620.                 return NGX_ERROR;
  621.             }

  622.         } else {
  623.             ctx->zstream.avail_out = 0;
  624.         }

  625.         b->flush = 1;

  626.         cl->buf = b;
  627.         cl->next = NULL;
  628.         *ctx->last_out = cl;
  629.         ctx->last_out = &cl->next;

  630.         r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;

  631.         return NGX_OK;
  632.     }

  633.     if (rc == Z_STREAM_END) {

  634.         if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) {
  635.             return NGX_ERROR;
  636.         }

  637.         return NGX_OK;
  638.     }

  639.     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

  640.     if (conf->no_buffer && ctx->in == NULL) {

  641.         cl = ngx_alloc_chain_link(r->pool);
  642.         if (cl == NULL) {
  643.             return NGX_ERROR;
  644.         }

  645.         cl->buf = ctx->out_buf;
  646.         cl->next = NULL;
  647.         *ctx->last_out = cl;
  648.         ctx->last_out = &cl->next;

  649.         return NGX_OK;
  650.     }

  651.     return NGX_AGAIN;
  652. }


  653. static ngx_int_t
  654. ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
  655.     ngx_http_gzip_ctx_t *ctx)
  656. {
  657.     int                rc;
  658.     ngx_buf_t         *b;
  659.     ngx_chain_t       *cl;
  660.     struct gztrailer  *trailer;

  661.     ctx->zin = ctx->zstream.total_in;
  662.     ctx->zout = 10 + ctx->zstream.total_out + 8;

  663.     rc = deflateEnd(&ctx->zstream);

  664.     if (rc != Z_OK) {
  665.         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
  666.                       "deflateEnd() failed: %d", rc);
  667.         return NGX_ERROR;
  668.     }

  669.     ngx_pfree(r->pool, ctx->preallocated);

  670.     cl = ngx_alloc_chain_link(r->pool);
  671.     if (cl == NULL) {
  672.         return NGX_ERROR;
  673.     }

  674.     cl->buf = ctx->out_buf;
  675.     cl->next = NULL;
  676.     *ctx->last_out = cl;
  677.     ctx->last_out = &cl->next;

  678.     if (ctx->zstream.avail_out >= 8) {
  679.         trailer = (struct gztrailer *) ctx->out_buf->last;
  680.         ctx->out_buf->last += 8;
  681.         ctx->out_buf->last_buf = 1;

  682.     } else {
  683.         b = ngx_create_temp_buf(r->pool, 8);
  684.         if (b == NULL) {
  685.             return NGX_ERROR;
  686.         }

  687.         b->last_buf = 1;

  688.         cl = ngx_alloc_chain_link(r->pool);
  689.         if (cl == NULL) {
  690.             return NGX_ERROR;
  691.         }

  692.         cl->buf = b;
  693.         cl->next = NULL;
  694.         *ctx->last_out = cl;
  695.         ctx->last_out = &cl->next;
  696.         trailer = (struct gztrailer *) b->pos;
  697.         b->last += 8;
  698.     }

  699. #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)

  700.     trailer->crc32 = ctx->crc32;
  701.     trailer->zlen = ctx->zin;

  702. #else

  703.     trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
  704.     trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);
  705.     trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
  706.     trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);

  707.     trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
  708.     trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);
  709.     trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
  710.     trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);

  711. #endif

  712.     ctx->zstream.avail_in = 0;
  713.     ctx->zstream.avail_out = 0;

  714.     ctx->done = 1;

  715.     r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;

  716.     return NGX_OK;
  717. }


  718. static void *
  719. ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size)
  720. {
  721.     ngx_http_gzip_ctx_t *ctx = opaque;

  722.     void        *p;
  723.     ngx_uint_t   alloc;

  724.     alloc = items * size;

  725.     if (alloc % 512 != 0 && alloc < 8192) {

  726.         /*
  727.          * The zlib deflate_state allocation, it takes about 6K,
  728.          * we allocate 8K.  Other allocations are divisible by 512.
  729.          */

  730.         alloc = 8192;
  731.     }

  732.     if (alloc <= ctx->allocated) {
  733.         p = ctx->free_mem;
  734.         ctx->free_mem += alloc;
  735.         ctx->allocated -= alloc;

  736.         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
  737.                        "gzip alloc: n:%ud s:%ud a:%ud p:%p",
  738.                        items, size, alloc, p);

  739.         return p;
  740.     }

  741.     ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0,
  742.                   "gzip filter failed to use preallocated memory: %ud of %ud",
  743.                   items * size, ctx->allocated);

  744.     p = ngx_palloc(ctx->request->pool, items * size);

  745.     return p;
  746. }


  747. static void
  748. ngx_http_gzip_filter_free(void *opaque, void *address)
  749. {
  750. #if 0
  751.     ngx_http_gzip_ctx_t *ctx = opaque;

  752.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
  753.                    "gzip free: %p", address);
  754. #endif
  755. }


  756. static void
  757. ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
  758.     ngx_http_gzip_ctx_t *ctx)
  759. {
  760.     ngx_chain_t  *cl;

  761.     for (cl = ctx->copied; cl; cl = cl->next) {
  762.         ngx_pfree(r->pool, cl->buf->start);
  763.     }

  764.     ctx->copied = NULL;
  765. }


  766. static ngx_int_t
  767. ngx_http_gzip_add_variables(ngx_conf_t *cf)
  768. {
  769.     ngx_http_variable_t  *var;

  770.     var = ngx_http_add_variable(cf, &ngx_http_gzip_ratio, NGX_HTTP_VAR_NOHASH);
  771.     if (var == NULL) {
  772.         return NGX_ERROR;
  773.     }

  774.     var->get_handler = ngx_http_gzip_ratio_variable;

  775.     return NGX_OK;
  776. }


  777. static ngx_int_t
  778. ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
  779.     ngx_http_variable_value_t *v, uintptr_t data)
  780. {
  781.     ngx_uint_t            zint, zfrac;
  782.     ngx_http_gzip_ctx_t  *ctx;

  783.     v->valid = 1;
  784.     v->no_cacheable = 0;
  785.     v->not_found = 0;

  786.     ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);

  787.     if (ctx == NULL || ctx->zout == 0) {
  788.         v->not_found = 1;
  789.         return NGX_OK;
  790.     }

  791.     v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN + 3);
  792.     if (v->data == NULL) {
  793.         return NGX_ERROR;
  794.     }

  795.     zint = (ngx_uint_t) (ctx->zin / ctx->zout);
  796.     zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);

  797.     if ((ctx->zin * 1000 / ctx->zout) % 10 > 4) {

  798.         /* the rounding, e.g., 2.125 to 2.13 */

  799.         zfrac++;

  800.         if (zfrac > 99) {
  801.             zint++;
  802.             zfrac = 0;
  803.         }
  804.     }

  805.     v->len = ngx_sprintf(v->data, "%ui.%02ui", zint, zfrac) - v->data;

  806.     return NGX_OK;
  807. }


  808. static void *
  809. ngx_http_gzip_create_conf(ngx_conf_t *cf)
  810. {
  811.     ngx_http_gzip_conf_t  *conf;

  812.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t));
  813.     if (conf == NULL) {
  814.         return NULL;
  815.     }

  816.     /*
  817.      * set by ngx_pcalloc():
  818.      *
  819.      *     conf->bufs.num = 0;
  820.      *     conf->types = { NULL };
  821.      *     conf->types_keys = NULL;
  822.      */

  823.     conf->enable = NGX_CONF_UNSET;
  824.     conf->no_buffer = NGX_CONF_UNSET;

  825.     conf->postpone_gzipping = NGX_CONF_UNSET_SIZE;
  826.     conf->level = NGX_CONF_UNSET;
  827.     conf->wbits = NGX_CONF_UNSET_SIZE;
  828.     conf->memlevel = NGX_CONF_UNSET_SIZE;
  829.     conf->min_length = NGX_CONF_UNSET;

  830.     return conf;
  831. }


  832. static char *
  833. ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  834. {
  835.     ngx_http_gzip_conf_t *prev = parent;
  836.     ngx_http_gzip_conf_t *conf = child;

  837.     ngx_conf_merge_value(conf->enable, prev->enable, 0);
  838.     ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);

  839.     ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
  840.                               (128 * 1024) / ngx_pagesize, ngx_pagesize);

  841.     ngx_conf_merge_size_value(conf->postpone_gzipping, prev->postpone_gzipping,
  842.                               0);
  843.     ngx_conf_merge_value(conf->level, prev->level, 1);
  844.     ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
  845.     ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,
  846.                               MAX_MEM_LEVEL - 1);
  847.     ngx_conf_merge_value(conf->min_length, prev->min_length, 20);

  848.     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
  849.                              &prev->types_keys, &prev->types,
  850.                              ngx_http_html_default_types)
  851.         != NGX_OK)
  852.     {
  853.         return NGX_CONF_ERROR;
  854.     }

  855.     return NGX_CONF_OK;
  856. }


  857. static ngx_int_t
  858. ngx_http_gzip_filter_init(ngx_conf_t *cf)
  859. {
  860.     ngx_http_next_header_filter = ngx_http_top_header_filter;
  861.     ngx_http_top_header_filter = ngx_http_gzip_header_filter;

  862.     ngx_http_next_body_filter = ngx_http_top_body_filter;
  863.     ngx_http_top_body_filter = ngx_http_gzip_body_filter;

  864.     return NGX_OK;
  865. }


  866. static char *
  867. ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data)
  868. {
  869.     size_t *np = data;

  870.     size_t  wbits, wsize;

  871.     wbits = 15;

  872.     for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {

  873.         if (wsize == *np) {
  874.             *np = wbits;

  875.             return NGX_CONF_OK;
  876.         }

  877.         wbits--;
  878.     }

  879.     return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k";
  880. }


  881. static char *
  882. ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data)
  883. {
  884.     size_t *np = data;

  885.     size_t  memlevel, hsize;

  886.     memlevel = 9;

  887.     for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) {

  888.         if (hsize == *np) {
  889.             *np = memlevel;

  890.             return NGX_CONF_OK;
  891.         }

  892.         memlevel--;
  893.     }

  894.     return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k";
  895. }