As discussed previously, here are my updates as a patch against 2.0.49.
They serve to enable working with compressed data coming from a proxy
(or other backend) and processing content in the output filter chain.
--
Nick Kew
Nick's manifesto: http://www.htmlhelp.com/~nick/
--- mod_deflate.c.old 2004-04-15 23:35:38.000000000 +0100
+++ mod_deflate.c 2004-04-15 23:38:02.000000000 +0100
@@ -23,6 +23,12 @@
*
* Written by Ian Holsman
*
+ * Modified by Nick Kew, April 2004
+ *
+ * FIX: deflate html content based on a NOTE from html-parse filter.
+ * ADD: inflate_out_filter to decompress content for output filters
+ * in a proxy with compressed backend. I don't understand the
+ * zlib stuff, so it's modified "blind" from the input filter.
*/
#include "httpd.h"
@@ -339,6 +345,11 @@
/* if they don't have the line, then they can't play */
accepts = apr_table_get(r->headers_in, "Accept-Encoding");
+
+ /* NRK: accept it if we removed Accept-Encoding earlier */
+ if (accepts == NULL) {
+ accepts = apr_table_get(r->notes, "Accept-Encoding");
+ }
if (accepts == NULL) {
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
@@ -834,10 +845,224 @@
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)
+{
+ 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 ;
+ }
+
+
+ APR_BRIGADE_FOREACH(bkt, 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)) {
+ 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);
}