comparison src/http/modules/ngx_http_gzip_filter.c @ 0:f0b350454894 NGINX_0_1_0

nginx 0.1.0 *) The first public version.
author Igor Sysoev <http://sysoev.ru>
date Mon, 04 Oct 2004 00:00:00 +0400
parents
children cc9f381affaa
comparison
equal deleted inserted replaced
-1:000000000000 0:f0b350454894
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_bufs_t bufs;
19
20 ngx_uint_t http_version;
21 ngx_uint_t proxied;
22
23 int level;
24 size_t wbits;
25 size_t memlevel;
26 ssize_t min_length;
27 } ngx_http_gzip_conf_t;
28
29
30 #define NGX_HTTP_GZIP_PROXIED_OFF 0x0002
31 #define NGX_HTTP_GZIP_PROXIED_EXPIRED 0x0004
32 #define NGX_HTTP_GZIP_PROXIED_NO_CACHE 0x0008
33 #define NGX_HTTP_GZIP_PROXIED_NO_STORE 0x0010
34 #define NGX_HTTP_GZIP_PROXIED_PRIVATE 0x0020
35 #define NGX_HTTP_GZIP_PROXIED_NO_LM 0x0040
36 #define NGX_HTTP_GZIP_PROXIED_NO_ETAG 0x0080
37 #define NGX_HTTP_GZIP_PROXIED_AUTH 0x0100
38 #define NGX_HTTP_GZIP_PROXIED_ANY 0x0200
39
40
41 typedef struct {
42 ngx_chain_t *in;
43 ngx_chain_t *free;
44 ngx_chain_t *busy;
45 ngx_chain_t *out;
46 ngx_chain_t **last_out;
47 ngx_buf_t *in_buf;
48 ngx_buf_t *out_buf;
49 ngx_int_t bufs;
50
51 off_t length;
52
53 void *preallocated;
54 char *free_mem;
55 ngx_uint_t allocated;
56
57 unsigned flush:4;
58 unsigned redo:1;
59 unsigned done:1;
60 #if 0
61 unsigned pass:1;
62 unsigned blocked:1;
63 #endif
64
65 size_t zin;
66 size_t zout;
67
68 uint32_t crc32;
69 z_stream zstream;
70 ngx_http_request_t *request;
71 } ngx_http_gzip_ctx_t;
72
73
74 static ngx_int_t ngx_http_gzip_proxied(ngx_http_request_t *r,
75 ngx_http_gzip_conf_t *conf);
76 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
77 u_int size);
78 static void ngx_http_gzip_filter_free(void *opaque, void *address);
79 ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx);
80
81 static u_char *ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf,
82 uintptr_t data);
83
84 static ngx_int_t ngx_http_gzip_pre_conf(ngx_conf_t *cf);
85 static ngx_int_t ngx_http_gzip_filter_init(ngx_cycle_t *cycle);
86 static void *ngx_http_gzip_create_conf(ngx_conf_t *cf);
87 static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
88 void *parent, void *child);
89 static char *ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data);
90 static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data);
91
92
93 static ngx_conf_num_bounds_t ngx_http_gzip_comp_level_bounds = {
94 ngx_conf_check_num_bounds, 1, 9
95 };
96
97 static ngx_conf_post_handler_pt ngx_http_gzip_set_window_p =
98 ngx_http_gzip_set_window;
99 static ngx_conf_post_handler_pt ngx_http_gzip_set_hash_p =
100 ngx_http_gzip_set_hash;
101
102
103
104 static ngx_conf_enum_t ngx_http_gzip_http_version[] = {
105 { ngx_string("1.0"), NGX_HTTP_VERSION_10 },
106 { ngx_string("1.1"), NGX_HTTP_VERSION_11 },
107 { ngx_null_string, 0 }
108 };
109
110
111 static ngx_conf_bitmask_t ngx_http_gzip_proxied_mask[] = {
112 { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF },
113 { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED },
114 { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE },
115 { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE },
116 { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE },
117 { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM },
118 { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG },
119 { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH },
120 { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY },
121 { ngx_null_string, 0 }
122 };
123
124
125 static ngx_command_t ngx_http_gzip_filter_commands[] = {
126
127 { ngx_string("gzip"),
128 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
129 ngx_conf_set_flag_slot,
130 NGX_HTTP_LOC_CONF_OFFSET,
131 offsetof(ngx_http_gzip_conf_t, enable),
132 NULL },
133
134 { ngx_string("gzip_buffers"),
135 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
136 ngx_conf_set_bufs_slot,
137 NGX_HTTP_LOC_CONF_OFFSET,
138 offsetof(ngx_http_gzip_conf_t, bufs),
139 NULL },
140
141 { ngx_string("gzip_comp_level"),
142 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
143 ngx_conf_set_num_slot,
144 NGX_HTTP_LOC_CONF_OFFSET,
145 offsetof(ngx_http_gzip_conf_t, level),
146 &ngx_http_gzip_comp_level_bounds },
147
148 { ngx_string("gzip_window"),
149 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
150 ngx_conf_set_size_slot,
151 NGX_HTTP_LOC_CONF_OFFSET,
152 offsetof(ngx_http_gzip_conf_t, wbits),
153 &ngx_http_gzip_set_window_p },
154
155 { ngx_string("gzip_hash"),
156 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
157 ngx_conf_set_size_slot,
158 NGX_HTTP_LOC_CONF_OFFSET,
159 offsetof(ngx_http_gzip_conf_t, memlevel),
160 &ngx_http_gzip_set_hash_p },
161
162 { ngx_string("gzip_no_buffer"),
163 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
164 ngx_conf_set_flag_slot,
165 NGX_HTTP_LOC_CONF_OFFSET,
166 offsetof(ngx_http_gzip_conf_t, no_buffer),
167 NULL },
168
169 { ngx_string("gzip_http_version"),
170 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY,
171 ngx_conf_set_enum_slot,
172 NGX_HTTP_LOC_CONF_OFFSET,
173 offsetof(ngx_http_gzip_conf_t, http_version),
174 &ngx_http_gzip_http_version },
175
176 { ngx_string("gzip_proxied"),
177 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY,
178 ngx_conf_set_bitmask_slot,
179 NGX_HTTP_LOC_CONF_OFFSET,
180 offsetof(ngx_http_gzip_conf_t, proxied),
181 &ngx_http_gzip_proxied_mask },
182
183 { ngx_string("gzip_min_length"),
184 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
185 ngx_conf_set_size_slot,
186 NGX_HTTP_LOC_CONF_OFFSET,
187 offsetof(ngx_http_gzip_conf_t, min_length),
188 NULL },
189
190 ngx_null_command
191 };
192
193
194 static ngx_http_module_t ngx_http_gzip_filter_module_ctx = {
195 ngx_http_gzip_pre_conf, /* pre conf */
196
197 NULL, /* create main configuration */
198 NULL, /* init main configuration */
199
200 NULL, /* create server configuration */
201 NULL, /* merge server configuration */
202
203 ngx_http_gzip_create_conf, /* create location configuration */
204 ngx_http_gzip_merge_conf, /* merge location configuration */
205 };
206
207
208 ngx_module_t ngx_http_gzip_filter_module = {
209 NGX_MODULE,
210 &ngx_http_gzip_filter_module_ctx, /* module context */
211 ngx_http_gzip_filter_commands, /* module directives */
212 NGX_HTTP_MODULE, /* module type */
213 ngx_http_gzip_filter_init, /* init module */
214 NULL /* init child */
215 };
216
217
218 static ngx_http_log_op_name_t ngx_http_gzip_log_fmt_ops[] = {
219 { ngx_string("gzip_ratio"), NGX_INT32_LEN + 3, ngx_http_gzip_log_ratio },
220 { ngx_null_string, 0, NULL }
221 };
222
223
224
225 static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
226
227 #if (HAVE_LITTLE_ENDIAN)
228
229 struct gztrailer {
230 uint32_t crc32;
231 uint32_t zlen;
232 };
233
234 #else /* HAVE_BIG_ENDIAN */
235
236 struct gztrailer {
237 u_char crc32[4];
238 u_char zlen[4];
239 };
240
241 #endif
242
243
244 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
245 static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
246
247
248 static ngx_int_t ngx_http_gzip_header_filter(ngx_http_request_t *r)
249 {
250 ngx_http_gzip_ctx_t *ctx;
251 ngx_http_gzip_conf_t *conf;
252
253 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
254
255 if (!conf->enable
256 || r->headers_out.status != NGX_HTTP_OK
257 || r->header_only
258 || r->http_version < conf->http_version
259 || (r->headers_out.content_encoding
260 && r->headers_out.content_encoding->value.len)
261 || r->headers_in.accept_encoding == NULL
262 || (r->headers_out.content_length_n != -1
263 && r->headers_out.content_length_n < conf->min_length)
264 || ngx_strstr(r->headers_in.accept_encoding->value.data, "gzip") == NULL
265 )
266 {
267 return ngx_http_next_header_filter(r);
268 }
269
270 /* TODO: "text/html" -> custom types */
271 if (r->headers_out.content_type
272 && ngx_strncasecmp(r->headers_out.content_type->value.data,
273 "text/html", 9) != 0)
274 {
275 return ngx_http_next_header_filter(r);
276 }
277
278
279 if (r->headers_in.via) {
280 if (conf->proxied & NGX_HTTP_GZIP_PROXIED_OFF) {
281 return ngx_http_next_header_filter(r);
282 }
283
284 if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_ANY)
285 && ngx_http_gzip_proxied(r, conf) == NGX_DECLINED)
286 {
287 return ngx_http_next_header_filter(r);
288 }
289 }
290
291
292 /*
293 * if the URL (without the "http://" prefix) is longer than 253 bytes
294 * then MSIE 4.x can not handle the compressed stream - it waits too long,
295 * hangs up or crashes
296 */
297
298 if (r->headers_in.msie4 && r->unparsed_uri.len > 200) {
299 return ngx_http_next_header_filter(r);
300 }
301
302
303 ngx_http_create_ctx(r, ctx, ngx_http_gzip_filter_module,
304 sizeof(ngx_http_gzip_ctx_t), NGX_ERROR);
305 ctx->request = r;
306
307 r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers);
308 if (r->headers_out.content_encoding == NULL) {
309 return NGX_ERROR;
310 }
311
312 r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1;
313 r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding";
314 r->headers_out.content_encoding->value.len = sizeof("gzip") - 1;
315 r->headers_out.content_encoding->value.data = (u_char *) "gzip";
316
317 ctx->length = r->headers_out.content_length_n;
318 r->headers_out.content_length_n = -1;
319 if (r->headers_out.content_length) {
320 r->headers_out.content_length->key.len = 0;
321 r->headers_out.content_length = NULL;
322 }
323 r->filter_need_in_memory = 1;
324
325 return ngx_http_next_header_filter(r);
326 }
327
328
329 static ngx_int_t ngx_http_gzip_proxied(ngx_http_request_t *r,
330 ngx_http_gzip_conf_t *conf)
331 {
332 time_t date, expires;
333
334 if (r->headers_in.authorization
335 && (conf->proxied & NGX_HTTP_GZIP_PROXIED_AUTH))
336 {
337 return NGX_OK;
338 }
339
340 if (r->headers_out.expires) {
341
342 if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_EXPIRED)) {
343 return NGX_DECLINED;
344 }
345
346 expires = ngx_http_parse_time(r->headers_out.expires->value.data,
347 r->headers_out.expires->value.len);
348 if (expires == NGX_ERROR) {
349 return NGX_DECLINED;
350 }
351
352 if (r->headers_out.date) {
353 date = ngx_http_parse_time(r->headers_out.date->value.data,
354 r->headers_out.date->value.len);
355 if (date == NGX_ERROR) {
356 return NGX_DECLINED;
357 }
358
359 } else {
360 date = ngx_time();
361 }
362
363 if (expires < date) {
364 return NGX_OK;
365 }
366
367 return NGX_DECLINED;
368 }
369
370 if (r->headers_out.cache_control) {
371
372 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_CACHE)
373 && ngx_strstr(r->headers_out.cache_control->value.data, "no-cache"))
374 {
375 return NGX_OK;
376 }
377
378 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_STORE)
379 && ngx_strstr(r->headers_out.cache_control->value.data, "no-store"))
380 {
381 return NGX_OK;
382 }
383
384 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_PRIVATE)
385 && ngx_strstr(r->headers_out.cache_control->value.data, "private"))
386 {
387 return NGX_OK;
388 }
389
390 return NGX_DECLINED;
391 }
392
393 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_LM)
394 && r->headers_out.last_modified)
395 {
396 return NGX_DECLINED;
397 }
398
399 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_ETAG)
400 && r->headers_out.etag)
401 {
402 return NGX_DECLINED;
403 }
404
405 return NGX_OK;
406 }
407
408
409 static ngx_int_t ngx_http_gzip_body_filter(ngx_http_request_t *r,
410 ngx_chain_t *in)
411 {
412 int rc, wbits, memlevel, last;
413 struct gztrailer *trailer;
414 ngx_buf_t *b;
415 ngx_chain_t *cl;
416 ngx_http_gzip_ctx_t *ctx;
417 ngx_http_gzip_conf_t *conf;
418
419 ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
420
421 if (ctx == NULL || ctx->done) {
422 return ngx_http_next_body_filter(r, in);
423 }
424
425 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
426
427 if (ctx->preallocated == NULL) {
428 wbits = conf->wbits;
429 memlevel = conf->memlevel;
430
431 if (ctx->length > 0) {
432
433 /* the actual zlib window size is smaller by 262 bytes */
434
435 while (ctx->length < ((1 << (wbits - 1)) - 262)) {
436 wbits--;
437 memlevel--;
438 }
439 }
440
441 /*
442 * We preallocate a memory for zlib in one buffer (200K-400K), this
443 * dicreases a number of malloc() and free() calls and also probably
444 * dicreases a number of syscalls (sbrk() or so).
445 * Besides we free this memory as soon as the gzipping will complete
446 * and do not wait while a whole response will be sent to a client.
447 *
448 * 8K is for zlib deflate_state, it takes
449 * * 5816 bytes on x86 and sparc64 (32-bit mode)
450 * * 5920 bytes on amd64 and sparc64
451 */
452
453 ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
454
455 if (!(ctx->preallocated = ngx_palloc(r->pool, ctx->allocated))) {
456 return NGX_ERROR;
457 }
458
459 ctx->free_mem = ctx->preallocated;
460
461 ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
462 ctx->zstream.zfree = ngx_http_gzip_filter_free;
463 ctx->zstream.opaque = ctx;
464
465 rc = deflateInit2(&ctx->zstream, conf->level, Z_DEFLATED,
466 -wbits, memlevel, Z_DEFAULT_STRATEGY);
467
468 if (rc != Z_OK) {
469 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
470 "deflateInit2() failed: %d", rc);
471 return ngx_http_gzip_error(ctx);
472 }
473
474 if (!(b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)))) {
475 return ngx_http_gzip_error(ctx);
476 }
477
478 b->memory = 1;
479 b->pos = gzheader;
480 b->last = b->pos + 10;
481
482 ngx_alloc_link_and_set_buf(cl, b, r->pool, ngx_http_gzip_error(ctx));
483 ctx->out = cl;
484 ctx->last_out = &cl->next;
485
486 ctx->crc32 = crc32(0L, Z_NULL, 0);
487 ctx->flush = Z_NO_FLUSH;
488 }
489
490 if (in) {
491 if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
492 return ngx_http_gzip_error(ctx);
493 }
494 }
495
496 last = NGX_NONE;
497
498 for ( ;; ) {
499
500 for ( ;; ) {
501
502 /* does zlib need a new data ? */
503
504 if (ctx->zstream.avail_in == 0
505 && ctx->flush == Z_NO_FLUSH
506 && !ctx->redo)
507 {
508 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
509 "gzip in: " PTR_FMT, ctx->in);
510
511 if (ctx->in == NULL) {
512 break;
513 }
514
515 ctx->in_buf = ctx->in->buf;
516 ctx->in = ctx->in->next;
517
518 ctx->zstream.next_in = ctx->in_buf->pos;
519 ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
520
521 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
522 "gzip in_buf:" PTR_FMT " ni:" PTR_FMT " ai:%d",
523 ctx->in_buf,
524 ctx->zstream.next_in, ctx->zstream.avail_in);
525
526 /* STUB */
527 if (ctx->in_buf->last < ctx->in_buf->pos) {
528 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
529 "zstream.avail_in is huge");
530 ctx->done = 1;
531 return NGX_ERROR;
532 }
533 /**/
534
535 if (ctx->in_buf->last_buf) {
536 ctx->flush = Z_FINISH;
537
538 } else if (ctx->in_buf->flush) {
539 ctx->flush = Z_SYNC_FLUSH;
540 }
541
542 if (ctx->zstream.avail_in == 0) {
543 if (ctx->flush == Z_NO_FLUSH) {
544 continue;
545 }
546
547 } else {
548 ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
549 ctx->zstream.avail_in);
550 }
551 }
552
553
554 /* is there a space for the gzipped data ? */
555
556 if (ctx->zstream.avail_out == 0) {
557
558 if (ctx->free) {
559 ctx->out_buf = ctx->free->buf;
560 ctx->free = ctx->free->next;
561
562 } else if (ctx->bufs < conf->bufs.num) {
563 ctx->out_buf = ngx_create_temp_buf(r->pool,
564 conf->bufs.size);
565 if (ctx->out_buf == NULL) {
566 return ngx_http_gzip_error(ctx);
567 }
568
569 ctx->out_buf->tag = (ngx_buf_tag_t)
570 &ngx_http_gzip_filter_module;
571 ctx->out_buf->recycled = 1;
572 ctx->bufs++;
573
574 } else {
575 #if 0
576 ctx->blocked = 1;
577 #endif
578 break;
579 }
580
581 #if 0
582 ctx->blocked = 0;
583 #endif
584 ctx->zstream.next_out = ctx->out_buf->pos;
585 ctx->zstream.avail_out = conf->bufs.size;
586 }
587
588 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
589 "deflate in: ni:%X no:%X ai:%d ao:%d fl:%d redo:%d",
590 ctx->zstream.next_in, ctx->zstream.next_out,
591 ctx->zstream.avail_in, ctx->zstream.avail_out,
592 ctx->flush, ctx->redo);
593
594 rc = deflate(&ctx->zstream, ctx->flush);
595
596 if (rc != Z_OK && rc != Z_STREAM_END) {
597 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
598 "deflate() failed: %d, %d", ctx->flush, rc);
599 return ngx_http_gzip_error(ctx);
600 }
601
602 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
603 "deflate out: ni:%X no:%X ai:%d ao:%d rc:%d",
604 ctx->zstream.next_in, ctx->zstream.next_out,
605 ctx->zstream.avail_in, ctx->zstream.avail_out,
606 rc);
607
608 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
609 "gzip in_buf:" PTR_FMT " pos:" PTR_FMT,
610 ctx->in_buf, ctx->in_buf->pos);
611
612
613 if (ctx->zstream.next_in) {
614 ctx->in_buf->pos = ctx->zstream.next_in;
615
616 if (ctx->zstream.avail_in == 0) {
617 ctx->zstream.next_in = NULL;
618 }
619 }
620
621 ctx->out_buf->last = ctx->zstream.next_out;
622
623 if (ctx->zstream.avail_out == 0) {
624
625 /* zlib wants to output some more gzipped data */
626
627 ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool,
628 ngx_http_gzip_error(ctx));
629 *ctx->last_out = cl;
630 ctx->last_out = &cl->next;
631
632 ctx->redo = 1;
633
634 continue;
635 }
636
637 ctx->redo = 0;
638
639 if (ctx->flush == Z_SYNC_FLUSH) {
640
641 ctx->out_buf->flush = 0;
642 ctx->flush = Z_NO_FLUSH;
643
644 ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool,
645 ngx_http_gzip_error(ctx));
646 *ctx->last_out = cl;
647 ctx->last_out = &cl->next;
648
649 #if 0
650 ctx->pass = 1;
651 #endif
652
653 break;
654 }
655
656 if (rc == Z_STREAM_END) {
657
658 ctx->zin = ctx->zstream.total_in;
659 ctx->zout = 10 + ctx->zstream.total_out + 8;
660
661 rc = deflateEnd(&ctx->zstream);
662
663 if (rc != Z_OK) {
664 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
665 "deflateEnd() failed: %d", rc);
666 return ngx_http_gzip_error(ctx);
667 }
668
669 ngx_pfree(r->pool, ctx->preallocated);
670
671 ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool,
672 ngx_http_gzip_error(ctx));
673 *ctx->last_out = cl;
674 ctx->last_out = &cl->next;
675
676 if (ctx->zstream.avail_out >= 8) {
677 trailer = (struct gztrailer *) ctx->out_buf->last;
678 ctx->out_buf->last += 8;
679 ctx->out_buf->last_buf = 1;
680
681 } else {
682 if (!(b = ngx_create_temp_buf(r->pool, 8))) {
683 return ngx_http_gzip_error(ctx);
684 }
685
686 b->last_buf = 1;
687
688 ngx_alloc_link_and_set_buf(cl, b, r->pool,
689 ngx_http_gzip_error(ctx));
690 *ctx->last_out = cl;
691 ctx->last_out = &cl->next;
692 trailer = (struct gztrailer *) b->pos;
693 b->last += 8;
694 }
695
696 #if (HAVE_LITTLE_ENDIAN)
697 trailer->crc32 = ctx->crc32;
698 trailer->zlen = ctx->zin;
699 #else
700 trailer->crc32[0] = ctx->crc32 & 0xff;
701 trailer->crc32[1] = (ctx->crc32 >> 8) & 0xff;
702 trailer->crc32[2] = (ctx->crc32 >> 16) & 0xff;
703 trailer->crc32[3] = (ctx->crc32 >> 24) & 0xff;
704
705 trailer->zlen[0] = ctx->zin & 0xff;
706 trailer->zlen[1] = (ctx->zin >> 8) & 0xff;
707 trailer->zlen[2] = (ctx->zin >> 16) & 0xff;
708 trailer->zlen[3] = (ctx->zin >> 24) & 0xff;
709 #endif
710
711 ctx->zstream.avail_in = 0;
712 ctx->zstream.avail_out = 0;
713
714 ctx->done = 1;
715 #if 0
716 ctx->pass = 1;
717 #endif
718
719 break;
720 }
721
722 if (conf->no_buffer && ctx->in == NULL) {
723 ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool,
724 ngx_http_gzip_error(ctx));
725 *ctx->last_out = cl;
726 ctx->last_out = &cl->next;
727
728 #if 0
729 ctx->pass = 1;
730 #endif
731
732 break;
733 }
734 }
735
736 #if 0
737
738 /* OLD CODE */
739
740 if (ctx->out) {
741 if (ctx->pass) {
742 ctx->pass = 0;
743
744 } else if (last == NGX_AGAIN) {
745 return last;
746 }
747
748 } else if (ctx->busy->buf && ngx_buf_size(ctx->busy->buf)) {
749 if (last != NGX_NONE) {
750 return last;
751 }
752
753 } else if (ctx->blocked) {
754 if (last != NGX_NONE) {
755 return last;
756 }
757
758 } else {
759 if (last == NGX_NONE) {
760 return NGX_OK;
761 }
762
763 return last;
764 }
765 #endif
766
767 /* NEW CODE */
768
769 if (last == NGX_AGAIN) {
770 return NGX_AGAIN;
771 }
772
773 if (ctx->out == NULL && ctx->busy == NULL) {
774 return NGX_OK;
775 }
776
777 /**/
778
779 last = ngx_http_next_body_filter(r, ctx->out);
780
781 /*
782 * we do not check NGX_AGAIN here because the downstream filters
783 * may free some buffers and zlib may compress some data into them
784 */
785
786 if (last == NGX_ERROR) {
787 return ngx_http_gzip_error(ctx);
788 }
789
790 ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out,
791 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
792 ctx->last_out = &ctx->out;
793
794 if (ctx->done) {
795 return last;
796 }
797 }
798 }
799
800
801 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size)
802 {
803 ngx_http_gzip_ctx_t *ctx = opaque;
804
805 void *p;
806 ngx_uint_t alloc;
807
808 alloc = items * size;
809 if (alloc % 512 != 0) {
810
811 /*
812 * the zlib deflate_state allocation, it takes about 6K, we allocate 8K
813 */
814
815 alloc = (alloc + ngx_pagesize - 1) & ~(ngx_pagesize - 1);
816 }
817
818 if (alloc <= ctx->allocated) {
819 p = ctx->free_mem;
820 ctx->free_mem += alloc;
821 ctx->allocated -= alloc;
822
823 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
824 "gzip alloc: n:%d s:%d a:%d p:" PTR_FMT,
825 items, size, alloc, p);
826
827 return p;
828 }
829
830 ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0,
831 "gzip filter failed to use preallocated memory: %d of %d",
832 items * size, ctx->allocated);
833
834 p = ngx_palloc(ctx->request->pool, items * size);
835
836 return p;
837 }
838
839
840 static void ngx_http_gzip_filter_free(void *opaque, void *address)
841 {
842 #if 0
843 ngx_http_gzip_ctx_t *ctx = opaque;
844
845 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
846 "gzip free: %X", address);
847 #endif
848 }
849
850
851 static u_char *ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf,
852 uintptr_t data)
853 {
854 ngx_uint_t zint, zfrac;
855 ngx_http_gzip_ctx_t *ctx;
856
857 ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
858
859 if (ctx == NULL || ctx->zout == 0) {
860 *buf = '-';
861 return buf + 1;
862 }
863
864 #if 0
865 return buf + ngx_snprintf((char *) buf, NGX_INT32_LEN + 4, "%.2f",
866 (float) ctx->zin / ctx->zout);
867 #endif
868
869 /* we prefer do not use FPU */
870
871 zint = (ngx_uint_t) (ctx->zin / ctx->zout);
872 zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);
873
874 if ((ctx->zin * 1000 / ctx->zout) %10 > 4) {
875 if (++zfrac > 99) {
876 zint++;
877 zfrac = 0;
878 }
879 }
880
881 return buf + ngx_snprintf((char *) buf, NGX_INT32_LEN + 4,
882 "%" NGX_UINT_T_FMT ".%02" NGX_UINT_T_FMT,
883 zint, zfrac);
884 }
885
886
887 ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx)
888 {
889 deflateEnd(&ctx->zstream);
890
891 ngx_pfree(ctx->request->pool, ctx->preallocated);
892
893 ctx->zstream.avail_in = 0;
894 ctx->zstream.avail_out = 0;
895
896 ctx->done = 1;
897
898 return NGX_ERROR;
899 }
900
901
902 static ngx_int_t ngx_http_gzip_pre_conf(ngx_conf_t *cf)
903 {
904 ngx_http_log_op_name_t *op;
905
906 for (op = ngx_http_gzip_log_fmt_ops; op->name.len; op++) { /* void */ }
907 op->op = NULL;
908
909 op = ngx_http_log_fmt_ops;
910
911 for (op = ngx_http_log_fmt_ops; op->op; op++) {
912 if (op->name.len == 0) {
913 op = (ngx_http_log_op_name_t *) op->op;
914 }
915 }
916
917 op->op = (ngx_http_log_op_pt) ngx_http_gzip_log_fmt_ops;
918
919 return NGX_OK;
920 }
921
922
923 static ngx_int_t ngx_http_gzip_filter_init(ngx_cycle_t *cycle)
924 {
925 ngx_http_next_header_filter = ngx_http_top_header_filter;
926 ngx_http_top_header_filter = ngx_http_gzip_header_filter;
927
928 ngx_http_next_body_filter = ngx_http_top_body_filter;
929 ngx_http_top_body_filter = ngx_http_gzip_body_filter;
930
931 return NGX_OK;
932 }
933
934
935 static void *ngx_http_gzip_create_conf(ngx_conf_t *cf)
936 {
937 ngx_http_gzip_conf_t *conf;
938
939 if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t)))) {
940 return NGX_CONF_ERROR;
941 }
942
943 /*
944
945 set by ngx_pcalloc():
946
947 conf->bufs.num = 0;
948 conf->proxied = 0;
949
950 */
951
952 conf->enable = NGX_CONF_UNSET;
953 conf->no_buffer = NGX_CONF_UNSET;
954
955 conf->http_version = NGX_CONF_UNSET_UINT;
956
957 conf->level = NGX_CONF_UNSET;
958 conf->wbits = (size_t) NGX_CONF_UNSET;
959 conf->memlevel = (size_t) NGX_CONF_UNSET;
960 conf->min_length = NGX_CONF_UNSET;
961
962 return conf;
963 }
964
965
966 static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
967 void *parent, void *child)
968 {
969 ngx_http_gzip_conf_t *prev = parent;
970 ngx_http_gzip_conf_t *conf = child;
971
972 ngx_conf_merge_value(conf->enable, prev->enable, 0);
973
974 ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize);
975
976 ngx_conf_merge_unsigned_value(conf->http_version, prev->http_version,
977 NGX_HTTP_VERSION_11);
978 ngx_conf_merge_bitmask_value(conf->proxied, prev->proxied,
979 (NGX_CONF_BITMASK_SET
980 |NGX_HTTP_GZIP_PROXIED_OFF));
981
982 ngx_conf_merge_value(conf->level, prev->level, 1);
983 ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
984 ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,
985 MAX_MEM_LEVEL - 1);
986 ngx_conf_merge_value(conf->min_length, prev->min_length, 0);
987 ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);
988
989 return NGX_CONF_OK;
990 }
991
992
993 static char *ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data)
994 {
995 int *np = data;
996
997 int wbits, wsize;
998
999 wbits = 15;
1000
1001 for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {
1002
1003 if (wsize == *np) {
1004 *np = wbits;
1005
1006 return NGX_CONF_OK;
1007 }
1008
1009 wbits--;
1010 }
1011
1012 return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k";
1013 }
1014
1015
1016 static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data)
1017 {
1018 int *np = data;
1019
1020 int memlevel, hsize;
1021
1022 memlevel = 9;
1023
1024 for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) {
1025
1026 if (hsize == *np) {
1027 *np = memlevel;
1028
1029 return NGX_CONF_OK;
1030 }
1031
1032 memlevel--;
1033 }
1034
1035 return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k";
1036 }