1 /* Copyright 2002-2004 The Apache Software Foundation
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 * mod_deflate.c: Perform deflate transfer-encoding on the fly
19 * Written by Ian Holsman, Justin Erenkrantz, and Nick Kew
23 * Portions of this software are based upon zlib code by Jean-loup Gailly
24 * (zlib functions gz_open and gzwrite, check_header)
28 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
29 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
30 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
31 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
32 #define COMMENT 0x10 /* bit 4 set: file comment present */
33 #define RESERVED 0xE0 /* bits 5..7: reserved */
37 #include "http_config.h"
39 #include "apr_strings.h"
40 #include "apr_general.h"
41 #include "util_filter.h"
42 #include "apr_buckets.h"
43 #include "http_request.h"
44 #define APR_WANT_STRFUNC
52 /* As part of the encoding process, we must send what our OS_CODE is
53 * (or so it seems based on what I can tell of how gzip encoding works).
55 * zutil.h is not always included with zlib distributions (it is a private
56 * header), so this is straight from zlib 1.1.3's zutil.h.
62 #ifdef WIN32 /* Window 95 & Windows NT */
66 #if defined(VAXC) || defined(VMS)
74 #if defined(ATARI) || defined(atarist)
78 #if defined(MACOS) || defined(TARGET_OS_MAC)
82 #ifdef __50SERIES /* Prime/PRIMOS */
91 #define OS_CODE 0x03 /* assume Unix */
95 static const char deflateFilterName[] = "DEFLATE";
96 module AP_MODULE_DECLARE_DATA deflate_module;
98 typedef struct deflate_filter_config_t
102 int compressionlevel;
103 apr_size_t bufferSize;
104 char *note_ratio_name;
105 char *note_input_name;
106 char *note_output_name;
107 } deflate_filter_config;
109 /* windowsize is negative to suppress Zlib header */
110 #define DEFAULT_COMPRESSION Z_DEFAULT_COMPRESSION
111 #define DEFAULT_WINDOWSIZE -15
112 #define DEFAULT_MEMLEVEL 9
113 #define DEFAULT_BUFFERSIZE 8096
115 /* Outputs a long in LSB order to the given file
116 * only the bottom 4 bits are required for the deflate file format.
118 static void putLong(unsigned char *string, unsigned long x)
120 string[0] = (unsigned char)(x & 0xff);
121 string[1] = (unsigned char)((x & 0xff00) >> 8);
122 string[2] = (unsigned char)((x & 0xff0000) >> 16);
123 string[3] = (unsigned char)((x & 0xff000000) >> 24);
126 /* Inputs a string and returns a long.
128 static unsigned long getLong(unsigned char *string)
130 return ((unsigned long)string[0])
131 | (((unsigned long)string[1]) << 8)
132 | (((unsigned long)string[2]) << 16)
133 | (((unsigned long)string[3]) << 24);
136 static void *create_deflate_server_config(apr_pool_t *p, server_rec *s)
138 deflate_filter_config *c = apr_pcalloc(p, sizeof *c);
140 c->memlevel = DEFAULT_MEMLEVEL;
141 c->windowSize = DEFAULT_WINDOWSIZE;
142 c->bufferSize = DEFAULT_BUFFERSIZE;
143 c->compressionlevel = DEFAULT_COMPRESSION;
148 static const char *deflate_set_window_size(cmd_parms *cmd, void *dummy,
151 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
158 return "DeflateWindowSize must be between 1 and 15";
160 c->windowSize = i * -1;
165 static const char *deflate_set_buffer_size(cmd_parms *cmd, void *dummy,
168 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
173 return "DeflateBufferSize should be positive";
176 c->bufferSize = (apr_size_t)n;
180 static const char *deflate_set_note(cmd_parms *cmd, void *dummy,
181 const char *arg1, const char *arg2)
183 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
187 c->note_ratio_name = apr_pstrdup(cmd->pool, arg1);
189 else if (!strcasecmp(arg1, "ratio")) {
190 c->note_ratio_name = apr_pstrdup(cmd->pool, arg2);
192 else if (!strcasecmp(arg1, "input")) {
193 c->note_input_name = apr_pstrdup(cmd->pool, arg2);
195 else if (!strcasecmp(arg1, "output")) {
196 c->note_output_name = apr_pstrdup(cmd->pool, arg2);
199 return apr_psprintf(cmd->pool, "Unknown note type %s", arg1);
205 static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy,
208 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
215 return "DeflateMemLevel must be between 1 and 9";
222 static const char *deflate_set_compressionlevel(cmd_parms *cmd, void *dummy,
225 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
232 return "Compression Level must be between 1 and 9";
234 c->compressionlevel = i;
240 static char deflate_magic[2] = { '\037', '\213' };
242 typedef struct deflate_ctx_t
245 unsigned char *buffer;
247 apr_bucket_brigade *bb, *proc_bb;
250 static apr_status_t deflate_out_filter(ap_filter_t *f,
251 apr_bucket_brigade *bb)
254 request_rec *r = f->r;
255 deflate_ctx *ctx = f->ctx;
257 deflate_filter_config *c = ap_get_module_config(r->server->module_config,
260 /* Do nothing if asked to filter nothing. */
261 if (APR_BRIGADE_EMPTY(bb)) {
265 /* If we don't have a context, we need to ensure that it is okay to send
266 * the deflated content. If we have a context, that means we've done
267 * this before and we liked it.
268 * This could be not so nice if we always fail. But, if we succeed,
269 * we're in better shape.
273 const char *encoding;
275 /* only work on main request/no subrequests */
276 if (!ap_is_initial_req(r)) {
277 ap_remove_output_filter(f);
278 return ap_pass_brigade(f->next, bb);
281 /* some browsers might have problems, so set no-gzip
282 * (with browsermatch) for them
284 if (apr_table_get(r->subprocess_env, "no-gzip")) {
285 ap_remove_output_filter(f);
286 return ap_pass_brigade(f->next, bb);
289 /* Some browsers might have problems with content types
290 * other than text/html, so set gzip-only-text/html
291 * (with browsermatch) for them
293 if (r->content_type == NULL
294 || strncmp(r->content_type, "text/html", 9)) {
295 const char *env_value = apr_table_get(r->subprocess_env,
296 "gzip-only-text/html");
297 if ( env_value && (strcmp(env_value,"1") == 0) ) {
298 ap_remove_output_filter(f);
299 return ap_pass_brigade(f->next, bb);
303 /* Let's see what our current Content-Encoding is.
304 * If it's already encoded, don't compress again.
305 * (We could, but let's not.)
307 encoding = apr_table_get(r->headers_out, "Content-Encoding");
311 err_enc = apr_table_get(r->err_headers_out, "Content-Encoding");
313 encoding = apr_pstrcat(r->pool, encoding, ",", err_enc, NULL);
317 encoding = apr_table_get(r->err_headers_out, "Content-Encoding");
320 if (r->content_encoding) {
321 encoding = encoding ? apr_pstrcat(r->pool, encoding, ",",
322 r->content_encoding, NULL)
323 : r->content_encoding;
327 const char *tmp = encoding;
329 token = ap_get_token(r->pool, &tmp, 0);
330 while (token && *token) {
331 /* stolen from mod_negotiation: */
332 if (strcmp(token, "identity") && strcmp(token, "7bit") &&
333 strcmp(token, "8bit") && strcmp(token, "binary")) {
335 ap_remove_output_filter(f);
336 return ap_pass_brigade(f->next, bb);
339 /* Otherwise, skip token */
343 token = (*tmp) ? ap_get_token(r->pool, &tmp, 0) : NULL;
347 /* Even if we don't accept this request based on it not having
348 * the Accept-Encoding, we need to note that we were looking
349 * for this header and downstream proxies should be aware of that.
351 apr_table_setn(r->headers_out, "Vary", "Accept-Encoding");
353 /* force-gzip will just force it out regardless if the browser
354 * can actually do anything with it.
356 if (!apr_table_get(r->subprocess_env, "force-gzip")) {
358 /* if they don't have the line, then they can't play */
359 accepts = apr_table_get(r->headers_in, "Accept-Encoding");
360 if (accepts == NULL) {
361 ap_remove_output_filter(f);
362 return ap_pass_brigade(f->next, bb);
365 token = ap_get_token(r->pool, &accepts, 0);
366 while (token && token[0] && strcasecmp(token, "gzip")) {
367 /* skip parameters, XXX: ;q=foo evaluation? */
368 while (*accepts == ';') {
370 token = ap_get_token(r->pool, &accepts, 1);
373 /* retrieve next token */
374 if (*accepts == ',') {
377 token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL;
380 /* No acceptable token found. */
381 if (token == NULL || token[0] == '\0') {
382 ap_remove_output_filter(f);
383 return ap_pass_brigade(f->next, bb);
387 /* Deflating a zero-length response would make it longer; the
388 * proxy may pass through an empty response for a 304 too.
389 * So we just need to fix up the headers as if we had a body.
391 if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bb))) {
392 if (!encoding || !strcasecmp(encoding, "identity")) {
393 apr_table_set(r->headers_out, "Content-Encoding", "gzip");
396 apr_table_merge(r->headers_out, "Content-Encoding", "gzip");
398 apr_table_unset(r->headers_out, "Content-Length");
400 ap_remove_output_filter(f);
401 return ap_pass_brigade(f->next, bb);
404 /* We're cool with filtering this. */
405 ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
406 ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
407 ctx->buffer = apr_palloc(r->pool, c->bufferSize);
409 zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED,
410 c->windowSize, c->memlevel,
415 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
416 "unable to init Zlib: "
417 "deflateInit2 returned %d: URL %s",
419 return ap_pass_brigade(f->next, bb);
422 /* RFC 1952 Section 2.3 dictates the gzip header:
424 * +---+---+---+---+---+---+---+---+---+---+
425 * |ID1|ID2|CM |FLG| MTIME |XFL|OS |
426 * +---+---+---+---+---+---+---+---+---+---+
428 * If we wish to populate in MTIME (as hinted in RFC 1952), do:
429 * putLong(date_array, apr_time_now() / APR_USEC_PER_SEC);
430 * where date_array is a char[4] and then print date_array in the
431 * MTIME position. WARNING: ENDIANNESS ISSUE HERE.
433 buf = apr_psprintf(r->pool, "%c%c%c%c%c%c%c%c%c%c", deflate_magic[0],
434 deflate_magic[1], Z_DEFLATED, 0 /* flags */,
435 0, 0, 0, 0 /* 4 chars for mtime */,
436 0 /* xflags */, OS_CODE);
437 e = apr_bucket_pool_create(buf, 10, r->pool, f->c->bucket_alloc);
438 APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
440 /* If the entire Content-Encoding is "identity", we can replace it. */
441 if (!encoding || !strcasecmp(encoding, "identity")) {
442 apr_table_setn(r->headers_out, "Content-Encoding", "gzip");
445 apr_table_mergen(r->headers_out, "Content-Encoding", "gzip");
447 apr_table_unset(r->headers_out, "Content-Length");
449 /* initialize deflate output buffer */
450 ctx->stream.next_out = ctx->buffer;
451 ctx->stream.avail_out = c->bufferSize;
454 while (!APR_BRIGADE_EMPTY(bb))
461 e = APR_BRIGADE_FIRST(bb);
463 if (APR_BUCKET_IS_EOS(e)) {
465 unsigned int deflate_len;
467 ctx->stream.avail_in = 0; /* should be zero already anyway */
469 deflate_len = c->bufferSize - ctx->stream.avail_out;
471 if (deflate_len != 0) {
472 b = apr_bucket_heap_create((char *)ctx->buffer,
475 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
476 ctx->stream.next_out = ctx->buffer;
477 ctx->stream.avail_out = c->bufferSize;
484 zRC = deflate(&ctx->stream, Z_FINISH);
486 if (deflate_len == 0 && zRC == Z_BUF_ERROR) {
490 done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END);
492 if (zRC != Z_OK && zRC != Z_STREAM_END) {
497 buf = apr_palloc(r->pool, 8);
498 putLong((unsigned char *)&buf[0], ctx->crc);
499 putLong((unsigned char *)&buf[4], ctx->stream.total_in);
501 b = apr_bucket_pool_create(buf, 8, r->pool, f->c->bucket_alloc);
502 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
503 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
504 "Zlib: Compressed %ld to %ld : URL %s",
505 ctx->stream.total_in, ctx->stream.total_out, r->uri);
507 /* leave notes for logging */
508 if (c->note_input_name) {
509 apr_table_setn(r->notes, c->note_input_name,
510 (ctx->stream.total_in > 0)
511 ? apr_off_t_toa(r->pool,
512 ctx->stream.total_in)
516 if (c->note_output_name) {
517 apr_table_setn(r->notes, c->note_output_name,
518 (ctx->stream.total_in > 0)
519 ? apr_off_t_toa(r->pool,
520 ctx->stream.total_out)
524 if (c->note_ratio_name) {
525 apr_table_setn(r->notes, c->note_ratio_name,
526 (ctx->stream.total_in > 0)
528 (int)(ctx->stream.total_out
530 / ctx->stream.total_in))
534 deflateEnd(&ctx->stream);
536 /* Remove EOS from the old list, and insert into the new. */
537 APR_BUCKET_REMOVE(e);
538 APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
540 /* Okay, we've seen the EOS.
541 * Time to pass it along down the chain.
543 return ap_pass_brigade(f->next, ctx->bb);
546 if (APR_BUCKET_IS_FLUSH(e)) {
550 apr_bucket_delete(e);
552 if (ctx->stream.avail_in > 0) {
553 zRC = deflate(&(ctx->stream), Z_SYNC_FLUSH);
559 ctx->stream.next_out = ctx->buffer;
560 len = c->bufferSize - ctx->stream.avail_out;
562 b = apr_bucket_heap_create((char *)ctx->buffer, len,
563 NULL, f->c->bucket_alloc);
564 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
565 ctx->stream.avail_out = c->bufferSize;
567 bkt = apr_bucket_flush_create(f->c->bucket_alloc);
568 APR_BRIGADE_INSERT_TAIL(ctx->bb, bkt);
569 rv = ap_pass_brigade(f->next, ctx->bb);
570 if (rv != APR_SUCCESS) {
577 apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
579 /* This crc32 function is from zlib. */
580 ctx->crc = crc32(ctx->crc, (const Bytef *)data, len);
583 ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness,
584 * but we'll just have to
586 ctx->stream.avail_in = len;
588 while (ctx->stream.avail_in != 0) {
589 if (ctx->stream.avail_out == 0) {
592 ctx->stream.next_out = ctx->buffer;
593 len = c->bufferSize - ctx->stream.avail_out;
595 b = apr_bucket_heap_create((char *)ctx->buffer, len,
596 NULL, f->c->bucket_alloc);
597 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
598 ctx->stream.avail_out = c->bufferSize;
599 /* Send what we have right now to the next filter. */
600 rv = ap_pass_brigade(f->next, ctx->bb);
601 if (rv != APR_SUCCESS) {
606 zRC = deflate(&(ctx->stream), Z_NO_FLUSH);
612 apr_bucket_delete(e);
615 apr_brigade_cleanup(bb);
619 /* This is the deflate input filter (inflates). */
620 static apr_status_t deflate_in_filter(ap_filter_t *f,
621 apr_bucket_brigade *bb,
622 ap_input_mode_t mode,
623 apr_read_type_e block,
627 request_rec *r = f->r;
628 deflate_ctx *ctx = f->ctx;
631 deflate_filter_config *c;
633 /* just get out of the way of things we don't want. */
634 if (mode != AP_MODE_READBYTES) {
635 return ap_get_brigade(f->next, bb, mode, block, readbytes);
638 c = ap_get_module_config(r->server->module_config, &deflate_module);
642 char *token, deflate_hdr[10];
643 const char *encoding;
646 /* only work on main request/no subrequests */
647 if (!ap_is_initial_req(r)) {
648 ap_remove_input_filter(f);
649 return ap_get_brigade(f->next, bb, mode, block, readbytes);
652 /* Let's see what our current Content-Encoding is.
653 * If gzip is present, don't gzip again. (We could, but let's not.)
655 encoding = apr_table_get(r->headers_in, "Content-Encoding");
657 const char *tmp = encoding;
659 token = ap_get_token(r->pool, &tmp, 0);
660 while (token && token[0]) {
661 if (!strcasecmp(token, "gzip")) {
665 /* Otherwise, skip token */
667 token = ap_get_token(r->pool, &tmp, 0);
672 ap_remove_input_filter(f);
673 return ap_get_brigade(f->next, bb, mode, block, readbytes);
676 f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
677 ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
678 ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
679 ctx->buffer = apr_palloc(r->pool, c->bufferSize);
681 rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10);
682 if (rv != APR_SUCCESS) {
687 rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len);
688 if (rv != APR_SUCCESS) {
692 /* We didn't get the magic bytes. */
694 deflate_hdr[0] != deflate_magic[0] ||
695 deflate_hdr[1] != deflate_magic[1]) {
699 /* We can't handle flags for now. */
700 if (deflate_hdr[3] != 0) {
704 zRC = inflateInit2(&ctx->stream, c->windowSize);
708 inflateEnd(&ctx->stream);
709 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
710 "unable to init Zlib: "
711 "inflateInit2 returned %d: URL %s",
713 ap_remove_input_filter(f);
714 return ap_get_brigade(f->next, bb, mode, block, readbytes);
717 /* initialize deflate output buffer */
718 ctx->stream.next_out = ctx->buffer;
719 ctx->stream.avail_out = c->bufferSize;
721 apr_brigade_cleanup(ctx->bb);
724 if (APR_BRIGADE_EMPTY(ctx->proc_bb)) {
725 rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
727 if (rv != APR_SUCCESS) {
728 /* What about APR_EAGAIN errors? */
729 inflateEnd(&ctx->stream);
733 for (bkt = APR_BRIGADE_FIRST(ctx->bb);
734 bkt != APR_BRIGADE_SENTINEL(ctx->bb);
735 bkt = APR_BUCKET_NEXT(bkt))
740 /* If we actually see the EOS, that means we screwed up! */
741 if (APR_BUCKET_IS_EOS(bkt)) {
742 inflateEnd(&ctx->stream);
746 if (APR_BUCKET_IS_FLUSH(bkt)) {
747 apr_bucket *tmp_heap;
748 zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
750 inflateEnd(&ctx->stream);
754 ctx->stream.next_out = ctx->buffer;
755 len = c->bufferSize - ctx->stream.avail_out;
757 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
758 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
759 NULL, f->c->bucket_alloc);
760 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
761 ctx->stream.avail_out = c->bufferSize;
763 /* Move everything to the returning brigade. */
764 APR_BUCKET_REMOVE(bkt);
765 APR_BRIGADE_CONCAT(bb, ctx->bb);
770 apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
772 /* pass through zlib inflate. */
773 ctx->stream.next_in = (unsigned char *)data;
774 ctx->stream.avail_in = len;
778 while (ctx->stream.avail_in != 0) {
779 if (ctx->stream.avail_out == 0) {
780 apr_bucket *tmp_heap;
781 ctx->stream.next_out = ctx->buffer;
782 len = c->bufferSize - ctx->stream.avail_out;
784 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
785 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
786 NULL, f->c->bucket_alloc);
787 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
788 ctx->stream.avail_out = c->bufferSize;
791 zRC = inflate(&ctx->stream, Z_NO_FLUSH);
793 if (zRC == Z_STREAM_END) {
798 inflateEnd(&ctx->stream);
802 if (zRC == Z_STREAM_END) {
803 apr_bucket *tmp_heap, *eos;
805 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
806 "Zlib: Inflated %ld to %ld : URL %s",
807 ctx->stream.total_in, ctx->stream.total_out,
810 len = c->bufferSize - ctx->stream.avail_out;
812 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
813 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
814 NULL, f->c->bucket_alloc);
815 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
816 ctx->stream.avail_out = c->bufferSize;
818 /* Is the remaining 8 bytes already in the avail stream? */
819 if (ctx->stream.avail_in >= 8) {
820 unsigned long compCRC, compLen;
821 compCRC = getLong(ctx->stream.next_in);
822 if (ctx->crc != compCRC) {
823 inflateEnd(&ctx->stream);
826 ctx->stream.next_in += 4;
827 compLen = getLong(ctx->stream.next_in);
828 if (ctx->stream.total_out != compLen) {
829 inflateEnd(&ctx->stream);
834 /* FIXME: We need to grab the 8 verification bytes
836 inflateEnd(&ctx->stream);
840 inflateEnd(&ctx->stream);
842 eos = apr_bucket_eos_create(f->c->bucket_alloc);
843 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos);
848 apr_brigade_cleanup(ctx->bb);
851 /* If we are about to return nothing for a 'blocking' read and we have
852 * some data in our zlib buffer, flush it out so we can return something.
854 if (block == APR_BLOCK_READ &&
855 APR_BRIGADE_EMPTY(ctx->proc_bb) &&
856 ctx->stream.avail_out < c->bufferSize) {
857 apr_bucket *tmp_heap;
859 ctx->stream.next_out = ctx->buffer;
860 len = c->bufferSize - ctx->stream.avail_out;
862 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
863 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
864 NULL, f->c->bucket_alloc);
865 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
866 ctx->stream.avail_out = c->bufferSize;
869 if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) {
870 apr_bucket_brigade *newbb;
872 /* May return APR_INCOMPLETE which is fine by us. */
873 apr_brigade_partition(ctx->proc_bb, readbytes, &bkt);
875 newbb = apr_brigade_split(ctx->proc_bb, bkt);
876 APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
877 APR_BRIGADE_CONCAT(ctx->proc_bb, newbb);
884 /* Filter to inflate for a content-transforming proxy. */
885 static apr_status_t inflate_out_filter(ap_filter_t *f,
886 apr_bucket_brigade *bb)
890 int deflate_init = 1;
892 request_rec *r = f->r;
893 deflate_ctx *ctx = f->ctx;
896 deflate_filter_config *c;
898 /* Do nothing if asked to filter nothing. */
899 if (APR_BRIGADE_EMPTY(bb)) {
903 c = ap_get_module_config(r->server->module_config, &deflate_module);
908 const char *encoding;
910 /* only work on main request/no subrequests */
911 if (!ap_is_initial_req(r)) {
912 ap_remove_output_filter(f);
913 return ap_pass_brigade(f->next, bb);
916 /* Let's see what our current Content-Encoding is.
917 * If gzip is present, don't gzip again. (We could, but let's not.)
919 encoding = apr_table_get(r->headers_out, "Content-Encoding");
921 const char *tmp = encoding;
923 token = ap_get_token(r->pool, &tmp, 0);
924 while (token && token[0]) {
925 if (!strcasecmp(token, "gzip")) {
929 /* Otherwise, skip token */
931 token = ap_get_token(r->pool, &tmp, 0);
936 ap_remove_output_filter(f);
937 return ap_pass_brigade(f->next, bb);
939 apr_table_unset(r->headers_out, "Content-Encoding");
941 /* No need to inflate HEAD or 204/304 */
942 if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bb))) {
943 ap_remove_output_filter(f);
944 return ap_pass_brigade(f->next, bb);
948 f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
949 ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
950 ctx->buffer = apr_palloc(r->pool, c->bufferSize);
953 zRC = inflateInit2(&ctx->stream, c->windowSize);
957 inflateEnd(&ctx->stream);
958 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
959 "unable to init Zlib: "
960 "inflateInit2 returned %d: URL %s",
962 ap_remove_output_filter(f);
963 return ap_pass_brigade(f->next, bb);
966 /* initialize deflate output buffer */
967 ctx->stream.next_out = ctx->buffer;
968 ctx->stream.avail_out = c->bufferSize;
973 for (bkt = APR_BRIGADE_FIRST(bb);
974 bkt != APR_BRIGADE_SENTINEL(bb);
975 bkt = APR_BUCKET_NEXT(bkt))
980 /* If we actually see the EOS, that means we screwed up! */
981 /* no it doesn't - not in a HEAD or 204/304 */
982 if (APR_BUCKET_IS_EOS(bkt)) {
983 inflateEnd(&ctx->stream);
984 return ap_pass_brigade(f->next, bb);
987 if (APR_BUCKET_IS_FLUSH(bkt)) {
988 apr_bucket *tmp_heap;
989 zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
991 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
992 "Inflate error %d on flush", zRC);
993 inflateEnd(&ctx->stream);
997 ctx->stream.next_out = ctx->buffer;
998 len = c->bufferSize - ctx->stream.avail_out;
1000 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
1001 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
1002 NULL, f->c->bucket_alloc);
1003 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
1004 ctx->stream.avail_out = c->bufferSize;
1006 /* Move everything to the returning brigade. */
1007 APR_BUCKET_REMOVE(bkt);
1012 apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
1014 /* first bucket contains zlib header */
1015 if (!deflate_init++) {
1017 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1018 "Insufficient data for inflate");
1019 return APR_EGENERAL;
1022 zlib_method = data[2];
1023 zlib_flags = data[3];
1024 if (zlib_method != Z_DEFLATED) {
1025 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1026 "inflate: data not deflated!");
1027 ap_remove_output_filter(f);
1028 return ap_pass_brigade(f->next, bb);
1030 if (data[0] != deflate_magic[0] ||
1031 data[1] != deflate_magic[1] ||
1032 (zlib_flags & RESERVED) != 0) {
1033 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1034 "inflate: bad header");
1035 return APR_EGENERAL ;
1040 if (zlib_flags & EXTRA_FIELD) {
1041 unsigned int bytes = (unsigned int)(data[0]);
1042 bytes += ((unsigned int)(data[1])) << 8;
1045 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1046 "inflate: extra field too big (not "
1048 return APR_EGENERAL;
1053 if (zlib_flags & ORIG_NAME) {
1054 while (len-- && *data++);
1056 if (zlib_flags & COMMENT) {
1057 while (len-- && *data++);
1059 if (zlib_flags & HEAD_CRC) {
1065 /* pass through zlib inflate. */
1066 ctx->stream.next_in = (unsigned char *)data;
1067 ctx->stream.avail_in = len;
1071 while (ctx->stream.avail_in != 0) {
1072 if (ctx->stream.avail_out == 0) {
1073 apr_bucket *tmp_heap;
1074 ctx->stream.next_out = ctx->buffer;
1075 len = c->bufferSize - ctx->stream.avail_out;
1077 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
1078 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
1079 NULL, f->c->bucket_alloc);
1080 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
1081 ctx->stream.avail_out = c->bufferSize;
1084 zRC = inflate(&ctx->stream, Z_NO_FLUSH);
1086 if (zRC == Z_STREAM_END) {
1091 inflateEnd(&ctx->stream);
1092 return APR_EGENERAL;
1095 if (zRC == Z_STREAM_END) {
1096 apr_bucket *tmp_heap, *eos;
1098 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1099 "Zlib: Inflated %ld to %ld : URL %s",
1100 ctx->stream.total_in, ctx->stream.total_out,
1103 len = c->bufferSize - ctx->stream.avail_out;
1105 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
1106 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
1107 NULL, f->c->bucket_alloc);
1108 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
1109 ctx->stream.avail_out = c->bufferSize;
1111 /* Is the remaining 8 bytes already in the avail stream? */
1112 if (ctx->stream.avail_in >= 8) {
1113 unsigned long compCRC, compLen;
1114 compCRC = getLong(ctx->stream.next_in);
1115 if (ctx->crc != compCRC) {
1116 inflateEnd(&ctx->stream);
1117 return APR_EGENERAL;
1119 ctx->stream.next_in += 4;
1120 compLen = getLong(ctx->stream.next_in);
1121 if (ctx->stream.total_out != compLen) {
1122 inflateEnd(&ctx->stream);
1123 return APR_EGENERAL;
1127 /* FIXME: We need to grab the 8 verification bytes
1129 inflateEnd(&ctx->stream);
1130 return APR_EGENERAL;
1133 inflateEnd(&ctx->stream);
1135 eos = apr_bucket_eos_create(f->c->bucket_alloc);
1136 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos);
1142 rv = ap_pass_brigade(f->next, ctx->proc_bb);
1143 apr_brigade_cleanup(ctx->proc_bb);
1147 static void register_hooks(apr_pool_t *p)
1149 ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL,
1150 AP_FTYPE_CONTENT_SET);
1151 ap_register_output_filter("INFLATE", inflate_out_filter, NULL,
1152 AP_FTYPE_RESOURCE-1);
1153 ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL,
1154 AP_FTYPE_CONTENT_SET);
1157 static const command_rec deflate_filter_cmds[] = {
1158 AP_INIT_TAKE12("DeflateFilterNote", deflate_set_note, NULL, RSRC_CONF,
1159 "Set a note to report on compression ratio"),
1160 AP_INIT_TAKE1("DeflateWindowSize", deflate_set_window_size, NULL,
1161 RSRC_CONF, "Set the Deflate window size (1-15)"),
1162 AP_INIT_TAKE1("DeflateBufferSize", deflate_set_buffer_size, NULL, RSRC_CONF,
1163 "Set the Deflate Buffer Size"),
1164 AP_INIT_TAKE1("DeflateMemLevel", deflate_set_memlevel, NULL, RSRC_CONF,
1165 "Set the Deflate Memory Level (1-9)"),
1166 AP_INIT_TAKE1("DeflateCompressionLevel", deflate_set_compressionlevel, NULL, RSRC_CONF,
1167 "Set the Deflate Compression Level (1-9)"),
1171 module AP_MODULE_DECLARE_DATA deflate_module = {
1172 STANDARD20_MODULE_STUFF,
1173 NULL, /* dir config creater */
1174 NULL, /* dir merger --- default is to override */
1175 create_deflate_server_config, /* server config */
1176 NULL, /* merge server config */
1177 deflate_filter_cmds, /* command table */
1178 register_hooks /* register hooks */