~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/filters/mod_deflate.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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_strings.h"
 
41
#include "apr_general.h"
 
42
#include "util_filter.h"
 
43
#include "apr_buckets.h"
 
44
#include "http_request.h"
 
45
#define APR_WANT_STRFUNC
 
46
#include "apr_want.h"
 
47
 
 
48
#include "zlib.h"
 
49
 
 
50
static const char deflateFilterName[] = "DEFLATE";
 
51
module AP_MODULE_DECLARE_DATA deflate_module;
 
52
 
 
53
typedef struct deflate_filter_config_t
 
54
{
 
55
    int windowSize;
 
56
    int memlevel;
 
57
    int compressionlevel;
 
58
    apr_size_t bufferSize;
 
59
    char *note_ratio_name;
 
60
    char *note_input_name;
 
61
    char *note_output_name;
 
62
} deflate_filter_config;
 
63
 
 
64
/* RFC 1952 Section 2.3 defines the gzip header:
 
65
 *
 
66
 * +---+---+---+---+---+---+---+---+---+---+
 
67
 * |ID1|ID2|CM |FLG|     MTIME     |XFL|OS |
 
68
 * +---+---+---+---+---+---+---+---+---+---+
 
69
 */
 
70
static const char gzip_header[10] =
 
71
{ '\037', '\213', Z_DEFLATED, 0,
 
72
  0, 0, 0, 0, /* mtime */
 
73
  0, 0x03 /* Unix OS_CODE */
 
74
};
 
75
 
 
76
/* magic header */
 
77
static const char deflate_magic[2] = { '\037', '\213' };
 
78
 
 
79
/* windowsize is negative to suppress Zlib header */
 
80
#define DEFAULT_COMPRESSION Z_DEFAULT_COMPRESSION
 
81
#define DEFAULT_WINDOWSIZE -15
 
82
#define DEFAULT_MEMLEVEL 9
 
83
#define DEFAULT_BUFFERSIZE 8096
 
84
 
 
85
/* Outputs a long in LSB order to the given file
 
86
 * only the bottom 4 bits are required for the deflate file format.
 
87
 */
 
88
static void putLong(unsigned char *string, unsigned long x)
 
89
{
 
90
    string[0] = (unsigned char)(x & 0xff);
 
91
    string[1] = (unsigned char)((x & 0xff00) >> 8);
 
92
    string[2] = (unsigned char)((x & 0xff0000) >> 16);
 
93
    string[3] = (unsigned char)((x & 0xff000000) >> 24);
 
94
}
 
95
 
 
96
/* Inputs a string and returns a long.
 
97
 */
 
98
static unsigned long getLong(unsigned char *string)
 
99
{
 
100
    return ((unsigned long)string[0])
 
101
          | (((unsigned long)string[1]) << 8)
 
102
          | (((unsigned long)string[2]) << 16)
 
103
          | (((unsigned long)string[3]) << 24);
 
104
}
 
105
 
 
106
static void *create_deflate_server_config(apr_pool_t *p, server_rec *s)
 
107
{
 
108
    deflate_filter_config *c = apr_pcalloc(p, sizeof *c);
 
109
 
 
110
    c->memlevel   = DEFAULT_MEMLEVEL;
 
111
    c->windowSize = DEFAULT_WINDOWSIZE;
 
112
    c->bufferSize = DEFAULT_BUFFERSIZE;
 
113
    c->compressionlevel = DEFAULT_COMPRESSION;
 
114
 
 
115
    return c;
 
116
}
 
117
 
 
118
static const char *deflate_set_window_size(cmd_parms *cmd, void *dummy,
 
119
                                           const char *arg)
 
120
{
 
121
    deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
 
122
                                                    &deflate_module);
 
123
    int i;
 
124
 
 
125
    i = atoi(arg);
 
126
 
 
127
    if (i < 1 || i > 15)
 
128
        return "DeflateWindowSize must be between 1 and 15";
 
129
 
 
130
    c->windowSize = i * -1;
 
131
 
 
132
    return NULL;
 
133
}
 
134
 
 
135
static const char *deflate_set_buffer_size(cmd_parms *cmd, void *dummy,
 
136
                                           const char *arg)
 
137
{
 
138
    deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
 
139
                                                    &deflate_module);
 
140
    int n = atoi(arg);
 
141
 
 
142
    if (n <= 0) {
 
143
        return "DeflateBufferSize should be positive";
 
144
    }
 
145
 
 
146
    c->bufferSize = (apr_size_t)n;
 
147
 
 
148
    return NULL;
 
149
}
 
150
static const char *deflate_set_note(cmd_parms *cmd, void *dummy,
 
151
                                    const char *arg1, const char *arg2)
 
152
{
 
153
    deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
 
154
                                                    &deflate_module);
 
155
 
 
156
    if (arg2 == NULL) {
 
157
        c->note_ratio_name = apr_pstrdup(cmd->pool, arg1);
 
158
    }
 
159
    else if (!strcasecmp(arg1, "ratio")) {
 
160
        c->note_ratio_name = apr_pstrdup(cmd->pool, arg2);
 
161
    }
 
162
    else if (!strcasecmp(arg1, "input")) {
 
163
        c->note_input_name = apr_pstrdup(cmd->pool, arg2);
 
164
    }
 
165
    else if (!strcasecmp(arg1, "output")) {
 
166
        c->note_output_name = apr_pstrdup(cmd->pool, arg2);
 
167
    }
 
168
    else {
 
169
        return apr_psprintf(cmd->pool, "Unknown note type %s", arg1);
 
170
    }
 
171
 
 
172
    return NULL;
 
