Mercurial > hg > nginx
comparison src/http/modules/ngx_http_gzip_filter_module.c @ 2410:bbfaeb220e90
postpone zlib memory allocation
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Tue, 16 Dec 2008 16:09:39 +0000 |
parents | 51487a60eba2 |
children | 086c20fe2e97 |
comparison
equal
deleted
inserted
replaced
2409:428244458039 | 2410:bbfaeb220e90 |
---|---|
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) |