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); }