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