~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/http/modules/ngx_http_gzip_filter_module.c

Version: ~ [ nginx-1.4.1 ] ~ [ nginx-1.5.0 ] ~

  1 
  2 /*
  3  * Copyright (C) Igor Sysoev
  4  * Copyright (C) Nginx, Inc.
  5  */
  6 
  7 
  8 #include <ngx_config.h>
  9 #include <ngx_core.h>
 10 #include <ngx_http.h>
 11 
 12 #include <zlib.h>
 13 
 14 
 15 typedef struct {
 16     ngx_flag_t           enable;
 17     ngx_flag_t           no_buffer;
 18 
 19     ngx_hash_t           types;
 20 
 21     ngx_bufs_t           bufs;
 22 
 23     size_t               postpone_gzipping;
 24     ngx_int_t            level;
 25     size_t               wbits;
 26     size_t               memlevel;
 27     ssize_t              min_length;
 28 
 29     ngx_array_t         *types_keys;
 30 } ngx_http_gzip_conf_t;
 31 
 32 
 33 typedef struct {
 34     ngx_chain_t         *in;
 35     ngx_chain_t         *free;
 36     ngx_chain_t         *busy;
 37     ngx_chain_t         *out;
 38     ngx_chain_t        **last_out;
 39 
 40     ngx_chain_t         *copied;
 41     ngx_chain_t         *copy_buf;
 42 
 43     ngx_buf_t           *in_buf;
 44     ngx_buf_t           *out_buf;
 45     ngx_int_t            bufs;
 46 
 47     void                *preallocated;
 48     char                *free_mem;
 49     ngx_uint_t           allocated;
 50 
 51     int                  wbits;
 52     int                  memlevel;
 53 
 54     unsigned             flush:4;
 55     unsigned             redo:1;
 56     unsigned             done:1;
 57     unsigned             nomem:1;
 58     unsigned             gzheader:1;
 59     unsigned             buffering:1;
 60 
 61     size_t               zin;
 62     size_t               zout;
 63 
 64     uint32_t             crc32;
 65     z_stream             zstream;
 66     ngx_http_request_t  *request;
 67 } ngx_http_gzip_ctx_t;
 68 
 69 
 70 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
 71 
 72 struct gztrailer {
 73     uint32_t  crc32;
 74     uint32_t  zlen;
 75 };
 76 
 77 #else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */
 78 
 79 struct gztrailer {
 80     u_char  crc32[4];
 81     u_char  zlen[4];
 82 };
 83 
 84 #endif
 85 
 86 
 87 static void ngx_http_gzip_filter_memory(ngx_http_request_t *r,
 88     ngx_http_gzip_ctx_t *ctx);
 89 static ngx_int_t ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx,
 90     ngx_chain_t *in);
 91 static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
 92     ngx_http_gzip_ctx_t *ctx);
 93 static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r,
 94     ngx_http_gzip_ctx_t *ctx);
 95 static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r,
 96     ngx_http_gzip_ctx_t *ctx);
 97 static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r,
 98     ngx_http_gzip_ctx_t *ctx);
 99 static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r,
