]> granicus.if.org Git - apache/commitdiff
mod_deflate:
authorIan Holsman <ianh@apache.org>
Fri, 16 Apr 2004 05:12:22 +0000 (05:12 +0000)
committerIan Holsman <ianh@apache.org>
Fri, 16 Apr 2004 05:12:22 +0000 (05:12 +0000)
- New option for DEFLATE output file (force-gzip), which skips checking the accept-encoding header.
- New output filter 'INFLATE' for uncompressing responses.

Submitted by:   Nick Kew <Nick at WebThing dot com>
Reviewed by:    Ian Holsman

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

CHANGES
docs/manual/mod/mod_deflate.xml
modules/filters/mod_deflate.c

diff --git a/CHANGES b/CHANGES
index b27bdf0bf7bffddcbb637d01b6ed0c938187808f..e820ae34c9b9939b02f616c5568eba1ee3d57303 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,10 @@ Changes with Apache 2.1.0-dev
 
   [Remove entries to the current 2.0 section below, when backported]
 
+  *) mod_deflate: New option for DEFLATE output file (force-gzip),
+     new output filter 'INFLATE' for uncompressing responses.
+     [Nick Kew <Nick at WebThing dot com>, Ian Holsman]
+
   *) Added new module mod_version, which provides version dependent
      configuration containers.  [AndrĂ© Malo]
 
index f81f69a9178cc3743c8e11c26164caaa9878fc16..6612187caad4d4159611b35fa7ad0817eb9b2302 100644 (file)
@@ -147,8 +147,36 @@ client</description>
         The <code>DEFLATE</code> filter is always inserted after RESOURCE
         filters like PHP or SSI. It never touches internal subrequests.
       </note>
+      <note><title>Note</title>
+        There is a environment variable <code>force-gzip</code>,
+        set via <directive module="core">SetEnv</directive>, which
+        will ignore the accept-encoding setting of your browser and will
+        send compressed output.
+      </note>
+
     </section>
+    <section id="inflate"><title>Output Decompression</title>
+      <p>The <module>mod_deflate</module> module also provides a filter for
+      inflating/uncompressing a gzip compressed response body. In order to activate
+      this feature you have to insert the <code>INFLATE</code> filter into
+      the outputfilter chain using <directive module="core"
+      >SetOutputFilter</directive> or <directive module="mod_mime"
+      >AddOutputFilter</directive>, for example:</p>
 
+      <example>
+        &lt;Location /dav-area&gt;<br />
+        <indent>
+          ProxyPass http://example.com/<br />
+          SetOutputFilter INFLATE<br />
+        </indent>
+        &lt;/Location&gt;
+      </example>
+
+      <p>This Example will uncompress gzip'ed output from example.com, so other
+      filters can do further processing with it.
+      </p>
+      
+    </section>
     <section id="input"><title>Input Decompression</title>
       <p>The <module>mod_deflate</module> module also provides a filter for
       decompressing a gzip compressed request body . In order to activate