173
}
 
174
 
 
175
static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy,
 
176
                                        const char *arg)
 
177
{
 
178
    deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
 
179
                                                    &deflate_module);
 
180
    int i;
 
181
 
 
182
    i = atoi(arg);
 
183
 
 
184
    if (i < 1 || i > 9)
 
185
        return "DeflateMemLevel must be between 1 and 9";
 
186
 
 
187
    c->memlevel = i;
 
188
 
 
189
    return NULL;
 
190
}
 
191
 
 
192
static const char *deflate_set_compressionlevel(cmd_parms *cmd, void *dummy,
 
193
                                        const char *arg)
 
194
{
 
195
    deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
 
196
                                                    &deflate_module);
 
197
    int i;
 
198
 
 
199
    i = atoi(arg);
 
200
 
 
201
    if (i < 1 || i > 9)
 
202
        return "Compression Level must be between 1 and 9";
 
203
 
 
204
    c->compressionlevel = i;
 
205
 
 
206
    return NULL;
 
207
}
 
208
 
 
209
typedef struct deflate_ctx_t
 
210
{
 
211
    z_stream stream;
 
212
    unsigned char *buffer;
 
213
    unsigned long crc;
 
214
    apr_bucket_brigade *bb, *proc_bb;
 
215
} deflate_ctx;
 
216
 
 
217
static apr_status_t deflate_out_filter(ap_filter_t *f,
 
218
                                       apr_bucket_brigade *bb)
 
219
{
 
220
    apr_bucket *e;
 
221
    request_rec *r = f->r;
 
222
    deflate_ctx *ctx = f->ctx;
 
223
    int zRC;
 
224
    deflate_filter_config *c = ap_get_module_config(r->server->module_config,
 
225
                                                    &deflate_module);
 
226
 
 
227
    /* Do nothing if asked to filter nothing. */
 
228
    if (APR_BRIGADE_EMPTY(bb)) {
 
229
        return APR_SUCCESS;
 
230
    }
 
231
 
 
232
    /* If we don't have a context, we need to ensure that it is okay to send
 
233
     * the deflated content.  If we have a context, that means we've done
 
234
     * this before and we liked it.
 
235
     * This could be not so nice if we always fail.  But, if we succeed,
 
236
     * we're in better shape.
 
237
     */
 
238
    if (!ctx) {
 
239
        char *token;
 
240
        const char *encoding;
 
241
 
 
242
        /* only work on main request/no subrequests */
 
243
        if (r->main != NULL) {
 
244
            ap_remove_output_filter(f);
 
245
            return ap_pass_brigade(f->next, bb);
 
246
        }
 
247
 
 
248
        /* some browsers might have problems, so set no-gzip
 
249
         * (with browsermatch) for them
 
250
         */
 
251
        if (apr_table_get(r->subprocess_env, "no-gzip")) {
 
252
            ap_remove_output_filter(f);
 
253
            return ap_pass_brigade(f->next, bb);
 
254
        }
 
255
 
 
256
        /* Some browsers might have problems with content types
 
257
         * other than text/html, so set gzip-only-text/html
 
258
         * (with browsermatch) for them
 
259
         */
 
260
        if (r->content_type == NULL
 
261
             || strncmp(r->content_type, "text/html", 9)) {
 
262
            const char *env_value = apr_table_get(r->subprocess_env,
 
263
                                                  "gzip-only-text/html");
 
264
            if ( env_value && (strcmp(env_value,"1") == 0) ) {
 
265
                ap_remove_output_filter(f);
 
266
                return ap_pass_brigade(f->next, bb);
 
267
            }
 
268
        }
 
269
 
 
270
        /* Let's see what our current Content-Encoding is.
 
271
         * If it's already encoded, don't compress again.
 
272
         * (We could, but let's not.)
 
273
         */
 
274
        encoding = apr_table_get(r->headers_out, "Content-Encoding");
 
275
        if (encoding) {
 
276
            const char *err_enc;
 
277
 
 
278
            err_enc = apr_table_get(r->err_headers_out, "Content-Encoding");
 
279
            if (err_enc) {
 
280
                encoding = apr_pstrcat(r->pool, encoding, ",", err_enc, NULL);
 
281
            }
 
282
        }
 
283
        else {
 
284
            encoding = apr_table_get(r->err_headers_out, "Content-Encoding");
 
285
        }
 
286
 
 
287
        if (r->content_encoding) {
 
288
            encoding = encoding ? apr_pstrcat(r->pool, encoding, ",",
 
289
                                              r->content_encoding, NULL)
 
290
                                : r->content_encoding;
 
291
        }
 
292
 
 
293
        if (encoding) {
 
294
            const char *tmp = encoding;
 
295
 
 
296
            token = ap_get_token(r->pool, &tmp, 0);
 
297
            while (token && *token) {
 
298
                /* stolen from mod_negotiation: */
 
299
                if (strcmp(token, "identity") && strcmp(token, "7bit") &&
 
300
                    strcmp(token, "8bit") && strcmp(token, "binary")) {
 
301
 
 
302
                    ap_remove_output_filter(f);
 
303
                    return ap_pass_brigade(f->next, bb);
 
304
                }
 
305
 
 
306
                /* Otherwise, skip token */
 
307
                if (*tmp) {
 
308
                    ++tmp;
 
309
                }
 
310
                token = (*tmp) ? ap_get_token(r->pool, &tmp, 0) : NULL;
 
311
            }
 
312
        }
 
313
 
 
314
        /* Even if we don't accept this request based on it not having
 
315
         * the Accept-Encoding, we need to note that we were looking
 
316
         * for this header and downstream proxies should be aware of that.
 
317
         */
 
318
        apr_table_mergen(r->headers_out, "Vary", "Accept-Encoding");
 
319
 
 
320
        /* force-gzip will just force it out regardless if the browser
 
321
         * can actually do anything with it.
 
322
         */
 
323
        if (!apr_table_get(r->subprocess_env, "force-gzip")) {
 
324
            const char *accepts;
 
325
            /* if they don't have the line, then they can't play */
 
326
            accepts = apr_table_get(r->headers_in, "Accept-Encoding");
 
327
            if (accepts == NULL) {
 
328
                ap_remove_output_filter(f);
 
329
                return ap_pass_brigade(f->next, bb);
 
330
            }
 
331
 
 
332
            token = ap_get_token(r->pool, &accepts, 0);
 
333
            while (token && token[0] && strcasecmp(token, "gzip")) {
 
334
                /* skip parameters, XXX: ;q=foo evaluation? */
 
335
                while (*accepts == ';') {
 
336
                    ++accepts;
 
337
                    token = ap_get_token(r->pool, &accepts, 1);
 
338
                }
 
339
 
 
340
                /* retrieve next token */
 
341
                if (*accepts == ',') {
 
342
                    ++accepts;
 
343
                }
 
344
                token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL;
 
345
            }
 
346
 
 
347
            /* No acceptable token found. */
 
348
            if (token == NULL || token[0] == '\0') {
 
349
                ap_remove_output_filter(f);
 
350
                return ap_pass_brigade(f->next, bb);
 
351
            }
 
352
        }
 
353
 
 
354
        /* For a 304 or 204 response there is no entity included in
 
355
         * the response and hence nothing to deflate. */
 
356
        if (r->status == HTTP_NOT_MODIFIED || r->status == HTTP_NO_CONTENT) {
 
357
            ap_remove_output_filter(f);
 
358
            return ap_pass_brigade(f->next, bb);
 
359
        }
 
360
 
 
361
        /* We're cool with filtering this. */
 
362
        ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
 
363
        ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
 
364
        ctx->buffer = apr_palloc(r->pool, c->bufferSize);
 
365
 
 
366
        zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED,
 
367
                           c->windowSize, c->memlevel,
 
368
                           Z_DEFAULT_STRATEGY);
 
369
 
 
370
        if (zRC != Z_OK) {
 
371
            f->ctx = NULL;
 
372
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
373
                          "unable to init Zlib: "
 
374
                          "deflateInit2 returned %d: URL %s",
 
375
                          zRC, r->uri);
 
376
            return ap_pass_brigade(f->next, bb);
 
377
        }
 
