nginx 0.1.19
[nginx-catap.git] / src / http / modules / ngx_http_gzip_filter.c
blobc7a16a7c8949837fdcffd52343f71969b80765b8
2 /*
3 * Copyright (C) Igor Sysoev
4 */
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
11 #include <zlib.h>
14 typedef struct {
15 ngx_flag_t enable;
16 ngx_flag_t no_buffer;
18 ngx_array_t *types; /* array of ngx_http_gzip_type_t */
20 ngx_bufs_t bufs;
22 ngx_uint_t http_version;
23 ngx_uint_t proxied;
25 int level;
26 size_t wbits;
27 size_t memlevel;
28 ssize_t min_length;
29 } ngx_http_gzip_conf_t;
32 typedef struct {
33 ngx_str_t name;
34 ngx_uint_t enable;
35 } ngx_http_gzip_type_t;
38 #define NGX_HTTP_GZIP_PROXIED_OFF 0x0002
39 #define NGX_HTTP_GZIP_PROXIED_EXPIRED 0x0004
40 #define NGX_HTTP_GZIP_PROXIED_NO_CACHE 0x0008
41 #define NGX_HTTP_GZIP_PROXIED_NO_STORE 0x0010
42 #define NGX_HTTP_GZIP_PROXIED_PRIVATE 0x0020
43 #define NGX_HTTP_GZIP_PROXIED_NO_LM 0x0040
44 #define NGX_HTTP_GZIP_PROXIED_NO_ETAG 0x0080
45 #define NGX_HTTP_GZIP_PROXIED_AUTH 0x0100
46 #define NGX_HTTP_GZIP_PROXIED_ANY 0x0200
49 typedef struct {
50 ngx_chain_t *in;
51 ngx_chain_t *free;
52 ngx_chain_t *busy;
53 ngx_chain_t *out;
54 ngx_chain_t **last_out;
55 ngx_buf_t *in_buf;
56 ngx_buf_t *out_buf;
57 ngx_int_t bufs;
59 off_t length;
61 void *preallocated;
62 char *free_mem;
63 ngx_uint_t allocated;
65 unsigned flush:4;
66 unsigned redo:1;
67 unsigned done:1;
69 size_t zin;
70 size_t zout;
72 uint32_t crc32;
73 z_stream zstream;
74 ngx_http_request_t *request;
75 } ngx_http_gzip_ctx_t;
78 static ngx_int_t ngx_http_gzip_proxied(ngx_http_request_t *r,
79 ngx_http_gzip_conf_t *conf);
80 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
81 u_int size);
82 static void ngx_http_gzip_filter_free(void *opaque, void *address);
83 static void ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx);
85 static u_char *ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf,
86 ngx_http_log_op_t *op);
88 static ngx_int_t ngx_http_gzip_add_log_formats(ngx_conf_t *cf);
90 static ngx_int_t ngx_http_gzip_filter_init(ngx_cycle_t *cycle);
91 static void *ngx_http_gzip_create_conf(ngx_conf_t *cf);
92 static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
93 void *parent, void *child);
94 static char *ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd,
95 void *conf);
96 static char *ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data);
97 static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data);
100 static ngx_conf_num_bounds_t ngx_http_gzip_comp_level_bounds = {
101 ngx_conf_check_num_bounds, 1, 9
104 static ngx_conf_post_handler_pt ngx_http_gzip_set_window_p =
105 ngx_http_gzip_set_window;
106 static ngx_conf_post_handler_pt ngx_http_gzip_set_hash_p =
107 ngx_http_gzip_set_hash;
111 static ngx_conf_enum_t ngx_http_gzip_http_version[] = {
112 { ngx_string("1.0"), NGX_HTTP_VERSION_10 },
113 { ngx_string("1.1"), NGX_HTTP_VERSION_11 },
114 { ngx_null_string, 0 }
118 static ngx_conf_bitmask_t ngx_http_gzip_proxied_mask[] = {
119 { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF },
120 { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED },
121 { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE },
122 { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE },
123 { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE },
124 { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM },
125 { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG },
126 { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH },
127 { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY },
128 { ngx_null_string, 0 }
132 static ngx_command_t ngx_http_gzip_filter_commands[] = {
134 { ngx_string("gzip"),
135 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
136 |NGX_CONF_FLAG,
137 ngx_conf_set_flag_slot,
138 NGX_HTTP_LOC_CONF_OFFSET,
139 offsetof(ngx_http_gzip_conf_t, enable),
140 NULL },
142 { ngx_string("gzip_buffers"),
143 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
144 ngx_conf_set_bufs_slot,
145 NGX_HTTP_LOC_CONF_OFFSET,
146 offsetof(ngx_http_gzip_conf_t, bufs),
147 NULL },
149 { ngx_string("gzip_types"),
150 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
151 ngx_http_gzip_set_types,
152 NGX_HTTP_LOC_CONF_OFFSET,
154 NULL },
156 { ngx_string("gzip_comp_level"),
157 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
158 ngx_conf_set_num_slot,
159 NGX_HTTP_LOC_CONF_OFFSET,
160 offsetof(ngx_http_gzip_conf_t, level),
161 &ngx_http_gzip_comp_level_bounds },
163 { ngx_string("gzip_window"),
164 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
165 ngx_conf_set_size_slot,
166 NGX_HTTP_LOC_CONF_OFFSET,
167 offsetof(ngx_http_gzip_conf_t, wbits),
168 &ngx_http_gzip_set_window_p },
170 { ngx_string("gzip_hash"),
171 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
172 ngx_conf_set_size_slot,
173 NGX_HTTP_LOC_CONF_OFFSET,
174 offsetof(ngx_http_gzip_conf_t, memlevel),
175 &ngx_http_gzip_set_hash_p },
177 { ngx_string("gzip_no_buffer"),
178 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
179 ngx_conf_set_flag_slot,
180 NGX_HTTP_LOC_CONF_OFFSET,
181 offsetof(ngx_http_gzip_conf_t, no_buffer),
182 NULL },
184 { ngx_string("gzip_http_version"),
185 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY,
186 ngx_conf_set_enum_slot,
187 NGX_HTTP_LOC_CONF_OFFSET,
188 offsetof(ngx_http_gzip_conf_t, http_version),
189 &ngx_http_gzip_http_version },
191 { ngx_string("gzip_proxied"),
192 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY,
193 ngx_conf_set_bitmask_slot,
194 NGX_HTTP_LOC_CONF_OFFSET,
195 offsetof(ngx_http_gzip_conf_t, proxied),
196 &ngx_http_gzip_proxied_mask },
198 { ngx_string("gzip_min_length"),
199 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
200 ngx_conf_set_size_slot,
201 NGX_HTTP_LOC_CONF_OFFSET,
202 offsetof(ngx_http_gzip_conf_t, min_length),
203 NULL },
205 ngx_null_command
209 static ngx_http_module_t ngx_http_gzip_filter_module_ctx = {
210 ngx_http_gzip_add_log_formats, /* pre conf */
212 NULL, /* create main configuration */
213 NULL, /* init main configuration */
215 NULL, /* create server configuration */
216 NULL, /* merge server configuration */
218 ngx_http_gzip_create_conf, /* create location configuration */
219 ngx_http_gzip_merge_conf /* merge location configuration */
223 ngx_module_t ngx_http_gzip_filter_module = {
224 NGX_MODULE,
225 &ngx_http_gzip_filter_module_ctx, /* module context */
226 ngx_http_gzip_filter_commands, /* module directives */
227 NGX_HTTP_MODULE, /* module type */
228 ngx_http_gzip_filter_init, /* init module */
229 NULL /* init process */
233 static ngx_http_log_op_name_t ngx_http_gzip_log_fmt_ops[] = {
234 { ngx_string("gzip_ratio"), NGX_INT32_LEN + 3,
235 NULL, NULL, ngx_http_gzip_log_ratio },
236 { ngx_null_string, 0, NULL, NULL, NULL }
241 static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
243 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
245 struct gztrailer {
246 uint32_t crc32;
247 uint32_t zlen;
250 #else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */
252 struct gztrailer {
253 u_char crc32[4];
254 u_char zlen[4];
257 #endif
260 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
261 static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
264 static ngx_int_t ngx_http_gzip_header_filter(ngx_http_request_t *r)
266 ngx_uint_t i, found;
267 ngx_http_gzip_ctx_t *ctx;
268 ngx_http_gzip_conf_t *conf;
269 ngx_http_gzip_type_t *type;
271 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
273 if (!conf->enable
274 || (r->headers_out.status != NGX_HTTP_OK
275 && r->headers_out.status != NGX_HTTP_FORBIDDEN
276 && r->headers_out.status != NGX_HTTP_NOT_FOUND)
277 || r->header_only
278 || r->http_version < conf->http_version
279 || r->headers_out.content_type == NULL
280 || (r->headers_out.content_encoding
281 && r->headers_out.content_encoding->value.len)
282 || r->headers_in.accept_encoding == NULL
283 || (r->headers_out.content_length_n != -1
284 && r->headers_out.content_length_n < conf->min_length)
285 || ngx_strstr(r->headers_in.accept_encoding->value.data, "gzip") == NULL
288 return ngx_http_next_header_filter(r);
292 found = 0;
293 type = conf->types->elts;
295 for (i = 0; i < conf->types->nelts; i++) {
296 if (r->headers_out.content_type->value.len >= type[i].name.len
297 && ngx_strncasecmp(r->headers_out.content_type->value.data,
298 type[i].name.data, type[i].name.len) == 0)
300 found = 1;
301 break;
305 if (!found) {
306 return ngx_http_next_header_filter(r);
310 if (r->headers_in.via) {
311 if (conf->proxied & NGX_HTTP_GZIP_PROXIED_OFF) {
312 return ngx_http_next_header_filter(r);
315 if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_ANY)
316 && ngx_http_gzip_proxied(r, conf) == NGX_DECLINED)
318 return ngx_http_next_header_filter(r);
324 * if the URL (without the "http://" prefix) is longer than 253 bytes
325 * then MSIE 4.x can not handle the compressed stream - it waits too long,
326 * hangs up or crashes
329 if (r->headers_in.msie4 && r->unparsed_uri.len > 200) {
330 return ngx_http_next_header_filter(r);
334 ngx_http_create_ctx(r, ctx, ngx_http_gzip_filter_module,
335 sizeof(ngx_http_gzip_ctx_t), NGX_ERROR);
336 ctx->request = r;
338 r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers);
339 if (r->headers_out.content_encoding == NULL) {
340 return NGX_ERROR;
343 r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1;
344 r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding";
345 r->headers_out.content_encoding->value.len = sizeof("gzip") - 1;
346 r->headers_out.content_encoding->value.data = (u_char *) "gzip";
348 ctx->length = r->headers_out.content_length_n;
349 r->headers_out.content_length_n = -1;
350 if (r->headers_out.content_length) {
351 r->headers_out.content_length->key.len = 0;
352 r->headers_out.content_length = NULL;
354 r->filter_need_in_memory = 1;
356 return ngx_http_next_header_filter(r);
360 static ngx_int_t ngx_http_gzip_proxied(ngx_http_request_t *r,
361 ngx_http_gzip_conf_t *conf)
363 time_t date, expires;
365 if (r->headers_in.authorization
366 && (conf->proxied & NGX_HTTP_GZIP_PROXIED_AUTH))
368 return NGX_OK;
371 if (r->headers_out.expires) {
373 if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_EXPIRED)) {
374 return NGX_DECLINED;
377 expires = ngx_http_parse_time(r->headers_out.expires->value.data,
378 r->headers_out.expires->value.len);
379 if (expires == NGX_ERROR) {
380 return NGX_DECLINED;
383 if (r->headers_out.date) {
384 date = ngx_http_parse_time(r->headers_out.date->value.data,
385 r->headers_out.date->value.len);
386 if (date == NGX_ERROR) {
387 return NGX_DECLINED;
390 } else {
391 date = ngx_time();
394 if (expires < date) {
395 return NGX_OK;
398 return NGX_DECLINED;
401 if (r->headers_out.cache_control) {
403 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_CACHE)
404 && ngx_strstr(r->headers_out.cache_control->value.data, "no-cache"))
406 return NGX_OK;
409 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_STORE)
410 && ngx_strstr(r->headers_out.cache_control->value.data, "no-store"))
412 return NGX_OK;
415 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_PRIVATE)
416 && ngx_strstr(r->headers_out.cache_control->value.data, "private"))
418 return NGX_OK;
421 return NGX_DECLINED;
424 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_LM)
425 && r->headers_out.last_modified)
427 return NGX_DECLINED;
430 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_ETAG)
431 && r->headers_out.etag)
433 return NGX_DECLINED;
436 return NGX_OK;
440 static ngx_int_t ngx_http_gzip_body_filter(ngx_http_request_t *r,
441 ngx_chain_t *in)
443 int rc, wbits, memlevel;
444 ngx_int_t last;
445 struct gztrailer *trailer;
446 ngx_buf_t *b;
447 ngx_chain_t *cl;
448 ngx_http_gzip_ctx_t *ctx;
449 ngx_http_gzip_conf_t *conf;
451 ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
453 if (ctx == NULL || ctx->done) {
454 return ngx_http_next_body_filter(r, in);
457 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
459 if (ctx->preallocated == NULL) {
460 wbits = conf->wbits;
461 memlevel = conf->memlevel;
463 if (ctx->length > 0) {
465 /* the actual zlib window size is smaller by 262 bytes */
467 while (ctx->length < ((1 << (wbits - 1)) - 262)) {
468 wbits--;
469 memlevel--;
474 * We preallocate a memory for zlib in one buffer (200K-400K), this
475 * decreases a number of malloc() and free() calls and also probably
476 * decreases a number of syscalls (sbrk() and so on).
477 * Besides we free this memory as soon as the gzipping will complete
478 * and do not wait while a whole response will be sent to a client.
480 * 8K is for zlib deflate_state, it takes
481 * * 5816 bytes on x86 and sparc64 (32-bit mode)
482 * * 5920 bytes on amd64 and sparc64
485 ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
487 if (!(ctx->preallocated = ngx_palloc(r->pool, ctx->allocated))) {
488 return NGX_ERROR;
491 ctx->free_mem = ctx->preallocated;
493 ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
494 ctx->zstream.zfree = ngx_http_gzip_filter_free;
495 ctx->zstream.opaque = ctx;
497 rc = deflateInit2(&ctx->zstream, conf->level, Z_DEFLATED,
498 -wbits, memlevel, Z_DEFAULT_STRATEGY);
500 if (rc != Z_OK) {
501 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
502 "deflateInit2() failed: %d", rc);
503 ngx_http_gzip_error(ctx);
504 return NGX_ERROR;
507 if (!(b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)))) {
508 ngx_http_gzip_error(ctx);
509 return NGX_ERROR;
512 b->memory = 1;
513 b->pos = gzheader;
514 b->last = b->pos + 10;
516 if (!(cl = ngx_alloc_chain_link(r->pool))) {
517 ngx_http_gzip_error(ctx);
518 return NGX_ERROR;
520 cl->buf = b;
521 cl->next = NULL;
524 * We pass the gzheader to the next filter now to avoid its linking
525 * to the ctx->busy chain. zlib does not usually output the compressed
526 * data in the initial iterations, so the gzheader that was linked
527 * to the ctx->busy chain would be flushed by ngx_http_write_filter().
530 if (ngx_http_next_body_filter(r, cl) == NGX_ERROR) {
531 ngx_http_gzip_error(ctx);
532 return NGX_ERROR;
535 ctx->last_out = &ctx->out;
537 ctx->crc32 = crc32(0L, Z_NULL, 0);
538 ctx->flush = Z_NO_FLUSH;
541 if (in) {
542 if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
543 ngx_http_gzip_error(ctx);
544 return NGX_ERROR;
548 last = NGX_NONE;
550 for ( ;; ) {
552 for ( ;; ) {
554 /* does zlib need a new data ? */
556 if (ctx->zstream.avail_in == 0
557 && ctx->flush == Z_NO_FLUSH
558 && !ctx->redo)
560 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
561 "gzip in: %p", ctx->in);
563 if (ctx->in == NULL) {
564 break;
567 ctx->in_buf = ctx->in->buf;
568 ctx->in = ctx->in->next;
570 ctx->zstream.next_in = ctx->in_buf->pos;
571 ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
573 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
574 "gzip in_buf:%p ni:%p ai:%ud",
575 ctx->in_buf,
576 ctx->zstream.next_in, ctx->zstream.avail_in);
578 /* STUB */
579 if (ctx->in_buf->last < ctx->in_buf->pos) {
580 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
581 "zstream.avail_in is huge");
582 ctx->done = 1;
583 return NGX_ERROR;
585 /**/
587 if (ctx->in_buf->last_buf) {
588 ctx->flush = Z_FINISH;
590 } else if (ctx->in_buf->flush) {
591 ctx->flush = Z_SYNC_FLUSH;
594 if (ctx->zstream.avail_in == 0) {
595 if (ctx->flush == Z_NO_FLUSH) {
596 continue;
599 } else {
600 ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
601 ctx->zstream.avail_in);
606 /* is there a space for the gzipped data ? */
608 if (ctx->zstream.avail_out == 0) {
610 if (ctx->free) {
611 ctx->out_buf = ctx->free->buf;
612 ctx->free = ctx->free->next;
614 } else if (ctx->bufs < conf->bufs.num) {
615 ctx->out_buf = ngx_create_temp_buf(r->pool,
616 conf->bufs.size);
617 if (ctx->out_buf == NULL) {
618 ngx_http_gzip_error(ctx);
619 return NGX_ERROR;
622 ctx->out_buf->tag = (ngx_buf_tag_t)
623 &ngx_http_gzip_filter_module;
624 ctx->out_buf->recycled = 1;
625 ctx->bufs++;
627 } else {
628 break;
631 ctx->zstream.next_out = ctx->out_buf->pos;
632 ctx->zstream.avail_out = conf->bufs.size;
635 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
636 "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
637 ctx->zstream.next_in, ctx->zstream.next_out,
638 ctx->zstream.avail_in, ctx->zstream.avail_out,
639 ctx->flush, ctx->redo);
641 rc = deflate(&ctx->zstream, ctx->flush);
643 if (rc != Z_OK && rc != Z_STREAM_END) {
644 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
645 "deflate() failed: %d, %d", ctx->flush, rc);
646 ngx_http_gzip_error(ctx);
647 return NGX_ERROR;
650 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
651 "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
652 ctx->zstream.next_in, ctx->zstream.next_out,
653 ctx->zstream.avail_in, ctx->zstream.avail_out,
654 rc);
656 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
657 "gzip in_buf:%p pos:%p",
658 ctx->in_buf, ctx->in_buf->pos);
661 if (ctx->zstream.next_in) {
662 ctx->in_buf->pos = ctx->zstream.next_in;
664 if (ctx->zstream.avail_in == 0) {
665 ctx->zstream.next_in = NULL;
669 ctx->out_buf->last = ctx->zstream.next_out;
671 if (ctx->zstream.avail_out == 0) {
673 /* zlib wants to output some more gzipped data */
675 if (!(cl = ngx_alloc_chain_link(r->pool))) {
676 ngx_http_gzip_error(ctx);
677 return NGX_ERROR;
679 cl->buf = ctx->out_buf;
680 cl->next = NULL;
681 *ctx->last_out = cl;
682 ctx->last_out = &cl->next;
684 ctx->redo = 1;
686 continue;
689 ctx->redo = 0;
691 if (ctx->flush == Z_SYNC_FLUSH) {
693 ctx->out_buf->flush = 0;
694 ctx->flush = Z_NO_FLUSH;
696 if (!(cl = ngx_alloc_chain_link(r->pool))) {
697 ngx_http_gzip_error(ctx);
698 return NGX_ERROR;
700 cl->buf = ctx->out_buf;
701 cl->next = NULL;
702 *ctx->last_out = cl;
703 ctx->last_out = &cl->next;
705 break;
708 if (rc == Z_STREAM_END) {
710 ctx->zin = ctx->zstream.total_in;
711 ctx->zout = 10 + ctx->zstream.total_out + 8;
713 rc = deflateEnd(&ctx->zstream);
715 if (rc != Z_OK) {
716 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
717 "deflateEnd() failed: %d", rc);
718 ngx_http_gzip_error(ctx);
719 return NGX_ERROR;
722 ngx_pfree(r->pool, ctx->preallocated);
724 if (!(cl = ngx_alloc_chain_link(r->pool))) {
725 ngx_http_gzip_error(ctx);
726 return NGX_ERROR;
728 cl->buf = ctx->out_buf;
729 cl->next = NULL;
730 *ctx->last_out = cl;
731 ctx->last_out = &cl->next;
733 if (ctx->zstream.avail_out >= 8) {
734 trailer = (struct gztrailer *) ctx->out_buf->last;
735 ctx->out_buf->last += 8;
736 ctx->out_buf->last_buf = 1;
738 } else {
739 if (!(b = ngx_create_temp_buf(r->pool, 8))) {
740 ngx_http_gzip_error(ctx);
741 return NGX_ERROR;
744 b->last_buf = 1;
746 if (!(cl = ngx_alloc_chain_link(r->pool))) {
747 ngx_http_gzip_error(ctx);
748 return NGX_ERROR;
750 cl->buf = b;
751 cl->next = NULL;
752 *ctx->last_out = cl;
753 ctx->last_out = &cl->next;
754 trailer = (struct gztrailer *) b->pos;
755 b->last += 8;
758 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
760 trailer->crc32 = ctx->crc32;
761 trailer->zlen = ctx->zin;
763 #else
764 trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
765 trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);
766 trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
767 trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);
769 trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
770 trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);
771 trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
772 trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);
773 #endif
775 ctx->zstream.avail_in = 0;
776 ctx->zstream.avail_out = 0;
778 ctx->done = 1;
780 break;
783 if (conf->no_buffer && ctx->in == NULL) {
784 if (!(cl = ngx_alloc_chain_link(r->pool))) {
785 ngx_http_gzip_error(ctx);
786 return NGX_ERROR;
788 cl->buf = ctx->out_buf;
789 cl->next = NULL;
790 *ctx->last_out = cl;
791 ctx->last_out = &cl->next;
793 break;
797 if (last == NGX_AGAIN && !ctx->done) {
798 return NGX_AGAIN;
801 if (ctx->out == NULL && ctx->busy == NULL) {
802 return NGX_OK;
805 last = ngx_http_next_body_filter(r, ctx->out);
808 * we do not check NGX_AGAIN here because the downstream filters
809 * may free some buffers and zlib may compress some data into them
812 if (last == NGX_ERROR) {
813 ngx_http_gzip_error(ctx);
814 return NGX_ERROR;
817 ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out,
818 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
819 ctx->last_out = &ctx->out;
821 if (ctx->done) {
822 return last;
828 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size)
830 ngx_http_gzip_ctx_t *ctx = opaque;
832 void *p;
833 ngx_uint_t alloc;
835 alloc = items * size;
837 if (alloc % 512 != 0) {
840 * The zlib deflate_state allocation, it takes about 6K,
841 * we allocate 8K. Other allocations are divisible by 512.
844 alloc = (alloc + ngx_pagesize - 1) & ~(ngx_pagesize - 1);
847 if (alloc <= ctx->allocated) {
848 p = ctx->free_mem;
849 ctx->free_mem += alloc;
850 ctx->allocated -= alloc;
852 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
853 "gzip alloc: n:%ud s:%ud a:%ud p:%p",
854 items, size, alloc, p);
856 return p;
859 ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0,
860 "gzip filter failed to use preallocated memory: %ud of %ud",
861 items * size, ctx->allocated);
863 p = ngx_palloc(ctx->request->pool, items * size);
865 return p;
869 static void ngx_http_gzip_filter_free(void *opaque, void *address)
871 #if 0
872 ngx_http_gzip_ctx_t *ctx = opaque;
874 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
875 "gzip free: %p", address);
876 #endif
880 static u_char *ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf,
881 ngx_http_log_op_t *op)
883 ngx_uint_t zint, zfrac;
884 ngx_http_gzip_ctx_t *ctx;
886 ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
888 if (ctx == NULL || ctx->zout == 0) {
889 *buf = '-';
890 return buf + 1;
893 zint = (ngx_uint_t) (ctx->zin / ctx->zout);
894 zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);
896 if ((ctx->zin * 1000 / ctx->zout) % 10 > 4) {
898 /* the rounding, e.g., 2.125 to 2.13 */
900 zfrac++;
902 if (zfrac > 99) {
903 zint++;
904 zfrac = 0;
908 return ngx_sprintf(buf, "%ui.%02ui", zint, zfrac);
912 static void ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx)
914 deflateEnd(&ctx->zstream);
916 if (ctx->preallocated) {
917 ngx_pfree(ctx->request->pool, ctx->preallocated);
920 ctx->zstream.avail_in = 0;
921 ctx->zstream.avail_out = 0;
923 ctx->done = 1;
925 return;
929 static ngx_int_t ngx_http_gzip_add_log_formats(ngx_conf_t *cf)
931 ngx_http_log_op_name_t *op;
933 for (op = ngx_http_gzip_log_fmt_ops; op->name.len; op++) { /* void */ }
934 op->run = NULL;
936 for (op = ngx_http_log_fmt_ops; op->run; op++) {
937 if (op->name.len == 0) {
938 op = (ngx_http_log_op_name_t *) op->run;
942 op->run = (ngx_http_log_op_run_pt) ngx_http_gzip_log_fmt_ops;
944 return NGX_OK;
948 static ngx_int_t ngx_http_gzip_filter_init(ngx_cycle_t *cycle)
950 ngx_http_next_header_filter = ngx_http_top_header_filter;
951 ngx_http_top_header_filter = ngx_http_gzip_header_filter;
953 ngx_http_next_body_filter = ngx_http_top_body_filter;
954 ngx_http_top_body_filter = ngx_http_gzip_body_filter;
956 return NGX_OK;
960 static void *ngx_http_gzip_create_conf(ngx_conf_t *cf)
962 ngx_http_gzip_conf_t *conf;
964 if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t)))) {
965 return NGX_CONF_ERROR;
969 * set by ngx_pcalloc():
971 * conf->bufs.num = 0;
972 * conf->proxied = 0;
973 * conf->types = NULL;
976 conf->enable = NGX_CONF_UNSET;
977 conf->no_buffer = NGX_CONF_UNSET;
979 conf->http_version = NGX_CONF_UNSET_UINT;
981 conf->level = NGX_CONF_UNSET;
982 conf->wbits = (size_t) NGX_CONF_UNSET;
983 conf->memlevel = (size_t) NGX_CONF_UNSET;
984 conf->min_length = NGX_CONF_UNSET;
986 return conf;
990 static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
991 void *parent, void *child)
993 ngx_http_gzip_conf_t *prev = parent;
994 ngx_http_gzip_conf_t *conf = child;
996 ngx_http_gzip_type_t *type;
998 ngx_conf_merge_value(conf->enable, prev->enable, 0);
1000 ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize);
1002 ngx_conf_merge_unsigned_value(conf->http_version, prev->http_version,
1003 NGX_HTTP_VERSION_11);
1004 ngx_conf_merge_bitmask_value(conf->proxied, prev->proxied,
1005 (NGX_CONF_BITMASK_SET
1006 |NGX_HTTP_GZIP_PROXIED_OFF));
1008 ngx_conf_merge_value(conf->level, prev->level, 1);
1009 ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
1010 ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,
1011 MAX_MEM_LEVEL - 1);
1012 ngx_conf_merge_value(conf->min_length, prev->min_length, 0);
1013 ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);
1015 if (conf->types == NULL) {
1016 if (prev->types == NULL) {
1017 conf->types = ngx_array_create(cf->pool, 1,
1018 sizeof(ngx_http_gzip_type_t));
1019 if (conf->types == NULL) {
1020 return NGX_CONF_ERROR;
1023 if (!(type = ngx_array_push(conf->types))) {
1024 return NGX_CONF_ERROR;
1027 type->name.len = sizeof("text/html") - 1;
1028 type->name.data = (u_char *) "text/html";
1029 type->enable = 1;
1031 } else {
1032 conf->types = prev->types;
1036 return NGX_CONF_OK;
1040 static char *ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd,
1041 void *conf)
1043 ngx_http_gzip_conf_t *gcf = conf;
1045 ngx_str_t *value;
1046 ngx_uint_t i;
1047 ngx_http_gzip_type_t *type;
1049 if (gcf->types == NULL) {
1050 gcf->types = ngx_array_create(cf->pool, 5,
1051 sizeof(ngx_http_gzip_type_t));
1052 if (gcf->types == NULL) {
1053 return NGX_CONF_ERROR;
1056 if (!(type = ngx_array_push(gcf->types))) {
1057 return NGX_CONF_ERROR;
1060 type->name.len = sizeof("text/html") - 1;
1061 type->name.data = (u_char *) "text/html";
1062 type->enable = 1;
1065 value = cf->args->elts;
1067 for (i = 1; i < cf->args->nelts; i++) {
1069 if (ngx_strcmp(value[i].data, "text/html") == 0) {
1070 continue;
1073 if (!(type = ngx_array_push(gcf->types))) {
1074 return NGX_CONF_ERROR;
1077 type->name.len = value[i].len;
1079 if (!(type->name.data = ngx_palloc(cf->pool, type->name.len + 1))) {
1080 return NGX_CONF_ERROR;
1083 ngx_cpystrn(type->name.data, value[i].data, type->name.len + 1);
1086 return NGX_CONF_OK;
1090 static char *ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data)
1092 int *np = data;
1094 int wbits, wsize;
1096 wbits = 15;
1098 for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {
1100 if (wsize == *np) {
1101 *np = wbits;
1103 return NGX_CONF_OK;
1106 wbits--;
1109 return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k";
1113 static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data)
1115 int *np = data;
1117 int memlevel, hsize;
1119 memlevel = 9;
1121 for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) {
1123 if (hsize == *np) {
1124 *np = memlevel;
1126 return NGX_CONF_OK;
1129 memlevel--;
1132 return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k";