comparison src/http/modules/ngx_http_gzip_filter_module.c @ 50:72eb30262aac NGINX_0_1_25

nginx 0.1.25 *) Bugfix: nginx did run on Linux parisc. *) Feature: nginx now does not start under FreeBSD if the sysctl kern.ipc.somaxconn value is too big. *) Bugfix: if a request was internally redirected by the ngx_http_index_module module to the ngx_http_proxy_module or ngx_http_fastcgi_module modules, then the index file was not closed after request completion. *) Feature: the "proxy_pass" can be used in location with regular expression. *) Feature: the ngx_http_rewrite_filter_module module supports the condition like "if ($HTTP_USER_AGENT ~ MSIE)". *) Bugfix: nginx started too slow if the large number of addresses and text values were used in the "geo" directive. *) Change: a variable name must be declared as "$name" in the "geo" directive. The previous variant without "$" is still supported, but will be removed soon. *) Feature: the "%{VARIABLE}v" logging parameter. *) Feature: the "set $name value" directive. *) Bugfix: gcc 4.0 compatibility. *) Feature: the --with-openssl-opt=OPTIONS autoconfiguration directive.
author Igor Sysoev <http://sysoev.ru>
date Sat, 19 Mar 2005 00:00:00 +0300
parents
children b55cbf18157e
comparison
equal deleted inserted replaced
49:93dabbc9efb9 50:72eb30262aac
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_array_t *types; /* array of ngx_http_gzip_type_t */
19
20 ngx_bufs_t bufs;
21
22 ngx_uint_t http_version;
23 ngx_uint_t proxied;
24
25 int level;
26 size_t wbits;
27 size_t memlevel;
28 ssize_t min_length;
29 } ngx_http_gzip_conf_t;
30
31
32 typedef struct {
33 ngx_str_t name;
34 ngx_uint_t enable;
35 } ngx_http_gzip_type_t;
36
37
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
47
48
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;
58
59 off_t length;
60
61 void *preallocated;
62 char *free_mem;
63 ngx_uint_t allocated;
64
65 unsigned flush:4;
66 unsigned redo:1;
67 unsigned done:1;
68
69 size_t zin;
70 size_t zout;
71
72 uint32_t crc32;
73 z_stream zstream;
74 ngx_http_request_t *request;
75 } ngx_http_gzip_ctx_t;
76
77
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);
84
85 static u_char *ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf,
86 ngx_http_log_op_t *op);
87
88 static ngx_int_t ngx_http_gzip_add_log_formats(ngx_conf_t *cf);
89
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);
98
99
100 static ngx_conf_num_bounds_t ngx_http_gzip_comp_level_bounds = {
101 ngx_conf_check_num_bounds, 1, 9
102 };
103
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;
108
109
110
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 }
115 };
116
117
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 }
129 };
130
131
132 static ngx_command_t ngx_http_gzip_filter_commands[] = {
133
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 },
141
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 },
148
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,
153 0,
154 NULL },
155
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 },
162
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 },
169
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 },
176
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 },
183
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 },
190
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 },
197
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 },
204
205 ngx_null_command
206 };
207
208
209 static ngx_http_module_t ngx_http_gzip_filter_module_ctx = {
210 ngx_http_gzip_add_log_formats, /* pre conf */
211
212 NULL, /* create main configuration */
213 NULL, /* init main configuration */
214
215 NULL, /* create server configuration */
216 NULL, /* merge server configuration */
217
218 ngx_http_gzip_create_conf, /* create location configuration */
219 ngx_http_gzip_merge_conf /* merge location configuration */
220 };
221
222
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 */
230 };
231
232
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 }
237 };
238
239
240
241 static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
242
243 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
244
245 struct gztrailer {
246 uint32_t crc32;
247 uint32_t zlen;
248 };
249
250 #else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */
251
252 struct gztrailer {
253 u_char crc32[4];
254 u_char zlen[4];
255 };
256
257 #endif
258
259
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;
262
263
264 static ngx_int_t
265 ngx_http_gzip_header_filter(ngx_http_request_t *r)
266 {
267 ngx_uint_t i, found;
268 ngx_http_gzip_ctx_t *ctx;
269 ngx_http_gzip_conf_t *conf;
270 ngx_http_gzip_type_t *type;
271
272 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
273
274 if (!conf->enable
275 || (r->headers_out.status != NGX_HTTP_OK
276 && r->headers_out.status != NGX_HTTP_FORBIDDEN
277 && r->headers_out.status != NGX_HTTP_NOT_FOUND)
278 || r->header_only
279 || r->http_version < conf->http_version
280 || r->headers_out.content_type == NULL
281 || (r->headers_out.content_encoding
282 && r->headers_out.content_encoding->value.len)
283 || r->headers_in.accept_encoding == NULL
284 || (r->headers_out.content_length_n != -1
285 && r->headers_out.content_length_n < conf->min_length)
286 || ngx_strstr(r->headers_in.accept_encoding->value.data, "gzip") == NULL
287 )
288 {
289 return ngx_http_next_header_filter(r);
290 }
291
292
293 found = 0;
294 type = conf->types->elts;
295
296 for (i = 0; i < conf->types->nelts; i++) {
297 if (r->headers_out.content_type->value.len >= type[i].name.len
298 && ngx_strncasecmp(r->headers_out.content_type->value.data,
299 type[i].name.data, type[i].name.len) == 0)
300 {
301 found = 1;
302 break;
303 }
304 }
305
306 if (!found) {
307 return ngx_http_next_header_filter(r);
308 }
309
310
311 if (r->headers_in.via) {
312 if (conf->proxied & NGX_HTTP_GZIP_PROXIED_OFF) {
313 return ngx_http_next_header_filter(r);
314 }
315
316 if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_ANY)
317 && ngx_http_gzip_proxied(r, conf) == NGX_DECLINED)
318 {
319 return ngx_http_next_header_filter(r);
320 }
321 }
322
323
324 /*
325 * if the URL (without the "http://" prefix) is longer than 253 bytes
326 * then MSIE 4.x can not handle the compressed stream - it waits too long,
327 * hangs up or crashes
328 */
329
330 if (r->headers_in.msie4 && r->unparsed_uri.len > 200) {
331 return ngx_http_next_header_filter(r);
332 }
333
334 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t));
335 if (ctx == NULL) {
336 return NGX_ERROR;
337 }
338
339 ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
340
341
342 ctx->request = r;
343
344 r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers);
345 if (r->headers_out.content_encoding == NULL) {
346 return NGX_ERROR;
347 }
348
349 r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1;
350 r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding";
351 r->headers_out.content_encoding->value.len = sizeof("gzip") - 1;
352 r->headers_out.content_encoding->value.data = (u_char *) "gzip";
353
354 ctx->length = r->headers_out.content_length_n;
355 r->headers_out.content_length_n = -1;
356 if (r->headers_out.content_length) {
357 r->headers_out.content_length->key.len = 0;
358 r->headers_out.content_length = NULL;
359 }
360 r->filter_need_in_memory = 1;
361
362 return ngx_http_next_header_filter(r);
363 }
364
365
366 static ngx_int_t
367 ngx_http_gzip_proxied(ngx_http_request_t *r, ngx_http_gzip_conf_t *conf)
368 {
369 time_t date, expires;
370
371 if (r->headers_in.authorization
372 && (conf->proxied & NGX_HTTP_GZIP_PROXIED_AUTH))
373 {
374 return NGX_OK;
375 }
376
377 if (r->headers_out.expires) {
378
379 if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_EXPIRED)) {
380 return NGX_DECLINED;
381 }
382
383 expires = ngx_http_parse_time(r->headers_out.expires->value.data,
384 r->headers_out.expires->value.len);
385 if (expires == NGX_ERROR) {
386 return NGX_DECLINED;
387 }
388
389 if (r->headers_out.date) {
390 date = ngx_http_parse_time(r->headers_out.date->value.data,
391 r->headers_out.date->value.len);
392 if (date == NGX_ERROR) {
393 return NGX_DECLINED;
394 }
395
396 } else {
397 date = ngx_time();
398 }
399
400 if (expires < date) {
401 return NGX_OK;
402 }
403
404 return NGX_DECLINED;
405 }
406
407 if (r->headers_out.cache_control) {
408
409 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_CACHE)
410 && ngx_strstr(r->headers_out.cache_control->value.data, "no-cache"))
411 {
412 return NGX_OK;
413 }
414
415 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_STORE)
416 && ngx_strstr(r->headers_out.cache_control->value.data, "no-store"))
417 {
418 return NGX_OK;
419 }
420
421 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_PRIVATE)
422 && ngx_strstr(r->headers_out.cache_control->value.data, "private"))
423 {
424 return NGX_OK;
425 }
426
427 return NGX_DECLINED;
428 }
429
430 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_LM)
431 && r->headers_out.last_modified)
432 {
433 return NGX_DECLINED;
434 }
435
436 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_ETAG)
437 && r->headers_out.etag)
438 {
439 return NGX_DECLINED;
440 }
441
442 return NGX_OK;
443 }
444
445
446 static ngx_int_t
447 ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
448 {
449 int rc, wbits, memlevel;
450 ngx_int_t last;
451 struct gztrailer *trailer;
452 ngx_buf_t *b;
453 ngx_chain_t *cl, out;
454 ngx_http_gzip_ctx_t *ctx;
455 ngx_http_gzip_conf_t *conf;
456
457 ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
458
459 if (ctx == NULL || ctx->done) {
460 return ngx_http_next_body_filter(r, in);
461 }
462
463 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
464
465 if (ctx->preallocated == NULL) {
466 wbits = conf->wbits;
467 memlevel = conf->memlevel;
468
469 if (ctx->length > 0) {
470
471 /* the actual zlib window size is smaller by 262 bytes */
472
473 while (ctx->length < ((1 << (wbits - 1)) - 262)) {
474 wbits--;
475 memlevel--;
476 }
477 }
478
479 /*
480 * We preallocate a memory for zlib in one buffer (200K-400K), this
481 * decreases a number of malloc() and free() calls and also probably
482 * decreases a number of syscalls (sbrk() and so on).
483 * Besides we free this memory as soon as the gzipping will complete
484 * and do not wait while a whole response will be sent to a client.
485 *
486 * 8K is for zlib deflate_state, it takes
487 * * 5816 bytes on x86 and sparc64 (32-bit mode)
488 * * 5920 bytes on amd64 and sparc64
489 */
490
491 ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
492
493 ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
494 if (ctx->preallocated == NULL) {
495 return NGX_ERROR;
496 }
497
498 ctx->free_mem = ctx->preallocated;
499
500 ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
501 ctx->zstream.zfree = ngx_http_gzip_filter_free;
502 ctx->zstream.opaque = ctx;
503
504 rc = deflateInit2(&ctx->zstream, conf->level, Z_DEFLATED,
505 -wbits, memlevel, Z_DEFAULT_STRATEGY);
506
507 if (rc != Z_OK) {
508 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
509 "deflateInit2() failed: %d", rc);
510 ngx_http_gzip_error(ctx);
511 return NGX_ERROR;
512 }
513
514 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
515 if (b == NULL) {
516 ngx_http_gzip_error(ctx);
517 return NGX_ERROR;
518 }
519
520 b->memory = 1;
521 b->pos = gzheader;
522 b->last = b->pos + 10;
523
524 out.buf = b;
525 out.next = NULL;
526
527 /*
528 * We pass the gzheader to the next filter now to avoid its linking
529 * to the ctx->busy chain. zlib does not usually output the compressed
530 * data in the initial iterations, so the gzheader that was linked
531 * to the ctx->busy chain would be flushed by ngx_http_write_filter().
532 */
533
534 if (ngx_http_next_body_filter(r, &out) == NGX_ERROR) {
535 ngx_http_gzip_error(ctx);
536 return NGX_ERROR;
537 }
538
539 ctx->last_out = &ctx->out;
540
541 ctx->crc32 = crc32(0L, Z_NULL, 0);
542 ctx->flush = Z_NO_FLUSH;
543 }
544
545 if (in) {
546 if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
547 ngx_http_gzip_error(ctx);
548 return NGX_ERROR;
549 }
550 }
551
552 last = NGX_NONE;
553
554 for ( ;; ) {
555
556 for ( ;; ) {
557
558 /* does zlib need a new data ? */
559
560 if (ctx->zstream.avail_in == 0
561 && ctx->flush == Z_NO_FLUSH
562 && !ctx->redo)
563 {
564 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
565 "gzip in: %p", ctx->in);
566
567 if (ctx->in == NULL) {
568 break;
569 }
570
571 ctx->in_buf = ctx->in->buf;
572 ctx->in = ctx->in->next;
573
574 ctx->zstream.next_in = ctx->in_buf->pos;
575 ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
576
577 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
578 "gzip in_buf:%p ni:%p ai:%ud",
579 ctx->in_buf,
580 ctx->zstream.next_in, ctx->zstream.avail_in);
581
582 /* STUB */
583 if (ctx->in_buf->last < ctx->in_buf->pos) {
584 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
585 "zstream.avail_in is huge");
586 ctx->done = 1;
587 return NGX_ERROR;
588 }
589 /**/
590
591 if (ctx->in_buf->last_buf) {
592 ctx->flush = Z_FINISH;
593
594 } else if (ctx->in_buf->flush) {
595 ctx->flush = Z_SYNC_FLUSH;
596 }
597
598 if (ctx->zstream.avail_in == 0) {
599 if (ctx->flush == Z_NO_FLUSH) {
600 continue;
601 }
602
603 } else {
604 ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
605 ctx->zstream.avail_in);
606 }
607 }
608
609
610 /* is there a space for the gzipped data ? */
611
612 if (ctx->zstream.avail_out == 0) {
613
614 if (ctx->free) {
615 ctx->out_buf = ctx->free->buf;
616 ctx->free = ctx->free->next;
617
618 } else if (ctx->bufs < conf->bufs.num) {
619 ctx->out_buf = ngx_create_temp_buf(r->pool,
620 conf->bufs.size);
621 if (ctx->out_buf == NULL) {
622 ngx_http_gzip_error(ctx);
623 return NGX_ERROR;
624 }
625
626 ctx->out_buf->tag = (ngx_buf_tag_t)
627 &ngx_http_gzip_filter_module;
628 ctx->out_buf->recycled = 1;
629 ctx->bufs++;
630
631 } else {
632 break;
633 }
634
635 ctx->zstream.next_out = ctx->out_buf->pos;
636 ctx->zstream.avail_out = conf->bufs.size;
637 }
638
639 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
640 "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
641 ctx->zstream.next_in, ctx->zstream.next_out,
642 ctx->zstream.avail_in, ctx->zstream.avail_out,
643 ctx->flush, ctx->redo);
644
645 rc = deflate(&ctx->zstream, ctx->flush);
646
647 if (rc != Z_OK && rc != Z_STREAM_END) {
648 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
649 "deflate() failed: %d, %d", ctx->flush, rc);
650 ngx_http_gzip_error(ctx);
651 return NGX_ERROR;
652 }
653
654 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
655 "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
656 ctx->zstream.next_in, ctx->zstream.next_out,
657 ctx->zstream.avail_in, ctx->zstream.avail_out,
658 rc);
659
660 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
661 "gzip in_buf:%p pos:%p",
662 ctx->in_buf, ctx->in_buf->pos);
663
664
665 if (ctx->zstream.next_in) {
666 ctx->in_buf->pos = ctx->zstream.next_in;
667
668 if (ctx->zstream.avail_in == 0) {
669 ctx->zstream.next_in = NULL;
670 }
671 }
672
673 ctx->out_buf->last = ctx->zstream.next_out;
674
675 if (ctx->zstream.avail_out == 0) {
676
677 /* zlib wants to output some more gzipped data */
678
679 cl = ngx_alloc_chain_link(r->pool);
680 if (cl == NULL) {
681 ngx_http_gzip_error(ctx);
682 return NGX_ERROR;
683 }
684
685 cl->buf = ctx->out_buf;
686 cl->next = NULL;
687 *ctx->last_out = cl;
688 ctx->last_out = &cl->next;
689
690 ctx->redo = 1;
691
692 continue;
693 }
694
695 ctx->redo = 0;
696
697 if (ctx->flush == Z_SYNC_FLUSH) {
698
699 ctx->out_buf->flush = 0;
700 ctx->flush = Z_NO_FLUSH;
701
702 cl = ngx_alloc_chain_link(r->pool);
703 if (cl == NULL) {
704 ngx_http_gzip_error(ctx);
705 return NGX_ERROR;
706 }
707
708 cl->buf = ctx->out_buf;
709 cl->next = NULL;
710 *ctx->last_out = cl;
711 ctx->last_out = &cl->next;
712
713 break;
714 }
715
716 if (rc == Z_STREAM_END) {
717
718 ctx->zin = ctx->zstream.total_in;
719 ctx->zout = 10 + ctx->zstream.total_out + 8;
720
721 rc = deflateEnd(&ctx->zstream);
722
723 if (rc != Z_OK) {
724 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
725 "deflateEnd() failed: %d", rc);
726 ngx_http_gzip_error(ctx);
727 return NGX_ERROR;
728 }
729
730 ngx_pfree(r->pool, ctx->preallocated);
731
732 cl = ngx_alloc_chain_link(r->pool);
733 if (cl == NULL) {
734 ngx_http_gzip_error(ctx);
735 return NGX_ERROR;
736 }
737
738 cl->buf = ctx->out_buf;
739 cl->next = NULL;
740 *ctx->last_out = cl;
741 ctx->last_out = &cl->next;
742
743 if (ctx->zstream.avail_out >= 8) {
744 trailer = (struct gztrailer *) ctx->out_buf->last;
745 ctx->out_buf->last += 8;
746 ctx->out_buf->last_buf = 1;
747
748 } else {
749 b = ngx_create_temp_buf(r->pool, 8);
750 if (b == NULL) {
751 ngx_http_gzip_error(ctx);
752 return NGX_ERROR;
753 }
754
755 b->last_buf = 1;
756
757 cl = ngx_alloc_chain_link(r->pool);
758 if (cl == NULL) {
759 ngx_http_gzip_error(ctx);
760 return NGX_ERROR;
761 }
762
763 cl->buf = b;
764 cl->next = NULL;
765 *ctx->last_out = cl;
766 ctx->last_out = &cl->next;
767 trailer = (struct gztrailer *) b->pos;
768 b->last += 8;
769 }
770
771 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
772
773 trailer->crc32 = ctx->crc32;
774 trailer->zlen = ctx->zin;
775
776 #else
777 trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
778 trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);
779 trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
780 trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);
781
782 trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
783 trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);
784 trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
785 trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);
786 #endif
787
788 ctx->zstream.avail_in = 0;
789 ctx->zstream.avail_out = 0;
790
791 ctx->done = 1;
792
793 break;
794 }
795
796 if (conf->no_buffer && ctx->in == NULL) {
797
798 cl = ngx_alloc_chain_link(r->pool);
799 if (cl == NULL) {
800 ngx_http_gzip_error(ctx);
801 return NGX_ERROR;
802 }
803
804 cl->buf = ctx->out_buf;
805 cl->next = NULL;
806 *ctx->last_out = cl;
807 ctx->last_out = &cl->next;
808
809 break;
810 }
811 }
812
813 if (last == NGX_AGAIN && !ctx->done) {
814 return NGX_AGAIN;
815 }
816
817 if (ctx->out == NULL && ctx->busy == NULL) {
818 return NGX_OK;
819 }
820
821 last = ngx_http_next_body_filter(r, ctx->out);
822
823 /*
824 * we do not check NGX_AGAIN here because the downstream filters
825 * may free some buffers and zlib may compress some data into them
826 */
827
828 if (last == NGX_ERROR) {
829 ngx_http_gzip_error(ctx);
830 return NGX_ERROR;
831 }
832
833 ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out,
834 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
835 ctx->last_out = &ctx->out;
836
837 if (ctx->done) {
838 return last;
839 }
840 }
841 }
842
843
844 static void *
845 ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size)
846 {
847 ngx_http_gzip_ctx_t *ctx = opaque;
848
849 void *p;
850 ngx_uint_t alloc;
851
852 alloc = items * size;
853
854 if (alloc % 512 != 0) {
855
856 /*
857 * The zlib deflate_state allocation, it takes about 6K,
858 * we allocate 8K. Other allocations are divisible by 512.
859 */
860
861 alloc = (alloc + ngx_pagesize - 1) & ~(ngx_pagesize - 1);
862 }
863
864 if (alloc <= ctx->allocated) {
865 p = ctx->free_mem;
866 ctx->free_mem += alloc;
867 ctx->allocated -= alloc;
868
869 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
870 "gzip alloc: n:%ud s:%ud a:%ud p:%p",
871 items, size, alloc, p);
872
873 return p;
874 }
875
876 ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0,
877 "gzip filter failed to use preallocated memory: %ud of %ud",
878 items * size, ctx->allocated);
879
880 p = ngx_palloc(ctx->request->pool, items * size);
881
882 return p;
883 }
884
885
886 static void
887 ngx_http_gzip_filter_free(void *opaque, void *address)
888 {
889 #if 0
890 ngx_http_gzip_ctx_t *ctx = opaque;
891
892 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
893 "gzip free: %p", address);
894 #endif
895 }
896
897
898 static u_char *
899 ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf,
900 ngx_http_log_op_t *op)
901 {
902 ngx_uint_t zint, zfrac;
903 ngx_http_gzip_ctx_t *ctx;
904
905 ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
906
907 if (ctx == NULL || ctx->zout == 0) {
908 *buf = '-';
909 return buf + 1;
910 }
911
912 zint = (ngx_uint_t) (ctx->zin / ctx->zout);
913 zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);
914
915 if ((ctx->zin * 1000 / ctx->zout) % 10 > 4) {
916
917 /* the rounding, e.g., 2.125 to 2.13 */
918
919 zfrac++;
920
921 if (zfrac > 99) {
922 zint++;
923 zfrac = 0;
924 }
925 }
926
927 return ngx_sprintf(buf, "%ui.%02ui", zint, zfrac);
928 }
929
930
931 static void
932 ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx)
933 {
934 deflateEnd(&ctx->zstream);
935
936 if (ctx->preallocated) {
937 ngx_pfree(ctx->request->pool, ctx->preallocated);
938 }
939
940 ctx->zstream.avail_in = 0;
941 ctx->zstream.avail_out = 0;
942
943 ctx->done = 1;
944
945 return;
946 }
947
948
949 static ngx_int_t
950 ngx_http_gzip_add_log_formats(ngx_conf_t *cf)
951 {
952 ngx_http_log_op_name_t *op;
953
954 for (op = ngx_http_gzip_log_fmt_ops; op->name.len; op++) { /* void */ }
955 op->run = NULL;
956
957 for (op = ngx_http_log_fmt_ops; op->run; op++) {
958 if (op->name.len == 0) {
959 op = (ngx_http_log_op_name_t *) op->run;
960 }
961 }
962
963 op->run = (ngx_http_log_op_run_pt) ngx_http_gzip_log_fmt_ops;
964
965 return NGX_OK;
966 }
967
968
969 static ngx_int_t
970 ngx_http_gzip_filter_init(ngx_cycle_t *cycle)
971 {
972 ngx_http_next_header_filter = ngx_http_top_header_filter;
973 ngx_http_top_header_filter = ngx_http_gzip_header_filter;
974
975 ngx_http_next_body_filter = ngx_http_top_body_filter;
976 ngx_http_top_body_filter = ngx_http_gzip_body_filter;
977
978 return NGX_OK;
979 }
980
981
982 static void *
983 ngx_http_gzip_create_conf(ngx_conf_t *cf)
984 {
985 ngx_http_gzip_conf_t *conf;
986
987 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t));
988 if (conf == NULL) {
989 return NGX_CONF_ERROR;
990 }
991
992 /*
993 * set by ngx_pcalloc():
994 *
995 * conf->bufs.num = 0;
996 * conf->proxied = 0;
997 * conf->types = NULL;
998 */
999
1000 conf->enable = NGX_CONF_UNSET;
1001 conf->no_buffer = NGX_CONF_UNSET;
1002
1003 conf->http_version = NGX_CONF_UNSET_UINT;
1004
1005 conf->level = NGX_CONF_UNSET;
1006 conf->wbits = (size_t) NGX_CONF_UNSET;
1007 conf->memlevel = (size_t) NGX_CONF_UNSET;
1008 conf->min_length = NGX_CONF_UNSET;
1009
1010 return conf;
1011 }
1012
1013
1014 static char *
1015 ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1016 {
1017 ngx_http_gzip_conf_t *prev = parent;
1018 ngx_http_gzip_conf_t *conf = child;
1019
1020 ngx_http_gzip_type_t *type;
1021
1022 ngx_conf_merge_value(conf->enable, prev->enable, 0);
1023
1024 ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize);
1025
1026 ngx_conf_merge_unsigned_value(conf->http_version, prev->http_version,
1027 NGX_HTTP_VERSION_11);
1028 ngx_conf_merge_bitmask_value(conf->proxied, prev->proxied,
1029 (NGX_CONF_BITMASK_SET|NGX_HTTP_GZIP_PROXIED_OFF));
1030
1031 ngx_conf_merge_value(conf->level, prev->level, 1);
1032 ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
1033 ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,
1034 MAX_MEM_LEVEL - 1);
1035 ngx_conf_merge_value(conf->min_length, prev->min_length, 0);
1036 ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);
1037
1038 if (conf->types == NULL) {
1039 if (prev->types == NULL) {
1040 conf->types = ngx_array_create(cf->pool, 1,
1041 sizeof(ngx_http_gzip_type_t));
1042 if (conf->types == NULL) {
1043 return NGX_CONF_ERROR;
1044 }
1045
1046 type = ngx_array_push(conf->types);
1047 if (type == NULL) {
1048 return NGX_CONF_ERROR;
1049 }
1050
1051 type->name.len = sizeof("text/html") - 1;
1052 type->name.data = (u_char *) "text/html";
1053 type->enable = 1;
1054
1055 } else {
1056 conf->types = prev->types;
1057 }
1058 }
1059
1060 return NGX_CONF_OK;
1061 }
1062
1063
1064 static char *
1065 ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1066 {
1067 ngx_http_gzip_conf_t *gcf = conf;
1068
1069 ngx_str_t *value;
1070 ngx_uint_t i;
1071 ngx_http_gzip_type_t *type;
1072
1073 if (gcf->types == NULL) {
1074 gcf->types = ngx_array_create(cf->pool, 4,
1075 sizeof(ngx_http_gzip_type_t));
1076 if (gcf->types == NULL) {
1077 return NGX_CONF_ERROR;
1078 }
1079
1080 type = ngx_array_push(gcf->types);
1081 if (type == NULL) {
1082 return NGX_CONF_ERROR;
1083 }
1084
1085 type->name.len = sizeof("text/html") - 1;
1086 type->name.data = (u_char *) "text/html";
1087 type->enable = 1;
1088 }
1089
1090 value = cf->args->elts;
1091
1092 for (i = 1; i < cf->args->nelts; i++) {
1093
1094 if (ngx_strcmp(value[i].data, "text/html") == 0) {
1095 continue;
1096 }
1097
1098 type = ngx_array_push(gcf->types);
1099 if (type == NULL) {
1100 return NGX_CONF_ERROR;
1101 }
1102
1103 type->name.len = value[i].len;
1104
1105 type->name.data = ngx_palloc(cf->pool, type->name.len + 1);
1106 if (type->name.data == NULL) {
1107 return NGX_CONF_ERROR;
1108 }
1109
1110 ngx_cpystrn(type->name.data, value[i].data, type->name.len + 1);
1111 }
1112
1113 return NGX_CONF_OK;
1114 }
1115
1116
1117 static char *
1118 ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data)
1119 {
1120 int *np = data;
1121
1122 int wbits, wsize;
1123
1124 wbits = 15;
1125
1126 for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {
1127
1128 if (wsize == *np) {
1129 *np = wbits;
1130
1131 return NGX_CONF_OK;
1132 }
1133
1134 wbits--;
1135 }
1136
1137 return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k";
1138 }
1139
1140
1141 static char *
1142 ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data)
1143 {
1144 int *np = data;
1145
1146 int memlevel, hsize;
1147
1148 memlevel = 9;
1149
1150 for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) {
1151
1152 if (hsize == *np) {
1153 *np = memlevel;
1154
1155 return NGX_CONF_OK;
1156 }
1157
1158 memlevel--;
1159 }
1160
1161 return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k";
1162 }