]> granicus.if.org Git - apache/blob - modules/filters/mod_deflate.c
apply Apache License, Version 2.0
[apache] / modules / filters / mod_deflate.c
1 /* Copyright 2000-2004 Apache Software Foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /*
17  * mod_deflate.c: Perform deflate transfer-encoding on the fly
18  *
19  * Written by Ian Holsman
20  *
21  */
22
23 /*
24  * Portions of this software are based upon public domain software
25  * (zlib functions gz_open and gzwrite)
26  */
27
28 #include "httpd.h"
29 #include "http_config.h"
30 #include "http_log.h"
31 #include "apr_strings.h"
32 #include "apr_general.h"
33 #include "util_filter.h"
34 #include "apr_buckets.h"
35 #include "http_request.h"
36 #define APR_WANT_STRFUNC
37 #include "apr_want.h"
38
39 #include "zlib.h"
40
41 #ifdef HAVE_ZUTIL_H
42 #include "zutil.h"
43 #else
44 /* As part of the encoding process, we must send what our OS_CODE is
45  * (or so it seems based on what I can tell of how gzip encoding works).
46  *
47  * zutil.h is not always included with zlib distributions (it is a private
48  * header), so this is straight from zlib 1.1.3's zutil.h.
49  */
50 #ifdef OS2
51 #define OS_CODE  0x06
52 #endif
53
54 #ifdef WIN32 /* Window 95 & Windows NT */
55 #define OS_CODE  0x0b
56 #endif
57
58 #if defined(VAXC) || defined(VMS)
59 #define OS_CODE  0x02
60 #endif
61
62 #ifdef AMIGA
63 #define OS_CODE  0x01
64 #endif
65
66 #if defined(ATARI) || defined(atarist)
67 #define OS_CODE  0x05
68 #endif
69
70 #if defined(MACOS) || defined(TARGET_OS_MAC)
71 #define OS_CODE  0x07
72 #endif
73
74 #ifdef __50SERIES /* Prime/PRIMOS */
75 #define OS_CODE  0x0F
76 #endif
77
78 #ifdef TOPS20
79 #define OS_CODE  0x0a
80 #endif
81
82 #ifndef OS_CODE
83 #define OS_CODE  0x03  /* assume Unix */
84 #endif
85 #endif
86
87 static const char deflateFilterName[] = "DEFLATE";
88 module AP_MODULE_DECLARE_DATA deflate_module;
89
90 typedef struct deflate_filter_config_t
91 {
92     int windowSize;
93     int memlevel;
94     int compressionlevel;
95     apr_size_t bufferSize;
96     char *note_ratio_name;
97     char *note_input_name;
98     char *note_output_name;
99 } deflate_filter_config;
100
101 /* windowsize is negative to suppress Zlib header */
102 #define DEFAULT_COMPRESSION Z_DEFAULT_COMPRESSION
103 #define DEFAULT_WINDOWSIZE -15
104 #define DEFAULT_MEMLEVEL 9
105 #define DEFAULT_BUFFERSIZE 8096
106
107 /* Outputs a long in LSB order to the given file
108  * only the bottom 4 bits are required for the deflate file format.
109  */
110 static void putLong(unsigned char *string, unsigned long x)
111 {
112     string[0] = (unsigned char)(x & 0xff);
113     string[1] = (unsigned char)((x & 0xff00) >> 8);
114     string[2] = (unsigned char)((x & 0xff0000) >> 16);
115     string[3] = (unsigned char)((x & 0xff000000) >> 24);
116 }
117
118 /* Inputs a string and returns a long.
119  */
120 static unsigned long getLong(unsigned char *string)
121 {
122     return ((unsigned long)string[0])
123           | (((unsigned long)string[1]) << 8)
124           | (((unsigned long)string[2]) << 16)
125           | (((unsigned long)string[3]) << 24);
126 }
127
128 static void *create_deflate_server_config(apr_pool_t *p, server_rec *s)
129 {
130     deflate_filter_config *c = apr_pcalloc(p, sizeof *c);
131
132     c->memlevel   = DEFAULT_MEMLEVEL;
133     c->windowSize = DEFAULT_WINDOWSIZE;
134     c->bufferSize = DEFAULT_BUFFERSIZE;
135     c->compressionlevel = DEFAULT_COMPRESSION;
136
137     return c;
138 }
139
140 static const char *deflate_set_window_size(cmd_parms *cmd, void *dummy,
141                                            const char *arg)
142 {
143     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
144                                                     &deflate_module);
145     int i;
146
147     i = atoi(arg);
148
149     if (i < 1 || i > 15)
150         return "DeflateWindowSize must be between 1 and 15";
151
152     c->windowSize = i * -1;
153
154     return NULL;
155 }
156
157 static const char *deflate_set_buffer_size(cmd_parms *cmd, void *dummy,
158                                            const char *arg)
159 {
160     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
161                                                     &deflate_module);
162     int n = atoi(arg);
163
164     if (n <= 0) {
165         return "DeflateBufferSize should be positive";
166     }
167
168     c->bufferSize = (apr_size_t)n;
169
170     return NULL;
171 }
172 static const char *deflate_set_note(cmd_parms *cmd, void *dummy,
173                                     const char *arg1, const char *arg2)
174 {
175     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
176                                                     &deflate_module);
177     
178     if (arg2 == NULL) {
179         c->note_ratio_name = apr_pstrdup(cmd->pool, arg1);
180     }
181     else if (!strcasecmp(arg1, "ratio")) {
182         c->note_ratio_name = apr_pstrdup(cmd->pool, arg2);
183     }
184     else if (!strcasecmp(arg1, "input")) {
185         c->note_input_name = apr_pstrdup(cmd->pool, arg2);
186     }
187     else if (!strcasecmp(arg1, "output")) {
188         c->note_output_name = apr_pstrdup(cmd->pool, arg2);
189     }
190     else {
191         return apr_psprintf(cmd->pool, "Unknown note type %s", arg1);
192     }
193
194     return NULL;
195 }
196
197 static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy,
198                                         const char *arg)
199 {
200     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
201                                                     &deflate_module);
202     int i;
203
204     i = atoi(arg);
205
206     if (i < 1 || i > 9)
207         return "DeflateMemLevel must be between 1 and 9";
208
209     c->memlevel = i;
210
211     return NULL;
212 }
213
214 static const char *deflate_set_compressionlevel(cmd_parms *cmd, void *dummy,
215                                         const char *arg)
216 {
217     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
218                                                     &deflate_module);
219     int i;
220
221     i = atoi(arg);
222
223     if (i < 1 || i > 9)
224         return "Compression Level must be between 1 and 9";
225
226     c->compressionlevel = i;
227
228     return NULL;
229 }
230
231 /* magic header */
232 static char deflate_magic[2] = { '\037', '\213' };
233
234 typedef struct deflate_ctx_t
235 {
236     z_stream stream;
237     unsigned char *buffer;
238     unsigned long crc;
239     apr_bucket_brigade *bb, *proc_bb;
240 } deflate_ctx;
241
242 static apr_status_t deflate_out_filter(ap_filter_t *f,
243                                        apr_bucket_brigade *bb)
244 {
245     apr_bucket *e;
246     request_rec *r = f->r;
247     deflate_ctx *ctx = f->ctx;
248     int zRC;
249     deflate_filter_config *c = ap_get_module_config(r->server->module_config,
250                                                     &deflate_module);
251
252     /* If we don't have a context, we need to ensure that it is okay to send
253      * the deflated content.  If we have a context, that means we've done
254      * this before and we liked it.
255      * This could be not so nice if we always fail.  But, if we succeed,
256      * we're in better shape.
257      */
258     if (!ctx) {
259         char *buf, *token;
260         const char *encoding, *accepts;
261
262         /* only work on main request/no subrequests */
263         if (r->main) {
264             ap_remove_output_filter(f);
265             return ap_pass_brigade(f->next, bb);
266         }
267
268         /* some browsers might have problems, so set no-gzip
269          * (with browsermatch) for them
270          */
271         if (apr_table_get(r->subprocess_env, "no-gzip")) {
272             ap_remove_output_filter(f);
273             return ap_pass_brigade(f->next, bb);
274         }
275
276         /* Some browsers might have problems with content types
277          * other than text/html, so set gzip-only-text/html
278          * (with browsermatch) for them
279          */
280         if (r->content_type == NULL
281              || strncmp(r->content_type, "text/html", 9)) {
282             const char *env_value = apr_table_get(r->subprocess_env,
283                                                   "gzip-only-text/html");
284             if ( env_value && (strcmp(env_value,"1") == 0) ) {
285                 ap_remove_output_filter(f);
286                 return ap_pass_brigade(f->next, bb);
287             }            
288         }
289
290         /* Let's see what our current Content-Encoding is.
291          * If it's already encoded, don't compress again.
292          * (We could, but let's not.)
293          */
294         encoding = apr_table_get(r->headers_out, "Content-Encoding");
295         if (encoding) {
296             const char *err_enc;
297
298             err_enc = apr_table_get(r->err_headers_out, "Content-Encoding");
299             if (err_enc) {
300                 encoding = apr_pstrcat(r->pool, encoding, ",", err_enc, NULL);
301             }
302         }
303         else {
304             encoding = apr_table_get(r->err_headers_out, "Content-Encoding");
305         }
306
307         if (r->content_encoding) {
308             encoding = encoding ? apr_pstrcat(r->pool, encoding, ",",
309                                               r->content_encoding, NULL)
310                                 : r->content_encoding;
311         }
312
313         if (encoding) {
314             const char *tmp = encoding;
315
316             token = ap_get_token(r->pool, &tmp, 0);
317             while (token && *token) {
318                 /* stolen from mod_negotiation: */
319                 if (strcmp(token, "identity") && strcmp(token, "7bit") &&
320                     strcmp(token, "8bit") && strcmp(token, "binary")) {
321
322                     ap_remove_output_filter(f);
323                     return ap_pass_brigade(f->next, bb);                        
324                 }
325
326                 /* Otherwise, skip token */
327                 if (*tmp) {
328                     ++tmp;
329                 }
330                 token = (*tmp) ? ap_get_token(r->pool, &tmp, 0) : NULL;
331             }
332         }
333
334         /* Even if we don't accept this request based on it not having
335          * the Accept-Encoding, we need to note that we were looking
336          * for this header and downstream proxies should be aware of that.
337          */
338         apr_table_setn(r->headers_out, "Vary", "Accept-Encoding");
339
340         /* if they don't have the line, then they can't play */
341         accepts = apr_table_get(r->headers_in, "Accept-Encoding");
342         if (accepts == NULL) {
343             ap_remove_output_filter(f);
344             return ap_pass_brigade(f->next, bb);
345         }
346
347         token = ap_get_token(r->pool, &accepts, 0);
348         while (token && token[0] && strcasecmp(token, "gzip")) {
349             /* skip parameters, XXX: ;q=foo evaluation? */
350             while (*accepts == ';') { 
351                 ++accepts;
352                 token = ap_get_token(r->pool, &accepts, 1);
353             }
354
355             /* retrieve next token */
356             if (*accepts == ',') {
357                 ++accepts;
358             }
359             token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL;
360         }
361
362         /* No acceptable token found. */
363         if (token == NULL || token[0] == '\0') {
364             ap_remove_output_filter(f);
365             return ap_pass_brigade(f->next, bb);
366         }
367
368         /* We're cool with filtering this. */
369         ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
370         ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
371         ctx->buffer = apr_palloc(r->pool, c->bufferSize);
372
373         zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED,
374                            c->windowSize, c->memlevel,
375                            Z_DEFAULT_STRATEGY);
376
377         if (zRC != Z_OK) {
378             f->ctx = NULL;
379             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
380                           "unable to init Zlib: "
381                           "deflateInit2 returned %d: URL %s",
382                           zRC, r->uri);
383             return ap_pass_brigade(f->next, bb);
384         }
385
386         /* RFC 1952 Section 2.3 dictates the gzip header:
387          *
388          * +---+---+---+---+---+---+---+---+---+---+
389          * |ID1|ID2|CM |FLG|     MTIME     |XFL|OS |
390          * +---+---+---+---+---+---+---+---+---+---+
391          *
392          * If we wish to populate in MTIME (as hinted in RFC 1952), do:
393          * putLong(date_array, apr_time_now() / APR_USEC_PER_SEC);
394          * where date_array is a char[4] and then print date_array in the
395          * MTIME position.  WARNING: ENDIANNESS ISSUE HERE.
396          */
397         buf = apr_psprintf(r->pool, "%c%c%c%c%c%c%c%c%c%c", deflate_magic[0],
398                            deflate_magic[1], Z_DEFLATED, 0 /* flags */,
399                            0, 0, 0, 0 /* 4 chars for mtime */,
400                            0 /* xflags */, OS_CODE);
401         e = apr_bucket_pool_create(buf, 10, r->pool, f->c->bucket_alloc);
402         APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
403
404         /* If the entire Content-Encoding is "identity", we can replace it. */
405         if (!encoding || !strcasecmp(encoding, "identity")) {
406             apr_table_setn(r->headers_out, "Content-Encoding", "gzip");
407         }
408         else {
409             apr_table_mergen(r->headers_out, "Content-Encoding", "gzip");
410         }
411         apr_table_unset(r->headers_out, "Content-Length");
412
413         /* initialize deflate output buffer */
414         ctx->stream.next_out = ctx->buffer;
415         ctx->stream.avail_out = c->bufferSize;
416     }
417     
418     for (e = APR_BRIGADE_FIRST(bb);
419          e != APR_BRIGADE_SENTINEL(bb);
420          e = APR_BUCKET_NEXT(e))
421     {
422         const char *data;
423         apr_bucket *b;
424         apr_size_t len;
425
426         int done = 0;
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             if (ctx->stream.avail_in > 0) {
515                 zRC = deflate(&(ctx->stream), Z_SYNC_FLUSH);
516                 if (zRC != Z_OK) {
517                     return APR_EGENERAL;
518                 }
519             }
520
521             ctx->stream.next_out = ctx->buffer;
522             len = c->bufferSize - ctx->stream.avail_out;
523
524             b = apr_bucket_heap_create((char *)ctx->buffer, len,
525                                        NULL, f->c->bucket_alloc);
526             APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
527             ctx->stream.avail_out = c->bufferSize;
528
529             bkt = apr_bucket_flush_create(f->c->bucket_alloc);
530             APR_BRIGADE_INSERT_TAIL(ctx->bb, bkt);
531             rv = ap_pass_brigade(f->next, ctx->bb);
532             if (rv != APR_SUCCESS) {
533                 return rv;
534             }
535             continue;
536         }
537
538         /* read */
539         apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
540
541         /* This crc32 function is from zlib. */
542         ctx->crc = crc32(ctx->crc, (const Bytef *)data, len);
543
544         /* write */
545         ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness,
546                                                       * but we'll just have to
547                                                       * trust zlib */
548         ctx->stream.avail_in = len;
549
550         while (ctx->stream.avail_in != 0) {
551             if (ctx->stream.avail_out == 0) {
552                 apr_status_t rv;
553
554                 ctx->stream.next_out = ctx->buffer;
555                 len = c->bufferSize - ctx->stream.avail_out;
556
557                 b = apr_bucket_heap_create((char *)ctx->buffer, len,
558                                            NULL, f->c->bucket_alloc);
559                 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
560                 ctx->stream.avail_out = c->bufferSize;
561                 /* Send what we have right now to the next filter. */
562                 rv = ap_pass_brigade(f->next, ctx->bb);
563                 if (rv != APR_SUCCESS) {
564                     return rv;
565                 }
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         for (bkt = APR_BRIGADE_FIRST(ctx->bb);
694              bkt != APR_BRIGADE_SENTINEL(ctx->bb);
695              bkt = APR_BUCKET_NEXT(bkt))
696         {
697             const char *data;
698             apr_size_t len;
699
700             /* If we actually see the EOS, that means we screwed up! */
701             if (APR_BUCKET_IS_EOS(bkt)) {
702                 inflateEnd(&ctx->stream);
703                 return APR_EGENERAL;
704             }
705
706             if (APR_BUCKET_IS_FLUSH(bkt)) {
707                 apr_bucket *tmp_heap;
708                 zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
709                 if (zRC != Z_OK) {
710                     inflateEnd(&ctx->stream);
711                     return APR_EGENERAL;
712                 }
713
714                 ctx->stream.next_out = ctx->buffer;
715                 len = c->bufferSize - ctx->stream.avail_out;
716
717                 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
718                 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
719                                                  NULL, f->c->bucket_alloc);
720                 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
721                 ctx->stream.avail_out = c->bufferSize;
722
723                 /* Move everything to the returning brigade. */
724                 APR_BUCKET_REMOVE(bkt);
725                 APR_BRIGADE_CONCAT(bb, ctx->bb);
726                 break;
727             }
728
729             /* read */
730             apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
731
732             /* pass through zlib inflate. */
733             ctx->stream.next_in = (unsigned char *)data;
734             ctx->stream.avail_in = len;
735
736             zRC = Z_OK;
737
738             while (ctx->stream.avail_in != 0) {
739                 if (ctx->stream.avail_out == 0) {
740                     apr_bucket *tmp_heap;
741                     ctx->stream.next_out = ctx->buffer;
742                     len = c->bufferSize - ctx->stream.avail_out;
743
744                     ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
745                     tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
746                                                       NULL, f->c->bucket_alloc);
747                     APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
748                     ctx->stream.avail_out = c->bufferSize;
749                 }
750
751                 zRC = inflate(&ctx->stream, Z_NO_FLUSH);
752
753                 if (zRC == Z_STREAM_END) {
754                     break;
755                 }
756
757                 if (zRC != Z_OK) {
758                     inflateEnd(&ctx->stream);
759                     return APR_EGENERAL;
760                 }
761             }
762             if (zRC == Z_STREAM_END) {
763                 apr_bucket *tmp_heap, *eos;
764
765                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
766                               "Zlib: Inflated %ld to %ld : URL %s",
767                               ctx->stream.total_in, ctx->stream.total_out,
768                               r->uri);
769
770                 len = c->bufferSize - ctx->stream.avail_out;
771
772                 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
773                 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
774                                                   NULL, f->c->bucket_alloc);
775                 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
776                 ctx->stream.avail_out = c->bufferSize;
777
778                 /* Is the remaining 8 bytes already in the avail stream? */
779                 if (ctx->stream.avail_in >= 8) {
780                     unsigned long compCRC, compLen;
781                     compCRC = getLong(ctx->stream.next_in);
782                     if (ctx->crc != compCRC) {
783                         inflateEnd(&ctx->stream);
784                         return APR_EGENERAL;
785                     }
786                     ctx->stream.next_in += 4;
787                     compLen = getLong(ctx->stream.next_in);
788                     if (ctx->stream.total_out != compLen) {
789                         inflateEnd(&ctx->stream);
790                         return APR_EGENERAL;
791                     }
792                 }
793                 else {
794                     /* FIXME: We need to grab the 8 verification bytes
795                      * from the wire! */
796                     inflateEnd(&ctx->stream);
797                     return APR_EGENERAL;
798                 }
799
800                 inflateEnd(&ctx->stream);
801
802                 eos = apr_bucket_eos_create(f->c->bucket_alloc);
803                 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos); 
804                 break;
805             }
806
807         }
808         apr_brigade_cleanup(ctx->bb);
809     }
810
811     /* If we are about to return nothing for a 'blocking' read and we have
812      * some data in our zlib buffer, flush it out so we can return something.
813      */
814     if (block == APR_BLOCK_READ &&
815         APR_BRIGADE_EMPTY(ctx->proc_bb) &&
816         ctx->stream.avail_out < c->bufferSize) {
817         apr_bucket *tmp_heap;
818         apr_size_t len;
819         ctx->stream.next_out = ctx->buffer;
820         len = c->bufferSize - ctx->stream.avail_out;
821
822         ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
823         tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
824                                           NULL, f->c->bucket_alloc);
825         APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
826         ctx->stream.avail_out = c->bufferSize;
827     }
828
829     if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) {
830         apr_bucket_brigade *newbb;
831
832         /* May return APR_INCOMPLETE which is fine by us. */
833         apr_brigade_partition(ctx->proc_bb, readbytes, &bkt);
834
835         newbb = apr_brigade_split(ctx->proc_bb, bkt);
836         APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
837         APR_BRIGADE_CONCAT(ctx->proc_bb, newbb);
838     }
839
840     return APR_SUCCESS;
841 }
842
843 static void register_hooks(apr_pool_t *p)
844 {
845     ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL,
846                               AP_FTYPE_CONTENT_SET);
847     ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL,
848                               AP_FTYPE_CONTENT_SET);
849 }
850
851 static const command_rec deflate_filter_cmds[] = {
852     AP_INIT_TAKE12("DeflateFilterNote", deflate_set_note, NULL, RSRC_CONF,
853                   "Set a note to report on compression ratio"),
854     AP_INIT_TAKE1("DeflateWindowSize", deflate_set_window_size, NULL,
855                   RSRC_CONF, "Set the Deflate window size (1-15)"),
856     AP_INIT_TAKE1("DeflateBufferSize", deflate_set_buffer_size, NULL, RSRC_CONF,
857                   "Set the Deflate Buffer Size"),
858     AP_INIT_TAKE1("DeflateMemLevel", deflate_set_memlevel, NULL, RSRC_CONF,
859                   "Set the Deflate Memory Level (1-9)"),
860     AP_INIT_TAKE1("DeflateCompressionLevel", deflate_set_compressionlevel, NULL, RSRC_CONF,
861                   "Set the Deflate Compression Level (1-9)"),
862     {NULL}
863 };
864
865 module AP_MODULE_DECLARE_DATA deflate_module = {
866     STANDARD20_MODULE_STUFF,
867     NULL,                         /* dir config creater */
868     NULL,                         /* dir merger --- default is to override */
869     create_deflate_server_config, /* server config */
870     NULL,                         /* merge server config */
871     deflate_filter_cmds,          /* command table */
872     register_hooks                /* register hooks */
873 };