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