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