]> granicus.if.org Git - apache/blob - modules/filters/mod_deflate.c
Check also err_headers_out for an already set Content-Encoding:
[apache] / modules / filters / mod_deflate.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * (zlib functions gz_open and gzwrite)
56  */
57
58 /*
59  * mod_deflate.c: Perform deflate transfer-encoding on the fly
60  *
61  * Written by Ian Holsman (IanH@apache.org)
62  *
63  */
64
65 #include "httpd.h"
66 #include "http_config.h"
67 #include "http_log.h"
68 #include "apr_strings.h"
69 #include "apr_general.h"
70 #include "util_filter.h"
71 #include "apr_buckets.h"
72 #include "http_request.h"
73 #define APR_WANT_STRFUNC
74 #include "apr_want.h"
75
76 #include "zlib.h"
77
78 #ifdef HAVE_ZUTIL_H
79 #include "zutil.h"
80 #else
81 /* As part of the encoding process, we must send what our OS_CODE is
82  * (or so it seems based on what I can tell of how gzip encoding works).
83  *
84  * zutil.h is not always included with zlib distributions (it is a private
85  * header), so this is straight from zlib 1.1.3's zutil.h.
86  */
87 #ifdef OS2
88 #define OS_CODE  0x06
89 #endif
90
91 #ifdef WIN32 /* Window 95 & Windows NT */
92 #define OS_CODE  0x0b
93 #endif
94
95 #if defined(VAXC) || defined(VMS)
96 #define OS_CODE  0x02
97 #endif
98
99 #ifdef AMIGA
100 #define OS_CODE  0x01
101 #endif
102
103 #if defined(ATARI) || defined(atarist)
104 #define OS_CODE  0x05
105 #endif
106
107 #if defined(MACOS) || defined(TARGET_OS_MAC)
108 #define OS_CODE  0x07
109 #endif
110
111 #ifdef __50SERIES /* Prime/PRIMOS */
112 #define OS_CODE  0x0F
113 #endif
114
115 #ifdef TOPS20
116 #define OS_CODE  0x0a
117 #endif
118
119 #ifndef OS_CODE
120 #define OS_CODE  0x03  /* assume Unix */
121 #endif
122 #endif
123
124 static const char deflateFilterName[] = "DEFLATE";
125 module AP_MODULE_DECLARE_DATA deflate_module;
126
127 typedef struct deflate_filter_config_t
128 {
129     int windowSize;
130     int memlevel;
131     int compressionlevel;
132     apr_size_t bufferSize;
133     char *note_ratio_name;
134     char *note_input_name;
135     char *note_output_name;
136 } deflate_filter_config;
137
138 /* windowsize is negative to suppress Zlib header */
139 #define DEFAULT_COMPRESSION Z_DEFAULT_COMPRESSION
140 #define DEFAULT_WINDOWSIZE -15
141 #define DEFAULT_MEMLEVEL 9
142 #define DEFAULT_BUFFERSIZE 8096
143
144 /* Outputs a long in LSB order to the given file
145  * only the bottom 4 bits are required for the deflate file format.
146  */
147 static void putLong(unsigned char *string, unsigned long x)
148 {
149     string[0] = (unsigned char)(x & 0xff);
150     string[1] = (unsigned char)((x & 0xff00) >> 8);
151     string[2] = (unsigned char)((x & 0xff0000) >> 16);
152     string[3] = (unsigned char)((x & 0xff000000) >> 24);
153 }
154
155 /* Inputs a string and returns a long.
156  */
157 static unsigned long getLong(unsigned char *string)
158 {
159     return ((unsigned long)string[0])
160           | (((unsigned long)string[1]) << 8)
161           | (((unsigned long)string[2]) << 16)
162           | (((unsigned long)string[3]) << 24);
163 }
164
165 static void *create_deflate_server_config(apr_pool_t *p, server_rec *s)
166 {
167     deflate_filter_config *c = apr_pcalloc(p, sizeof *c);
168
169     c->memlevel   = DEFAULT_MEMLEVEL;
170     c->windowSize = DEFAULT_WINDOWSIZE;
171     c->bufferSize = DEFAULT_BUFFERSIZE;
172     c->compressionlevel = DEFAULT_COMPRESSION;
173
174     return c;
175 }
176
177 static const char *deflate_set_window_size(cmd_parms *cmd, void *dummy,
178                                            const char *arg)
179 {
180     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
181                                                     &deflate_module);
182     int i;
183
184     i = atoi(arg);
185
186     if (i < 1 || i > 15)
187         return "DeflateWindowSize must be between 1 and 15";
188
189     c->windowSize = i * -1;
190
191     return NULL;
192 }
193
194 static const char *deflate_set_buffer_size(cmd_parms *cmd, void *dummy,
195                                            const char *arg)
196 {
197     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
198                                                     &deflate_module);
199     int n = atoi(arg);
200
201     if (n <= 0) {
202         return "DeflateBufferSize should be positive";
203     }
204
205     c->bufferSize = (apr_size_t)n;
206
207     return NULL;
208 }
209 static const char *deflate_set_note(cmd_parms *cmd, void *dummy,
210                                     const char *arg1, const char *arg2)
211 {
212     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
213                                                     &deflate_module);
214     
215     if (arg2 == NULL) {
216         c->note_ratio_name = apr_pstrdup(cmd->pool, arg1);
217     }
218     else if (!strcasecmp(arg1, "ratio")) {
219         c->note_ratio_name = apr_pstrdup(cmd->pool, arg2);
220     }
221     else if (!strcasecmp(arg1, "input")) {
222         c->note_input_name = apr_pstrdup(cmd->pool, arg2);
223     }
224     else if (!strcasecmp(arg1, "output")) {
225         c->note_output_name = apr_pstrdup(cmd->pool, arg2);
226     }
227     else {
228         return apr_psprintf(cmd->pool, "Unknown note type %s", arg1);
229     }
230
231     return NULL;
232 }
233
234 static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy,
235                                         const char *arg)
236 {
237     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
238                                                     &deflate_module);
239     int i;
240
241     i = atoi(arg);
242
243     if (i < 1 || i > 9)
244         return "DeflateMemLevel must be between 1 and 9";
245
246     c->memlevel = i;
247
248     return NULL;
249 }
250
251 static const char *deflate_set_compressionlevel(cmd_parms *cmd, void *dummy,
252                                         const char *arg)
253 {
254     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
255                                                     &deflate_module);
256     int i;
257
258     i = atoi(arg);
259
260     if (i < 1 || i > 9)
261         return "Compression Level must be between 1 and 9";
262
263     c->compressionlevel = i;
264
265     return NULL;
266 }
267
268 /* magic header */
269 static char deflate_magic[2] = { '\037', '\213' };
270
271 typedef struct deflate_ctx_t
272 {
273     z_stream stream;
274     unsigned char *buffer;
275     unsigned long crc;
276     apr_bucket_brigade *bb, *proc_bb;
277 } deflate_ctx;
278
279 static apr_status_t deflate_out_filter(ap_filter_t *f,
280                                        apr_bucket_brigade *bb)
281 {
282     apr_bucket *e;
283     request_rec *r = f->r;
284     deflate_ctx *ctx = f->ctx;
285     int zRC;
286     deflate_filter_config *c = ap_get_module_config(r->server->module_config,
287                                                     &deflate_module);
288
289     /* If we don't have a context, we need to ensure that it is okay to send
290      * the deflated content.  If we have a context, that means we've done
291      * this before and we liked it.
292      * This could be not so nice if we always fail.  But, if we succeed,
293      * we're in better shape.
294      */
295     if (!ctx) {
296         char *buf, *token;
297         const char *encoding, *accepts;
298
299         /* only work on main request/no subrequests */
300         if (r->main) {
301             ap_remove_output_filter(f);
302             return ap_pass_brigade(f->next, bb);
303         }
304
305         /* some browsers might have problems, so set no-gzip
306          * (with browsermatch) for them
307          */
308         if (apr_table_get(r->subprocess_env, "no-gzip")) {
309             ap_remove_output_filter(f);
310             return ap_pass_brigade(f->next, bb);
311         }
312
313         /* Some browsers might have problems with content types
314          * other than text/html, so set gzip-only-text/html
315          * (with browsermatch) for them
316          */
317         if (r->content_type == NULL
318              || strncmp(r->content_type, "text/html", 9)) {
319             const char *env_value = apr_table_get(r->subprocess_env,
320                                                   "gzip-only-text/html");
321             if ( env_value && (strcmp(env_value,"1") == 0) ) {
322                 ap_remove_output_filter(f);
323                 return ap_pass_brigade(f->next, bb);
324             }            
325         }
326
327         /* Let's see what our current Content-Encoding is.
328          * If gzip is present, don't gzip again.  (We could, but let's not.)
329          */
330         encoding = apr_table_get(r->headers_out, "Content-Encoding");
331         if (encoding) {
332             const char *err_enc;
333
334             err_enc = apr_table_get(r->err_headers_out, "Content-Encoding");
335             if (err_enc) {
336                 encoding = apr_pstrcat(r->pool, encoding, ",", err_enc, NULL);
337             }
338         }
339         else {
340             encoding = apr_table_get(r->err_headers_out, "Content-Encoding");
341         }
342
343         if (encoding) {
344             const char *tmp = encoding;
345
346             token = ap_get_token(r->pool, &tmp, 0);
347             while (token && token[0]) {
348                 if (!strcasecmp(token, "gzip")) {
349                     ap_remove_output_filter(f);
350                     return ap_pass_brigade(f->next, bb);                        
351                 }
352                 /* Otherwise, skip token */
353                 tmp++;
354                 token = ap_get_token(r->pool, &tmp, 0);
355             }
356         }
357
358         /* Even if we don't accept this request based on it not having
359          * the Accept-Encoding, we need to note that we were looking
360          * for this header and downstream proxies should be aware of that.
361          */
362         apr_table_setn(r->headers_out, "Vary", "Accept-Encoding");
363
364         /* if they don't have the line, then they can't play */
365         accepts = apr_table_get(r->headers_in, "Accept-Encoding");
366         if (accepts == NULL) {
367             ap_remove_output_filter(f);
368             return ap_pass_brigade(f->next, bb);
369         }
370
371         token = ap_get_token(r->pool, &accepts, 0);
372         while (token && token[0] && strcasecmp(token, "gzip")) {
373             /* skip token */
374             accepts++;
375             token = ap_get_token(r->pool, &accepts, 0);
376         }
377
378         /* No acceptable token found. */
379         if (token == NULL || token[0] == '\0') {
380             ap_remove_output_filter(f);
381             return ap_pass_brigade(f->next, bb);
382         }
383
384         /* We're cool with filtering this. */
385         ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
386         ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
387         ctx->buffer = apr_palloc(r->pool, c->bufferSize);
388
389         zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED,
390                            c->windowSize, c->memlevel,
391                            Z_DEFAULT_STRATEGY);
392
393         if (zRC != Z_OK) {
394             f->ctx = NULL;
395             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
396                           "unable to init Zlib: "
397                           "deflateInit2 returned %d: URL %s",
398                           zRC, r->uri);
399             return ap_pass_brigade(f->next, bb);
400         }
401
402         /* RFC 1952 Section 2.3 dictates the gzip header:
403          *
404          * +---+---+---+---+---+---+---+---+---+---+
405          * |ID1|ID2|CM |FLG|     MTIME     |XFL|OS |
406          * +---+---+---+---+---+---+---+---+---+---+
407          *
408          * If we wish to populate in MTIME (as hinted in RFC 1952), do:
409          * putLong(date_array, apr_time_now() / APR_USEC_PER_SEC);
410          * where date_array is a char[4] and then print date_array in the
411          * MTIME position.  WARNING: ENDIANNESS ISSUE HERE.
412          */
413         buf = apr_psprintf(r->pool, "%c%c%c%c%c%c%c%c%c%c", deflate_magic[0],
414                            deflate_magic[1], Z_DEFLATED, 0 /* flags */,
415                            0, 0, 0, 0 /* 4 chars for mtime */,
416                            0 /* xflags */, OS_CODE);
417         e = apr_bucket_pool_create(buf, 10, r->pool, f->c->bucket_alloc);
418         APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
419
420         /* If the entire Content-Encoding is "identity", we can replace it. */
421         if (!encoding || !strcasecmp(encoding, "identity")) {
422             apr_table_setn(r->headers_out, "Content-Encoding", "gzip");
423         }
424         else {
425             apr_table_mergen(r->headers_out, "Content-Encoding", "gzip");
426         }
427         apr_table_unset(r->headers_out, "Content-Length");
428
429         /* initialize deflate output buffer */
430         ctx->stream.next_out = ctx->buffer;
431         ctx->stream.avail_out = c->bufferSize;
432     }
433     
434     APR_BRIGADE_FOREACH(e, bb) {
435         const char *data;
436         apr_bucket *b;
437         apr_size_t len;
438
439         int done = 0;
440
441         if (APR_BUCKET_IS_EOS(e)) {
442             char *buf;
443             unsigned int deflate_len;
444
445             ctx->stream.avail_in = 0; /* should be zero already anyway */
446             for (;;) {
447                 deflate_len = c->bufferSize - ctx->stream.avail_out;
448
449                 if (deflate_len != 0) {
450                     b = apr_bucket_heap_create((char *)ctx->buffer,
451                                                deflate_len, NULL,
452                                                f->c->bucket_alloc);
453                     APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
454                     ctx->stream.next_out = ctx->buffer;
455                     ctx->stream.avail_out = c->bufferSize;
456                 }
457
458                 if (done) {
459                     break;
460                 }
461
462                 zRC = deflate(&ctx->stream, Z_FINISH);
463
464                 if (deflate_len == 0 && zRC == Z_BUF_ERROR) {
465                     zRC = Z_OK;
466                 }
467
468                 done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END);
469
470                 if (zRC != Z_OK && zRC != Z_STREAM_END) {
471                     break;
472                 }
473             }
474
475             buf = apr_palloc(r->pool, 8);
476             putLong((unsigned char *)&buf[0], ctx->crc);
477             putLong((unsigned char *)&buf[4], ctx->stream.total_in);
478
479             b = apr_bucket_pool_create(buf, 8, r->pool, f->c->bucket_alloc);
480             APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
481             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
482                           "Zlib: Compressed %ld to %ld : URL %s",
483                           ctx->stream.total_in, ctx->stream.total_out, r->uri);
484
485             /* leave notes for logging */
486             if (c->note_input_name) {
487                 apr_table_setn(r->notes, c->note_input_name,
488                                (ctx->stream.total_in > 0)
489                                 ? apr_off_t_toa(r->pool,
490                                                 ctx->stream.total_in)
491                                 : "-");
492             }
493
494             if (c->note_output_name) {
495                 apr_table_setn(r->notes, c->note_output_name,
496                                (ctx->stream.total_in > 0)
497                                 ? apr_off_t_toa(r->pool,
498                                                 ctx->stream.total_out)
499                                 : "-");
500             }
501
502             if (c->note_ratio_name) {
503                 apr_table_setn(r->notes, c->note_ratio_name,
504                                (ctx->stream.total_in > 0)
505                                 ? apr_itoa(r->pool,
506                                            (int)(ctx->stream.total_out
507                                                  * 100
508                                                  / ctx->stream.total_in))
509                                 : "-");
510             }
511
512             deflateEnd(&ctx->stream);
513
514             /* Remove EOS from the old list, and insert into the new. */
515             APR_BUCKET_REMOVE(e);
516             APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
517
518             /* Okay, we've seen the EOS.
519              * Time to pass it along down the chain.
520              */
521             return ap_pass_brigade(f->next, ctx->bb);
522         }
523
524         if (APR_BUCKET_IS_FLUSH(e)) {
525             apr_bucket *bkt;
526             zRC = deflate(&(ctx->stream), Z_SYNC_FLUSH);
527             if (zRC != Z_OK) {
528                 return APR_EGENERAL;
529             }
530
531             ctx->stream.next_out = ctx->buffer;
532             len = c->bufferSize - ctx->stream.avail_out;
533
534             b = apr_bucket_heap_create((char *)ctx->buffer, len,
535                                        NULL, f->c->bucket_alloc);
536             APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
537             ctx->stream.avail_out = c->bufferSize;
538
539             bkt = apr_bucket_flush_create(f->c->bucket_alloc);
540             APR_BRIGADE_INSERT_TAIL(ctx->bb, bkt);
541             ap_pass_brigade(f->next, ctx->bb);
542             continue;
543         }
544
545         /* read */
546         apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
547
548         /* This crc32 function is from zlib. */
549         ctx->crc = crc32(ctx->crc, (const Bytef *)data, len);
550
551         /* write */
552         ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness,
553                                                       * but we'll just have to
554                                                       * trust zlib */
555         ctx->stream.avail_in = len;
556
557         while (ctx->stream.avail_in != 0) {
558             if (ctx->stream.avail_out == 0) {
559                 ctx->stream.next_out = ctx->buffer;
560                 len = c->bufferSize - ctx->stream.avail_out;
561
562                 b = apr_bucket_heap_create((char *)ctx->buffer, len,
563                                            NULL, f->c->bucket_alloc);
564                 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
565                 ctx->stream.avail_out = c->bufferSize;
566             }
567
568             zRC = deflate(&(ctx->stream), Z_NO_FLUSH);
569
570             if (zRC != Z_OK)
571                 return APR_EGENERAL;
572         }
573     }
574
575     apr_brigade_cleanup(bb);
576     return APR_SUCCESS;
577 }
578
579 /* This is the deflate input filter (inflates).  */
580 static apr_status_t deflate_in_filter(ap_filter_t *f,
581                                       apr_bucket_brigade *bb,
582                                       ap_input_mode_t mode,
583                                       apr_read_type_e block,
584                                       apr_off_t readbytes)
585 {
586     apr_bucket *bkt;
587     request_rec *r = f->r;
588     deflate_ctx *ctx = f->ctx;
589     int zRC;
590     apr_status_t rv;
591     deflate_filter_config *c;
592
593     /* just get out of the way of things we don't want. */
594     if (mode != AP_MODE_READBYTES) {
595         return ap_get_brigade(f->next, bb, mode, block, readbytes);
596     }
597
598     c = ap_get_module_config(r->server->module_config, &deflate_module);
599
600     if (!ctx) {
601         int found = 0;
602         char *token, deflate_hdr[10];
603         const char *encoding;
604         apr_size_t len;
605
606         /* only work on main request/no subrequests */
607         if (r->main) {
608             ap_remove_input_filter(f);
609             return ap_get_brigade(f->next, bb, mode, block, readbytes);
610         }
611
612         /* Let's see what our current Content-Encoding is.
613          * If gzip is present, don't gzip again.  (We could, but let's not.)
614          */
615         encoding = apr_table_get(r->headers_in, "Content-Encoding");
616         if (encoding) {
617             const char *tmp = encoding;
618
619             token = ap_get_token(r->pool, &tmp, 0);
620             while (token && token[0]) {
621                 if (!strcasecmp(token, "gzip")) {
622                     found = 1;
623                     break;
624                 }
625                 /* Otherwise, skip token */
626                 tmp++;
627                 token = ap_get_token(r->pool, &tmp, 0);
628             }
629         }
630
631         if (found == 0) {
632             ap_remove_input_filter(f);
633             return ap_get_brigade(f->next, bb, mode, block, readbytes);
634         }
635
636         f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
637         ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
638         ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
639         ctx->buffer = apr_palloc(r->pool, c->bufferSize);
640
641         rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10);
642         if (rv != APR_SUCCESS) {
643             return rv;
644         }
645
646         len = 10; 
647         rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len); 
648         if (rv != APR_SUCCESS) {
649             return rv;
650         }
651
652         /* We didn't get the magic bytes. */
653         if (len != 10 ||
654             deflate_hdr[0] != deflate_magic[0] ||
655             deflate_hdr[1] != deflate_magic[1]) {
656             return APR_EGENERAL;
657         }
658
659         /* We can't handle flags for now. */
660         if (deflate_hdr[3] != 0) {
661             return APR_EGENERAL;
662         }
663
664         zRC = inflateInit2(&ctx->stream, c->windowSize);
665
666         if (zRC != Z_OK) {
667             f->ctx = NULL;
668             inflateEnd(&ctx->stream);
669             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
670                           "unable to init Zlib: "
671                           "inflateInit2 returned %d: URL %s",
672                           zRC, r->uri);
673             ap_remove_input_filter(f);
674             return ap_get_brigade(f->next, bb, mode, block, readbytes);
675         }
676
677         /* initialize deflate output buffer */
678         ctx->stream.next_out = ctx->buffer;
679         ctx->stream.avail_out = c->bufferSize;
680
681         apr_brigade_cleanup(ctx->bb);
682     }
683
684     if (APR_BRIGADE_EMPTY(ctx->proc_bb)) {
685         rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
686
687         if (rv != APR_SUCCESS) {
688             /* What about APR_EAGAIN errors? */
689             inflateEnd(&ctx->stream);
690             return rv;
691         }
692
693         APR_BRIGADE_FOREACH(bkt, ctx->bb) {
694             const char *data;
695             apr_size_t len;
696
697             /* If we actually see the EOS, that means we screwed up! */
698             if (APR_BUCKET_IS_EOS(bkt)) {
699                 inflateEnd(&ctx->stream);
700                 return APR_EGENERAL;
701             }
702
703             if (APR_BUCKET_IS_FLUSH(bkt)) {
704                 apr_bucket *tmp_heap;
705                 zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
706                 if (zRC != Z_OK) {
707                     inflateEnd(&ctx->stream);
708                     return APR_EGENERAL;
709                 }
710
711                 ctx->stream.next_out = ctx->buffer;
712                 len = c->bufferSize - ctx->stream.avail_out;
713
714                 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
715                 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
716                                                  NULL, f->c->bucket_alloc);
717                 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
718                 ctx->stream.avail_out = c->bufferSize;
719
720                 /* Move everything to the returning brigade. */
721                 APR_BUCKET_REMOVE(bkt);
722                 APR_BRIGADE_CONCAT(bb, ctx->bb);
723                 break;
724             }
725
726             /* read */
727             apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
728
729             /* pass through zlib inflate. */
730             ctx->stream.next_in = (unsigned char *)data;
731             ctx->stream.avail_in = len;
732
733             zRC = Z_OK;
734
735             while (ctx->stream.avail_in != 0) {
736                 if (ctx->stream.avail_out == 0) {
737                     apr_bucket *tmp_heap;
738                     ctx->stream.next_out = ctx->buffer;
739                     len = c->bufferSize - ctx->stream.avail_out;
740
741                     ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
742                     tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
743                                                       NULL, f->c->bucket_alloc);
744                     APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
745                     ctx->stream.avail_out = c->bufferSize;
746                 }
747
748                 zRC = inflate(&ctx->stream, Z_NO_FLUSH);
749
750                 if (zRC == Z_STREAM_END) {
751                     break;
752                 }
753
754                 if (zRC != Z_OK) {
755                     inflateEnd(&ctx->stream);
756                     return APR_EGENERAL;
757                 }
758             }
759             if (zRC == Z_STREAM_END) {
760                 apr_bucket *tmp_heap, *eos;
761
762                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
763                               "Zlib: Inflated %ld to %ld : URL %s",
764                               ctx->stream.total_in, ctx->stream.total_out,
765                               r->uri);
766
767                 len = c->bufferSize - ctx->stream.avail_out;
768
769                 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
770                 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
771                                                   NULL, f->c->bucket_alloc);
772                 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
773                 ctx->stream.avail_out = c->bufferSize;
774
775                 /* Is the remaining 8 bytes already in the avail stream? */
776                 if (ctx->stream.avail_in >= 8) {
777                     unsigned long compCRC, compLen;
778                     compCRC = getLong(ctx->stream.next_in);
779                     if (ctx->crc != compCRC) {
780                         inflateEnd(&ctx->stream);
781                         return APR_EGENERAL;
782                     }
783                     ctx->stream.next_in += 4;
784                     compLen = getLong(ctx->stream.next_in);
785                     if (ctx->stream.total_out != compLen) {
786                         inflateEnd(&ctx->stream);
787                         return APR_EGENERAL;
788                     }
789                 }
790                 else {
791                     /* FIXME: We need to grab the 8 verification bytes
792                      * from the wire! */
793                     inflateEnd(&ctx->stream);
794                     return APR_EGENERAL;
795                 }
796
797                 inflateEnd(&ctx->stream);
798
799                 eos = apr_bucket_eos_create(f->c->bucket_alloc);
800                 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos); 
801                 break;
802             }
803
804         }
805         apr_brigade_cleanup(ctx->bb);
806     }
807
808     /* If we are about to return nothing for a 'blocking' read and we have
809      * some data in our zlib buffer, flush it out so we can return something.
810      */
811     if (block == APR_BLOCK_READ &&
812         APR_BRIGADE_EMPTY(ctx->proc_bb) &&
813         ctx->stream.avail_out < c->bufferSize) {
814         apr_bucket *tmp_heap;
815         apr_size_t len;
816         ctx->stream.next_out = ctx->buffer;
817         len = c->bufferSize - ctx->stream.avail_out;
818
819         ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
820         tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
821                                           NULL, f->c->bucket_alloc);
822         APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
823         ctx->stream.avail_out = c->bufferSize;
824     }
825
826     if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) {
827         apr_bucket_brigade *newbb;
828
829         /* May return APR_INCOMPLETE which is fine by us. */
830         apr_brigade_partition(ctx->proc_bb, readbytes, &bkt);
831
832         newbb = apr_brigade_split(ctx->proc_bb, bkt);
833         APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
834         APR_BRIGADE_CONCAT(ctx->proc_bb, newbb);
835     }
836
837     return APR_SUCCESS;
838 }
839
840 static void register_hooks(apr_pool_t *p)
841 {
842     ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL,
843                               AP_FTYPE_CONTENT_SET);
844     ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL,
845                               AP_FTYPE_CONTENT_SET);
846 }
847
848 static const command_rec deflate_filter_cmds[] = {
849     AP_INIT_TAKE12("DeflateFilterNote", deflate_set_note, NULL, RSRC_CONF,
850                   "Set a note to report on compression ratio"),
851     AP_INIT_TAKE1("DeflateWindowSize", deflate_set_window_size, NULL,
852                   RSRC_CONF, "Set the Deflate window size (1-15)"),
853     AP_INIT_TAKE1("DeflateBufferSize", deflate_set_buffer_size, NULL, RSRC_CONF,
854                   "Set the Deflate Buffer Size"),
855     AP_INIT_TAKE1("DeflateMemLevel", deflate_set_memlevel, NULL, RSRC_CONF,
856                   "Set the Deflate Memory Level (1-9)"),
857     AP_INIT_TAKE1("CompressionLevel", deflate_set_compressionlevel, NULL, RSRC_CONF,
858                   "Set the Deflate Compression Level (1-9)"),
859     {NULL}
860 };
861
862 module AP_MODULE_DECLARE_DATA deflate_module = {
863     STANDARD20_MODULE_STUFF,
864     NULL,                         /* dir config creater */
865     NULL,                         /* dir merger --- default is to override */
866     create_deflate_server_config, /* server config */
867     NULL,                         /* merge server config */
868     deflate_filter_cmds,          /* command table */
869     register_hooks                /* register hooks */
870 };