Mercurial > hg > nginx-vendor-0-7
comparison src/http/modules/ngx_http_gzip_filter_module.c @ 416:b4f69f2ef02c NGINX_0_7_20
nginx 0.7.20
*) Changes in the ngx_http_gzip_filter_module.
*) Feature: the ngx_http_limit_req_module.
*) Bugfix: worker processes might exit on a SIGBUS signal on sparc and
ppc platforms; the bug had appeared in 0.7.3.
Thanks to Maxim Dounin.
*) Bugfix: the "proxy_pass http://host/some:uri" directives did not
work; the bug had appeared in 0.7.12.
*) Bugfix: in HTTPS mode requests might fail with the "bad write retry"
error.
*) Bugfix: the ngx_http_secure_link_module did not work inside
locations, whose names are less than 3 characters.
*) Bugfix: $server_addr variable might have no value.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Mon, 10 Nov 2008 00:00:00 +0300 |
parents | 05981f639d21 |
children | fd759445d8a8 |
comparison
equal
deleted
inserted
replaced
415:5410f1e19796 | 416:b4f69f2ef02c |
---|---|
45 ngx_uint_t allocated; | 45 ngx_uint_t allocated; |
46 | 46 |
47 unsigned flush:4; | 47 unsigned flush:4; |
48 unsigned redo:1; | 48 unsigned redo:1; |
49 unsigned done:1; | 49 unsigned done:1; |
50 unsigned nomem:1; | |
51 unsigned gzheader:1; | |
50 | 52 |
51 size_t zin; | 53 size_t zin; |
52 size_t zout; | 54 size_t zout; |
53 | 55 |
54 uint32_t crc32; | 56 uint32_t crc32; |
55 z_stream zstream; | 57 z_stream zstream; |
56 ngx_http_request_t *request; | 58 ngx_http_request_t *request; |
57 } ngx_http_gzip_ctx_t; | 59 } ngx_http_gzip_ctx_t; |
58 | 60 |
59 | 61 |
62 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) | |
63 | |
64 struct gztrailer { | |
65 uint32_t crc32; | |
66 uint32_t zlen; | |
67 }; | |
68 | |
69 #else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */ | |
70 | |
71 struct gztrailer { | |
72 u_char crc32[4]; | |
73 u_char zlen[4]; | |
74 }; | |
75 | |
76 #endif | |
77 | |
78 | |
79 static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r, | |
80 ngx_http_gzip_ctx_t *ctx); | |
81 static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, | |
82 ngx_http_gzip_ctx_t *ctx); | |
83 static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r, | |
84 ngx_http_gzip_ctx_t *ctx); | |
85 static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, | |
86 ngx_http_gzip_ctx_t *ctx); | |
87 static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r, | |
88 ngx_http_gzip_ctx_t *ctx); | |
89 static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r, | |
90 ngx_http_gzip_ctx_t *ctx); | |
91 | |
60 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, | 92 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, |
61 u_int size); | 93 u_int size); |
62 static void ngx_http_gzip_filter_free(void *opaque, void *address); | 94 static void ngx_http_gzip_filter_free(void *opaque, void *address); |
63 static void ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx); | |
64 | 95 |
65 static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf); | 96 static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf); |
66 static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r, | 97 static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r, |
67 ngx_http_variable_value_t *v, uintptr_t data); | 98 ngx_http_variable_value_t *v, uintptr_t data); |
68 | 99 |
174 NULL, /* exit master */ | 205 NULL, /* exit master */ |
175 NGX_MODULE_V1_PADDING | 206 NGX_MODULE_V1_PADDING |
176 }; | 207 }; |
177 | 208 |
178 | 209 |
179 static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; | |
180 | |
181 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) | |
182 | |
183 struct gztrailer { | |
184 uint32_t crc32; | |
185 uint32_t zlen; | |
186 }; | |
187 | |
188 #else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */ | |
189 | |
190 struct gztrailer { | |
191 u_char crc32[4]; | |
192 u_char zlen[4]; | |
193 }; | |
194 | |
195 #endif | |
196 | |
197 | |
198 static ngx_str_t ngx_http_gzip_ratio = ngx_string("gzip_ratio"); | 210 static ngx_str_t ngx_http_gzip_ratio = ngx_string("gzip_ratio"); |
199 | 211 |
200 static ngx_http_output_header_filter_pt ngx_http_next_header_filter; | 212 static ngx_http_output_header_filter_pt ngx_http_next_header_filter; |
201 static ngx_http_output_body_filter_pt ngx_http_next_body_filter; | 213 static ngx_http_output_body_filter_pt ngx_http_next_body_filter; |
202 | 214 |
259 | 271 |
260 | 272 |
261 static ngx_int_t | 273 static ngx_int_t |
262 ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) | 274 ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) |
263 { | 275 { |
264 int rc, wbits, memlevel; | 276 int rc; |
265 ngx_int_t last; | 277 ngx_chain_t *cl; |
266 struct gztrailer *trailer; | 278 ngx_http_gzip_ctx_t *ctx; |
267 ngx_buf_t *b; | |
268 ngx_chain_t *cl, out; | |
269 ngx_http_gzip_ctx_t *ctx; | |
270 ngx_http_gzip_conf_t *conf; | |
271 | 279 |
272 ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); | 280 ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); |
273 | 281 |
274 if (ctx == NULL || ctx->done) { | 282 if (ctx == NULL || ctx->done) { |
275 return ngx_http_next_body_filter(r, in); | 283 return ngx_http_next_body_filter(r, in); |
276 } | 284 } |
277 | 285 |
278 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); | |
279 | |
280 if (ctx->preallocated == NULL) { | 286 if (ctx->preallocated == NULL) { |
281 wbits = conf->wbits; | 287 if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) { |
282 memlevel = conf->memlevel; | 288 goto failed; |
283 | 289 } |
284 if (ctx->length > 0) { | |
285 | |
286 /* the actual zlib window size is smaller by 262 bytes */ | |
287 | |
288 while (ctx->length < ((1 << (wbits - 1)) - 262)) { | |
289 wbits--; | |
290 memlevel--; | |
291 } | |
292 } | |
293 | |
294 /* | |
295 * We preallocate a memory for zlib in one buffer (200K-400K), this | |
296 * decreases a number of malloc() and free() calls and also probably | |
297 * decreases a number of syscalls (sbrk() and so on). | |
298 * Besides we free this memory as soon as the gzipping will complete | |
299 * and do not wait while a whole response will be sent to a client. | |
300 * | |
301 * 8K is for zlib deflate_state, it takes | |
302 * *) 5816 bytes on i386 and sparc64 (32-bit mode) | |
303 * *) 5920 bytes on amd64 and sparc64 | |
304 */ | |
305 | |
306 ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); | |
307 | |
308 ctx->preallocated = ngx_palloc(r->pool, ctx->allocated); | |
309 if (ctx->preallocated == NULL) { | |
310 return NGX_ERROR; | |
311 } | |
312 | |
313 ctx->free_mem = ctx->preallocated; | |
314 | |
315 ctx->zstream.zalloc = ngx_http_gzip_filter_alloc; | |
316 ctx->zstream.zfree = ngx_http_gzip_filter_free; | |
317 ctx->zstream.opaque = ctx; | |
318 | |
319 rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED, | |
320 -wbits, memlevel, Z_DEFAULT_STRATEGY); | |
321 | |
322 if (rc != Z_OK) { | |
323 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
324 "deflateInit2() failed: %d", rc); | |
325 ngx_http_gzip_error(ctx); | |
326 return NGX_ERROR; | |
327 } | |
328 | |
329 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); | |
330 if (b == NULL) { | |
331 ngx_http_gzip_error(ctx); | |
332 return NGX_ERROR; | |
333 } | |
334 | |
335 b->memory = 1; | |
336 b->pos = gzheader; | |
337 b->last = b->pos + 10; | |
338 | |
339 out.buf = b; | |
340 out.next = NULL; | |
341 | |
342 /* | |
343 * We pass the gzheader to the next filter now to avoid its linking | |
344 * to the ctx->busy chain. zlib does not usually output the compressed | |
345 * data in the initial iterations, so the gzheader that was linked | |
346 * to the ctx->busy chain would be flushed by ngx_http_write_filter(). | |
347 */ | |
348 | |
349 if (ngx_http_next_body_filter(r, &out) == NGX_ERROR) { | |
350 ngx_http_gzip_error(ctx); | |
351 return NGX_ERROR; | |
352 } | |
353 | |
354 r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; | |
355 | |
356 ctx->last_out = &ctx->out; | |
357 | |
358 ctx->crc32 = crc32(0L, Z_NULL, 0); | |
359 ctx->flush = Z_NO_FLUSH; | |
360 } | 290 } |
361 | 291 |
362 if (in) { | 292 if (in) { |
363 if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { | 293 if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { |
364 ngx_http_gzip_error(ctx); | 294 goto failed; |
365 return NGX_ERROR; | 295 } |
366 } | 296 } |
367 } | 297 |
368 | 298 if (ctx->nomem) { |
369 last = NGX_NONE; | 299 |
300 /* flush busy buffers */ | |
301 | |
302 if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) { | |
303 goto failed; | |
304 } | |
305 | |
306 cl = NULL; | |
307 | |
308 ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl, | |
309 (ngx_buf_tag_t) &ngx_http_gzip_filter_module); | |
310 ctx->nomem = 0; | |
311 } | |
370 | 312 |
371 for ( ;; ) { | 313 for ( ;; ) { |
372 | 314 |
315 /* cycle while we can write to a client */ | |
316 | |
373 for ( ;; ) { | 317 for ( ;; ) { |
374 | 318 |
375 /* does zlib need a new data ? */ | 319 /* cycle while there is data to feed zlib and ... */ |
376 | 320 |
377 if (ctx->zstream.avail_in == 0 | 321 rc = ngx_http_gzip_filter_add_data(r, ctx); |
378 && ctx->flush == Z_NO_FLUSH | 322 |
379 && !ctx->redo) | 323 if (rc == NGX_DECLINED) { |
380 { | 324 break; |
381 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
382 "gzip in: %p", ctx->in); | |
383 | |
384 if (ctx->in == NULL) { | |
385 break; | |
386 } | |
387 | |
388 ctx->in_buf = ctx->in->buf; | |
389 ctx->in = ctx->in->next; | |
390 | |
391 ctx->zstream.next_in = ctx->in_buf->pos; | |
392 ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; | |
393 | |
394 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
395 "gzip in_buf:%p ni:%p ai:%ud", | |
396 ctx->in_buf, | |
397 ctx->zstream.next_in, ctx->zstream.avail_in); | |
398 | |
399 /* STUB */ | |
400 if (ctx->in_buf->last < ctx->in_buf->pos) { | |
401 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
402 "zstream.avail_in is huge"); | |
403 ctx->done = 1; | |
404 return NGX_ERROR; | |
405 } | |
406 /**/ | |
407 | |
408 if (ctx->in_buf->last_buf) { | |
409 ctx->flush = Z_FINISH; | |
410 | |
411 } else if (ctx->in_buf->flush) { | |
412 ctx->flush = Z_SYNC_FLUSH; | |
413 } | |
414 | |
415 if (ctx->zstream.avail_in == 0) { | |
416 if (ctx->flush == Z_NO_FLUSH) { | |
417 continue; | |
418 } | |
419 | |
420 } else { | |
421 ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, | |
422 ctx->zstream.avail_in); | |
423 } | |
424 } | 325 } |
425 | 326 |
426 | 327 if (rc == NGX_AGAIN) { |
427 /* is there a space for the gzipped data ? */ | |
428 | |
429 if (ctx->zstream.avail_out == 0) { | |
430 | |
431 if (ctx->free) { | |
432 ctx->out_buf = ctx->free->buf; | |
433 ctx->free = ctx->free->next; | |
434 | |
435 } else if (ctx->bufs < conf->bufs.num) { | |
436 ctx->out_buf = ngx_create_temp_buf(r->pool, | |
437 conf->bufs.size); | |
438 if (ctx->out_buf == NULL) { | |
439 ngx_http_gzip_error(ctx); | |
440 return NGX_ERROR; | |
441 } | |
442 | |
443 ctx->out_buf->tag = (ngx_buf_tag_t) | |
444 &ngx_http_gzip_filter_module; | |
445 ctx->out_buf->recycled = 1; | |
446 ctx->bufs++; | |
447 | |
448 } else { | |
449 break; | |
450 } | |
451 | |
452 ctx->zstream.next_out = ctx->out_buf->pos; | |
453 ctx->zstream.avail_out = conf->bufs.size; | |
454 } | |
455 | |
456 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
457 "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", | |
458 ctx->zstream.next_in, ctx->zstream.next_out, | |
459 ctx->zstream.avail_in, ctx->zstream.avail_out, | |
460 ctx->flush, ctx->redo); | |
461 | |
462 rc = deflate(&ctx->zstream, ctx->flush); | |
463 | |
464 if (rc != Z_OK && rc != Z_STREAM_END) { | |
465 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
466 "deflate() failed: %d, %d", ctx->flush, rc); | |
467 ngx_http_gzip_error(ctx); | |
468 return NGX_ERROR; | |
469 } | |
470 | |
471 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
472 "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", | |
473 ctx->zstream.next_in, ctx->zstream.next_out, | |
474 ctx->zstream.avail_in, ctx->zstream.avail_out, | |
475 rc); | |
476 | |
477 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
478 "gzip in_buf:%p pos:%p", | |
479 ctx->in_buf, ctx->in_buf->pos); | |
480 | |
481 | |
482 if (ctx->zstream.next_in) { | |
483 ctx->in_buf->pos = ctx->zstream.next_in; | |
484 | |
485 if (ctx->zstream.avail_in == 0) { | |
486 ctx->zstream.next_in = NULL; | |
487 } | |
488 } | |
489 | |
490 ctx->out_buf->last = ctx->zstream.next_out; | |
491 | |
492 if (ctx->zstream.avail_out == 0) { | |
493 | |
494 /* zlib wants to output some more gzipped data */ | |
495 | |
496 cl = ngx_alloc_chain_link(r->pool); | |
497 if (cl == NULL) { | |
498 ngx_http_gzip_error(ctx); | |
499 return NGX_ERROR; | |
500 } | |
501 | |
502 cl->buf = ctx->out_buf; | |
503 cl->next = NULL; | |
504 *ctx->last_out = cl; | |
505 ctx->last_out = &cl->next; | |
506 | |
507 ctx->redo = 1; | |
508 | |
509 continue; | 328 continue; |
510 } | 329 } |
511 | 330 |
512 ctx->redo = 0; | 331 |
513 | 332 /* ... there are buffers to write zlib output */ |
514 if (ctx->flush == Z_SYNC_FLUSH) { | 333 |
515 | 334 rc = ngx_http_gzip_filter_get_buf(r, ctx); |
516 ctx->zstream.avail_out = 0; | 335 |
517 ctx->out_buf->flush = 1; | 336 if (rc == NGX_DECLINED) { |
518 ctx->flush = Z_NO_FLUSH; | |
519 | |
520 cl = ngx_alloc_chain_link(r->pool); | |
521 if (cl == NULL) { | |
522 ngx_http_gzip_error(ctx); | |
523 return NGX_ERROR; | |
524 } | |
525 | |
526 cl->buf = ctx->out_buf; | |
527 cl->next = NULL; | |
528 *ctx->last_out = cl; | |
529 ctx->last_out = &cl->next; | |
530 | |
531 break; | 337 break; |
532 } | 338 } |
533 | 339 |
534 if (rc == Z_STREAM_END) { | 340 if (rc == NGX_ERROR) { |
535 | 341 goto failed; |
536 ctx->zin = ctx->zstream.total_in; | 342 } |
537 ctx->zout = 10 + ctx->zstream.total_out + 8; | 343 |
538 | 344 |
539 rc = deflateEnd(&ctx->zstream); | 345 rc = ngx_http_gzip_filter_deflate(r, ctx); |
540 | 346 |
541 if (rc != Z_OK) { | 347 if (rc == NGX_OK) { |
542 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
543 "deflateEnd() failed: %d", rc); | |
544 ngx_http_gzip_error(ctx); | |
545 return NGX_ERROR; | |
546 } | |
547 | |
548 ngx_pfree(r->pool, ctx->preallocated); | |
549 | |
550 cl = ngx_alloc_chain_link(r->pool); | |
551 if (cl == NULL) { | |
552 ngx_http_gzip_error(ctx); | |
553 return NGX_ERROR; | |
554 } | |
555 | |
556 cl->buf = ctx->out_buf; | |
557 cl->next = NULL; | |
558 *ctx->last_out = cl; | |
559 ctx->last_out = &cl->next; | |
560 | |
561 if (ctx->zstream.avail_out >= 8) { | |
562 trailer = (struct gztrailer *) ctx->out_buf->last; | |
563 ctx->out_buf->last += 8; | |
564 ctx->out_buf->last_buf = 1; | |
565 | |
566 } else { | |
567 b = ngx_create_temp_buf(r->pool, 8); | |
568 if (b == NULL) { | |
569 ngx_http_gzip_error(ctx); | |
570 return NGX_ERROR; | |
571 } | |
572 | |
573 b->last_buf = 1; | |
574 | |
575 cl = ngx_alloc_chain_link(r->pool); | |
576 if (cl == NULL) { | |
577 ngx_http_gzip_error(ctx); | |
578 return NGX_ERROR; | |
579 } | |
580 | |
581 cl->buf = b; | |
582 cl->next = NULL; | |
583 *ctx->last_out = cl; | |
584 ctx->last_out = &cl->next; | |
585 trailer = (struct gztrailer *) b->pos; | |
586 b->last += 8; | |
587 } | |
588 | |
589 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) | |
590 | |
591 trailer->crc32 = ctx->crc32; | |
592 trailer->zlen = ctx->zin; | |
593 | |
594 #else | |
595 trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff); | |
596 trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff); | |
597 trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff); | |
598 trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff); | |
599 | |
600 trailer->zlen[0] = (u_char) (ctx->zin & 0xff); | |
601 trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff); | |
602 trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff); | |
603 trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff); | |
604 #endif | |
605 | |
606 ctx->zstream.avail_in = 0; | |
607 ctx->zstream.avail_out = 0; | |
608 | |
609 ctx->done = 1; | |
610 | |
611 r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; | |
612 | |
613 break; | 348 break; |
614 } | 349 } |
615 | 350 |
616 if (conf->no_buffer && ctx->in == NULL) { | 351 if (rc == NGX_ERROR) { |
617 | 352 goto failed; |
618 cl = ngx_alloc_chain_link(r->pool); | |
619 if (cl == NULL) { | |
620 ngx_http_gzip_error(ctx); | |
621 return NGX_ERROR; | |
622 } | |
623 | |
624 cl->buf = ctx->out_buf; | |
625 cl->next = NULL; | |
626 *ctx->last_out = cl; | |
627 ctx->last_out = &cl->next; | |
628 | |
629 break; | |
630 } | 353 } |
354 | |
355 /* rc == NGX_AGAIN */ | |
631 } | 356 } |
632 | 357 |
633 if (ctx->out == NULL) { | 358 if (ctx->out == NULL) { |
634 | 359 return ctx->busy ? NGX_AGAIN : NGX_OK; |
635 if (last == NGX_AGAIN) { | 360 } |
636 return NGX_AGAIN; | 361 |
362 if (!ctx->gzheader) { | |
363 if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) { | |
364 goto failed; | |
637 } | 365 } |
638 | 366 } |
639 if (ctx->busy == NULL) { | 367 |
640 return NGX_OK; | 368 rc = ngx_http_next_body_filter(r, ctx->out); |
641 } | 369 |
642 } | 370 if (rc == NGX_ERROR) { |
643 | 371 goto failed; |
644 last = ngx_http_next_body_filter(r, ctx->out); | |
645 | |
646 /* | |
647 * we do not check NGX_AGAIN here because the downstream filters | |
648 * may free some buffers and zlib may compress some data into them | |
649 */ | |
650 | |
651 if (last == NGX_ERROR) { | |
652 ngx_http_gzip_error(ctx); | |
653 return NGX_ERROR; | |
654 } | 372 } |
655 | 373 |
656 ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, | 374 ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, |
657 (ngx_buf_tag_t) &ngx_http_gzip_filter_module); | 375 (ngx_buf_tag_t) &ngx_http_gzip_filter_module); |
658 ctx->last_out = &ctx->out; | 376 ctx->last_out = &ctx->out; |
659 | 377 |
378 ctx->nomem = 0; | |
379 | |
660 if (ctx->done) { | 380 if (ctx->done) { |
661 return last; | 381 return rc; |
662 } | 382 } |
663 } | 383 } |
384 | |
385 /* unreachable */ | |
386 | |
387 failed: | |
388 | |
389 ctx->done = 1; | |
390 | |
391 if (ctx->preallocated) { | |
392 deflateEnd(&ctx->zstream); | |
393 | |
394 ngx_pfree(r->pool, ctx->preallocated); | |
395 } | |
396 | |
397 return NGX_ERROR; | |
398 } | |
399 | |
400 | |
401 static ngx_int_t | |
402 ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r, | |
403 ngx_http_gzip_ctx_t *ctx) | |
404 { | |
405 int rc, wbits, memlevel; | |
406 ngx_http_gzip_conf_t *conf; | |
407 | |
408 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); | |
409 | |
410 wbits = conf->wbits; | |
411 memlevel = conf->memlevel; | |
412 | |
413 if (ctx->length > 0) { | |
414 | |
415 /* the actual zlib window size is smaller by 262 bytes */ | |
416 | |
417 while (ctx->length < ((1 << (wbits - 1)) - 262)) { | |
418 wbits--; | |
419 memlevel--; | |
420 } | |
421 } | |
422 | |
423 /* | |
424 * We preallocate a memory for zlib in one buffer (200K-400K), this | |
425 * decreases a number of malloc() and free() calls and also probably | |
426 * decreases a number of syscalls (sbrk()/mmap() and so on). | |
427 * Besides we free the memory as soon as a gzipping will complete | |
428 * and do not wait while a whole response will be sent to a client. | |
429 * | |
430 * 8K is for zlib deflate_state, it takes | |
431 * *) 5816 bytes on i386 and sparc64 (32-bit mode) | |
432 * *) 5920 bytes on amd64 and sparc64 | |
433 */ | |
434 | |
435 ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); | |
436 | |
437 ctx->preallocated = ngx_palloc(r->pool, ctx->allocated); | |
438 if (ctx->preallocated == NULL) { | |
439 return NGX_ERROR; | |
440 } | |
441 | |
442 ctx->free_mem = ctx->preallocated; | |
443 | |
444 ctx->zstream.zalloc = ngx_http_gzip_filter_alloc; | |
445 ctx->zstream.zfree = ngx_http_gzip_filter_free; | |
446 ctx->zstream.opaque = ctx; | |
447 | |
448 rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED, | |
449 -wbits, memlevel, Z_DEFAULT_STRATEGY); | |
450 | |
451 if (rc != Z_OK) { | |
452 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
453 "deflateInit2() failed: %d", rc); | |
454 return NGX_ERROR; | |
455 } | |
456 | |
457 r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; | |
458 | |
459 ctx->last_out = &ctx->out; | |
460 ctx->crc32 = crc32(0L, Z_NULL, 0); | |
461 ctx->flush = Z_NO_FLUSH; | |
462 | |
463 return NGX_OK; | |
464 } | |
465 | |
466 | |
467 static ngx_int_t | |
468 ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) | |
469 { | |
470 ngx_buf_t *b; | |
471 ngx_chain_t *cl; | |
472 static u_char gzheader[10] = | |
473 { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; | |
474 | |
475 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); | |
476 if (b == NULL) { | |
477 return NGX_ERROR; | |
478 } | |
479 | |
480 b->memory = 1; | |
481 b->pos = gzheader; | |
482 b->last = b->pos + 10; | |
483 | |
484 cl = ngx_alloc_chain_link(r->pool); | |
485 if (cl == NULL) { | |
486 return NGX_ERROR; | |
487 } | |
488 | |
489 cl->buf = b; | |
490 cl->next = ctx->out; | |
491 ctx->out = cl; | |
492 | |
493 ctx->gzheader = 1; | |
494 | |
495 return NGX_OK; | |
496 } | |
497 | |
498 | |
499 static ngx_int_t | |
500 ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) | |
501 { | |
502 if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) { | |
503 return NGX_OK; | |
504 } | |
505 | |
506 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
507 "gzip in: %p", ctx->in); | |
508 | |
509 if (ctx->in == NULL) { | |
510 return NGX_DECLINED; | |
511 } | |
512 | |
513 ctx->in_buf = ctx->in->buf; | |
514 ctx->in = ctx->in->next; | |
515 | |
516 ctx->zstream.next_in = ctx->in_buf->pos; | |
517 ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; | |
518 | |
519 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
520 "gzip in_buf:%p ni:%p ai:%ud", | |
521 ctx->in_buf, | |
522 ctx->zstream.next_in, ctx->zstream.avail_in); | |
523 | |
524 if (ctx->in_buf->last_buf) { | |
525 ctx->flush = Z_FINISH; | |
526 | |
527 } else if (ctx->in_buf->flush) { | |
528 ctx->flush = Z_SYNC_FLUSH; | |
529 } | |
530 | |
531 if (ctx->zstream.avail_in) { | |
532 | |
533 ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, | |
534 ctx->zstream.avail_in); | |
535 | |
536 } else if (ctx->flush == Z_NO_FLUSH) { | |
537 return NGX_AGAIN; | |
538 } | |
539 | |
540 return NGX_OK; | |
541 } | |
542 | |
543 | |
544 static ngx_int_t | |
545 ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) | |
546 { | |
547 ngx_http_gzip_conf_t *conf; | |
548 | |
549 if (ctx->zstream.avail_out) { | |
550 return NGX_OK; | |
551 } | |
552 | |
553 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); | |
554 | |
555 if (ctx->free) { | |
556 ctx->out_buf = ctx->free->buf; | |
557 ctx->free = ctx->free->next; | |
558 | |
559 } else if (ctx->bufs < conf->bufs.num) { | |
560 | |
561 ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size); | |
562 if (ctx->out_buf == NULL) { | |
563 return NGX_ERROR; | |
564 } | |
565 | |
566 ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module; | |
567 ctx->out_buf->recycled = 1; | |
568 ctx->bufs++; | |
569 | |
570 } else { | |
571 ctx->nomem = 1; | |
572 return NGX_DECLINED; | |
573 } | |
574 | |
575 ctx->zstream.next_out = ctx->out_buf->pos; | |
576 ctx->zstream.avail_out = conf->bufs.size; | |
577 | |
578 return NGX_OK; | |
579 } | |
580 | |
581 | |
582 static ngx_int_t | |
583 ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) | |
584 { | |
585 int rc; | |
586 ngx_chain_t *cl; | |
587 ngx_http_gzip_conf_t *conf; | |
588 | |
589 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
590 "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", | |
591 ctx->zstream.next_in, ctx->zstream.next_out, | |
592 ctx->zstream.avail_in, ctx->zstream.avail_out, | |
593 ctx->flush, ctx->redo); | |
594 | |
595 rc = deflate(&ctx->zstream, ctx->flush); | |
596 | |
597 if (rc != Z_OK && rc != Z_STREAM_END) { | |
598 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
599 "deflate() failed: %d, %d", ctx->flush, rc); | |
600 return NGX_ERROR; | |
601 } | |
602 | |
603 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
604 "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", | |
605 ctx->zstream.next_in, ctx->zstream.next_out, | |
606 ctx->zstream.avail_in, ctx->zstream.avail_out, | |
607 rc); | |
608 | |
609 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
610 "gzip in_buf:%p pos:%p", | |
611 ctx->in_buf, ctx->in_buf->pos); | |
612 | |
613 if (ctx->zstream.next_in) { | |
614 ctx->in_buf->pos = ctx->zstream.next_in; | |
615 | |
616 if (ctx->zstream.avail_in == 0) { | |
617 ctx->zstream.next_in = NULL; | |
618 } | |
619 } | |
620 | |
621 ctx->out_buf->last = ctx->zstream.next_out; | |
622 | |
623 if (ctx->zstream.avail_out == 0) { | |
624 | |
625 /* zlib wants to output some more gzipped data */ | |
626 | |
627 cl = ngx_alloc_chain_link(r->pool); | |
628 if (cl == NULL) { | |
629 return NGX_ERROR; | |
630 } | |
631 | |
632 cl->buf = ctx->out_buf; | |
633 cl->next = NULL; | |
634 *ctx->last_out = cl; | |
635 ctx->last_out = &cl->next; | |
636 | |
637 ctx->redo = 1; | |
638 | |
639 return NGX_AGAIN; | |
640 } | |
641 | |
642 ctx->redo = 0; | |
643 | |
644 if (ctx->flush == Z_SYNC_FLUSH) { | |
645 | |
646 ctx->zstream.avail_out = 0; | |
647 ctx->out_buf->flush = 1; | |
648 ctx->flush = Z_NO_FLUSH; | |
649 | |
650 cl = ngx_alloc_chain_link(r->pool); | |
651 if (cl == NULL) { | |
652 return NGX_ERROR; | |
653 } | |
654 | |
655 cl->buf = ctx->out_buf; | |
656 cl->next = NULL; | |
657 *ctx->last_out = cl; | |
658 ctx->last_out = &cl->next; | |
659 | |
660 return NGX_OK; | |
661 } | |
662 | |
663 if (rc == Z_STREAM_END) { | |
664 | |
665 if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) { | |
666 return NGX_ERROR; | |
667 } | |
668 | |
669 return NGX_OK; | |
670 } | |
671 | |
672 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); | |
673 | |
674 if (conf->no_buffer && ctx->in == NULL) { | |
675 | |
676 cl = ngx_alloc_chain_link(r->pool); | |
677 if (cl == NULL) { | |
678 return NGX_ERROR; | |
679 } | |
680 | |
681 cl->buf = ctx->out_buf; | |
682 cl->next = NULL; | |
683 *ctx->last_out = cl; | |
684 ctx->last_out = &cl->next; | |
685 | |
686 return NGX_OK; | |
687 } | |
688 | |
689 return NGX_AGAIN; | |
690 } | |
691 | |
692 | |
693 static ngx_int_t | |
694 ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r, | |
695 ngx_http_gzip_ctx_t *ctx) | |
696 { | |
697 int rc; | |
698 ngx_buf_t *b; | |
699 ngx_chain_t *cl; | |
700 struct gztrailer *trailer; | |
701 | |
702 ctx->zin = ctx->zstream.total_in; | |
703 ctx->zout = 10 + ctx->zstream.total_out + 8; | |
704 | |
705 rc = deflateEnd(&ctx->zstream); | |
706 | |
707 if (rc != Z_OK) { | |
708 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
709 "deflateEnd() failed: %d", rc); | |
710 return NGX_ERROR; | |
711 } | |
712 | |
713 ngx_pfree(r->pool, ctx->preallocated); | |
714 | |
715 cl = ngx_alloc_chain_link(r->pool); | |
716 if (cl == NULL) { | |
717 return NGX_ERROR; | |
718 } | |
719 | |
720 cl->buf = ctx->out_buf; | |
721 cl->next = NULL; | |
722 *ctx->last_out = cl; | |
723 ctx->last_out = &cl->next; | |
724 | |
725 if (ctx->zstream.avail_out >= 8) { | |
726 trailer = (struct gztrailer *) ctx->out_buf->last; | |
727 ctx->out_buf->last += 8; | |
728 ctx->out_buf->last_buf = 1; | |
729 | |
730 } else { | |
731 b = ngx_create_temp_buf(r->pool, 8); | |
732 if (b == NULL) { | |
733 return NGX_ERROR; | |
734 } | |
735 | |
736 b->last_buf = 1; | |
737 | |
738 cl = ngx_alloc_chain_link(r->pool); | |
739 if (cl == NULL) { | |
740 return NGX_ERROR; | |
741 } | |
742 | |
743 cl->buf = b; | |
744 cl->next = NULL; | |
745 *ctx->last_out = cl; | |
746 ctx->last_out = &cl->next; | |
747 trailer = (struct gztrailer *) b->pos; | |
748 b->last += 8; | |
749 } | |
750 | |
751 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) | |
752 | |
753 trailer->crc32 = ctx->crc32; | |
754 trailer->zlen = ctx->zin; | |
755 | |
756 #else | |
757 | |
758 trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff); | |
759 trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff); | |
760 trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff); | |
761 trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff); | |
762 | |
763 trailer->zlen[0] = (u_char) (ctx->zin & 0xff); | |
764 trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff); | |
765 trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff); | |
766 trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff); | |
767 | |
768 #endif | |
769 | |
770 ctx->zstream.avail_in = 0; | |
771 ctx->zstream.avail_out = 0; | |
772 | |
773 ctx->done = 1; | |
774 | |
775 r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; | |
776 | |
777 return NGX_OK; | |
664 } | 778 } |
665 | 779 |
666 | 780 |
667 static void * | 781 static void * |
668 ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size) | 782 ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size) |
713 ngx_http_gzip_ctx_t *ctx = opaque; | 827 ngx_http_gzip_ctx_t *ctx = opaque; |
714 | 828 |
715 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0, | 829 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0, |
716 "gzip free: %p", address); | 830 "gzip free: %p", address); |
717 #endif | 831 #endif |
718 } | |
719 | |
720 | |
721 static void | |
722 ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx) | |
723 { | |
724 deflateEnd(&ctx->zstream); | |
725 | |
726 if (ctx->preallocated) { | |
727 ngx_pfree(ctx->request->pool, ctx->preallocated); | |
728 } | |
729 | |
730 ctx->zstream.avail_in = 0; | |
731 ctx->zstream.avail_out = 0; | |
732 | |
733 ctx->done = 1; | |
734 | |
735 return; | |
736 } | 832 } |
737 | 833 |
738 | 834 |
739 static ngx_int_t | 835 static ngx_int_t |
740 ngx_http_gzip_add_variables(ngx_conf_t *cf) | 836 ngx_http_gzip_add_variables(ngx_conf_t *cf) |
869 | 965 |
870 | 966 |
871 static char * | 967 static char * |
872 ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data) | 968 ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data) |
873 { | 969 { |
874 int *np = data; | 970 size_t *np = data; |
875 | 971 |
876 int wbits, wsize; | 972 size_t wbits, wsize; |
877 | 973 |
878 wbits = 15; | 974 wbits = 15; |
879 | 975 |
880 for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) { | 976 for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) { |
881 | 977 |
893 | 989 |
894 | 990 |
895 static char * | 991 static char * |
896 ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data) | 992 ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data) |
897 { | 993 { |
898 int *np = data; | 994 size_t *np = data; |
899 | 995 |
900 int memlevel, hsize; | 996 size_t memlevel, hsize; |
901 | 997 |
902 memlevel = 9; | 998 memlevel = 9; |
903 | 999 |
904 for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) { | 1000 for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) { |
905 | 1001 |