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 apr_status_t deflate_out_filter(ap_filter_t *f,
218
apr_bucket_brigade *bb)
221
request_rec *r = f->r;
222
deflate_ctx *ctx = f->ctx;
224
deflate_filter_config *c = ap_get_module_config(r->server->module_config,
227
/* Do nothing if asked to filter nothing. */
228
if (APR_BRIGADE_EMPTY(bb)) {
232
/* If we don't have a context, we need to ensure that it is okay to send
233
* the deflated content. If we have a context, that means we've done
234
* this before and we liked it.
235
* This could be not so nice if we always fail. But, if we succeed,
236
* we're in better shape.
240
const char *encoding;
242
/* only work on main request/no subrequests */
243
if (r->main != NULL) {
244
ap_remove_output_filter(f);
245
return ap_pass_brigade(f->next, bb);
248
/* some browsers might have problems, so set no-gzip
249
* (with browsermatch) for them
251
if (apr_table_get(r->subprocess_env, "no-gzip")) {
252
ap_remove_output_filter(f);
253
return ap_pass_brigade(f->next, bb);
256
/* Some browsers might have problems with content types
257
* other than text/html, so set gzip-only-text/html
258
* (with browsermatch) for them
260
if (r->content_type == NULL
261
|| strncmp(r->content_type, "text/html", 9)) {
262
const char *env_value = apr_table_get(r->subprocess_env,
263
"gzip-only-text/html");
264
if ( env_value && (strcmp(env_value,"1") == 0) ) {
265
ap_remove_output_filter(f);
266
return ap_pass_brigade(f->next, bb);
270
/* Let's see what our current Content-Encoding is.
271
* If it's already encoded, don't compress again.
272
* (We could, but let's not.)
274
encoding = apr_table_get(r->headers_out, "Content-Encoding");
278
err_enc = apr_table_get(r->err_headers_out, "Content-Encoding");
280
encoding = apr_pstrcat(r->pool, encoding, ",", err_enc, NULL);
284
encoding = apr_table_get(r->err_headers_out, "Content-Encoding");
287
if (r->content_encoding) {
288
encoding = encoding ? apr_pstrcat(r->pool, encoding, ",",
289
r->content_encoding, NULL)
290
: r->content_encoding;
294
const char *tmp = encoding;
296
token = ap_get_token(r->pool, &tmp, 0);
297
while (token && *token) {
298
/* stolen from mod_negotiation: */
299
if (strcmp(token, "identity") && strcmp(token, "7bit") &&
300
strcmp(token, "8bit") && strcmp(token, "binary")) {
302
ap_remove_output_filter(f);
303
return ap_pass_brigade(f->next, bb);
306
/* Otherwise, skip token */
310
token = (*tmp) ? ap_get_token(r->pool, &tmp, 0) : NULL;
314
/* Even if we don't accept this request based on it not having
315
* the Accept-Encoding, we need to note that we were looking
316
* for this header and downstream proxies should be aware of that.
318
apr_table_mergen(r->headers_out, "Vary", "Accept-Encoding");
320
/* force-gzip will just force it out regardless if the browser
321
* can actually do anything with it.
323
if (!apr_table_get(r->subprocess_env, "force-gzip")) {
325
/* if they don't have the line, then they can't play */
326
accepts = apr_table_get(r->headers_in, "Accept-Encoding");
327
if (accepts == NULL) {
328
ap_remove_output_filter(f);
329
return ap_pass_brigade(f->next, bb);
332
token = ap_get_token(r->pool, &accepts, 0);
333
while (token && token[0] && strcasecmp(token, "gzip")) {
334
/* skip parameters, XXX: ;q=foo evaluation? */
335
while (*accepts == ';') {
337
token = ap_get_token(r->pool, &accepts, 1);
340
/* retrieve next token */
341
if (*accepts == ',') {
344
token = (*accepts) ? ap_get_token(r->pool, &accepts, 0) : NULL;
347
/* No acceptable token found. */
348
if (token == NULL || token[0] == '\0') {
349
ap_remove_output_filter(f);
350
return ap_pass_brigade(f->next, bb);
354
/* For a 304 or 204 response there is no entity included in
355
* the response and hence nothing to deflate. */
356
if (r->status == HTTP_NOT_MODIFIED || r->status == HTTP_NO_CONTENT) {
357
ap_remove_output_filter(f);
358
return ap_pass_brigade(f->next, bb);
361
/* We're cool with filtering this. */
362
ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
363
ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
364
ctx->buffer = apr_palloc(r->pool, c->bufferSize);
366
zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED,
367
c->windowSize, c->memlevel,
372
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
373
"unable to init Zlib: "
374
"deflateInit2 returned %d: URL %s",
376
return ap_pass_brigade(f->next, bb);
379
/* add immortal gzip header */
380
e = apr_bucket_immortal_create(gzip_header, sizeof gzip_header,
382
APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
384
/* If the entire Content-Encoding is "identity", we can replace it. */
385
if (!encoding || !strcasecmp(encoding, "identity")) {
386
apr_table_setn(r->headers_out, "Content-Encoding", "gzip");
389
apr_table_mergen(r->headers_out, "Content-Encoding", "gzip");
391
apr_table_unset(r->headers_out, "Content-Length");
393
/* initialize deflate output buffer */
394
ctx->stream.next_out = ctx->buffer;
395
ctx->stream.avail_out = c->bufferSize;
398
while (!APR_BRIGADE_EMPTY(bb))
405
e = APR_BRIGADE_FIRST(bb);
407
if (APR_BUCKET_IS_EOS(e)) {
409
unsigned int deflate_len;
411
ctx->stream.avail_in = 0; /* should be zero already anyway */
413
deflate_len = c->bufferSize - ctx->stream.avail_out;
415
if (deflate_len != 0) {
416
b = apr_bucket_heap_create((char *)ctx->buffer,
419
APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
420
ctx->stream.next_out = ctx->buffer;
421
ctx->stream.avail_out = c->bufferSize;
428
zRC = deflate(&ctx->stream, Z_FINISH);
430
if (deflate_len == 0 && zRC == Z_BUF_ERROR) {
434
done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END);
436
if (zRC != Z_OK && zRC != Z_STREAM_END) {
441
buf = apr_palloc(r->pool, 8);
442
putLong((unsigned char *)&buf[0], ctx->crc);
443
putLong((unsigned char *)&buf[4], ctx->stream.total_in);
445
b = apr_bucket_pool_create(buf, 8, r->pool, f->c->bucket_alloc);
446
APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
447
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
448
"Zlib: Compressed %ld to %ld : URL %s",
449
ctx->stream.total_in, ctx->stream.total_out, r->uri);
451
/* leave notes for logging */
452
if (c->note_input_name) {
453
apr_table_setn(r->notes, c->note_input_name,
454
(ctx->stream.total_in > 0)
455
? apr_off_t_toa(r->pool,
456
ctx->stream.total_in)
460
if (c->note_output_name) {
461
apr_table_setn(r->notes, c->note_output_name,
462
(ctx->stream.total_in > 0)
463
? apr_off_t_toa(r->pool,
464
ctx->stream.total_out)
468
if (c->note_ratio_name) {
469
apr_table_setn(r->notes, c->note_ratio_name,
470
(ctx->stream.total_in > 0)
472
(int)(ctx->stream.total_out
474
/ ctx->stream.total_in))
478
deflateEnd(&ctx->stream);
480
/* Remove EOS from the old list, and insert into the new. */
481
APR_BUCKET_REMOVE(e);
482
APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
484
/* Okay, we've seen the EOS.
485
* Time to pass it along down the chain.
487
return ap_pass_brigade(f->next, ctx->bb);
490
if (APR_BUCKET_IS_FLUSH(e)) {
494
apr_bucket_delete(e);
496
if (ctx->stream.avail_in > 0) {
497
zRC = deflate(&(ctx->stream), Z_SYNC_FLUSH);
503
ctx->stream.next_out = ctx->buffer;
504
len = c->bufferSize - ctx->stream.avail_out;
506
b = apr_bucket_heap_create((char *)ctx->buffer, len,
507
NULL, f->c->bucket_alloc);
508
APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
509
ctx->stream.avail_out = c->bufferSize;
511
bkt = apr_bucket_flush_create(f->c->bucket_alloc);
512
APR_BRIGADE_INSERT_TAIL(ctx->bb, bkt);
513
rv = ap_pass_brigade(f->next, ctx->bb);
514
if (rv != APR_SUCCESS) {
521
apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
523
/* This crc32 function is from zlib. */
524
ctx->crc = crc32(ctx->crc, (const Bytef *)data, len);
527
ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness,
528
* but we'll just have to
530
ctx->stream.avail_in = len;
532
while (ctx->stream.avail_in != 0) {
533
if (ctx->stream.avail_out == 0) {
536
ctx->stream.next_out = ctx->buffer;
537
len = c->bufferSize - ctx->stream.avail_out;
539
b = apr_bucket_heap_create((char *)ctx->buffer, len,
540
NULL, f->c->bucket_alloc);
541
APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
542
ctx->stream.avail_out = c->bufferSize;
543
/* Send what we have right now to the next filter. */
544
rv = ap_pass_brigade(f->next, ctx->bb);
545
if (rv != APR_SUCCESS) {
550
zRC = deflate(&(ctx->stream), Z_NO_FLUSH);
556
apr_bucket_delete(e);
559
apr_brigade_cleanup(bb);
563
/* This is the deflate input filter (inflates). */
564
static apr_status_t deflate_in_filter(ap_filter_t *f,
565
apr_bucket_brigade *bb,
566
ap_input_mode_t mode,
567
apr_read_type_e block,
571
request_rec *r = f->r;
572
deflate_ctx *ctx = f->ctx;
575
deflate_filter_config *c;
577
/* just get out of the way of things we don't want. */
578
if (mode != AP_MODE_READBYTES) {
579
return ap_get_brigade(f->next, bb, mode, block, readbytes);
582
c = ap_get_module_config(r->server->module_config, &deflate_module);
586
char *token, deflate_hdr[10];
587
const char *encoding;
590
/* only work on main request/no subrequests */
591
if (!ap_is_initial_req(r)) {
592
ap_remove_input_filter(f);
593
return ap_get_brigade(f->next, bb, mode, block, readbytes);
596
/* Let's see what our current Content-Encoding is.
597
* If gzip is present, don't gzip again. (We could, but let's not.)
599
encoding = apr_table_get(r->headers_in, "Content-Encoding");
601
const char *tmp = encoding;
603
token = ap_get_token(r->pool, &tmp, 0);
604
while (token && token[0]) {
605
if (!strcasecmp(token, "gzip")) {
609
/* Otherwise, skip token */
611
token = ap_get_token(r->pool, &tmp, 0);
616
ap_remove_input_filter(f);
617
return ap_get_brigade(f->next, bb, mode, block, readbytes);
620
f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
621
ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
622
ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
623
ctx->buffer = apr_palloc(r->pool, c->bufferSize);
625
rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10);
626
if (rv != APR_SUCCESS) {
631
rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len);
632
if (rv != APR_SUCCESS) {
636
/* We didn't get the magic bytes. */
638
deflate_hdr[0] != deflate_magic[0] ||
639
deflate_hdr[1] != deflate_magic[1]) {
643
/* We can't handle flags for now. */
644
if (deflate_hdr[3] != 0) {
648
zRC = inflateInit2(&ctx->stream, c->windowSize);
652
inflateEnd(&ctx->stream);
653
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
654
"unable to init Zlib: "
655
"inflateInit2 returned %d: URL %s",
657
ap_remove_input_filter(f);
658
return ap_get_brigade(f->next, bb, mode, block, readbytes);
661
/* initialize deflate output buffer */
662
ctx->stream.next_out = ctx->buffer;
663
ctx->stream.avail_out = c->bufferSize;
665
apr_brigade_cleanup(ctx->bb);
668
if (APR_BRIGADE_EMPTY(ctx->proc_bb)) {
669
rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
671
if (rv != APR_SUCCESS) {
672
/* What about APR_EAGAIN errors? */
673
inflateEnd(&ctx->stream);
677
for (bkt = APR_BRIGADE_FIRST(ctx->bb);
678
bkt != APR_BRIGADE_SENTINEL(ctx->bb);
679
bkt = APR_BUCKET_NEXT(bkt))
684
/* If we actually see the EOS, that means we screwed up! */
685
if (APR_BUCKET_IS_EOS(bkt)) {
686
inflateEnd(&ctx->stream);
690
if (APR_BUCKET_IS_FLUSH(bkt)) {
691
apr_bucket *tmp_heap;
692
zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
694
inflateEnd(&ctx->stream);
698
ctx->stream.next_out = ctx->buffer;
699
len = c->bufferSize - ctx->stream.avail_out;
701
ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
702
tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
703
NULL, f->c->bucket_alloc);
704
APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
705
ctx->stream.avail_out = c->bufferSize;
707
/* Move everything to the returning brigade. */
708
APR_BUCKET_REMOVE(bkt);
709
APR_BRIGADE_CONCAT(bb, ctx->bb);
714
apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
716
/* pass through zlib inflate. */
717
ctx->stream.next_in = (unsigned char *)data;
718
ctx->stream.avail_in = len;
722
while (ctx->stream.avail_in != 0) {
723
if (ctx->stream.avail_out == 0) {
724
apr_bucket *tmp_heap;
725
ctx->stream.next_out = ctx->buffer;
726
len = c->bufferSize - ctx->stream.avail_out;
728
ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
729
tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
730
NULL, f->c->bucket_alloc);
731
APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
732
ctx->stream.avail_out = c->bufferSize;
735
zRC = inflate(&ctx->stream, Z_NO_FLUSH);
737
if (zRC == Z_STREAM_END) {
742
inflateEnd(&ctx->stream);
746
if (zRC == Z_STREAM_END) {
747
apr_bucket *tmp_heap, *eos;
749
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
750
"Zlib: Inflated %ld to %ld : URL %s",
751
ctx->stream.total_in, ctx->stream.total_out,
754
len = c->bufferSize - ctx->stream.avail_out;
756
ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
757
tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
758
NULL, f->c->bucket_alloc);
759
APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
760
ctx->stream.avail_out = c->bufferSize;
762
/* Is the remaining 8 bytes already in the avail stream? */
763
if (ctx->stream.avail_in >= 8) {
764
unsigned long compCRC, compLen;
765
compCRC = getLong(ctx->stream.next_in);
766
if (ctx->crc != compCRC) {
767
inflateEnd(&ctx->stream);
770
ctx->stream.next_in += 4;
771
compLen = getLong(ctx->stream.next_in);
772
if (ctx->stream.total_out != compLen) {
773
inflateEnd(&ctx->stream);
778
/* FIXME: We need to grab the 8 verification bytes
780
inflateEnd(&ctx->stream);
784
inflateEnd(&ctx->stream);
786
eos = apr_bucket_eos_create(f->c->bucket_alloc);
787
APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos);
792
apr_brigade_cleanup(ctx->bb);
795
/* If we are about to return nothing for a 'blocking' read and we have
796
* some data in our zlib buffer, flush it out so we can return something.
798
if (block == APR_BLOCK_READ &&
799
APR_BRIGADE_EMPTY(ctx->proc_bb) &&
800
ctx->stream.avail_out < c->bufferSize) {
801
apr_bucket *tmp_heap;
803
ctx->stream.next_out = ctx->buffer;
804
len = c->bufferSize - ctx->stream.avail_out;
806
ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
807
tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
808
NULL, f->c->bucket_alloc);
809
APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
810
ctx->stream.avail_out = c->bufferSize;
813
if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) {
814
apr_bucket_brigade *newbb;
816
/* May return APR_INCOMPLETE which is fine by us. */
817
apr_brigade_partition(ctx->proc_bb, readbytes, &bkt);
819
newbb = apr_brigade_split(ctx->proc_bb, bkt);
820
APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
821
APR_BRIGADE_CONCAT(ctx->proc_bb, newbb);
828
/* Filter to inflate for a content-transforming proxy. */
829
static apr_status_t inflate_out_filter(ap_filter_t *f,
830
apr_bucket_brigade *bb)
834
int deflate_init = 1;
836
request_rec *r = f->r;
837
deflate_ctx *ctx = f->ctx;
840
deflate_filter_config *c;
842
/* Do nothing if asked to filter nothing. */
843
if (APR_BRIGADE_EMPTY(bb)) {
847
c = ap_get_module_config(r->server->module_config, &deflate_module);
852
const char *encoding;
854
/* only work on main request/no subrequests */
855
if (!ap_is_initial_req(r)) {
856
ap_remove_output_filter(f);
857
return ap_pass_brigade(f->next, bb);
860
/* Let's see what our current Content-Encoding is.
861
* If gzip is present, don't gzip again. (We could, but let's not.)
863
encoding = apr_table_get(r->headers_out, "Content-Encoding");
865
const char *tmp = encoding;
867
token = ap_get_token(r->pool, &tmp, 0);
868
while (token && token[0]) {
869
if (!strcasecmp(token, "gzip")) {
873
/* Otherwise, skip token */
875
token = ap_get_token(r->pool, &tmp, 0);
880
ap_remove_output_filter(f);
881
return ap_pass_brigade(f->next, bb);
883
apr_table_unset(r->headers_out, "Content-Encoding");
885
/* No need to inflate HEAD or 204/304 */
886
if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bb))) {
887
ap_remove_output_filter(f);
888
return ap_pass_brigade(f->next, bb);
892
f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
893
ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
894
ctx->buffer = apr_palloc(r->pool, c->bufferSize);
897
zRC = inflateInit2(&ctx->stream, c->windowSize);
901
inflateEnd(&ctx->stream);
902
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
903
"unable to init Zlib: "
904
"inflateInit2 returned %d: URL %s",
906
ap_remove_output_filter(f);
907
return ap_pass_brigade(f->next, bb);
910
/* initialize deflate output buffer */
911
ctx->stream.next_out = ctx->buffer;
912
ctx->stream.avail_out = c->bufferSize;
917
for (bkt = APR_BRIGADE_FIRST(bb);
918
bkt != APR_BRIGADE_SENTINEL(bb);
919
bkt = APR_BUCKET_NEXT(bkt))
924
/* If we actually see the EOS, that means we screwed up! */
925
/* no it doesn't - not in a HEAD or 204/304 */
926
if (APR_BUCKET_IS_EOS(bkt)) {
927
inflateEnd(&ctx->stream);
928
return ap_pass_brigade(f->next, bb);
931
if (APR_BUCKET_IS_FLUSH(bkt)) {
932
apr_bucket *tmp_heap;
933
zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
935
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
936
"Inflate error %d on flush", zRC);
937
inflateEnd(&ctx->stream);
941
ctx->stream.next_out = ctx->buffer;
942
len = c->bufferSize - ctx->stream.avail_out;
944
ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
945
tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
946
NULL, f->c->bucket_alloc);
947
APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
948
ctx->stream.avail_out = c->bufferSize;
950
/* Move everything to the returning brigade. */
951
APR_BUCKET_REMOVE(bkt);
956
apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
958
/* first bucket contains zlib header */
959
if (!deflate_init++) {
961
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
962
"Insufficient data for inflate");
966
zlib_method = data[2];
967
zlib_flags = data[3];
968
if (zlib_method != Z_DEFLATED) {
969
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
970
"inflate: data not deflated!");
971
ap_remove_output_filter(f);
972
return ap_pass_brigade(f->next, bb);
974
if (data[0] != deflate_magic[0] ||
975
data[1] != deflate_magic[1] ||
976
(zlib_flags & RESERVED) != 0) {
977
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
978
"inflate: bad header");
979
return APR_EGENERAL ;
984
if (zlib_flags & EXTRA_FIELD) {
985
unsigned int bytes = (unsigned int)(data[0]);
986
bytes += ((unsigned int)(data[1])) << 8;
989
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
990
"inflate: extra field too big (not "
997
if (zlib_flags & ORIG_NAME) {
998
while (len-- && *data++);
1000
if (zlib_flags & COMMENT) {
1001
while (len-- && *data++);
1003
if (zlib_flags & HEAD_CRC) {
1009
/* pass through zlib inflate. */
1010
ctx->stream.next_in = (unsigned char *)data;
1011
ctx->stream.avail_in = len;
1015
while (ctx->stream.avail_in != 0) {
1016
if (ctx->stream.avail_out == 0) {
1017
apr_bucket *tmp_heap;
1018
ctx->stream.next_out = ctx->buffer;
1019
len = c->bufferSize - ctx->stream.avail_out;
1021
ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
1022
tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
1023
NULL, f->c->bucket_alloc);
1024
APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
1025
ctx->stream.avail_out = c->bufferSize;
1028
zRC = inflate(&ctx->stream, Z_NO_FLUSH);
1030
if (zRC == Z_STREAM_END) {
1035
inflateEnd(&ctx->stream);
1036
return APR_EGENERAL;
1039
if (zRC == Z_STREAM_END) {
1040
apr_bucket *tmp_heap, *eos;
1042
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1043
"Zlib: Inflated %ld to %ld : URL %s",
1044
ctx->stream.total_in, ctx->stream.total_out,
1047
len = c->bufferSize - ctx->stream.avail_out;
1049
ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
1050
tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
1051
NULL, f->c->bucket_alloc);
1052
APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
1053
ctx->stream.avail_out = c->bufferSize;
1055
/* Is the remaining 8 bytes already in the avail stream? */
1056
if (ctx->stream.avail_in >= 8) {
1057
unsigned long compCRC, compLen;
1058
compCRC = getLong(ctx->stream.next_in);
1059
if (ctx->crc != compCRC) {
1060
inflateEnd(&ctx->stream);
1061
return APR_EGENERAL;
1063
ctx->stream.next_in += 4;
1064
compLen = getLong(ctx->stream.next_in);
1065
if (ctx->stream.total_out != compLen) {
1066
inflateEnd(&ctx->stream);
1067
return APR_EGENERAL;
1071
/* FIXME: We need to grab the 8 verification bytes
1073
inflateEnd(&ctx->stream);
1074
return APR_EGENERAL;
1077
inflateEnd(&ctx->stream);
1079
eos = apr_bucket_eos_create(f->c->bucket_alloc);
1080
APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos);
1086
rv = ap_pass_brigade(f->next, ctx->proc_bb);
1087
apr_brigade_cleanup(ctx->proc_bb);
1091
static void register_hooks(apr_pool_t *p)
1093
ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL,
1094
AP_FTYPE_CONTENT_SET);
1095
ap_register_output_filter("INFLATE", inflate_out_filter, NULL,
1096
AP_FTYPE_RESOURCE-1);
1097
ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL,
1098
AP_FTYPE_CONTENT_SET);
1101
static const command_rec deflate_filter_cmds[] = {
1102
AP_INIT_TAKE12("DeflateFilterNote", deflate_set_note, NULL, RSRC_CONF,
1103
"Set a note to report on compression ratio"),
1104
AP_INIT_TAKE1("DeflateWindowSize", deflate_set_window_size, NULL,
1105
RSRC_CONF, "Set the Deflate window size (1-15)"),
1106
AP_INIT_TAKE1("DeflateBufferSize", deflate_set_buffer_size, NULL, RSRC_CONF,
1107
"Set the Deflate Buffer Size"),
1108
AP_INIT_TAKE1("DeflateMemLevel", deflate_set_memlevel, NULL, RSRC_CONF,
1109
"Set the Deflate Memory Level (1-9)"),
1110
AP_INIT_TAKE1("DeflateCompressionLevel", deflate_set_compressionlevel, NULL, RSRC_CONF,
1111
"Set the Deflate Compression Level (1-9)"),
1115
module AP_MODULE_DECLARE_DATA deflate_module = {
1116
STANDARD20_MODULE_STUFF,
1117
NULL, /* dir config creater */
1118
NULL, /* dir merger --- default is to override */
1119
create_deflate_server_config, /* server config */
1120
NULL, /* merge server config */
1121
deflate_filter_cmds, /* command table */
1122
register_hooks /* register hooks */