diff options
author | stbuehler <stbuehler@152afb58-edef-0310-8abb-c4023f1b3aa9> | 2015-09-18 15:15:18 +0000 |
---|---|---|
committer | stbuehler <stbuehler@152afb58-edef-0310-8abb-c4023f1b3aa9> | 2015-09-18 15:15:18 +0000 |
commit | 8b2630a82fbecfd57fa38aebb397a755936690e5 (patch) | |
tree | a9cfcd7bb5bea87d63fc8ef81c8456a130a249bc /src/mod_deflate.c | |
parent | e57c8295ebe92b58ca3e68fa8ea8f70d4b0b4cee (diff) | |
download | lighttpd-master.tar.gz |
git-svn-id: svn://svn.lighttpd.net/lighttpd/trunk@3041 152afb58-edef-0310-8abb-c4023f1b3aa9
Diffstat (limited to 'src/mod_deflate.c')
-rw-r--r-- | src/mod_deflate.c | 1408 |
1 files changed, 0 insertions, 1408 deletions
diff --git a/src/mod_deflate.c b/src/mod_deflate.c deleted file mode 100644 index 79deb11d..00000000 --- a/src/mod_deflate.c +++ /dev/null @@ -1,1408 +0,0 @@ -/* - * mod_deflate - dynamic compression - * - * This plugin is a response filter. - * - */ -#include <sys/types.h> -#include <sys/stat.h> - -#include <fcntl.h> -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <time.h> -#include <assert.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" -#include "response.h" -#include "joblist.h" -#include "stat_cache.h" -#include "filter.h" - -#include "plugin.h" - -#include "crc32.h" -#include "etag.h" -#include "inet_ntop_cache.h" - -#if defined HAVE_ZLIB_H && defined HAVE_LIBZ -# define USE_ZLIB -# include <zlib.h> -#endif - -#if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2 -# define USE_BZ2LIB -/* we don't need stdio interface */ -# define BZ_NO_STDIO -# include <bzlib.h> -#endif - -#include "sys-mmap.h" - -/* request: accept-encoding */ -#define HTTP_ACCEPT_ENCODING_IDENTITY BV(0) -#define HTTP_ACCEPT_ENCODING_GZIP BV(1) -#define HTTP_ACCEPT_ENCODING_DEFLATE BV(2) -#define HTTP_ACCEPT_ENCODING_COMPRESS BV(3) -#define HTTP_ACCEPT_ENCODING_BZIP2 BV(4) - -/* encoding names */ -#define ENCODING_NAME_IDENTITY "identity" -#define ENCODING_NAME_GZIP "gzip" -#define ENCODING_NAME_DEFLATE "deflate" -#define ENCODING_NAME_COMPRESS "compress" -#define ENCODING_NAME_BZIP2 "bzip2" - - -#define CONFIG_DEFLATE_OUTPUT_BUFFER_SIZE "deflate.output-buffer-size" -#define CONFIG_DEFLATE_MIMETYPES "deflate.mimetypes" -#define CONFIG_DEFLATE_COMPRESSION_LEVEL "deflate.compression-level" -#define CONFIG_DEFLATE_MEM_LEVEL "deflate.mem-level" -#define CONFIG_DEFLATE_WINDOW_SIZE "deflate.window-size" -#define CONFIG_DEFLATE_MIN_COMPRESS_SIZE "deflate.min-compress-size" -#define CONFIG_DEFLATE_WORK_BLOCK_SIZE "deflate.work-block-size" -#define CONFIG_DEFLATE_ENABLED "deflate.enabled" -#define CONFIG_DEFLATE_DEBUG "deflate.debug" -#define CONFIG_DEFLATE_ALLOWED_ENCODINGS "deflate.allowed_encodings" -#define CONFIG_DEFLATE_SYNC_FLUSH "deflate.sync-flush" - -#define KByte * 1024 -#define MByte * 1024 KByte -#define GByte * 1024 MByte - -typedef struct { - unsigned short debug; - unsigned short enabled; - unsigned short sync_flush; - unsigned short output_buffer_size; - unsigned short min_compress_size; - unsigned short work_block_size; - int allowed_encodings; - short mem_level; - short compression_level; - short window_size; - array *mimetypes; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - buffer *tmp_buf; - array *encodings_arr; - - plugin_config **config_storage; - plugin_config conf; -} plugin_data; - -typedef struct { - off_t bytes_in; - filter *fl; - chunkqueue *in; - chunkqueue *out; - buffer *output; - /* compression type & state */ - int compression_type; - int stream_open; -#ifdef USE_ZLIB - unsigned long crc; - z_stream z; - unsigned short gzip_header; -#endif -#ifdef USE_BZ2LIB - bz_stream bz; -#endif - plugin_data *plugin_data; -} handler_ctx; - -static handler_ctx *handler_ctx_init() { - handler_ctx *hctx; - - hctx = calloc(1, sizeof(*hctx)); - hctx->in = NULL; - hctx->out = NULL; - - return hctx; -} - -static void handler_ctx_free(handler_ctx *hctx) { - free(hctx); -} - -INIT_FUNC(mod_deflate_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->tmp_buf = buffer_init(); - p->encodings_arr = array_init(); - - return p; -} - -FREE_FUNC(mod_deflate_free) { - plugin_data *p = p_d; - - UNUSED(srv); - - if (!p) return HANDLER_GO_ON; - - if (p->config_storage) { - size_t i; - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s = p->config_storage[i]; - - if (!s) continue; - - array_free(s->mimetypes); - free(s); - } - free(p->config_storage); - } - - buffer_free(p->tmp_buf); - array_free(p->encodings_arr); - - free(p); - - return HANDLER_GO_ON; -} - -SETDEFAULTS_FUNC(mod_deflate_setdefaults) { - plugin_data *p = p_d; - size_t i = 0; - - config_values_t cv[] = { - { CONFIG_DEFLATE_OUTPUT_BUFFER_SIZE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_MIMETYPES, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_COMPRESSION_LEVEL, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_MEM_LEVEL, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_WINDOW_SIZE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_MIN_COMPRESS_SIZE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_WORK_BLOCK_SIZE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_ENABLED, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_DEBUG, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_SYNC_FLUSH, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_ALLOWED_ENCODINGS, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - array_reset(p->encodings_arr); - - s = calloc(1, sizeof(plugin_config)); - s->enabled = 1; - s->sync_flush = 0; - s->allowed_encodings = 0; - s->debug = 0; - s->output_buffer_size = 0; - s->mem_level = 9; - s->window_size = 15; - s->min_compress_size = 0; - s->work_block_size = 2048; - s->compression_level = -1; - s->mimetypes = array_init(); - - cv[0].destination = &(s->output_buffer_size); - cv[1].destination = s->mimetypes; - cv[2].destination = &(s->compression_level); - cv[3].destination = &(s->mem_level); - cv[4].destination = &(s->window_size); - cv[5].destination = &(s->min_compress_size); - cv[6].destination = &(s->work_block_size); - cv[7].destination = &(s->enabled); - cv[8].destination = &(s->debug); - cv[9].destination = &(s->sync_flush); - cv[10].destination = p->encodings_arr; /* temp array for allowed encodings list */ - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - - if (p->encodings_arr->used) { - size_t j = 0; - for (j = 0; j < p->encodings_arr->used; j++) { - data_string *ds = (data_string *)p->encodings_arr->data[j]; -#ifdef USE_ZLIB - if (NULL != strstr(BUF_STR(ds->value), ENCODING_NAME_GZIP)) - s->allowed_encodings |= HTTP_ACCEPT_ENCODING_GZIP; - if (NULL != strstr(BUF_STR(ds->value), ENCODING_NAME_DEFLATE)) - s->allowed_encodings |= HTTP_ACCEPT_ENCODING_DEFLATE; -#endif - /* - if (NULL != strstr(BUF_STR(ds->value), ENCODING_NAME_COMPRESS)) - s->allowed_encodings |= HTTP_ACCEPT_ENCODING_COMPRESS; - */ -#ifdef USE_BZ2LIB - if (NULL != strstr(BUF_STR(ds->value), ENCODING_NAME_BZIP2)) - s->allowed_encodings |= HTTP_ACCEPT_ENCODING_BZIP2; -#endif - } - } else { - /* default encodings */ - s->allowed_encodings = HTTP_ACCEPT_ENCODING_IDENTITY | HTTP_ACCEPT_ENCODING_GZIP | - HTTP_ACCEPT_ENCODING_DEFLATE | HTTP_ACCEPT_ENCODING_COMPRESS | HTTP_ACCEPT_ENCODING_BZIP2; - } - - if((s->compression_level < 1 || s->compression_level > 9) && - s->compression_level != -1) { - ERROR("compression-level must be between 1 and 9: %i", s->compression_level); - return HANDLER_ERROR; - } - - if(s->mem_level < 1 || s->mem_level > 9) { - ERROR("mem-level must be between 1 and 9: %i", s->mem_level); - return HANDLER_ERROR; - } - - if(s->window_size < 1 || s->window_size > 15) { - ERROR("window-size must be between 1 and 15: %i", s->window_size); - return HANDLER_ERROR; - } - s->window_size = 0 - s->window_size; - - if(s->sync_flush) { - s->output_buffer_size = 0; - } - } - - return HANDLER_GO_ON; - -} - -#ifdef USE_ZLIB -/* Copied gzip_header from apache 2.2's mod_deflate.c */ -/* RFC 1952 Section 2.3 defines the gzip header: - * - * +---+---+---+---+---+---+---+---+---+---+ - * |ID1|ID2|CM |FLG| MTIME |XFL|OS | - * +---+---+---+---+---+---+---+---+---+---+ - */ -static const char gzip_header[10] = -{ '\037', '\213', Z_DEFLATED, 0, - 0, 0, 0, 0, /* mtime */ - 0, 0x03 /* Unix OS_CODE */ -}; -static int stream_deflate_init(server *srv, connection *con, handler_ctx *hctx) { - plugin_data *p = hctx->plugin_data; - z_stream *z; - int r, compression_level; - - UNUSED(srv); - UNUSED(con); - - z = &(hctx->z); - z->zalloc = Z_NULL; - z->zfree = Z_NULL; - z->opaque = Z_NULL; - z->total_in = 0; - z->total_out = 0; - z->next_out = NULL; - z->avail_out = 0; - - compression_level = p->conf.compression_level; - if(compression_level == -1) - compression_level = Z_DEFAULT_COMPRESSION; - - if(p->conf.debug) { - TRACE("output-buffer-size: %i", p->conf.output_buffer_size); - TRACE("compression-level: %i", compression_level); - TRACE("mem-level: %i", p->conf.mem_level); - TRACE("window-size: %i", p->conf.window_size); - TRACE("min-compress-size: %i", p->conf.min_compress_size); - TRACE("work-block-size: %i", p->conf.work_block_size); - } - if (Z_OK != (r = deflateInit2(z, - compression_level, - Z_DEFLATED, - p->conf.window_size, /* supress zlib-header */ - p->conf.mem_level, - Z_DEFAULT_STRATEGY))) { - ERROR("deflateInit2() failed with %d", r); - return -1; - } - hctx->stream_open = 1; - - return 0; -} - -static int stream_deflate_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { - plugin_data *p = hctx->plugin_data; - z_stream *z; - int len; - int in = 0, out = 0; - - UNUSED(srv); - UNUSED(con); - - z = &(hctx->z); - - if(z->next_out == NULL) { - z->next_out = (unsigned char *)hctx->output->ptr; - z->avail_out = hctx->output->size; - } - - if(hctx->compression_type == HTTP_ACCEPT_ENCODING_GZIP) { - if(hctx->gzip_header == 0) { - hctx->gzip_header = 1; - /* copy gzip header into output buffer */ - buffer_copy_memory(hctx->output, gzip_header, sizeof(gzip_header)); - if(p->conf.debug) { - TRACE("gzip_header len=%zu", sizeof(gzip_header)); - } - /* initialize crc32 */ - hctx->crc = crc32(0L, Z_NULL, 0); - z->next_out = (unsigned char *)(hctx->output->ptr + sizeof(gzip_header)); - z->avail_out = hctx->output->size - sizeof(gzip_header); - } - hctx->crc = crc32(hctx->crc, start, st_size); - } - - z->next_in = start; - z->avail_in = st_size; - hctx->bytes_in += st_size; - - /* compress data */ - in = z->avail_in; - do { - if (Z_OK != deflate(z, Z_NO_FLUSH)) { - deflateEnd(z); - hctx->stream_open = 0; - return -1; - } - - if(z->avail_out == 0 || z->avail_in > 0) { - len = hctx->output->size - z->avail_out; - out += len; - chunkqueue_append_mem(hctx->out, hctx->output->ptr, len); - hctx->out->bytes_in += len; - z->next_out = (unsigned char *)hctx->output->ptr; - z->avail_out = hctx->output->size; - } - } while (z->avail_in > 0); - - if(p->conf.debug) { - TRACE("compress: in=%i, out=%i", in, out); - } - return st_size; -} - -static int stream_deflate_flush(server *srv, connection *con, handler_ctx *hctx, int end) { - plugin_data *p = hctx->plugin_data; - z_stream *z; - int len; - int rc = 0; - int done; - int flush = 1; - int in = 0, out = 0; - - UNUSED(srv); - UNUSED(con); - - z = &(hctx->z); - - if(z->next_out == NULL) { - z->next_out = (unsigned char *)hctx->output->ptr; - z->avail_out = hctx->output->size; - } - /* compress data */ - in = z->avail_in; - do { - done = 1; - if(end) { - rc = deflate(z, Z_FINISH); - if (rc == Z_OK) { - done = 0; - } else if (rc != Z_STREAM_END) { - deflateEnd(z); - hctx->stream_open = 0; - return -1; - } - } else { - if(p->conf.sync_flush) { - rc = deflate(z, Z_SYNC_FLUSH); - } else if(z->avail_in > 0) { - if(p->conf.output_buffer_size > 0) flush = 0; - rc = deflate(z, Z_NO_FLUSH); - } else { - if(p->conf.output_buffer_size > 0) flush = 0; - rc = Z_OK; - } - if (rc != Z_OK) { - deflateEnd(z); - hctx->stream_open = 0; - return -1; - } - } - - len = hctx->output->size - z->avail_out; - if(z->avail_out == 0 || (flush && len > 0)) { - out += len; - chunkqueue_append_mem(hctx->out, hctx->output->ptr, len); - hctx->out->bytes_in += len; - z->next_out = (unsigned char *)hctx->output->ptr; - z->avail_out = hctx->output->size; - } - } while (z->avail_in != 0 || !done); - - - if(p->conf.debug) { - TRACE("flush: in=%i, out=%i", in, out); - } - if(p->conf.sync_flush) { - z->next_out = NULL; - z->avail_out = 0; - } - return 0; -} - -static int stream_deflate_end(server *srv, connection *con, handler_ctx *hctx) { - plugin_data *p = hctx->plugin_data; - z_stream *z; - int rc; - - UNUSED(srv); - UNUSED(con); - - z = &(hctx->z); - if(!hctx->stream_open) return 0; - hctx->stream_open = 0; - - if(hctx->compression_type == HTTP_ACCEPT_ENCODING_GZIP && (hctx->gzip_header)) { - /* write gzip footer */ - unsigned char c[8]; - - c[0] = (hctx->crc >> 0) & 0xff; - c[1] = (hctx->crc >> 8) & 0xff; - c[2] = (hctx->crc >> 16) & 0xff; - c[3] = (hctx->crc >> 24) & 0xff; - c[4] = (z->total_in >> 0) & 0xff; - c[5] = (z->total_in >> 8) & 0xff; - c[6] = (z->total_in >> 16) & 0xff; - c[7] = (z->total_in >> 24) & 0xff; - /* append footer to write_queue */ - chunkqueue_append_mem(hctx->out, (char *)c, 8); - hctx->out->bytes_in += 8; - if(p->conf.debug) { - TRACE("gzip_footer len=%i", 8); - } - } - - if ((rc = deflateEnd(z)) != Z_OK) { - if(rc == Z_DATA_ERROR) return 0; - if(z->msg != NULL) { - ERROR("deflateEnd error ret=%i, msg='%s'", rc, z->msg); - } else { - ERROR("deflateEnd error ret=%i", rc); - } - return -1; - } - return 0; -} - -#endif - -#ifdef USE_BZ2LIB -static int stream_bzip2_init(server *srv, connection *con, handler_ctx *hctx) { - plugin_data *p = hctx->plugin_data; - bz_stream *bz; - int compression_level; - - UNUSED(srv); - UNUSED(con); - - bz = &(hctx->bz); - bz->bzalloc = NULL; - bz->bzfree = NULL; - bz->opaque = NULL; - bz->total_in_lo32 = 0; - bz->total_in_hi32 = 0; - bz->total_out_lo32 = 0; - bz->total_out_hi32 = 0; - - compression_level = p->conf.compression_level; - if(compression_level == -1) - compression_level = 9; - - if(p->conf.debug) { - TRACE("output-buffer-size: %i", p->conf.output_buffer_size); - TRACE("compression-level: %i", compression_level); - TRACE("mem-level: %i", p->conf.mem_level); - TRACE("window-size: %i", p->conf.window_size); - TRACE("min-compress-size: %i", p->conf.min_compress_size); - TRACE("work-block-size: %i", p->conf.work_block_size); - } - if (BZ_OK != BZ2_bzCompressInit(bz, - compression_level, /* blocksize */ - 0, /* no output */ - 30)) { /* workFactor: default */ - return -1; - } - hctx->stream_open = 1; - - return 0; -} - -static int stream_bzip2_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { - plugin_data *p = hctx->plugin_data; - bz_stream *bz; - int len; - int rc; - int in = 0, out = 0; - - UNUSED(srv); - UNUSED(con); - - bz = &(hctx->bz); - - if(bz->next_out == NULL) { - bz->next_out = hctx->output->ptr; - bz->avail_out = hctx->output->size; - } - - bz->next_in = (char *)start; - bz->avail_in = st_size; - hctx->bytes_in += st_size; - - /* compress data */ - in = bz->avail_in; - do { - rc = BZ2_bzCompress(bz, BZ_RUN); - if (rc != BZ_RUN_OK) { - BZ2_bzCompressEnd(bz); - hctx->stream_open = 0; - return -1; - } - - if(bz->avail_out == 0 || bz->avail_in > 0) { - len = hctx->output->size - bz->avail_out; - out += len; - chunkqueue_append_mem(hctx->out, hctx->output->ptr, len); - hctx->out->bytes_in += len; - bz->next_out = hctx->output->ptr; - bz->avail_out = hctx->output->size; - } - } while (bz->avail_in > 0); - if(p->conf.debug) { - TRACE("compress: in=%i, out=%i", in, out); - } - return st_size; -} - -static int stream_bzip2_flush(server *srv, connection *con, handler_ctx *hctx, int end) { - plugin_data *p = hctx->plugin_data; - bz_stream *bz; - int len; - int rc; - int done; - int flush = 1; - int in = 0, out = 0; - - UNUSED(srv); - UNUSED(con); - - bz = &(hctx->bz); - - if(bz->next_out == NULL) { - bz->next_out = hctx->output->ptr; - bz->avail_out = hctx->output->size; - } - /* compress data */ - in = bz->avail_in; - do { - done = 1; - if(end) { - rc = BZ2_bzCompress(bz, BZ_FINISH); - if (rc == BZ_FINISH_OK) { - done = 0; - } else if (rc != BZ_STREAM_END) { - BZ2_bzCompressEnd(bz); - hctx->stream_open = 0; - return -1; - } - } else if(bz->avail_in > 0) { - rc = BZ2_bzCompress(bz, BZ_RUN); - if (rc != BZ_RUN_OK) { - BZ2_bzCompressEnd(bz); - hctx->stream_open = 0; - return -1; - } - if(p->conf.output_buffer_size > 0) flush = 0; - } - - len = hctx->output->size - bz->avail_out; - if(bz->avail_out == 0 || (flush && len > 0)) { - out += len; - chunkqueue_append_mem(hctx->out, hctx->output->ptr, len); - hctx->out->bytes_in += len; - bz->next_out = hctx->output->ptr; - bz->avail_out = hctx->output->size; - } - } while (bz->avail_in != 0 || !done); - if(p->conf.debug) { - TRACE("flush: in=%i, out=%i", in, out); - } - if(p->conf.sync_flush) { - bz->next_out = NULL; - bz->avail_out = 0; - } - return 0; -} - -static int stream_bzip2_end(server *srv, connection *con, handler_ctx *hctx) { - plugin_data *p = hctx->plugin_data; - bz_stream *bz; - int rc; - - UNUSED(p); - UNUSED(srv); - UNUSED(con); - - bz = &(hctx->bz); - if(!hctx->stream_open) return 0; - hctx->stream_open = 0; - - if ((rc = BZ2_bzCompressEnd(bz)) != BZ_OK) { - if(rc == BZ_DATA_ERROR) return 0; - ERROR("BZ2_bzCompressEnd error ret=%i", rc); - return -1; - } - return 0; -} - -#endif - -static int mod_deflate_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { - int ret = -1; - if(st_size == 0) return 0; - switch(hctx->compression_type) { -#ifdef USE_ZLIB - case HTTP_ACCEPT_ENCODING_GZIP: - case HTTP_ACCEPT_ENCODING_DEFLATE: - ret = stream_deflate_compress(srv, con, hctx, start, st_size); - break; -#endif -#ifdef USE_BZ2LIB - case HTTP_ACCEPT_ENCODING_BZIP2: - ret = stream_bzip2_compress(srv, con, hctx, start, st_size); - break; -#endif - default: - ret = -1; - break; - } - - return ret; -} - -static int mod_deflate_stream_flush(server *srv, connection *con, handler_ctx *hctx, int end) { - int ret = -1; - - if(hctx->bytes_in == 0) return 0; - switch(hctx->compression_type) { -#ifdef USE_ZLIB - case HTTP_ACCEPT_ENCODING_GZIP: - case HTTP_ACCEPT_ENCODING_DEFLATE: - ret = stream_deflate_flush(srv, con, hctx, end); - break; -#endif -#ifdef USE_BZ2LIB - case HTTP_ACCEPT_ENCODING_BZIP2: - ret = stream_bzip2_flush(srv, con, hctx, end); - break; -#endif - default: - ret = -1; - break; - } - - return ret; -} - -static int mod_deflate_stream_end(server *srv, connection *con, handler_ctx *hctx) { - int ret = -1; - switch(hctx->compression_type) { -#ifdef USE_ZLIB - case HTTP_ACCEPT_ENCODING_GZIP: - case HTTP_ACCEPT_ENCODING_DEFLATE: - ret = stream_deflate_end(srv, con, hctx); - break; -#endif -#ifdef USE_BZ2LIB - case HTTP_ACCEPT_ENCODING_BZIP2: - ret = stream_bzip2_end(srv, con, hctx); - break; -#endif - default: - ret = -1; - break; - } - - return ret; -} - -static int mod_deflate_file_chunk(server *srv, connection *con, handler_ctx *hctx, chunk *c, off_t st_size) { - plugin_data *p = hctx->plugin_data; - off_t abs_offset; - off_t toSend; - stat_cache_entry *sce = NULL; - size_t we_want_to_mmap = 2 MByte; - size_t we_want_to_send = st_size; - char *start = NULL; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { - ERROR("stat_cache_get_entry('%s') failed: %s", - SAFE_BUF_STR(c->file.name), strerror(errno)); - return -1; - } - - abs_offset = c->file.start + c->offset; - - if (c->file.length + c->file.start > sce->st.st_size) { - ERROR("file '%s' was shrinked: was %ju, is %ju (%ju, %ju)", - SAFE_BUF_STR(c->file.name), (intmax_t) c->file.length + c->file.start, (intmax_t) sce->st.st_size, - (intmax_t) c->file.start, (intmax_t) c->offset); - - return -1; - } - - we_want_to_send = st_size; - /* mmap the buffer - * - first mmap - * - new mmap as the we are at the end of the last one */ - if (c->file.mmap.start == MAP_FAILED || - abs_offset == (off_t)(c->file.mmap.offset + c->file.mmap.length)) { - - /* Optimizations for the future: - * - * adaptive mem-mapping - * the problem: - * we mmap() the whole file. If someone has alot large files and 32bit - * machine the virtual address area will be unrun and we will have a failing - * mmap() call. - * solution: - * only mmap 16M in one chunk and move the window as soon as we have finished - * the first 8M - * - * read-ahead buffering - * the problem: - * sending out several large files in parallel trashes the read-ahead of the - * kernel leading to long wait-for-seek times. - * solutions: (increasing complexity) - * 1. use madvise - * 2. use a internal read-ahead buffer in the chunk-structure - * 3. use non-blocking IO for file-transfers - * */ - - /* all mmap()ed areas are 512kb expect the last which might be smaller */ - size_t to_mmap; - - /* this is a remap, move the mmap-offset */ - if (c->file.mmap.start != MAP_FAILED) { - munmap(c->file.mmap.start, c->file.mmap.length); - c->file.mmap.offset += we_want_to_mmap; - } else { - /* in case the range-offset is after the first mmap()ed area we skip the area */ - c->file.mmap.offset = 0; - - while (c->file.mmap.offset + (off_t) we_want_to_mmap < c->file.start) { - c->file.mmap.offset += we_want_to_mmap; - } - } - - /* length is rel, c->offset too, assume there is no limit at the mmap-boundaries */ - to_mmap = (c->file.start + c->file.length) - c->file.mmap.offset; - if(to_mmap > we_want_to_mmap) to_mmap = we_want_to_mmap; - /* we have more to send than we can mmap() at once */ - if(we_want_to_send > to_mmap) we_want_to_send = to_mmap; - - if (-1 == c->file.fd) { /* open the file if not already open */ - if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { - ERROR("open failed for '%s': %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - return -1; - } -#ifdef FD_CLOEXEC - fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); -#endif - } - - if (MAP_FAILED == (c->file.mmap.start = mmap(0, to_mmap, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) { - /* close it here, otherwise we'd have to set FD_CLOEXEC */ - - ERROR("mmap failed for '%s' (%i): %s", SAFE_BUF_STR(c->file.name), c->file.fd, strerror(errno)); - return -1; - } - - c->file.mmap.length = to_mmap; -#ifdef LOCAL_BUFFERING - buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length); -#else -#ifdef HAVE_MADVISE - /* don't advise files < 64Kb */ - if (c->file.mmap.length > (64 KByte) && - 0 != madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED)) { - ERROR("madvise failed for '%s' (%i): %s", SAFE_BUF_STR(c->file.name), c->file.fd, strerror(errno)); - } -#endif -#endif - - /* chunk_reset() or chunk_free() will cleanup for us */ - } - - /* to_send = abs_mmap_end - abs_offset */ - toSend = (c->file.mmap.offset + c->file.mmap.length) - (abs_offset); - if (toSend > (off_t) we_want_to_send) toSend = we_want_to_send; - - if (toSend < 0) { - ERROR("toSend is negative: %u %u %u %u", - (unsigned int) toSend, - (unsigned int) c->file.mmap.length, - (unsigned int) abs_offset, - (unsigned int) c->file.mmap.offset); - assert(toSend < 0); - } - -#ifdef LOCAL_BUFFERING - start = c->mem->ptr; -#else - start = c->file.mmap.start; -#endif - - if(p->conf.debug) { - TRACE("compress file chunk: offset=%i, toSend=%i", (int)c->offset, (int)toSend); - } - if (mod_deflate_compress(srv, con, hctx, - (unsigned char *)start + (abs_offset - c->file.mmap.offset), toSend) < 0) { - ERROR("%s", "compress failed."); - return -1; - } - - return toSend; -} - -static int deflate_compress_cleanup(server *srv, connection *con, handler_ctx *hctx) { - plugin_data *p = hctx->plugin_data; - int rc; - - rc = mod_deflate_stream_end(srv, con, hctx); - if(rc < 0) { - TRACE("error closing compressed stream for '%s', compressing with %d: %d", - SAFE_BUF_STR(con->uri.path_raw), hctx->compression_type, rc); - } - - if(p->conf.debug && hctx->bytes_in < hctx->out->bytes_in) { - TRACE("compressing uri '%s' increased the sent content-size from %jd to %jd", - SAFE_BUF_STR(con->uri.path_raw), (intmax_t) hctx->bytes_in, (intmax_t) hctx->out->bytes_in); - } - - /* cleanup compression state */ - if(hctx->output != p->tmp_buf) { - buffer_free(hctx->output); - } - handler_ctx_free(hctx); - con->plugin_ctx[p->id] = NULL; - - return 0; -} - -/** - * compress the in queue and move the content to the out queue - * - */ -static handler_t deflate_compress_response(server *srv, connection *con, handler_ctx *hctx, int end) { - plugin_data *p = hctx->plugin_data; - chunk *c; - size_t chunks_written = 0; - int chunk_finished = 0; - int rc=-1; - int we_want = 0, we_have = 0; - int out = 0, max = 0; - - we_have = chunkqueue_length(hctx->in); - if (p->conf.debug) { - TRACE("compress: in_queue len=%i", we_have); - } - /* calculate max bytes to compress for this call. */ - if (!end) { - max = p->conf.work_block_size * 1024; - if(max == 0 || max > we_have) max = we_have; - } else { - max = we_have; - } - - /* Compress chunks from in queue into chunks for out queue */ - for (c = hctx->in->first; c && max > 0; c = c->next) { - chunk_finished = 0; - we_have = 0; - we_want = 0; - - switch(c->type) { - case MEM_CHUNK: - if (c->mem->used == 0) continue; - - we_have = c->mem->used - c->offset - 1; - if (we_have == 0) continue; - - we_want = we_have < max ? we_have : max; - - if (mod_deflate_compress(srv, con, hctx, (unsigned char *)(c->mem->ptr + c->offset), we_want) < 0) { - ERROR("%s", "compress failed."); - return HANDLER_ERROR; - } - - break; - case FILE_CHUNK: - if (c->file.length == 0) continue; - - we_have = c->file.length - c->offset; - if (we_have == 0) continue; - - we_want = we_have < max ? we_have : max; - - if ((we_want = mod_deflate_file_chunk(srv, con, hctx, c, we_want)) < 0) { - ERROR("%s", "compress file chunk failed."); - return HANDLER_ERROR; - } - - break; - default: - ERROR("type not known: %d", c->type); - - return HANDLER_ERROR; - } - - hctx->in->bytes_out += we_want; - c->offset += we_want; - out += we_want; - max -= we_want; - - if (c->type == FILE_CHUNK && - c->offset == c->file.length && - c->file.mmap.start != MAP_FAILED) { - /* we don't need the mmaping anymore */ - munmap(c->file.mmap.start, c->file.mmap.length); - c->file.mmap.start = MAP_FAILED; - } - - if (we_have == we_want) { - /* chunk finished */ - chunk_finished = 1; - chunks_written++; - } - /* make sure we finished compressing the chunk before going to the next chunk */ - if(!chunk_finished) break; - } - - if (p->conf.debug) { - TRACE("compressed bytes: %i", out); - } - - if (chunks_written > 0) { - chunkqueue_remove_finished_chunks(hctx->in); - } - - /* check if we finished compressing all the content. */ - end = (hctx->in->is_closed && hctx->in->bytes_in == hctx->in->bytes_out); - - if (p->conf.debug) { - TRACE("end: %d - %jd - %jd", hctx->in->is_closed, (intmax_t) hctx->in->bytes_in, (intmax_t) hctx->in->bytes_out); - } - - /* flush the output buffer to make room for more data. */ - rc = mod_deflate_stream_flush(srv, con, hctx, end); - - if (rc < 0) { - ERROR("%s", "flush error"); - } - - if (end) { - hctx->out->is_closed = 1; - if(p->conf.debug) { - TRACE("finished uri: '%s', query: '%s'", SAFE_BUF_STR(con->uri.path_raw), SAFE_BUF_STR(con->uri.query)); - } - } else if (hctx->in->first) { - /* We have more data to compress. */ - joblist_append(srv, con); - } - - return HANDLER_GO_ON; -} - -static int mod_deflate_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - - PATCH_OPTION(output_buffer_size); - PATCH_OPTION(mimetypes); - PATCH_OPTION(compression_level); - PATCH_OPTION(mem_level); - PATCH_OPTION(window_size); - PATCH_OPTION(min_compress_size); - PATCH_OPTION(work_block_size); - PATCH_OPTION(enabled); - PATCH_OPTION(debug); - PATCH_OPTION(allowed_encodings); - PATCH_OPTION(sync_flush); - - /* skip the first, the global context */ - for (i = 1; i < srv->config_context->used; i++) { - data_config *dc = (data_config *)srv->config_context->data[i]; - s = p->config_storage[i]; - - /* condition didn't match */ - if (!config_check_cond(srv, con, dc)) continue; - - /* merge config */ - for (j = 0; j < dc->value->used; j++) { - data_unset *du = dc->value->data[j]; - - if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_OUTPUT_BUFFER_SIZE))) { - PATCH_OPTION(output_buffer_size); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_MIMETYPES))) { - PATCH_OPTION(mimetypes); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_COMPRESSION_LEVEL))) { - PATCH_OPTION(compression_level); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_MEM_LEVEL))) { - PATCH_OPTION(mem_level); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_WINDOW_SIZE))) { - PATCH_OPTION(window_size); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_MIN_COMPRESS_SIZE))) { - PATCH_OPTION(min_compress_size); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_WORK_BLOCK_SIZE))) { - PATCH_OPTION(work_block_size); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_ENABLED))) { - PATCH_OPTION(enabled); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_DEBUG))) { - PATCH_OPTION(debug); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_ALLOWED_ENCODINGS))) { - PATCH_OPTION(allowed_encodings); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_SYNC_FLUSH))) { - PATCH_OPTION(sync_flush); - } - } - } - - return 0; -} - -PHYSICALPATH_FUNC(mod_deflate_handle_response_header) { - plugin_data *p = p_d; - handler_ctx *hctx; - filter *fl; - chunkqueue *in; - data_string *ds; - int accept_encoding = 0; - char *value; - int matched_encodings = 0; - const char *compression_name = NULL; - int file_len=0; - int rc=-2; - int end = 0; - size_t m; - - /* disable compression for some http status types. */ - switch(con->http_status) { - case 100: - case 101: - case 204: - case 205: - case 304: - /* disable compression as we have no response entity */ - return HANDLER_GO_ON; - default: - break; - } - - mod_deflate_patch_connection(srv, con, p); - - /* is compression allowed */ - if(!p->conf.enabled) { - if(p->conf.debug) { - TRACE("%s", "compression disabled."); - } - return HANDLER_GO_ON; - } - - /* Check if response has a Content-Encoding. */ - if (NULL != (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Content-Encoding")))) { - return HANDLER_GO_ON; - } - - /* Check Accept-Encoding for supported encoding. */ - if (NULL == (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Accept-Encoding")))) { - return HANDLER_GO_ON; - } - - /* get client side support encodings */ - value = ds->value->ptr; -#ifdef USE_ZLIB - if (NULL != strstr(value, ENCODING_NAME_GZIP)) accept_encoding |= HTTP_ACCEPT_ENCODING_GZIP; - if (NULL != strstr(value, ENCODING_NAME_DEFLATE)) accept_encoding |= HTTP_ACCEPT_ENCODING_DEFLATE; -#endif - /* if (NULL != strstr(value, ENCODING_NAME_COMPRESS)) accept_encoding |= HTTP_ACCEPT_ENCODING_COMPRESS; */ -#ifdef USE_BZ2LIB - if (NULL != strstr(value, ENCODING_NAME_BZIP2)) accept_encoding |= HTTP_ACCEPT_ENCODING_BZIP2; -#endif - if (NULL != strstr(value, ENCODING_NAME_IDENTITY)) accept_encoding |= HTTP_ACCEPT_ENCODING_IDENTITY; - - /* find matching encodings */ - matched_encodings = accept_encoding & p->conf.allowed_encodings; - if (!matched_encodings) { - return HANDLER_GO_ON; - } - -#if 0 - /* TODO: add option to disable compression for HTTP 1.0 clients. */ - if (con->request.true_http_10_client) { - /*disable gzip/bzip2 when we meet HTTP 1.0 client with Accept-Encoding - * maybe old buggy proxy server - */ - /* most of buggy clients are Yahoo Slurp;) */ - if (p->conf.debug) TRACE("Buggy HTTP 1.0 client sending 'Accept Encoding: gzip, deflate': %i", - (char *) inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - return HANDLER_GO_ON; - } -#endif - - /* get filter. */ - fl = filter_chain_get_filter(con->send_filters, p->id); - if (!fl) { - if(p->conf.debug) TRACE("%s", "add deflate filter to filter chain"); - fl = filter_chain_create_filter(con->send_filters, p->id); - } - /* get our input and output chunkqueues. */ - if (!fl || !fl->prev) { - filter_chain_remove_filter(con->send_filters, fl); - return HANDLER_GO_ON; - } - in = fl->prev->cq; - - /* check if size of response is below min-compress-size */ - if(in->is_closed && con->request.http_method != HTTP_METHOD_HEAD) { - file_len = chunkqueue_length(in); - if(file_len == 0) { - filter_chain_remove_filter(con->send_filters, fl); - return HANDLER_GO_ON; - } - } else { - file_len = 0; - } - - if(file_len > 0 && p->conf.min_compress_size > 0 && file_len < p->conf.min_compress_size) { - if(p->conf.debug) { - TRACE("Content-Length smaller then min_compress_size: file_len=%i", file_len); - } - filter_chain_remove_filter(con->send_filters, fl); - return HANDLER_GO_ON; - } - - /* Check mimetype in response header "Content-Type" */ - if (NULL != (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Content-Type")))) { - int found = 0; - if(p->conf.debug) { - TRACE("Content-Type: %s", SAFE_BUF_STR(ds->value)); - } - for (m = 0; m < p->conf.mimetypes->used; m++) { - data_string *mimetype = (data_string *)p->conf.mimetypes->data[m]; - - if(p->conf.debug) { - TRACE("mime-type: %s", SAFE_BUF_STR(mimetype->value)); - } - if (strncmp(mimetype->value->ptr, ds->value->ptr, mimetype->value->used-1) == 0) { - /* mimetype found */ - found = 1; - break; - } - } - if(!found && p->conf.mimetypes->used > 0) { - if(p->conf.debug) { - TRACE("No compression for mimetype: %s", SAFE_BUF_STR(ds->value)); - } - filter_chain_remove_filter(con->send_filters, fl); - return HANDLER_GO_ON; - } -#if 0 - if(strncasecmp(ds->value->ptr, "application/x-javascript", 24) == 0) { - /*reset compress type to deflate for javascript - * prevent buggy IE6 SP1 doesn't work for js in IFrame - */ - matched_encodings = HTTP_ACCEPT_ENCODING_DEFLATE; - } -#endif - } - - /* the response might change according to Accept-Encoding */ - if (NULL != (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Vary")))) { - /* append Accept-Encoding to Vary header */ - if (NULL == strstr(ds->value->ptr, "Accept-Encoding")) { - buffer_append_string_len(ds->value, CONST_STR_LEN(",Accept-Encoding")); - if (p->conf.debug) { - TRACE("appending ,Accept-Encoding for '%s'", SAFE_BUF_STR(con->uri.path)); - } - } - } else { - if (p->conf.debug) { - TRACE("add Vary: Accept-Encoding for '%s'", SAFE_BUF_STR(con->uri.path)); - } - response_header_insert(srv, con, CONST_STR_LEN("Vary"), - CONST_STR_LEN("Accept-Encoding")); - } - - /* enable compression */ - hctx = handler_ctx_init(); - hctx->plugin_data = p; - hctx->fl = fl; - hctx->in = fl->prev->cq; - hctx->out = fl->cq; - - rc = -1; - - /* select best matching encoding */ - if (matched_encodings & HTTP_ACCEPT_ENCODING_BZIP2) { -#ifdef USE_BZ2LIB - hctx->compression_type = HTTP_ACCEPT_ENCODING_BZIP2; - compression_name = ENCODING_NAME_BZIP2; - rc = stream_bzip2_init(srv, con, hctx); -#endif - } else if (matched_encodings & HTTP_ACCEPT_ENCODING_GZIP) { -#ifdef USE_ZLIB - hctx->compression_type = HTTP_ACCEPT_ENCODING_GZIP; - compression_name = ENCODING_NAME_GZIP; - rc = stream_deflate_init(srv, con, hctx); - } else if (matched_encodings & HTTP_ACCEPT_ENCODING_DEFLATE) { - hctx->compression_type = HTTP_ACCEPT_ENCODING_DEFLATE; - compression_name = ENCODING_NAME_DEFLATE; - rc = stream_deflate_init(srv, con, hctx); -#endif - } else if (matched_encodings & HTTP_ACCEPT_ENCODING_IDENTITY) { - /* no compression */ - } else { - ERROR("Failed to initialize compression for '%s' with compression = %s", - SAFE_BUF_STR(con->uri.path), - value); - } - - if (rc == -1 && compression_name) { - ERROR("Failed to initialize compression for '%s' with compression = %s", - SAFE_BUF_STR(con->uri.path), - compression_name); - } - - if (rc < 0) { - filter_chain_remove_filter(con->send_filters, hctx->fl); - handler_ctx_free(hctx); - return HANDLER_GO_ON; - } - - if (p->conf.debug) { - TRACE("compressing '%s' with compression = %s", - SAFE_BUF_STR(con->uri.path), - compression_name); - } - - /* setup output buffer. */ - if(p->conf.sync_flush || p->conf.output_buffer_size == 0) { - buffer_prepare_copy(p->tmp_buf, 32 * 1024); - hctx->output = p->tmp_buf; - } else { - hctx->output = buffer_init(); - buffer_prepare_copy(hctx->output, p->conf.output_buffer_size); - } - con->plugin_ctx[p->id] = hctx; - - /* set Content-Encoding to show selected compression type. */ - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name)); - - /* clear old Content-Length */ - con->response.content_length = -1; - - if (hctx->in->is_closed && - (p->conf.work_block_size == 0 || file_len < (p->conf.work_block_size * 1024)) - && con->request.http_method != HTTP_METHOD_HEAD) { - end = 1; - if(p->conf.debug) { - TRACE("Compress all content and use Content-Length header: uncompress len=%i", file_len); - } - } - - if (p->conf.debug) - TRACE("end = %i for uri '%s'", end, SAFE_BUF_STR(con->uri.path)); - - rc = deflate_compress_response(srv, con, hctx, end); - /* check if we finished compressing all the content. */ - if (rc == HANDLER_GO_ON && hctx->out->is_closed) { - con->response.content_length = chunkqueue_length(hctx->out); - - deflate_compress_cleanup(srv, con, hctx); - } - - return rc; -} - -CONNECTION_FUNC(mod_deflate_handle_filter_response_content) { - plugin_data *p = p_d; - handler_ctx *hctx = con->plugin_ctx[p->id]; - handler_t ret; - - if (hctx == NULL) return HANDLER_GO_ON; - if (!hctx->stream_open) return HANDLER_GO_ON; - - /** - * HEAD requests don't have a content body */ - if (con->request.http_method == HTTP_METHOD_HEAD) return HANDLER_GO_ON; - - /** compress the streams */ - ret = deflate_compress_response(srv, con, hctx, 0); - - if (ret == HANDLER_GO_ON && hctx->out->is_closed) { - deflate_compress_cleanup(srv, con, hctx); - } - - return ret; -} - -static handler_t mod_deflate_cleanup(server *srv, connection *con, void *p_d) { - plugin_data *p = p_d; - handler_ctx *hctx = con->plugin_ctx[p->id]; - - if(hctx == NULL) return HANDLER_GO_ON; - - if(p->conf.debug && hctx->stream_open) { - TRACE("stream open at cleanup. uri='%s', query='%s'", SAFE_BUF_STR(con->uri.path_raw), SAFE_BUF_STR(con->uri.query)); - } - - deflate_compress_cleanup(srv, con, hctx); - - return HANDLER_GO_ON; -} - -LI_EXPORT int mod_deflate_plugin_init(plugin *p); -LI_EXPORT int mod_deflate_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("deflate"); - - p->init = mod_deflate_init; - p->cleanup = mod_deflate_free; - p->set_defaults = mod_deflate_setdefaults; - p->connection_reset = mod_deflate_cleanup; - p->handle_connection_close = mod_deflate_cleanup; - p->handle_response_header = mod_deflate_handle_response_header; - p->handle_filter_response_content = mod_deflate_handle_filter_response_content; - - p->data = NULL; - - return 0; -} |