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