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 49#include "zlib.h" 50 51static const char deflateFilterName[] = "DEFLATE"; 52module AP_MODULE_DECLARE_DATA deflate_module; 53 54typedef struct deflate_filter_config_t 55{ 56 int windowSize; 57 int memlevel; 58 int compressionlevel; 59 apr_size_t bufferSize; 60 char *note_ratio_name; 61 char *note_input_name; 62 char *note_output_name; 63} deflate_filter_config; 64 65/* RFC 1952 Section 2.3 defines the gzip header: 66 * 67 * +---+---+---+---+---+---+---+---+---+---+ 68 * |ID1|ID2|CM |FLG| MTIME |XFL|OS | 69 * +---+---+---+---+---+---+---+---+---+---+ 70 */ 71static const char gzip_header[10] = 72{ '\037', '\213', Z_DEFLATED, 0, 73 0, 0, 0, 0, /* mtime */ 74 0, 0x03 /* Unix OS_CODE */ 75}; 76 77/* magic header */ 78static const char deflate_magic[2] = { '\037', '\213' }; 79 80/* windowsize is negative to suppress Zlib header */ 81#define DEFAULT_COMPRESSION Z_DEFAULT_COMPRESSION 82#define DEFAULT_WINDOWSIZE -15 83#define DEFAULT_MEMLEVEL 9 84#define DEFAULT_BUFFERSIZE 8096 85 86 87/* Check whether a request is gzipped, so we can un-gzip it. 88 * If a request has multiple encodings, we need the gzip 89 * to be the outermost non-identity encoding. 90 */ 91static int check_gzip(request_rec *r, apr_table_t *hdrs1, apr_table_t *hdrs2) 92{ 93 int found = 0; 94 apr_table_t *hdrs = hdrs1; 95 const char *encoding = apr_table_get(hdrs, "Content-Encoding"); 96 97 if (!encoding && (hdrs2 != NULL)) { 98 /* the output filter has two tables and a content_encoding to check */ 99 encoding = apr_table_get(hdrs2, "Content-Encoding"); 100 hdrs = hdrs2; 101 if (!encoding) { 102 encoding = r->content_encoding; 103 hdrs = NULL; 104 } 105 } 106 if (encoding && *encoding) { 107 108 /* check the usual/simple case first */ 109 if (!strcasecmp(encoding, "gzip") 110 || !strcasecmp(encoding, "x-gzip")) { 111 found = 1; 112 if (hdrs) { 113 apr_table_unset(hdrs, "Content-Encoding"); 114 } 115 else { 116 r->content_encoding = NULL; 117 } 118 } 119 else if (ap_strchr_c(encoding, ',') != NULL) { 120 /* If the outermost encoding isn't gzip, there's nowt 121 * we can do. So only check the last non-identity token 122 */ 123 char *new_encoding = apr_pstrdup(r->pool, encoding); 124 char *ptr; 125 for(;;) { 126 char *token = ap_strrchr(new_encoding, ','); 127 if (!token) { /* gzip:identity or other:identity */ 128 if (!strcasecmp(new_encoding, "gzip") 129 || !strcasecmp(new_encoding, "x-gzip")) { 130 found = 1; 131 if (hdrs) { 132 apr_table_unset(hdrs, "Content-Encoding"); 133 } 134 else { 135 r->content_encoding = NULL; 136 } 137 } 138 break; /* seen all tokens */ 139 } 140 for (ptr=token+1; apr_isspace(*ptr); ++ptr); 141 if (!strcasecmp(ptr, "gzip") 142 || !strcasecmp(ptr, "x-gzip")) { 143 *token = '\0'; 144 if (hdrs) { 145 apr_table_setn(hdrs, "Content-Encoding", new_encoding); 146 } 147 else { 148 r->content_encoding = new_encoding; 149 } 150 found = 1; 151 } 152 else if (!ptr[0] || !strcasecmp(ptr, "identity")) { 153 *token = '\0'; 154 continue; /* strip the token and find the next one */ 155 } 156 break; /* found a non-identity token */ 157 } 158 } 159 } 160 return found; 161} 162 163/* Outputs a long in LSB order to the given file 164 * only the bottom 4 bits are required for the deflate file format. 165 */ 166static void putLong(unsigned char *string, unsigned long x) 167{ 168 string[0] = (unsigned char)(x & 0xff); 169 string[1] = (unsigned char)((x & 0xff00) >> 8); 170 string[2] = (unsigned char)((x & 0xff0000) >> 16); 171 string[3] = (unsigned char)((x & 0xff000000) >> 24); 172} 173 174/* Inputs a string and returns a long. 175 */ 176static unsigned long getLong(unsigned char *string) 177{ 178 return ((unsigned long)string[0]) 179 | (((unsigned long)string[1]) << 8) 180 | (((unsigned long)string[2]) << 16) 181 | (((unsigned long)string[3]) << 24); 182} 183 184static void *create_deflate_server_config(apr_pool_t *p, server_rec *s) 185{ 186 deflate_filter_config *c = apr_pcalloc(p, sizeof *c); 187 188 c->memlevel = DEFAULT_MEMLEVEL; 189 c->windowSize = DEFAULT_WINDOWSIZE; 190 c->bufferSize = DEFAULT_BUFFERSIZE; 191 c->compressionlevel = DEFAULT_COMPRESSION; 192 193 return c; 194} 195 196static const char *deflate_set_window_size(cmd_parms *cmd, void *dummy, 197 const char *arg) 198{ 199 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, 200 &deflate_module); 201 int i; 202 203 i = atoi(arg); 204 205 if (i < 1 || i > 15) 206 return "DeflateWindowSize must be between 1 and 15"; 207 208 c->windowSize = i * -1; 209 210 return NULL; 211} 212 213static const char *deflate_set_buffer_size(cmd_parms *cmd, void *dummy, 214 const char *arg) 215{ 216 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, 217 &deflate_module); 218 int n = atoi(arg); 219 220 if (n <= 0) { 221 return "DeflateBufferSize should be positive"; 222 } 223 224 c->bufferSize = (apr_size_t)n; 225 226 return NULL; 227} 228static const char *deflate_set_note(cmd_parms *cmd, void *dummy, 229 const char *arg1, const char *arg2) 230{ 231 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, 232 &deflate_module); 233 234 if (arg2 == NULL) { 235 c->note_ratio_name = apr_pstrdup(cmd->pool, arg1); 236 } 237 else if (!strcasecmp(arg1, "ratio")) { 238 c->note_ratio_name = apr_pstrdup(cmd->pool, arg2); 239 } 240 else if (!strcasecmp(arg1, "input")) { 241 c->note_input_name = apr_pstrdup(cmd->pool, arg2); 242 } 243 else if (!strcasecmp(arg1, "output")) { 244 c->note_output_name = apr_pstrdup(cmd->pool, arg2); 245 } 246 else { 247 return apr_psprintf(cmd->pool, "Unknown note type %s", arg1); 248 } 249 250 return NULL; 251} 252 253static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy, 254 const char *arg) 255{ 256 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, 257 &deflate_module); 258 int i; 259 260 i = atoi(arg); 261 262 if (i < 1 || i > 9) 263 return "DeflateMemLevel must be between 1 and 9"; 264 265 c->memlevel = i; 266 267 return NULL; 268} 269 270static const char *deflate_set_compressionlevel(cmd_parms *cmd, void *dummy, 271 const char *arg) 272{ 273 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, 274 &deflate_module); 275 int i; 276 277 i = atoi(arg); 278 279 if (i < 1 || i > 9) 280 return "Compression Level must be between 1 and 9"; 281 282 c->compressionlevel = i; 283 284 return NULL; 285} 286 287typedef struct deflate_ctx_t 288{ 289 z_stream stream; 290 unsigned char *buffer; 291 unsigned long crc; 292 apr_bucket_brigade *bb, *proc_bb; 293 int (*libz_end_func)(z_streamp); 294 unsigned char *validation_buffer; 295 apr_size_t validation_buffer_length; 296 int inflate_init; 297} deflate_ctx; 298 299/* Number of validation bytes (CRC and length) after the compressed data */ 300#define VALIDATION_SIZE 8 301/* Do not update ctx->crc, see comment in flush_libz_buffer */ 302#define NO_UPDATE_CRC 0 303/* Do update ctx->crc, see comment in flush_libz_buffer */ 304#define UPDATE_CRC 1 305 306static int flush_libz_buffer(deflate_ctx *ctx, deflate_filter_config *c, 307 struct apr_bucket_alloc_t *bucket_alloc, 308 int (*libz_func)(z_streamp, int), int flush, 309 int crc) 310{ 311 int zRC = Z_OK; 312 int done = 0; 313 unsigned int deflate_len; 314 apr_bucket *b; 315 316 for (;;) { 317 deflate_len = c->bufferSize - ctx->stream.avail_out; 318 319 if (deflate_len != 0) { 320 /* 321 * Do we need to update ctx->crc? Usually this is the case for 322 * inflate action where we need to do a crc on the output, whereas 323 * in the deflate case we need to do a crc on the input 324 */ 325 if (crc) { 326 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, 327 deflate_len); 328 } 329 b = apr_bucket_heap_create((char *)ctx->buffer, 330 deflate_len, NULL, 331 bucket_alloc); 332 APR_BRIGADE_INSERT_TAIL(ctx->bb, b); 333 ctx->stream.next_out = ctx->buffer; 334 ctx->stream.avail_out = c->bufferSize; 335 } 336 337 if (done) 338 break; 339 340 zRC = libz_func(&ctx->stream, flush); 341 342 /* 343 * We can ignore Z_BUF_ERROR because: 344 * When we call libz_func we can assume that 345 * 346 * - avail_in is zero (due to the surrounding code that calls 347 * flush_libz_buffer) 348 * - avail_out is non zero due to our actions some lines above 349 * 350 * So the only reason for Z_BUF_ERROR is that the internal libz 351 * buffers are now empty and thus we called libz_func one time 352 * too often. This does not hurt. It simply says that we are done. 353 */ 354 if (zRC == Z_BUF_ERROR) { 355 zRC = Z_OK; 356 break; 357 } 358 359 done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END); 360 361 if (zRC != Z_OK && zRC != Z_STREAM_END) 362 break; 363 } 364 return zRC; 365} 366 367static apr_status_t deflate_ctx_cleanup(void *data) 368{ 369 deflate_ctx *ctx = (deflate_ctx *)data; 370 371 if (ctx) 372 ctx->libz_end_func(&ctx->stream); 373 return APR_SUCCESS; 374} 375 376static apr_status_t deflate_out_filter(ap_filter_t *f, 377 apr_bucket_brigade *bb) 378{ 379 apr_bucket *e; 380 request_rec *r = f->r; 381 deflate_ctx *ctx = f->ctx; 382 int zRC; 383 deflate_filter_config *c; 384 385 /* Do nothing if asked to filter nothing. */ 386 if (APR_BRIGADE_EMPTY(bb)) { 387 return ap_pass_brigade(f->next, bb); 388 } 389 390 c = ap_get_module_config(r->server->module_config, 391 &deflate_module); 392 393 /* If we don't have a context, we need to ensure that it is okay to send 394 * the deflated content. If we have a context, that means we've done 395 * this before and we liked it. 396 * This could be not so nice if we always fail. But, if we succeed, 397 * we're in better shape. 398 */ 399 if (!ctx) { 400 char *token; 401 const char *encoding; 402 403 /* only work on main request/no subrequests */ 404 if (r->main != NULL) { 405 ap_remove_output_filter(f); 406 return ap_pass_brigade(f->next, bb); 407 } 408 409 /* some browsers might have problems, so set no-gzip 410 * (with browsermatch) for them 411 */ 412 if (apr_table_get(r->subprocess_env, "no-gzip")) { 413 ap_remove_output_filter(f); 414 return ap_pass_brigade(f->next, bb); 415 } 416 417 /* We can't operate on Content-Ranges */ 418 if (apr_table_get(r->headers_out, "Content-Range") != NULL) { 419 ap_remove_output_filter(f); 420 return ap_pass_brigade(f->next, bb); 421 } 422 423 /* Some browsers might have problems with content types 424 * other than text/html, so set gzip-only-text/html 425 * (with browsermatch) for them 426 */ 427 if (r->content_type == NULL 428 || strncmp(r->content_type, "text/html", 9)) { 429 const char *env_value = apr_table_get(r->subprocess_env, 430 "gzip-only-text/html"); 431 if ( env_value && (strcmp(env_value,"1") == 0) ) { 432 ap_remove_output_filter(f); 433 return ap_pass_brigade(f->next, bb); 434 } 435 } 436 437 /* Let's see what our current Content-Encoding is. 438 * If it's already encoded, don't compress again. 439 * (We could, but let's not.) 440 */ 441 encoding = apr_table_get(r->headers_out, "Content-Encoding"); 442 if (encoding) { 443 const char *err_enc; 444 445 err_enc = apr_table_get(r->err_headers_out, "Content-Encoding"); 446 if (err_enc) { 447 encoding = apr_pstrcat(r->pool, encoding, ",", err_enc, NULL); 448 } 449 } 450 else { 451 encoding = apr_table_get(r->err_headers_out, "Content-Encoding"); 452 } 453 454 if (r->content_encoding) { 455 encoding = encoding ? apr_pstrcat(r->pool, encoding, ",", 456 r->content_encoding, NULL) 457 : r->content_encoding; 458 } 459 460 if (encoding) { 461 const char *tmp = encoding; 462 463 token = ap_get_token(r->pool, &tmp, 0); 464 while (token && *token) { 465 /* stolen from mod_negotiation: */ 466 if (strcmp(token, "identity") && strcmp(token, "7bit") && 467 strcmp(token, "8bit") && strcmp(token, "binary")) { 468 469 ap_remove_output_filter(f); 470 return ap_pass_brigade(f->next, bb); 471 } 472 473 /* Otherwise, skip token */ 474 if (*tmp) { 475 ++tmp; 476 } 477 token = (*tmp) ? ap_get_token(r->pool, &tmp, 0) : NULL; 478 } 479 } 480 481 /* Even if we don't accept this request based on it not having 482 * the Accept-Encoding, we need to note that we were looking 483 * for this header and downstream proxies should be aware of that. 484 */ 485 apr_table_mergen(r->headers_out, "Vary", "Accept-Encoding"); 486 487 /* force-gzip will just force it out regardless if the browser 488 * can actually do anything with it. 489 */ 490 if (!apr_table_get(r->subprocess_env, "force-gzip")) { 491 const char *accepts; 492 /* if they don't have the line, then they can't play */ 493 accepts = apr_table_get(r->headers_in, "Accept-Encoding"); 494 if (accepts == NULL) { 495 ap_remove_output_filter(f); 496 return ap_pass_brigade(f->next, bb); 497 } 498 499 token = ap_get_token(r->pool, &accepts, 0); 500 while (token && token[0] && strcasecmp(token, "gzip")) { 501 /* skip parameters, XXX: ;q=foo evaluation? */ 502 while (*accepts == ';') { 503 ++accepts; 504 token = ap_get_token(r->pool, &accepts, 1); 505 } 506 507 /* retrieve next token */ 508 if (*accepts == ',') { 509 ++accepts; 510 } 511 token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL; 512 } 513 514 /* No acceptable token found. */ 515 if (token == NULL || token[0] == '\0') { 516 ap_remove_output_filter(f); 517 return ap_pass_brigade(f->next, bb); 518 } 519 } 520 521 /* For a 304 or 204 response there is no entity included in 522 * the response and hence nothing to deflate. */ 523 if (r->status == HTTP_NOT_MODIFIED || r->status == HTTP_NO_CONTENT) { 524 ap_remove_output_filter(f); 525 return ap_pass_brigade(f->next, bb); 526 } 527 528 /* We're cool with filtering this. */ 529 ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx)); 530 ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); 531 ctx->buffer = apr_palloc(r->pool, c->bufferSize); 532 ctx->libz_end_func = deflateEnd; 533 534 zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED, 535 c->windowSize, c->memlevel, 536 Z_DEFAULT_STRATEGY); 537 538 if (zRC != Z_OK) { 539 deflateEnd(&ctx->stream); 540 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 541 "unable to init Zlib: " 542 "deflateInit2 returned %d: URL %s", 543 zRC, r->uri); 544 /* 545 * Remove ourselves as it does not make sense to return: 546 * We are not able to init libz and pass data down the chain 547 * uncompressed. 548 */ 549 ap_remove_output_filter(f); 550 return ap_pass_brigade(f->next, bb); 551 } 552 /* 553 * Register a cleanup function to ensure that we cleanup the internal 554 * libz resources. 555 */ 556 apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup, 557 apr_pool_cleanup_null); 558 559 /* add immortal gzip header */ 560 e = apr_bucket_immortal_create(gzip_header, sizeof gzip_header, 561 f->c->bucket_alloc); 562 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 563 564 /* If the entire Content-Encoding is "identity", we can replace it. */ 565 if (!encoding || !strcasecmp(encoding, "identity")) { 566 apr_table_setn(r->headers_out, "Content-Encoding", "gzip"); 567 } 568 else { 569 apr_table_mergen(r->headers_out, "Content-Encoding", "gzip"); 570 } 571 apr_table_unset(r->headers_out, "Content-Length"); 572 apr_table_unset(r->headers_out, "Content-MD5"); 573 574 /* initialize deflate output buffer */ 575 ctx->stream.next_out = ctx->buffer; 576 ctx->stream.avail_out = c->bufferSize; 577 } 578 579 while (!APR_BRIGADE_EMPTY(bb)) 580 { 581 const char *data; 582 apr_bucket *b; 583 apr_size_t len; 584 585 /* 586 * Optimization: If we are a HEAD request and bytes_sent is not zero 587 * it means that we have passed the content-length filter once and 588 * have more data to sent. This means that the content-length filter 589 * could not determine our content-length for the response to the 590 * HEAD request anyway (the associated GET request would deliver the 591 * body in chunked encoding) and we can stop compressing. 592 */ 593 if (r->header_only && r->bytes_sent) { 594 ap_remove_output_filter(f); 595 return ap_pass_brigade(f->next, bb); 596 } 597 598 e = APR_BRIGADE_FIRST(bb); 599 600 if (APR_BUCKET_IS_EOS(e)) { 601 char *buf; 602 603 ctx->stream.avail_in = 0; /* should be zero already anyway */ 604 /* flush the remaining data from the zlib buffers */ 605 flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, Z_FINISH, 606 NO_UPDATE_CRC); 607 608 buf = apr_palloc(r->pool, VALIDATION_SIZE); 609 putLong((unsigned char *)&buf[0], ctx->crc); 610 putLong((unsigned char *)&buf[4], ctx->stream.total_in); 611 612 b = apr_bucket_pool_create(buf, VALIDATION_SIZE, r->pool, 613 f->c->bucket_alloc); 614 APR_BRIGADE_INSERT_TAIL(ctx->bb, b); 615 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 616 "Zlib: Compressed %ld to %ld : URL %s", 617 ctx->stream.total_in, ctx->stream.total_out, r->uri); 618 619 /* leave notes for logging */ 620 if (c->note_input_name) { 621 apr_table_setn(r->notes, c->note_input_name, 622 (ctx->stream.total_in > 0) 623 ? apr_off_t_toa(r->pool, 624 ctx->stream.total_in) 625 : "-"); 626 } 627 628 if (c->note_output_name) { 629 apr_table_setn(r->notes, c->note_output_name, 630 (ctx->stream.total_in > 0) 631 ? apr_off_t_toa(r->pool, 632 ctx->stream.total_out) 633 : "-"); 634 } 635 636 if (c->note_ratio_name) { 637 apr_table_setn(r->notes, c->note_ratio_name, 638 (ctx->stream.total_in > 0) 639 ? apr_itoa(r->pool, 640 (int)(ctx->stream.total_out 641 * 100 642 / ctx->stream.total_in)) 643 : "-"); 644 } 645 646 deflateEnd(&ctx->stream); 647 /* No need for cleanup any longer */ 648 apr_pool_cleanup_kill(r->pool, ctx, deflate_ctx_cleanup); 649 650 /* Remove EOS from the old list, and insert into the new. */ 651 APR_BUCKET_REMOVE(e); 652 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 653 654 /* Okay, we've seen the EOS. 655 * Time to pass it along down the chain. 656 */ 657 return ap_pass_brigade(f->next, ctx->bb); 658 } 659 660 if (APR_BUCKET_IS_FLUSH(e)) { 661 apr_status_t rv; 662 663 /* flush the remaining data from the zlib buffers */ 664 zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, 665 Z_SYNC_FLUSH, NO_UPDATE_CRC); 666 if (zRC != Z_OK) { 667 return APR_EGENERAL; 668 } 669 670 /* Remove flush bucket from old brigade anf insert into the new. */ 671 APR_BUCKET_REMOVE(e); 672 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 673 rv = ap_pass_brigade(f->next, ctx->bb); 674 if (rv != APR_SUCCESS) { 675 return rv; 676 } 677 continue; 678 } 679 680 if (APR_BUCKET_IS_METADATA(e)) { 681 /* 682 * Remove meta data bucket from old brigade and insert into the 683 * new. 684 */ 685 APR_BUCKET_REMOVE(e); 686 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 687 continue; 688 } 689 690 /* read */ 691 apr_bucket_read(e, &data, &len, APR_BLOCK_READ); 692 693 /* This crc32 function is from zlib. */ 694 ctx->crc = crc32(ctx->crc, (const Bytef *)data, len); 695 696 /* write */ 697 ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness, 698 * but we'll just have to 699 * trust zlib */ 700 ctx->stream.avail_in = len; 701 702 while (ctx->stream.avail_in != 0) { 703 if (ctx->stream.avail_out == 0) { 704 apr_status_t rv; 705 706 ctx->stream.next_out = ctx->buffer; 707 len = c->bufferSize - ctx->stream.avail_out; 708 709 b = apr_bucket_heap_create((char *)ctx->buffer, len, 710 NULL, f->c->bucket_alloc); 711 APR_BRIGADE_INSERT_TAIL(ctx->bb, b); 712 ctx->stream.avail_out = c->bufferSize; 713 /* Send what we have right now to the next filter. */ 714 rv = ap_pass_brigade(f->next, ctx->bb); 715 if (rv != APR_SUCCESS) { 716 return rv; 717 } 718 } 719 720 zRC = deflate(&(ctx->stream), Z_NO_FLUSH); 721 722 if (zRC != Z_OK) { 723 return APR_EGENERAL; 724 } 725 } 726 727 apr_bucket_delete(e); 728 } 729 730 apr_brigade_cleanup(bb); 731 return APR_SUCCESS; 732} 733 734/* This is the deflate input filter (inflates). */ 735static apr_status_t deflate_in_filter(ap_filter_t *f, 736 apr_bucket_brigade *bb, 737 ap_input_mode_t mode, 738 apr_read_type_e block, 739 apr_off_t readbytes) 740{ 741 apr_bucket *bkt; 742 request_rec *r = f->r; 743 deflate_ctx *ctx = f->ctx; 744 int zRC; 745 apr_status_t rv; 746 deflate_filter_config *c; 747 748 /* just get out of the way of things we don't want. */ 749 if (mode != AP_MODE_READBYTES) { 750 return ap_get_brigade(f->next, bb, mode, block, readbytes); 751 } 752 753 c = ap_get_module_config(r->server->module_config, &deflate_module); 754 755 if (!ctx) { 756 char deflate_hdr[10]; 757 apr_size_t len; 758 759 /* only work on main request/no subrequests */ 760 if (!ap_is_initial_req(r)) { 761 ap_remove_input_filter(f); 762 return ap_get_brigade(f->next, bb, mode, block, readbytes); 763 } 764 765 /* We can't operate on Content-Ranges */ 766 if (apr_table_get(r->headers_in, "Content-Range") != NULL) { 767 ap_remove_input_filter(f); 768 return ap_get_brigade(f->next, bb, mode, block, readbytes); 769 } 770 771 /* Check whether request body is gzipped. 772 * 773 * If it is, we're transforming the contents, invalidating 774 * some request headers including Content-Encoding. 775 * 776 * If not, we just remove ourself. 777 */ 778 if (check_gzip(r, r->headers_in, NULL) == 0) { 779 ap_remove_input_filter(f); 780 return ap_get_brigade(f->next, bb, mode, block, readbytes); 781 } 782 783 f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); 784 ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); 785 ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc); 786 ctx->buffer = apr_palloc(r->pool, c->bufferSize); 787 788 rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10); 789 if (rv != APR_SUCCESS) { 790 return rv; 791 } 792 793 apr_table_unset(r->headers_in, "Content-Length"); 794 apr_table_unset(r->headers_in, "Content-MD5"); 795 796 len = 10; 797 rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len); 798 if (rv != APR_SUCCESS) { 799 return rv; 800 } 801 802 /* We didn't get the magic bytes. */ 803 if (len != 10 || 804 deflate_hdr[0] != deflate_magic[0] || 805 deflate_hdr[1] != deflate_magic[1]) { 806 return APR_EGENERAL; 807 } 808 809 /* We can't handle flags for now. */ 810 if (deflate_hdr[3] != 0) { 811 return APR_EGENERAL; 812 } 813 814 zRC = inflateInit2(&ctx->stream, c->windowSize); 815 816 if (zRC != Z_OK) { 817 f->ctx = NULL; 818 inflateEnd(&ctx->stream); 819 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 820 "unable to init Zlib: " 821 "inflateInit2 returned %d: URL %s", 822 zRC, r->uri); 823 ap_remove_input_filter(f); 824 return ap_get_brigade(f->next, bb, mode, block, readbytes); 825 } 826 827 /* initialize deflate output buffer */ 828 ctx->stream.next_out = ctx->buffer; 829 ctx->stream.avail_out = c->bufferSize; 830 831 apr_brigade_cleanup(ctx->bb); 832 } 833 834 if (APR_BRIGADE_EMPTY(ctx->proc_bb)) { 835 rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes); 836 837 if (rv != APR_SUCCESS) { 838 /* What about APR_EAGAIN errors? */ 839 inflateEnd(&ctx->stream); 840 return rv; 841 } 842 843 for (bkt = APR_BRIGADE_FIRST(ctx->bb); 844 bkt != APR_BRIGADE_SENTINEL(ctx->bb); 845 bkt = APR_BUCKET_NEXT(bkt)) 846 { 847 const char *data; 848 apr_size_t len; 849 850 /* If we actually see the EOS, that means we screwed up! */ 851 if (APR_BUCKET_IS_EOS(bkt)) { 852 inflateEnd(&ctx->stream); 853 return APR_EGENERAL; 854 } 855 856 if (APR_BUCKET_IS_FLUSH(bkt)) { 857 apr_bucket *tmp_heap; 858 zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH); 859 if (zRC != Z_OK) { 860 inflateEnd(&ctx->stream); 861 return APR_EGENERAL; 862 } 863 864 ctx->stream.next_out = ctx->buffer; 865 len = c->bufferSize - ctx->stream.avail_out; 866 867 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); 868 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, 869 NULL, f->c->bucket_alloc); 870 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); 871 ctx->stream.avail_out = c->bufferSize; 872 873 /* Move everything to the returning brigade. */ 874 APR_BUCKET_REMOVE(bkt); 875 APR_BRIGADE_CONCAT(bb, ctx->bb); 876 break; 877 } 878 879 /* read */ 880 apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ); 881 882 /* pass through zlib inflate. */ 883 ctx->stream.next_in = (unsigned char *)data; 884 ctx->stream.avail_in = len; 885 886 zRC = Z_OK; 887 888 while (ctx->stream.avail_in != 0) { 889 if (ctx->stream.avail_out == 0) { 890 apr_bucket *tmp_heap; 891 ctx->stream.next_out = ctx->buffer; 892 len = c->bufferSize - ctx->stream.avail_out; 893 894 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); 895 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, 896 NULL, f->c->bucket_alloc); 897 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); 898 ctx->stream.avail_out = c->bufferSize; 899 } 900 901 zRC = inflate(&ctx->stream, Z_NO_FLUSH); 902 903 if (zRC == Z_STREAM_END) { 904 break; 905 } 906 907 if (zRC != Z_OK) { 908 inflateEnd(&ctx->stream); 909 return APR_EGENERAL; 910 } 911 } 912 if (zRC == Z_STREAM_END) { 913 apr_bucket *tmp_heap, *eos; 914 915 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 916 "Zlib: Inflated %ld to %ld : URL %s", 917 ctx->stream.total_in, ctx->stream.total_out, 918 r->uri); 919 920 len = c->bufferSize - ctx->stream.avail_out; 921 922 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); 923 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, 924 NULL, f->c->bucket_alloc); 925 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); 926 ctx->stream.avail_out = c->bufferSize; 927 928 /* Is the remaining 8 bytes already in the avail stream? */ 929 if (ctx->stream.avail_in >= 8) { 930 unsigned long compCRC, compLen; 931 compCRC = getLong(ctx->stream.next_in); 932 if (ctx->crc != compCRC) { 933 inflateEnd(&ctx->stream); 934 return APR_EGENERAL; 935 } 936 ctx->stream.next_in += 4; 937 compLen = getLong(ctx->stream.next_in); 938 if (ctx->stream.total_out != compLen) { 939 inflateEnd(&ctx->stream); 940 return APR_EGENERAL; 941 } 942 } 943 else { 944 /* FIXME: We need to grab the 8 verification bytes 945 * from the wire! */ 946 inflateEnd(&ctx->stream); 947 return APR_EGENERAL; 948 } 949 950 inflateEnd(&ctx->stream); 951 952 eos = apr_bucket_eos_create(f->c->bucket_alloc); 953 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos); 954 break; 955 } 956 957 } 958 apr_brigade_cleanup(ctx->bb); 959 } 960 961 /* If we are about to return nothing for a 'blocking' read and we have 962 * some data in our zlib buffer, flush it out so we can return something. 963 */ 964 if (block == APR_BLOCK_READ && 965 APR_BRIGADE_EMPTY(ctx->proc_bb) && 966 ctx->stream.avail_out < c->bufferSize) { 967 apr_bucket *tmp_heap; 968 apr_size_t len; 969 ctx->stream.next_out = ctx->buffer; 970 len = c->bufferSize - ctx->stream.avail_out; 971 972 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); 973 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, 974 NULL, f->c->bucket_alloc); 975 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); 976 ctx->stream.avail_out = c->bufferSize; 977 } 978 979 if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) { 980 apr_bucket_brigade *newbb; 981 982 /* May return APR_INCOMPLETE which is fine by us. */ 983 apr_brigade_partition(ctx->proc_bb, readbytes, &bkt); 984 985 newbb = apr_brigade_split(ctx->proc_bb, bkt); 986 APR_BRIGADE_CONCAT(bb, ctx->proc_bb); 987 APR_BRIGADE_CONCAT(ctx->proc_bb, newbb); 988 } 989 990 return APR_SUCCESS; 991} 992 993 994/* Filter to inflate for a content-transforming proxy. */ 995static apr_status_t inflate_out_filter(ap_filter_t *f, 996 apr_bucket_brigade *bb) 997{ 998 int zlib_method; 999 int zlib_flags; 1000 apr_bucket *e; 1001 request_rec *r = f->r; 1002 deflate_ctx *ctx = f->ctx; 1003 int zRC; 1004 apr_status_t rv; 1005 deflate_filter_config *c; 1006 1007 /* Do nothing if asked to filter nothing. */ 1008 if (APR_BRIGADE_EMPTY(bb)) { 1009 return ap_pass_brigade(f->next, bb); 1010 } 1011 1012 c = ap_get_module_config(r->server->module_config, &deflate_module); 1013 1014 if (!ctx) { 1015 1016 /* only work on main request/no subrequests */ 1017 if (!ap_is_initial_req(r)) { 1018 ap_remove_output_filter(f); 1019 return ap_pass_brigade(f->next, bb); 1020 } 1021 1022 /* We can't operate on Content-Ranges */ 1023 if (apr_table_get(r->headers_out, "Content-Range") != NULL) { 1024 ap_remove_output_filter(f); 1025 return ap_pass_brigade(f->next, bb); 1026 } 1027 1028 /* 1029 * Let's see what our current Content-Encoding is. 1030 * Only inflate if gzipped. 1031 */ 1032 if (check_gzip(r, r->headers_out, r->err_headers_out) == 0) { 1033 ap_remove_output_filter(f); 1034 return ap_pass_brigade(f->next, bb); 1035 } 1036 1037 /* No need to inflate HEAD or 204/304 */ 1038 if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bb))) { 1039 ap_remove_output_filter(f); 1040 return ap_pass_brigade(f->next, bb); 1041 } 1042 1043 f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); 1044 ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); 1045 ctx->buffer = apr_palloc(r->pool, c->bufferSize); 1046 ctx->libz_end_func = inflateEnd; 1047 ctx->validation_buffer = NULL; 1048 ctx->validation_buffer_length = 0; 1049 1050 zRC = inflateInit2(&ctx->stream, c->windowSize); 1051 1052 if (zRC != Z_OK) { 1053 f->ctx = NULL; 1054 inflateEnd(&ctx->stream); 1055 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 1056 "unable to init Zlib: " 1057 "inflateInit2 returned %d: URL %s", 1058 zRC, r->uri); 1059 /* 1060 * Remove ourselves as it does not make sense to return: 1061 * We are not able to init libz and pass data down the chain 1062 * compressed. 1063 */ 1064 ap_remove_output_filter(f); 1065 return ap_pass_brigade(f->next, bb); 1066 } 1067 1068 /* 1069 * Register a cleanup function to ensure that we cleanup the internal 1070 * libz resources. 1071 */ 1072 apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup, 1073 apr_pool_cleanup_null); 1074 1075 /* these are unlikely to be set anyway, but ... */ 1076 apr_table_unset(r->headers_out, "Content-Length"); 1077 apr_table_unset(r->headers_out, "Content-MD5"); 1078 1079 /* initialize inflate output buffer */ 1080 ctx->stream.next_out = ctx->buffer; 1081 ctx->stream.avail_out = c->bufferSize; 1082 1083 ctx->inflate_init = 0; 1084 } 1085 1086 while (!APR_BRIGADE_EMPTY(bb)) 1087 { 1088 const char *data; 1089 apr_bucket *b; 1090 apr_size_t len; 1091 1092 e = APR_BRIGADE_FIRST(bb); 1093 1094 if (APR_BUCKET_IS_EOS(e)) { 1095 /* 1096 * We are really done now. Ensure that we never return here, even 1097 * if a second EOS bucket falls down the chain. Thus remove 1098 * ourselves. 1099 */ 1100 ap_remove_output_filter(f); 1101 /* should be zero already anyway */ 1102 ctx->stream.avail_in = 0; 1103 /* 1104 * Flush the remaining data from the zlib buffers. It is correct 1105 * to use Z_SYNC_FLUSH in this case and not Z_FINISH as in the 1106 * deflate case. In the inflate case Z_FINISH requires to have a 1107 * large enough output buffer to put ALL data in otherwise it 1108 * fails, whereas in the deflate case you can empty a filled output 1109 * buffer and call it again until no more output can be created. 1110 */ 1111 flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate, Z_SYNC_FLUSH, 1112 UPDATE_CRC); 1113 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 1114 "Zlib: Inflated %ld to %ld : URL %s", 1115 ctx->stream.total_in, ctx->stream.total_out, r->uri); 1116 1117 if (ctx->validation_buffer_length == VALIDATION_SIZE) { 1118 unsigned long compCRC, compLen; 1119 compCRC = getLong(ctx->validation_buffer); 1120 if (ctx->crc != compCRC) { 1121 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 1122 "Zlib: Checksum of inflated stream invalid"); 1123 return APR_EGENERAL; 1124 } 1125 ctx->validation_buffer += VALIDATION_SIZE / 2; 1126 compLen = getLong(ctx->validation_buffer); 1127 if (ctx->stream.total_out != compLen) { 1128 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 1129 "Zlib: Length of inflated stream invalid"); 1130 return APR_EGENERAL; 1131 } 1132 } 1133 else { 1134 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 1135 "Zlib: Validation bytes not present"); 1136 return APR_EGENERAL; 1137 } 1138 1139 inflateEnd(&ctx->stream); 1140 /* No need for cleanup any longer */ 1141 apr_pool_cleanup_kill(r->pool, ctx, deflate_ctx_cleanup); 1142 1143 /* Remove EOS from the old list, and insert into the new. */ 1144 APR_BUCKET_REMOVE(e); 1145 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 1146 1147 /* 1148 * Okay, we've seen the EOS. 1149 * Time to pass it along down the chain. 1150 */ 1151 return ap_pass_brigade(f->next, ctx->bb); 1152 } 1153 1154 if (APR_BUCKET_IS_FLUSH(e)) { 1155 apr_status_t rv; 1156 1157 /* flush the remaining data from the zlib buffers */ 1158 zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate, 1159 Z_SYNC_FLUSH, UPDATE_CRC); 1160 if (zRC != Z_OK) { 1161 return APR_EGENERAL; 1162 } 1163 1164 /* Remove flush bucket from old brigade anf insert into the new. */ 1165 APR_BUCKET_REMOVE(e); 1166 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 1167 rv = ap_pass_brigade(f->next, ctx->bb); 1168 if (rv != APR_SUCCESS) { 1169 return rv; 1170 } 1171 continue; 1172 } 1173 1174 if (APR_BUCKET_IS_METADATA(e)) { 1175 /* 1176 * Remove meta data bucket from old brigade and insert into the 1177 * new. 1178 */ 1179 APR_BUCKET_REMOVE(e); 1180 APR_BRIGADE_INSERT_TAIL(ctx->bb, e); 1181 continue; 1182 } 1183 1184 /* read */ 1185 apr_bucket_read(e, &data, &len, APR_BLOCK_READ); 1186 1187 /* first bucket contains zlib header */ 1188 if (!ctx->inflate_init++) { 1189 if (len < 10) { 1190 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 1191 "Insufficient data for inflate"); 1192 return APR_EGENERAL; 1193 } 1194 else { 1195 zlib_method = data[2]; 1196 zlib_flags = data[3]; 1197 if (zlib_method != Z_DEFLATED) { 1198 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 1199 "inflate: data not deflated!"); 1200 ap_remove_output_filter(f); 1201 return ap_pass_brigade(f->next, bb); 1202 } 1203 if (data[0] != deflate_magic[0] || 1204 data[1] != deflate_magic[1] || 1205 (zlib_flags & RESERVED) != 0) { 1206 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 1207 "inflate: bad header"); 1208 return APR_EGENERAL ; 1209 } 1210 data += 10 ; 1211 len -= 10 ; 1212 } 1213 if (zlib_flags & EXTRA_FIELD) { 1214 unsigned int bytes = (unsigned int)(data[0]); 1215 bytes += ((unsigned int)(data[1])) << 8; 1216 bytes += 2; 1217 if (len < bytes) { 1218 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 1219 "inflate: extra field too big (not " 1220 "supported)"); 1221 return APR_EGENERAL; 1222 } 1223 data += bytes; 1224 len -= bytes; 1225 } 1226 if (zlib_flags & ORIG_NAME) { 1227 while (len-- && *data++); 1228 } 1229 if (zlib_flags & COMMENT) { 1230 while (len-- && *data++); 1231 } 1232 if (zlib_flags & HEAD_CRC) { 1233 len -= 2; 1234 data += 2; 1235 } 1236 } 1237 1238 /* pass through zlib inflate. */ 1239 ctx->stream.next_in = (unsigned char *)data; 1240 ctx->stream.avail_in = len; 1241 1242 if (ctx->validation_buffer) { 1243 if (ctx->validation_buffer_length < VALIDATION_SIZE) { 1244 apr_size_t copy_size; 1245 1246 copy_size = VALIDATION_SIZE - ctx->validation_buffer_length; 1247 if (copy_size > ctx->stream.avail_in) 1248 copy_size = ctx->stream.avail_in; 1249 memcpy(ctx->validation_buffer + ctx->validation_buffer_length, 1250 ctx->stream.next_in, copy_size); 1251 /* Saved copy_size bytes */ 1252 ctx->stream.avail_in -= copy_size; 1253 ctx->validation_buffer_length += copy_size; 1254 } 1255 if (ctx->stream.avail_in) { 1256 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 1257 "Zlib: %d bytes of garbage at the end of " 1258 "compressed stream.", ctx->stream.avail_in); 1259 /* 1260 * There is nothing worth consuming for zlib left, because it is 1261 * either garbage data or the data has been copied to the 1262 * validation buffer (processing validation data is no business 1263 * for zlib). So set ctx->stream.avail_in to zero to indicate 1264 * this to the following while loop. 1265 */ 1266 ctx->stream.avail_in = 0; 1267 } 1268 } 1269 1270 zRC = Z_OK; 1271 1272 while (ctx->stream.avail_in != 0) { 1273 if (ctx->stream.avail_out == 0) { 1274 1275 ctx->stream.next_out = ctx->buffer; 1276 len = c->bufferSize - ctx->stream.avail_out; 1277 1278 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); 1279 b = apr_bucket_heap_create((char *)ctx->buffer, len, 1280 NULL, f->c->bucket_alloc); 1281 APR_BRIGADE_INSERT_TAIL(ctx->bb, b); 1282 ctx->stream.avail_out = c->bufferSize; 1283 /* Send what we have right now to the next filter. */ 1284 rv = ap_pass_brigade(f->next, ctx->bb); 1285 if (rv != APR_SUCCESS) { 1286 return rv; 1287 } 1288 } 1289 1290 zRC = inflate(&ctx->stream, Z_NO_FLUSH); 1291 1292 if (zRC == Z_STREAM_END) { 1293 /* 1294 * We have inflated all data. Now try to capture the 1295 * validation bytes. We may not have them all available 1296 * right now, but capture what is there. 1297 */ 1298 ctx->validation_buffer = apr_pcalloc(f->r->pool, 1299 VALIDATION_SIZE); 1300 if (ctx->stream.avail_in > VALIDATION_SIZE) { 1301 ctx->validation_buffer_length = VALIDATION_SIZE; 1302 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 1303 "Zlib: %d bytes of garbage at the end of " 1304 "compressed stream.", 1305 ctx->stream.avail_in - VALIDATION_SIZE); 1306 } else if (ctx->stream.avail_in > 0) { 1307 ctx->validation_buffer_length = ctx->stream.avail_in; 1308 } 1309 if (ctx->validation_buffer_length) 1310 memcpy(ctx->validation_buffer, ctx->stream.next_in, 1311 ctx->validation_buffer_length); 1312 break; 1313 } 1314 1315 if (zRC != Z_OK) { 1316 return APR_EGENERAL; 1317 } 1318 } 1319 1320 apr_bucket_delete(e); 1321 } 1322 1323 apr_brigade_cleanup(bb); 1324 return APR_SUCCESS; 1325} 1326 1327#define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH 1328static void register_hooks(apr_pool_t *p) 1329{ 1330 ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL, 1331 AP_FTYPE_CONTENT_SET); 1332 ap_register_output_filter("INFLATE", inflate_out_filter, NULL, 1333 AP_FTYPE_RESOURCE-1); 1334 ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL, 1335 AP_FTYPE_CONTENT_SET); 1336} 1337 1338static const command_rec deflate_filter_cmds[] = { 1339 AP_INIT_TAKE12("DeflateFilterNote", deflate_set_note, NULL, RSRC_CONF, 1340 "Set a note to report on compression ratio"), 1341 AP_INIT_TAKE1("DeflateWindowSize", deflate_set_window_size, NULL, 1342 RSRC_CONF, "Set the Deflate window size (1-15)"), 1343 AP_INIT_TAKE1("DeflateBufferSize", deflate_set_buffer_size, NULL, RSRC_CONF, 1344 "Set the Deflate Buffer Size"), 1345 AP_INIT_TAKE1("DeflateMemLevel", deflate_set_memlevel, NULL, RSRC_CONF, 1346 "Set the Deflate Memory Level (1-9)"), 1347 AP_INIT_TAKE1("DeflateCompressionLevel", deflate_set_compressionlevel, NULL, RSRC_CONF, 1348 "Set the Deflate Compression Level (1-9)"), 1349 {NULL} 1350}; 1351 1352module AP_MODULE_DECLARE_DATA deflate_module = { 1353 STANDARD20_MODULE_STUFF, 1354 NULL, /* dir config creater */ 1355 NULL, /* dir merger --- default is to override */ 1356 create_deflate_server_config, /* server config */ 1357 NULL, /* merge server config */ 1358 deflate_filter_cmds, /* command table */ 1359 register_hooks /* register hooks */ 1360}; 1361