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