378
 
 
379
        /* add immortal gzip header */
 
380
        e = apr_bucket_immortal_create(gzip_header, sizeof gzip_header,
 
381
                                       f->c->bucket_alloc);
 
382
        APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
 
383
 
 
384
        /* If the entire Content-Encoding is "identity", we can replace it. */
 
385
        if (!encoding || !strcasecmp(encoding, "identity")) {
 
386
            apr_table_setn(r->headers_out, "Content-Encoding", "gzip");
 
387
        }
 
388
        else {
 
389
            apr_table_mergen(r->headers_out, "Content-Encoding", "gzip");
 
390
        }
 
391
        apr_table_unset(r->headers_out, "Content-Length");
 
392
 
 
393
        /* initialize deflate output buffer */
 
394
        ctx->stream.next_out = ctx->buffer;
 
395
        ctx->stream.avail_out = c->bufferSize;
 
396
    }
 
397
 
 
398
    while (!APR_BRIGADE_EMPTY(bb))
 
399
    {
 
400
        const char *data;
 
401
        apr_bucket *b;
 
402
        apr_size_t len;
 
403
        int done = 0;
 
404
 
 
405
        e = APR_BRIGADE_FIRST(bb);
 
406
 
 
407
        if (APR_BUCKET_IS_EOS(e)) {
 
408
            char *buf;
 
409
            unsigned int deflate_len;
 
410
 
 
411
            ctx->stream.avail_in = 0; /* should be zero already anyway */
 
412
            for (;;) {
 
413
                deflate_len = c->bufferSize - ctx->stream.avail_out;
 
414
 
 
415
                if (deflate_len != 0) {
 
416
                    b = apr_bucket_heap_create((char *)ctx->buffer,
 
417
                                               deflate_len, NULL,
 
418
                                               f->c->bucket_alloc);
 
419
                    APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
 
420
                    ctx->stream.next_out = ctx->buffer;
 
421
                    ctx->stream.avail_out = c->bufferSize;
 
422
                }
 
423
 
 
424
                if (done) {
 
425
                    break;
 
426
                }
 
427
 
 
428
                zRC = deflate(&ctx->stream, Z_FINISH);
 
429
 
 
430
                if (deflate_len == 0 && zRC == Z_BUF_ERROR) {
 
431
                    zRC = Z_OK;
 
432
                }
 
433
 
 
434
                done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END);
 
435
 
 
436
                if (zRC != Z_OK && zRC != Z_STREAM_END) {
 
437
                    break;
 
438
                }
 
439
            }
 
440
 
 
441
            buf = apr_palloc(r->pool, 8);
 
442
            putLong((unsigned char *)&buf[0], ctx->crc);
 
443
            putLong((unsigned char *)&buf[4], ctx->stream.total_in);
 
444
 
 
445
            b = apr_bucket_pool_create(buf, 8, r->pool, f->c->bucket_alloc);
 
446
            APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
 
447
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
 
448
                          "Zlib: Compressed %ld to %ld : URL %s",
 
449
                          ctx->stream.total_in, ctx->stream.total_out, r->uri);
 
