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