diff options
author | Ian Holsman <ianh@apache.org> | 2004-04-16 07:12:22 +0200 |
---|---|---|
committer | Ian Holsman <ianh@apache.org> | 2004-04-16 07:12:22 +0200 |
commit | 51e8b9afa4a4584b7d93cfb00a8f60e2887d00fd (patch) | |
tree | 4bf1591828a530676b3c1c64fbe9f844aa67c568 /modules/filters | |
parent | * server/vhost.c (remove_unused_name_vhosts): Remove redundant check; (diff) | |
download | apache2-51e8b9afa4a4584b7d93cfb00a8f60e2887d00fd.tar.xz apache2-51e8b9afa4a4584b7d93cfb00a8f60e2887d00fd.zip |
mod_deflate:
- 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
Diffstat (limited to 'modules/filters')
-rw-r--r-- | modules/filters/mod_deflate.c | 275 |
1 files changed, 249 insertions, 26 deletions
diff --git a/modules/filters/mod_deflate.c b/modules/filters/mod_deflate.c index f90e85b7e4..dc0bd5f812 100644 --- a/modules/filters/mod_deflate.c +++ b/modules/filters/mod_deflate.c @@ -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); } |