Annotation of embedaddon/nginx/src/http/modules/ngx_http_gunzip_filter_module.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (C) Igor Sysoev
4: * Copyright (C) Maxim Dounin
5: * Copyright (C) Nginx, Inc.
6: */
7:
8:
9: #include <ngx_config.h>
10: #include <ngx_core.h>
11: #include <ngx_http.h>
12:
13: #include <zlib.h>
14:
15:
16: typedef struct {
17: ngx_flag_t enable;
18: ngx_bufs_t bufs;
19: } ngx_http_gunzip_conf_t;
20:
21:
22: typedef struct {
23: ngx_chain_t *in;
24: ngx_chain_t *free;
25: ngx_chain_t *busy;
26: ngx_chain_t *out;
27: ngx_chain_t **last_out;
28:
29: ngx_buf_t *in_buf;
30: ngx_buf_t *out_buf;
31: ngx_int_t bufs;
32:
33: unsigned started:1;
34: unsigned flush:4;
35: unsigned redo:1;
36: unsigned done:1;
37: unsigned nomem:1;
38:
39: z_stream zstream;
40: ngx_http_request_t *request;
41: } ngx_http_gunzip_ctx_t;
42:
43:
44: static ngx_int_t ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
45: ngx_http_gunzip_ctx_t *ctx);
46: static ngx_int_t ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,
47: ngx_http_gunzip_ctx_t *ctx);
48: static ngx_int_t ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,
49: ngx_http_gunzip_ctx_t *ctx);
50: static ngx_int_t ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,
51: ngx_http_gunzip_ctx_t *ctx);
52: static ngx_int_t ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,
53: ngx_http_gunzip_ctx_t *ctx);
54:
55: static void *ngx_http_gunzip_filter_alloc(void *opaque, u_int items,
56: u_int size);
57: static void ngx_http_gunzip_filter_free(void *opaque, void *address);
58:
59: static ngx_int_t ngx_http_gunzip_filter_init(ngx_conf_t *cf);
60: static void *ngx_http_gunzip_create_conf(ngx_conf_t *cf);
61: static char *ngx_http_gunzip_merge_conf(ngx_conf_t *cf,
62: void *parent, void *child);
63:
64:
65: static ngx_command_t ngx_http_gunzip_filter_commands[] = {
66:
67: { ngx_string("gunzip"),
68: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
69: ngx_conf_set_flag_slot,
70: NGX_HTTP_LOC_CONF_OFFSET,
71: offsetof(ngx_http_gunzip_conf_t, enable),
72: NULL },
73:
74: { ngx_string("gunzip_buffers"),
75: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
76: ngx_conf_set_bufs_slot,
77: NGX_HTTP_LOC_CONF_OFFSET,
78: offsetof(ngx_http_gunzip_conf_t, bufs),
79: NULL },
80:
81: ngx_null_command
82: };
83:
84:
85: static ngx_http_module_t ngx_http_gunzip_filter_module_ctx = {
86: NULL, /* preconfiguration */
87: ngx_http_gunzip_filter_init, /* postconfiguration */
88:
89: NULL, /* create main configuration */
90: NULL, /* init main configuration */
91:
92: NULL, /* create server configuration */
93: NULL, /* merge server configuration */
94:
95: ngx_http_gunzip_create_conf, /* create location configuration */
96: ngx_http_gunzip_merge_conf /* merge location configuration */
97: };
98:
99:
100: ngx_module_t ngx_http_gunzip_filter_module = {
101: NGX_MODULE_V1,
102: &ngx_http_gunzip_filter_module_ctx, /* module context */
103: ngx_http_gunzip_filter_commands, /* module directives */
104: NGX_HTTP_MODULE, /* module type */
105: NULL, /* init master */
106: NULL, /* init module */
107: NULL, /* init process */
108: NULL, /* init thread */
109: NULL, /* exit thread */
110: NULL, /* exit process */
111: NULL, /* exit master */
112: NGX_MODULE_V1_PADDING
113: };
114:
115:
116: static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
117: static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
118:
119:
120: static ngx_int_t
121: ngx_http_gunzip_header_filter(ngx_http_request_t *r)
122: {
123: ngx_http_gunzip_ctx_t *ctx;
124: ngx_http_gunzip_conf_t *conf;
125:
126: conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);
127:
128: /* TODO support multiple content-codings */
129: /* TODO always gunzip - due to configuration or module request */
130: /* TODO ignore content encoding? */
131:
132: if (!conf->enable
133: || r->headers_out.content_encoding == NULL
134: || r->headers_out.content_encoding->value.len != 4
135: || ngx_strncasecmp(r->headers_out.content_encoding->value.data,
136: (u_char *) "gzip", 4) != 0)
137: {
138: return ngx_http_next_header_filter(r);
139: }
140:
141: r->gzip_vary = 1;
142:
143: if (!r->gzip_tested) {
144: if (ngx_http_gzip_ok(r) == NGX_OK) {
145: return ngx_http_next_header_filter(r);
146: }
147:
148: } else if (r->gzip_ok) {
149: return ngx_http_next_header_filter(r);
150: }
151:
152: ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gunzip_ctx_t));
153: if (ctx == NULL) {
154: return NGX_ERROR;
155: }
156:
157: ngx_http_set_ctx(r, ctx, ngx_http_gunzip_filter_module);
158:
159: ctx->request = r;
160:
161: r->filter_need_in_memory = 1;
162:
163: r->headers_out.content_encoding->hash = 0;
164: r->headers_out.content_encoding = NULL;
165:
166: ngx_http_clear_content_length(r);
167: ngx_http_clear_accept_ranges(r);
168: ngx_http_clear_etag(r);
169:
170: return ngx_http_next_header_filter(r);
171: }
172:
173:
174: static ngx_int_t
175: ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
176: {
177: int rc;
178: ngx_chain_t *cl;
179: ngx_http_gunzip_ctx_t *ctx;
180:
181: ctx = ngx_http_get_module_ctx(r, ngx_http_gunzip_filter_module);
182:
183: if (ctx == NULL || ctx->done) {
184: return ngx_http_next_body_filter(r, in);
185: }
186:
187: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
188: "http gunzip filter");
189:
190: if (!ctx->started) {
191: if (ngx_http_gunzip_filter_inflate_start(r, ctx) != NGX_OK) {
192: goto failed;
193: }
194: }
195:
196: if (in) {
197: if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
198: goto failed;
199: }
200: }
201:
202: if (ctx->nomem) {
203:
204: /* flush busy buffers */
205:
206: if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
207: goto failed;
208: }
209:
210: cl = NULL;
211:
212: ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
213: (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
214: ctx->nomem = 0;
215: }
216:
217: for ( ;; ) {
218:
219: /* cycle while we can write to a client */
220:
221: for ( ;; ) {
222:
223: /* cycle while there is data to feed zlib and ... */
224:
225: rc = ngx_http_gunzip_filter_add_data(r, ctx);
226:
227: if (rc == NGX_DECLINED) {
228: break;
229: }
230:
231: if (rc == NGX_AGAIN) {
232: continue;
233: }
234:
235:
236: /* ... there are buffers to write zlib output */
237:
238: rc = ngx_http_gunzip_filter_get_buf(r, ctx);
239:
240: if (rc == NGX_DECLINED) {
241: break;
242: }
243:
244: if (rc == NGX_ERROR) {
245: goto failed;
246: }
247:
248: rc = ngx_http_gunzip_filter_inflate(r, ctx);
249:
250: if (rc == NGX_OK) {
251: break;
252: }
253:
254: if (rc == NGX_ERROR) {
255: goto failed;
256: }
257:
258: /* rc == NGX_AGAIN */
259: }
260:
261: if (ctx->out == NULL) {
262: return ctx->busy ? NGX_AGAIN : NGX_OK;
263: }
264:
265: rc = ngx_http_next_body_filter(r, ctx->out);
266:
267: if (rc == NGX_ERROR) {
268: goto failed;
269: }
270:
271: ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
272: (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
273: ctx->last_out = &ctx->out;
274:
275: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
276: "gunzip out: %p", ctx->out);
277:
278: ctx->nomem = 0;
279:
280: if (ctx->done) {
281: return rc;
282: }
283: }
284:
285: /* unreachable */
286:
287: failed:
288:
289: ctx->done = 1;
290:
291: return NGX_ERROR;
292: }
293:
294:
295: static ngx_int_t
296: ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
297: ngx_http_gunzip_ctx_t *ctx)
298: {
299: int rc;
300:
301: ctx->zstream.next_in = Z_NULL;
302: ctx->zstream.avail_in = 0;
303:
304: ctx->zstream.zalloc = ngx_http_gunzip_filter_alloc;
305: ctx->zstream.zfree = ngx_http_gunzip_filter_free;
306: ctx->zstream.opaque = ctx;
307:
308: /* windowBits +16 to decode gzip, zlib 1.2.0.4+ */
309: rc = inflateInit2(&ctx->zstream, MAX_WBITS + 16);
310:
311: if (rc != Z_OK) {
312: ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
313: "inflateInit2() failed: %d", rc);
314: return NGX_ERROR;
315: }
316:
317: ctx->started = 1;
318:
319: ctx->last_out = &ctx->out;
320: ctx->flush = Z_NO_FLUSH;
321:
322: return NGX_OK;
323: }
324:
325:
326: static ngx_int_t
327: ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,
328: ngx_http_gunzip_ctx_t *ctx)
329: {
330: if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
331: return NGX_OK;
332: }
333:
334: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
335: "gunzip in: %p", ctx->in);
336:
337: if (ctx->in == NULL) {
338: return NGX_DECLINED;
339: }
340:
341: ctx->in_buf = ctx->in->buf;
342: ctx->in = ctx->in->next;
343:
344: ctx->zstream.next_in = ctx->in_buf->pos;
345: ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
346:
347: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
348: "gunzip in_buf:%p ni:%p ai:%ud",
349: ctx->in_buf,
350: ctx->zstream.next_in, ctx->zstream.avail_in);
351:
352: if (ctx->in_buf->last_buf || ctx->in_buf->last_in_chain) {
353: ctx->flush = Z_FINISH;
354:
355: } else if (ctx->in_buf->flush) {
356: ctx->flush = Z_SYNC_FLUSH;
357:
358: } else if (ctx->zstream.avail_in == 0) {
359: /* ctx->flush == Z_NO_FLUSH */
360: return NGX_AGAIN;
361: }
362:
363: return NGX_OK;
364: }
365:
366:
367: static ngx_int_t
368: ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,
369: ngx_http_gunzip_ctx_t *ctx)
370: {
371: ngx_http_gunzip_conf_t *conf;
372:
373: if (ctx->zstream.avail_out) {
374: return NGX_OK;
375: }
376:
377: conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);
378:
379: if (ctx->free) {
380: ctx->out_buf = ctx->free->buf;
381: ctx->free = ctx->free->next;
382:
383: ctx->out_buf->flush = 0;
384:
385: } else if (ctx->bufs < conf->bufs.num) {
386:
387: ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
388: if (ctx->out_buf == NULL) {
389: return NGX_ERROR;
390: }
391:
392: ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gunzip_filter_module;
393: ctx->out_buf->recycled = 1;
394: ctx->bufs++;
395:
396: } else {
397: ctx->nomem = 1;
398: return NGX_DECLINED;
399: }
400:
401: ctx->zstream.next_out = ctx->out_buf->pos;
402: ctx->zstream.avail_out = conf->bufs.size;
403:
404: return NGX_OK;
405: }
406:
407:
408: static ngx_int_t
409: ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,
410: ngx_http_gunzip_ctx_t *ctx)
411: {
412: int rc;
413: ngx_buf_t *b;
414: ngx_chain_t *cl;
415:
416: ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
417: "inflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
418: ctx->zstream.next_in, ctx->zstream.next_out,
419: ctx->zstream.avail_in, ctx->zstream.avail_out,
420: ctx->flush, ctx->redo);
421:
422: rc = inflate(&ctx->zstream, ctx->flush);
423:
424: if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {
425: ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
426: "inflate() failed: %d, %d", ctx->flush, rc);
427: return NGX_ERROR;
428: }
429:
430: ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
431: "inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
432: ctx->zstream.next_in, ctx->zstream.next_out,
433: ctx->zstream.avail_in, ctx->zstream.avail_out,
434: rc);
435:
436: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
437: "gunzip in_buf:%p pos:%p",
438: ctx->in_buf, ctx->in_buf->pos);
439:
440: if (ctx->zstream.next_in) {
441: ctx->in_buf->pos = ctx->zstream.next_in;
442:
443: if (ctx->zstream.avail_in == 0) {
444: ctx->zstream.next_in = NULL;
445: }
446: }
447:
448: ctx->out_buf->last = ctx->zstream.next_out;
449:
450: if (ctx->zstream.avail_out == 0) {
451:
452: /* zlib wants to output some more data */
453:
454: cl = ngx_alloc_chain_link(r->pool);
455: if (cl == NULL) {
456: return NGX_ERROR;
457: }
458:
459: cl->buf = ctx->out_buf;
460: cl->next = NULL;
461: *ctx->last_out = cl;
462: ctx->last_out = &cl->next;
463:
464: ctx->redo = 1;
465:
466: return NGX_AGAIN;
467: }
468:
469: ctx->redo = 0;
470:
471: if (ctx->flush == Z_SYNC_FLUSH) {
472:
473: ctx->flush = Z_NO_FLUSH;
474:
475: cl = ngx_alloc_chain_link(r->pool);
476: if (cl == NULL) {
477: return NGX_ERROR;
478: }
479:
480: b = ctx->out_buf;
481:
482: if (ngx_buf_size(b) == 0) {
483:
484: b = ngx_calloc_buf(ctx->request->pool);
485: if (b == NULL) {
486: return NGX_ERROR;
487: }
488:
489: } else {
490: ctx->zstream.avail_out = 0;
491: }
492:
493: b->flush = 1;
494:
495: cl->buf = b;
496: cl->next = NULL;
497: *ctx->last_out = cl;
498: ctx->last_out = &cl->next;
499:
500: return NGX_OK;
501: }
502:
503: if (rc == Z_STREAM_END && ctx->flush == Z_FINISH
504: && ctx->zstream.avail_in == 0)
505: {
506:
507: if (ngx_http_gunzip_filter_inflate_end(r, ctx) != NGX_OK) {
508: return NGX_ERROR;
509: }
510:
511: return NGX_OK;
512: }
513:
514: if (rc == Z_STREAM_END && ctx->zstream.avail_in > 0) {
515:
516: rc = inflateReset(&ctx->zstream);
517:
518: if (rc != Z_OK) {
519: ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
520: "inflateReset() failed: %d", rc);
521: return NGX_ERROR;
522: }
523:
524: ctx->redo = 1;
525:
526: return NGX_AGAIN;
527: }
528:
529: if (ctx->in == NULL) {
530:
531: b = ctx->out_buf;
532:
533: if (ngx_buf_size(b) == 0) {
534: return NGX_OK;
535: }
536:
537: cl = ngx_alloc_chain_link(r->pool);
538: if (cl == NULL) {
539: return NGX_ERROR;
540: }
541:
542: ctx->zstream.avail_out = 0;
543:
544: cl->buf = b;
545: cl->next = NULL;
546: *ctx->last_out = cl;
547: ctx->last_out = &cl->next;
548:
549: return NGX_OK;
550: }
551:
552: return NGX_AGAIN;
553: }
554:
555:
556: static ngx_int_t
557: ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,
558: ngx_http_gunzip_ctx_t *ctx)
559: {
560: int rc;
561: ngx_buf_t *b;
562: ngx_chain_t *cl;
563:
564: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
565: "gunzip inflate end");
566:
567: rc = inflateEnd(&ctx->zstream);
568:
569: if (rc != Z_OK) {
570: ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
571: "inflateEnd() failed: %d", rc);
572: return NGX_ERROR;
573: }
574:
575: b = ctx->out_buf;
576:
577: if (ngx_buf_size(b) == 0) {
578:
579: b = ngx_calloc_buf(ctx->request->pool);
580: if (b == NULL) {
581: return NGX_ERROR;
582: }
583: }
584:
585: cl = ngx_alloc_chain_link(r->pool);
586: if (cl == NULL) {
587: return NGX_ERROR;
588: }
589:
590: cl->buf = b;
591: cl->next = NULL;
592: *ctx->last_out = cl;
593: ctx->last_out = &cl->next;
594:
595: b->last_buf = (r == r->main) ? 1 : 0;
596: b->last_in_chain = 1;
597: b->sync = 1;
598:
599: ctx->done = 1;
600:
601: return NGX_OK;
602: }
603:
604:
605: static void *
606: ngx_http_gunzip_filter_alloc(void *opaque, u_int items, u_int size)
607: {
608: ngx_http_gunzip_ctx_t *ctx = opaque;
609:
610: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
611: "gunzip alloc: n:%ud s:%ud",
612: items, size);
613:
614: return ngx_palloc(ctx->request->pool, items * size);
615: }
616:
617:
618: static void
619: ngx_http_gunzip_filter_free(void *opaque, void *address)
620: {
621: #if 0
622: ngx_http_gunzip_ctx_t *ctx = opaque;
623:
624: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
625: "gunzip free: %p", address);
626: #endif
627: }
628:
629:
630: static void *
631: ngx_http_gunzip_create_conf(ngx_conf_t *cf)
632: {
633: ngx_http_gunzip_conf_t *conf;
634:
635: conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gunzip_conf_t));
636: if (conf == NULL) {
637: return NULL;
638: }
639:
640: /*
641: * set by ngx_pcalloc():
642: *
643: * conf->bufs.num = 0;
644: */
645:
646: conf->enable = NGX_CONF_UNSET;
647:
648: return conf;
649: }
650:
651:
652: static char *
653: ngx_http_gunzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
654: {
655: ngx_http_gunzip_conf_t *prev = parent;
656: ngx_http_gunzip_conf_t *conf = child;
657:
658: ngx_conf_merge_value(conf->enable, prev->enable, 0);
659:
660: ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
661: (128 * 1024) / ngx_pagesize, ngx_pagesize);
662:
663: return NGX_CONF_OK;
664: }
665:
666:
667: static ngx_int_t
668: ngx_http_gunzip_filter_init(ngx_conf_t *cf)
669: {
670: ngx_http_next_header_filter = ngx_http_top_header_filter;
671: ngx_http_top_header_filter = ngx_http_gunzip_header_filter;
672:
673: ngx_http_next_body_filter = ngx_http_top_body_filter;
674: ngx_http_top_body_filter = ngx_http_gunzip_body_filter;
675:
676: return NGX_OK;
677: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>