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