]> granicus.if.org Git - apache/commitdiff
Input filter support for mod_deflate.
authorJustin Erenkrantz <jerenkrantz@apache.org>
Wed, 29 May 2002 10:27:05 +0000 (10:27 +0000)
committerJustin Erenkrantz <jerenkrantz@apache.org>
Wed, 29 May 2002 10:27:05 +0000 (10:27 +0000)
Highly experimental, but known to work for some tests.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@95345 13f79535-47bb-0310-9956-ffa450edef68

modules/filters/mod_deflate.c

index 90b4e1569e8bce122fb453a326284c2097c2e2b7..5b7b4ae6fd9ce5806b0ee0dba81e16f1fe2a9fc3 100644 (file)
@@ -147,6 +147,22 @@ static void putLong(char *string, unsigned long x)
     }
 }
 
+/* Inputs a string and returns a long.
+ */
+static unsigned long getLong(char *string)
+{
+    int n = 3;
+    unsigned long x = 0;
+
+    while (n) {
+        x |= (unsigned long)((unsigned char)string[n--]) & 0xff;
+        x <<= 8;
+    }
+
+    x |= (unsigned long)((unsigned char)string[0]) & 0xff;
+    return x;
+}
+
 static void *create_deflate_server_config(apr_pool_t *p, server_rec *s)
 {
     deflate_filter_config *c = apr_pcalloc(p, sizeof *c);
@@ -217,14 +233,14 @@ static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy,
 }
 
 /* magic header */