450
 
 
451
            /* leave notes for logging */
 
452
            if (c->note_input_name) {
 
453
                apr_table_setn(r->notes, c->note_input_name,
 
454
                               (ctx->stream.total_in > 0)
 
455
                                ? apr_off_t_toa(r->pool,
 
456
                                                ctx->stream.total_in)
 
457
                                : "-");
 
458
            }
 
459
 
 
460
            if (c->note_output_name) {
 
461
                apr_table_setn(r->notes, c->note_output_name,
 
462
                               (ctx->stream.total_in > 0)
 
463
                                ? apr_off_t_toa(r->pool,
 
464
                                                ctx->stream.total_out)
 
465
                                : "-");
 
466
            }
 
467
 
 
468
            if (c->note_ratio_name) {
 
469
                apr_table_setn(r->notes, c->note_ratio_name,
 
470
                               (ctx->stream.total_in > 0)
 
471
                                ? apr_itoa(r->pool,
 
472
                                           (int)(ctx->stream.total_out
 
473
                                                 * 100
 
474
                                                 / ctx->stream.total_in))
 
475
                                : "-");
 
476
            }
 
477
 
 
478
            deflateEnd(&ctx->stream);
 
479
 
 
480
            /* Remove EOS from the old list, and insert into the new. */
 
481
            APR_BUCKET_REMOVE(e);
 
482
            APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
 
483
 
 
484
            /* Okay, we've seen the EOS.
 
485
             * Time to pass it along down the chain.
 
486
             */
 
487
            return ap_pass_brigade(f->next, ctx->bb);
 
488
        }
 
489
 
 
490
        if (APR_BUCKET_IS_FLUSH(e)) {
 
491
            apr_bucket *bkt;
 
492
            apr_status_t rv;
 
493
 
 
494
            apr_bucket_delete(e);
 
495
 
 
496
            if (ctx->stream.avail_in > 0) {
 
497
                zRC = deflate(&(ctx->stream), Z_SYNC_FLUSH);
 
498
                if (zRC != Z_OK) {
 
499
                    return APR_EGENERAL;
 
500
                }
 
501
            }
 
502
 
 
503
            ctx->stream.next_out = ctx->buffer;
 
504
            len = c->bufferSize - ctx->stream.avail_out;
 
505
 
 
506
            b = apr_bucket_heap_create((char *)ctx->buffer, len,
 
507
                                       NULL, f->c->bucket_alloc);
 
508
            APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
 
509
            ctx->stream.avail_out = c->bufferSize;
 
510
 
 
511
            bkt = apr_bucket_flush_create(f->c->bucket_alloc);
 
512
            APR_BRIGADE_INSERT_TAIL(ctx->bb, bkt);
 
513
            rv = ap_pass_brigade(f->next, ctx->bb);
 
514
            if (rv != APR_SUCCESS) {
 
515
                return rv;
 
516
            }
 
517
            continue;
 
518
        }
 
519
 
 
520
        /* read */
 
521
        apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
 
522
 
 
523
        /* This crc32 function is from zlib. */
 
524
        ctx->crc = crc32(ctx->crc, (const Bytef *)data, len);
 
525
 
 
526
        /* write */
 
527
        ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness,
 
528
                                                      * but we'll just have to
 
529
                                                      * trust zlib */
 
530
        ctx->stream.avail_in = len;
 
531
 
 
532
        while (ctx->stream.avail_in != 0) {
 
533
            if (ctx->stream.avail_out == 0) {
 
534
                apr_status_t rv;
 
535
 
 
536
                ctx->stream.next_out = ctx->buffer;
 
537
                len = c->bufferSize - ctx->stream.avail_out;
 
538
 
 
539
                b = apr_bucket_heap_create((char *)ctx->buffer, len,
 
540
                                           NULL, f->c->bucket_alloc);
 
541
                APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
 
542
                ctx->stream.avail_out = c->bufferSize;
 
543
                /* Send what we have right now to the next filter. */
 
544
                rv = ap_pass_brigade(f->next, ctx->bb);
 
545
                if (rv != APR_SUCCESS) {
 
546
                    return rv;
 
547
                }
 
548
            }
 
549
 
 
550
            zRC = deflate(&(ctx->stream), Z_NO_FLUSH);
 
551
 
 
552
            if (zRC != Z_OK)
 
553
                return APR_EGENERAL;
 
554
        }
 
555
 
 
556
        apr_bucket_delete(e);
 
557
    }
 
558
 
 
559
    apr_brigade_cleanup(bb);
 
560
    return APR_SUCCESS;
 
561
}
 
562
 
 
563
/* This is the deflate input filter (inflates).  */
 
564
static apr_status_t deflate_in_filter(ap_filter_t *f,
 
565
                                      apr_bucket_brigade *bb,
 
566
                                      ap_input_mode_t mode,
 
567
                                      apr_read_type_e block,
 
568
                                      apr_off_t readbytes)
 
