comparison src/http/modules/ngx_http_gzip_filter_module.c @ 441:fd759445d8a8 NGINX_0_7_28

nginx 0.7.28 *) Change: in memory allocation in the ngx_http_gzip_filter_module. *) Change: the default "gzip_buffers" directive values have been changed to 32 4k or 16 8k from 4 4k/8k.
author Igor Sysoev <http://sysoev.ru>
date Mon, 22 Dec 2008 00:00:00 +0300
parents b4f69f2ef02c
children 49a0eb7ce20c
comparison
equal deleted inserted replaced
440:e2c4e8b635a8 441:fd759445d8a8
32 ngx_chain_t *in; 32 ngx_chain_t *in;
33 ngx_chain_t *free; 33 ngx_chain_t *free;
34 ngx_chain_t *busy; 34 ngx_chain_t *busy;
35 ngx_chain_t *out; 35 ngx_chain_t *out;
36 ngx_chain_t **last_out; 36 ngx_chain_t **last_out;
37
38 ngx_chain_t *copied;
39 ngx_chain_t *copy_buf;
40
37 ngx_buf_t *in_buf; 41 ngx_buf_t *in_buf;
38 ngx_buf_t *out_buf; 42 ngx_buf_t *out_buf;
39 ngx_int_t bufs; 43 ngx_int_t bufs;
40 44
41 off_t length;
42
43 void *preallocated; 45 void *preallocated;
44 char *free_mem; 46 char *free_mem;
45 ngx_uint_t allocated; 47 ngx_uint_t allocated;
48
49 int wbits;
50 int memlevel;
46 51
47 unsigned flush:4; 52 unsigned flush:4;
48 unsigned redo:1; 53 unsigned redo:1;
49 unsigned done:1; 54 unsigned done:1;
50 unsigned nomem:1; 55 unsigned nomem:1;
51 unsigned gzheader:1; 56 unsigned gzheader:1;
57 unsigned buffering:1;
52 58
53 size_t zin; 59 size_t zin;
54 size_t zout; 60 size_t zout;
55 61
56 uint32_t crc32; 62 uint32_t crc32;
74 }; 80 };
75 81
76 #endif 82 #endif
77 83
78 84
85 static void ngx_http_gzip_filter_memory(ngx_http_request_t *r,
86 ngx_http_gzip_ctx_t *ctx);
87 static ngx_int_t ngx_http_gzip_filter_copy_recycled(ngx_http_gzip_ctx_t *ctx,
88 ngx_chain_t *in);
79 static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r, 89 static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
80 ngx_http_gzip_ctx_t *ctx); 90 ngx_http_gzip_ctx_t *ctx);
81 static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, 91 static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r,
82 ngx_http_gzip_ctx_t *ctx); 92 ngx_http_gzip_ctx_t *ctx);
83 static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r, 93 static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r,
90 ngx_http_gzip_ctx_t *ctx); 100 ngx_http_gzip_ctx_t *ctx);
91 101
92 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, 102 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
93 u_int size); 103 u_int size);
94 static void ngx_http_gzip_filter_free(void *opaque, void *address); 104 static void ngx_http_gzip_filter_free(void *opaque, void *address);
105 static void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
106 ngx_http_gzip_ctx_t *ctx);
95 107
96 static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf); 108 static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf);
97 static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r, 109 static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
98 ngx_http_variable_value_t *v, uintptr_t data); 110 ngx_http_variable_value_t *v, uintptr_t data);
99 111
243 } 255 }
244 256
245 ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module); 257 ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
246 258
247 ctx->request = r; 259 ctx->request = r;
260 ctx->buffering = 1;
261
262 ngx_http_gzip_filter_memory(r, ctx);
248 263
249 h = ngx_list_push(&r->headers_out.headers); 264 h = ngx_list_push(&r->headers_out.headers);
250 if (h == NULL) { 265 if (h == NULL) {
251 return NGX_ERROR; 266 return NGX_ERROR;
252 } 267 }
257 h->value.len = sizeof("gzip") - 1; 272 h->value.len = sizeof("gzip") - 1;
258 h->value.data = (u_char *) "gzip"; 273 h->value.data = (u_char *) "gzip";
259 274
260 r->headers_out.content_encoding = h; 275 r->headers_out.content_encoding = h;
261 276
262 ctx->length = r->headers_out.content_length_n;
263
264 r->main_filter_need_in_memory = 1; 277 r->main_filter_need_in_memory = 1;
265 278
266 ngx_http_clear_content_length(r); 279 ngx_http_clear_content_length(r);
267 ngx_http_clear_accept_ranges(r); 280 ngx_http_clear_accept_ranges(r);
268 281
281 294
282 if (ctx == NULL || ctx->done) { 295 if (ctx == NULL || ctx->done) {
283 return ngx_http_next_body_filter(r, in); 296 return ngx_http_next_body_filter(r, in);
284 } 297 }
285 298
299 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
300 "http gzip filter");
301
302 if (ctx->buffering) {
303
304 if (in) {
305 switch (ngx_http_gzip_filter_copy_recycled(ctx, in)) {
306
307 case NGX_OK:
308 return NGX_OK;
309
310 case NGX_DONE:
311 in = NULL;
312 break;
313
314 default: /* NGX_ERROR */
315 goto failed;
316 }
317
318 } else {
319 ctx->buffering = 0;
320 }
321 }
322
286 if (ctx->preallocated == NULL) { 323 if (ctx->preallocated == NULL) {
287 if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) { 324 if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) {
288 goto failed; 325 goto failed;
289 } 326 }
290 } 327 }
354 391
355 /* rc == NGX_AGAIN */ 392 /* rc == NGX_AGAIN */
356 } 393 }
357 394
358 if (ctx->out == NULL) { 395 if (ctx->out == NULL) {
396 ngx_http_gzip_filter_free_copy_buf(r, ctx);
397
359 return ctx->busy ? NGX_AGAIN : NGX_OK; 398 return ctx->busy ? NGX_AGAIN : NGX_OK;
360 } 399 }
361 400
362 if (!ctx->gzheader) { 401 if (!ctx->gzheader) {
363 if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) { 402 if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) {
369 408
370 if (rc == NGX_ERROR) { 409 if (rc == NGX_ERROR) {
371 goto failed; 410 goto failed;
372 } 411 }
373 412
413 ngx_http_gzip_filter_free_copy_buf(r, ctx);
414
374 ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, 415 ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out,
375 (ngx_buf_tag_t) &ngx_http_gzip_filter_module); 416 (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
376 ctx->last_out = &ctx->out; 417 ctx->last_out = &ctx->out;
377 418
378 ctx->nomem = 0; 419 ctx->nomem = 0;
392 deflateEnd(&ctx->zstream); 433 deflateEnd(&ctx->zstream);
393 434
394 ngx_pfree(r->pool, ctx->preallocated); 435 ngx_pfree(r->pool, ctx->preallocated);
395 } 436 }
396 437
438 ngx_http_gzip_filter_free_copy_buf(r, ctx);
439
397 return NGX_ERROR; 440 return NGX_ERROR;
398 } 441 }
399 442
400 443
401 static ngx_int_t 444 static void
402 ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r, 445 ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
403 ngx_http_gzip_ctx_t *ctx) 446 {
404 { 447 int wbits, memlevel;
405 int rc, wbits, memlevel;
406 ngx_http_gzip_conf_t *conf; 448 ngx_http_gzip_conf_t *conf;
407 449
408 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); 450 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
409 451
410 wbits = conf->wbits; 452 wbits = conf->wbits;
411 memlevel = conf->memlevel; 453 memlevel = conf->memlevel;
412 454
413 if (ctx->length > 0) { 455 if (r->headers_out.content_length_n > 0) {
414 456
415 /* the actual zlib window size is smaller by 262 bytes */ 457 /* the actual zlib window size is smaller by 262 bytes */
416 458
417 while (ctx->length < ((1 << (wbits - 1)) - 262)) { 459 while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) {
418 wbits--; 460 wbits--;
419 memlevel--; 461 memlevel--;
420 } 462 }
421 } 463 }
464
465 ctx->wbits = wbits;
466 ctx->memlevel = memlevel;
422 467
423 /* 468 /*
424 * We preallocate a memory for zlib in one buffer (200K-400K), this 469 * We preallocate a memory for zlib in one buffer (200K-400K), this
425 * decreases a number of malloc() and free() calls and also probably 470 * decreases a number of malloc() and free() calls and also probably
426 * decreases a number of syscalls (sbrk()/mmap() and so on). 471 * decreases a number of syscalls (sbrk()/mmap() and so on).
431 * *) 5816 bytes on i386 and sparc64 (32-bit mode) 476 * *) 5816 bytes on i386 and sparc64 (32-bit mode)
432 * *) 5920 bytes on amd64 and sparc64 477 * *) 5920 bytes on amd64 and sparc64
433 */ 478 */
434 479
435 ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); 480 ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
481 }
482
483
484 static ngx_int_t
485 ngx_http_gzip_filter_copy_recycled(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in)
486 {
487 size_t size, buffered;
488 ngx_buf_t *b, *buf;
489 ngx_chain_t *cl, **ll;
490 ngx_http_request_t *r;
491
492 r = ctx->request;
493
494 r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
495
496 buffered = 0;
497 ll = &ctx->in;
498
499 for (cl = ctx->in; cl; cl = cl->next) {
500 buffered += cl->buf->last - cl->buf->pos;
501 ll = &cl->next;
502 }
503
504 while (in) {
505 cl = ngx_alloc_chain_link(r->pool);
506 if (cl == NULL) {
507 return NGX_ERROR;
508 }
509
510 b = in->buf;
511
512 size = b->last - b->pos;
513 buffered += size;
514
515 if (b->flush || b->last_buf) {
516 ctx->buffering = 0;
517
518 } else if (buffered > ctx->allocated / 2) {
519
520 /*
521 * With default memory settings zlib starts to output gzipped data
522 * only after it has got about 90K, so it makes sense to allocate
523 * zlib memory (200-400K) only after we have enough data
524 * to compress. Although we copy recycled buffers, nevertheless
525 * for responses up to 120K this allows to allocate zlib memory,
526 * to compress and to output the response in one step
527 * using hot CPU cache.
528 */
529
530 ctx->buffering = 0;
531 }
532
533 if (ctx->buffering && size && b->recycled) {
534
535 buf = ngx_create_temp_buf(r->pool, size);
536 if (buf == NULL) {
537 return NGX_ERROR;
538 }
539
540 buf->last = ngx_cpymem(buf->pos, b->pos, size);
541 b->pos = b->last;
542
543 buf->last_buf = b->last_buf;
544 buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
545
546 cl->buf = buf;
547
548 } else {
549 cl->buf = b;
550 }
551
552 *ll = cl;
553 ll = &cl->next;
554 in = in->next;
555 }
556
557 *ll = NULL;
558
559 return ctx->buffering ? NGX_OK : NGX_DONE;
560 }
561
562
563 static ngx_int_t
564 ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
565 ngx_http_gzip_ctx_t *ctx)
566 {
567 int rc;
568 ngx_http_gzip_conf_t *conf;
569
570 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
436 571
437 ctx->preallocated = ngx_palloc(r->pool, ctx->allocated); 572 ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
438 if (ctx->preallocated == NULL) { 573 if (ctx->preallocated == NULL) {
439 return NGX_ERROR; 574 return NGX_ERROR;
440 } 575 }
444 ctx->zstream.zalloc = ngx_http_gzip_filter_alloc; 579 ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
445 ctx->zstream.zfree = ngx_http_gzip_filter_free; 580 ctx->zstream.zfree = ngx_http_gzip_filter_free;
446 ctx->zstream.opaque = ctx; 581 ctx->zstream.opaque = ctx;
447 582
448 rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED, 583 rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
449 -wbits, memlevel, Z_DEFAULT_STRATEGY); 584 - ctx->wbits, ctx->memlevel, Z_DEFAULT_STRATEGY);
450 585
451 if (rc != Z_OK) { 586 if (rc != Z_OK) {
452 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, 587 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
453 "deflateInit2() failed: %d", rc); 588 "deflateInit2() failed: %d", rc);
454 return NGX_ERROR; 589 return NGX_ERROR;
455 } 590 }
456 591
457 r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
458
459 ctx->last_out = &ctx->out; 592 ctx->last_out = &ctx->out;
460 ctx->crc32 = crc32(0L, Z_NULL, 0); 593 ctx->crc32 = crc32(0L, Z_NULL, 0);
461 ctx->flush = Z_NO_FLUSH; 594 ctx->flush = Z_NO_FLUSH;
462 595
463 return NGX_OK; 596 return NGX_OK;
508 641
509 if (ctx->in == NULL) { 642 if (ctx->in == NULL) {
510 return NGX_DECLINED; 643 return NGX_DECLINED;
511 } 644 }
512 645
646 if (ctx->copy_buf) {
647
648 /*
649 * to avoid CPU cache trashing we do not free() just quit buf,
650 * but postpone free()ing after zlib compressing and data output
651 */
652
653 ctx->copy_buf->next = ctx->copied;
654 ctx->copied = ctx->copy_buf;
655 ctx->copy_buf = NULL;
656 }
657
513 ctx->in_buf = ctx->in->buf; 658 ctx->in_buf = ctx->in->buf;
659
660 if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) {
661 ctx->copy_buf = ctx->in;
662 }
663
514 ctx->in = ctx->in->next; 664 ctx->in = ctx->in->next;
515 665
516 ctx->zstream.next_in = ctx->in_buf->pos; 666 ctx->zstream.next_in = ctx->in_buf->pos;
517 ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; 667 ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
518 668
827 ngx_http_gzip_ctx_t *ctx = opaque; 977 ngx_http_gzip_ctx_t *ctx = opaque;
828 978
829 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0, 979 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
830 "gzip free: %p", address); 980 "gzip free: %p", address);
831 #endif 981 #endif
982 }
983
984
985 static void
986 ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
987 ngx_http_gzip_ctx_t *ctx)
988 {
989 ngx_chain_t *cl;
990
991 for (cl = ctx->copied; cl; cl = cl->next) {
992 ngx_pfree(r->pool, cl->buf->start);
993 }
994
995 ctx->copied = NULL;
832 } 996 }
833 997
834 998
835 static ngx_int_t 999 static ngx_int_t
836 ngx_http_gzip_add_variables(ngx_conf_t *cf) 1000 ngx_http_gzip_add_variables(ngx_conf_t *cf)
928 ngx_http_gzip_conf_t *prev = parent; 1092 ngx_http_gzip_conf_t *prev = parent;
929 ngx_http_gzip_conf_t *conf = child; 1093 ngx_http_gzip_conf_t *conf = child;
930 1094
931 ngx_conf_merge_value(conf->enable, prev->enable, 0); 1095 ngx_conf_merge_value(conf->enable, prev->enable, 0);
932 1096
933 ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize); 1097 ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
1098 (128 * 1024) / ngx_pagesize, ngx_pagesize);
934 1099
935 ngx_conf_merge_value(conf->level, prev->level, 1); 1100 ngx_conf_merge_value(conf->level, prev->level, 1);
936 ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS); 1101 ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
937 ngx_conf_merge_size_value(conf->memlevel, prev->memlevel, 1102 ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,
938 MAX_MEM_LEVEL - 1); 1103 MAX_MEM_LEVEL - 1);