1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * mod_deflate.c: Perform deflate content-encoding on the fly 19 * 20 * Written by Ian Holsman, Justin Erenkrantz, and Nick Kew 21 */ 22 23/* 24 * Portions of this software are based upon zlib code by Jean-loup Gailly 25 * (zlib functions gz_open and gzwrite, check_header) 26 */ 27 28/* zlib flags */ 29#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ 30#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ 31#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ 32#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ 33#define COMMENT 0x10 /* bit 4 set: file comment present */ 34#define RESERVED 0xE0 /* bits 5..7: reserved */ 35 36 37#include "httpd.h" 38#include "http_config.h" 39#include "http_log.h" 40#include "apr_lib.h" 41#include "apr_strings.h" 42#include "apr_general.h" 43#include "util_filter.h" 44#include "apr_buckets.h" 45#include "http_request.h" 46#define APR_WANT_STRFUNC 47#include "apr_want.h" 48#include "mod_ssl.h" 49 50#include "zlib.h" 51 52static const char deflateFilterName[] = "DEFLATE"; 53module AP_MODULE_DECLARE_DATA deflate_module; 54 55typedef struct deflate_filter_config_t 56{ 57 int windowSize; 58 int memlevel; 59 int compressionlevel; 60 apr_size_t bufferSize; 61 char *note_ratio_name; 62 char *note_input_name; 63 char *note_output_name; 64} deflate_filter_config; 65 66/* RFC 1952 Section 2.3 defines the gzip header: 67 * 68 * +---+---+---+---+---+---+---+---+---+---+ 69 * |ID1|ID2|CM |FLG| MTIME |XFL|OS | 70 * +---+---+---+---+---+---+---+---+---+---+ 71 */ 72static const char gzip_header[10] = 73{ '\037', '\213', Z_DEFLATED, 0, 74 0, 0, 0, 0, /* mtime */ 75 0, 0x03 /* Unix OS_CODE */ 76}; 77 78/* magic header */ 79static const char deflate_magic[2] = { '\037', '\213' }; 80 81/* windowsize is negative to suppress Zlib header */ 82#define DEFAULT_COMPRESSION Z_DEFAULT_COMPRESSION 83#define DEFAULT_WINDOWSIZE -15 84#define DEFAULT_MEMLEVEL 9 85#define DEFAULT_BUFFERSIZE 8096 86 87static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *mod_deflate_ssl_var = NULL; 88 89/* Check whether a request is gzipped, so we can un-gzip it. 90 * If a request has multiple encodings, we need the gzip 91 * to be the outermost non-identity encoding. 92 */ 93static int check_gzip(request_rec *r, apr_table_t *hdrs1, apr_table_t *hdrs2) 94{ 95 int found = 0; 96 apr_table_t *hdrs = hdrs1; 97 const char *encoding = apr_table_get(hdrs, "Content-Encoding"); 98 99 if (!encoding && (hdrs2 != NULL)) { 100 /* the output filter has two tables and a content_encoding to check */ 101 encoding = apr_table_get(hdrs2, "Content-Encoding"); 102 hdrs = hdrs2; 103 if (!encoding) { 104 encoding = r->content_encoding; 105 hdrs = NULL; 106 } 107 } 108 if (encoding && *encoding) { 109 110 /* check the usual/simple case first */ 111 if (!strcasecmp(encoding, "gzip") 112 || !strcasecmp(encoding, "x-gzip")) { 113 found = 1; 114 if (hdrs) { 115 apr_table_unset(hdrs, "Content-Encoding"); 116 } 117 else { 118 r->content_encoding = NULL; 119 } 120 } 121 else if (ap_strchr_c(encoding, ',') != NULL) { 122 /* If the outermost encoding isn't gzip, there's nowt 123 * we can do. So only check the last non-identity token 124 */ 125 char *new_encoding = apr_pstrdup(r->pool, encoding); 126 char *ptr; 127 for(;;) { 128 char *token = ap_strrchr(new_encoding, ','); 129 if (!token) { /* gzip:identity or other:identity */ 130 if (!strcasecmp(new_encoding, "gzip") 131 || !strcasecmp(new_encoding, "x-gzip")) { 132 found = 1; 133 if (hdrs) { 134 apr_table_unset(hdrs, "Content-Encoding"); 135 } 136 else { 137 r->content_encoding = NULL; 138 } 139 } 140 break; /* seen all tokens */ 141 } 142 for (ptr=token+1; apr_isspace(*ptr); ++ptr); 143 if (!strcasecmp(ptr, "gzip") 144 || !strcasecmp(ptr, "x-gzip")) { 145 *token = '\0'; 146 if (hdrs) { 147 apr_table_setn(hdrs, "Content-Encoding", new_encoding); 148 } 149 else { 150 r->content_encoding = new_encoding; 151 } 152 found = 1; 153 } 154 else if (!ptr[0] || !strcasecmp(ptr, "identity")) { 155 *token = '\0'; 156 continue; /* strip the token and find the next one */ 157 } 158 break; /* found a non-identity token */ 159 } 160 } 161 } 162 /* 163 * If we have dealt with the headers above but content_encoding was set 164 * before sync it with the new value in the hdrs table as 165 * r->content_encoding takes precedence later on in the http_header_filter 166 * and hence would destroy what we have just set in the hdrs table. 167 */ 168 if (hdrs && r->content_encoding) { 169 r->content_encoding = apr_table_get(hdrs, "Content-Encoding"); 170 } 171 return found; 172} 173 174/* Outputs a long in LSB order to the given file 175 * only the bottom 4 bits are required for the deflate file format. 176 */ 177static void putLong(unsigned char *string, unsigned long x) 178{ 179 string[0] = (unsigned char)(x & 0xff); 180 string[1] = (unsigned char)((x & 0xff00) >> 8); 181 string[2] = (unsigned char)((x & 0xff0000) >> 16); 182 string[3] = (unsigned char)((x & 0xff000000) >> 24); 183} 184 185/* Inputs a string and returns a long. 186 */ 187static unsigned long getLong(unsigned char *string) 188{ 189 return ((unsigned long)string[0]) 190 | (((unsigned long)string[1]) << 8) 191 | (((unsigned long)string[2]) << 16) 192 | (((unsigned long)string[3]) << 24); 193} 194 195static void *create_deflate_server_config(apr_pool_t *p, server_rec *s) 196{ 197 deflate_filter_config *c = apr_pcalloc(p, sizeof *c); 198 199 c->memlevel = DEFAULT_MEMLEVEL; 200 c->windowSize = DEFAULT_WINDOWSIZE; 201 c->bufferSize = DEFAULT_BUFFERSIZE; 202 c->compressionlevel = DEFAULT_COMPRESSION; 203 204 return c; 205} 206 207static const char *deflate_set_window_size(cmd_parms *cmd, void *dummy, 208 const char *arg) 209{ 210 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, 211 &deflate_module); 212 int i; 213 214 i = atoi(arg); 215 216 if (i < 1 || i > 15) 217 return "DeflateWindowSize must be between 1 and 15"; 218 219 c->windowSize = i * -1; 220 221 return NULL; 222} 223 224static const char *deflate_set_buffer_size(cmd_parms *cmd, void *dummy, 225 const char *arg) 226{ 227 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, 228 &deflate_module); 229 int n = atoi(arg); 230 231 if (n <= 0) { 232 return "DeflateBufferSize should be positive"; 233 } 234 235 c->bufferSize = (apr_size_t)n; 236 237 return NULL; 238} 239static const char *deflate_set_note(cmd_parms *cmd, void *dummy, 240 const char *arg1, const char *arg2) 241{ 242 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, 243 &deflate_module); 244 245 if (arg2 == NULL) { 246 c->note_ratio_name = apr_pstrdup(cmd->pool, arg1); 247 } 248 else if (!strcasecmp(arg1, "ratio")) { 249 c->note_ratio_name = apr_pstrdup(cmd->pool, arg2); 250 } 251 else if (!strcasecmp(arg1, "input")) { 252 c->note_input_name = apr_pstrdup(cmd->pool, arg2); 253 } 254 else if (!strcasecmp(arg1, "output")) { 255 c->note_output_name = apr_pstrdup(cmd->pool, arg2); 256 } 257 else { 258 return apr_psprintf(cmd->pool, "Unknown note type %s", arg1); 259 } 260 261 return NULL; 262} 263 264static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy, 265 const char *arg) 266{ 267 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, 268 &deflate_module); 269 int i; 270 271 i = atoi(arg); 272 273 if (i < 1 || i > 9) 274 return "DeflateMemLevel must be between 1 and 9"; 275 276 c->memlevel = i; 277 278 return NULL; 279} 280 281static const char *deflate_set_compressionlevel(cmd_parms *cmd, void *dummy, 282 const char *arg) 283{ 284 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, 285 &deflate_module); 286 int i; 287 288 i = atoi(arg); 289 290 if (i < 1 || i > 9) 291 return "Compression Level must be between 1 and 9"; 292 293 c->compressionlevel = i; 294 295 return NULL; 296} 297 298typedef struct deflate_ctx_t 299{ 300 z_stream stream; 301 unsigned char *buffer; 302 unsigned long crc; 303 apr_bucket_brigade *bb, *proc_bb; 304 int (*libz_end_func)(z_streamp); 305 unsigned char *validation_buffer; 306 apr_size_t validation_buffer_length; 307 unsigned int inflate_init:1; 308 unsigned int filter_init:1; 309 unsigned int done:1; 310} deflate_ctx; 311 312/* Number of validation bytes (CRC and length) after the compressed data */ 313#define VALIDATION_SIZE 8 314/* Do not update ctx->crc, see comment in flush_libz_buffer */ 315#define NO_UPDATE_CRC 0 316/* Do update ctx->crc, see comment in flush_libz_buffer */ 317#define UPDATE_CRC 1 318 319static int flush_libz_buffer(deflate_ctx *ctx, deflate_filter_config *c, 320 struct apr_bucket_alloc_t *bucket_alloc, 321 int (*libz_func)(z_streamp, int), int flush, 322 int crc) 323{ 324 int zRC = Z_OK; 325 int done = 0; 326 unsigned int deflate_len; 327 apr_bucket *b; 328 329 for (;;) { 330 deflate_len = c->bufferSize - ctx->stream.avail_out; 331 332 if (deflate_len != 0) { 333 /* 334 * Do we need to update ctx->crc? Usually this is the case for 335 * inflate action where we need to do a crc on the output, whereas 336 * in the deflate case we need to do a crc on the input 337 */ 338 if (crc) { 339 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, 340 deflate_len); 341 } 342 b = apr_bucket_heap_create((char *)ctx->buffer, 343 deflate_len, NULL, 344 bucket_alloc); 345 APR_BRIGADE_INSERT_TAIL(ctx->bb, b); 346 ctx->stream.next_out = ctx->buffer; 347 ctx->stream.avail_out = c->bufferSize; 348 } 349 350 if (done) 351 break; 352 353 zRC = libz_func(&ctx->stream, flush); 354 355 /* 356 * We can ignore Z_BUF_ERROR because: 357 * When we call libz_func we can assume that 358 * 359 * - avail_in is zero (due to the surrounding code that calls 360 * flush_libz_buffer) 361 * - avail_out is non zero due to our actions some lines above 362 * 363 * So the only reason for Z_BUF_ERROR is that the internal libz 364 * buffers are now empty and thus we called libz_func one time 365 * too often. This does not hurt. It simply says that we are done. 366 */ 367 if (zRC == Z_BUF_ERROR) { 368 zRC = Z_OK; 369 break; 370 } 371 372 done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END); 373 374 if (zRC != Z_OK && zRC != Z_STREAM_END) 375 break; 376 } 377 return zRC; 378} 379 380static apr_status_t deflate_ctx_cleanup(void *data) 381{ 382 deflate_ctx *ctx = (deflate_ctx *)data; 383 384 if (ctx) 385 ctx->libz_end_func(&ctx->stream); 386 return APR_SUCCESS; 387} 388 389/* ETag must be unique among the possible representations, so a change 390 * to content-encoding requires a corresponding change to the ETag. 391 * This routine appends -transform (e.g., -gzip) to the entity-tag 392 * value inside the double-quotes if an ETag has already been set 393 * and its value already contains double-quotes. PR 39727 394 */ 395static void deflate_check_etag(request_rec *r, const char *transform) 396{ 397 const char *etag = apr_table_get(r->headers_out, "ETag"); 398 apr_size_t etaglen; 399 400 if ((etag && ((etaglen = strlen(etag)) > 2))) { 401 if (etag[etaglen - 1] == '"') { 402 apr_size_t transformlen = strlen(transform); 403 char *newtag = apr_palloc(r->pool, etaglen + transformlen + 2); 404 char *d = newtag; 405 char *e = d + etaglen - 1; 406 const char *s = etag; 407 408 for (; d < e; ++d, ++s) { 409 *d = *s; /* copy etag to newtag up to last quote */ 410 } 411 *d++ = '-'; /* append dash to newtag */ 412 s = transform; 413 e = d + transformlen; 414 for (; d < e; ++d, ++s) { 415 *d = *s; /* copy transform to newtag */ 416 } 417 *d++ = '"'; /* append quote to newtag */ 418 *d = '\0'; /* null terminate newtag */ 419 420 apr_table_setn(r->headers_out, "ETag", newtag); 421 } 422 } 423} 424 425static int have_ssl_compression(request_rec *r) 426{ 427 const char *comp; 428 if (mod_deflate_ssl_var == NULL) 429 return 0; 430 comp = mod_deflate_ssl_var(r->pool, r->server, r->connection, r, 431 "SSL_COMPRESS_METHOD"); 432 if (comp == NULL || *comp == '\0' || strcmp(comp, "NULL") == 0) 433 return 0; 434 return 1; 435} 436 437static apr_status_t deflate_out_filter(ap_filter_t *f, 438 apr_bucket_brigade *bb) 439{ 440 apr_bucket *e; 441 request_rec *r = f->r; 442 deflate_ctx *ctx = f->ctx; 443 int zRC; 444 apr_size_t len = 0, blen; 445 const char *data; 446 deflate_filter_config *c; 447 448 /* Do nothing if asked to filter nothing. */ 449 if (APR_BRIGADE_EMPTY(bb)) { 450 return APR_SUCCESS; 451 } 452 453 c = ap_get_module_config(r->server->module_config, 454 &deflate_module); 455 456 /* If we don't have a context, we need to ensure that it is okay to send 457 * the deflated content. If we have a context, that means we've done 458 * this before and we liked it. 459 * This could be not so nice if we always fail. But, if we succeed, 460 * we're in better shape. 461 */ 462 if (!ctx) { 463 char *token; 464 const char *encoding; 465 466 if (have_ssl_compression(r)) { 467 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 468 "Compression enabled at SSL level; not compressing " 469 "at HTTP level."); 470 ap_remove_output_filter(f); 471 return ap_pass_brigade(f->next, bb); 472 } 473 474 /* We have checked above that bb is not empty */ 475 e = APR_BRIGADE_LAST(bb); 476 if (APR_BUCKET_IS_EOS(e)) { 477 /* 478 * If we already know the size of the response, we can skip 479 * compression on responses smaller than the compression overhead. 480 * However, if we compress, we must initialize deflate_out before 481 * calling ap_pass_brigade() for the first time. Otherwise the 482 * headers will be sent to the client without 483 * "Content-Encoding: gzip". 484 */ 485 e = APR_BRIGADE_FIRST(bb); 486 while (1) { 487 apr_status_t rc; 488 if (APR_BUCKET_IS_EOS(e)) { 489 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 490 "Not compressing very small response of %" 491 APR_SIZE_T_FMT " bytes", len); 492 ap_remove_output_filter(f); 493 return ap_pass_brigade(f->next, bb); 494 } 495 if (APR_BUCKET_IS_METADATA(e)) { 496 e = APR_BUCKET_NEXT(e); 497 continue; 498 } 499 500 rc = apr_bucket_read(e, &data, &blen, APR_BLOCK_READ); 501 if (rc != APR_SUCCESS) 502 return rc; 503 len += blen; 504 /* 50 is for Content-Encoding and Vary headers and ETag suffix */ 505 if (len > sizeof(gzip_header) + VALIDATION_SIZE + 50) 506 break; 507 508 e = APR_BUCKET_NEXT(e); 509 } 510 } 511 512 ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx)); 513 514 /* 515 * Only work on main request, not subrequests, 516 * that are not a 204 response with no content 517 * and are not tagged with the no-gzip env variable 518 * and not a partial response to a Range request. 519 */ 520 if ((r->main != NULL) || (r->status == HTTP_NO_CONTENT) || 521 apr_table_get(r->subprocess_env, "no-gzip") || 522 apr_table_get(r->headers_out, "Content-Range") 523 ) { 524 if (APLOG_R_IS_LEVEL(r, APLOG_TRACE1)) { 525 const char *reason = 526 (r->main != NULL) ? "subrequest" : 527 (r->status == HTTP_NO_CONTENT) ? "no content" : 528 apr_table_get(r->subprocess_env, "no-gzip") ? "no-gzip" : 529 "content-range"; 530 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 531 "Not compressing (%s)", reason); 532 } 533 ap_remove_output_filter(f); 534 return ap_pass_brigade(f->next, bb); 535 } 536 537 /* Some browsers might have problems with content types 538 * other than text/html, so set gzip-only-text/html 539 * (with browsermatch) for them 540 */ 541 if (r->content_type == NULL 542 || strncmp(r->content_type, "text/html", 9)) { 543 const char *env_value = apr_table_get(r->subprocess_env, 544 "gzip-only-text/html"); 545 if ( env_value && (strcmp(env_value,"1") == 0) ) { 546 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 547 "Not compressing, (gzip-only-text/html)"); 548 ap_remove_output_filter(f); 549 return ap_pass_brigade(f->next, bb); 550 } 551 } 552 553 /* Let's see what our current Content-Encoding is. 554 * If it's already encoded, don't compress again. 555 * (We could, but let's not.) 556 */ 557 encoding = apr_table_get(r->headers_out, "Content-Encoding"); 558 if (encoding) { 559 const char *err_enc; 560 561 err_enc = apr_table_get(r->err_headers_out, "Content-Encoding"); 562 if (err_enc) { 563 encoding = apr_pstrcat(r->pool, encoding, ",", err_enc, NULL); 564 } 565 } 566 else { 567 encoding = apr_table_get(r->err_headers_out, "Content-Encoding"); 568 } 569 570 if (r->content_encoding) { 571 encoding = encoding ? apr_pstrcat(r->pool, encoding, ",", 572 r->content_encoding, NULL) 573 : r->content_encoding; 574 } 575 576 if (encoding) { 577 const char *tmp = encoding; 578 579 token = ap_get_token(r->pool, &tmp, 0); 580 while (token && *token) { 581 /* stolen from mod_negotiation: */ 582 if (strcmp(token, "identity") && strcmp(token, "7bit") && 583 strcmp(token, "8bit") && strcmp(token, "binary")) { 584 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 585 "Not compressing (content-encoding already " 586 " set: %s)", token); 587 ap_remove_output_filter(f); 588 return ap_pass_brigade(f->next, bb); 589 } 590 591 /* Otherwise, skip token */ 592 if (*tmp) { 593 ++tmp; 594 } 595 token = (*tmp) ? ap_get_token(r->pool, &tmp, 0) : NULL; 596 } 597 } 598 599 /* Even if we don't accept this request based on it not having 600 * the Accept-Encoding, we need to note that we were looking 601 * for this header and downstream proxies should be aware of that. 602 */ 603 apr_table_mergen(r->headers_out, "Vary", "Accept-Encoding"); 604 605 /* force-gzip will just force it out regardless if the browser 606 * can actually do anything with it. 607 */ 608 if (!apr_table_get(r->subprocess_env, "force-gzip")) { 609 const char *accepts; 610 /* if they don't have the line, then they can't play */ 611 accepts = apr_table_get(r->headers_in, "Accept-Encoding"); 612 if (accepts == NULL) { 613 ap_remove_output_filter(f); 614 return ap_pass_brigade(f->next, bb); 615 } 616 617 token = ap_get_token(r->pool, &accepts, 0); 618 while (token && token[0] && strcasecmp(token, "gzip")) { 619 /* skip parameters, XXX: ;q=foo evaluation? */ 620 while (*accepts == ';') { 621 ++accepts; 622 ap_get_token(r->pool, &accepts, 1); 623 } 624 625 /* retrieve next token */ 626 if (*accepts == ',') { 627 ++accepts; 628 } 629 token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL; 630 } 631 632 /* No acceptable token found. */ 633 if (token == NULL || token[0] == '\0') { 634 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 635 "Not compressing (no Accept-Encoding: gzip)"); 636 ap_remove_output_filter(f); 637 return ap_pass_brigade(f->next, bb); 638 } 639 } 640 else { 641 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 642 "Forcing compression (force-gzip set)"); 643 } 644 645 /* At this point we have decided to filter the content. Let's try to 646 * to initialize zlib (except for 304 responses, where we will only 647 * send out the headers). 648 */ 649 650 if (r->status != HTTP_NOT_MODIFIED) { 651 ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); 652 ctx->buffer = apr_palloc(r->pool, c->bufferSize); 653 ctx->libz_end_func = deflateEnd; 654 655 zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED, 656 c->windowSize, c->memlevel, 657 Z_DEFAULT_STRATEGY); 658 659 if (zRC != Z_OK) { 660 deflateEnd(&ctx->stream); 661 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01383) 662 "unable to init Zlib: " 663 "deflateInit2 returned %d: URL %s", 664 zRC, r->uri); 665 /* 666 * Remove ourselves as it does not make sense to return: 667 * We are not able to init libz and pass data down the chain 668 * uncompressed. 669 */ 670 ap_remove_output_filter(f); 671 return ap_pass_brigade(f->next, bb); 672 } 673 /* 674 * Register a cleanup function to ensure that we cleanup the internal 675 * libz resources. 676 */ 677 apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup, 678 apr_pool_cleanup_null); 679 680 /* Set the filter init flag so subsequent invocations know we are 681 * active. 682 */ 683 ctx->filter_init = 1; 684 } 685 686 /* 687 * Zlib initialization worked, so we can now change the important 688 * content metadata before sending the response out. 689 */ 690 691 /* If the entire Content-Encoding is "identity", we can replace it. */ 692 if (!encoding || !strcasecmp(encoding, "identity")) { 693 apr_table_setn(r->headers_out, "Content-Encoding", "gzip"); 694 } 695 else { 696 apr_table_mergen(r->headers_out, "Content-Encoding", "gzip"); 697 } 698 /* Fix r->content_encoding if it was set before */ 699 if (r->content_encoding) { 700 r->content_encoding = apr_table_get(r->headers_out, 701 "Content-Encoding"); 702 } 703 apr_table_unset(r->headers_out, "Content-Length"); 704 apr_table_unset(r->headers_out, "Content-MD5"); 705 deflate_check_etag(r, "gzip"); 706 707 /* For a 304 response, only change the headers */ 708 if (r->status == HTTP_NOT_MODIFIED) { 709 ap_remove_output_filter(f); 710 return ap_pass_brigade(f->next, bb); 711 } 712 713 /* add immortal gzip header */ 714 e = apr_bucket_immortal_create(gzip_header, sizeof gzip_header, 715 f->c->bucket_alloc); 716 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 717 718 /* initialize deflate output buffer */ 719 ctx->stream.next_out = ctx->buffer; 720 ctx->stream.avail_out = c->bufferSize; 721 } else if (!ctx->filter_init) { 722 /* Hmm. We've run through the filter init before as we have a ctx, 723 * but we never initialized. We probably have a dangling ref. Bail. 724 */ 725 return ap_pass_brigade(f->next, bb); 726 } 727 728 while (!APR_BRIGADE_EMPTY(bb)) 729 { 730 apr_bucket *b; 731 732 /* 733 * Optimization: If we are a HEAD request and bytes_sent is not zero 734 * it means that we have passed the content-length filter once and 735 * have more data to sent. This means that the content-length filter 736 * could not determine our content-length for the response to the 737 * HEAD request anyway (the associated GET request would deliver the 738 * body in chunked encoding) and we can stop compressing. 739 */ 740 if (r->header_only && r->bytes_sent) { 741 ap_remove_output_filter(f); 742 return ap_pass_brigade(f->next, bb); 743 } 744 745 e = APR_BRIGADE_FIRST(bb); 746 747 if (APR_BUCKET_IS_EOS(e)) { 748 char *buf; 749 750 ctx->stream.avail_in = 0; /* should be zero already anyway */ 751 /* flush the remaining data from the zlib buffers */ 752 flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, Z_FINISH, 753 NO_UPDATE_CRC); 754 755 buf = apr_palloc(r->pool, VALIDATION_SIZE); 756 putLong((unsigned char *)&buf[0], ctx->crc); 757 putLong((unsigned char *)&buf[4], ctx->stream.total_in); 758 759 b = apr_bucket_pool_create(buf, VALIDATION_SIZE, r->pool, 760 f->c->bucket_alloc); 761 APR_BRIGADE_INSERT_TAIL(ctx->bb, b); 762 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01384) 763 "Zlib: Compressed %ld to %ld : URL %s", 764 ctx->stream.total_in, ctx->stream.total_out, r->uri); 765 766 /* leave notes for logging */ 767 if (c->note_input_name) { 768 apr_table_setn(r->notes, c->note_input_name, 769 (ctx->stream.total_in > 0) 770 ? apr_off_t_toa(r->pool, 771 ctx->stream.total_in) 772 : "-"); 773 } 774 775 if (c->note_output_name) { 776 apr_table_setn(r->notes, c->note_output_name, 777 (ctx->stream.total_in > 0) 778 ? apr_off_t_toa(r->pool, 779 ctx->stream.total_out) 780 : "-"); 781 } 782 783 if (c->note_ratio_name) { 784 apr_table_setn(r->notes, c->note_ratio_name, 785 (ctx->stream.total_in > 0) 786 ? apr_itoa(r->pool, 787 (int)(ctx->stream.total_out 788 * 100 789 / ctx->stream.total_in)) 790 : "-"); 791 } 792 793 deflateEnd(&ctx->stream); 794 /* No need for cleanup any longer */ 795 apr_pool_cleanup_kill(r->pool, ctx, deflate_ctx_cleanup); 796 797 /* Remove EOS from the old list, and insert into the new. */ 798 APR_BUCKET_REMOVE(e); 799 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 800 801 /* Okay, we've seen the EOS. 802 * Time to pass it along down the chain. 803 */ 804 return ap_pass_brigade(f->next, ctx->bb); 805 } 806 807 if (APR_BUCKET_IS_FLUSH(e)) { 808 apr_status_t rv; 809 810 /* flush the remaining data from the zlib buffers */ 811 zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, 812 Z_SYNC_FLUSH, NO_UPDATE_CRC); 813 if (zRC != Z_OK) { 814 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01385) 815 "Zlib error %d flushing zlib output buffer (%s)", 816 zRC, ctx->stream.msg); 817 return APR_EGENERAL; 818 } 819 820 /* Remove flush bucket from old brigade anf insert into the new. */ 821 APR_BUCKET_REMOVE(e); 822 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 823 rv = ap_pass_brigade(f->next, ctx->bb); 824 if (rv != APR_SUCCESS) { 825 return rv; 826 } 827 continue; 828 } 829 830 if (APR_BUCKET_IS_METADATA(e)) { 831 /* 832 * Remove meta data bucket from old brigade and insert into the 833 * new. 834 */ 835 APR_BUCKET_REMOVE(e); 836 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 837 continue; 838 } 839 840 /* read */ 841 apr_bucket_read(e, &data, &len, APR_BLOCK_READ); 842 843 /* This crc32 function is from zlib. */ 844 ctx->crc = crc32(ctx->crc, (const Bytef *)data, len); 845 846 /* write */ 847 ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness, 848 * but we'll just have to 849 * trust zlib */ 850 ctx->stream.avail_in = len; 851 852 while (ctx->stream.avail_in != 0) { 853 if (ctx->stream.avail_out == 0) { 854 apr_status_t rv; 855 856 ctx->stream.next_out = ctx->buffer; 857 len = c->bufferSize - ctx->stream.avail_out; 858 859 b = apr_bucket_heap_create((char *)ctx->buffer, len, 860 NULL, f->c->bucket_alloc); 861 APR_BRIGADE_INSERT_TAIL(ctx->bb, b); 862 ctx->stream.avail_out = c->bufferSize; 863 /* Send what we have right now to the next filter. */ 864 rv = ap_pass_brigade(f->next, ctx->bb); 865 if (rv != APR_SUCCESS) { 866 return rv; 867 } 868 } 869 870 zRC = deflate(&(ctx->stream), Z_NO_FLUSH); 871 872 if (zRC != Z_OK) { 873 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01386) 874 "Zlib error %d deflating data (%s)", zRC, 875 ctx->stream.msg); 876 return APR_EGENERAL; 877 } 878 } 879 880 apr_bucket_delete(e); 881 } 882 883 apr_brigade_cleanup(bb); 884 return APR_SUCCESS; 885} 886 887/* This is the deflate input filter (inflates). */ 888static apr_status_t deflate_in_filter(ap_filter_t *f, 889 apr_bucket_brigade *bb, 890 ap_input_mode_t mode, 891 apr_read_type_e block, 892 apr_off_t readbytes) 893{ 894 apr_bucket *bkt; 895 request_rec *r = f->r; 896 deflate_ctx *ctx = f->ctx; 897 int zRC; 898 apr_status_t rv; 899 deflate_filter_config *c; 900 901 /* just get out of the way of things we don't want. */ 902 if (mode != AP_MODE_READBYTES) { 903 return ap_get_brigade(f->next, bb, mode, block, readbytes); 904 } 905 906 c = ap_get_module_config(r->server->module_config, &deflate_module); 907 908 if (!ctx) { 909 char deflate_hdr[10]; 910 apr_size_t len; 911 912 /* only work on main request/no subrequests */ 913 if (!ap_is_initial_req(r)) { 914 ap_remove_input_filter(f); 915 return ap_get_brigade(f->next, bb, mode, block, readbytes); 916 } 917 918 /* We can't operate on Content-Ranges */ 919 if (apr_table_get(r->headers_in, "Content-Range") != NULL) { 920 ap_remove_input_filter(f); 921 return ap_get_brigade(f->next, bb, mode, block, readbytes); 922 } 923 924 /* Check whether request body is gzipped. 925 * 926 * If it is, we're transforming the contents, invalidating 927 * some request headers including Content-Encoding. 928 * 929 * If not, we just remove ourself. 930 */ 931 if (check_gzip(r, r->headers_in, NULL) == 0) { 932 ap_remove_input_filter(f); 933 return ap_get_brigade(f->next, bb, mode, block, readbytes); 934 } 935 936 f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); 937 ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); 938 ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc); 939 ctx->buffer = apr_palloc(r->pool, c->bufferSize); 940 941 rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10); 942 if (rv != APR_SUCCESS) { 943 return rv; 944 } 945 946 /* zero length body? step aside */ 947 bkt = APR_BRIGADE_FIRST(ctx->bb); 948 if (APR_BUCKET_IS_EOS(bkt)) { 949 ap_remove_input_filter(f); 950 return ap_get_brigade(f->next, bb, mode, block, readbytes); 951 } 952 953 apr_table_unset(r->headers_in, "Content-Length"); 954 apr_table_unset(r->headers_in, "Content-MD5"); 955 956 len = 10; 957 rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len); 958 if (rv != APR_SUCCESS) { 959 return rv; 960 } 961 962 /* We didn't get the magic bytes. */ 963 if (len != 10 || 964 deflate_hdr[0] != deflate_magic[0] || 965 deflate_hdr[1] != deflate_magic[1]) { 966 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01387) "Zlib: Invalid header"); 967 return APR_EGENERAL; 968 } 969 970 /* We can't handle flags for now. */ 971 if (deflate_hdr[3] != 0) { 972 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01388) 973 "Zlib: Unsupported flags %02x", (int)deflate_hdr[3]); 974 return APR_EGENERAL; 975 } 976 977 zRC = inflateInit2(&ctx->stream, c->windowSize); 978 979 if (zRC != Z_OK) { 980 f->ctx = NULL; 981 inflateEnd(&ctx->stream); 982 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01389) 983 "unable to init Zlib: " 984 "inflateInit2 returned %d: URL %s", 985 zRC, r->uri); 986 ap_remove_input_filter(f); 987 return ap_get_brigade(f->next, bb, mode, block, readbytes); 988 } 989 990 /* initialize deflate output buffer */ 991 ctx->stream.next_out = ctx->buffer; 992 ctx->stream.avail_out = c->bufferSize; 993 994 apr_brigade_cleanup(ctx->bb); 995 } 996 997 if (APR_BRIGADE_EMPTY(ctx->proc_bb)) { 998 rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes); 999 1000 if (rv != APR_SUCCESS) { 1001 /* What about APR_EAGAIN errors? */ 1002 inflateEnd(&ctx->stream); 1003 return rv; 1004 } 1005 1006 for (bkt = APR_BRIGADE_FIRST(ctx->bb); 1007 bkt != APR_BRIGADE_SENTINEL(ctx->bb); 1008 bkt = APR_BUCKET_NEXT(bkt)) 1009 { 1010 const char *data; 1011 apr_size_t len; 1012 1013 if (APR_BUCKET_IS_EOS(bkt)) { 1014 if (!ctx->done) { 1015 inflateEnd(&ctx->stream); 1016 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02481) 1017 "Encountered premature end-of-stream while inflating"); 1018 return APR_EGENERAL; 1019 } 1020 1021 /* Move everything to the returning brigade. */ 1022 APR_BUCKET_REMOVE(bkt); 1023 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, bkt); 1024 ap_remove_input_filter(f); 1025 break; 1026 } 1027 1028 if (APR_BUCKET_IS_FLUSH(bkt)) { 1029 apr_bucket *tmp_heap; 1030 zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH); 1031 if (zRC != Z_OK) { 1032 inflateEnd(&ctx->stream); 1033 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01391) 1034 "Zlib error %d inflating data (%s)", zRC, 1035 ctx->stream.msg); 1036 return APR_EGENERAL; 1037 } 1038 1039 ctx->stream.next_out = ctx->buffer; 1040 len = c->bufferSize - ctx->stream.avail_out; 1041 1042 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); 1043 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, 1044 NULL, f->c->bucket_alloc); 1045 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); 1046 ctx->stream.avail_out = c->bufferSize; 1047 1048 /* Move everything to the returning brigade. */ 1049 APR_BUCKET_REMOVE(bkt); 1050 APR_BRIGADE_CONCAT(bb, ctx->bb); 1051 break; 1052 } 1053 1054 /* sanity check - data after completed compressed body and before eos? */ 1055 if (ctx->done) { 1056 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02482) 1057 "Encountered extra data after compressed data"); 1058 return APR_EGENERAL; 1059 } 1060 1061 /* read */ 1062 apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ); 1063 1064 /* pass through zlib inflate. */ 1065 ctx->stream.next_in = (unsigned char *)data; 1066 ctx->stream.avail_in = len; 1067 1068 zRC = Z_OK; 1069 1070 while (ctx->stream.avail_in != 0) { 1071 if (ctx->stream.avail_out == 0) { 1072 apr_bucket *tmp_heap; 1073 ctx->stream.next_out = ctx->buffer; 1074 len = c->bufferSize - ctx->stream.avail_out; 1075 1076 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); 1077 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, 1078 NULL, f->c->bucket_alloc); 1079 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); 1080 ctx->stream.avail_out = c->bufferSize; 1081 } 1082 1083 zRC = inflate(&ctx->stream, Z_NO_FLUSH); 1084 1085 if (zRC == Z_STREAM_END) { 1086 break; 1087 } 1088 1089 if (zRC != Z_OK) { 1090 inflateEnd(&ctx->stream); 1091 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01392) 1092 "Zlib error %d inflating data (%s)", zRC, 1093 ctx->stream.msg); 1094 return APR_EGENERAL; 1095 } 1096 } 1097 if (zRC == Z_STREAM_END) { 1098 apr_bucket *tmp_heap; 1099 apr_size_t avail; 1100 1101 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01393) 1102 "Zlib: Inflated %ld to %ld : URL %s", 1103 ctx->stream.total_in, ctx->stream.total_out, 1104 r->uri); 1105 1106 len = c->bufferSize - ctx->stream.avail_out; 1107 1108 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); 1109 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, 1110 NULL, f->c->bucket_alloc); 1111 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); 1112 ctx->stream.avail_out = c->bufferSize; 1113 1114 avail = ctx->stream.avail_in; 1115 1116 /* Is the remaining 8 bytes already in the avail stream? */ 1117 if (avail >= 8) { 1118 unsigned long compCRC, compLen; 1119 compCRC = getLong(ctx->stream.next_in); 1120 if (ctx->crc != compCRC) { 1121 inflateEnd(&ctx->stream); 1122 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01394) 1123 "Zlib: CRC error inflating data"); 1124 return APR_EGENERAL; 1125 } 1126 ctx->stream.next_in += 4; 1127 compLen = getLong(ctx->stream.next_in); 1128 if (ctx->stream.total_out != compLen) { 1129 inflateEnd(&ctx->stream); 1130 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01395) 1131 "Zlib: Length %ld of inflated data does " 1132 "not match expected value %ld", 1133 ctx->stream.total_out, compLen); 1134 return APR_EGENERAL; 1135 } 1136 } 1137 else { 1138 /* FIXME: We need to grab the 8 verification bytes 1139 * from the wire! */ 1140 inflateEnd(&ctx->stream); 1141 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01396) 1142 "Verification data not available (bug?)"); 1143 return APR_EGENERAL; 1144 } 1145 1146 inflateEnd(&ctx->stream); 1147 1148 ctx->done = 1; 1149 1150 /* Did we have trailing data behind the closing 8 bytes? */ 1151 if (avail > 8) { 1152 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02485) 1153 "Encountered extra data after compressed data"); 1154 return APR_EGENERAL; 1155 } 1156 } 1157 1158 } 1159 apr_brigade_cleanup(ctx->bb); 1160 } 1161 1162 /* If we are about to return nothing for a 'blocking' read and we have 1163 * some data in our zlib buffer, flush it out so we can return something. 1164 */ 1165 if (block == APR_BLOCK_READ && 1166 APR_BRIGADE_EMPTY(ctx->proc_bb) && 1167 ctx->stream.avail_out < c->bufferSize) { 1168 apr_bucket *tmp_heap; 1169 apr_size_t len; 1170 ctx->stream.next_out = ctx->buffer; 1171 len = c->bufferSize - ctx->stream.avail_out; 1172 1173 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); 1174 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, 1175 NULL, f->c->bucket_alloc); 1176 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); 1177 ctx->stream.avail_out = c->bufferSize; 1178 } 1179 1180 if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) { 1181 if (apr_brigade_partition(ctx->proc_bb, readbytes, &bkt) == APR_INCOMPLETE) { 1182 APR_BRIGADE_CONCAT(bb, ctx->proc_bb); 1183 } 1184 else { 1185 APR_BRIGADE_CONCAT(bb, ctx->proc_bb); 1186 apr_brigade_split_ex(bb, bkt, ctx->proc_bb); 1187 } 1188 } 1189 1190 return APR_SUCCESS; 1191} 1192 1193 1194/* Filter to inflate for a content-transforming proxy. */ 1195static apr_status_t inflate_out_filter(ap_filter_t *f, 1196 apr_bucket_brigade *bb) 1197{ 1198 int zlib_method; 1199 int zlib_flags; 1200 apr_bucket *e; 1201 request_rec *r = f->r; 1202 deflate_ctx *ctx = f->ctx; 1203 int zRC; 1204 apr_status_t rv; 1205 deflate_filter_config *c; 1206 1207 /* Do nothing if asked to filter nothing. */ 1208 if (APR_BRIGADE_EMPTY(bb)) { 1209 return APR_SUCCESS; 1210 } 1211 1212 c = ap_get_module_config(r->server->module_config, &deflate_module); 1213 1214 if (!ctx) { 1215 1216 /* 1217 * Only work on main request, not subrequests, 1218 * that are not a 204 response with no content 1219 * and not a partial response to a Range request, 1220 * and only when Content-Encoding ends in gzip. 1221 */ 1222 if (!ap_is_initial_req(r) || (r->status == HTTP_NO_CONTENT) || 1223 (apr_table_get(r->headers_out, "Content-Range") != NULL) || 1224 (check_gzip(r, r->headers_out, r->err_headers_out) == 0) 1225 ) { 1226 ap_remove_output_filter(f); 1227 return ap_pass_brigade(f->next, bb); 1228 } 1229 1230 /* 1231 * At this point we have decided to filter the content, so change 1232 * important content metadata before sending any response out. 1233 * Content-Encoding was already reset by the check_gzip() call. 1234 */ 1235 apr_table_unset(r->headers_out, "Content-Length"); 1236 apr_table_unset(r->headers_out, "Content-MD5"); 1237 deflate_check_etag(r, "gunzip"); 1238 1239 /* For a 304 response, only change the headers */ 1240 if (r->status == HTTP_NOT_MODIFIED) { 1241 ap_remove_output_filter(f); 1242 return ap_pass_brigade(f->next, bb); 1243 } 1244 1245 f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); 1246 ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); 1247 ctx->buffer = apr_palloc(r->pool, c->bufferSize); 1248 ctx->libz_end_func = inflateEnd; 1249 ctx->validation_buffer = NULL; 1250 ctx->validation_buffer_length = 0; 1251 1252 zRC = inflateInit2(&ctx->stream, c->windowSize); 1253 1254 if (zRC != Z_OK) { 1255 f->ctx = NULL; 1256 inflateEnd(&ctx->stream); 1257 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01397) 1258 "unable to init Zlib: " 1259 "inflateInit2 returned %d: URL %s", 1260 zRC, r->uri); 1261 /* 1262 * Remove ourselves as it does not make sense to return: 1263 * We are not able to init libz and pass data down the chain 1264 * compressed. 1265 */ 1266 ap_remove_output_filter(f); 1267 return ap_pass_brigade(f->next, bb); 1268 } 1269 1270 /* 1271 * Register a cleanup function to ensure that we cleanup the internal 1272 * libz resources. 1273 */ 1274 apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup, 1275 apr_pool_cleanup_null); 1276 1277 /* initialize inflate output buffer */ 1278 ctx->stream.next_out = ctx->buffer; 1279 ctx->stream.avail_out = c->bufferSize; 1280 1281 ctx->inflate_init = 0; 1282 } 1283 1284 while (!APR_BRIGADE_EMPTY(bb)) 1285 { 1286 const char *data; 1287 apr_bucket *b; 1288 apr_size_t len; 1289 1290 e = APR_BRIGADE_FIRST(bb); 1291 1292 if (APR_BUCKET_IS_EOS(e)) { 1293 /* 1294 * We are really done now. Ensure that we never return here, even 1295 * if a second EOS bucket falls down the chain. Thus remove 1296 * ourselves. 1297 */ 1298 ap_remove_output_filter(f); 1299 /* should be zero already anyway */ 1300 ctx->stream.avail_in = 0; 1301 /* 1302 * Flush the remaining data from the zlib buffers. It is correct 1303 * to use Z_SYNC_FLUSH in this case and not Z_FINISH as in the 1304 * deflate case. In the inflate case Z_FINISH requires to have a 1305 * large enough output buffer to put ALL data in otherwise it 1306 * fails, whereas in the deflate case you can empty a filled output 1307 * buffer and call it again until no more output can be created. 1308 */ 1309 flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate, Z_SYNC_FLUSH, 1310 UPDATE_CRC); 1311 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01398) 1312 "Zlib: Inflated %ld to %ld : URL %s", 1313 ctx->stream.total_in, ctx->stream.total_out, r->uri); 1314 1315 if (ctx->validation_buffer_length == VALIDATION_SIZE) { 1316 unsigned long compCRC, compLen; 1317 compCRC = getLong(ctx->validation_buffer); 1318 if (ctx->crc != compCRC) { 1319 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01399) 1320 "Zlib: Checksum of inflated stream invalid"); 1321 return APR_EGENERAL; 1322 } 1323 ctx->validation_buffer += VALIDATION_SIZE / 2; 1324 compLen = getLong(ctx->validation_buffer); 1325 if (ctx->stream.total_out != compLen) { 1326 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01400) 1327 "Zlib: Length of inflated stream invalid"); 1328 return APR_EGENERAL; 1329 } 1330 } 1331 else { 1332 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01401) 1333 "Zlib: Validation bytes not present"); 1334 return APR_EGENERAL; 1335 } 1336 1337 inflateEnd(&ctx->stream); 1338 /* No need for cleanup any longer */ 1339 apr_pool_cleanup_kill(r->pool, ctx, deflate_ctx_cleanup); 1340 1341 /* Remove EOS from the old list, and insert into the new. */ 1342 APR_BUCKET_REMOVE(e); 1343 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 1344 1345 /* 1346 * Okay, we've seen the EOS. 1347 * Time to pass it along down the chain. 1348 */ 1349 return ap_pass_brigade(f->next, ctx->bb); 1350 } 1351 1352 if (APR_BUCKET_IS_FLUSH(e)) { 1353 apr_status_t rv; 1354 1355 /* flush the remaining data from the zlib buffers */ 1356 zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate, 1357 Z_SYNC_FLUSH, UPDATE_CRC); 1358 if (zRC != Z_OK) { 1359 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01402) 1360 "Zlib error %d flushing inflate buffer (%s)", 1361 zRC, ctx->stream.msg); 1362 return APR_EGENERAL; 1363 } 1364 1365 /* Remove flush bucket from old brigade anf insert into the new. */ 1366 APR_BUCKET_REMOVE(e); 1367 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 1368 rv = ap_pass_brigade(f->next, ctx->bb); 1369 if (rv != APR_SUCCESS) { 1370 return rv; 1371 } 1372 continue; 1373 } 1374 1375 if (APR_BUCKET_IS_METADATA(e)) { 1376 /* 1377 * Remove meta data bucket from old brigade and insert into the 1378 * new. 1379 */ 1380 APR_BUCKET_REMOVE(e); 1381 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 1382 continue; 1383 } 1384 1385 /* read */ 1386 apr_bucket_read(e, &data, &len, APR_BLOCK_READ); 1387 1388 /* first bucket contains zlib header */ 1389 if (!ctx->inflate_init) { 1390 ctx->inflate_init = 1; 1391 if (len < 10) { 1392 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01403) 1393 "Insufficient data for inflate"); 1394 return APR_EGENERAL; 1395 } 1396 else { 1397 zlib_method = data[2]; 1398 zlib_flags = data[3]; 1399 if (zlib_method != Z_DEFLATED) { 1400 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01404) 1401 "inflate: data not deflated!"); 1402 ap_remove_output_filter(f); 1403 return ap_pass_brigade(f->next, bb); 1404 } 1405 if (data[0] != deflate_magic[0] || 1406 data[1] != deflate_magic[1] || 1407 (zlib_flags & RESERVED) != 0) { 1408 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01405) 1409 "inflate: bad header"); 1410 return APR_EGENERAL ; 1411 } 1412 data += 10 ; 1413 len -= 10 ; 1414 } 1415 if (zlib_flags & EXTRA_FIELD) { 1416 unsigned int bytes = (unsigned int)(data[0]); 1417 bytes += ((unsigned int)(data[1])) << 8; 1418 bytes += 2; 1419 if (len < bytes) { 1420 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01406) 1421 "inflate: extra field too big (not " 1422 "supported)"); 1423 return APR_EGENERAL; 1424 } 1425 data += bytes; 1426 len -= bytes; 1427 } 1428 if (zlib_flags & ORIG_NAME) { 1429 while (len-- && *data++); 1430 } 1431 if (zlib_flags & COMMENT) { 1432 while (len-- && *data++); 1433 } 1434 if (zlib_flags & HEAD_CRC) { 1435 len -= 2; 1436 data += 2; 1437 } 1438 } 1439 1440 /* pass through zlib inflate. */ 1441 ctx->stream.next_in = (unsigned char *)data; 1442 ctx->stream.avail_in = len; 1443 1444 if (ctx->validation_buffer) { 1445 if (ctx->validation_buffer_length < VALIDATION_SIZE) { 1446 apr_size_t copy_size; 1447 1448 copy_size = VALIDATION_SIZE - ctx->validation_buffer_length; 1449 if (copy_size > ctx->stream.avail_in) 1450 copy_size = ctx->stream.avail_in; 1451 memcpy(ctx->validation_buffer + ctx->validation_buffer_length, 1452 ctx->stream.next_in, copy_size); 1453 /* Saved copy_size bytes */ 1454 ctx->stream.avail_in -= copy_size; 1455 ctx->validation_buffer_length += copy_size; 1456 } 1457 if (ctx->stream.avail_in) { 1458 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01407) 1459 "Zlib: %d bytes of garbage at the end of " 1460 "compressed stream.", ctx->stream.avail_in); 1461 /* 1462 * There is nothing worth consuming for zlib left, because it is 1463 * either garbage data or the data has been copied to the 1464 * validation buffer (processing validation data is no business 1465 * for zlib). So set ctx->stream.avail_in to zero to indicate 1466 * this to the following while loop. 1467 */ 1468 ctx->stream.avail_in = 0; 1469 } 1470 } 1471 1472 while (ctx->stream.avail_in != 0) { 1473 if (ctx->stream.avail_out == 0) { 1474 1475 ctx->stream.next_out = ctx->buffer; 1476 len = c->bufferSize - ctx->stream.avail_out; 1477 1478 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); 1479 b = apr_bucket_heap_create((char *)ctx->buffer, len, 1480 NULL, f->c->bucket_alloc); 1481 APR_BRIGADE_INSERT_TAIL(ctx->bb, b); 1482 ctx->stream.avail_out = c->bufferSize; 1483 /* Send what we have right now to the next filter. */ 1484 rv = ap_pass_brigade(f->next, ctx->bb); 1485 if (rv != APR_SUCCESS) { 1486 return rv; 1487 } 1488 } 1489 1490 zRC = inflate(&ctx->stream, Z_NO_FLUSH); 1491 1492 if (zRC == Z_STREAM_END) { 1493 /* 1494 * We have inflated all data. Now try to capture the 1495 * validation bytes. We may not have them all available 1496 * right now, but capture what is there. 1497 */ 1498 ctx->validation_buffer = apr_pcalloc(f->r->pool, 1499 VALIDATION_SIZE); 1500 if (ctx->stream.avail_in > VALIDATION_SIZE) { 1501 ctx->validation_buffer_length = VALIDATION_SIZE; 1502 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01408) 1503 "Zlib: %d bytes of garbage at the end of " 1504 "compressed stream.", 1505 ctx->stream.avail_in - VALIDATION_SIZE); 1506 } else if (ctx->stream.avail_in > 0) { 1507 ctx->validation_buffer_length = ctx->stream.avail_in; 1508 } 1509 if (ctx->validation_buffer_length) 1510 memcpy(ctx->validation_buffer, ctx->stream.next_in, 1511 ctx->validation_buffer_length); 1512 break; 1513 } 1514 1515 if (zRC != Z_OK) { 1516 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01409) 1517 "Zlib error %d inflating data (%s)", zRC, 1518 ctx->stream.msg); 1519 return APR_EGENERAL; 1520 } 1521 } 1522 1523 apr_bucket_delete(e); 1524 } 1525 1526 apr_brigade_cleanup(bb); 1527 return APR_SUCCESS; 1528} 1529 1530static int mod_deflate_post_config(apr_pool_t *pconf, apr_pool_t *plog, 1531 apr_pool_t *ptemp, server_rec *s) 1532{ 1533 mod_deflate_ssl_var = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); 1534 return OK; 1535} 1536 1537 1538#define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH 1539static void register_hooks(apr_pool_t *p) 1540{ 1541 ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL, 1542 AP_FTYPE_CONTENT_SET); 1543 ap_register_output_filter("INFLATE", inflate_out_filter, NULL, 1544 AP_FTYPE_RESOURCE-1); 1545 ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL, 1546 AP_FTYPE_CONTENT_SET); 1547 ap_hook_post_config(mod_deflate_post_config, NULL, NULL, APR_HOOK_MIDDLE); 1548} 1549 1550static const command_rec deflate_filter_cmds[] = { 1551 AP_INIT_TAKE12("DeflateFilterNote", deflate_set_note, NULL, RSRC_CONF, 1552 "Set a note to report on compression ratio"), 1553 AP_INIT_TAKE1("DeflateWindowSize", deflate_set_window_size, NULL, 1554 RSRC_CONF, "Set the Deflate window size (1-15)"), 1555 AP_INIT_TAKE1("DeflateBufferSize", deflate_set_buffer_size, NULL, RSRC_CONF, 1556 "Set the Deflate Buffer Size"), 1557 AP_INIT_TAKE1("DeflateMemLevel", deflate_set_memlevel, NULL, RSRC_CONF, 1558 "Set the Deflate Memory Level (1-9)"), 1559 AP_INIT_TAKE1("DeflateCompressionLevel", deflate_set_compressionlevel, NULL, RSRC_CONF, 1560 "Set the Deflate Compression Level (1-9)"), 1561 {NULL} 1562}; 1563 1564AP_DECLARE_MODULE(deflate) = { 1565 STANDARD20_MODULE_STUFF, 1566 NULL, /* dir config creater */ 1567 NULL, /* dir merger --- default is to override */ 1568 create_deflate_server_config, /* server config */ 1569 NULL, /* merge server config */ 1570 deflate_filter_cmds, /* command table */ 1571 register_hooks /* register hooks */ 1572}; 1573