Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_gzip_filter_module.c
Go to the documentation of this file.
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 {
18 
20 
22 
25  size_t wbits;
26  size_t memlevel;
27  ssize_t min_length;
28 
31 
32 
33 typedef struct {
39 
42 
46 
47  void *preallocated;
48  char *free_mem;
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;
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 = {
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"),
134  |NGX_CONF_FLAG,
137  offsetof(ngx_http_gzip_conf_t, enable),
138  NULL },
139 
140  { ngx_string("gzip_buffers"),
144  offsetof(ngx_http_gzip_conf_t, bufs),
145  NULL },
146 
147  { ngx_string("gzip_types"),
151  offsetof(ngx_http_gzip_conf_t, types_keys),
153 
154  { ngx_string("gzip_comp_level"),
158  offsetof(ngx_http_gzip_conf_t, level),
159  &ngx_http_gzip_comp_level_bounds },
160 
161  { ngx_string("gzip_window"),
165  offsetof(ngx_http_gzip_conf_t, wbits),
166  &ngx_http_gzip_window_p },
167 
168  { ngx_string("gzip_hash"),
172  offsetof(ngx_http_gzip_conf_t, memlevel),
173  &ngx_http_gzip_hash_p },
174 
175  { ngx_string("postpone_gzipping"),
179  offsetof(ngx_http_gzip_conf_t, postpone_gzipping),
180  NULL },
181 
182  { ngx_string("gzip_no_buffer"),
186  offsetof(ngx_http_gzip_conf_t, no_buffer),
187  NULL },
188 
189  { ngx_string("gzip_min_length"),
193  offsetof(ngx_http_gzip_conf_t, min_length),
194  NULL },
195 
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 
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 */
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;
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
252  || (r->headers_out.content_length_n != -1
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  {
265 
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 
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");
304 
306 
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 
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;
533  ngx_http_gzip_conf_t *conf;
534 
535  r = ctx->request;
536 
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) {
619  "deflateInit2() failed: %d", rc);
620  return NGX_ERROR;
621  }
622 
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 
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 
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 
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) {
783  "deflate() failed: %d, %d", ctx->flush, rc);
784  return NGX_ERROR;
785  }
786 
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 
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) {
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 
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 
1004  "gzip alloc: n:%ud s:%ud a:%ud p:%p",
1005  items, size, alloc, p);
1006 
1007  return p;
1008  }
1009 
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 
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 
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 
1150  0);
1151  ngx_conf_merge_value(conf->level, prev->level, 1);
1152  ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
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,
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 }