569
{
 
570
    apr_bucket *bkt;
 
571
    request_rec *r = f->r;
 
572
    deflate_ctx *ctx = f->ctx;
 
573
    int zRC;
 
574
    apr_status_t rv;
 
575
    deflate_filter_config *c;
 
576
 
 
577
    /* just get out of the way of things we don't want. */
 
578
    if (mode != AP_MODE_READBYTES) {
 
579
        return ap_get_brigade(f->next, bb, mode, block, readbytes);
 
580
    }
 
581
 
 
582
    c = ap_get_module_config(r->server->module_config, &deflate_module);
 
583
 
 
584
    if (!ctx) {
 
585
        int found = 0;
 
586
        char *token, deflate_hdr[10];
 
587
        const char *encoding;
 
588
        apr_size_t len;
 
589
 
 
590
        /* only work on main request/no subrequests */
 
591
        if (!ap_is_initial_req(r)) {
 
592
            ap_remove_input_filter(f);
 
593
            return ap_get_brigade(f->next, bb, mode, block, readbytes);
 
594
        }
 
595
 
 
596
        /* Let's see what our current Content-Encoding is.
 
597
         * If gzip is present, don't gzip again.  (We could, but let's not.)
 
598
         */
 
599
        encoding = apr_table_get(r->headers_in, "Content-Encoding");
 
600
        if (encoding) {
 
601
            const char *tmp = encoding;
 
602
 
 
603
            token = ap_get_token(r->pool, &tmp, 0);
 
604
            while (token && token[0]) {
 
605
                if (!strcasecmp(token, "gzip")) {
 
606
                    found = 1;
 
607
                    break;
 
608
                }
 
609
                /* Otherwise, skip token */
 
610
                tmp++;
 
611
                token = ap_get_token(r->pool, &tmp, 0);
 
612
            }
 
613
        }
 
614
 
 
615
        if (found == 0) {
 
616
            ap_remove_input_filter(f);
 
617
            return ap_get_brigade(f->next, bb, mode, block, readbytes);
 
618
        }
 
619
 
 
620
        f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
 
621
        ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
 
622
        ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
 
623
        ctx->buffer = apr_palloc(r->pool, c->bufferSize);
 
624
 
 
625
        rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10);
 
626
        if (rv != APR_SUCCESS) {
 
627
            return rv;
 
628
        }
 
629
 
 
630
        len = 10;
 
631
        rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len);
 
632
        if (rv != APR_SUCCESS) {
 
633
            return rv;
 
634
        }
 
635
 
 
636
        /* We didn't get the magic bytes. */
 
637
        if (len != 10 ||
 
638
            deflate_hdr[0] != deflate_magic[0] ||
 
639
            deflate_hdr[1] != deflate_magic[1]) {
 
640
            return APR_EGENERAL;
 
641
        }
 
642
 
 
643
        /* We can't handle flags for now. */
 
644
        if (deflate_hdr[3] != 0) {
 
645
            return APR_EGENERAL;
 
646
        }
 
647
 
 
648
        zRC = inflateInit2(&ctx->stream, c->windowSize);
 
649
 
 
650
        if (zRC != Z_OK) {
 
651
            f->ctx = NULL;
 
652
            inflateEnd(&ctx->stream);
 
653
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
654
                          "unable to init Zlib: "
 
655
                          "inflateInit2 returned %d: URL %s",
 
656
                          zRC, r->uri);
 
657
            ap_remove_input_filter(f);
 
658
            return ap_get_brigade(f->next, bb, mode, block, readbytes);
 
659
        }
 
660
 
 
661
        /* initialize deflate output buffer */
 
662
        ctx->stream.next_out = ctx->buffer;
 
663
        ctx->stream.avail_out = c->bufferSize;
 
664
 
 
665
        apr_brigade_cleanup(ctx->bb);
 
666
    }
 
667
 
 
668
    if (APR_BRIGADE_EMPTY(ctx->proc_bb)) {
 
669
        rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
 
670
 
 
671
        if (rv != APR_SUCCESS) {
 
672
            /* What about APR_EAGAIN errors? */
 
673
            inflateEnd(&ctx->stream);
 
674
            return rv;
 
675
        }
 
676
 
 
677
        for (bkt = APR_BRIGADE_FIRST(ctx->bb);
 
678
             bkt != APR_BRIGADE_SENTINEL(ctx->bb);
 
679
             bkt = APR_BUCKET_NEXT(bkt))
 
680
        {
 
681
            const char *data;
 
682
            apr_size_t len;
 
683
 
 
684
            /* If we actually see the EOS, that means we screwed up! */
 
685
            if (APR_BUCKET_IS_EOS(bkt)) {
 
686
                inflateEnd(&ctx->stream);
 
687
                return APR_EGENERAL;
 
688
            }
 
689
 
 
690
            if (APR_BUCKET_IS_FLUSH(bkt)) {
 
691
                apr_bucket *tmp_heap;
 
692
                zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
 
693
                if (zRC != Z_OK) {
 
694
                    inflateEnd(&ctx->stream);
 
695
                    return APR_EGENERAL;
 
696
                }
 
697
 
 
698
                ctx->stream.next_out = ctx->buffer;
 
699
                len = c->bufferSize - ctx->stream.avail_out;
 
700
 
 
701
                ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
 
702
                tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
 
703
                                                 NULL, f->c->bucket_alloc);
 
704
                APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
 
705
                ctx->stream.avail_out = c->bufferSize;
 
706
 
 
707
                /* Move everything to the returning brigade. */
 
708
                APR_BUCKET_REMOVE(bkt);
 
709
                APR_BRIGADE_CONCAT(bb, ctx->bb);
 
710
                break;
 
711
            }
 
712
 
 
713
            /* read */
 
714
            apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
 
715
 
 
716
            /* pass through zlib inflate. */
 
717
            ctx->stream.next_in = (unsigned char *)data;
 
718
            ctx->stream.avail_in = len;
 
719
 
 
720
            zRC = Z_OK;
 
721
 
 
722
            while (ctx->stream.avail_in != 0) {
 
723
                if (ctx->stream.avail_out == 0) {
 
724
                    apr_bucket *tmp_heap;
 
725
                    ctx->stream.next_out = ctx->buffer;
 
726
                    len = c->bufferSize - ctx->stream.avail_out;
 
727
 
 
728
                    ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
 
729
                    tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
 
730
                                                      NULL, f->c->bucket_alloc);
 
731
                    APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
 
732
                    ctx->stream.avail_out = c->bufferSize;
 
733
                }
 
734
 
 
735
                zRC = inflate(&ctx->stream, Z_NO_FLUSH);
 
736
 
 
737
                if (zRC == Z_STREAM_END) {
 
738
                    break;
 
739
                }
 
740
 
 
741
                if (zRC != Z_OK) {
 
742
                    inflateEnd(&ctx->stream);
 
743
                    return APR_EGENERAL;
 
744
                }
 
745
            }
 