100     ngx_http_gzip_ctx_t *ctx);
101 static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
102     ngx_http_gzip_ctx_t *ctx);
103 
104 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
105     u_int size);
106 static void ngx_http_gzip_filter_free(void *opaque, void *address);
107 static void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
108     ngx_http_gzip_ctx_t *ctx);
109 
110 static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf);
111 static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
112     ngx_http_variable_value_t *v, uintptr_t data);
113 
114 static ngx_int_t ngx_http_gzip_filter_init(ngx_conf_t *cf);
115 static void *ngx_http_gzip_create_conf(ngx_conf_t *cf);
116 static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
117     void *parent, void *child);
118 static char *ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data);
119 static char *ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data);
120 
121 
122 static ngx_conf_num_bounds_t  ngx_http_gzip_comp_level_bounds = {
123     ngx_conf_check_num_bounds, 1, 9
124 };
125 
126 static ngx_conf_post_handler_pt  ngx_http_gzip_window_p = ngx_http_gzip_window;
127 static ngx_conf_post_handler_pt  ngx_http_gzip_hash_p = ngx_http_gzip_hash;
128 
129 
130 static ngx_command_t  ngx_http_gzip_filter_commands[] = {
131 
132     { ngx_string("gzip"),
133       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
134                         |NGX_CONF_FLAG,
135       ngx_conf_set_flag_slot,
136       NGX_HTTP_LOC_CONF_OFFSET,
137       offsetof(ngx_http_gzip_conf_t, enable),
138       NULL },
139 
140     { ngx_string("gzip_buffers"),
141       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
142       ngx_conf_set_bufs_slot,
143       NGX_HTTP_LOC_CONF_OFFSET,
144       offsetof(ngx_http_gzip_conf_t, bufs),
145       NULL },
146 
147     { ngx_string("gzip_types"),
148       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
149       ngx_http_types_slot,
150       NGX_HTTP_LOC_CONF_OFFSET,
151       offsetof(ngx_http_gzip_conf_t, types_keys),
152       &ngx_http_html_default_types[0] },
153 
154     { ngx_string("gzip_comp_level"),
155       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
156       ngx_conf_set_num_slot,
157       NGX_HTTP_LOC_CONF_OFFSET,
158       offsetof(ngx_http_gzip_conf_t, level),
159       &ngx_http_gzip_comp_level_bounds },
160 
161     { ngx_string("gzip_window"),
162       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
163       ngx_conf_set_size_slot,
164       NGX_HTTP_LOC_CONF_OFFSET,
165       offsetof(ngx_http_gzip_conf_t, wbits),
166       &ngx_http_gzip_window_p },
167 
168     { ngx_string("gzip_hash"),
169       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
170       ngx_conf_set_size_slot,
171       NGX_HTTP_LOC_CONF_OFFSET,
172       offsetof(ngx_http_gzip_conf_t, memlevel),
173       &ngx_http_gzip_hash_p },
174 
175     { ngx_string("postpone_gzipping"),
176       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
177       ngx_conf_set_size_slot,
178       NGX_HTTP_LOC_CONF_OFFSET,
179       offsetof(ngx_http_gzip_conf_t, postpone_gzipping),
180       NULL },
181 
182     { ngx_string("gzip_no_buffer"),
183       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
184       ngx_conf_set_flag_slot,
185       NGX_HTTP_LOC_CONF_OFFSET,
186       offsetof(ngx_http_gzip_conf_t, no_buffer),
187       NULL },
188 
189     { ngx_string("gzip_min_length"),
190       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
191       ngx_conf_set_size_slot,
192       NGX_HTTP_LOC_CONF_OFFSET,
193       offsetof(ngx_http_gzip_conf_t, min_length),
194       NULL },
195 
196       ngx_null_command
197 };
198 
199 
200 static ngx_http_module_t  ngx_http_gzip_filter_module_ctx = {
201     ngx_http_gzip_add_variables,           /* preconfiguration */
202     ngx_http_gzip_filter_init,             /* postconfiguration */
203 
204     NULL,                                  /* create main configuration */
205     NULL,                                  /* init main configuration */
206 
207     NULL,                                  /* create server configuration */
208     NULL,                                  /* merge server configuration */
209 
210     ngx_http_gzip_create_conf,             /* create location configuration */
211     ngx_http_gzip_merge_conf               /* merge location configuration */
212 };
213 
214 
215 ngx_module_t  ngx_http_gzip_filter_module = {
216     NGX_MODULE_V1,
217     &ngx_http_gzip_filter_module_ctx,      /* module context */
218     ngx_http_gzip_filter_commands,         /* module directives */
219     NGX_HTTP_MODULE,                       /* module type */
220     NULL,                                  /* init master */
221     NULL,                                  /* init module */
222     NULL,                                  /* init process */
223     NULL,                                  /* init thread */
224     NULL,                                  /* exit thread */
225     NULL,                                  /* exit process */
226     NULL,                                  /* exit master */
227     NGX_MODULE_V1_PADDING
228 };
229 
230 
231 static ngx_str_t  ngx_http_gzip_ratio = ngx_string("gzip_ratio");
232 
233 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
234 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
235 
236 
237 static ngx_int_t
238 ngx_http_gzip_header_filter(ngx_http_request_t *r)
239 {
240     ngx_table_elt_t       *h;
241     ngx_http_gzip_ctx_t   *ctx;
242     ngx_http_gzip_conf_t  *conf;
243 
244     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
245 
246     if (!conf->enable
247         || (r->headers_out.status != NGX_HTTP_OK
248             && r->headers_out.status != NGX_HTTP_FORBIDDEN
249             && r->headers_out.status != NGX_HTTP_NOT_FOUND)
250         || (r->headers_out.content_encoding
251             && r->headers_out.content_encoding->value.len)
252         || (r->headers_out.content_length_n != -1
253             && r->headers_out.content_length_n < conf->min_length)
254         || ngx_http_test_content_type(r, &conf->types) == NULL
255         || r->header_only)
256     {
257         return ngx_http_next_header_filter(r);
258     }
259 
260     r->gzip_vary = 1;
261 
262 #if (NGX_HTTP_DEGRADATION)
263     {
264     ngx_http_core_loc_conf_t  *clcf;
265 
266     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
267 
268     if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) {
269         return ngx_http_next_header_filter(r);
270     }
271     }
272 #endif
273 
274     if (!r->gzip_tested) {
275         if (ngx_http_gzip_ok(r) != NGX_OK) {
276             return ngx_http_next_header_filter(r);
277         }
278 
279     } else if (!r->gzip_ok) {
280         return ngx_http_next_header_filter(r);
281     }
282 
283     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t));
284     if (ctx == NULL) {
285         return NGX_ERROR;
286     }
287 
288     ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
289 
290     ctx->request = r;
291     ctx->buffering = (conf->postpone_gzipping != 0);
292 
293     ngx_http_gzip_filter_memory(r, ctx);
294 
295     h = ngx_list_push(&r->headers_out.headers);
296     if (h == NULL) {
297         return NGX_ERROR;
298     }
299 
300     h->hash = 1;
301     ngx_str_set(&h->key, "Content-Encoding");
302     ngx_str_set(&h->value, "gzip");
303     r->headers_out.content_encoding = h;
304 
305     r->main_filter_need_in_memory = 1;
306 
307     ngx_http_clear_content_length(r);
308     ngx_http_clear_accept_ranges(r);
309     ngx_http_clear_etag(r);
310 
311     return ngx_http_next_header_filter(r);
312 }
313 
314 
315 static ngx_int_t
316 ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
317 {
318     int                   rc;
319     ngx_chain_t          *cl;
320     ngx_http_gzip_ctx_t  *ctx;
321 
322     ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
323 
324     if (ctx == NULL || ctx->done || r->header_only) {
325         return ngx_http_next_body_filter(r, in);
326     }
327 
328     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
329                    "http gzip filter");
330 
331     if (ctx->buffering) {
332 
333         /*
334          * With default memory settings zlib starts to output gzipped data
335          * only after it has got about 90K, so it makes sense to allocate
336          * zlib memory (200-400K) only after we have enough data to compress.
337          * Although we copy buffers, nevertheless for not big responses
338          * this allows to allocate zlib memory, to compress and to output
339          * the response in one step using hot CPU cache.
340          */
341 
342         if (in) {
343             switch (ngx_http_gzip_filter_buffer(ctx, in)) {
344 
345             case NGX_OK:
346                 return NGX_OK;
347 
348             case NGX_DONE:
349                 in = NULL;
350                 break;
351 
352             default:  /* NGX_ERROR */
353                 goto failed;
354             }
355 
356         } else {
357             ctx->buffering = 0;
358         }
359     }
360 
361     if (ctx->preallocated == NULL) {
362         if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) {
363             goto failed;
364         }
365     }
366 
367     if (in) {
368         if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
369             goto failed;
370         }
371     }
372 
373     if (ctx->nomem) {
374 
375         /* flush busy buffers */
376 
377         if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
378             goto failed;
379         }
380 
381         cl = NULL;
382 
383         ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
384                                 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
385         ctx->nomem = 0;
386     }
387 
388     for ( ;; ) {
389 
390         /* cycle while we can write to a client */
391 
392         for ( ;; ) {
393 
394             /* cycle while there is data to feed zlib and ... */
395 
396             rc = ngx_http_gzip_filter_add_data(r, ctx);
397 
398             if (rc == NGX_DECLINED) {
399                 break;
400             }
401 
402             if (rc == NGX_AGAIN) {
403                 continue;
404             }
405 
406 
407             /* ... there are buffers to write zlib output */
408 
409             rc = ngx_http_gzip_filter_get_buf(r, ctx);
410 
411             if (rc == NGX_DECLINED) {
412                 break;
413             }
414 
415             if (rc == NGX_ERROR) {
416                 goto failed;
417             }
418 
419 
420             rc = ngx_http_gzip_filter_deflate(r, ctx);
421 
422             if (rc == NGX_OK) {
423                 break;
424             }
425 
426             if (rc == NGX_ERROR) {
427                 goto failed;
428             }
429 
430             /* rc == NGX_AGAIN */
431         }
432 
433         if (ctx->out == NULL) {
434             ngx_http_gzip_filter_free_copy_buf(r, ctx);
435 
436             return ctx->busy ? NGX_AGAIN : NGX_OK;
437         }
438 
439         if (!ctx->gzheader) {
440             if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) {
441                 goto failed;
442             }
443         }
444 
445         rc = ngx_http_next_body_filter(r, ctx->out);
446 
447         if (rc == NGX_ERROR) {
448             goto failed;
449         }
450 
451         ngx_http_gzip_filter_free_copy_buf(r, ctx);
452 
453         ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
454                                 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
455         ctx->last_out = &ctx->out;
456 
457         ctx->nomem = 0;
458 
459         if (ctx->done) {
460             return rc;
461         }
462     }
463 
464     /* unreachable */
465 
466 failed:
467 
468     ctx->done = 1;
469 
470     if (ctx->preallocated) {
471         deflateEnd(&ctx->zstream);
472 
473         ngx_pfree(r->pool, ctx->preallocated);
474     }
475 
476     ngx_http_gzip_filter_free_copy_buf(r, ctx);
477 
478     return NGX_ERROR;
479 }
480 
481 
482 static void
483 ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
484 {
485     int                    wbits, memlevel;
486     ngx_http_gzip_conf_t  *conf;
487 
488     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
489 
490     wbits = conf->wbits;
491     memlevel = conf->memlevel;
492 
493     if (r->headers_out.content_length_n > 0) {
494 
495         /* the actual zlib window size is smaller by 262 bytes */
496 
497         while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) {
498             wbits--;
499             memlevel--;
500         }
501 
502         if (memlevel < 1) {
503             memlevel = 1;
504         }
505     }
506 
507     ctx->wbits = wbits;
508     ctx->memlevel = memlevel;
509 
510     /*
511      * We preallocate a memory for zlib in one buffer (200K-400K), this
512      * decreases a number of malloc() and free() calls and also probably
513      * decreases a number of syscalls (sbrk()/mmap() and so on).
514      * Besides we free the memory as soon as a gzipping will complete
515      * and do not wait while a whole response will be sent to a client.
516      *
517      * 8K is for zlib deflate_state, it takes
518      *  *) 5816 bytes on i386 and sparc64 (32-bit mode)
519      *  *) 5920 bytes on amd64 and sparc64
520      */
521 
522     ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
523 }
524 
525 
526 static ngx_int_t
527 ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in)
528 {
529     size_t                 size, buffered;
530     ngx_buf_t             *b, *buf;
531     ngx_chain_t           *cl, **ll;
532     ngx_http_request_t    *r;
533     ngx_http_gzip_conf_t  *conf;
534 
535     r = ctx->request;
536 
537     r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
538 
539     buffered = 0;
540     ll = &ctx->in;
541 
542     for (cl = ctx->in; cl; cl = cl->next) {
543         buffered += cl->buf->last - cl->buf->pos;
544         ll = &cl->next;
545     }
546 
547     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
548 
549     while (in) {
550         cl = ngx_alloc_chain_link(r->pool);
551         if (cl == NULL) {
552             return NGX_ERROR;
553         }
554 
555         b = in->buf;
556 
557         size = b->last - b->pos;
558         buffered += size;
559 
560         if (b->flush || b->last_buf || buffered > conf->postpone_gzipping) {
561             ctx->buffering = 0;
562         }
563 
564         if (ctx->buffering && size) {
565 
566             buf = ngx_create_temp_buf(r->pool, size);
567             if (buf == NULL) {
568                 return NGX_ERROR;
569             }
570 
571             buf->last = ngx_cpymem(buf->pos, b->pos, size);
572             b->pos = b->last;
573 
574             buf->last_buf = b->last_buf;
575             buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
576 
577             cl->buf = buf;
578 
579         } else {
580             cl->buf = b;
581         }
582 
583         *ll = cl;
584         ll = &cl->next;
585         in = in->next;
586     }
587 
588     *ll = NULL;
589 
590     return ctx->buffering ? NGX_OK : NGX_DONE;
591 }
592 
593 
594 static ngx_int_t
595 ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
596     ngx_http_gzip_ctx_t *ctx)
597 {
598     int                    rc;
599     ngx_http_gzip_conf_t  *conf;
600 
601     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
602 
603     ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
604     if (ctx->preallocated == NULL) {
605         return NGX_ERROR;
606     }
607 
608     ctx->free_mem = ctx->preallocated;
609 
610     ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
611     ctx->zstream.zfree = ngx_http_gzip_filter_free;
612     ctx->zstream.opaque = ctx;
613 
614     rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
615                       - ctx->wbits, ctx->memlevel, Z_DEFAULT_STRATEGY);
616 
617     if (rc != Z_OK) {
618         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
619                       "deflateInit2() failed: %d", rc);
620         return NGX_ERROR;
621     }
622 
623     r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
624 
625     ctx->last_out = &ctx->out;
626     ctx->crc32 = crc32(0L, Z_NULL, 0);
627     ctx->flush = Z_NO_FLUSH;
628 
629     return NGX_OK;
630 }
631 
632 
633 static ngx_int_t
634 ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
635 {
636     ngx_buf_t      *b;
637     ngx_chain_t    *cl;
638     static u_char  gzheader[10] =
639                                { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
640 
641     b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
642     if (b == NULL) {
643         return NGX_ERROR;
644     }
645 
646     b->memory = 1;
647     b->pos = gzheader;
648     b->last = b->pos + 10;
649 
650     cl = ngx_alloc_chain_link(r->pool);
651     if (cl == NULL) {
652         return NGX_ERROR;
653     }
654 
655     cl->buf = b;
656     cl->next = ctx->out;
657     ctx->out = cl;
658 
659     ctx->gzheader = 1;
660 
661     return NGX_OK;
662 }
663 
664 
665 static ngx_int_t
666 ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
667 {
668     if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
669         return NGX_OK;
670     }
671 
672     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
673                    "gzip in: %p", ctx->in);
674 
675     if (ctx->in == NULL) {
676         return NGX_DECLINED;
677     }
678 
679     if (ctx->copy_buf) {
680 
681         /*
682          * to avoid CPU cache trashing we do not free() just quit buf,
683          * but postpone free()ing after zlib compressing and data output
684          */
685 
686         ctx->copy_buf->next = ctx->copied;
687         ctx->copied = ctx->copy_buf;
688         ctx->copy_buf = NULL;
689     }
690 
691     ctx->in_buf = ctx->in->buf;
692 
693     if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) {
694         ctx->copy_buf = ctx->in;
695     }
696 
697     ctx->in = ctx->in->next;
698 
699     ctx->zstream.next_in = ctx->in_buf->pos;
700     ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
701 
702     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
703                    "gzip in_buf:%p ni:%p ai:%ud",
704                    ctx->in_buf,
705                    ctx->zstream.next_in, ctx->zstream.avail_in);
706 
707     if (ctx->in_buf->last_buf) {
708         ctx->flush = Z_FINISH;
709 
710     } else if (ctx->in_buf->flush) {
711         ctx->flush = Z_SYNC_FLUSH;
712     }
713 
714     if (ctx->zstream.avail_in) {
715 
716         ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
717                            ctx->zstream.avail_in);
718 
719     } else if (ctx->flush == Z_NO_FLUSH) {
720         return NGX_AGAIN;
721     }
722 
723     return NGX_OK;
724 }
725 
726 
727 static ngx_int_t
728 ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
729 {
730     ngx_http_gzip_conf_t  *conf;
731 
732     if (ctx->zstream.avail_out) {
733         return NGX_OK;
734     }
735 
736     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
737 
738     if (ctx->free) {
739         ctx->out_buf = ctx->free->buf;
740         ctx->free = ctx->free->next;
741 
742     } else if (ctx->bufs < conf->bufs.num) {
743 
744         ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
745         if (ctx->out_buf == NULL) {
746             return NGX_ERROR;
747         }
748 
749         ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
750         ctx->out_buf->recycled = 1;
751         ctx->bufs++;
752 
753     } else {
754         ctx->nomem = 1;
755         return NGX_DECLINED;
756     }
757 
758     ctx->zstream.next_out = ctx->out_buf->pos;
759     ctx->zstream.avail_out = conf->bufs.size;
760 
761     return NGX_OK;
762 }
763 
764 
765 static ngx_int_t
766 ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
767 {
768     int                    rc;
769     ngx_buf_t             *b;
770     ngx_chain_t           *cl;
771     ngx_http_gzip_conf_t  *conf;
772 
773     ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
774                  "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
775                  ctx->zstream.next_in, ctx->zstream.next_out,
776                  ctx->zstream.avail_in, ctx->zstream.avail_out,
777                  ctx->flush, ctx->redo);
778 
779     rc = deflate(&ctx->zstream, ctx->flush);
780 
781     if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {
782         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
783                       "deflate() failed: %d, %d", ctx->flush, rc);
784         return NGX_ERROR;
785     }
786 
787     ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
788                    "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
789                    ctx->zstream.next_in, ctx->zstream.next_out,
790                    ctx->zstream.avail_in, ctx->zstream.avail_out,
791                    rc);
792 
793     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
794                    "gzip in_buf:%p pos:%p",
795                    ctx->in_buf, ctx->in_buf->pos);
796 
797     if (ctx->zstream.next_in) {
798         ctx->in_buf->pos = ctx->zstream.next_in;
799 
800         if (ctx->zstream.avail_in == 0) {
801             ctx->zstream.next_in = NULL;
802         }
803     }
804 
805     ctx->out_buf->last = ctx->zstream.next_out;
806 
807     if (ctx->zstream.avail_out == 0) {
808 
809         /* zlib wants to output some more gzipped data */
810 
811         cl = ngx_alloc_chain_link(r->pool);
812         if (cl == NULL) {
813             return NGX_ERROR;
814         }
815 
816         cl->buf = ctx->out_buf;
817         cl->next = NULL;
818         *ctx->last_out = cl;
819         ctx->last_out = &cl->next;
820 
821         ctx->redo = 1;
822 
823         return NGX_AGAIN;
824     }
825 
826     ctx->redo = 0;
827 
828     if (ctx->flush == Z_SYNC_FLUSH) {
829 
830         ctx->flush = Z_NO_FLUSH;
831 
832         cl = ngx_alloc_chain_link(r->pool);
833         if (cl == NULL) {
834             return NGX_ERROR;
835         }
836 
837         b = ctx->out_buf;
838 
839         if (ngx_buf_size(b) == 0) {
840 
841             b = ngx_calloc_buf(ctx->request->pool);
842             if (b == NULL) {
843                 return NGX_ERROR;
844             }
845 
846         } else {
847             ctx->zstream.avail_out = 0;
848         }
849 
850         b->flush = 1;
851 
852         cl->buf = b;
853         cl->next = NULL;
854         *ctx->last_out = cl;
855         ctx->last_out = &cl->next;
856 
857         return NGX_OK;
858     }
859 
860     if (rc == Z_STREAM_END) {
861 
862         if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) {
863             return NGX_ERROR;
864         }
865 
866         return NGX_OK;
867     }
868 
869     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
870 
871     if (conf->no_buffer && ctx->in == NULL) {
872 
873         cl = ngx_alloc_chain_link(r->pool);
874         if (cl == NULL) {
875             return NGX_ERROR;
876         }
877 
878         cl->buf = ctx->out_buf;
879         cl->next = NULL;
880         *ctx->last_out = cl;
881         ctx->last_out = &cl->next;
882 
883         return NGX_OK;
884     }
885 
886     return NGX_AGAIN;
887 }
888 
889 
890 static ngx_int_t
891 ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
892     ngx_http_gzip_ctx_t *ctx)
893 {
894     int                rc;
895     ngx_buf_t         *b;
896     ngx_chain_t       *cl;
897     struct gztrailer  *trailer;
898 
899     ctx->zin = ctx->zstream.total_in;
900     ctx->zout = 10 + ctx->zstream.total_out + 8;
901 
902     rc = deflateEnd(&ctx->zstream);
903 
904     if (rc != Z_OK) {
905         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
906                       "deflateEnd() failed: %d", rc);
907         return NGX_ERROR;
908     }
909 
910     ngx_pfree(r->pool, ctx->preallocated);
911 
912     cl = ngx_alloc_chain_link(r->pool);
913     if (cl == NULL) {
914         return NGX_ERROR;
915     }
916 
917     cl->buf = ctx->out_buf;
918     cl->next = NULL;
919     *ctx->last_out = cl;
920     ctx->last_out = &cl->next;
921 
922     if (ctx->zstream.avail_out >= 8) {
923         trailer = (struct gztrailer *) ctx->out_buf->last;
924         ctx->out_buf->last += 8;
925         ctx->out_buf->last_buf = 1;
926 
927     } else {
928         b = ngx_create_temp_buf(r->pool, 8);
929         if (b == NULL) {
930             return NGX_ERROR;
931         }
932 
933         b->last_buf = 1;
934 
935         cl = ngx_alloc_chain_link(r->pool);
936         if (cl == NULL) {
937             return NGX_ERROR;
938         }
939 
940         cl->buf = b;
941         cl->next = NULL;
942         *ctx->last_out = cl;
943         ctx->last_out = &cl->next;
944         trailer = (struct gztrailer *) b->pos;
945         b->last += 8;
946     }
947 
948 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
949 
950     trailer->crc32 = ctx->crc32;
951     trailer->zlen = ctx->zin;
952 
953 #else
954 
955     trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
956     trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);
957     trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
958     trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);
959 
960     trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
961     trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);
962     trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
963     trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);
964 
965 #endif
966 
967     ctx->zstream.avail_in = 0;
968     ctx->zstream.avail_out = 0;
969 
970     ctx->done = 1;
971 
972     r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
973 
974     return NGX_OK;
975 }
976 
977 
978 static void *
979 ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size)
980 {
981     ngx_http_gzip_ctx_t *ctx = opaque;
982 
983     void        *p;
984     ngx_uint_t   alloc;
985 
986     alloc = items * size;
987 
988     if (alloc % 512 != 0 && alloc < 8192) {
989 
990         /*
991          * The zlib deflate_state allocation, it takes about 6K,
992          * we allocate 8K.  Other allocations are divisible by 512.
993          */
994 
995         alloc = 8192;
996     }
997 
998     if (alloc <= ctx->allocated) {
999         p = ctx->free_mem;
1000         ctx->free_mem += alloc;
1001         ctx->allocated -= alloc;
1002 
1003         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
1004                        "gzip alloc: n:%ud s:%ud a:%ud p:%p",
1005                        items, size, alloc, p);
1006 
1007         return p;
1008     }
1009 
1010     ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0,
1011                   "gzip filter failed to use preallocated memory: %ud of %ud",
1012                   items * size, ctx->allocated);
1013 
1014     p = ngx_palloc(ctx->request->pool, items * size);
1015 
1016     return p;
1017 }
1018 
1019 
1020 static void
1021 ngx_http_gzip_filter_free(void *opaque, void *address)
1022 {
1023 #if 0
1024     ngx_http_gzip_ctx_t *ctx = opaque;
1025 
1026     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
1027                    "gzip free: %p", address);
1028 #endif
1029 }
1030 
1031 
1032 static void
1033 ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
1034     ngx_http_gzip_ctx_t *ctx)
1035 {
1036     ngx_chain_t  *cl;
1037 
1038     for (cl = ctx->copied; cl; cl = cl->next) {
1039         ngx_pfree(r->pool, cl->buf->start);
1040     }
1041 
1042     ctx->copied = NULL;
1043 }
1044 
1045 
1046 static ngx_int_t
1047 ngx_http_gzip_add_variables(ngx_conf_t *cf)
1048 {
1049     ngx_http_variable_t  *var;
1050 
1051     var = ngx_http_add_variable(cf, &ngx_http_gzip_ratio, NGX_HTTP_VAR_NOHASH);
1052     if (var == NULL) {
1053         return NGX_ERROR;
1054     }
1055 
1056     var->get_handler = ngx_http_gzip_ratio_variable;
1057 
1058     return NGX_OK;
1059 }
1060 
1061 
1062 static ngx_int_t
1063 ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
1064     ngx_http_variable_value_t *v, uintptr_t data)
1065 {
1066     ngx_uint_t            zint, zfrac;
1067     ngx_http_gzip_ctx_t  *ctx;
1068 
1069     v->valid = 1;
1070     v->no_cacheable = 0;
1071     v->not_found = 0;
1072 
1073     ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
1074 
1075     if (ctx == NULL || ctx->zout == 0) {
1076         v->not_found = 1;
1077         return NGX_OK;
1078     }
1079 
1080     v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN + 3);
1081     if (v->data == NULL) {
1082         return NGX_ERROR;
1083     }
1084 
1085     zint = (ngx_uint_t) (ctx->zin / ctx->zout);
1086     zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);
1087 
1088     if ((ctx->zin * 1000 / ctx->zout) % 10 > 4) {
1089 
1090         /* the rounding, e.g., 2.125 to 2.13 */
1091 
1092         zfrac++;
1093 
1094         if (zfrac > 99) {
1095             zint++;
1096             zfrac = 0;
1097         }
1098     }
1099 
1100     v->len = ngx_sprintf(v->data, "%ui.%02ui", zint, zfrac) - v->data;
1101 
1102     return NGX_OK;
1103 }
1104 
1105 
1106 static void *
1107 ngx_http_gzip_create_conf(ngx_conf_t *cf)
1108 {
1109     ngx_http_gzip_conf_t  *conf;
1110 
1111     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t));
1112     if (conf == NULL) {
1113         return NULL;
1114     }
1115 
1116     /*
1117      * set by ngx_pcalloc():
1118      *
1119      *     conf->bufs.num = 0;
1120      *     conf->types = { NULL };
1121      *     conf->types_keys = NULL;
1122      */
1123 
1124     conf->enable = NGX_CONF_UNSET;
1125     conf->no_buffer = NGX_CONF_UNSET;
1126 
1127     conf->postpone_gzipping = NGX_CONF_UNSET_SIZE;
1128     conf->level = NGX_CONF_UNSET;
1129     conf->wbits = NGX_CONF_UNSET_SIZE;
1130     conf->memlevel = NGX_CONF_UNSET_SIZE;
1131     conf->min_length = NGX_CONF_UNSET;
1132 
1133     return conf;
1134 }
1135 
1136 
1137 static char *
1138 ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1139 {
1140     ngx_http_gzip_conf_t *prev = parent;
1141     ngx_http_gzip_conf_t *conf = child;
1142 
1143     ngx_conf_merge_value(conf->enable, prev->enable, 0);
1144     ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);
1145 
1146     ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
1147                               (128 * 1024) / ngx_pagesize, ngx_pagesize);
1148 
1149     ngx_conf_merge_size_value(conf->postpone_gzipping, prev->postpone_gzipping,
1150                               0);
1151     ngx_conf_merge_value(conf->level, prev->level, 1);
1152     ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
1153     ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,
1154                               MAX_MEM_LEVEL - 1);
1155     ngx_conf_merge_value(conf->min_length, prev->min_length, 20);
1156 
1157     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
1158                              &prev->types_keys, &prev->types,
1159                              ngx_http_html_default_types)
1160         != NGX_OK)
1161     {
1162         return NGX_CONF_ERROR;
1163     }
1164 
1165     return NGX_CONF_OK;
1166 }
1167 
1168 
1169 static ngx_int_t
1170 ngx_http_gzip_filter_init(ngx_conf_t *cf)
1171 {
1172     ngx_http_next_header_filter = ngx_http_top_header_filter;
1173     ngx_http_top_header_filter = ngx_http_gzip_header_filter;
1174 
1175     ngx_http_next_body_filter = ngx_http_top_body_filter;
1176     ngx_http_top_body_filter = ngx_http_gzip_body_filter;
1177 
1178     return NGX_OK;
1179 }
1180 
1181 
1182 static char *
1183 ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data)
1184 {
1185     size_t *np = data;
1186 
1187     size_t  wbits, wsize;
1188 
1189     wbits = 15;
1190 
1191     for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {
1192 
1193         if (wsize == *np) {
1194             *np = wbits;
1195 
1196             return NGX_CONF_OK;
1197         }
1198 
1199         wbits--;
1200     }
1201 
1202     return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k";
1203 }
1204 
1205 
1206 static char *
1207 ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data)
1208 {
1209     size_t *np = data;
1210 
1211     size_t  memlevel, hsize;
1212 
1213     memlevel = 9;
1214 
1215     for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) {
1216 
1217         if (hsize == *np) {
1218             *np = memlevel;
1219 
1220             return NGX_CONF_OK;
1221         }
1222 
1223         memlevel--;
1224     }
1225 
1226     return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k";
1227 }
1228 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.