Main Page | Modules | Namespace List | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages | Examples

mod_deflate.c

Go to the documentation of this file.
00001 /* Copyright 2002-2005 The Apache Software Foundation or its licensors, as
00002  * applicable.
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 /*
00018  * Portions of this software are based upon public domain software
00019  * (zlib functions gz_open and gzwrite)
00020  */
00021 
00022 /*
00023  * mod_deflate.c: Perform deflate transfer-encoding on the fly
00024  *
00025  * Written by Ian Holsman
00026  *
00027  */
00028 
00029 #include "httpd.h"
00030 #include "http_config.h"
00031 #include "http_log.h"
00032 #include "apr_strings.h"
00033 #include "apr_general.h"
00034 #include "util_filter.h"
00035 #include "apr_buckets.h"
00036 #include "http_request.h"
00037 #define APR_WANT_STRFUNC
00038 #include "apr_want.h"
00039 
00040 #include "zlib.h"
00041 
00042 #ifdef HAVE_ZUTIL_H
00043 #include "zutil.h"
00044 #else
00045 /* As part of the encoding process, we must send what our OS_CODE is
00046  * (or so it seems based on what I can tell of how gzip encoding works).
00047  *
00048  * zutil.h is not always included with zlib distributions (it is a private
00049  * header), so this is straight from zlib 1.1.3's zutil.h.
00050  */
00051 #ifdef OS2
00052 #define OS_CODE  0x06
00053 #endif
00054 
00055 #ifdef WIN32 /* Window 95 & Windows NT */
00056 #define OS_CODE  0x0b
00057 #endif
00058 
00059 #if defined(VAXC) || defined(VMS)
00060 #define OS_CODE  0x02
00061 #endif
00062 
00063 #ifdef AMIGA
00064 #define OS_CODE  0x01
00065 #endif
00066 
00067 #if defined(ATARI) || defined(atarist)
00068 #define OS_CODE  0x05
00069 #endif
00070 
00071 #if defined(MACOS) || defined(TARGET_OS_MAC)
00072 #define OS_CODE  0x07
00073 #endif
00074 
00075 #ifdef __50SERIES /* Prime/PRIMOS */
00076 #define OS_CODE  0x0F
00077 #endif
00078 
00079 #ifdef TOPS20
00080 #define OS_CODE  0x0a
00081 #endif
00082 
00083 #ifndef OS_CODE
00084 #define OS_CODE  0x03  /* assume Unix */
00085 #endif
00086 #endif
00087 
00088 static const char deflateFilterName[] = "DEFLATE";
00089 module AP_MODULE_DECLARE_DATA deflate_module;
00090 
00091 typedef struct deflate_filter_config_t
00092 {
00093     int windowSize;
00094     int memlevel;
00095     int compressionlevel;
00096     apr_size_t bufferSize;
00097     char *note_ratio_name;
00098     char *note_input_name;
00099     char *note_output_name;
00100 } deflate_filter_config;
00101 
00102 /* windowsize is negative to suppress Zlib header */
00103 #define DEFAULT_COMPRESSION Z_DEFAULT_COMPRESSION
00104 #define DEFAULT_WINDOWSIZE -15
00105 #define DEFAULT_MEMLEVEL 9
00106 #define DEFAULT_BUFFERSIZE 8096
00107 
00108 /* Outputs a long in LSB order to the given file
00109  * only the bottom 4 bits are required for the deflate file format.
00110  */
00111 static void putLong(unsigned char *string, unsigned long x)
00112 {
00113     string[0] = (unsigned char)(x & 0xff);
00114     string[1] = (unsigned char)((x & 0xff00) >> 8);
00115     string[2] = (unsigned char)((x & 0xff0000) >> 16);
00116     string[3] = (unsigned char)((x & 0xff000000) >> 24);
00117 }
00118 
00119 /* Inputs a string and returns a long.
00120  */
00121 static unsigned long getLong(unsigned char *string)
00122 {
00123     return ((unsigned long)string[0])
00124           | (((unsigned long)string[1]) << 8)
00125           | (((unsigned long)string[2]) << 16)
00126           | (((unsigned long)string[3]) << 24);
00127 }
00128 
00129 static void *create_deflate_server_config(apr_pool_t *p, server_rec *s)
00130 {
00131     deflate_filter_config *c = apr_pcalloc(p, sizeof *c);
00132 
00133     c->memlevel   = DEFAULT_MEMLEVEL;
00134     c->windowSize = DEFAULT_WINDOWSIZE;
00135     c->bufferSize = DEFAULT_BUFFERSIZE;
00136     c->compressionlevel = DEFAULT_COMPRESSION;
00137 
00138     return c;
00139 }
00140 
00141 static const char *deflate_set_window_size(cmd_parms *cmd, void *dummy,
00142                                            const char *arg)
00143 {
00144     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
00145                                                     &deflate_module);
00146     int i;
00147 
00148     i = atoi(arg);
00149 
00150     if (i < 1 || i > 15)
00151         return "DeflateWindowSize must be between 1 and 15";
00152 
00153     c->windowSize = i * -1;
00154 
00155     return NULL;
00156 }
00157 
00158 static const char *deflate_set_buffer_size(cmd_parms *cmd, void *dummy,
00159                                            const char *arg)
00160 {
00161     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
00162                                                     &deflate_module);
00163     int n = atoi(arg);
00164 
00165     if (n <= 0) {
00166         return "DeflateBufferSize should be positive";
00167     }
00168 
00169     c->bufferSize = (apr_size_t)n;
00170 
00171     return NULL;
00172 }
00173 static const char *deflate_set_note(cmd_parms *cmd, void *dummy,
00174                                     const char *arg1, const char *arg2)
00175 {
00176     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
00177                                                     &deflate_module);
00178     
00179     if (arg2 == NULL) {
00180         c->note_ratio_name = apr_pstrdup(cmd->pool, arg1);
00181     }
00182     else if (!strcasecmp(arg1, "ratio")) {
00183         c->note_ratio_name = apr_pstrdup(cmd->pool, arg2);
00184     }
00185     else if (!strcasecmp(arg1, "input")) {
00186         c->note_input_name = apr_pstrdup(cmd->pool, arg2);
00187     }
00188     else if (!strcasecmp(arg1, "output")) {
00189         c->note_output_name = apr_pstrdup(cmd->pool, arg2);
00190     }
00191     else {
00192         return apr_psprintf(cmd->pool, "Unknown note type %s", arg1);
00193     }
00194 
00195     return NULL;
00196 }
00197 
00198 static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy,
00199                                         const char *arg)
00200 {
00201     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
00202                                                     &deflate_module);
00203     int i;
00204 
00205     i = atoi(arg);
00206 
00207     if (i < 1 || i > 9)
00208         return "DeflateMemLevel must be between 1 and 9";
00209 
00210     c->memlevel = i;
00211 
00212     return NULL;
00213 }
00214 
00215 static const char *deflate_set_compressionlevel(cmd_parms *cmd, void *dummy,
00216                                         const char *arg)
00217 {
00218     deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
00219                                                     &deflate_module);
00220     int i;
00221 
00222     i = atoi(arg);
00223 
00224     if (i < 1 || i > 9)
00225         return "Compression Level must be between 1 and 9";
00226 
00227     c->compressionlevel = i;
00228 
00229     return NULL;
00230 }
00231 
00232 /* magic header */
00233 static char deflate_magic[2] = { '\037', '\213' };
00234 
00235 typedef struct deflate_ctx_t
00236 {
00237     z_stream stream;
00238     unsigned char *buffer;
00239     unsigned long crc;
00240     apr_bucket_brigade *bb, *proc_bb;
00241 } deflate_ctx;
00242 
00243 static apr_status_t deflate_out_filter(ap_filter_t *f,
00244                                        apr_bucket_brigade *bb)
00245 {
00246     apr_bucket *e;
00247     request_rec *r = f->r;
00248     deflate_ctx *ctx = f->ctx;
00249     int zRC;
00250     deflate_filter_config *c = ap_get_module_config(r->server->module_config,
00251                                                     &deflate_module);
00252 
00253     /* If we don't have a context, we need to ensure that it is okay to send
00254      * the deflated content.  If we have a context, that means we've done
00255      * this before and we liked it.
00256      * This could be not so nice if we always fail.  But, if we succeed,
00257      * we're in better shape.
00258      */
00259     if (!ctx) {
00260         char *buf, *token;
00261         const char *encoding, *accepts;
00262 
00263         /* only work on main request/no subrequests */
00264         if (r->main) {
00265             ap_remove_output_filter(f);
00266             return ap_pass_brigade(f->next, bb);
00267         }
00268 
00269         /* some browsers might have problems, so set no-gzip
00270          * (with browsermatch) for them
00271          */
00272         if (apr_table_get(r->subprocess_env, "no-gzip")) {
00273             ap_remove_output_filter(f);
00274             return ap_pass_brigade(f->next, bb);
00275         }
00276 
00277         /* Some browsers might have problems with content types
00278          * other than text/html, so set gzip-only-text/html
00279          * (with browsermatch) for them
00280          */
00281         if (r->content_type == NULL
00282              || strncmp(r->content_type, "text/html", 9)) {
00283             const char *env_value = apr_table_get(r->subprocess_env,
00284                                                   "gzip-only-text/html");
00285             if ( env_value && (strcmp(env_value,"1") == 0) ) {
00286                 ap_remove_output_filter(f);
00287                 return ap_pass_brigade(f->next, bb);
00288             }            
00289         }
00290 
00291         /* Let's see what our current Content-Encoding is.
00292          * If it's already encoded, don't compress again.
00293          * (We could, but let's not.)
00294          */
00295         encoding = apr_table_get(r->headers_out, "Content-Encoding");
00296         if (encoding) {
00297             const char *err_enc;
00298 
00299             err_enc = apr_table_get(r->err_headers_out, "Content-Encoding");
00300             if (err_enc) {
00301                 encoding = apr_pstrcat(r->pool, encoding, ",", err_enc, NULL);
00302             }
00303         }
00304         else {
00305             encoding = apr_table_get(r->err_headers_out, "Content-Encoding");
00306         }
00307 
00308         if (r->content_encoding) {
00309             encoding = encoding ? apr_pstrcat(r->pool, encoding, ",",
00310                                               r->content_encoding, NULL)
00311                                 : r->content_encoding;
00312         }
00313 
00314         if (encoding) {
00315             const char *tmp = encoding;
00316 
00317             token = ap_get_token(r->pool, &tmp, 0);
00318             while (token && *token) {
00319                 /* stolen from mod_negotiation: */
00320                 if (strcmp(token, "identity") && strcmp(token, "7bit") &&
00321                     strcmp(token, "8bit") && strcmp(token, "binary")) {
00322 
00323                     ap_remove_output_filter(f);
00324                     return ap_pass_brigade(f->next, bb);                        
00325                 }
00326 
00327                 /* Otherwise, skip token */
00328                 if (*tmp) {
00329                     ++tmp;
00330                 }
00331                 token = (*tmp) ? ap_get_token(r->pool, &tmp, 0) : NULL;
00332             }
00333         }
00334 
00335         /* Even if we don't accept this request based on it not having
00336          * the Accept-Encoding, we need to note that we were looking
00337          * for this header and downstream proxies should be aware of that.
00338          */
00339         apr_table_setn(r->headers_out, "Vary", "Accept-Encoding");
00340 
00341         /* if they don't have the line, then they can't play */
00342         accepts = apr_table_get(r->headers_in, "Accept-Encoding");
00343         if (accepts == NULL) {
00344             ap_remove_output_filter(f);
00345             return ap_pass_brigade(f->next, bb);
00346         }
00347 
00348         token = ap_get_token(r->pool, &accepts, 0);
00349         while (token && token[0] && strcasecmp(token, "gzip")) {
00350             /* skip parameters, XXX: ;q=foo evaluation? */
00351             while (*accepts == ';') { 
00352                 ++accepts;
00353                 token = ap_get_token(r->pool, &accepts, 1);
00354             }
00355 
00356             /* retrieve next token */
00357             if (*accepts == ',') {
00358                 ++accepts;
00359             }
00360             token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL;
00361         }
00362 
00363         /* No acceptable token found. */
00364         if (token == NULL || token[0] == '\0') {
00365             ap_remove_output_filter(f);
00366             return ap_pass_brigade(f->next, bb);
00367         }
00368 
00369         /* We're cool with filtering this. */
00370         ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
00371         ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
00372         ctx->buffer = apr_palloc(r->pool, c->bufferSize);
00373 
00374         zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED,
00375                            c->windowSize, c->memlevel,
00376                            Z_DEFAULT_STRATEGY);
00377 
00378         if (zRC != Z_OK) {
00379             f->ctx = NULL;
00380             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
00381                           "unable to init Zlib: "
00382                           "deflateInit2 returned %d: URL %s",
00383                           zRC, r->uri);
00384             return ap_pass_brigade(f->next, bb);
00385         }
00386 
00387         /* RFC 1952 Section 2.3 dictates the gzip header:
00388          *
00389          * +---+---+---+---+---+---+---+---+---+---+
00390          * |ID1|ID2|CM |FLG|     MTIME     |XFL|OS |
00391          * +---+---+---+---+---+---+---+---+---+---+
00392          *
00393          * If we wish to populate in MTIME (as hinted in RFC 1952), do:
00394          * putLong(date_array, apr_time_now() / APR_USEC_PER_SEC);
00395          * where date_array is a char[4] and then print date_array in the
00396          * MTIME position.  WARNING: ENDIANNESS ISSUE HERE.
00397          */
00398         buf = apr_psprintf(r->pool, "%c%c%c%c%c%c%c%c%c%c", deflate_magic[0],
00399                            deflate_magic[1], Z_DEFLATED, 0 /* flags */,
00400                            0, 0, 0, 0 /* 4 chars for mtime */,
00401                            0 /* xflags */, OS_CODE);
00402         e = apr_bucket_pool_create(buf, 10, r->pool, f->c->bucket_alloc);
00403         APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
00404 
00405         /* If the entire Content-Encoding is "identity", we can replace it. */
00406         if (!encoding || !strcasecmp(encoding, "identity")) {
00407             apr_table_setn(r->headers_out, "Content-Encoding", "gzip");
00408         }
00409         else {
00410             apr_table_mergen(r->headers_out, "Content-Encoding", "gzip");
00411         }
00412         apr_table_unset(r->headers_out, "Content-Length");
00413 
00414         /* initialize deflate output buffer */
00415         ctx->stream.next_out = ctx->buffer;
00416         ctx->stream.avail_out = c->bufferSize;
00417     }
00418     
00419     while (!APR_BRIGADE_EMPTY(bb))
00420     {
00421         const char *data;
00422         apr_bucket *b;
00423         apr_size_t len;
00424         int done = 0;
00425 
00426         e = APR_BRIGADE_FIRST(bb);
00427 
00428         if (APR_BUCKET_IS_EOS(e)) {
00429             char *buf;
00430             unsigned int deflate_len;
00431 
00432             ctx->stream.avail_in = 0; /* should be zero already anyway */
00433             for (;;) {
00434                 deflate_len = c->bufferSize - ctx->stream.avail_out;
00435 
00436                 if (deflate_len != 0) {
00437                     b = apr_bucket_heap_create((char *)ctx->buffer,
00438                                                deflate_len, NULL,
00439                                                f->c->bucket_alloc);
00440                     APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
00441                     ctx->stream.next_out = ctx->buffer;
00442                     ctx->stream.avail_out = c->bufferSize;
00443                 }
00444 
00445                 if (done) {
00446                     break;
00447                 }
00448 
00449                 zRC = deflate(&ctx->stream, Z_FINISH);
00450 
00451                 if (deflate_len == 0 && zRC == Z_BUF_ERROR) {
00452                     zRC = Z_OK;
00453                 }
00454 
00455                 done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END);
00456 
00457                 if (zRC != Z_OK && zRC != Z_STREAM_END) {
00458                     break;
00459                 }
00460             }
00461 
00462             buf = apr_palloc(r->pool, 8);
00463             putLong((unsigned char *)&buf[0], ctx->crc);
00464             putLong((unsigned char *)&buf[4], ctx->stream.total_in);
00465 
00466             b = apr_bucket_pool_create(buf, 8, r->pool, f->c->bucket_alloc);
00467             APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
00468             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
00469                           "Zlib: Compressed %ld to %ld : URL %s",
00470                           ctx->stream.total_in, ctx->stream.total_out, r->uri);
00471 
00472             /* leave notes for logging */
00473             if (c->note_input_name) {
00474                 apr_table_setn(r->notes, c->note_input_name,
00475                                (ctx->stream.total_in > 0)
00476                                 ? apr_off_t_toa(r->pool,
00477                                                 ctx->stream.total_in)
00478                                 : "-");
00479             }
00480 
00481             if (c->note_output_name) {
00482                 apr_table_setn(r->notes, c->note_output_name,
00483                                (ctx->stream.total_in > 0)
00484                                 ? apr_off_t_toa(r->pool,
00485                                                 ctx->stream.total_out)
00486                                 : "-");
00487             }
00488 
00489             if (c->note_ratio_name) {
00490                 apr_table_setn(r->notes, c->note_ratio_name,
00491                                (ctx->stream.total_in > 0)
00492                                 ? apr_itoa(r->pool,
00493                                            (int)(ctx->stream.total_out
00494                                                  * 100
00495                                                  / ctx->stream.total_in))
00496                                 : "-");
00497             }
00498 
00499             deflateEnd(&ctx->stream);
00500 
00501             /* Remove EOS from the old list, and insert into the new. */
00502             APR_BUCKET_REMOVE(e);
00503             APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
00504 
00505             /* Okay, we've seen the EOS.
00506              * Time to pass it along down the chain.
00507              */
00508             return ap_pass_brigade(f->next, ctx->bb);
00509         }
00510 
00511         if (APR_BUCKET_IS_FLUSH(e)) {
00512             apr_bucket *bkt;
00513             apr_status_t rv;
00514 
00515             apr_bucket_delete(e);
00516 
00517             if (ctx->stream.avail_in > 0) {
00518                 zRC = deflate(&(ctx->stream), Z_SYNC_FLUSH);
00519                 if (zRC != Z_OK) {
00520                     return APR_EGENERAL;
00521                 }
00522             }
00523 
00524             ctx->stream.next_out = ctx->buffer;
00525             len = c->bufferSize - ctx->stream.avail_out;
00526 
00527             b = apr_bucket_heap_create((char *)ctx->buffer, len,
00528                                        NULL, f->c->bucket_alloc);
00529             APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
00530             ctx->stream.avail_out = c->bufferSize;
00531 
00532             bkt = apr_bucket_flush_create(f->c->bucket_alloc);
00533             APR_BRIGADE_INSERT_TAIL(ctx->bb, bkt);
00534             rv = ap_pass_brigade(f->next, ctx->bb);
00535             if (rv != APR_SUCCESS) {
00536                 return rv;
00537             }
00538             continue;
00539         }
00540 
00541         /* read */
00542         apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
00543 
00544         /* This crc32 function is from zlib. */
00545         ctx->crc = crc32(ctx->crc, (const Bytef *)data, len);
00546 
00547         /* write */
00548         ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness,
00549                                                       * but we'll just have to
00550                                                       * trust zlib */
00551         ctx->stream.avail_in = len;
00552 
00553         while (ctx->stream.avail_in != 0) {
00554             if (ctx->stream.avail_out == 0) {
00555                 apr_status_t rv;
00556 
00557                 ctx->stream.next_out = ctx->buffer;
00558                 len = c->bufferSize - ctx->stream.avail_out;
00559 
00560                 b = apr_bucket_heap_create((char *)ctx->buffer, len,
00561                                            NULL, f->c->bucket_alloc);
00562                 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
00563                 ctx->stream.avail_out = c->bufferSize;
00564                 /* Send what we have right now to the next filter. */
00565                 rv = ap_pass_brigade(f->next, ctx->bb);
00566                 if (rv != APR_SUCCESS) {
00567                     return rv;
00568                 }
00569             }
00570 
00571             zRC = deflate(&(ctx->stream), Z_NO_FLUSH);
00572 
00573             if (zRC != Z_OK)
00574                 return APR_EGENERAL;
00575         }
00576 
00577         apr_bucket_delete(e);
00578     }
00579 
00580     apr_brigade_cleanup(bb);
00581     return APR_SUCCESS;
00582 }
00583 
00584 /* This is the deflate input filter (inflates).  */
00585 static apr_status_t deflate_in_filter(ap_filter_t *f,
00586                                       apr_bucket_brigade *bb,
00587                                       ap_input_mode_t mode,
00588                                       apr_read_type_e block,
00589                                       apr_off_t readbytes)
00590 {
00591     apr_bucket *bkt;
00592     request_rec *r = f->r;
00593     deflate_ctx *ctx = f->ctx;
00594     int zRC;
00595     apr_status_t rv;
00596     deflate_filter_config *c;
00597 
00598     /* just get out of the way of things we don't want. */
00599     if (mode != AP_MODE_READBYTES) {
00600         return ap_get_brigade(f->next, bb, mode, block, readbytes);
00601     }
00602 
00603     c = ap_get_module_config(r->server->module_config, &deflate_module);
00604 
00605     if (!ctx) {
00606         int found = 0;
00607         char *token, deflate_hdr[10];
00608         const char *encoding;
00609         apr_size_t len;
00610 
00611         /* only work on main request/no subrequests */
00612         if (r->main) {
00613             ap_remove_input_filter(f);
00614             return ap_get_brigade(f->next, bb, mode, block, readbytes);
00615         }
00616 
00617         /* Let's see what our current Content-Encoding is.
00618          * If gzip is present, don't gzip again.  (We could, but let's not.)
00619          */
00620         encoding = apr_table_get(r->headers_in, "Content-Encoding");
00621         if (encoding) {
00622             const char *tmp = encoding;
00623 
00624             token = ap_get_token(r->pool, &tmp, 0);
00625             while (token && token[0]) {
00626                 if (!strcasecmp(token, "gzip")) {
00627                     found = 1;
00628                     break;
00629                 }
00630                 /* Otherwise, skip token */
00631                 tmp++;
00632                 token = ap_get_token(r->pool, &tmp, 0);
00633             }
00634         }
00635 
00636         if (found == 0) {
00637             ap_remove_input_filter(f);
00638             return ap_get_brigade(f->next, bb, mode, block, readbytes);
00639         }
00640 
00641         f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
00642         ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
00643         ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
00644         ctx->buffer = apr_palloc(r->pool, c->bufferSize);
00645 
00646         rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10);
00647         if (rv != APR_SUCCESS) {
00648             return rv;
00649         }
00650 
00651         len = 10; 
00652         rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len); 
00653         if (rv != APR_SUCCESS) {
00654             return rv;
00655         }
00656 
00657         /* We didn't get the magic bytes. */
00658         if (len != 10 ||
00659             deflate_hdr[0] != deflate_magic[0] ||
00660             deflate_hdr[1] != deflate_magic[1]) {
00661             return APR_EGENERAL;
00662         }
00663 
00664         /* We can't handle flags for now. */
00665         if (deflate_hdr[3] != 0) {
00666             return APR_EGENERAL;
00667         }
00668 
00669         zRC = inflateInit2(&ctx->stream, c->windowSize);
00670 
00671         if (zRC != Z_OK) {
00672             f->ctx = NULL;
00673             inflateEnd(&ctx->stream);
00674             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
00675                           "unable to init Zlib: "
00676                           "inflateInit2 returned %d: URL %s",
00677                           zRC, r->uri);
00678             ap_remove_input_filter(f);
00679             return ap_get_brigade(f->next, bb, mode, block, readbytes);
00680         }
00681 
00682         /* initialize deflate output buffer */
00683         ctx->stream.next_out = ctx->buffer;
00684         ctx->stream.avail_out = c->bufferSize;
00685 
00686         apr_brigade_cleanup(ctx->bb);
00687     }
00688 
00689     if (APR_BRIGADE_EMPTY(ctx->proc_bb)) {
00690         rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
00691 
00692         if (rv != APR_SUCCESS) {
00693             /* What about APR_EAGAIN errors? */
00694             inflateEnd(&ctx->stream);
00695             return rv;
00696         }
00697 
00698         APR_BRIGADE_FOREACH(bkt, ctx->bb) {
00699             const char *data;
00700             apr_size_t len;
00701 
00702             /* If we actually see the EOS, that means we screwed up! */
00703             if (APR_BUCKET_IS_EOS(bkt)) {
00704                 inflateEnd(&ctx->stream);
00705                 return APR_EGENERAL;
00706             }
00707 
00708             if (APR_BUCKET_IS_FLUSH(bkt)) {
00709                 apr_bucket *tmp_heap;
00710                 zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
00711                 if (zRC != Z_OK) {
00712                     inflateEnd(&ctx->stream);
00713                     return APR_EGENERAL;
00714                 }
00715 
00716                 ctx->stream.next_out = ctx->buffer;
00717                 len = c->bufferSize - ctx->stream.avail_out;
00718 
00719                 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
00720                 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
00721                                                  NULL, f->c->bucket_alloc);
00722                 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
00723                 ctx->stream.avail_out = c->bufferSize;
00724 
00725                 /* Move everything to the returning brigade. */
00726                 APR_BUCKET_REMOVE(bkt);
00727                 APR_BRIGADE_CONCAT(bb, ctx->bb);
00728                 break;
00729             }
00730 
00731             /* read */
00732             apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
00733 
00734             /* pass through zlib inflate. */
00735             ctx->stream.next_in = (unsigned char *)data;
00736             ctx->stream.avail_in = len;
00737 
00738             zRC = Z_OK;
00739 
00740             while (ctx->stream.avail_in != 0) {
00741                 if (ctx->stream.avail_out == 0) {
00742                     apr_bucket *tmp_heap;
00743                     ctx->stream.next_out = ctx->buffer;
00744                     len = c->bufferSize - ctx->stream.avail_out;
00745 
00746                     ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
00747                     tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
00748                                                       NULL, f->c->bucket_alloc);
00749                     APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
00750                     ctx->stream.avail_out = c->bufferSize;
00751                 }
00752 
00753                 zRC = inflate(&ctx->stream, Z_NO_FLUSH);
00754 
00755                 if (zRC == Z_STREAM_END) {
00756                     break;
00757                 }
00758 
00759                 if (zRC != Z_OK) {
00760                     inflateEnd(&ctx->stream);
00761                     return APR_EGENERAL;
00762                 }
00763             }
00764             if (zRC == Z_STREAM_END) {
00765                 apr_bucket *tmp_heap, *eos;
00766 
00767                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
00768                               "Zlib: Inflated %ld to %ld : URL %s",
00769                               ctx->stream.total_in, ctx->stream.total_out,
00770                               r->uri);
00771 
00772                 len = c->bufferSize - ctx->stream.avail_out;
00773 
00774                 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
00775                 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
00776                                                   NULL, f->c->bucket_alloc);
00777                 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
00778                 ctx->stream.avail_out = c->bufferSize;
00779 
00780                 /* Is the remaining 8 bytes already in the avail stream? */
00781                 if (ctx->stream.avail_in >= 8) {
00782                     unsigned long compCRC, compLen;
00783                     compCRC = getLong(ctx->stream.next_in);
00784                     if (ctx->crc != compCRC) {
00785                         inflateEnd(&ctx->stream);
00786                         return APR_EGENERAL;
00787                     }
00788                     ctx->stream.next_in += 4;
00789                     compLen = getLong(ctx->stream.next_in);
00790                     if (ctx->stream.total_out != compLen) {
00791                         inflateEnd(&ctx->stream);
00792                         return APR_EGENERAL;
00793                     }
00794                 }
00795                 else {
00796                     /* FIXME: We need to grab the 8 verification bytes
00797                      * from the wire! */
00798                     inflateEnd(&ctx->stream);
00799                     return APR_EGENERAL;
00800                 }
00801 
00802                 inflateEnd(&ctx->stream);
00803 
00804                 eos = apr_bucket_eos_create(f->c->bucket_alloc);
00805                 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos); 
00806                 break;
00807             }
00808 
00809         }
00810         apr_brigade_cleanup(ctx->bb);
00811     }
00812 
00813     /* If we are about to return nothing for a 'blocking' read and we have
00814      * some data in our zlib buffer, flush it out so we can return something.
00815      */
00816     if (block == APR_BLOCK_READ &&
00817         APR_BRIGADE_EMPTY(ctx->proc_bb) &&
00818         ctx->stream.avail_out < c->bufferSize) {
00819         apr_bucket *tmp_heap;
00820         apr_size_t len;
00821         ctx->stream.next_out = ctx->buffer;
00822         len = c->bufferSize - ctx->stream.avail_out;
00823 
00824         ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
00825         tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
00826                                           NULL, f->c->bucket_alloc);
00827         APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
00828         ctx->stream.avail_out = c->bufferSize;
00829     }
00830 
00831     if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) {
00832         apr_bucket_brigade *newbb;
00833 
00834         /* May return APR_INCOMPLETE which is fine by us. */
00835         apr_brigade_partition(ctx->proc_bb, readbytes, &bkt);
00836 
00837         newbb = apr_brigade_split(ctx->proc_bb, bkt);
00838         APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
00839         APR_BRIGADE_CONCAT(ctx->proc_bb, newbb);
00840     }
00841 
00842     return APR_SUCCESS;
00843 }
00844 
00845 static void register_hooks(apr_pool_t *p)
00846 {
00847     ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL,
00848                               AP_FTYPE_CONTENT_SET);
00849     ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL,
00850                               AP_FTYPE_CONTENT_SET);
00851 }
00852 
00853 static const command_rec deflate_filter_cmds[] = {
00854     AP_INIT_TAKE12("DeflateFilterNote", deflate_set_note, NULL, RSRC_CONF,
00855                   "Set a note to report on compression ratio"),
00856     AP_INIT_TAKE1("DeflateWindowSize", deflate_set_window_size, NULL,
00857                   RSRC_CONF, "Set the Deflate window size (1-15)"),
00858     AP_INIT_TAKE1("DeflateBufferSize", deflate_set_buffer_size, NULL, RSRC_CONF,
00859                   "Set the Deflate Buffer Size"),
00860     AP_INIT_TAKE1("DeflateMemLevel", deflate_set_memlevel, NULL, RSRC_CONF,
00861                   "Set the Deflate Memory Level (1-9)"),
00862     AP_INIT_TAKE1("DeflateCompressionLevel", deflate_set_compressionlevel, NULL, RSRC_CONF,
00863                   "Set the Deflate Compression Level (1-9)"),
00864     {NULL}
00865 };
00866 
00867 module AP_MODULE_DECLARE_DATA deflate_module = {
00868     STANDARD20_MODULE_STUFF,
00869     NULL,                         /* dir config creater */
00870     NULL,                         /* dir merger --- default is to override */
00871     create_deflate_server_config, /* server config */
00872     NULL,                         /* merge server config */
00873     deflate_filter_cmds,          /* command table */
00874     register_hooks                /* register hooks */
00875 };