index f90e85b7e4100344f6bcb4cd3ae09fe222e3e5fa..dc0bd5f8124148388d39a345fecd86f2ad7c98d1 100644 (file)
@@ -16,8 +16,7 @@
 /*
  * mod_deflate.c: Perform deflate transfer-encoding on the fly
  *
- * Written by Ian Holsman
- *
+ * Written by Ian Holsman, Justin Erenkrantz, and Nick Kew
  */
 
 /*
@@ -257,7 +256,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f,
      */
     if (!ctx) {
         char *buf, *token;
-        const char *encoding, *accepts;
+        const char *encoding;
 
         /* only work on main request/no subrequests */
         if (r->main) {
@@ -320,7 +319,7 @@ static apr_status_t deflate_out_filter(ap_filter_t *f,
                     strcmp(token, "8bit") && strcmp(token, "binary")) {
 
                     ap_remove_output_filter(f);
-                    return ap_pass_brigade(f->next, bb);                       
+                    return ap_pass_brigade(f->next, bb);
                 }
 
                 /* Otherwise, skip token */
@@ -337,34 +336,40 @@ static apr_status_t deflate_out_filter(ap_filter_t *f,
          */
         apr_table_setn(r->headers_out, "Vary", "Accept-Encoding");
 
-        /* if they don't have the line, then they can't play */
-        accepts = apr_table_get(r->headers_in, "Accept-Encoding");
-        if (accepts == NULL) {
-            ap_remove_output_filter(f);
-            return ap_pass_brigade(f->next, bb);
-        }
 
-        token = ap_get_token(r->pool, &accepts, 0);
-        while (token && token[0] && strcasecmp(token, "gzip")) {
-            /* skip parameters, XXX: ;q=foo evaluation? */
-            while (*accepts == ';') { 
-                ++accepts;
-                token = ap_get_token(r->pool, &accepts, 1);
+        /* force-gzip will just force it out regardless if the browser
+         * can actually do anything with it.
+         */
+        if (apr_table_get(r->subprocess_env, "force-gzip") != NULL) {
+            const char *accepts;
+            /* if they don't have the line, then they can't play */
+            accepts = apr_table_get(r->headers_in, "Accept-Encoding");
+            if (accepts == NULL) {
+                ap_remove_output_filter(f);
+                return ap_pass_brigade(f->next, bb);
             }
 
-            /* retrieve next token */
-            if (*accepts == ',') {
-                ++accepts;
+            token = ap_get_token(r->pool, &accepts, 0);
+            while (token && token[0] && strcasecmp(token, "gzip")) {
+                /* skip parameters, XXX: ;q=foo evaluation? */
+                while (*accepts == ';') { 
+                    ++accepts;
+                    token = ap_get_token(r->pool, &accepts, 1);
+                }
+
+                /* retrieve next token */
+                if (*accepts == ',') {
+                    ++accepts;
+                }
+                token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL;
             }
-            token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL;
-        }
 
-        /* No acceptable token found. */
-        if (token == NULL || token[0] == '\0') {
-            ap_remove_output_filter(f);
-            return ap_pass_brigade(f->next, bb);
+            /* No acceptable token found. */
+            if (token == NULL || token[0] == '\0') {
+                ap_remove_output_filter(f);
+                return ap_pass_brigade(f->next, bb);
+            }
         }
-
         /* We're cool with filtering this. */
         ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
         ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
@@ -840,10 +845,228 @@ static apr_status_t deflate_in_filter(ap_filter_t *f,
     return APR_SUCCESS;
 }
 
+
+/* Filter to inflate for a content-transforming proxy.  */
+static apr_status_t inflate_out_filter(ap_filter_t *f,
+                                      apr_bucket_brigade *bb)
+{
+    /* have we read the zlib header in yet? assume we have in a previous pass */
+    int deflate_init = 1; 
+    apr_bucket *bkt;
+    request_rec *r = f->r;
+    deflate_ctx *ctx = f->ctx;
+    int zRC;
+    apr_status_t rv;
+    deflate_filter_config *c;
+
+    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_output_filter(f);
+            return ap_pass_brigade(f->next, bb);
+        }
+
+        /* 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_out, "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_output_filter(f);
+            return ap_pass_brigade(f->next, bb);
+        }
+
+        f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+        ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+        ctx->buffer = apr_palloc(r->pool, c->bufferSize);
+
+
+        zRC = inflateInit2(&ctx->stream, c->windowSize);
+
+        if (zRC != Z_OK) {
+            f->ctx = NULL;
+            inflateEnd(&ctx->stream);
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                          "unable to init Zlib: "
+                          "inflateInit2 returned %d: URL %s",
+                          zRC, r->uri);
+            ap_remove_output_filter(f);
+            return ap_pass_brigade(f->next, bb);
+        }
+
+        /* initialize deflate output buffer */
+        ctx->stream.next_out = ctx->buffer;
+        ctx->stream.avail_out = c->bufferSize;
+
+        deflate_init = 0;
+    }
+
+    for (bkt = APR_BRIGADE_FIRST(bb);
+         bkt != APR_BRIGADE_SENTINEL(bb);
+         bkt = APR_BUCKET_NEXT(bkt))
+    {
+        const char *data;
+        apr_size_t len;
+
+        /* If we actually see the EOS, that means we screwed up! */
+        if (APR_BUCKET_IS_EOS(bkt)) {
+            inflateEnd(&ctx->stream);
+            return APR_EGENERAL;
+        }
+
+        if (APR_BUCKET_IS_FLUSH(bkt)) {
+            apr_bucket *tmp_heap;
+            zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
+            if (zRC != Z_OK) {
+                inflateEnd(&ctx->stream);
+                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);
+            break;
+        }
+
+        /* read */
+        apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
+
+        /* first bucket contains zlib header */
+        if ( ! deflate_init++ ) {
+            if ( len < 10 ) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                              "Insufficient data for inflate");
+                return APR_EGENERAL ;
+            } 
+            else  {
+                if (data[0] != deflate_magic[0] ||
+                    data[1] != deflate_magic[1]) {
+                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                                      "deflate: bad header");
+                    return APR_EGENERAL ;
+                }
+                data += 10 ;
+                len -= 10 ;
+           }
+        }
+
+        /* pass through zlib inflate. */
+        ctx->stream.next_in = (unsigned char *)data;
+        ctx->stream.avail_in = len;
+
+        zRC = Z_OK;
+
+        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) {
+                    inflateEnd(&ctx->stream);
+                    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) {
+                    inflateEnd(&ctx->stream);
+                    return APR_EGENERAL;
+                }
+                ctx->stream.next_in += 4;
+                compLen = getLong(ctx->stream.next_in);
+                if (ctx->stream.total_out != compLen) {
+                    inflateEnd(&ctx->stream);
+                    return APR_EGENERAL;
+                }
+            }
+            else {
+                /* FIXME: We need to grab the 8 verification bytes
+                 * from the wire! */
+                inflateEnd(&ctx->stream);
+                return APR_EGENERAL;
+            }
+
+            inflateEnd(&ctx->stream);
+
+            eos = apr_bucket_eos_create(f->c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos); 
+            break;
+        }
+
+    }
+
+    rv = ap_pass_brigade(f->next, ctx->proc_bb);
+    apr_brigade_cleanup(ctx->proc_bb);
+    return rv ;
+}
+
 static void register_hooks(apr_pool_t *p)
 {
     ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL,
                               AP_FTYPE_CONTENT_SET);
+    ap_register_output_filter("INFLATE", inflate_out_filter, NULL,
+                              AP_FTYPE_RESOURCE-1);
     ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL,
                               AP_FTYPE_CONTENT_SET);
 }