summaryrefslogtreecommitdiffstats
path: root/modules/filters
diff options
context:
space:
mode:
authorIan Holsman <ianh@apache.org>2004-04-16 07:12:22 +0200
committerIan Holsman <ianh@apache.org>2004-04-16 07:12:22 +0200
commit51e8b9afa4a4584b7d93cfb00a8f60e2887d00fd (patch)
tree4bf1591828a530676b3c1c64fbe9f844aa67c568 /modules/filters
parent* server/vhost.c (remove_unused_name_vhosts): Remove redundant check; (diff)
downloadapache2-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.c275
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);
}