1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * mod_deflate.c: Perform deflate content-encoding on the fly
20 * Written by Ian Holsman, Justin Erenkrantz, and Nick Kew
24 * Portions of this software are based upon zlib code by Jean-loup Gailly
25 * (zlib functions gz_open and gzwrite, check_header)
29 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
30 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
31 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
32 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
33 #define COMMENT 0x10 /* bit 4 set: file comment present */
34 #define RESERVED 0xE0 /* bits 5..7: reserved */
38 #include "http_config.h"
40 #include "apr_strings.h"
41 #include "apr_general.h"
42 #include "util_filter.h"
43 #include "apr_buckets.h"
44 #include "http_request.h"
45 #define APR_WANT_STRFUNC
50 static const char deflateFilterName[] = "DEFLATE";
51 module AP_MODULE_DECLARE_DATA deflate_module;
53 typedef struct deflate_filter_config_t
58 apr_size_t bufferSize;
59 char *note_ratio_name;
60 char *note_input_name;
61 char *note_output_name;
62 } deflate_filter_config;
64 /* RFC 1952 Section 2.3 defines the gzip header:
66 * +---+---+---+---+---+---+---+---+---+---+
67 * |ID1|ID2|CM |FLG| MTIME |XFL|OS |
68 * +---+---+---+---+---+---+---+---+---+---+
70 static const char gzip_header[10] =
71 { '\037', '\213', Z_DEFLATED, 0,
72 0, 0, 0, 0, /* mtime */
73 0, 0x03 /* Unix OS_CODE */
77 static const char deflate_magic[2] = { '\037', '\213' };
79 /* windowsize is negative to suppress Zlib header */
80 #define DEFAULT_COMPRESSION Z_DEFAULT_COMPRESSION
81 #define DEFAULT_WINDOWSIZE -15
82 #define DEFAULT_MEMLEVEL 9
83 #define DEFAULT_BUFFERSIZE 8096
85 /* Outputs a long in LSB order to the given file
86 * only the bottom 4 bits are required for the deflate file format.
88 static void putLong(unsigned char *string, unsigned long x)
90 string[0] = (unsigned char)(x & 0xff);
91 string[1] = (unsigned char)((x & 0xff00) >> 8);
92 string[2] = (unsigned char)((x & 0xff0000) >> 16);
93 string[3] = (unsigned char)((x & 0xff000000) >> 24);
96 /* Inputs a string and returns a long.
98 static unsigned long getLong(unsigned char *string)
100 return ((unsigned long)string[0])
101 | (((unsigned long)string[1]) << 8)
102 | (((unsigned long)string[2]) << 16)
103 | (((unsigned long)string[3]) << 24);
106 static void *create_deflate_server_config(apr_pool_t *p, server_rec *s)
108 deflate_filter_config *c = apr_pcalloc(p, sizeof *c);
110 c->memlevel = DEFAULT_MEMLEVEL;
111 c->windowSize = DEFAULT_WINDOWSIZE;
112 c->bufferSize = DEFAULT_BUFFERSIZE;
113 c->compressionlevel = DEFAULT_COMPRESSION;
118 static const char *deflate_set_window_size(cmd_parms *cmd, void *dummy,
121 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
128 return "DeflateWindowSize must be between 1 and 15";
130 c->windowSize = i * -1;
135 static const char *deflate_set_buffer_size(cmd_parms *cmd, void *dummy,
138 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
143 return "DeflateBufferSize should be positive";
146 c->bufferSize = (apr_size_t)n;
150 static const char *deflate_set_note(cmd_parms *cmd, void *dummy,
151 const char *arg1, const char *arg2)
153 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
157 c->note_ratio_name = apr_pstrdup(cmd->pool, arg1);
159 else if (!strcasecmp(arg1, "ratio")) {
160 c->note_ratio_name = apr_pstrdup(cmd->pool, arg2);
162 else if (!strcasecmp(arg1, "input")) {
163 c->note_input_name = apr_pstrdup(cmd->pool, arg2);
165 else if (!strcasecmp(arg1, "output")) {
166 c->note_output_name = apr_pstrdup(cmd->pool, arg2);
169 return apr_psprintf(cmd->pool, "Unknown note type %s", arg1);
175 static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy,
178 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
185 return "DeflateMemLevel must be between 1 and 9";
192 static const char *deflate_set_compressionlevel(cmd_parms *cmd, void *dummy,
195 deflate_filter_config *c = ap_get_module_config(cmd->server->module_config,
202 return "Compression Level must be between 1 and 9";
204 c->compressionlevel = i;
209 typedef struct deflate_ctx_t
212 unsigned char *buffer;
214 apr_bucket_brigade *bb, *proc_bb;
217 static int flush_libz_buffer(deflate_ctx *ctx, deflate_filter_config *c,
218 struct apr_bucket_alloc_t *bucket_alloc,
219 int (*libz_func)(z_streamp, int), int flush)
223 unsigned int deflate_len;
227 deflate_len = c->bufferSize - ctx->stream.avail_out;
229 if (deflate_len != 0) {
230 b = apr_bucket_heap_create((char *)ctx->buffer,
233 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
234 ctx->stream.next_out = ctx->buffer;
235 ctx->stream.avail_out = c->bufferSize;
241 zRC = libz_func(&ctx->stream, flush);
243 if (deflate_len == 0 && zRC == Z_BUF_ERROR)
246 done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END);
248 if (zRC != Z_OK && zRC != Z_STREAM_END)
254 static apr_status_t deflate_out_filter(ap_filter_t *f,
255 apr_bucket_brigade *bb)
258 request_rec *r = f->r;
259 deflate_ctx *ctx = f->ctx;
261 deflate_filter_config *c = ap_get_module_config(r->server->module_config,
264 /* Do nothing if asked to filter nothing. */
265 if (APR_BRIGADE_EMPTY(bb)) {
269 /* If we don't have a context, we need to ensure that it is okay to send
270 * the deflated content. If we have a context, that means we've done
271 * this before and we liked it.
272 * This could be not so nice if we always fail. But, if we succeed,
273 * we're in better shape.
277 const char *encoding;
279 /* only work on main request/no subrequests */
280 if (r->main != NULL) {
281 ap_remove_output_filter(f);
282 return ap_pass_brigade(f->next, bb);
285 /* some browsers might have problems, so set no-gzip
286 * (with browsermatch) for them
288 if (apr_table_get(r->subprocess_env, "no-gzip")) {
289 ap_remove_output_filter(f);
290 return ap_pass_brigade(f->next, bb);
293 /* Some browsers might have problems with content types
294 * other than text/html, so set gzip-only-text/html
295 * (with browsermatch) for them
297 if (r->content_type == NULL
298 || strncmp(r->content_type, "text/html", 9)) {
299 const char *env_value = apr_table_get(r->subprocess_env,
300 "gzip-only-text/html");
301 if ( env_value && (strcmp(env_value,"1") == 0) ) {
302 ap_remove_output_filter(f);
303 return ap_pass_brigade(f->next, bb);
307 /* Let's see what our current Content-Encoding is.
308 * If it's already encoded, don't compress again.
309 * (We could, but let's not.)
311 encoding = apr_table_get(r->headers_out, "Content-Encoding");
315 err_enc = apr_table_get(r->err_headers_out, "Content-Encoding");
317 encoding = apr_pstrcat(r->pool, encoding, ",", err_enc, NULL);
321 encoding = apr_table_get(r->err_headers_out, "Content-Encoding");
324 if (r->content_encoding) {
325 encoding = encoding ? apr_pstrcat(r->pool, encoding, ",",
326 r->content_encoding, NULL)
327 : r->content_encoding;
331 const char *tmp = encoding;
333 token = ap_get_token(r->pool, &tmp, 0);
334 while (token && *token) {
335 /* stolen from mod_negotiation: */
336 if (strcmp(token, "identity") && strcmp(token, "7bit") &&
337 strcmp(token, "8bit") && strcmp(token, "binary")) {
339 ap_remove_output_filter(f);
340 return ap_pass_brigade(f->next, bb);
343 /* Otherwise, skip token */
347 token = (*tmp) ? ap_get_token(r->pool, &tmp, 0) : NULL;
351 /* Even if we don't accept this request based on it not having
352 * the Accept-Encoding, we need to note that we were looking
353 * for this header and downstream proxies should be aware of that.
355 apr_table_mergen(r->headers_out, "Vary", "Accept-Encoding");
357 /* force-gzip will just force it out regardless if the browser
358 * can actually do anything with it.
360 if (!apr_table_get(r->subprocess_env, "force-gzip")) {
362 /* if they don't have the line, then they can't play */
363 accepts = apr_table_get(r->headers_in, "Accept-Encoding");
364 if (accepts == NULL) {
365 ap_remove_output_filter(f);
366 return ap_pass_brigade(f->next, bb);
369 token = ap_get_token(r->pool, &accepts, 0);
370 while (token && token[0] && strcasecmp(token, "gzip")) {
371 /* skip parameters, XXX: ;q=foo evaluation? */
372 while (*accepts == ';') {
374 token = ap_get_token(r->pool, &accepts, 1);
377 /* retrieve next token */
378 if (*accepts == ',') {
381 token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL;
384 /* No acceptable token found. */
385 if (token == NULL || token[0] == '\0') {
386 ap_remove_output_filter(f);
387 return ap_pass_brigade(f->next, bb);
391 /* For a 304 or 204 response there is no entity included in
392 * the response and hence nothing to deflate. */
393 if (r->status == HTTP_NOT_MODIFIED || r->status == HTTP_NO_CONTENT) {
394 ap_remove_output_filter(f);
395 return ap_pass_brigade(f->next, bb);
398 /* We're cool with filtering this. */
399 ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
400 ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
401 ctx->buffer = apr_palloc(r->pool, c->bufferSize);
403 zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED,
404 c->windowSize, c->memlevel,
408 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
409 "unable to init Zlib: "
410 "deflateInit2 returned %d: URL %s",
413 * Remove ourselves as it does not make sense to return:
414 * We are not able to init libz and pass data down the chain
417 ap_remove_output_filter(f);
418 return ap_pass_brigade(f->next, bb);
421 /* add immortal gzip header */
422 e = apr_bucket_immortal_create(gzip_header, sizeof gzip_header,
424 APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
426 /* If the entire Content-Encoding is "identity", we can replace it. */
427 if (!encoding || !strcasecmp(encoding, "identity")) {
428 apr_table_setn(r->headers_out, "Content-Encoding", "gzip");
431 apr_table_mergen(r->headers_out, "Content-Encoding", "gzip");
433 apr_table_unset(r->headers_out, "Content-Length");
435 /* initialize deflate output buffer */
436 ctx->stream.next_out = ctx->buffer;
437 ctx->stream.avail_out = c->bufferSize;
440 while (!APR_BRIGADE_EMPTY(bb))
446 e = APR_BRIGADE_FIRST(bb);
448 if (APR_BUCKET_IS_EOS(e)) {
451 ctx->stream.avail_in = 0; /* should be zero already anyway */
452 /* flush the remaining data from the zlib buffers */
453 flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, Z_FINISH);
455 buf = apr_palloc(r->pool, 8);
456 putLong((unsigned char *)&buf[0], ctx->crc);
457 putLong((unsigned char *)&buf[4], ctx->stream.total_in);
459 b = apr_bucket_pool_create(buf, 8, r->pool, f->c->bucket_alloc);
460 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
461 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
462 "Zlib: Compressed %ld to %ld : URL %s",
463 ctx->stream.total_in, ctx->stream.total_out, r->uri);
465 /* leave notes for logging */
466 if (c->note_input_name) {
467 apr_table_setn(r->notes, c->note_input_name,
468 (ctx->stream.total_in > 0)
469 ? apr_off_t_toa(r->pool,
470 ctx->stream.total_in)
474 if (c->note_output_name) {
475 apr_table_setn(r->notes, c->note_output_name,
476 (ctx->stream.total_in > 0)
477 ? apr_off_t_toa(r->pool,
478 ctx->stream.total_out)
482 if (c->note_ratio_name) {
483 apr_table_setn(r->notes, c->note_ratio_name,
484 (ctx->stream.total_in > 0)
486 (int)(ctx->stream.total_out
488 / ctx->stream.total_in))
492 deflateEnd(&ctx->stream);
494 /* Remove EOS from the old list, and insert into the new. */
495 APR_BUCKET_REMOVE(e);
496 APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
498 /* Okay, we've seen the EOS.
499 * Time to pass it along down the chain.
501 return ap_pass_brigade(f->next, ctx->bb);
504 if (APR_BUCKET_IS_FLUSH(e)) {
507 /* flush the remaining data from the zlib buffers */
508 zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate,
512 * Things screwed up. It is likely that we never return into
513 * this filter, so clean libz's internal structures to avoid a
514 * possible memory leak.
516 deflateEnd(&ctx->stream);
517 /* Remove ourselves to ensure that we really NEVER come back */
518 ap_remove_output_filter(f);
522 /* Remove flush bucket from old brigade anf insert into the new. */
523 APR_BUCKET_REMOVE(e);
524 APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
525 rv = ap_pass_brigade(f->next, ctx->bb);
526 if (rv != APR_SUCCESS) {
528 * Things screwed up. It is likely that we never return into
529 * this filter, so clean libz's internal structures to avoid a
530 * possible memory leak.
532 deflateEnd(&ctx->stream);
533 /* Remove ourselves to ensure that we really NEVER come back */
534 ap_remove_output_filter(f);
541 apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
543 /* This crc32 function is from zlib. */
544 ctx->crc = crc32(ctx->crc, (const Bytef *)data, len);
547 ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness,
548 * but we'll just have to
550 ctx->stream.avail_in = len;
552 while (ctx->stream.avail_in != 0) {
553 if (ctx->stream.avail_out == 0) {
556 ctx->stream.next_out = ctx->buffer;
557 len = c->bufferSize - ctx->stream.avail_out;
559 b = apr_bucket_heap_create((char *)ctx->buffer, len,
560 NULL, f->c->bucket_alloc);
561 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
562 ctx->stream.avail_out = c->bufferSize;
563 /* Send what we have right now to the next filter. */
564 rv = ap_pass_brigade(f->next, ctx->bb);
565 if (rv != APR_SUCCESS) {
567 * Things screwed up. It is likely that we never return into
568 * this filter, so clean libz's internal structures to avoid a
569 * possible memory leak.
571 deflateEnd(&ctx->stream);
572 /* Remove ourselves to ensure that we really NEVER come back */
573 ap_remove_output_filter(f);
578 zRC = deflate(&(ctx->stream), Z_NO_FLUSH);
582 * Things screwed up. It is likely that we never return into
583 * this filter, so clean libz's internal structures to avoid a
584 * possible memory leak.
586 deflateEnd(&ctx->stream);
587 /* Remove ourselves to ensure that we really NEVER come back */
588 ap_remove_output_filter(f);
593 apr_bucket_delete(e);
596 apr_brigade_cleanup(bb);
600 /* This is the deflate input filter (inflates). */
601 static apr_status_t deflate_in_filter(ap_filter_t *f,
602 apr_bucket_brigade *bb,
603 ap_input_mode_t mode,
604 apr_read_type_e block,
608 request_rec *r = f->r;
609 deflate_ctx *ctx = f->ctx;
612 deflate_filter_config *c;
614 /* just get out of the way of things we don't want. */
615 if (mode != AP_MODE_READBYTES) {
616 return ap_get_brigade(f->next, bb, mode, block, readbytes);
619 c = ap_get_module_config(r->server->module_config, &deflate_module);
623 char *token, deflate_hdr[10];
624 const char *encoding;
627 /* only work on main request/no subrequests */
628 if (!ap_is_initial_req(r)) {
629 ap_remove_input_filter(f);
630 return ap_get_brigade(f->next, bb, mode, block, readbytes);
633 /* Let's see what our current Content-Encoding is.
634 * If gzip is present, don't gzip again. (We could, but let's not.)
636 encoding = apr_table_get(r->headers_in, "Content-Encoding");
638 const char *tmp = encoding;
640 token = ap_get_token(r->pool, &tmp, 0);
641 while (token && token[0]) {
642 if (!strcasecmp(token, "gzip")) {
646 /* Otherwise, skip token */
648 token = ap_get_token(r->pool, &tmp, 0);
653 ap_remove_input_filter(f);
654 return ap_get_brigade(f->next, bb, mode, block, readbytes);
657 f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
658 ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
659 ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
660 ctx->buffer = apr_palloc(r->pool, c->bufferSize);
662 rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10);
663 if (rv != APR_SUCCESS) {
668 rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len);
669 if (rv != APR_SUCCESS) {
673 /* We didn't get the magic bytes. */
675 deflate_hdr[0] != deflate_magic[0] ||
676 deflate_hdr[1] != deflate_magic[1]) {
680 /* We can't handle flags for now. */
681 if (deflate_hdr[3] != 0) {
685 zRC = inflateInit2(&ctx->stream, c->windowSize);
689 inflateEnd(&ctx->stream);
690 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
691 "unable to init Zlib: "
692 "inflateInit2 returned %d: URL %s",
694 ap_remove_input_filter(f);
695 return ap_get_brigade(f->next, bb, mode, block, readbytes);
698 /* initialize deflate output buffer */
699 ctx->stream.next_out = ctx->buffer;
700 ctx->stream.avail_out = c->bufferSize;
702 apr_brigade_cleanup(ctx->bb);
705 if (APR_BRIGADE_EMPTY(ctx->proc_bb)) {
706 rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
708 if (rv != APR_SUCCESS) {
709 /* What about APR_EAGAIN errors? */
710 inflateEnd(&ctx->stream);
714 for (bkt = APR_BRIGADE_FIRST(ctx->bb);
715 bkt != APR_BRIGADE_SENTINEL(ctx->bb);
716 bkt = APR_BUCKET_NEXT(bkt))
721 /* If we actually see the EOS, that means we screwed up! */
722 if (APR_BUCKET_IS_EOS(bkt)) {
723 inflateEnd(&ctx->stream);
727 if (APR_BUCKET_IS_FLUSH(bkt)) {
728 apr_bucket *tmp_heap;
729 zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
731 inflateEnd(&ctx->stream);
735 ctx->stream.next_out = ctx->buffer;
736 len = c->bufferSize - ctx->stream.avail_out;
738 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
739 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
740 NULL, f->c->bucket_alloc);
741 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
742 ctx->stream.avail_out = c->bufferSize;
744 /* Move everything to the returning brigade. */
745 APR_BUCKET_REMOVE(bkt);
746 APR_BRIGADE_CONCAT(bb, ctx->bb);
751 apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
753 /* pass through zlib inflate. */
754 ctx->stream.next_in = (unsigned char *)data;
755 ctx->stream.avail_in = len;
759 while (ctx->stream.avail_in != 0) {
760 if (ctx->stream.avail_out == 0) {
761 apr_bucket *tmp_heap;
762 ctx->stream.next_out = ctx->buffer;
763 len = c->bufferSize - ctx->stream.avail_out;
765 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
766 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
767 NULL, f->c->bucket_alloc);
768 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
769 ctx->stream.avail_out = c->bufferSize;
772 zRC = inflate(&ctx->stream, Z_NO_FLUSH);
774 if (zRC == Z_STREAM_END) {
779 inflateEnd(&ctx->stream);
783 if (zRC == Z_STREAM_END) {
784 apr_bucket *tmp_heap, *eos;
786 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
787 "Zlib: Inflated %ld to %ld : URL %s",
788 ctx->stream.total_in, ctx->stream.total_out,
791 len = c->bufferSize - ctx->stream.avail_out;
793 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
794 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
795 NULL, f->c->bucket_alloc);
796 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
797 ctx->stream.avail_out = c->bufferSize;
799 /* Is the remaining 8 bytes already in the avail stream? */
800 if (ctx->stream.avail_in >= 8) {
801 unsigned long compCRC, compLen;
802 compCRC = getLong(ctx->stream.next_in);
803 if (ctx->crc != compCRC) {
804 inflateEnd(&ctx->stream);
807 ctx->stream.next_in += 4;
808 compLen = getLong(ctx->stream.next_in);
809 if (ctx->stream.total_out != compLen) {
810 inflateEnd(&ctx->stream);
815 /* FIXME: We need to grab the 8 verification bytes
817 inflateEnd(&ctx->stream);
821 inflateEnd(&ctx->stream);
823 eos = apr_bucket_eos_create(f->c->bucket_alloc);
824 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos);
829 apr_brigade_cleanup(ctx->bb);
832 /* If we are about to return nothing for a 'blocking' read and we have
833 * some data in our zlib buffer, flush it out so we can return something.
835 if (block == APR_BLOCK_READ &&
836 APR_BRIGADE_EMPTY(ctx->proc_bb) &&
837 ctx->stream.avail_out < c->bufferSize) {
838 apr_bucket *tmp_heap;
840 ctx->stream.next_out = ctx->buffer;
841 len = c->bufferSize - ctx->stream.avail_out;
843 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
844 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
845 NULL, f->c->bucket_alloc);
846 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
847 ctx->stream.avail_out = c->bufferSize;
850 if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) {
851 apr_bucket_brigade *newbb;
853 /* May return APR_INCOMPLETE which is fine by us. */
854 apr_brigade_partition(ctx->proc_bb, readbytes, &bkt);
856 newbb = apr_brigade_split(ctx->proc_bb, bkt);
857 APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
858 APR_BRIGADE_CONCAT(ctx->proc_bb, newbb);
865 /* Filter to inflate for a content-transforming proxy. */
866 static apr_status_t inflate_out_filter(ap_filter_t *f,
867 apr_bucket_brigade *bb)
871 int deflate_init = 1;
873 request_rec *r = f->r;
874 deflate_ctx *ctx = f->ctx;
877 deflate_filter_config *c;
879 /* Do nothing if asked to filter nothing. */
880 if (APR_BRIGADE_EMPTY(bb)) {
884 c = ap_get_module_config(r->server->module_config, &deflate_module);
889 const char *encoding;
891 /* only work on main request/no subrequests */
892 if (!ap_is_initial_req(r)) {
893 ap_remove_output_filter(f);
894 return ap_pass_brigade(f->next, bb);
897 /* Let's see what our current Content-Encoding is.
898 * If gzip is present, don't gzip again. (We could, but let's not.)
900 encoding = apr_table_get(r->headers_out, "Content-Encoding");
902 const char *tmp = encoding;
904 token = ap_get_token(r->pool, &tmp, 0);
905 while (token && token[0]) {
906 if (!strcasecmp(token, "gzip")) {
910 /* Otherwise, skip token */
912 token = ap_get_token(r->pool, &tmp, 0);
917 ap_remove_output_filter(f);
918 return ap_pass_brigade(f->next, bb);
920 apr_table_unset(r->headers_out, "Content-Encoding");
922 /* No need to inflate HEAD or 204/304 */
923 if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bb))) {
924 ap_remove_output_filter(f);
925 return ap_pass_brigade(f->next, bb);
929 f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
930 ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
931 ctx->buffer = apr_palloc(r->pool, c->bufferSize);
934 zRC = inflateInit2(&ctx->stream, c->windowSize);
938 inflateEnd(&ctx->stream);
939 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
940 "unable to init Zlib: "
941 "inflateInit2 returned %d: URL %s",
943 ap_remove_output_filter(f);
944 return ap_pass_brigade(f->next, bb);
947 /* initialize deflate output buffer */
948 ctx->stream.next_out = ctx->buffer;
949 ctx->stream.avail_out = c->bufferSize;
954 for (bkt = APR_BRIGADE_FIRST(bb);
955 bkt != APR_BRIGADE_SENTINEL(bb);
956 bkt = APR_BUCKET_NEXT(bkt))
961 /* If we actually see the EOS, that means we screwed up! */
962 /* no it doesn't - not in a HEAD or 204/304 */
963 if (APR_BUCKET_IS_EOS(bkt)) {
964 inflateEnd(&ctx->stream);
965 return ap_pass_brigade(f->next, bb);
968 if (APR_BUCKET_IS_FLUSH(bkt)) {
973 apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
975 /* first bucket contains zlib header */
976 if (!deflate_init++) {
978 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
979 "Insufficient data for inflate");
983 zlib_method = data[2];
984 zlib_flags = data[3];
985 if (zlib_method != Z_DEFLATED) {
986 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
987 "inflate: data not deflated!");
988 ap_remove_output_filter(f);
989 return ap_pass_brigade(f->next, bb);
991 if (data[0] != deflate_magic[0] ||
992 data[1] != deflate_magic[1] ||
993 (zlib_flags & RESERVED) != 0) {
994 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
995 "inflate: bad header");
996 return APR_EGENERAL ;
1001 if (zlib_flags & EXTRA_FIELD) {
1002 unsigned int bytes = (unsigned int)(data[0]);
1003 bytes += ((unsigned int)(data[1])) << 8;
1006 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1007 "inflate: extra field too big (not "
1009 return APR_EGENERAL;
1014 if (zlib_flags & ORIG_NAME) {
1015 while (len-- && *data++);
1017 if (zlib_flags & COMMENT) {
1018 while (len-- && *data++);
1020 if (zlib_flags & HEAD_CRC) {
1026 /* pass through zlib inflate. */
1027 ctx->stream.next_in = (unsigned char *)data;
1028 ctx->stream.avail_in = len;
1032 while (ctx->stream.avail_in != 0) {
1033 if (ctx->stream.avail_out == 0) {
1034 apr_bucket *tmp_heap;
1035 ctx->stream.next_out = ctx->buffer;
1036 len = c->bufferSize - ctx->stream.avail_out;
1038 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
1039 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
1040 NULL, f->c->bucket_alloc);
1041 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
1042 ctx->stream.avail_out = c->bufferSize;
1045 zRC = inflate(&ctx->stream, Z_NO_FLUSH);
1047 if (zRC == Z_STREAM_END) {
1052 inflateEnd(&ctx->stream);
1053 return APR_EGENERAL;
1056 if (zRC == Z_STREAM_END) {
1057 apr_bucket *tmp_heap, *eos;
1059 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1060 "Zlib: Inflated %ld to %ld : URL %s",
1061 ctx->stream.total_in, ctx->stream.total_out,
1064 len = c->bufferSize - ctx->stream.avail_out;
1066 ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
1067 tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
1068 NULL, f->c->bucket_alloc);
1069 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
1070 ctx->stream.avail_out = c->bufferSize;
1072 /* Is the remaining 8 bytes already in the avail stream? */
1073 if (ctx->stream.avail_in >= 8) {
1074 unsigned long compCRC, compLen;
1075 compCRC = getLong(ctx->stream.next_in);
1076 if (ctx->crc != compCRC) {
1077 inflateEnd(&ctx->stream);
1078 return APR_EGENERAL;
1080 ctx->stream.next_in += 4;
1081 compLen = getLong(ctx->stream.next_in);
1082 if (ctx->stream.total_out != compLen) {
1083 inflateEnd(&ctx->stream);
1084 return APR_EGENERAL;
1088 /* FIXME: We need to grab the 8 verification bytes
1090 inflateEnd(&ctx->stream);
1091 return APR_EGENERAL;
1094 inflateEnd(&ctx->stream);
1096 eos = apr_bucket_eos_create(f->c->bucket_alloc);
1097 APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos);
1103 rv = ap_pass_brigade(f->next, ctx->proc_bb);
1104 apr_brigade_cleanup(ctx->proc_bb);
1108 #define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH
1109 static void register_hooks(apr_pool_t *p)
1111 ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL,
1112 AP_FTYPE_CONTENT_SET);
1113 ap_register_output_filter("INFLATE", inflate_out_filter, NULL,
1114 AP_FTYPE_RESOURCE-1);
1115 ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL,
1116 AP_FTYPE_CONTENT_SET);
1119 static const command_rec deflate_filter_cmds[] = {
1120 AP_INIT_TAKE12("DeflateFilterNote", deflate_set_note, NULL, RSRC_CONF,
1121 "Set a note to report on compression ratio"),
1122 AP_INIT_TAKE1("DeflateWindowSize", deflate_set_window_size, NULL,
1123 RSRC_CONF, "Set the Deflate window size (1-15)"),
1124 AP_INIT_TAKE1("DeflateBufferSize", deflate_set_buffer_size, NULL, RSRC_CONF,
1125 "Set the Deflate Buffer Size"),
1126 AP_INIT_TAKE1("DeflateMemLevel", deflate_set_memlevel, NULL, RSRC_CONF,
1127 "Set the Deflate Memory Level (1-9)"),
1128 AP_INIT_TAKE1("DeflateCompressionLevel", deflate_set_compressionlevel, NULL, RSRC_CONF,
1129 "Set the Deflate Compression Level (1-9)"),
1133 module AP_MODULE_DECLARE_DATA deflate_module = {
1134 STANDARD20_MODULE_STUFF,
1135 NULL, /* dir config creater */
1136 NULL, /* dir merger --- default is to override */
1137 create_deflate_server_config, /* server config */
1138 NULL, /* merge server config */
1139 deflate_filter_cmds, /* command table */
1140 register_hooks /* register hooks */