-static int deflate_magic[2] = { 0x1f, 0x8b };
+static char deflate_magic[2] = { '\037', '\213' };
 
 typedef struct deflate_ctx_t
 {
     z_stream stream;
     unsigned char *buffer;
     unsigned long crc;
-    apr_bucket_brigade *bb;
+    apr_bucket_brigade *bb, *proc_bb;
 } deflate_ctx;
 
 static apr_status_t deflate_out_filter(ap_filter_t *f,
@@ -504,10 +520,245 @@ static apr_status_t deflate_out_filter(ap_filter_t *f,
     return APR_SUCCESS;
 }
 
+/* This is the deflate input filter (inflates).  */
+static apr_status_t deflate_in_filter(ap_filter_t *f,
+                                      apr_bucket_brigade *bb,
+                                      ap_input_mode_t mode,
+                                      apr_read_type_e block,
+                                      apr_off_t readbytes)
+{
+    apr_bucket *bkt;
+    request_rec *r = f->r;
+    deflate_ctx *ctx = f->ctx;
+    int zRC;
+    apr_status_t rv;
+    deflate_filter_config *c;
+
+    /* just get out of the way of things we don't want. */
+    if (mode != AP_MODE_READBYTES) {
+        return ap_get_brigade(f->next, bb, mode, block, readbytes);
+    }
+
+    c = ap_get_module_config(r->server->module_config, &deflate_module);
+
+    if (!ctx) {
+        int found = 0;
+        char *token, deflate_hdr[10];
+        const char *encoding;
+        apr_size_t len;
+
+        /* only work on main request/no subrequests */
+        if (r->main) {
+            ap_remove_input_filter(f);
+            return ap_get_brigade(f->next, bb, mode, block, readbytes);
+        }
+
+        /* Let's see what our current Content-Encoding is.
+         * If gzip is present, don't gzip again.  (We could, but let's not.)
+         */
+        encoding = apr_table_get(r->headers_in, "Content-Encoding");
+        if (encoding) {
+            const char *tmp = encoding;
+
+            token = ap_get_token(r->pool, &tmp, 0);
+            while (token && token[0]) {
+                if (!strcasecmp(token, "gzip")) {
+                    found = 1;
+                    break;
+                }
+                /* Otherwise, skip token */
+                tmp++;
+                token = ap_get_token(r->pool, &tmp, 0);
+            }
+        }
+
+        if (found == 0) {
+            ap_remove_input_filter(f);
+            return ap_get_brigade(f->next, bb, mode, block, readbytes);
+        }
+
+        f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+        ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+        ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+        ctx->buffer = apr_palloc(r->pool, c->bufferSize);
+
+        rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10);
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+      
+        len = 10; 
+        rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len); 
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+
+        /* We didn't get the magic bytes. */
+        if (len != 10 ||
+            deflate_hdr[0] != deflate_magic[0] ||
+            deflate_hdr[1] != deflate_magic[1]) {
+            return APR_EGENERAL;
+        }
+
+        /* We can't handle flags for now. */
+        if (deflate_hdr[3] != 0) {
+            return APR_EGENERAL;
+        }
+
+        zRC = inflateInit2(&ctx->stream, c->windowSize);
+
+        if (zRC != Z_OK) {
+            f->ctx = NULL;
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                          "unable to init Zlib: "
+                          "inflateInit2 returned %d: URL %s",
+                          zRC, r->uri);
+            ap_remove_input_filter(f);
+            return ap_get_brigade(f->next, bb, mode, block, readbytes);
+        }
+
+        /* initialize deflate output buffer */
+        ctx->stream.next_out = ctx->buffer;
+        ctx->stream.avail_out = c->bufferSize;
+
+        apr_brigade_cleanup(ctx->bb);
+    }
+
+    if (APR_BRIGADE_EMPTY(ctx->proc_bb)) {
+        rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
+
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+
+        APR_BRIGADE_FOREACH(bkt, ctx->bb) {
+            const char *data;
+            apr_size_t len;
+
+            /* If we actually see the EOS, that means we screwed up! */
+            if (APR_BUCKET_IS_EOS(bkt)) {
+                return APR_EGENERAL;
+            }
+
+            if (APR_BUCKET_IS_FLUSH(bkt)) {
+                apr_bucket *tmp_heap;
+                zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
+                if (zRC != Z_OK) {
+                    return APR_EGENERAL;
+                }
+
+                ctx->stream.next_out = ctx->buffer;
+                len = c->bufferSize - ctx->stream.avail_out;
+
+                ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
+                tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
+                                                 NULL, f->c->bucket_alloc);
+                APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
+                ctx->stream.avail_out = c->bufferSize;
+
+                /* Move everything to the returning brigade. */
+                APR_BUCKET_REMOVE(bkt);
+                APR_BRIGADE_CONCAT(bb, ctx->bb);
+                break;
+            }
+
+            /* read */
+            apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
+
+            /* pass through zlib inflate. */
+            ctx->stream.next_in = (unsigned char *)data;
+            ctx->stream.avail_in = len;
+
+            while (ctx->stream.avail_in != 0) {
+                if (ctx->stream.avail_out == 0) {
+                    apr_bucket *tmp_heap;
+                    ctx->stream.next_out = ctx->buffer;
+                    len = c->bufferSize - ctx->stream.avail_out;
+
+                    ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
+                    tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
+                                                      NULL, f->c->bucket_alloc);
+                    APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
+                    ctx->stream.avail_out = c->bufferSize;
+                }
+
+                zRC = inflate(&ctx->stream, Z_NO_FLUSH);
+
+                if (zRC == Z_STREAM_END) {
+                    break;
+                }
+
+                if (zRC != Z_OK) {
+                    return APR_EGENERAL;
+                }
+            }
+            if (zRC == Z_STREAM_END) {
+                apr_bucket *tmp_heap, *eos;
+
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                              "Zlib: Inflated %ld to %ld : URL %s",
+                              ctx->stream.total_in, ctx->stream.total_out,
+                              r->uri);
+
+                len = c->bufferSize - ctx->stream.avail_out;
+
+                ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
+                tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
+                                                  NULL, f->c->bucket_alloc);
+                APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
+                ctx->stream.avail_out = c->bufferSize;
+
+                /* Is the remaining 8 bytes already in the avail stream? */
+                if (ctx->stream.avail_in >= 8) {
+                    unsigned long compCRC, compLen;
+                    compCRC = getLong(ctx->stream.next_in);
+                    if (ctx->crc != compCRC) {
+                        return APR_EGENERAL;
+                    }
+                    ctx->stream.next_in += 4;
+                    compLen = getLong(ctx->stream.next_in);
+                    if (ctx->stream.total_out != compLen) {
+                        return APR_EGENERAL;
+                    }
+                }
+                else {
+                    /* FIXME: We need to grab the 8 verification bytes
+                     * from the wire! */
+                    return APR_EGENERAL;
+                }
+
+                inflateEnd(&ctx->stream);
+                apr_brigade_cleanup(ctx->bb);
+    
+                eos = apr_bucket_eos_create(f->c->bucket_alloc);
+                APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos); 
+                break;
+            }
+        }
+    }
+
+    if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) {
+        apr_bucket_brigade *newbb;
+
+        /* May return APR_INCOMPLETE which is fine by us. */
+        apr_brigade_partition(ctx->proc_bb, readbytes, &bkt);
+
+        newbb = apr_brigade_split(ctx->proc_bb, bkt);
+        APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
+        APR_BRIGADE_CONCAT(ctx->proc_bb, newbb);
+    }
+
+    
+
+    return APR_SUCCESS;
+}
+
 static void register_hooks(apr_pool_t *p)
 {
     ap_register_output_filter(deflateFilterName, deflate_out_filter,
                               AP_FTYPE_CONTENT_SET);
+    ap_register_input_filter(deflateFilterName, deflate_in_filter,
+                              AP_FTYPE_CONTENT_SET);
 }
 
 static const command_rec deflate_filter_cmds[] = {