746
            if (zRC == Z_STREAM_END) {
 
747
                apr_bucket *tmp_heap, *eos;
 
748
 
 
749
                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
 
750
                              "Zlib: Inflated %ld to %ld : URL %s",
 
751
                              ctx->stream.total_in, ctx->stream.total_out,
 
752
                              r->uri);
 
753
 
 
754
                len = c->bufferSize - ctx->stream.avail_out;
 
755
 
 
756
                ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
 
757
                tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
 
758
                                                  NULL, f->c->bucket_alloc);
 
759
                APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
 
760
                ctx->stream.avail_out = c->bufferSize;
 
761
 
 
762
                /* Is the remaining 8 bytes already in the avail stream? */
 
763
                if (ctx->stream.avail_in >= 8) {
 
764
                    unsigned long compCRC, compLen;
 
765
                    compCRC = getLong(ctx->stream.next_in);
 
766
                    if (ctx->crc != compCRC) {
 
767
                        inflateEnd(&ctx->stream);
 
768
                        return APR_EGENERAL;
 
769
                    }
 
770
                    ctx->stream.next_in += 4;
 
771
                    compLen = getLong(ctx->stream.next_in);
 
772
                    if (ctx->stream.total_out != compLen) {
 
773
                        inflateEnd(&ctx->stream);
 
774
                        return APR_EGENERAL;
 
775
                    }
 
776
                }
 
777
                else {
 
778
                    /* FIXME: We need to grab the 8 verification bytes
 
779
                     * from the wire! */
 
780
                    inflateEnd(&ctx->stream);
 
781
                    return APR_EGENERAL;
 
782
                }
 
783
 
 
784
                inflateEnd(&ctx->stream);
 
785
 
 
786
                eos = apr_bucket_eos_create(f->c->bucket_alloc);
 
787
                APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos);
 
788
                break;
 
789
            }
 
790
 
 
791
        }
 
792
        apr_brigade_cleanup(ctx->bb);
 
793
    }
 
794
 
 
795
    /* If we are about to return nothing for a 'blocking' read and we have
 
796
     * some data in our zlib buffer, flush it out so we can return something.
 
797
     */
 
798
    if (block == APR_BLOCK_READ &&
 
799
        APR_BRIGADE_EMPTY(ctx->proc_bb) &&
 
800
        ctx->stream.avail_out < c->bufferSize) {
 
801
        apr_bucket *tmp_heap;
 
802
        apr_size_t len;
 
803
        ctx->stream.next_out = ctx->buffer;
 
804
        len = c->bufferSize - ctx->stream.avail_out;
 
805
 
 
806
        ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
 
807
        tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
 
808
                                          NULL, f->c->bucket_alloc);
 
809
        APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
 
810
        ctx->stream.avail_out = c->bufferSize;
 
811
    }
 
812
 
 
813
    if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) {
 
814
        apr_bucket_brigade *newbb;
 
815
 
 
816
        /* May return APR_INCOMPLETE which is fine by us. */
 
817
        apr_brigade_partition(ctx->proc_bb, readbytes, &bkt);
 
818
 
 
819
        newbb = apr_brigade_split(ctx->proc_bb, bkt);
 
820
        APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
 
821
        APR_BRIGADE_CONCAT(ctx->proc_bb, newbb);
 
822
    }
 
823
 
 
824
    return APR_SUCCESS;
 
825
}
 
826
 
 
827
 
 
828
/* Filter to inflate for a content-transforming proxy.  */
 
829
static apr_status_t inflate_out_filter(ap_filter_t *f,
 
830
                                      apr_bucket_brigade *bb)
 
831
{
 
832
    int zlib_method;
 
833
    int zlib_flags;
 
834
    int deflate_init = 1;
 
835
    apr_bucket *bkt;
 
836
    request_rec *r = f->r;
 
837
    deflate_ctx *ctx = f->ctx;
 
838
    int zRC;
 
839
    apr_status_t rv;
 
840
    deflate_filter_config *c;
 
841
 
 
842
    /* Do nothing if asked to filter nothing. */
 
843
    if (APR_BRIGADE_EMPTY(bb)) {
 
844
        return APR_SUCCESS;
 
845
    }
 
846
 
 
847
    c = ap_get_module_config(r->server->module_config, &deflate_module);
 
848
 
 
849
    if (!ctx) {
 
850
        int found = 0;
 
851
        char *token;
 
852
        const char *encoding;
 
853
 
 
854
        /* only work on main request/no subrequests */
 
855
        if (!ap_is_initial_req(r)) {
 
856
            ap_remove_output_filter(f);
 
857
            return ap_pass_brigade(f->next, bb);
 
858
        }
 
859
 
 
860
        /* Let's see what our current Content-Encoding is.
 
861
         * If gzip is present, don't gzip again.  (We could, but let's not.)
 
862
         */
 
863
        encoding = apr_table_get(r->headers_out, "Content-Encoding");
 
864
        if (encoding) {
 
865
            const char *tmp = encoding;
 
866
 
 
867
            token = ap_get_token(r->pool, &tmp, 0);
 
868
            while (token && token[0]) {
 
869
                if (!strcasecmp(token, "gzip")) {
 
870
                    found = 1;
 
871
                    break;
 
872
                }
 
873
                /* Otherwise, skip token */
 
874
                tmp++;
 
875
                token = ap_get_token(r->pool, &tmp, 0);
 
876
            }
 
877
        }
 
878
 
 
879
        if (found == 0) {
 
880
            ap_remove_output_filter(f);
 
881
            return ap_pass_brigade(f->next, bb);
 
882
        }
 
883
        apr_table_unset(r->headers_out, "Content-Encoding");
 
884
 
 
885
        /* No need to inflate HEAD or 204/304 */
 
886
        if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bb))) {
 
887
            ap_remove_output_filter(f);
 
888
            return ap_pass_brigade(f->next, bb);
 
889
        }
 
