Mercurial > hg > nginx
comparison src/http/modules/ngx_http_gzip_filter_module.c @ 2291:e9a1cddf2aaa
*) split ngx_http_gzip_body_filter()
*) send gzheader together with the filter's first output
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Thu, 06 Nov 2008 19:13:47 +0000 |
parents | 1a7567e5b988 |
children | 51487a60eba2 |
comparison
equal
deleted
inserted
replaced
2290:1a7567e5b988 | 2291:e9a1cddf2aaa |
---|---|
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; | 50 unsigned nomem:1; |
51 unsigned gzheader:1; | |
51 | 52 |
52 size_t zin; | 53 size_t zin; |
53 size_t zout; | 54 size_t zout; |
54 | 55 |
55 uint32_t crc32; | 56 uint32_t crc32; |
56 z_stream zstream; | 57 z_stream zstream; |
57 ngx_http_request_t *request; | 58 ngx_http_request_t *request; |
58 } ngx_http_gzip_ctx_t; | 59 } ngx_http_gzip_ctx_t; |
59 | 60 |
60 | 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 | |
61 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, |
62 u_int size); | 93 u_int size); |
63 static void ngx_http_gzip_filter_free(void *opaque, void *address); | 94 static void ngx_http_gzip_filter_free(void *opaque, void *address); |
64 static void ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx); | |
65 | 95 |
66 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); |
67 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, |
68 ngx_http_variable_value_t *v, uintptr_t data); | 98 ngx_http_variable_value_t *v, uintptr_t data); |
69 | 99 |
175 NULL, /* exit master */ | 205 NULL, /* exit master */ |
176 NGX_MODULE_V1_PADDING | 206 NGX_MODULE_V1_PADDING |
177 }; | 207 }; |
178 | 208 |
179 | 209 |
180 static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; | |
181 | |
182 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) | |
183 | |
184 struct gztrailer { | |
185 uint32_t crc32; | |
186 uint32_t zlen; | |
187 }; | |
188 | |
189 #else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */ | |
190 | |
191 struct gztrailer { | |
192 u_char crc32[4]; | |
193 u_char zlen[4]; | |
194 }; | |
195 | |
196 #endif | |
197 | |
198 | |
199 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"); |
200 | 211 |
201 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; |
202 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; |
203 | 214 |
260 | 271 |
261 | 272 |
262 static ngx_int_t | 273 static ngx_int_t |
263 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) |
264 { | 275 { |
265 int rc, wbits, memlevel; | 276 int rc; |
266 struct gztrailer *trailer; | 277 ngx_chain_t *cl; |
267 ngx_buf_t *b; | 278 ngx_http_gzip_ctx_t *ctx; |
268 ngx_chain_t *cl; | |
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 cl = ngx_alloc_chain_link(r->pool); | |
340 if (cl == NULL) { | |
341 ngx_http_gzip_error(ctx); | |
342 return NGX_ERROR; | |
343 } | |
344 | |
345 cl->buf = b; | |
346 cl->next = NULL; | |
347 ctx->out = cl; | |
348 ctx->last_out = &cl->next; | |
349 | |
350 r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; | |
351 | |
352 ctx->crc32 = crc32(0L, Z_NULL, 0); | |
353 ctx->flush = Z_NO_FLUSH; | |
354 } | 290 } |
355 | 291 |
356 if (in) { | 292 if (in) { |
357 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) { |
358 ngx_http_gzip_error(ctx); | 294 goto failed; |
359 return NGX_ERROR; | |
360 } | 295 } |
361 } | 296 } |
362 | 297 |
363 if (ctx->nomem) { | 298 if (ctx->nomem) { |
364 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 299 |
365 "gzip nomem"); | 300 /* flush busy buffers */ |
366 | 301 |
367 if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) { | 302 if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) { |
368 ngx_http_gzip_error(ctx); | 303 goto failed; |
369 return NGX_ERROR; | |
370 } | 304 } |
371 | 305 |
372 cl = NULL; | 306 cl = NULL; |
373 | 307 |
374 ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl, | 308 ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl, |
376 ctx->nomem = 0; | 310 ctx->nomem = 0; |
377 } | 311 } |
378 | 312 |
379 for ( ;; ) { | 313 for ( ;; ) { |
380 | 314 |
315 /* cycle while we can write to a client */ | |
316 | |
381 for ( ;; ) { | 317 for ( ;; ) { |
382 | 318 |
383 /* does zlib need a new data ? */ | 319 /* cycle while there is data to feed zlib and ... */ |
384 | 320 |
385 if (ctx->zstream.avail_in == 0 | 321 rc = ngx_http_gzip_filter_add_data(r, ctx); |
386 && ctx->flush == Z_NO_FLUSH | 322 |
387 && !ctx->redo) | 323 if (rc == NGX_DECLINED) { |
388 { | 324 break; |
389 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
390 "gzip in: %p", ctx->in); | |
391 | |
392 if (ctx->in == NULL) { | |
393 break; | |
394 } | |
395 | |
396 ctx->in_buf = ctx->in->buf; | |
397 ctx->in = ctx->in->next; | |
398 | |
399 ctx->zstream.next_in = ctx->in_buf->pos; | |
400 ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; | |
401 | |
402 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
403 "gzip in_buf:%p ni:%p ai:%ud", | |
404 ctx->in_buf, | |
405 ctx->zstream.next_in, ctx->zstream.avail_in); | |
406 | |
407 /* STUB */ | |
408 if (ctx->in_buf->last < ctx->in_buf->pos) { | |
409 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
410 "zstream.avail_in is huge"); | |
411 ctx->done = 1; | |
412 return NGX_ERROR; | |
413 } | |
414 /**/ | |
415 | |
416 if (ctx->in_buf->last_buf) { | |
417 ctx->flush = Z_FINISH; | |
418 | |
419 } else if (ctx->in_buf->flush) { | |
420 ctx->flush = Z_SYNC_FLUSH; | |
421 } | |
422 | |
423 if (ctx->zstream.avail_in == 0) { | |
424 if (ctx->flush == Z_NO_FLUSH) { | |
425 continue; | |
426 } | |
427 | |
428 } else { | |
429 ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, | |
430 ctx->zstream.avail_in); | |
431 } | |
432 } | 325 } |
433 | 326 |
434 | 327 if (rc == NGX_AGAIN) { |
435 /* is there a space for the gzipped data ? */ | |
436 | |
437 if (ctx->zstream.avail_out == 0) { | |
438 | |
439 if (ctx->free) { | |
440 ctx->out_buf = ctx->free->buf; | |
441 ctx->free = ctx->free->next; | |
442 | |
443 } else if (ctx->bufs < conf->bufs.num) { | |
444 ctx->out_buf = ngx_create_temp_buf(r->pool, | |
445 conf->bufs.size); | |
446 if (ctx->out_buf == NULL) { | |
447 ngx_http_gzip_error(ctx); | |
448 return NGX_ERROR; | |
449 } | |
450 | |
451 ctx->out_buf->tag = (ngx_buf_tag_t) | |
452 &ngx_http_gzip_filter_module; | |
453 ctx->out_buf->recycled = 1; | |
454 ctx->bufs++; | |
455 | |
456 } else { | |
457 ctx->nomem = 1; | |
458 break; | |
459 } | |
460 | |
461 ctx->zstream.next_out = ctx->out_buf->pos; | |
462 ctx->zstream.avail_out = conf->bufs.size; | |
463 } | |
464 | |
465 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
466 "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", | |
467 ctx->zstream.next_in, ctx->zstream.next_out, | |
468 ctx->zstream.avail_in, ctx->zstream.avail_out, | |
469 ctx->flush, ctx->redo); | |
470 | |
471 rc = deflate(&ctx->zstream, ctx->flush); | |
472 | |
473 if (rc != Z_OK && rc != Z_STREAM_END) { | |
474 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
475 "deflate() failed: %d, %d", ctx->flush, rc); | |
476 ngx_http_gzip_error(ctx); | |
477 return NGX_ERROR; | |
478 } | |
479 | |
480 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
481 "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", | |
482 ctx->zstream.next_in, ctx->zstream.next_out, | |
483 ctx->zstream.avail_in, ctx->zstream.avail_out, | |
484 rc); | |
485 | |
486 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
487 "gzip in_buf:%p pos:%p", | |
488 ctx->in_buf, ctx->in_buf->pos); | |
489 | |
490 | |
491 if (ctx->zstream.next_in) { | |
492 ctx->in_buf->pos = ctx->zstream.next_in; | |
493 | |
494 if (ctx->zstream.avail_in == 0) { | |
495 ctx->zstream.next_in = NULL; | |
496 } | |
497 } | |
498 | |
499 ctx->out_buf->last = ctx->zstream.next_out; | |
500 | |
501 if (ctx->zstream.avail_out == 0) { | |
502 | |
503 /* zlib wants to output some more gzipped data */ | |
504 | |
505 cl = ngx_alloc_chain_link(r->pool); | |
506 if (cl == NULL) { | |
507 ngx_http_gzip_error(ctx); | |
508 return NGX_ERROR; | |
509 } | |
510 | |
511 cl->buf = ctx->out_buf; | |
512 cl->next = NULL; | |
513 *ctx->last_out = cl; | |
514 ctx->last_out = &cl->next; | |
515 | |
516 ctx->redo = 1; | |
517 | |
518 continue; | 328 continue; |
519 } | 329 } |
520 | 330 |
521 ctx->redo = 0; | 331 |
522 | 332 /* ... there are buffers to write zlib output */ |
523 if (ctx->flush == Z_SYNC_FLUSH) { | 333 |
524 | 334 rc = ngx_http_gzip_filter_get_buf(r, ctx); |
525 ctx->zstream.avail_out = 0; | 335 |
526 ctx->out_buf->flush = 1; | 336 if (rc == NGX_DECLINED) { |
527 ctx->flush = Z_NO_FLUSH; | |
528 | |
529 cl = ngx_alloc_chain_link(r->pool); | |
530 if (cl == NULL) { | |
531 ngx_http_gzip_error(ctx); | |
532 return NGX_ERROR; | |
533 } | |
534 | |
535 cl->buf = ctx->out_buf; | |
536 cl->next = NULL; | |
537 *ctx->last_out = cl; | |
538 ctx->last_out = &cl->next; | |
539 | |
540 break; | 337 break; |
541 } | 338 } |
542 | 339 |
543 if (rc == Z_STREAM_END) { | 340 if (rc == NGX_ERROR) { |
544 | 341 goto failed; |
545 ctx->zin = ctx->zstream.total_in; | 342 } |
546 ctx->zout = 10 + ctx->zstream.total_out + 8; | 343 |
547 | 344 |
548 rc = deflateEnd(&ctx->zstream); | 345 rc = ngx_http_gzip_filter_deflate(r, ctx); |
549 | 346 |
550 if (rc != Z_OK) { | 347 if (rc == NGX_OK) { |
551 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
552 "deflateEnd() failed: %d", rc); | |
553 ngx_http_gzip_error(ctx); | |
554 return NGX_ERROR; | |
555 } | |
556 | |
557 ngx_pfree(r->pool, ctx->preallocated); | |
558 | |
559 cl = ngx_alloc_chain_link(r->pool); | |
560 if (cl == NULL) { | |
561 ngx_http_gzip_error(ctx); | |
562 return NGX_ERROR; | |
563 } | |
564 | |
565 cl->buf = ctx->out_buf; | |
566 cl->next = NULL; | |
567 *ctx->last_out = cl; | |
568 ctx->last_out = &cl->next; | |
569 | |
570 if (ctx->zstream.avail_out >= 8) { | |
571 trailer = (struct gztrailer *) ctx->out_buf->last; | |
572 ctx->out_buf->last += 8; | |
573 ctx->out_buf->last_buf = 1; | |
574 | |
575 } else { | |
576 b = ngx_create_temp_buf(r->pool, 8); | |
577 if (b == NULL) { | |
578 ngx_http_gzip_error(ctx); | |
579 return NGX_ERROR; | |
580 } | |
581 | |
582 b->last_buf = 1; | |
583 | |
584 cl = ngx_alloc_chain_link(r->pool); | |
585 if (cl == NULL) { | |
586 ngx_http_gzip_error(ctx); | |
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 trailer = (struct gztrailer *) b->pos; | |
595 b->last += 8; | |
596 } | |
597 | |
598 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) | |
599 | |
600 trailer->crc32 = ctx->crc32; | |
601 trailer->zlen = ctx->zin; | |
602 | |
603 #else | |
604 trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff); | |
605 trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff); | |
606 trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff); | |
607 trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff); | |
608 | |
609 trailer->zlen[0] = (u_char) (ctx->zin & 0xff); | |
610 trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff); | |
611 trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff); | |
612 trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff); | |
613 #endif | |
614 | |
615 ctx->zstream.avail_in = 0; | |
616 ctx->zstream.avail_out = 0; | |
617 | |
618 ctx->done = 1; | |
619 | |
620 r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; | |
621 | |
622 break; | 348 break; |
623 } | 349 } |
624 | 350 |
625 if (conf->no_buffer && ctx->in == NULL) { | 351 if (rc == NGX_ERROR) { |
626 | 352 goto failed; |
627 cl = ngx_alloc_chain_link(r->pool); | |
628 if (cl == NULL) { | |
629 ngx_http_gzip_error(ctx); | |
630 return NGX_ERROR; | |
631 } | |
632 | |
633 cl->buf = ctx->out_buf; | |
634 cl->next = NULL; | |
635 *ctx->last_out = cl; | |
636 ctx->last_out = &cl->next; | |
637 | |
638 break; | |
639 } | 353 } |
354 | |
355 /* rc == NGX_AGAIN */ | |
640 } | 356 } |
641 | 357 |
642 if (ctx->out == NULL) { | 358 if (ctx->out == NULL) { |
643 return NGX_AGAIN; | 359 return NGX_AGAIN; |
644 } | 360 } |
645 | 361 |
362 if (!ctx->gzheader) { | |
363 if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) { | |
364 goto failed; | |
365 } | |
366 } | |
367 | |
646 rc = ngx_http_next_body_filter(r, ctx->out); | 368 rc = ngx_http_next_body_filter(r, ctx->out); |
647 | 369 |
648 if (rc == NGX_ERROR) { | 370 if (rc == NGX_ERROR) { |
649 ngx_http_gzip_error(ctx); | 371 goto failed; |
650 return NGX_ERROR; | |
651 } | 372 } |
652 | 373 |
653 ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, | 374 ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, |
654 (ngx_buf_tag_t) &ngx_http_gzip_filter_module); | 375 (ngx_buf_tag_t) &ngx_http_gzip_filter_module); |
655 ctx->last_out = &ctx->out; | 376 ctx->last_out = &ctx->out; |
658 | 379 |
659 if (ctx->done) { | 380 if (ctx->done) { |
660 return rc; | 381 return rc; |
661 } | 382 } |
662 } | 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; | |
663 } | 778 } |
664 | 779 |
665 | 780 |
666 static void * | 781 static void * |
667 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) |
712 ngx_http_gzip_ctx_t *ctx = opaque; | 827 ngx_http_gzip_ctx_t *ctx = opaque; |
713 | 828 |
714 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, |
715 "gzip free: %p", address); | 830 "gzip free: %p", address); |
716 #endif | 831 #endif |
717 } | |
718 | |
719 | |
720 static void | |
721 ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx) | |
722 { | |
723 deflateEnd(&ctx->zstream); | |
724 | |
725 if (ctx->preallocated) { | |
726 ngx_pfree(ctx->request->pool, ctx->preallocated); | |
727 } | |
728 | |
729 ctx->zstream.avail_in = 0; | |
730 ctx->zstream.avail_out = 0; | |
731 | |
732 ctx->done = 1; | |
733 | |
734 return; | |
735 } | 832 } |
736 | 833 |
737 | 834 |
738 static ngx_int_t | 835 static ngx_int_t |
739 ngx_http_gzip_add_variables(ngx_conf_t *cf) | 836 ngx_http_gzip_add_variables(ngx_conf_t *cf) |