xref: /lighttpd1.4/src/mod_deflate.c (revision e11f72b1)
1 /* mod_deflate
2  *
3  *
4  * bug fix on Robert Jakabosky from alphatrade.com's lighttp 1.4.10 mod_deflate patch
5  *
6  * Bug fix and new features:
7  * 1) fix loop bug when content-length is bigger than work-block-size*k
8  *
9  * -------
10  *
11  * lighttpd-1.4.26.mod_deflate.patch from
12  *   https://redmine.lighttpd.net/projects/1/wiki/Docs_ModDeflate
13  *
14  * -------
15  *
16  * Patch further modified in this incarnation.
17  *
18  * Note: this patch only handles completed responses
19  *         (r->resp_body_finished)
20  *       this patch does not currently handle streaming dynamic responses,
21  *       and therefore also does not worry about Transfer-Encoding: chunked
22  *       (or having separate con->output_queue for chunked-encoded output)
23  *       (or using separate buffers per connection instead of p->tmp_buf)
24  *       (or handling interactions with block buffering and write timeouts)
25  *
26  * Bug fix:
27  * - fixed major bug with compressing chunks with offset > 0
28  *     x-ref:
29  *       "Response breaking in mod_deflate"
30  *       https://redmine.lighttpd.net/issues/986
31  * - fix broken (in some cases) chunk accounting in deflate_compress_response()
32  * - fix broken bzip2
33  *     x-ref:
34  *       "mod_deflate's bzip2 broken by default"
35  *       https://redmine.lighttpd.net/issues/2035
36  * - fix mismatch with current chunk interfaces
37  *     x-ref:
38  *       "Weird things in chunk.c (functions only handling specific cases, unexpected behaviour)"
39  *       https://redmine.lighttpd.net/issues/1510
40  *
41  * Behavior changes from prior patch:
42  * - deflate.mimetypes must now be configured to enable compression
43  *     deflate.mimetypes = ( )          # compress nothing (disabled; default)
44  *     deflate.mimetypes = ( "" )       # compress all mimetypes
45  *     deflate.mimetypes = ( "text/" )  # compress text/... mimetypes
46  *     x-ref:
47  *       "mod_deflate enabled by default"
48  *       https://redmine.lighttpd.net/issues/1394
49  * - deflate.enabled directive removed (see new behavior of deflate.mimetypes)
50  * - deflate.debug removed (was developer debug trace, not end-user debug)
51  * - deflate.bzip2 replaced with deflate.allowed-encodings (like mod_compress)
52  *     x-ref:
53  *       "mod_deflate should allow limiting of compression algorithm from the configuration file"
54  *       https://redmine.lighttpd.net/issues/996
55  *       "mod_compress disabling methods"
56  *       https://redmine.lighttpd.net/issues/1773
57  * - deflate.nocompress-url removed since disabling compression for a URL
58  *   can now easily be done by setting to a blank list either directive
59  *   deflate.accept_encodings = () or deflate.mimetypes = () in a conditional
60  *   block, e.g. $HTTP["url"] =~ "....." { deflate.mimetypes = ( ) }
61  * - deflate.sync-flush removed; controlled by r->conf.stream_response_body
62  *     (though streaming compression not currently implemented in mod_deflate)
63  * - inactive directives in this patch
64  *       (since r->resp_body_finished required)
65  *     deflate.work-block-size
66  *     deflate.output-buffer-size
67  * - remove weak file size check; SIGBUS is trapped, file that shrink will error
68  *     x-ref:
69  *       "mod_deflate: filesize check is too weak"
70  *       https://redmine.lighttpd.net/issues/1512
71  * - change default deflate.min-compress-size from 0 to now be 256
72  *   http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits
73  *   Apache 2.4 mod_deflate minimum is 68 bytes
74  *   Akamai recommends minimum 860 bytes
75  *   Google recommends minimum be somewhere in range between 150 and 1024 bytes
76  * - deflate.max-compress-size new directive (in kb like compress.max_filesize)
77  * - deflate.mem-level removed (too many knobs for little benefit)
78  * - deflate.window-size removed (too many knobs for little benefit)
79  *
80  * Future:
81  * - config directives may be changed, renamed, or removed
82  *   e.g. A set of reasonable defaults might be chosen
83  *        instead of making them configurable.
84  *     deflate.min-compress-size
85  * - might add deflate.mimetypes-exclude = ( ... ) for list of mimetypes
86  *   to avoid compressing, even if a broader deflate.mimetypes matched,
87  *   e.g. to compress all "text/" except "text/special".
88  * - mod_compress and mod_deflate might merge overlapping feature sets
89  *     (mod_compress.cache-dir does not yet have an equivalent in mod_deflate)
90  *
91  * Implementation notes:
92  * - http_chunk_append_mem() used instead of http_chunk_append_buffer()
93  *   so that p->tmp_buf can be large and re-used.  This results in an extra copy
94  *   of compressed data before data is sent to network, though if the compressed
95  *   size is larger than 64k, it ends up being sent to a temporary file on
96  *   disk without suffering an extra copy in memory, and without extra chunk
97  *   create and destroy.  If this is ever changed to give away buffers, then use
98  *   a unique hctx->output buffer per hctx; do not reuse p->tmp_buf across
99  *   multiple requests being handled in parallel.
100  */
101 #include "first.h"
102 
103 #include <sys/types.h>
104 #include <sys/stat.h>
105 #include "sys-mmap.h"
106 
107 #include <fcntl.h>
108 #include <stdlib.h>
109 #include <string.h>
110 #include <errno.h>
111 #include <time.h>
112 #include <unistd.h>     /* read() */
113 
114 #include "base.h"
115 #include "fdevent.h"
116 #include "log.h"
117 #include "buffer.h"
118 #include "etag.h"
119 #include "http_chunk.h"
120 #include "http_header.h"
121 #include "response.h"
122 
123 #include "plugin.h"
124 
125 #if defined HAVE_ZLIB_H && defined HAVE_LIBZ
126 # define USE_ZLIB
127 # include <zlib.h>
128 #endif
129 #ifndef Z_DEFAULT_COMPRESSION
130 #define Z_DEFAULT_COMPRESSION -1
131 #endif
132 #ifndef MAX_WBITS
133 #define MAX_WBITS 15
134 #endif
135 
136 #if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2
137 # define USE_BZ2LIB
138 /* we don't need stdio interface */
139 # define BZ_NO_STDIO
140 # include <bzlib.h>
141 #endif
142 
143 #if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP
144 #define USE_MMAP
145 
146 #include "sys-mmap.h"
147 #include <setjmp.h>
148 #include <signal.h>
149 
150 static volatile int sigbus_jmp_valid;
151 static sigjmp_buf sigbus_jmp;
152 
153 static void sigbus_handler(int sig) {
154 	UNUSED(sig);
155 	if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1);
156 	log_failed_assert(__FILE__, __LINE__, "SIGBUS");
157 }
158 #endif
159 
160 /* request: accept-encoding */
161 #define HTTP_ACCEPT_ENCODING_IDENTITY BV(0)
162 #define HTTP_ACCEPT_ENCODING_GZIP     BV(1)
163 #define HTTP_ACCEPT_ENCODING_DEFLATE  BV(2)
164 #define HTTP_ACCEPT_ENCODING_COMPRESS BV(3)
165 #define HTTP_ACCEPT_ENCODING_BZIP2    BV(4)
166 #define HTTP_ACCEPT_ENCODING_X_GZIP   BV(5)
167 #define HTTP_ACCEPT_ENCODING_X_BZIP2  BV(6)
168 
169 #define KByte * 1024
170 #define MByte * 1024 KByte
171 #define GByte * 1024 MByte
172 
173 typedef struct {
174 	const array	*mimetypes;
175 	unsigned int	max_compress_size;
176 	unsigned short	min_compress_size;
177 	unsigned short	output_buffer_size;
178 	unsigned short	work_block_size;
179 	unsigned short	sync_flush;
180 	short		compression_level;
181 	short		allowed_encodings;
182 	double		max_loadavg;
183 } plugin_config;
184 
185 typedef struct {
186     PLUGIN_DATA;
187     plugin_config defaults;
188     plugin_config conf;
189 
190     buffer tmp_buf;
191 } plugin_data;
192 
193 typedef struct {
194 	union {
195 	      #ifdef USE_ZLIB
196 		z_stream z;
197 	      #endif
198 	      #ifdef USE_BZ2LIB
199 		bz_stream bz;
200 	      #endif
201 		int dummy;
202 	} u;
203 	off_t bytes_in;
204 	off_t bytes_out;
205 	chunkqueue *in_queue;
206 	buffer *output;
207 	plugin_data *plugin_data;
208 	int compression_type;
209 	log_error_st *errh;
210 } handler_ctx;
211 
212 static handler_ctx *handler_ctx_init() {
213 	handler_ctx *hctx;
214 
215 	hctx = calloc(1, sizeof(*hctx));
216 	hctx->in_queue = chunkqueue_init();
217 
218 	return hctx;
219 }
220 
221 static void handler_ctx_free(handler_ctx *hctx) {
222       #if 0
223 	if (hctx->output != &p->tmp_buf) {
224 		buffer_free(hctx->output);
225 	}
226       #endif
227 	chunkqueue_free(hctx->in_queue);
228 	free(hctx);
229 }
230 
231 INIT_FUNC(mod_deflate_init) {
232     plugin_data * const p = calloc(1, sizeof(plugin_data));
233     buffer_string_prepare_copy(&p->tmp_buf, 64 KByte);
234     return p;
235 }
236 
237 FREE_FUNC(mod_deflate_free) {
238     plugin_data *p = p_d;
239     free(p->tmp_buf.ptr);
240 }
241 
242 static void mod_deflate_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
243     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
244       case 0: /* deflate.mimetypes */
245         pconf->mimetypes = cpv->v.a;
246         break;
247       case 1: /* deflate.allowed-encodings */
248         pconf->allowed_encodings = (short)cpv->v.shrt;
249         break;
250       case 2: /* deflate.max-compress-size */
251         pconf->max_compress_size = cpv->v.u;
252         break;
253       case 3: /* deflate.min-compress-size */
254         pconf->min_compress_size = cpv->v.shrt;
255         break;
256       case 4: /* deflate.compression-level */
257         pconf->compression_level = (short)cpv->v.shrt;
258         break;
259       case 5: /* deflate.output-buffer-size */
260         pconf->output_buffer_size = cpv->v.shrt;
261         break;
262       case 6: /* deflate.work-block-size */
263         pconf->work_block_size = cpv->v.shrt;
264         break;
265       case 7: /* deflate.max-loadavg */
266         pconf->max_loadavg = cpv->v.d;
267         break;
268       default:/* should not happen */
269         return;
270     }
271 }
272 
273 static void mod_deflate_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
274     do {
275         mod_deflate_merge_config_cpv(pconf, cpv);
276     } while ((++cpv)->k_id != -1);
277 }
278 
279 static void mod_deflate_patch_config(request_st * const r, plugin_data * const p) {
280     memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
281     for (int i = 1, used = p->nconfig; i < used; ++i) {
282         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
283             mod_deflate_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
284     }
285 }
286 
287 static short mod_deflate_encodings_to_flags(const array *encodings) {
288     short allowed_encodings = 0;
289     if (encodings->used) {
290         for (uint32_t j = 0; j < encodings->used; ++j) {
291           #if defined(USE_ZLIB) || defined(USE_BZ2LIB)
292             data_string *ds = (data_string *)encodings->data[j];
293           #endif
294           #ifdef USE_ZLIB
295             if (NULL != strstr(ds->value.ptr, "gzip"))
296                 allowed_encodings |= HTTP_ACCEPT_ENCODING_GZIP
297                                   |  HTTP_ACCEPT_ENCODING_X_GZIP;
298             if (NULL != strstr(ds->value.ptr, "x-gzip"))
299                 allowed_encodings |= HTTP_ACCEPT_ENCODING_X_GZIP;
300             if (NULL != strstr(ds->value.ptr, "deflate"))
301                 allowed_encodings |= HTTP_ACCEPT_ENCODING_DEFLATE;
302             /*
303             if (NULL != strstr(ds->value.ptr, "compress"))
304                 allowed_encodings |= HTTP_ACCEPT_ENCODING_COMPRESS;
305             */
306           #endif
307           #ifdef USE_BZ2LIB
308             if (NULL != strstr(ds->value.ptr, "bzip2"))
309                 allowed_encodings |= HTTP_ACCEPT_ENCODING_BZIP2
310                                   |  HTTP_ACCEPT_ENCODING_X_BZIP2;
311             if (NULL != strstr(ds->value.ptr, "x-bzip2"))
312                 allowed_encodings |= HTTP_ACCEPT_ENCODING_X_BZIP2;
313           #endif
314         }
315     }
316     else {
317         /* default encodings */
318       #ifdef USE_ZLIB
319         allowed_encodings |= HTTP_ACCEPT_ENCODING_GZIP
320                           |  HTTP_ACCEPT_ENCODING_X_GZIP
321                           |  HTTP_ACCEPT_ENCODING_DEFLATE;
322       #endif
323       #ifdef USE_BZ2LIB
324         allowed_encodings |= HTTP_ACCEPT_ENCODING_BZIP2
325                           |  HTTP_ACCEPT_ENCODING_X_BZIP2;
326       #endif
327     }
328     return allowed_encodings;
329 }
330 
331 SETDEFAULTS_FUNC(mod_deflate_set_defaults) {
332     static const config_plugin_keys_t cpk[] = {
333       { CONST_STR_LEN("deflate.mimetypes"),
334         T_CONFIG_ARRAY_VLIST,
335         T_CONFIG_SCOPE_CONNECTION }
336      ,{ CONST_STR_LEN("deflate.allow-encodings"),
337         T_CONFIG_ARRAY_VLIST,
338         T_CONFIG_SCOPE_CONNECTION }
339      ,{ CONST_STR_LEN("deflate.max-compress-size"),
340         T_CONFIG_INT,
341         T_CONFIG_SCOPE_CONNECTION }
342      ,{ CONST_STR_LEN("deflate.min-compress-size"),
343         T_CONFIG_SHORT,
344         T_CONFIG_SCOPE_CONNECTION }
345      ,{ CONST_STR_LEN("deflate.compression-level"),
346         T_CONFIG_SHORT,
347         T_CONFIG_SCOPE_CONNECTION }
348      ,{ CONST_STR_LEN("deflate.output-buffer-size"),
349         T_CONFIG_SHORT,
350         T_CONFIG_SCOPE_CONNECTION }
351      ,{ CONST_STR_LEN("deflate.work-block-size"),
352         T_CONFIG_SHORT,
353         T_CONFIG_SCOPE_CONNECTION }
354      ,{ CONST_STR_LEN("deflate.max-loadavg"),
355         T_CONFIG_STRING,
356         T_CONFIG_SCOPE_CONNECTION }
357      ,{ NULL, 0,
358         T_CONFIG_UNSET,
359         T_CONFIG_SCOPE_UNSET }
360     };
361 
362     plugin_data * const p = p_d;
363     if (!config_plugin_values_init(srv, p, cpk, "mod_deflate"))
364         return HANDLER_ERROR;
365 
366     /* process and validate config directives
367      * (init i to 0 if global context; to 1 to skip empty global context) */
368     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
369         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
370         for (; -1 != cpv->k_id; ++cpv) {
371             switch (cpv->k_id) {
372               case 0: /* deflate.mimetypes */
373                 /* mod_deflate matches mimetype as prefix of Content-Type
374                  * so ignore '*' at end of mimetype for end-user flexibility
375                  * in specifying trailing wildcard to grouping of mimetypes */
376                 for (uint32_t m = 0; m < cpv->v.a->used; ++m) {
377                     buffer *mimetype=&((data_string *)cpv->v.a->data[m])->value;
378                     size_t len = buffer_string_length(mimetype);
379                     if (len > 2 && mimetype->ptr[len-1] == '*')
380                         buffer_string_set_length(mimetype, len-1);
381                 }
382                 if (0 == cpv->v.a->used) cpv->v.a = NULL;
383                 break;
384               case 1: /* deflate.allowed-encodings */
385                 cpv->v.shrt = (unsigned short)
386                   mod_deflate_encodings_to_flags(cpv->v.a);
387                 cpv->vtype = T_CONFIG_SHORT;
388                 break;
389               case 2: /* deflate.max-compress-size */
390               case 3: /* deflate.min-compress-size */
391                 break;
392               case 4: /* deflate.compression-level */
393                 if ((cpv->v.shrt < 1 || cpv->v.shrt > 9)
394                     && *(short *)&cpv->v.shrt != -1) {
395                     log_error(srv->errh, __FILE__, __LINE__,
396                       "compression-level must be between 1 and 9: %hu",
397                       cpv->v.shrt);
398                     return HANDLER_ERROR;
399                 }
400                 break;
401               case 5: /* deflate.output-buffer-size */
402               case 6: /* deflate.work-block-size */
403                 break;
404               case 7: /* deflate.max-loadavg */
405                 cpv->v.d = (!buffer_string_is_empty(cpv->v.b))
406                   ? strtod(cpv->v.b->ptr, NULL)
407                   : 0.0;
408                 break;
409               default:/* should not happen */
410                 break;
411             }
412         }
413     }
414 
415     p->defaults.allowed_encodings = 0;
416     p->defaults.max_compress_size = 128*1024; /*(128 MB measured as num KB)*/
417     p->defaults.min_compress_size = 256;
418     p->defaults.compression_level = -1;
419     p->defaults.output_buffer_size = 0;
420     p->defaults.work_block_size = 2048;
421     p->defaults.max_loadavg = 0.0;
422     p->defaults.sync_flush = 0;
423 
424     /* initialize p->defaults from global config context */
425     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
426         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
427         if (-1 != cpv->k_id)
428             mod_deflate_merge_config(&p->defaults, cpv);
429     }
430 
431     return HANDLER_GO_ON;
432 }
433 
434 
435 #if defined(USE_ZLIB) || defined(USE_BZ2LIB)
436 static int stream_http_chunk_append_mem(request_st * const r, const handler_ctx * const hctx, size_t len) {
437 	/* future: might also write stream to hctx temporary file in compressed file cache */
438 	return http_chunk_append_mem(r, hctx->output->ptr, len);
439 }
440 #endif
441 
442 
443 #ifdef USE_ZLIB
444 
445 static int stream_deflate_init(handler_ctx *hctx) {
446 	z_stream * const z = &hctx->u.z;
447 	const plugin_data * const p = hctx->plugin_data;
448 	z->zalloc = Z_NULL;
449 	z->zfree = Z_NULL;
450 	z->opaque = Z_NULL;
451 	z->total_in = 0;
452 	z->total_out = 0;
453 	z->next_out = (unsigned char *)hctx->output->ptr;
454 	z->avail_out = hctx->output->size;
455 
456 	if (Z_OK != deflateInit2(z,
457 				 p->conf.compression_level > 0
458 				  ? p->conf.compression_level
459 				  : Z_DEFAULT_COMPRESSION,
460 				 Z_DEFLATED,
461 				 (hctx->compression_type == HTTP_ACCEPT_ENCODING_GZIP)
462 				  ? (MAX_WBITS | 16) /*(0x10 flags gzip header, trailer)*/
463 				  : -MAX_WBITS,      /*(negate to suppress zlib header)*/
464 				 8, /* default memLevel */
465 				 Z_DEFAULT_STRATEGY)) {
466 		return -1;
467 	}
468 
469 	return 0;
470 }
471 
472 static int stream_deflate_compress(request_st * const r, handler_ctx * const hctx, unsigned char * const start, off_t st_size) {
473 	z_stream * const z = &(hctx->u.z);
474 	size_t len;
475 
476 	z->next_in = start;
477 	z->avail_in = st_size;
478 	hctx->bytes_in += st_size;
479 
480 	/* compress data */
481 	do {
482 		if (Z_OK != deflate(z, Z_NO_FLUSH)) return -1;
483 
484 		if (z->avail_out == 0 || z->avail_in > 0) {
485 			len = hctx->output->size - z->avail_out;
486 			hctx->bytes_out += len;
487 			stream_http_chunk_append_mem(r, hctx, len);
488 			z->next_out = (unsigned char *)hctx->output->ptr;
489 			z->avail_out = hctx->output->size;
490 		}
491 	} while (z->avail_in > 0);
492 
493 	return 0;
494 }
495 
496 static int stream_deflate_flush(request_st * const r, handler_ctx * const hctx, int end) {
497 	z_stream * const z = &(hctx->u.z);
498 	const plugin_data *p = hctx->plugin_data;
499 	size_t len;
500 	int rc = 0;
501 	int done;
502 
503 	/* compress data */
504 	do {
505 		done = 1;
506 		if (end) {
507 			rc = deflate(z, Z_FINISH);
508 			if (rc == Z_OK) {
509 				done = 0;
510 			} else if (rc != Z_STREAM_END) {
511 				return -1;
512 			}
513 		} else {
514 			if (p->conf.sync_flush) {
515 				rc = deflate(z, Z_SYNC_FLUSH);
516 				if (rc != Z_OK) return -1;
517 			} else if (z->avail_in > 0) {
518 				rc = deflate(z, Z_NO_FLUSH);
519 				if (rc != Z_OK) return -1;
520 			}
521 		}
522 
523 		len = hctx->output->size - z->avail_out;
524 		if (z->avail_out == 0 || (len > 0 && (end || p->conf.sync_flush))) {
525 			hctx->bytes_out += len;
526 			stream_http_chunk_append_mem(r, hctx, len);
527 			z->next_out = (unsigned char *)hctx->output->ptr;
528 			z->avail_out = hctx->output->size;
529 		}
530 	} while (z->avail_in != 0 || !done);
531 
532 	return 0;
533 }
534 
535 static int stream_deflate_end(handler_ctx *hctx) {
536 	z_stream * const z = &(hctx->u.z);
537 	int rc = deflateEnd(z);
538 	if (Z_OK == rc || Z_DATA_ERROR == rc) return 0;
539 
540 	if (z->msg != NULL) {
541 		log_error(hctx->errh, __FILE__, __LINE__,
542 		  "deflateEnd error ret=%d, msg=%s", rc, z->msg);
543 	} else {
544 		log_error(hctx->errh, __FILE__, __LINE__,
545 		  "deflateEnd error ret=%d", rc);
546 	}
547 	return -1;
548 }
549 
550 #endif
551 
552 
553 #ifdef USE_BZ2LIB
554 
555 static int stream_bzip2_init(handler_ctx *hctx) {
556 	bz_stream * const bz = &hctx->u.bz;
557 	const plugin_data * const p = hctx->plugin_data;
558 	bz->bzalloc = NULL;
559 	bz->bzfree = NULL;
560 	bz->opaque = NULL;
561 	bz->total_in_lo32 = 0;
562 	bz->total_in_hi32 = 0;
563 	bz->total_out_lo32 = 0;
564 	bz->total_out_hi32 = 0;
565 	bz->next_out = hctx->output->ptr;
566 	bz->avail_out = hctx->output->size;
567 
568 	if (BZ_OK != BZ2_bzCompressInit(bz,
569 					p->conf.compression_level > 0
570 					 ? p->conf.compression_level
571 					 : 9, /* blocksize = 900k */
572 					0,    /* verbosity */
573 					0)) { /* workFactor: default */
574 		return -1;
575 	}
576 
577 	return 0;
578 }
579 
580 static int stream_bzip2_compress(request_st * const r, handler_ctx * const hctx, unsigned char * const start, off_t st_size) {
581 	bz_stream * const bz = &(hctx->u.bz);
582 	size_t len;
583 
584 	bz->next_in = (char *)start;
585 	bz->avail_in = st_size;
586 	hctx->bytes_in += st_size;
587 
588 	/* compress data */
589 	do {
590 		if (BZ_RUN_OK != BZ2_bzCompress(bz, BZ_RUN)) return -1;
591 
592 		if (bz->avail_out == 0 || bz->avail_in > 0) {
593 			len = hctx->output->size - bz->avail_out;
594 			hctx->bytes_out += len;
595 			stream_http_chunk_append_mem(r, hctx, len);
596 			bz->next_out = hctx->output->ptr;
597 			bz->avail_out = hctx->output->size;
598 		}
599 	} while (bz->avail_in > 0);
600 
601 	return 0;
602 }
603 
604 static int stream_bzip2_flush(request_st * const r, handler_ctx * const hctx, int end) {
605 	bz_stream * const bz = &(hctx->u.bz);
606 	const plugin_data *p = hctx->plugin_data;
607 	size_t len;
608 	int rc;
609 	int done;
610 
611 	/* compress data */
612 	do {
613 		done = 1;
614 		if (end) {
615 			rc = BZ2_bzCompress(bz, BZ_FINISH);
616 			if (rc == BZ_FINISH_OK) {
617 				done = 0;
618 			} else if (rc != BZ_STREAM_END) {
619 				return -1;
620 			}
621 		} else if (bz->avail_in > 0) {
622 			/* p->conf.sync_flush not implemented here,
623 			 * which would loop on BZ_FLUSH while BZ_FLUSH_OK
624 			 * until BZ_RUN_OK returned */
625 			rc = BZ2_bzCompress(bz, BZ_RUN);
626 			if (rc != BZ_RUN_OK) {
627 				return -1;
628 			}
629 		}
630 
631 		len = hctx->output->size - bz->avail_out;
632 		if (bz->avail_out == 0 || (len > 0 && (end || p->conf.sync_flush))) {
633 			hctx->bytes_out += len;
634 			stream_http_chunk_append_mem(r, hctx, len);
635 			bz->next_out = hctx->output->ptr;
636 			bz->avail_out = hctx->output->size;
637 		}
638 	} while (bz->avail_in != 0 || !done);
639 
640 	return 0;
641 }
642 
643 static int stream_bzip2_end(handler_ctx *hctx) {
644 	bz_stream * const bz = &(hctx->u.bz);
645 	int rc = BZ2_bzCompressEnd(bz);
646 	if (BZ_OK == rc || BZ_DATA_ERROR == rc) return 0;
647 
648 	log_error(hctx->errh, __FILE__, __LINE__,
649 	  "BZ2_bzCompressEnd error ret=%d", rc);
650 	return -1;
651 }
652 
653 #endif
654 
655 
656 static int mod_deflate_stream_init(handler_ctx *hctx) {
657 	switch(hctx->compression_type) {
658 #ifdef USE_ZLIB
659 	case HTTP_ACCEPT_ENCODING_GZIP:
660 	case HTTP_ACCEPT_ENCODING_DEFLATE:
661 		return stream_deflate_init(hctx);
662 #endif
663 #ifdef USE_BZ2LIB
664 	case HTTP_ACCEPT_ENCODING_BZIP2:
665 		return stream_bzip2_init(hctx);
666 #endif
667 	default:
668 		return -1;
669 	}
670 }
671 
672 static int mod_deflate_compress(request_st * const r, handler_ctx * const hctx, unsigned char * const start, off_t st_size) {
673 	if (0 == st_size) return 0;
674 	switch(hctx->compression_type) {
675 #ifdef USE_ZLIB
676 	case HTTP_ACCEPT_ENCODING_GZIP:
677 	case HTTP_ACCEPT_ENCODING_DEFLATE:
678 		return stream_deflate_compress(r, hctx, start, st_size);
679 #endif
680 #ifdef USE_BZ2LIB
681 	case HTTP_ACCEPT_ENCODING_BZIP2:
682 		return stream_bzip2_compress(r, hctx, start, st_size);
683 #endif
684 	default:
685 		UNUSED(r);
686 		UNUSED(start);
687 		return -1;
688 	}
689 }
690 
691 static int mod_deflate_stream_flush(request_st * const r, handler_ctx * const hctx, int end) {
692 	if (0 == hctx->bytes_in) return 0;
693 	switch(hctx->compression_type) {
694 #ifdef USE_ZLIB
695 	case HTTP_ACCEPT_ENCODING_GZIP:
696 	case HTTP_ACCEPT_ENCODING_DEFLATE:
697 		return stream_deflate_flush(r, hctx, end);
698 #endif
699 #ifdef USE_BZ2LIB
700 	case HTTP_ACCEPT_ENCODING_BZIP2:
701 		return stream_bzip2_flush(r, hctx, end);
702 #endif
703 	default:
704 		UNUSED(r);
705 		UNUSED(end);
706 		return -1;
707 	}
708 }
709 
710 static void mod_deflate_note_ratio(request_st * const r, handler_ctx * const hctx) {
711     /* store compression ratio in environment
712      * for possible logging by mod_accesslog
713      * (late in response handling, so not seen by most other modules) */
714     /*(should be called only at end of successful response compression)*/
715     char ratio[LI_ITOSTRING_LENGTH];
716     if (0 == hctx->bytes_in) return;
717     size_t len =
718       li_itostrn(ratio, sizeof(ratio), hctx->bytes_out * 100 / hctx->bytes_in);
719     http_header_env_set(r, CONST_STR_LEN("ratio"), ratio, len);
720 }
721 
722 static int mod_deflate_stream_end(handler_ctx *hctx) {
723 	switch(hctx->compression_type) {
724 #ifdef USE_ZLIB
725 	case HTTP_ACCEPT_ENCODING_GZIP:
726 	case HTTP_ACCEPT_ENCODING_DEFLATE:
727 		return stream_deflate_end(hctx);
728 #endif
729 #ifdef USE_BZ2LIB
730 	case HTTP_ACCEPT_ENCODING_BZIP2:
731 		return stream_bzip2_end(hctx);
732 #endif
733 	default:
734 		return -1;
735 	}
736 }
737 
738 static void deflate_compress_cleanup(request_st * const r, handler_ctx * const hctx) {
739 	const plugin_data *p = hctx->plugin_data;
740 	r->plugin_ctx[p->id] = NULL;
741 
742 	if (0 != mod_deflate_stream_end(hctx)) {
743 		log_error(r->conf.errh, __FILE__, __LINE__, "error closing stream");
744 	}
745 
746       #if 1 /* unnecessary if deflate.min-compress-size is set to a reasonable value */
747 	if (hctx->bytes_in < hctx->bytes_out) {
748 		log_error(r->conf.errh, __FILE__, __LINE__,
749 		  "uri %s in=%lld smaller than out=%lld", r->target.ptr,
750 		  (long long)hctx->bytes_in, (long long)hctx->bytes_out);
751 	}
752       #endif
753 
754 	handler_ctx_free(hctx);
755 }
756 
757 
758 static int mod_deflate_file_chunk(request_st * const r, handler_ctx * const hctx, chunk * const c, off_t st_size) {
759 	off_t abs_offset;
760 	off_t toSend = -1;
761 	char *start;
762 #ifdef USE_MMAP
763 	off_t we_want_to_mmap = 2 MByte;
764 	off_t we_want_to_send = st_size;
765 	volatile int mapped = 0;/* quiet warning: might be clobbered by 'longjmp' */
766 #else
767 	start = NULL;
768 #endif
769 
770 	if (-1 == c->file.fd) {  /* open the file if not already open */
771 		if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, r->conf.follow_symlink, O_RDONLY, 0))) {
772 			log_perror(r->conf.errh, __FILE__, __LINE__, "open failed %s", c->mem->ptr);
773 			return -1;
774 		}
775 	}
776 
777 	abs_offset = c->file.start + c->offset;
778 
779 #ifdef USE_MMAP
780 	/* mmap the buffer
781 	 * - first mmap
782 	 * - new mmap as the we are at the end of the last one */
783 	if (c->file.mmap.start == MAP_FAILED ||
784 	    abs_offset == (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
785 
786 		/* Optimizations for the future:
787 		 *
788 		 * adaptive mem-mapping
789 		 *   the problem:
790 		 *     we mmap() the whole file. If someone has alot large files and 32bit
791 		 *     machine the virtual address area will be unrun and we will have a failing
792 		 *     mmap() call.
793 		 *   solution:
794 		 *     only mmap 16M in one chunk and move the window as soon as we have finished
795 		 *     the first 8M
796 		 *
797 		 * read-ahead buffering
798 		 *   the problem:
799 		 *     sending out several large files in parallel trashes the read-ahead of the
800 		 *     kernel leading to long wait-for-seek times.
801 		 *   solutions: (increasing complexity)
802 		 *     1. use madvise
803 		 *     2. use a internal read-ahead buffer in the chunk-structure
804 		 *     3. use non-blocking IO for file-transfers
805 		 *   */
806 
807 		/* all mmap()ed areas are 512kb expect the last which might be smaller */
808 		off_t to_mmap;
809 
810 		/* this is a remap, move the mmap-offset */
811 		if (c->file.mmap.start != MAP_FAILED) {
812 			munmap(c->file.mmap.start, c->file.mmap.length);
813 			c->file.mmap.offset += we_want_to_mmap;
814 		} else {
815 			/* in case the range-offset is after the first mmap()ed area we skip the area */
816 			c->file.mmap.offset = 0;
817 
818 			while (c->file.mmap.offset + we_want_to_mmap < c->file.start) {
819 				c->file.mmap.offset += we_want_to_mmap;
820 			}
821 		}
822 
823 		/* length is rel, c->offset too, assume there is no limit at the mmap-boundaries */
824 		to_mmap = (c->file.start + c->file.length) - c->file.mmap.offset;
825 		if (to_mmap > we_want_to_mmap) to_mmap = we_want_to_mmap;
826 		/* we have more to send than we can mmap() at once */
827 		if (we_want_to_send > to_mmap) we_want_to_send = to_mmap;
828 
829 		if (MAP_FAILED == (c->file.mmap.start = mmap(0, (size_t)to_mmap, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))
830 		    && (errno != EINVAL || MAP_FAILED == (c->file.mmap.start = mmap(0, (size_t)to_mmap, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset)))) {
831 			log_perror(r->conf.errh, __FILE__, __LINE__,
832 			  "mmap failed %s %d", c->mem->ptr, c->file.fd);
833 
834 			return -1;
835 		}
836 
837 		c->file.mmap.length = to_mmap;
838 #ifdef HAVE_MADVISE
839 		/* don't advise files < 64Kb */
840 		if (c->file.mmap.length > (64 KByte) &&
841 		    0 != madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED)) {
842 			log_perror(r->conf.errh, __FILE__, __LINE__,
843 			  "madvise failed %s %d", c->mem->ptr, c->file.fd);
844 		}
845 #endif
846 
847 		/* chunk_reset() or chunk_free() will cleanup for us */
848 	}
849 
850 	/* to_send = abs_mmap_end - abs_offset */
851 	toSend = (c->file.mmap.offset + c->file.mmap.length) - abs_offset;
852 	if (toSend > we_want_to_send) toSend = we_want_to_send;
853 
854 	if (toSend < 0) {
855 		log_error(r->conf.errh, __FILE__, __LINE__,
856 		  "toSend is negative: %lld %lld %lld %lld",
857 		  (long long)toSend,
858 		  (long long)c->file.mmap.length,
859 		  (long long)abs_offset,
860 		  (long long)c->file.mmap.offset);
861 		force_assert(toSend < 0);
862 	}
863 
864 	start = c->file.mmap.start;
865 	mapped = 1;
866 #endif
867 
868 	if (MAP_FAILED == c->file.mmap.start) {
869 		toSend = st_size;
870 		if (toSend > 2 MByte) toSend = 2 MByte;
871 		if (NULL == (start = malloc((size_t)toSend)) || -1 == lseek(c->file.fd, abs_offset, SEEK_SET) || toSend != read(c->file.fd, start, (size_t)toSend)) {
872 			log_perror(r->conf.errh, __FILE__, __LINE__, "reading %s failed", c->mem->ptr);
873 
874 			free(start);
875 			return -1;
876 		}
877 		abs_offset = 0;
878 	}
879 
880 #ifdef USE_MMAP
881 	if (mapped) {
882 		signal(SIGBUS, sigbus_handler);
883 		sigbus_jmp_valid = 1;
884 		if (0 != sigsetjmp(sigbus_jmp, 1)) {
885 			sigbus_jmp_valid = 0;
886 
887 			log_error(r->conf.errh, __FILE__, __LINE__,
888 			  "SIGBUS in mmap: %s %d", c->mem->ptr, c->file.fd);
889 			return -1;
890 		}
891 	}
892 #endif
893 
894 	if (mod_deflate_compress(r, hctx,
895 				(unsigned char *)start + (abs_offset - c->file.mmap.offset), toSend) < 0) {
896 		log_error(r->conf.errh, __FILE__, __LINE__, "compress failed.");
897 		toSend = -1;
898 	}
899 
900 #ifdef USE_MMAP
901 	if (mapped)
902 		sigbus_jmp_valid = 0;
903 	else
904 #endif
905 		free(start);
906 
907 	return toSend;
908 }
909 
910 
911 static handler_t deflate_compress_response(request_st * const r, handler_ctx * const hctx) {
912 	off_t len, max;
913 	int close_stream;
914 
915 	/* move all chunk from write_queue into our in_queue, then adjust
916 	 * counters since r->write_queue is reused for compressed output */
917 	chunkqueue * const cq = r->write_queue;
918 	len = chunkqueue_length(cq);
919 	chunkqueue_remove_finished_chunks(cq);
920 	chunkqueue_append_chunkqueue(hctx->in_queue, cq);
921 	cq->bytes_in  -= len;
922 	cq->bytes_out -= len;
923 
924 	max = chunkqueue_length(hctx->in_queue);
925       #if 0
926 	/* calculate max bytes to compress for this call */
927 	if (p->conf.sync_flush && max > (len = p->conf.work_block_size << 10)) {
928 		max = len;
929 	}
930       #endif
931 
932 	/* Compress chunks from in_queue into chunks for write_queue */
933 	while (max) {
934 		chunk *c = hctx->in_queue->first;
935 
936 		switch(c->type) {
937 		case MEM_CHUNK:
938 			len = buffer_string_length(c->mem) - c->offset;
939 			if (len > max) len = max;
940 			if (mod_deflate_compress(r, hctx, (unsigned char *)c->mem->ptr+c->offset, len) < 0) {
941 				log_error(r->conf.errh, __FILE__, __LINE__, "compress failed.");
942 				return HANDLER_ERROR;
943 			}
944 			break;
945 		case FILE_CHUNK:
946 			len = c->file.length - c->offset;
947 			if (len > max) len = max;
948 			if ((len = mod_deflate_file_chunk(r, hctx, c, len)) < 0) {
949 				log_error(r->conf.errh, __FILE__, __LINE__, "compress file chunk failed.");
950 				return HANDLER_ERROR;
951 			}
952 			break;
953 		default:
954 			log_error(r->conf.errh, __FILE__, __LINE__, "%d type not known", c->type);
955 			return HANDLER_ERROR;
956 		}
957 
958 		max -= len;
959 		chunkqueue_mark_written(hctx->in_queue, len);
960 	}
961 
962 	/*(currently should always be true)*/
963 	/*(current implementation requires response be complete)*/
964 	close_stream = (r->resp_body_finished
965                         && chunkqueue_is_empty(hctx->in_queue));
966 	if (mod_deflate_stream_flush(r, hctx, close_stream) < 0) {
967 		log_error(r->conf.errh, __FILE__, __LINE__, "flush error");
968 		return HANDLER_ERROR;
969 	}
970 
971 	return close_stream ? HANDLER_FINISHED : HANDLER_GO_ON;
972 }
973 
974 
975 static int mod_deflate_choose_encoding (const char *value, plugin_data *p, const char **label) {
976 	/* get client side support encodings */
977 	int accept_encoding = 0;
978       #if !defined(USE_ZLIB) && !defined(USE_BZ2LIB)
979 	UNUSED(value);
980       #else
981         for (; *value; ++value) {
982             const char *v;
983             while (*value == ' ' || *value == ',') ++value;
984             v = value;
985             while (*value!=' ' && *value!=',' && *value!=';' && *value!='\0')
986                 ++value;
987             switch (value - v) {
988               case 4:
989                #ifdef USE_ZLIB
990                 if (0 == memcmp(v, "gzip", 4))
991                     accept_encoding |= HTTP_ACCEPT_ENCODING_GZIP;
992                #endif
993                 break;
994               case 5:
995                #ifdef USE_BZ2LIB
996                 if (0 == memcmp(v, "bzip2", 5))
997                     accept_encoding |= HTTP_ACCEPT_ENCODING_BZIP2;
998                #endif
999                 break;
1000               case 6:
1001                #ifdef USE_ZLIB
1002                 if (0 == memcmp(v, "x-gzip", 6))
1003                     accept_encoding |= HTTP_ACCEPT_ENCODING_X_GZIP;
1004                #endif
1005                 break;
1006               case 7:
1007                #ifdef USE_ZLIB
1008                 if (0 == memcmp(v, "deflate", 7))
1009                     accept_encoding |= HTTP_ACCEPT_ENCODING_DEFLATE;
1010                #endif
1011                #ifdef USE_BZ2LIB
1012                 if (0 == memcmp(v, "x-bzip2", 7))
1013                     accept_encoding |= HTTP_ACCEPT_ENCODING_X_BZIP2;
1014                #endif
1015                 break;
1016              #if 0
1017               case 8:
1018                 if (0 == memcmp(v, "identity", 8))
1019                     accept_encoding |= HTTP_ACCEPT_ENCODING_IDENTITY;
1020                 else if (0 == memcmp(v, "compress", 8))
1021                     accept_encoding |= HTTP_ACCEPT_ENCODING_COMPRESS;
1022                 break;
1023              #endif
1024               default:
1025                 break;
1026             }
1027             if (*value == ';') {
1028                 while (*value != ',' && *value != '\0') ++value;
1029             }
1030             if (*value == '\0') break;
1031         }
1032       #endif
1033 
1034 	/* mask to limit to allowed_encodings */
1035 	accept_encoding &= p->conf.allowed_encodings;
1036 
1037 	/* select best matching encoding */
1038 #ifdef USE_BZ2LIB
1039 	if (accept_encoding & HTTP_ACCEPT_ENCODING_BZIP2) {
1040 		*label = "bzip2";
1041 		return HTTP_ACCEPT_ENCODING_BZIP2;
1042 	} else if (accept_encoding & HTTP_ACCEPT_ENCODING_X_BZIP2) {
1043 		*label = "x-bzip2";
1044 		return HTTP_ACCEPT_ENCODING_BZIP2;
1045 	} else
1046 #endif
1047 #ifdef USE_ZLIB
1048 	if (accept_encoding & HTTP_ACCEPT_ENCODING_GZIP) {
1049 		*label = "gzip";
1050 		return HTTP_ACCEPT_ENCODING_GZIP;
1051 	} else if (accept_encoding & HTTP_ACCEPT_ENCODING_X_GZIP) {
1052 		*label = "x-gzip";
1053 		return HTTP_ACCEPT_ENCODING_GZIP;
1054 	} else if (accept_encoding & HTTP_ACCEPT_ENCODING_DEFLATE) {
1055 		*label = "deflate";
1056 		return HTTP_ACCEPT_ENCODING_DEFLATE;
1057 	} else
1058 #endif
1059 	if (0 == accept_encoding) {
1060 		return 0;
1061 	} else {
1062 		return 0;
1063 	}
1064 }
1065 
1066 REQUEST_FUNC(mod_deflate_handle_response_start) {
1067 	plugin_data *p = p_d;
1068 	const buffer *vbro;
1069 	buffer *vb;
1070 	handler_ctx *hctx;
1071 	const char *label;
1072 	off_t len;
1073 	size_t etaglen = 0;
1074 	int compression_type;
1075 	handler_t rc;
1076 
1077 	/*(current implementation requires response be complete)*/
1078 	if (!r->resp_body_finished) return HANDLER_GO_ON;
1079 	if (r->http_method == HTTP_METHOD_HEAD) return HANDLER_GO_ON;
1080 	if (r->resp_htags & HTTP_HEADER_TRANSFER_ENCODING) return HANDLER_GO_ON;
1081 
1082 	/* disable compression for some http status types. */
1083 	switch(r->http_status) {
1084 	case 100:
1085 	case 101:
1086 	case 204:
1087 	case 205:
1088 	case 304:
1089 		/* disable compression as we have no response entity */
1090 		return HANDLER_GO_ON;
1091 	default:
1092 		break;
1093 	}
1094 
1095 	mod_deflate_patch_config(r, p);
1096 
1097 	/* check if deflate configured for any mimetypes */
1098 	if (NULL == p->conf.mimetypes) return HANDLER_GO_ON;
1099 
1100 	/* check if size of response is below min-compress-size or exceeds max*/
1101 	/* (r->resp_body_finished checked at top of routine) */
1102 	len = chunkqueue_length(r->write_queue);
1103 	if (len <= (off_t)p->conf.min_compress_size) return HANDLER_GO_ON;
1104 	if (p->conf.max_compress_size /*(max_compress_size in KB)*/
1105 	    && len > ((off_t)p->conf.max_compress_size << 10)) {
1106 		return HANDLER_GO_ON;
1107 	}
1108 
1109 	/* Check if response has a Content-Encoding. */
1110 	vbro = http_header_response_get(r, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"));
1111 	if (NULL != vbro) return HANDLER_GO_ON;
1112 
1113 	/* Check Accept-Encoding for supported encoding. */
1114 	vbro = http_header_request_get(r, HTTP_HEADER_ACCEPT_ENCODING, CONST_STR_LEN("Accept-Encoding"));
1115 	if (NULL == vbro) return HANDLER_GO_ON;
1116 
1117 	/* find matching encodings */
1118 	compression_type = mod_deflate_choose_encoding(vbro->ptr, p, &label);
1119 	if (!compression_type) return HANDLER_GO_ON;
1120 
1121 	/* Check mimetype in response header "Content-Type" */
1122 	if (NULL != (vbro = http_header_response_get(r, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type")))) {
1123 		if (NULL == array_match_value_prefix(p->conf.mimetypes, vbro)) return HANDLER_GO_ON;
1124 	} else {
1125 		/* If no Content-Type set, compress only if first p->conf.mimetypes value is "" */
1126 		data_string *mimetype = (data_string *)p->conf.mimetypes->data[0];
1127 		if (!buffer_string_is_empty(&mimetype->value)) return HANDLER_GO_ON;
1128 	}
1129 
1130 	/* Vary: Accept-Encoding (response might change according to request Accept-Encoding) */
1131 	if (NULL != (vb = http_header_response_get(r, HTTP_HEADER_VARY, CONST_STR_LEN("Vary")))) {
1132 		if (NULL == strstr(vb->ptr, "Accept-Encoding")) {
1133 			buffer_append_string_len(vb, CONST_STR_LEN(",Accept-Encoding"));
1134 		}
1135 	} else {
1136 		http_header_response_append(r, HTTP_HEADER_VARY,
1137 					    CONST_STR_LEN("Vary"),
1138 					    CONST_STR_LEN("Accept-Encoding"));
1139 	}
1140 
1141 	/* check ETag as is done in http_response_handle_cachable()
1142 	 * (slightly imperfect (close enough?) match of ETag "000000" to "000000-gzip") */
1143 	vb = http_header_response_get(r, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"));
1144 	if (NULL != vb && (r->rqst_htags & HTTP_HEADER_IF_NONE_MATCH)) {
1145 		const buffer *if_none_match = http_header_response_get(r, HTTP_HEADER_IF_NONE_MATCH, CONST_STR_LEN("If-None-Match"));
1146 		etaglen = buffer_string_length(vb);
1147 		if (etaglen
1148 		    && r->http_status < 300 /*(want 2xx only)*/
1149 		    && NULL != if_none_match
1150 		    && 0 == strncmp(if_none_match->ptr, vb->ptr, etaglen-1)
1151 		    && if_none_match->ptr[etaglen-1] == '-'
1152 		    && 0 == strncmp(if_none_match->ptr+etaglen, label, strlen(label))) {
1153 
1154 			if (http_method_get_or_head(r->http_method)) {
1155 				/* modify ETag response header in-place to remove '"' and append '-label"' */
1156 				vb->ptr[etaglen-1] = '-'; /*(overwrite end '"')*/
1157 				buffer_append_string(vb, label);
1158 				buffer_append_string_len(vb, CONST_STR_LEN("\""));
1159 				/*buffer_copy_buffer(&r->physical.etag, vb);*//*(keep in sync?)*/
1160 				r->http_status = 304;
1161 			} else {
1162 				r->http_status = 412;
1163 			}
1164 
1165 			/* response_start hook occurs after error docs have been handled.
1166 			 * For now, send back empty response body.
1167 			 * In the future, might extract the error doc code so that it
1168 			 * might be run again if response_start hooks return with
1169 			 * changed http_status and r->handler_module = NULL */
1170 			/* clear content length even if 304 since compressed length unknown */
1171 			http_response_body_clear(r, 0);
1172 
1173 			r->resp_body_finished = 1;
1174 			r->handler_module = NULL;
1175 			return HANDLER_GO_ON;
1176 		}
1177 	}
1178 
1179 	if (0.0 < p->conf.max_loadavg && p->conf.max_loadavg < r->con->srv->loadavg[0]) {
1180 		return HANDLER_GO_ON;
1181 	}
1182 
1183 	/* update ETag, if ETag response header is set */
1184 	if (etaglen) {
1185 		/* modify ETag response header in-place to remove '"' and append '-label"' */
1186 		vb->ptr[etaglen-1] = '-'; /*(overwrite end '"')*/
1187 		buffer_append_string(vb, label);
1188 		buffer_append_string_len(vb, CONST_STR_LEN("\""));
1189 		/*buffer_copy_buffer(&r->physical.etag, vb);*//*(keep in sync?)*/
1190 	}
1191 
1192 	/* set Content-Encoding to show selected compression type */
1193 	http_header_response_set(r, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"), label, strlen(label));
1194 
1195 	/* clear Content-Length and r->write_queue if HTTP HEAD request
1196 	 * (alternatively, could return original Content-Length with HEAD
1197 	 *  request if ETag not modified and Content-Encoding not added) */
1198 	if (HTTP_METHOD_HEAD == r->http_method) {
1199 		/* ensure that uncompressed Content-Length is not sent in HEAD response */
1200 		http_response_body_clear(r, 0);
1201 		return HANDLER_GO_ON;
1202 	}
1203 
1204 	/* future: might use ETag to check if compressed content is in compressed file cache */
1205 	/*if (etaglen) { ... } *//* return if in file cache after updating r->write_queue */
1206 
1207 	/* enable compression */
1208 	p->conf.sync_flush =
1209 	  (r->conf.stream_response_body && 0 == p->conf.output_buffer_size);
1210 	hctx = handler_ctx_init();
1211 	hctx->plugin_data = p;
1212 	hctx->compression_type = compression_type;
1213 	hctx->errh = r->conf.errh;
1214 	/* setup output buffer */
1215 	buffer_clear(&p->tmp_buf);
1216 	hctx->output = &p->tmp_buf;
1217 	if (0 != mod_deflate_stream_init(hctx)) {
1218 		/*(should not happen unless ENOMEM)*/
1219 		handler_ctx_free(hctx);
1220 		log_error(r->conf.errh, __FILE__, __LINE__,
1221 		  "Failed to initialize compression %s", label);
1222 		/* restore prior Etag and unset Content-Encoding */
1223 		if (etaglen) {
1224 			vb->ptr[etaglen-1] = '"'; /*(overwrite '-')*/
1225 			buffer_string_set_length(vb, etaglen);
1226 		}
1227 		http_header_response_unset(r, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"));
1228 		return HANDLER_GO_ON;
1229 	}
1230 
1231 	if (r->resp_htags & HTTP_HEADER_CONTENT_LENGTH) {
1232 		http_header_response_unset(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"));
1233 	}
1234 	r->plugin_ctx[p->id] = hctx;
1235 
1236 	rc = deflate_compress_response(r, hctx);
1237 	if (HANDLER_GO_ON != rc) {
1238 		if (HANDLER_FINISHED == rc) {
1239 			mod_deflate_note_ratio(r, hctx);
1240 		}
1241 		deflate_compress_cleanup(r, hctx);
1242 		if (HANDLER_ERROR == rc) return HANDLER_ERROR;
1243 	}
1244 
1245 	return HANDLER_GO_ON;
1246 }
1247 
1248 static handler_t mod_deflate_cleanup(request_st * const r, void *p_d) {
1249 	plugin_data *p = p_d;
1250 	handler_ctx *hctx = r->plugin_ctx[p->id];
1251 
1252 	if (NULL != hctx) deflate_compress_cleanup(r, hctx);
1253 
1254 	return HANDLER_GO_ON;
1255 }
1256 
1257 int mod_deflate_plugin_init(plugin *p);
1258 int mod_deflate_plugin_init(plugin *p) {
1259 	p->version     = LIGHTTPD_VERSION_ID;
1260 	p->name        = "deflate";
1261 
1262 	p->init		= mod_deflate_init;
1263 	p->cleanup	= mod_deflate_free;
1264 	p->set_defaults	= mod_deflate_set_defaults;
1265 	p->connection_reset	= mod_deflate_cleanup;
1266 	p->handle_response_start	= mod_deflate_handle_response_start;
1267 
1268 	return 0;
1269 }
1270