890
 
 
891
 
 
892
        f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
 
893
        ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
 
894
        ctx->buffer = apr_palloc(r->pool, c->bufferSize);
 
895
 
 
896
 
 
897
        zRC = inflateInit2(&ctx->stream, c->windowSize);
 
898
 
 
899
        if (zRC != Z_OK) {
 
900
            f->ctx = NULL;
 
901
            inflateEnd(&ctx->stream);
 
902
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
903
                          "unable to init Zlib: "
 
904
                          "inflateInit2 returned %d: URL %s",
 
905
                          zRC, r->uri);
 
906
            ap_remove_output_filter(f);
 
907
            return ap_pass_brigade(f->next, bb);
 
908
        }
 
909
 
 
910
        /* initialize deflate output buffer */
 
911
        ctx->stream.next_out = ctx->buffer;
 
912
        ctx->stream.avail_out = c->bufferSize;
 
913
 
 
914
        deflate_init = 0;
 
915
    }
 
916
 
 
917
    for (bkt = APR_BRIGADE_FIRST(bb);
 
918
         bkt != APR_BRIGADE_SENTINEL(bb);
 
919
         bkt = APR_BUCKET_NEXT(bkt))
 
920
    {
 
921
        const char *data;
 
922
        apr_size_t len;
 
923
 
 
924
        /* If we actually see the EOS, that means we screwed up! */
 
925
        /* no it doesn't - not in a HEAD or 204/304 */
 
926
        if (APR_BUCKET_IS_EOS(bkt)) {
 
927
            inflateEnd(&ctx->stream);
 
928
            return ap_pass_brigade(f->next, bb);
 
929
        }
 
930
 
 
931
        if (APR_BUCKET_IS_FLUSH(bkt)) {
 
932
            apr_bucket *tmp_heap;
 
933
            zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
 
934
            if (zRC != Z_OK) {
 
935
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
936
                              "Inflate error %d on flush", zRC);
 
937
                inflateEnd(&ctx->stream);
 
938
                return APR_EGENERAL;
 
939
            }
 
940
 
 
941
            ctx->stream.next_out = ctx->buffer;
 
942
            len = c->bufferSize - ctx->stream.avail_out;
 
943
 
 
944
            ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
 
945
            tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
 
946
                                             NULL, f->c->bucket_alloc);
 
947
            APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
 
948
            ctx->stream.avail_out = c->bufferSize;
 
949
 
 
950
            /* Move everything to the returning brigade. */
 
951
            APR_BUCKET_REMOVE(bkt);
 
952
            break;
 
953
        }
 
954
 
 
955
        /* read */
 
956
        apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
 
957
 
 
958
        /* first bucket contains zlib header */
 
959
        if (!deflate_init++) {
 
960
            if (len < 10) {
 
961
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
962
                              "Insufficient data for inflate");
 
963
                return APR_EGENERAL;
 
964
            }
 
965
            else  {
 
966
                zlib_method = data[2];
 
967
                zlib_flags = data[3];
 
968
                if (zlib_method != Z_DEFLATED) {
 
969
                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
 
970
                                  "inflate: data not deflated!");
 
971
                    ap_remove_output_filter(f);
 
972
                    return ap_pass_brigade(f->next, bb);
 
973
                }
 
974
                if (data[0] != deflate_magic[0] ||
 
975
                    data[1] != deflate_magic[1] ||
 
976
                    (zlib_flags & RESERVED) != 0) {
 
977
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
978
                                      "inflate: bad header");
 
979
                    return APR_EGENERAL ;
 
980
                }
 
981
                data += 10 ;
 
982
                len -= 10 ;
 
983
           }
 
984
           if (zlib_flags & EXTRA_FIELD) {
 
985
               unsigned int bytes = (unsigned int)(data[0]);
 
986
               bytes += ((unsigned int)(data[1])) << 8;
 
987
               bytes += 2;
 
988
               if (len < bytes) {
 
989
                   ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
990
                                 "inflate: extra field too big (not "
 
991
                                 "supported)");
 
992
                   return APR_EGENERAL;
 
993
               }
 
994
               data += bytes;
 
995
               len -= bytes;
 
996
           }
 
997
           if (zlib_flags & ORIG_NAME) {
 
998
               while (len-- && *data++);
 
999
           }
 
1000
           if (zlib_flags & COMMENT) {
 
1001
               while (len-- && *data++);
 
1002
           }
 
1003
           if (zlib_flags & HEAD_CRC) {
 
1004
                len -= 2;
 
1005
                data += 2;
 
1006
           }
 
