From: Justin Erenkrantz Date: Wed, 29 May 2002 10:27:05 +0000 (+0000) Subject: Input filter support for mod_deflate. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=76c24a9790443eff08e54bfbcd9bcc1d4587c4f4;p=apache Input filter support for mod_deflate. 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 --- diff --git a/modules/filters/mod_deflate.c b/modules/filters/mod_deflate.c index 90b4e1569e..5b7b4ae6fd 100644 --- a/modules/filters/mod_deflate.c +++ b/modules/filters/mod_deflate.c @@ -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[] = {