Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_gunzip_filter_module.c
Go to the documentation of this file.
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 {
20 
21 
22 typedef struct {
28 
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;
42 
43 
44 static ngx_int_t ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
46 static ngx_int_t ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,
48 static ngx_int_t ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,
50 static ngx_int_t ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,
52 static ngx_int_t ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,
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"),
71  offsetof(ngx_http_gunzip_conf_t, enable),
72  NULL },
73 
74  { ngx_string("gunzip_buffers"),
78  offsetof(ngx_http_gunzip_conf_t, bufs),
79  NULL },
80 
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 
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 */
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 {
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
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 
164  r->headers_out.content_encoding = NULL;
165 
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;
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 
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 
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,
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) {
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,
329 {
330  if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
331  return NGX_OK;
332  }
333 
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 
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,
370 {
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,
411 {
412  int rc;
413  ngx_buf_t *b;
414  ngx_chain_t *cl;
415 
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) {
426  "inflate() failed: %d, %d", ctx->flush, rc);
427  return NGX_ERROR;
428  }
429 
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 
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) {
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,
559 {
560  int rc;
561  ngx_buf_t *b;
562  ngx_chain_t *cl;
563 
565  "gunzip inflate end");
566 
567  rc = inflateEnd(&ctx->zstream);
568 
569  if (rc != Z_OK) {
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 
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 
625  "gunzip free: %p", address);
626 #endif
627 }
628 
629 
630 static void *
631 ngx_http_gunzip_create_conf(ngx_conf_t *cf)
632 {
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 }