1007
        }
 
1008
 
 
1009
        /* pass through zlib inflate. */
 
1010
        ctx->stream.next_in = (unsigned char *)data;
 
1011
        ctx->stream.avail_in = len;
 
1012
 
 
1013
        zRC = Z_OK;
 
1014
 
 
1015
        while (ctx->stream.avail_in != 0) {
 
1016
            if (ctx->stream.avail_out == 0) {
 
1017
                apr_bucket *tmp_heap;
 
1018
                ctx->stream.next_out = ctx->buffer;
 
1019
                len = c->bufferSize - ctx->stream.avail_out;
 
1020
 
 
1021
                ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
 
1022
                tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
 
1023
                                                  NULL, f->c->bucket_alloc);
 
1024
                APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
 
1025
                ctx->stream.avail_out = c->bufferSize;
 
1026
            }
 
1027
 
 
1028
            zRC = inflate(&ctx->stream, Z_NO_FLUSH);
 
1029
 
 
1030
            if (zRC == Z_STREAM_END) {
 
1031
                break;
 
1032
            }
 
1033
 
 
1034
            if (zRC != Z_OK) {
 
1035
                    inflateEnd(&ctx->stream);
 
1036
                    return APR_EGENERAL;
 
1037
            }
 
1038
        }
 
1039
        if (zRC == Z_STREAM_END) {
 
1040
            apr_bucket *tmp_heap, *eos;
 
1041
 
 
1042
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
 
1043
                          "Zlib: Inflated %ld to %ld : URL %s",
 
1044
                          ctx->stream.total_in, ctx->stream.total_out,
 
1045
                          r->uri);
 
1046
 
 
1047
            len = c->bufferSize - ctx->stream.avail_out;
 
1048
 
 
1049
            ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
 
1050
            tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
 
1051
                                              NULL, f->c->bucket_alloc);
 
1052
            APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
 
1053
            ctx->stream.avail_out = c->bufferSize;
 
1054
 
 
1055
            /* Is the remaining 8 bytes already in the avail stream? */
 
1056
            if (ctx->stream.avail_in >= 8) {
 
1057
                unsigned long compCRC, compLen;
 
1058
                compCRC = getLong(ctx->stream.next_in);
 
1059
                if (ctx->crc != compCRC) {
 
1060
                    inflateEnd(&ctx->stream);
 
1061
                    return APR_EGENERAL;
 
1062
                }
 
1063
                ctx->stream.next_in += 4;
 
1064
                compLen = getLong(ctx->stream.next_in);
 
1065
                if (ctx->stream.total_out != compLen) {
 
1066
                    inflateEnd(&ctx->stream);
 
1067
                    return APR_EGENERAL;
 
1068
                }
 
1069
            }
 
1070
            else {
 
1071
                /* FIXME: We need to grab the 8 verification bytes
 
1072
                 * from the wire! */
 
1073
                inflateEnd(&ctx->stream);
 
1074
                return APR_EGENERAL;
 
1075
            }
 
1076
 
 
1077
            inflateEnd(&ctx->stream);
 
1078
 
 
1079
            eos = apr_bucket_eos_create(f->c->bucket_alloc);
 
1080
            APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos);
 
1081
            break;
 
1082
        }
 
1083
 
 
1084
    }
 
1085
 
 
1086
    rv = ap_pass_brigade(f->next, ctx->proc_bb);
 
1087
    apr_brigade_cleanup(ctx->proc_bb);
 
1088
    return rv ;
 
1089
}
 
1090
 
 
1091
static void register_hooks(apr_pool_t *p)
 
1092
{
 
1093
    ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL,
 
1094
                              AP_FTYPE_CONTENT_SET);
 
1095
    ap_register_output_filter("INFLATE", inflate_out_filter, NULL,
 
1096
                              AP_FTYPE_RESOURCE-1);
 
1097
    ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL,
 
1098
                              AP_FTYPE_CONTENT_SET);
 
1099
}
 
1100
 
 
1101
static const command_rec deflate_filter_cmds[] = {
 
1102
    AP_INIT_TAKE12("DeflateFilterNote", deflate_set_note, NULL, RSRC_CONF,
 
1103
                  "Set a note to report on compression ratio"),
 
1104
    AP_INIT_TAKE1("DeflateWindowSize", deflate_set_window_size, NULL,
 
1105
                  RSRC_CONF, "Set the Deflate window size (1-15)"),
 
1106
    AP_INIT_TAKE1("DeflateBufferSize", deflate_set_buffer_size, NULL, RSRC_CONF,
 
1107
                  "Set the Deflate Buffer Size"),
 
1108
    AP_INIT_TAKE1("DeflateMemLevel", deflate_set_memlevel, NULL, RSRC_CONF,
 
1109
                  "Set the Deflate Memory Level (1-9)"),
 
1110
    AP_INIT_TAKE1("DeflateCompressionLevel", deflate_set_compressionlevel, NULL, RSRC_CONF,
 
1111
                  "Set the Deflate Compression Level (1-9)"),
 
1112
    {NULL}
 
1113
};
 
1114
 
 
1115
module AP_MODULE_DECLARE_DATA deflate_module = {
 
1116
    STANDARD20_MODULE_STUFF,
 
1117
    NULL,                         /* dir config creater */
 
1118
    NULL,                         /* dir merger --- default is to override */
 
1119
    create_deflate_server_config, /* server config */
 
1120
    NULL,                         /* merge server config */
 
1121
    deflate_filter_cmds,          /* command table */
 
1122
    register_hooks                /* register hooks */
 
1123
};