3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
18 ngx_array_t
*types
; /* array of ngx_http_gzip_type_t */
22 ngx_uint_t http_version
;
29 } ngx_http_gzip_conf_t
;
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
54 ngx_chain_t
**last_out
;
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
,
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
,
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
137 ngx_conf_set_flag_slot
,
138 NGX_HTTP_LOC_CONF_OFFSET
,
139 offsetof(ngx_http_gzip_conf_t
, enable
),
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
),
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
,
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
),
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
),
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
= {
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)
250 #else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */
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
)
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
);
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
)
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
);
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)
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
);
338 r
->headers_out
.content_encoding
= ngx_list_push(&r
->headers_out
.headers
);
339 if (r
->headers_out
.content_encoding
== NULL
) {
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
))
371 if (r
->headers_out
.expires
) {
373 if (!(conf
->proxied
& NGX_HTTP_GZIP_PROXIED_EXPIRED
)) {
377 expires
= ngx_http_parse_time(r
->headers_out
.expires
->value
.data
,
378 r
->headers_out
.expires
->value
.len
);
379 if (expires
== NGX_ERROR
) {
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
) {
394 if (expires
< date
) {
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"))
409 if ((conf
->proxied
& NGX_HTTP_GZIP_PROXIED_NO_STORE
)
410 && ngx_strstr(r
->headers_out
.cache_control
->value
.data
, "no-store"))
415 if ((conf
->proxied
& NGX_HTTP_GZIP_PROXIED_PRIVATE
)
416 && ngx_strstr(r
->headers_out
.cache_control
->value
.data
, "private"))
424 if ((conf
->proxied
& NGX_HTTP_GZIP_PROXIED_NO_LM
)
425 && r
->headers_out
.last_modified
)
430 if ((conf
->proxied
& NGX_HTTP_GZIP_PROXIED_NO_ETAG
)
431 && r
->headers_out
.etag
)
440 static ngx_int_t
ngx_http_gzip_body_filter(ngx_http_request_t
*r
,
443 int rc
, wbits
, memlevel
;
445 struct gztrailer
*trailer
;
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
) {
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)) {
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
))) {
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
);
501 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, 0,
502 "deflateInit2() failed: %d", rc
);
503 ngx_http_gzip_error(ctx
);
507 if (!(b
= ngx_pcalloc(r
->pool
, sizeof(ngx_buf_t
)))) {
508 ngx_http_gzip_error(ctx
);
514 b
->last
= b
->pos
+ 10;
516 if (!(cl
= ngx_alloc_chain_link(r
->pool
))) {
517 ngx_http_gzip_error(ctx
);
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
);
535 ctx
->last_out
= &ctx
->out
;
537 ctx
->crc32
= crc32(0L, Z_NULL
, 0);
538 ctx
->flush
= Z_NO_FLUSH
;
542 if (ngx_chain_add_copy(r
->pool
, &ctx
->in
, in
) == NGX_ERROR
) {
543 ngx_http_gzip_error(ctx
);
554 /* does zlib need a new data ? */
556 if (ctx
->zstream
.avail_in
== 0
557 && ctx
->flush
== Z_NO_FLUSH
560 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
561 "gzip in: %p", ctx
->in
);
563 if (ctx
->in
== NULL
) {
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",
576 ctx
->zstream
.next_in
, ctx
->zstream
.avail_in
);
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");
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
) {
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) {
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
,
617 if (ctx
->out_buf
== NULL
) {
618 ngx_http_gzip_error(ctx
);
622 ctx
->out_buf
->tag
= (ngx_buf_tag_t
)
623 &ngx_http_gzip_filter_module
;
624 ctx
->out_buf
->recycled
= 1;
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
);
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
,
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
);
679 cl
->buf
= ctx
->out_buf
;
682 ctx
->last_out
= &cl
->next
;
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
);
700 cl
->buf
= ctx
->out_buf
;
703 ctx
->last_out
= &cl
->next
;
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
);
716 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, 0,
717 "deflateEnd() failed: %d", rc
);
718 ngx_http_gzip_error(ctx
);
722 ngx_pfree(r
->pool
, ctx
->preallocated
);
724 if (!(cl
= ngx_alloc_chain_link(r
->pool
))) {
725 ngx_http_gzip_error(ctx
);
728 cl
->buf
= ctx
->out_buf
;
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;
739 if (!(b
= ngx_create_temp_buf(r
->pool
, 8))) {
740 ngx_http_gzip_error(ctx
);
746 if (!(cl
= ngx_alloc_chain_link(r
->pool
))) {
747 ngx_http_gzip_error(ctx
);
753 ctx
->last_out
= &cl
->next
;
754 trailer
= (struct gztrailer
*) b
->pos
;
758 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
760 trailer
->crc32
= ctx
->crc32
;
761 trailer
->zlen
= ctx
->zin
;
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);
775 ctx
->zstream
.avail_in
= 0;
776 ctx
->zstream
.avail_out
= 0;
783 if (conf
->no_buffer
&& ctx
->in
== NULL
) {
784 if (!(cl
= ngx_alloc_chain_link(r
->pool
))) {
785 ngx_http_gzip_error(ctx
);
788 cl
->buf
= ctx
->out_buf
;
791 ctx
->last_out
= &cl
->next
;
797 if (last
== NGX_AGAIN
&& !ctx
->done
) {
801 if (ctx
->out
== NULL
&& ctx
->busy
== NULL
) {
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
);
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
;
828 static void *ngx_http_gzip_filter_alloc(void *opaque
, u_int items
, u_int size
)
830 ngx_http_gzip_ctx_t
*ctx
= opaque
;
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
) {
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
);
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
);
869 static void ngx_http_gzip_filter_free(void *opaque
, void *address
)
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
);
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) {
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 */
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;
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 */ }
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
;
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
;
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;
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
;
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
,
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";
1032 conf
->types
= prev
->types
;
1040 static char *ngx_http_gzip_set_types(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
1043 ngx_http_gzip_conf_t
*gcf
= conf
;
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";
1065 value
= cf
->args
->elts
;
1067 for (i
= 1; i
< cf
->args
->nelts
; i
++) {
1069 if (ngx_strcmp(value
[i
].data
, "text/html") == 0) {
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);
1090 static char *ngx_http_gzip_set_window(ngx_conf_t
*cf
, void *post
, void *data
)
1098 for (wsize
= 32 * 1024; wsize
> 256; wsize
>>= 1) {
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
)
1117 int memlevel
, hsize
;
1121 for (hsize
= 128 * 1024; hsize
> 256; hsize
>>= 1) {
1132 return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k";