/[Apache-SVN]/httpd/httpd/trunk/modules/filters/mod_deflate.c
ViewVC logotype

Contents of /httpd/httpd/trunk/modules/filters/mod_deflate.c

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:eol-style native

apache@apache.org
ViewVC Help
Powered by ViewVC 1.1.2