1 /* mod_deflate 2 * 3 * 4 * bug fix on Robert Jakabosky from alphatrade.com's lighttp 1.4.10 mod_deflate patch 5 * 6 * Bug fix and new features: 7 * 1) fix loop bug when content-length is bigger than work-block-size*k 8 * 9 * ------- 10 * 11 * lighttpd-1.4.26.mod_deflate.patch from 12 * https://redmine.lighttpd.net/projects/1/wiki/Docs_ModDeflate 13 * 14 * ------- 15 * 16 * Patch further modified in this incarnation. 17 * 18 * Note: this patch only handles completed responses 19 * (r->resp_body_finished) 20 * this patch does not currently handle streaming dynamic responses, 21 * and therefore also does not worry about Transfer-Encoding: chunked 22 * (or having separate con->output_queue for chunked-encoded output) 23 * (or using separate buffers per connection instead of p->tmp_buf) 24 * (or handling interactions with block buffering and write timeouts) 25 * 26 * Bug fix: 27 * - fixed major bug with compressing chunks with offset > 0 28 * x-ref: 29 * "Response breaking in mod_deflate" 30 * https://redmine.lighttpd.net/issues/986 31 * - fix broken (in some cases) chunk accounting in deflate_compress_response() 32 * - fix broken bzip2 33 * x-ref: 34 * "mod_deflate's bzip2 broken by default" 35 * https://redmine.lighttpd.net/issues/2035 36 * - fix mismatch with current chunk interfaces 37 * x-ref: 38 * "Weird things in chunk.c (functions only handling specific cases, unexpected behaviour)" 39 * https://redmine.lighttpd.net/issues/1510 40 * 41 * Behavior changes from prior patch: 42 * - deflate.mimetypes must now be configured to enable compression 43 * deflate.mimetypes = ( ) # compress nothing (disabled; default) 44 * deflate.mimetypes = ( "" ) # compress all mimetypes 45 * deflate.mimetypes = ( "text/" ) # compress text/... mimetypes 46 * x-ref: 47 * "mod_deflate enabled by default" 48 * https://redmine.lighttpd.net/issues/1394 49 * - deflate.enabled directive removed (see new behavior of deflate.mimetypes) 50 * - deflate.debug removed (was developer debug trace, not end-user debug) 51 * - deflate.bzip2 replaced with deflate.allowed-encodings (like mod_compress) 52 * x-ref: 53 * "mod_deflate should allow limiting of compression algorithm from the configuration file" 54 * https://redmine.lighttpd.net/issues/996 55 * "mod_compress disabling methods" 56 * https://redmine.lighttpd.net/issues/1773 57 * - deflate.nocompress-url removed since disabling compression for a URL 58 * can now easily be done by setting to a blank list either directive 59 * deflate.accept_encodings = () or deflate.mimetypes = () in a conditional 60 * block, e.g. $HTTP["url"] =~ "....." { deflate.mimetypes = ( ) } 61 * - deflate.sync-flush removed; controlled by r->conf.stream_response_body 62 * (though streaming compression not currently implemented in mod_deflate) 63 * - inactive directives in this patch 64 * (since r->resp_body_finished required) 65 * deflate.work-block-size 66 * deflate.output-buffer-size 67 * - remove weak file size check; SIGBUS is trapped, file that shrink will error 68 * x-ref: 69 * "mod_deflate: filesize check is too weak" 70 * https://redmine.lighttpd.net/issues/1512 71 * - change default deflate.min-compress-size from 0 to now be 256 72 * http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits 73 * Apache 2.4 mod_deflate minimum is 68 bytes 74 * Akamai recommends minimum 860 bytes 75 * Google recommends minimum be somewhere in range between 150 and 1024 bytes 76 * - deflate.max-compress-size new directive (in kb like compress.max_filesize) 77 * - deflate.mem-level removed (too many knobs for little benefit) 78 * - deflate.window-size removed (too many knobs for little benefit) 79 * 80 * Future: 81 * - config directives may be changed, renamed, or removed 82 * e.g. A set of reasonable defaults might be chosen 83 * instead of making them configurable. 84 * deflate.min-compress-size 85 * - might add deflate.mimetypes-exclude = ( ... ) for list of mimetypes 86 * to avoid compressing, even if a broader deflate.mimetypes matched, 87 * e.g. to compress all "text/" except "text/special". 88 * - mod_compress and mod_deflate might merge overlapping feature sets 89 * (mod_compress.cache-dir does not yet have an equivalent in mod_deflate) 90 * 91 * Implementation notes: 92 * - http_chunk_append_mem() used instead of http_chunk_append_buffer() 93 * so that p->tmp_buf can be large and re-used. This results in an extra copy 94 * of compressed data before data is sent to network, though if the compressed 95 * size is larger than 64k, it ends up being sent to a temporary file on 96 * disk without suffering an extra copy in memory, and without extra chunk 97 * create and destroy. If this is ever changed to give away buffers, then use 98 * a unique hctx->output buffer per hctx; do not reuse p->tmp_buf across 99 * multiple requests being handled in parallel. 100 */ 101 #include "first.h" 102 103 #include <sys/types.h> 104 #include <sys/stat.h> 105 #include "sys-mmap.h" 106 107 #include <fcntl.h> 108 #include <stdlib.h> 109 #include <string.h> 110 #include <errno.h> 111 #include <time.h> 112 #include <unistd.h> /* read() */ 113 114 #include "base.h" 115 #include "fdevent.h" 116 #include "log.h" 117 #include "buffer.h" 118 #include "etag.h" 119 #include "http_chunk.h" 120 #include "http_header.h" 121 #include "response.h" 122 123 #include "plugin.h" 124 125 #if defined HAVE_ZLIB_H && defined HAVE_LIBZ 126 # define USE_ZLIB 127 # include <zlib.h> 128 #endif 129 #ifndef Z_DEFAULT_COMPRESSION 130 #define Z_DEFAULT_COMPRESSION -1 131 #endif 132 #ifndef MAX_WBITS 133 #define MAX_WBITS 15 134 #endif 135 136 #if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2 137 # define USE_BZ2LIB 138 /* we don't need stdio interface */ 139 # define BZ_NO_STDIO 140 # include <bzlib.h> 141 #endif 142 143 #if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP 144 #define USE_MMAP 145 146 #include "sys-mmap.h" 147 #include <setjmp.h> 148 #include <signal.h> 149 150 static volatile int sigbus_jmp_valid; 151 static sigjmp_buf sigbus_jmp; 152 153 static void sigbus_handler(int sig) { 154 UNUSED(sig); 155 if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1); 156 log_failed_assert(__FILE__, __LINE__, "SIGBUS"); 157 } 158 #endif 159 160 /* request: accept-encoding */ 161 #define HTTP_ACCEPT_ENCODING_IDENTITY BV(0) 162 #define HTTP_ACCEPT_ENCODING_GZIP BV(1) 163 #define HTTP_ACCEPT_ENCODING_DEFLATE BV(2) 164 #define HTTP_ACCEPT_ENCODING_COMPRESS BV(3) 165 #define HTTP_ACCEPT_ENCODING_BZIP2 BV(4) 166 #define HTTP_ACCEPT_ENCODING_X_GZIP BV(5) 167 #define HTTP_ACCEPT_ENCODING_X_BZIP2 BV(6) 168 169 #define KByte * 1024 170 #define MByte * 1024 KByte 171 #define GByte * 1024 MByte 172 173 typedef struct { 174 const array *mimetypes; 175 unsigned int max_compress_size; 176 unsigned short min_compress_size; 177 unsigned short output_buffer_size; 178 unsigned short work_block_size; 179 unsigned short sync_flush; 180 short compression_level; 181 short allowed_encodings; 182 double max_loadavg; 183 } plugin_config; 184 185 typedef struct { 186 PLUGIN_DATA; 187 plugin_config defaults; 188 plugin_config conf; 189 190 buffer tmp_buf; 191 } plugin_data; 192 193 typedef struct { 194 union { 195 #ifdef USE_ZLIB 196 z_stream z; 197 #endif 198 #ifdef USE_BZ2LIB 199 bz_stream bz; 200 #endif 201 int dummy; 202 } u; 203 off_t bytes_in; 204 off_t bytes_out; 205 chunkqueue *in_queue; 206 buffer *output; 207 plugin_data *plugin_data; 208 int compression_type; 209 log_error_st *errh; 210 } handler_ctx; 211 212 static handler_ctx *handler_ctx_init() { 213 handler_ctx *hctx; 214 215 hctx = calloc(1, sizeof(*hctx)); 216 hctx->in_queue = chunkqueue_init(); 217 218 return hctx; 219 } 220 221 static void handler_ctx_free(handler_ctx *hctx) { 222 #if 0 223 if (hctx->output != &p->tmp_buf) { 224 buffer_free(hctx->output); 225 } 226 #endif 227 chunkqueue_free(hctx->in_queue); 228 free(hctx); 229 } 230 231 INIT_FUNC(mod_deflate_init) { 232 plugin_data * const p = calloc(1, sizeof(plugin_data)); 233 buffer_string_prepare_copy(&p->tmp_buf, 64 KByte); 234 return p; 235 } 236 237 FREE_FUNC(mod_deflate_free) { 238 plugin_data *p = p_d; 239 free(p->tmp_buf.ptr); 240 } 241 242 static void mod_deflate_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 243 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 244 case 0: /* deflate.mimetypes */ 245 pconf->mimetypes = cpv->v.a; 246 break; 247 case 1: /* deflate.allowed-encodings */ 248 pconf->allowed_encodings = (short)cpv->v.shrt; 249 break; 250 case 2: /* deflate.max-compress-size */ 251 pconf->max_compress_size = cpv->v.u; 252 break; 253 case 3: /* deflate.min-compress-size */ 254 pconf->min_compress_size = cpv->v.shrt; 255 break; 256 case 4: /* deflate.compression-level */ 257 pconf->compression_level = (short)cpv->v.shrt; 258 break; 259 case 5: /* deflate.output-buffer-size */ 260 pconf->output_buffer_size = cpv->v.shrt; 261 break; 262 case 6: /* deflate.work-block-size */ 263 pconf->work_block_size = cpv->v.shrt; 264 break; 265 case 7: /* deflate.max-loadavg */ 266 pconf->max_loadavg = cpv->v.d; 267 break; 268 default:/* should not happen */ 269 return; 270 } 271 } 272 273 static void mod_deflate_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 274 do { 275 mod_deflate_merge_config_cpv(pconf, cpv); 276 } while ((++cpv)->k_id != -1); 277 } 278 279 static void mod_deflate_patch_config(request_st * const r, plugin_data * const p) { 280 memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); 281 for (int i = 1, used = p->nconfig; i < used; ++i) { 282 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 283 mod_deflate_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 284 } 285 } 286 287 static short mod_deflate_encodings_to_flags(const array *encodings) { 288 short allowed_encodings = 0; 289 if (encodings->used) { 290 for (uint32_t j = 0; j < encodings->used; ++j) { 291 #if defined(USE_ZLIB) || defined(USE_BZ2LIB) 292 data_string *ds = (data_string *)encodings->data[j]; 293 #endif 294 #ifdef USE_ZLIB 295 if (NULL != strstr(ds->value.ptr, "gzip")) 296 allowed_encodings |= HTTP_ACCEPT_ENCODING_GZIP 297 | HTTP_ACCEPT_ENCODING_X_GZIP; 298 if (NULL != strstr(ds->value.ptr, "x-gzip")) 299 allowed_encodings |= HTTP_ACCEPT_ENCODING_X_GZIP; 300 if (NULL != strstr(ds->value.ptr, "deflate")) 301 allowed_encodings |= HTTP_ACCEPT_ENCODING_DEFLATE; 302 /* 303 if (NULL != strstr(ds->value.ptr, "compress")) 304 allowed_encodings |= HTTP_ACCEPT_ENCODING_COMPRESS; 305 */ 306 #endif 307 #ifdef USE_BZ2LIB 308 if (NULL != strstr(ds->value.ptr, "bzip2")) 309 allowed_encodings |= HTTP_ACCEPT_ENCODING_BZIP2 310 | HTTP_ACCEPT_ENCODING_X_BZIP2; 311 if (NULL != strstr(ds->value.ptr, "x-bzip2")) 312 allowed_encodings |= HTTP_ACCEPT_ENCODING_X_BZIP2; 313 #endif 314 } 315 } 316 else { 317 /* default encodings */ 318 #ifdef USE_ZLIB 319 allowed_encodings |= HTTP_ACCEPT_ENCODING_GZIP 320 | HTTP_ACCEPT_ENCODING_X_GZIP 321 | HTTP_ACCEPT_ENCODING_DEFLATE; 322 #endif 323 #ifdef USE_BZ2LIB 324 allowed_encodings |= HTTP_ACCEPT_ENCODING_BZIP2 325 | HTTP_ACCEPT_ENCODING_X_BZIP2; 326 #endif 327 } 328 return allowed_encodings; 329 } 330 331 SETDEFAULTS_FUNC(mod_deflate_set_defaults) { 332 static const config_plugin_keys_t cpk[] = { 333 { CONST_STR_LEN("deflate.mimetypes"), 334 T_CONFIG_ARRAY_VLIST, 335 T_CONFIG_SCOPE_CONNECTION } 336 ,{ CONST_STR_LEN("deflate.allow-encodings"), 337 T_CONFIG_ARRAY_VLIST, 338 T_CONFIG_SCOPE_CONNECTION } 339 ,{ CONST_STR_LEN("deflate.max-compress-size"), 340 T_CONFIG_INT, 341 T_CONFIG_SCOPE_CONNECTION } 342 ,{ CONST_STR_LEN("deflate.min-compress-size"), 343 T_CONFIG_SHORT, 344 T_CONFIG_SCOPE_CONNECTION } 345 ,{ CONST_STR_LEN("deflate.compression-level"), 346 T_CONFIG_SHORT, 347 T_CONFIG_SCOPE_CONNECTION } 348 ,{ CONST_STR_LEN("deflate.output-buffer-size"), 349 T_CONFIG_SHORT, 350 T_CONFIG_SCOPE_CONNECTION } 351 ,{ CONST_STR_LEN("deflate.work-block-size"), 352 T_CONFIG_SHORT, 353 T_CONFIG_SCOPE_CONNECTION } 354 ,{ CONST_STR_LEN("deflate.max-loadavg"), 355 T_CONFIG_STRING, 356 T_CONFIG_SCOPE_CONNECTION } 357 ,{ NULL, 0, 358 T_CONFIG_UNSET, 359 T_CONFIG_SCOPE_UNSET } 360 }; 361 362 plugin_data * const p = p_d; 363 if (!config_plugin_values_init(srv, p, cpk, "mod_deflate")) 364 return HANDLER_ERROR; 365 366 /* process and validate config directives 367 * (init i to 0 if global context; to 1 to skip empty global context) */ 368 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 369 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 370 for (; -1 != cpv->k_id; ++cpv) { 371 switch (cpv->k_id) { 372 case 0: /* deflate.mimetypes */ 373 /* mod_deflate matches mimetype as prefix of Content-Type 374 * so ignore '*' at end of mimetype for end-user flexibility 375 * in specifying trailing wildcard to grouping of mimetypes */ 376 for (uint32_t m = 0; m < cpv->v.a->used; ++m) { 377 buffer *mimetype=&((data_string *)cpv->v.a->data[m])->value; 378 size_t len = buffer_string_length(mimetype); 379 if (len > 2 && mimetype->ptr[len-1] == '*') 380 buffer_string_set_length(mimetype, len-1); 381 } 382 if (0 == cpv->v.a->used) cpv->v.a = NULL; 383 break; 384 case 1: /* deflate.allowed-encodings */ 385 cpv->v.shrt = (unsigned short) 386 mod_deflate_encodings_to_flags(cpv->v.a); 387 cpv->vtype = T_CONFIG_SHORT; 388 break; 389 case 2: /* deflate.max-compress-size */ 390 case 3: /* deflate.min-compress-size */ 391 break; 392 case 4: /* deflate.compression-level */ 393 if ((cpv->v.shrt < 1 || cpv->v.shrt > 9) 394 && *(short *)&cpv->v.shrt != -1) { 395 log_error(srv->errh, __FILE__, __LINE__, 396 "compression-level must be between 1 and 9: %hu", 397 cpv->v.shrt); 398 return HANDLER_ERROR; 399 } 400 break; 401 case 5: /* deflate.output-buffer-size */ 402 case 6: /* deflate.work-block-size */ 403 break; 404 case 7: /* deflate.max-loadavg */ 405 cpv->v.d = (!buffer_string_is_empty(cpv->v.b)) 406 ? strtod(cpv->v.b->ptr, NULL) 407 : 0.0; 408 break; 409 default:/* should not happen */ 410 break; 411 } 412 } 413 } 414 415 p->defaults.allowed_encodings = 0; 416 p->defaults.max_compress_size = 128*1024; /*(128 MB measured as num KB)*/ 417 p->defaults.min_compress_size = 256; 418 p->defaults.compression_level = -1; 419 p->defaults.output_buffer_size = 0; 420 p->defaults.work_block_size = 2048; 421 p->defaults.max_loadavg = 0.0; 422 p->defaults.sync_flush = 0; 423 424 /* initialize p->defaults from global config context */ 425 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 426 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 427 if (-1 != cpv->k_id) 428 mod_deflate_merge_config(&p->defaults, cpv); 429 } 430 431 return HANDLER_GO_ON; 432 } 433 434 435 #if defined(USE_ZLIB) || defined(USE_BZ2LIB) 436 static int stream_http_chunk_append_mem(request_st * const r, const handler_ctx * const hctx, size_t len) { 437 /* future: might also write stream to hctx temporary file in compressed file cache */ 438 return http_chunk_append_mem(r, hctx->output->ptr, len); 439 } 440 #endif 441 442 443 #ifdef USE_ZLIB 444 445 static int stream_deflate_init(handler_ctx *hctx) { 446 z_stream * const z = &hctx->u.z; 447 const plugin_data * const p = hctx->plugin_data; 448 z->zalloc = Z_NULL; 449 z->zfree = Z_NULL; 450 z->opaque = Z_NULL; 451 z->total_in = 0; 452 z->total_out = 0; 453 z->next_out = (unsigned char *)hctx->output->ptr; 454 z->avail_out = hctx->output->size; 455 456 if (Z_OK != deflateInit2(z, 457 p->conf.compression_level > 0 458 ? p->conf.compression_level 459 : Z_DEFAULT_COMPRESSION, 460 Z_DEFLATED, 461 (hctx->compression_type == HTTP_ACCEPT_ENCODING_GZIP) 462 ? (MAX_WBITS | 16) /*(0x10 flags gzip header, trailer)*/ 463 : -MAX_WBITS, /*(negate to suppress zlib header)*/ 464 8, /* default memLevel */ 465 Z_DEFAULT_STRATEGY)) { 466 return -1; 467 } 468 469 return 0; 470 } 471 472 static int stream_deflate_compress(request_st * const r, handler_ctx * const hctx, unsigned char * const start, off_t st_size) { 473 z_stream * const z = &(hctx->u.z); 474 size_t len; 475 476 z->next_in = start; 477 z->avail_in = st_size; 478 hctx->bytes_in += st_size; 479 480 /* compress data */ 481 do { 482 if (Z_OK != deflate(z, Z_NO_FLUSH)) return -1; 483 484 if (z->avail_out == 0 || z->avail_in > 0) { 485 len = hctx->output->size - z->avail_out; 486 hctx->bytes_out += len; 487 stream_http_chunk_append_mem(r, hctx, len); 488 z->next_out = (unsigned char *)hctx->output->ptr; 489 z->avail_out = hctx->output->size; 490 } 491 } while (z->avail_in > 0); 492 493 return 0; 494 } 495 496 static int stream_deflate_flush(request_st * const r, handler_ctx * const hctx, int end) { 497 z_stream * const z = &(hctx->u.z); 498 const plugin_data *p = hctx->plugin_data; 499 size_t len; 500 int rc = 0; 501 int done; 502 503 /* compress data */ 504 do { 505 done = 1; 506 if (end) { 507 rc = deflate(z, Z_FINISH); 508 if (rc == Z_OK) { 509 done = 0; 510 } else if (rc != Z_STREAM_END) { 511 return -1; 512 } 513 } else { 514 if (p->conf.sync_flush) { 515 rc = deflate(z, Z_SYNC_FLUSH); 516 if (rc != Z_OK) return -1; 517 } else if (z->avail_in > 0) { 518 rc = deflate(z, Z_NO_FLUSH); 519 if (rc != Z_OK) return -1; 520 } 521 } 522 523 len = hctx->output->size - z->avail_out; 524 if (z->avail_out == 0 || (len > 0 && (end || p->conf.sync_flush))) { 525 hctx->bytes_out += len; 526 stream_http_chunk_append_mem(r, hctx, len); 527 z->next_out = (unsigned char *)hctx->output->ptr; 528 z->avail_out = hctx->output->size; 529 } 530 } while (z->avail_in != 0 || !done); 531 532 return 0; 533 } 534 535 static int stream_deflate_end(handler_ctx *hctx) { 536 z_stream * const z = &(hctx->u.z); 537 int rc = deflateEnd(z); 538 if (Z_OK == rc || Z_DATA_ERROR == rc) return 0; 539 540 if (z->msg != NULL) { 541 log_error(hctx->errh, __FILE__, __LINE__, 542 "deflateEnd error ret=%d, msg=%s", rc, z->msg); 543 } else { 544 log_error(hctx->errh, __FILE__, __LINE__, 545 "deflateEnd error ret=%d", rc); 546 } 547 return -1; 548 } 549 550 #endif 551 552 553 #ifdef USE_BZ2LIB 554 555 static int stream_bzip2_init(handler_ctx *hctx) { 556 bz_stream * const bz = &hctx->u.bz; 557 const plugin_data * const p = hctx->plugin_data; 558 bz->bzalloc = NULL; 559 bz->bzfree = NULL; 560 bz->opaque = NULL; 561 bz->total_in_lo32 = 0; 562 bz->total_in_hi32 = 0; 563 bz->total_out_lo32 = 0; 564 bz->total_out_hi32 = 0; 565 bz->next_out = hctx->output->ptr; 566 bz->avail_out = hctx->output->size; 567 568 if (BZ_OK != BZ2_bzCompressInit(bz, 569 p->conf.compression_level > 0 570 ? p->conf.compression_level 571 : 9, /* blocksize = 900k */ 572 0, /* verbosity */ 573 0)) { /* workFactor: default */ 574 return -1; 575 } 576 577 return 0; 578 } 579 580 static int stream_bzip2_compress(request_st * const r, handler_ctx * const hctx, unsigned char * const start, off_t st_size) { 581 bz_stream * const bz = &(hctx->u.bz); 582 size_t len; 583 584 bz->next_in = (char *)start; 585 bz->avail_in = st_size; 586 hctx->bytes_in += st_size; 587 588 /* compress data */ 589 do { 590 if (BZ_RUN_OK != BZ2_bzCompress(bz, BZ_RUN)) return -1; 591 592 if (bz->avail_out == 0 || bz->avail_in > 0) { 593 len = hctx->output->size - bz->avail_out; 594 hctx->bytes_out += len; 595 stream_http_chunk_append_mem(r, hctx, len); 596 bz->next_out = hctx->output->ptr; 597 bz->avail_out = hctx->output->size; 598 } 599 } while (bz->avail_in > 0); 600 601 return 0; 602 } 603 604 static int stream_bzip2_flush(request_st * const r, handler_ctx * const hctx, int end) { 605 bz_stream * const bz = &(hctx->u.bz); 606 const plugin_data *p = hctx->plugin_data; 607 size_t len; 608 int rc; 609 int done; 610 611 /* compress data */ 612 do { 613 done = 1; 614 if (end) { 615 rc = BZ2_bzCompress(bz, BZ_FINISH); 616 if (rc == BZ_FINISH_OK) { 617 done = 0; 618 } else if (rc != BZ_STREAM_END) { 619 return -1; 620 } 621 } else if (bz->avail_in > 0) { 622 /* p->conf.sync_flush not implemented here, 623 * which would loop on BZ_FLUSH while BZ_FLUSH_OK 624 * until BZ_RUN_OK returned */ 625 rc = BZ2_bzCompress(bz, BZ_RUN); 626 if (rc != BZ_RUN_OK) { 627 return -1; 628 } 629 } 630 631 len = hctx->output->size - bz->avail_out; 632 if (bz->avail_out == 0 || (len > 0 && (end || p->conf.sync_flush))) { 633 hctx->bytes_out += len; 634 stream_http_chunk_append_mem(r, hctx, len); 635 bz->next_out = hctx->output->ptr; 636 bz->avail_out = hctx->output->size; 637 } 638 } while (bz->avail_in != 0 || !done); 639 640 return 0; 641 } 642 643 static int stream_bzip2_end(handler_ctx *hctx) { 644 bz_stream * const bz = &(hctx->u.bz); 645 int rc = BZ2_bzCompressEnd(bz); 646 if (BZ_OK == rc || BZ_DATA_ERROR == rc) return 0; 647 648 log_error(hctx->errh, __FILE__, __LINE__, 649 "BZ2_bzCompressEnd error ret=%d", rc); 650 return -1; 651 } 652 653 #endif 654 655 656 static int mod_deflate_stream_init(handler_ctx *hctx) { 657 switch(hctx->compression_type) { 658 #ifdef USE_ZLIB 659 case HTTP_ACCEPT_ENCODING_GZIP: 660 case HTTP_ACCEPT_ENCODING_DEFLATE: 661 return stream_deflate_init(hctx); 662 #endif 663 #ifdef USE_BZ2LIB 664 case HTTP_ACCEPT_ENCODING_BZIP2: 665 return stream_bzip2_init(hctx); 666 #endif 667 default: 668 return -1; 669 } 670 } 671 672 static int mod_deflate_compress(request_st * const r, handler_ctx * const hctx, unsigned char * const start, off_t st_size) { 673 if (0 == st_size) return 0; 674 switch(hctx->compression_type) { 675 #ifdef USE_ZLIB 676 case HTTP_ACCEPT_ENCODING_GZIP: 677 case HTTP_ACCEPT_ENCODING_DEFLATE: 678 return stream_deflate_compress(r, hctx, start, st_size); 679 #endif 680 #ifdef USE_BZ2LIB 681 case HTTP_ACCEPT_ENCODING_BZIP2: 682 return stream_bzip2_compress(r, hctx, start, st_size); 683 #endif 684 default: 685 UNUSED(r); 686 UNUSED(start); 687 return -1; 688 } 689 } 690 691 static int mod_deflate_stream_flush(request_st * const r, handler_ctx * const hctx, int end) { 692 if (0 == hctx->bytes_in) return 0; 693 switch(hctx->compression_type) { 694 #ifdef USE_ZLIB 695 case HTTP_ACCEPT_ENCODING_GZIP: 696 case HTTP_ACCEPT_ENCODING_DEFLATE: 697 return stream_deflate_flush(r, hctx, end); 698 #endif 699 #ifdef USE_BZ2LIB 700 case HTTP_ACCEPT_ENCODING_BZIP2: 701 return stream_bzip2_flush(r, hctx, end); 702 #endif 703 default: 704 UNUSED(r); 705 UNUSED(end); 706 return -1; 707 } 708 } 709 710 static void mod_deflate_note_ratio(request_st * const r, handler_ctx * const hctx) { 711 /* store compression ratio in environment 712 * for possible logging by mod_accesslog 713 * (late in response handling, so not seen by most other modules) */ 714 /*(should be called only at end of successful response compression)*/ 715 char ratio[LI_ITOSTRING_LENGTH]; 716 if (0 == hctx->bytes_in) return; 717 size_t len = 718 li_itostrn(ratio, sizeof(ratio), hctx->bytes_out * 100 / hctx->bytes_in); 719 http_header_env_set(r, CONST_STR_LEN("ratio"), ratio, len); 720 } 721 722 static int mod_deflate_stream_end(handler_ctx *hctx) { 723 switch(hctx->compression_type) { 724 #ifdef USE_ZLIB 725 case HTTP_ACCEPT_ENCODING_GZIP: 726 case HTTP_ACCEPT_ENCODING_DEFLATE: 727 return stream_deflate_end(hctx); 728 #endif 729 #ifdef USE_BZ2LIB 730 case HTTP_ACCEPT_ENCODING_BZIP2: 731 return stream_bzip2_end(hctx); 732 #endif 733 default: 734 return -1; 735 } 736 } 737 738 static void deflate_compress_cleanup(request_st * const r, handler_ctx * const hctx) { 739 const plugin_data *p = hctx->plugin_data; 740 r->plugin_ctx[p->id] = NULL; 741 742 if (0 != mod_deflate_stream_end(hctx)) { 743 log_error(r->conf.errh, __FILE__, __LINE__, "error closing stream"); 744 } 745 746 #if 1 /* unnecessary if deflate.min-compress-size is set to a reasonable value */ 747 if (hctx->bytes_in < hctx->bytes_out) { 748 log_error(r->conf.errh, __FILE__, __LINE__, 749 "uri %s in=%lld smaller than out=%lld", r->target.ptr, 750 (long long)hctx->bytes_in, (long long)hctx->bytes_out); 751 } 752 #endif 753 754 handler_ctx_free(hctx); 755 } 756 757 758 static int mod_deflate_file_chunk(request_st * const r, handler_ctx * const hctx, chunk * const c, off_t st_size) { 759 off_t abs_offset; 760 off_t toSend = -1; 761 char *start; 762 #ifdef USE_MMAP 763 off_t we_want_to_mmap = 2 MByte; 764 off_t we_want_to_send = st_size; 765 volatile int mapped = 0;/* quiet warning: might be clobbered by 'longjmp' */ 766 #else 767 start = NULL; 768 #endif 769 770 if (-1 == c->file.fd) { /* open the file if not already open */ 771 if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, r->conf.follow_symlink, O_RDONLY, 0))) { 772 log_perror(r->conf.errh, __FILE__, __LINE__, "open failed %s", c->mem->ptr); 773 return -1; 774 } 775 } 776 777 abs_offset = c->file.start + c->offset; 778 779 #ifdef USE_MMAP 780 /* mmap the buffer 781 * - first mmap 782 * - new mmap as the we are at the end of the last one */ 783 if (c->file.mmap.start == MAP_FAILED || 784 abs_offset == (off_t)(c->file.mmap.offset + c->file.mmap.length)) { 785 786 /* Optimizations for the future: 787 * 788 * adaptive mem-mapping 789 * the problem: 790 * we mmap() the whole file. If someone has alot large files and 32bit 791 * machine the virtual address area will be unrun and we will have a failing 792 * mmap() call. 793 * solution: 794 * only mmap 16M in one chunk and move the window as soon as we have finished 795 * the first 8M 796 * 797 * read-ahead buffering 798 * the problem: 799 * sending out several large files in parallel trashes the read-ahead of the 800 * kernel leading to long wait-for-seek times. 801 * solutions: (increasing complexity) 802 * 1. use madvise 803 * 2. use a internal read-ahead buffer in the chunk-structure 804 * 3. use non-blocking IO for file-transfers 805 * */ 806 807 /* all mmap()ed areas are 512kb expect the last which might be smaller */ 808 off_t to_mmap; 809 810 /* this is a remap, move the mmap-offset */ 811 if (c->file.mmap.start != MAP_FAILED) { 812 munmap(c->file.mmap.start, c->file.mmap.length); 813 c->file.mmap.offset += we_want_to_mmap; 814 } else { 815 /* in case the range-offset is after the first mmap()ed area we skip the area */ 816 c->file.mmap.offset = 0; 817 818 while (c->file.mmap.offset + we_want_to_mmap < c->file.start) { 819 c->file.mmap.offset += we_want_to_mmap; 820 } 821 } 822 823 /* length is rel, c->offset too, assume there is no limit at the mmap-boundaries */ 824 to_mmap = (c->file.start + c->file.length) - c->file.mmap.offset; 825 if (to_mmap > we_want_to_mmap) to_mmap = we_want_to_mmap; 826 /* we have more to send than we can mmap() at once */ 827 if (we_want_to_send > to_mmap) we_want_to_send = to_mmap; 828 829 if (MAP_FAILED == (c->file.mmap.start = mmap(0, (size_t)to_mmap, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset)) 830 && (errno != EINVAL || MAP_FAILED == (c->file.mmap.start = mmap(0, (size_t)to_mmap, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset)))) { 831 log_perror(r->conf.errh, __FILE__, __LINE__, 832 "mmap failed %s %d", c->mem->ptr, c->file.fd); 833 834 return -1; 835 } 836 837 c->file.mmap.length = to_mmap; 838 #ifdef HAVE_MADVISE 839 /* don't advise files < 64Kb */ 840 if (c->file.mmap.length > (64 KByte) && 841 0 != madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED)) { 842 log_perror(r->conf.errh, __FILE__, __LINE__, 843 "madvise failed %s %d", c->mem->ptr, c->file.fd); 844 } 845 #endif 846 847 /* chunk_reset() or chunk_free() will cleanup for us */ 848 } 849 850 /* to_send = abs_mmap_end - abs_offset */ 851 toSend = (c->file.mmap.offset + c->file.mmap.length) - abs_offset; 852 if (toSend > we_want_to_send) toSend = we_want_to_send; 853 854 if (toSend < 0) { 855 log_error(r->conf.errh, __FILE__, __LINE__, 856 "toSend is negative: %lld %lld %lld %lld", 857 (long long)toSend, 858 (long long)c->file.mmap.length, 859 (long long)abs_offset, 860 (long long)c->file.mmap.offset); 861 force_assert(toSend < 0); 862 } 863 864 start = c->file.mmap.start; 865 mapped = 1; 866 #endif 867 868 if (MAP_FAILED == c->file.mmap.start) { 869 toSend = st_size; 870 if (toSend > 2 MByte) toSend = 2 MByte; 871 if (NULL == (start = malloc((size_t)toSend)) || -1 == lseek(c->file.fd, abs_offset, SEEK_SET) || toSend != read(c->file.fd, start, (size_t)toSend)) { 872 log_perror(r->conf.errh, __FILE__, __LINE__, "reading %s failed", c->mem->ptr); 873 874 free(start); 875 return -1; 876 } 877 abs_offset = 0; 878 } 879 880 #ifdef USE_MMAP 881 if (mapped) { 882 signal(SIGBUS, sigbus_handler); 883 sigbus_jmp_valid = 1; 884 if (0 != sigsetjmp(sigbus_jmp, 1)) { 885 sigbus_jmp_valid = 0; 886 887 log_error(r->conf.errh, __FILE__, __LINE__, 888 "SIGBUS in mmap: %s %d", c->mem->ptr, c->file.fd); 889 return -1; 890 } 891 } 892 #endif 893 894 if (mod_deflate_compress(r, hctx, 895 (unsigned char *)start + (abs_offset - c->file.mmap.offset), toSend) < 0) { 896 log_error(r->conf.errh, __FILE__, __LINE__, "compress failed."); 897 toSend = -1; 898 } 899 900 #ifdef USE_MMAP 901 if (mapped) 902 sigbus_jmp_valid = 0; 903 else 904 #endif 905 free(start); 906 907 return toSend; 908 } 909 910 911 static handler_t deflate_compress_response(request_st * const r, handler_ctx * const hctx) { 912 off_t len, max; 913 int close_stream; 914 915 /* move all chunk from write_queue into our in_queue, then adjust 916 * counters since r->write_queue is reused for compressed output */ 917 chunkqueue * const cq = r->write_queue; 918 len = chunkqueue_length(cq); 919 chunkqueue_remove_finished_chunks(cq); 920 chunkqueue_append_chunkqueue(hctx->in_queue, cq); 921 cq->bytes_in -= len; 922 cq->bytes_out -= len; 923 924 max = chunkqueue_length(hctx->in_queue); 925 #if 0 926 /* calculate max bytes to compress for this call */ 927 if (p->conf.sync_flush && max > (len = p->conf.work_block_size << 10)) { 928 max = len; 929 } 930 #endif 931 932 /* Compress chunks from in_queue into chunks for write_queue */ 933 while (max) { 934 chunk *c = hctx->in_queue->first; 935 936 switch(c->type) { 937 case MEM_CHUNK: 938 len = buffer_string_length(c->mem) - c->offset; 939 if (len > max) len = max; 940 if (mod_deflate_compress(r, hctx, (unsigned char *)c->mem->ptr+c->offset, len) < 0) { 941 log_error(r->conf.errh, __FILE__, __LINE__, "compress failed."); 942 return HANDLER_ERROR; 943 } 944 break; 945 case FILE_CHUNK: 946 len = c->file.length - c->offset; 947 if (len > max) len = max; 948 if ((len = mod_deflate_file_chunk(r, hctx, c, len)) < 0) { 949 log_error(r->conf.errh, __FILE__, __LINE__, "compress file chunk failed."); 950 return HANDLER_ERROR; 951 } 952 break; 953 default: 954 log_error(r->conf.errh, __FILE__, __LINE__, "%d type not known", c->type); 955 return HANDLER_ERROR; 956 } 957 958 max -= len; 959 chunkqueue_mark_written(hctx->in_queue, len); 960 } 961 962 /*(currently should always be true)*/ 963 /*(current implementation requires response be complete)*/ 964 close_stream = (r->resp_body_finished 965 && chunkqueue_is_empty(hctx->in_queue)); 966 if (mod_deflate_stream_flush(r, hctx, close_stream) < 0) { 967 log_error(r->conf.errh, __FILE__, __LINE__, "flush error"); 968 return HANDLER_ERROR; 969 } 970 971 return close_stream ? HANDLER_FINISHED : HANDLER_GO_ON; 972 } 973 974 975 static int mod_deflate_choose_encoding (const char *value, plugin_data *p, const char **label) { 976 /* get client side support encodings */ 977 int accept_encoding = 0; 978 #if !defined(USE_ZLIB) && !defined(USE_BZ2LIB) 979 UNUSED(value); 980 #else 981 for (; *value; ++value) { 982 const char *v; 983 while (*value == ' ' || *value == ',') ++value; 984 v = value; 985 while (*value!=' ' && *value!=',' && *value!=';' && *value!='\0') 986 ++value; 987 switch (value - v) { 988 case 4: 989 #ifdef USE_ZLIB 990 if (0 == memcmp(v, "gzip", 4)) 991 accept_encoding |= HTTP_ACCEPT_ENCODING_GZIP; 992 #endif 993 break; 994 case 5: 995 #ifdef USE_BZ2LIB 996 if (0 == memcmp(v, "bzip2", 5)) 997 accept_encoding |= HTTP_ACCEPT_ENCODING_BZIP2; 998 #endif 999 break; 1000 case 6: 1001 #ifdef USE_ZLIB 1002 if (0 == memcmp(v, "x-gzip", 6)) 1003 accept_encoding |= HTTP_ACCEPT_ENCODING_X_GZIP; 1004 #endif 1005 break; 1006 case 7: 1007 #ifdef USE_ZLIB 1008 if (0 == memcmp(v, "deflate", 7)) 1009 accept_encoding |= HTTP_ACCEPT_ENCODING_DEFLATE; 1010 #endif 1011 #ifdef USE_BZ2LIB 1012 if (0 == memcmp(v, "x-bzip2", 7)) 1013 accept_encoding |= HTTP_ACCEPT_ENCODING_X_BZIP2; 1014 #endif 1015 break; 1016 #if 0 1017 case 8: 1018 if (0 == memcmp(v, "identity", 8)) 1019 accept_encoding |= HTTP_ACCEPT_ENCODING_IDENTITY; 1020 else if (0 == memcmp(v, "compress", 8)) 1021 accept_encoding |= HTTP_ACCEPT_ENCODING_COMPRESS; 1022 break; 1023 #endif 1024 default: 1025 break; 1026 } 1027 if (*value == ';') { 1028 while (*value != ',' && *value != '\0') ++value; 1029 } 1030 if (*value == '\0') break; 1031 } 1032 #endif 1033 1034 /* mask to limit to allowed_encodings */ 1035 accept_encoding &= p->conf.allowed_encodings; 1036 1037 /* select best matching encoding */ 1038 #ifdef USE_BZ2LIB 1039 if (accept_encoding & HTTP_ACCEPT_ENCODING_BZIP2) { 1040 *label = "bzip2"; 1041 return HTTP_ACCEPT_ENCODING_BZIP2; 1042 } else if (accept_encoding & HTTP_ACCEPT_ENCODING_X_BZIP2) { 1043 *label = "x-bzip2"; 1044 return HTTP_ACCEPT_ENCODING_BZIP2; 1045 } else 1046 #endif 1047 #ifdef USE_ZLIB 1048 if (accept_encoding & HTTP_ACCEPT_ENCODING_GZIP) { 1049 *label = "gzip"; 1050 return HTTP_ACCEPT_ENCODING_GZIP; 1051 } else if (accept_encoding & HTTP_ACCEPT_ENCODING_X_GZIP) { 1052 *label = "x-gzip"; 1053 return HTTP_ACCEPT_ENCODING_GZIP; 1054 } else if (accept_encoding & HTTP_ACCEPT_ENCODING_DEFLATE) { 1055 *label = "deflate"; 1056 return HTTP_ACCEPT_ENCODING_DEFLATE; 1057 } else 1058 #endif 1059 if (0 == accept_encoding) { 1060 return 0; 1061 } else { 1062 return 0; 1063 } 1064 } 1065 1066 REQUEST_FUNC(mod_deflate_handle_response_start) { 1067 plugin_data *p = p_d; 1068 const buffer *vbro; 1069 buffer *vb; 1070 handler_ctx *hctx; 1071 const char *label; 1072 off_t len; 1073 size_t etaglen = 0; 1074 int compression_type; 1075 handler_t rc; 1076 1077 /*(current implementation requires response be complete)*/ 1078 if (!r->resp_body_finished) return HANDLER_GO_ON; 1079 if (r->http_method == HTTP_METHOD_HEAD) return HANDLER_GO_ON; 1080 if (r->resp_htags & HTTP_HEADER_TRANSFER_ENCODING) return HANDLER_GO_ON; 1081 1082 /* disable compression for some http status types. */ 1083 switch(r->http_status) { 1084 case 100: 1085 case 101: 1086 case 204: 1087 case 205: 1088 case 304: 1089 /* disable compression as we have no response entity */ 1090 return HANDLER_GO_ON; 1091 default: 1092 break; 1093 } 1094 1095 mod_deflate_patch_config(r, p); 1096 1097 /* check if deflate configured for any mimetypes */ 1098 if (NULL == p->conf.mimetypes) return HANDLER_GO_ON; 1099 1100 /* check if size of response is below min-compress-size or exceeds max*/ 1101 /* (r->resp_body_finished checked at top of routine) */ 1102 len = chunkqueue_length(r->write_queue); 1103 if (len <= (off_t)p->conf.min_compress_size) return HANDLER_GO_ON; 1104 if (p->conf.max_compress_size /*(max_compress_size in KB)*/ 1105 && len > ((off_t)p->conf.max_compress_size << 10)) { 1106 return HANDLER_GO_ON; 1107 } 1108 1109 /* Check if response has a Content-Encoding. */ 1110 vbro = http_header_response_get(r, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding")); 1111 if (NULL != vbro) return HANDLER_GO_ON; 1112 1113 /* Check Accept-Encoding for supported encoding. */ 1114 vbro = http_header_request_get(r, HTTP_HEADER_ACCEPT_ENCODING, CONST_STR_LEN("Accept-Encoding")); 1115 if (NULL == vbro) return HANDLER_GO_ON; 1116 1117 /* find matching encodings */ 1118 compression_type = mod_deflate_choose_encoding(vbro->ptr, p, &label); 1119 if (!compression_type) return HANDLER_GO_ON; 1120 1121 /* Check mimetype in response header "Content-Type" */ 1122 if (NULL != (vbro = http_header_response_get(r, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type")))) { 1123 if (NULL == array_match_value_prefix(p->conf.mimetypes, vbro)) return HANDLER_GO_ON; 1124 } else { 1125 /* If no Content-Type set, compress only if first p->conf.mimetypes value is "" */ 1126 data_string *mimetype = (data_string *)p->conf.mimetypes->data[0]; 1127 if (!buffer_string_is_empty(&mimetype->value)) return HANDLER_GO_ON; 1128 } 1129 1130 /* Vary: Accept-Encoding (response might change according to request Accept-Encoding) */ 1131 if (NULL != (vb = http_header_response_get(r, HTTP_HEADER_VARY, CONST_STR_LEN("Vary")))) { 1132 if (NULL == strstr(vb->ptr, "Accept-Encoding")) { 1133 buffer_append_string_len(vb, CONST_STR_LEN(",Accept-Encoding")); 1134 } 1135 } else { 1136 http_header_response_append(r, HTTP_HEADER_VARY, 1137 CONST_STR_LEN("Vary"), 1138 CONST_STR_LEN("Accept-Encoding")); 1139 } 1140 1141 /* check ETag as is done in http_response_handle_cachable() 1142 * (slightly imperfect (close enough?) match of ETag "000000" to "000000-gzip") */ 1143 vb = http_header_response_get(r, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag")); 1144 if (NULL != vb && (r->rqst_htags & HTTP_HEADER_IF_NONE_MATCH)) { 1145 const buffer *if_none_match = http_header_response_get(r, HTTP_HEADER_IF_NONE_MATCH, CONST_STR_LEN("If-None-Match")); 1146 etaglen = buffer_string_length(vb); 1147 if (etaglen 1148 && r->http_status < 300 /*(want 2xx only)*/ 1149 && NULL != if_none_match 1150 && 0 == strncmp(if_none_match->ptr, vb->ptr, etaglen-1) 1151 && if_none_match->ptr[etaglen-1] == '-' 1152 && 0 == strncmp(if_none_match->ptr+etaglen, label, strlen(label))) { 1153 1154 if (http_method_get_or_head(r->http_method)) { 1155 /* modify ETag response header in-place to remove '"' and append '-label"' */ 1156 vb->ptr[etaglen-1] = '-'; /*(overwrite end '"')*/ 1157 buffer_append_string(vb, label); 1158 buffer_append_string_len(vb, CONST_STR_LEN("\"")); 1159 /*buffer_copy_buffer(&r->physical.etag, vb);*//*(keep in sync?)*/ 1160 r->http_status = 304; 1161 } else { 1162 r->http_status = 412; 1163 } 1164 1165 /* response_start hook occurs after error docs have been handled. 1166 * For now, send back empty response body. 1167 * In the future, might extract the error doc code so that it 1168 * might be run again if response_start hooks return with 1169 * changed http_status and r->handler_module = NULL */ 1170 /* clear content length even if 304 since compressed length unknown */ 1171 http_response_body_clear(r, 0); 1172 1173 r->resp_body_finished = 1; 1174 r->handler_module = NULL; 1175 return HANDLER_GO_ON; 1176 } 1177 } 1178 1179 if (0.0 < p->conf.max_loadavg && p->conf.max_loadavg < r->con->srv->loadavg[0]) { 1180 return HANDLER_GO_ON; 1181 } 1182 1183 /* update ETag, if ETag response header is set */ 1184 if (etaglen) { 1185 /* modify ETag response header in-place to remove '"' and append '-label"' */ 1186 vb->ptr[etaglen-1] = '-'; /*(overwrite end '"')*/ 1187 buffer_append_string(vb, label); 1188 buffer_append_string_len(vb, CONST_STR_LEN("\"")); 1189 /*buffer_copy_buffer(&r->physical.etag, vb);*//*(keep in sync?)*/ 1190 } 1191 1192 /* set Content-Encoding to show selected compression type */ 1193 http_header_response_set(r, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"), label, strlen(label)); 1194 1195 /* clear Content-Length and r->write_queue if HTTP HEAD request 1196 * (alternatively, could return original Content-Length with HEAD 1197 * request if ETag not modified and Content-Encoding not added) */ 1198 if (HTTP_METHOD_HEAD == r->http_method) { 1199 /* ensure that uncompressed Content-Length is not sent in HEAD response */ 1200 http_response_body_clear(r, 0); 1201 return HANDLER_GO_ON; 1202 } 1203 1204 /* future: might use ETag to check if compressed content is in compressed file cache */ 1205 /*if (etaglen) { ... } *//* return if in file cache after updating r->write_queue */ 1206 1207 /* enable compression */ 1208 p->conf.sync_flush = 1209 (r->conf.stream_response_body && 0 == p->conf.output_buffer_size); 1210 hctx = handler_ctx_init(); 1211 hctx->plugin_data = p; 1212 hctx->compression_type = compression_type; 1213 hctx->errh = r->conf.errh; 1214 /* setup output buffer */ 1215 buffer_clear(&p->tmp_buf); 1216 hctx->output = &p->tmp_buf; 1217 if (0 != mod_deflate_stream_init(hctx)) { 1218 /*(should not happen unless ENOMEM)*/ 1219 handler_ctx_free(hctx); 1220 log_error(r->conf.errh, __FILE__, __LINE__, 1221 "Failed to initialize compression %s", label); 1222 /* restore prior Etag and unset Content-Encoding */ 1223 if (etaglen) { 1224 vb->ptr[etaglen-1] = '"'; /*(overwrite '-')*/ 1225 buffer_string_set_length(vb, etaglen); 1226 } 1227 http_header_response_unset(r, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding")); 1228 return HANDLER_GO_ON; 1229 } 1230 1231 if (r->resp_htags & HTTP_HEADER_CONTENT_LENGTH) { 1232 http_header_response_unset(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); 1233 } 1234 r->plugin_ctx[p->id] = hctx; 1235 1236 rc = deflate_compress_response(r, hctx); 1237 if (HANDLER_GO_ON != rc) { 1238 if (HANDLER_FINISHED == rc) { 1239 mod_deflate_note_ratio(r, hctx); 1240 } 1241 deflate_compress_cleanup(r, hctx); 1242 if (HANDLER_ERROR == rc) return HANDLER_ERROR; 1243 } 1244 1245 return HANDLER_GO_ON; 1246 } 1247 1248 static handler_t mod_deflate_cleanup(request_st * const r, void *p_d) { 1249 plugin_data *p = p_d; 1250 handler_ctx *hctx = r->plugin_ctx[p->id]; 1251 1252 if (NULL != hctx) deflate_compress_cleanup(r, hctx); 1253 1254 return HANDLER_GO_ON; 1255 } 1256 1257 int mod_deflate_plugin_init(plugin *p); 1258 int mod_deflate_plugin_init(plugin *p) { 1259 p->version = LIGHTTPD_VERSION_ID; 1260 p->name = "deflate"; 1261 1262 p->init = mod_deflate_init; 1263 p->cleanup = mod_deflate_free; 1264 p->set_defaults = mod_deflate_set_defaults; 1265 p->connection_reset = mod_deflate_cleanup; 1266 p->handle_response_start = mod_deflate_handle_response_start; 1267 1268 return 0; 1269 } 1270