7 #include "stat_cache.h"
14 #include <sys/types.h>
27 #if defined HAVE_ZLIB_H && defined HAVE_LIBZ
32 #if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2
34 /* we don't need stdio interface */
39 #if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP
46 static volatile int sigbus_jmp_valid
;
47 static sigjmp_buf sigbus_jmp
;
49 static void sigbus_handler(int sig
) {
51 if (sigbus_jmp_valid
) siglongjmp(sigbus_jmp
, 1);
52 log_failed_assert(__FILE__
, __LINE__
, "SIGBUS");
56 /* request: accept-encoding */
57 #define HTTP_ACCEPT_ENCODING_IDENTITY BV(0)
58 #define HTTP_ACCEPT_ENCODING_GZIP BV(1)
59 #define HTTP_ACCEPT_ENCODING_DEFLATE BV(2)
60 #define HTTP_ACCEPT_ENCODING_COMPRESS BV(3)
61 #define HTTP_ACCEPT_ENCODING_BZIP2 BV(4)
62 #define HTTP_ACCEPT_ENCODING_X_GZIP BV(5)
63 #define HTTP_ACCEPT_ENCODING_X_BZIP2 BV(6)
66 # define mkdir(x,y) mkdir(x)
70 buffer
*compress_cache_dir
;
72 off_t compress_max_filesize
; /** max filesize in kb */
73 int allowed_encodings
;
81 plugin_config
**config_storage
;
85 INIT_FUNC(mod_compress_init
) {
88 p
= calloc(1, sizeof(*p
));
90 p
->ofn
= buffer_init();
96 FREE_FUNC(mod_compress_free
) {
101 if (!p
) return HANDLER_GO_ON
;
106 if (p
->config_storage
) {
108 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
109 plugin_config
*s
= p
->config_storage
[i
];
111 if (NULL
== s
) continue;
113 array_free(s
->compress
);
114 buffer_free(s
->compress_cache_dir
);
118 free(p
->config_storage
);
124 return HANDLER_GO_ON
;
127 /* 0 on success, -1 for error */
128 static int mkdir_recursive(char *dir
) {
134 while ((p
= strchr(p
+ 1, '/')) != NULL
) {
137 if ((mkdir(dir
, 0700) != 0) && (errno
!= EEXIST
)) {
143 if (!*p
) return 0; /* Ignore trailing slash */
146 return (mkdir(dir
, 0700) != 0) && (errno
!= EEXIST
) ? -1 : 0;
149 /* 0 on success, -1 for error */
150 static int mkdir_for_file(char *filename
) {
153 if (!filename
|| !filename
[0])
156 while ((p
= strchr(p
+ 1, '/')) != NULL
) {
159 if ((mkdir(filename
, 0700) != 0) && (errno
!= EEXIST
)) {
165 if (!*p
) return -1; /* Unexpected trailing slash in filename */
171 SETDEFAULTS_FUNC(mod_compress_setdefaults
) {
172 plugin_data
*p
= p_d
;
175 config_values_t cv
[] = {
176 { "compress.cache-dir", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
177 { "compress.filetype", NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
},
178 { "compress.max-filesize", NULL
, T_CONFIG_SHORT
, T_CONFIG_SCOPE_CONNECTION
},
179 { "compress.allowed-encodings", NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
},
180 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
183 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
185 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
186 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
188 array
*encodings_arr
= array_init();
190 s
= calloc(1, sizeof(plugin_config
));
191 s
->compress_cache_dir
= buffer_init();
192 s
->compress
= array_init();
193 s
->compress_max_filesize
= 0;
194 s
->allowed_encodings
= 0;
196 cv
[0].destination
= s
->compress_cache_dir
;
197 cv
[1].destination
= s
->compress
;
198 cv
[2].destination
= &(s
->compress_max_filesize
);
199 cv
[3].destination
= encodings_arr
; /* temp array for allowed encodings list */
201 p
->config_storage
[i
] = s
;
203 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
204 return HANDLER_ERROR
;
207 if (encodings_arr
->used
) {
209 for (j
= 0; j
< encodings_arr
->used
; j
++) {
210 data_string
*ds
= (data_string
*)encodings_arr
->data
[j
];
212 if (NULL
!= strstr(ds
->value
->ptr
, "gzip"))
213 s
->allowed_encodings
|= HTTP_ACCEPT_ENCODING_GZIP
| HTTP_ACCEPT_ENCODING_X_GZIP
;
214 if (NULL
!= strstr(ds
->value
->ptr
, "x-gzip"))
215 s
->allowed_encodings
|= HTTP_ACCEPT_ENCODING_X_GZIP
;
216 if (NULL
!= strstr(ds
->value
->ptr
, "deflate"))
217 s
->allowed_encodings
|= HTTP_ACCEPT_ENCODING_DEFLATE
;
219 if (NULL != strstr(ds->value->ptr, "compress"))
220 s->allowed_encodings |= HTTP_ACCEPT_ENCODING_COMPRESS;
224 if (NULL
!= strstr(ds
->value
->ptr
, "bzip2"))
225 s
->allowed_encodings
|= HTTP_ACCEPT_ENCODING_BZIP2
| HTTP_ACCEPT_ENCODING_X_BZIP2
;
226 if (NULL
!= strstr(ds
->value
->ptr
, "x-bzip2"))
227 s
->allowed_encodings
|= HTTP_ACCEPT_ENCODING_X_BZIP2
;
231 /* default encodings */
232 s
->allowed_encodings
= 0
234 | HTTP_ACCEPT_ENCODING_GZIP
| HTTP_ACCEPT_ENCODING_X_GZIP
| HTTP_ACCEPT_ENCODING_DEFLATE
237 | HTTP_ACCEPT_ENCODING_BZIP2
| HTTP_ACCEPT_ENCODING_X_BZIP2
242 array_free(encodings_arr
);
244 if (!buffer_string_is_empty(s
->compress_cache_dir
)) {
246 mkdir_recursive(s
->compress_cache_dir
->ptr
);
248 if (0 != stat(s
->compress_cache_dir
->ptr
, &st
)) {
249 log_error_write(srv
, __FILE__
, __LINE__
, "sbs", "can't stat compress.cache-dir",
250 s
->compress_cache_dir
, strerror(errno
));
252 return HANDLER_ERROR
;
257 return HANDLER_GO_ON
;
262 static int deflate_file_to_buffer_gzip(server
*srv
, connection
*con
, plugin_data
*p
, char *start
, off_t st_size
, time_t mtime
) {
275 if (Z_OK
!= deflateInit2(&z
,
276 Z_DEFAULT_COMPRESSION
,
278 -MAX_WBITS
, /* supress zlib-header */
280 Z_DEFAULT_STRATEGY
)) {
284 z
.next_in
= (unsigned char *)start
;
285 z
.avail_in
= st_size
;
289 buffer_string_prepare_copy(p
->b
, (z
.avail_in
* 1.1) + 12 + 18);
291 /* write gzip header */
293 c
= (unsigned char *)p
->b
->ptr
;
297 c
[3] = 0; /* options */
298 c
[4] = (mtime
>> 0) & 0xff;
299 c
[5] = (mtime
>> 8) & 0xff;
300 c
[6] = (mtime
>> 16) & 0xff;
301 c
[7] = (mtime
>> 24) & 0xff;
302 c
[8] = 0x00; /* extra flags */
303 c
[9] = 0x03; /* UNIX */
306 z
.next_out
= (unsigned char *)p
->b
->ptr
+ outlen
;
307 z
.avail_out
= p
->b
->size
- outlen
- 9;
310 if (Z_STREAM_END
!= deflate(&z
, Z_FINISH
)) {
316 outlen
+= z
.total_out
;
318 crc
= generate_crc32c(start
, st_size
);
320 c
= (unsigned char *)p
->b
->ptr
+ outlen
;
322 c
[0] = (crc
>> 0) & 0xff;
323 c
[1] = (crc
>> 8) & 0xff;
324 c
[2] = (crc
>> 16) & 0xff;
325 c
[3] = (crc
>> 24) & 0xff;
326 c
[4] = (z
.total_in
>> 0) & 0xff;
327 c
[5] = (z
.total_in
>> 8) & 0xff;
328 c
[6] = (z
.total_in
>> 16) & 0xff;
329 c
[7] = (z
.total_in
>> 24) & 0xff;
331 buffer_commit(p
->b
, outlen
);
333 if (Z_OK
!= deflateEnd(&z
)) {
340 static int deflate_file_to_buffer_deflate(server
*srv
, connection
*con
, plugin_data
*p
, unsigned char *start
, off_t st_size
) {
350 if (Z_OK
!= deflateInit2(&z
,
351 Z_DEFAULT_COMPRESSION
,
353 -MAX_WBITS
, /* supress zlib-header */
355 Z_DEFAULT_STRATEGY
)) {
360 z
.avail_in
= st_size
;
363 buffer_string_prepare_copy(p
->b
, (z
.avail_in
* 1.1) + 12);
365 z
.next_out
= (unsigned char *)p
->b
->ptr
;
366 z
.avail_out
= p
->b
->size
- 1;
369 if (Z_STREAM_END
!= deflate(&z
, Z_FINISH
)) {
374 if (Z_OK
!= deflateEnd(&z
)) {
379 buffer_commit(p
->b
, z
.total_out
);
387 static int deflate_file_to_buffer_bzip2(server
*srv
, connection
*con
, plugin_data
*p
, unsigned char *start
, off_t st_size
) {
397 if (BZ_OK
!= BZ2_bzCompressInit(&bz
,
398 9, /* blocksize = 900k */
400 0)) { /* workFactor: default */
404 bz
.next_in
= (char *)start
;
405 bz
.avail_in
= st_size
;
406 bz
.total_in_lo32
= 0;
407 bz
.total_in_hi32
= 0;
409 buffer_string_prepare_copy(p
->b
, (bz
.avail_in
* 1.1) + 12);
411 bz
.next_out
= p
->b
->ptr
;
412 bz
.avail_out
= p
->b
->size
- 1;
413 bz
.total_out_lo32
= 0;
414 bz
.total_out_hi32
= 0;
416 if (BZ_STREAM_END
!= BZ2_bzCompress(&bz
, BZ_FINISH
)) {
417 BZ2_bzCompressEnd(&bz
);
421 if (BZ_OK
!= BZ2_bzCompressEnd(&bz
)) {
425 /* file is too large for now */
426 if (bz
.total_out_hi32
) return -1;
429 buffer_commit(p
->b
, bz
.total_out_lo32
);
435 static int deflate_file_to_file(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*fn
, stat_cache_entry
*sce
, int type
) {
439 volatile int mapped
= 0;/* quiet warning: might be clobbered by 'longjmp' */
442 const char *filename
= fn
->ptr
;
446 if ((off_t
)(sce
->st
.st_size
* 1.1) < sce
->st
.st_size
) return -1;
448 /* don't mmap files > 128Mb
450 * we could use a sliding window, but currently there is no need for it
453 if (sce
->st
.st_size
> 128 * 1024 * 1024) return -1;
455 buffer_reset(p
->ofn
);
456 buffer_copy_buffer(p
->ofn
, p
->conf
.compress_cache_dir
);
457 buffer_append_slash(p
->ofn
);
459 if (0 == strncmp(con
->physical
.path
->ptr
, con
->physical
.doc_root
->ptr
, buffer_string_length(con
->physical
.doc_root
))) {
460 buffer_append_string(p
->ofn
, con
->physical
.path
->ptr
+ buffer_string_length(con
->physical
.doc_root
));
462 buffer_append_string_buffer(p
->ofn
, con
->uri
.path
);
466 case HTTP_ACCEPT_ENCODING_GZIP
:
467 case HTTP_ACCEPT_ENCODING_X_GZIP
:
468 buffer_append_string_len(p
->ofn
, CONST_STR_LEN("-gzip-"));
470 case HTTP_ACCEPT_ENCODING_DEFLATE
:
471 buffer_append_string_len(p
->ofn
, CONST_STR_LEN("-deflate-"));
473 case HTTP_ACCEPT_ENCODING_BZIP2
:
474 case HTTP_ACCEPT_ENCODING_X_BZIP2
:
475 buffer_append_string_len(p
->ofn
, CONST_STR_LEN("-bzip2-"));
478 log_error_write(srv
, __FILE__
, __LINE__
, "sd", "unknown compression type", type
);
482 buffer_append_string_buffer(p
->ofn
, sce
->etag
);
484 if (-1 == mkdir_for_file(p
->ofn
->ptr
)) {
485 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "couldn't create directory for file", p
->ofn
);
489 if (-1 == (ofd
= open(p
->ofn
->ptr
, O_WRONLY
| O_CREAT
| O_EXCL
| O_BINARY
, 0600))) {
490 if (errno
== EEXIST
) {
491 /* cache-entry exists */
493 log_error_write(srv
, __FILE__
, __LINE__
, "bs", p
->ofn
, "compress-cache hit");
495 buffer_copy_buffer(con
->physical
.path
, p
->ofn
);
500 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "creating cachefile", p
->ofn
, "failed", strerror(errno
));
505 log_error_write(srv
, __FILE__
, __LINE__
, "bs", p
->ofn
, "compress-cache miss");
507 if (-1 == (ifd
= open(filename
, O_RDONLY
| O_BINARY
))) {
508 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "opening plain-file", fn
, "failed", strerror(errno
));
512 /* Remove the incomplete cache file, so that later hits aren't served from it */
513 if (-1 == unlink(p
->ofn
->ptr
)) {
514 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "unlinking incomplete cachefile", p
->ofn
, "failed:", strerror(errno
));
521 if (MAP_FAILED
!= (start
= mmap(NULL
, sce
->st
.st_size
, PROT_READ
, MAP_SHARED
, ifd
, 0))) {
523 signal(SIGBUS
, sigbus_handler
);
524 sigbus_jmp_valid
= 1;
525 if (0 != sigsetjmp(sigbus_jmp
, 1)) {
526 sigbus_jmp_valid
= 0;
528 log_error_write(srv
, __FILE__
, __LINE__
, "sbd", "SIGBUS in mmap:",
531 munmap(start
, sce
->st
.st_size
);
535 /* Remove the incomplete cache file, so that later hits aren't served from it */
536 if (-1 == unlink(p
->ofn
->ptr
)) {
537 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "unlinking incomplete cachefile", p
->ofn
, "failed:", strerror(errno
));
543 #endif /* FIXME: might attempt to read very large file completely into memory; see compress.max-filesize config option */
544 if (NULL
== (start
= malloc(sce
->st
.st_size
)) || sce
->st
.st_size
!= read(ifd
, start
, sce
->st
.st_size
)) {
545 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "reading", fn
, "failed", strerror(errno
));
551 /* Remove the incomplete cache file, so that later hits aren't served from it */
552 if (-1 == unlink(p
->ofn
->ptr
)) {
553 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "unlinking incomplete cachefile", p
->ofn
, "failed:", strerror(errno
));
562 case HTTP_ACCEPT_ENCODING_GZIP
:
563 case HTTP_ACCEPT_ENCODING_X_GZIP
:
564 ret
= deflate_file_to_buffer_gzip(srv
, con
, p
, start
, sce
->st
.st_size
, sce
->st
.st_mtime
);
566 case HTTP_ACCEPT_ENCODING_DEFLATE
:
567 ret
= deflate_file_to_buffer_deflate(srv
, con
, p
, start
, sce
->st
.st_size
);
571 case HTTP_ACCEPT_ENCODING_BZIP2
:
572 case HTTP_ACCEPT_ENCODING_X_BZIP2
:
573 ret
= deflate_file_to_buffer_bzip2(srv
, con
, p
, start
, sce
->st
.st_size
);
579 r
= write(ofd
, CONST_BUF_LEN(p
->b
));
581 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "writing cachefile", p
->ofn
, "failed:", strerror(errno
));
583 } else if ((size_t)r
!= buffer_string_length(p
->b
)) {
584 log_error_write(srv
, __FILE__
, __LINE__
, "sbs", "writing cachefile", p
->ofn
, "failed: not enough bytes written");
591 sigbus_jmp_valid
= 0;
592 munmap(start
, sce
->st
.st_size
);
599 if (0 != close(ofd
) || ret
!= 0) {
601 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "writing cachefile", p
->ofn
, "failed:", strerror(errno
));
604 /* Remove the incomplete cache file, so that later hits aren't served from it */
605 if (-1 == unlink(p
->ofn
->ptr
)) {
606 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "unlinking incomplete cachefile", p
->ofn
, "failed:", strerror(errno
));
612 buffer_copy_buffer(con
->physical
.path
, p
->ofn
);
617 static int deflate_file_to_buffer(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*fn
, stat_cache_entry
*sce
, int type
) {
621 volatile int mapped
= 0;/* quiet warning: might be clobbered by 'longjmp' */
626 if ((off_t
)(sce
->st
.st_size
* 1.1) < sce
->st
.st_size
) return -1;
628 /* don't mmap files > 128M
630 * we could use a sliding window, but currently there is no need for it
633 if (sce
->st
.st_size
> 128 * 1024 * 1024) return -1;
636 if (-1 == (ifd
= open(fn
->ptr
, O_RDONLY
| O_BINARY
))) {
637 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "opening plain-file", fn
, "failed", strerror(errno
));
643 if (MAP_FAILED
!= (start
= mmap(NULL
, sce
->st
.st_size
, PROT_READ
, MAP_SHARED
, ifd
, 0))) {
645 signal(SIGBUS
, sigbus_handler
);
646 sigbus_jmp_valid
= 1;
647 if (0 != sigsetjmp(sigbus_jmp
, 1)) {
648 sigbus_jmp_valid
= 0;
650 log_error_write(srv
, __FILE__
, __LINE__
, "sbd", "SIGBUS in mmap:",
653 munmap(start
, sce
->st
.st_size
);
658 #endif /* FIXME: might attempt to read very large file completely into memory; see compress.max-filesize config option */
659 if (NULL
== (start
= malloc(sce
->st
.st_size
)) || sce
->st
.st_size
!= read(ifd
, start
, sce
->st
.st_size
)) {
660 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "reading", fn
, "failed", strerror(errno
));
669 case HTTP_ACCEPT_ENCODING_GZIP
:
670 case HTTP_ACCEPT_ENCODING_X_GZIP
:
671 ret
= deflate_file_to_buffer_gzip(srv
, con
, p
, start
, sce
->st
.st_size
, sce
->st
.st_mtime
);
673 case HTTP_ACCEPT_ENCODING_DEFLATE
:
674 ret
= deflate_file_to_buffer_deflate(srv
, con
, p
, start
, sce
->st
.st_size
);
678 case HTTP_ACCEPT_ENCODING_BZIP2
:
679 case HTTP_ACCEPT_ENCODING_X_BZIP2
:
680 ret
= deflate_file_to_buffer_bzip2(srv
, con
, p
, start
, sce
->st
.st_size
);
690 sigbus_jmp_valid
= 0;
691 munmap(start
, sce
->st
.st_size
);
698 if (ret
!= 0) return -1;
700 chunkqueue_reset(con
->write_queue
);
701 chunkqueue_append_buffer(con
->write_queue
, p
->b
);
703 buffer_reset(con
->physical
.path
);
705 con
->file_finished
= 1;
706 con
->file_started
= 1;
714 static int mod_compress_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
716 plugin_config
*s
= p
->config_storage
[0];
718 PATCH(compress_cache_dir
);
720 PATCH(compress_max_filesize
);
721 PATCH(allowed_encodings
);
723 /* skip the first, the global context */
724 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
725 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
726 s
= p
->config_storage
[i
];
728 /* condition didn't match */
729 if (!config_check_cond(srv
, con
, dc
)) continue;
732 for (j
= 0; j
< dc
->value
->used
; j
++) {
733 data_unset
*du
= dc
->value
->data
[j
];
735 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("compress.cache-dir"))) {
736 PATCH(compress_cache_dir
);
737 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("compress.filetype"))) {
739 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("compress.max-filesize"))) {
740 PATCH(compress_max_filesize
);
741 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("compress.allowed-encodings"))) {
742 PATCH(allowed_encodings
);
751 static int mod_compress_contains_encoding(const char *headervalue
, const char *encoding
, size_t len
) {
752 const char *m
= headervalue
;
754 while (*m
== ',' || *m
== ' ' || *m
== '\t') {
757 if (0 == strncasecmp(m
, encoding
, len
)) {
758 /*(not a full HTTP field parse: not parsing for q-values and not handling q=0)*/
760 if (*m
== '\0' || *m
== ',' || *m
== ';' || *m
== ' ' || *m
== '\t')
762 } else if (*m
!= '\0') {
765 } while ((m
= strchr(m
, ',')));
769 PHYSICALPATH_FUNC(mod_compress_physical
) {
770 plugin_data
*p
= p_d
;
773 stat_cache_entry
*sce
= NULL
;
774 buffer
*mtime
= NULL
;
775 buffer
*content_type
;
777 if (con
->mode
!= DIRECT
|| con
->http_status
) return HANDLER_GO_ON
;
779 /* only GET and POST can get compressed */
780 if (con
->request
.http_method
!= HTTP_METHOD_GET
&&
781 con
->request
.http_method
!= HTTP_METHOD_POST
) {
782 return HANDLER_GO_ON
;
785 if (buffer_string_is_empty(con
->physical
.path
)) {
786 return HANDLER_GO_ON
;
789 mod_compress_patch_connection(srv
, con
, p
);
791 max_fsize
= p
->conf
.compress_max_filesize
;
793 if (con
->conf
.log_request_handling
) {
794 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- handling file as static file");
797 if (HANDLER_ERROR
== stat_cache_get_entry(srv
, con
, con
->physical
.path
, &sce
)) {
798 con
->http_status
= 403;
800 log_error_write(srv
, __FILE__
, __LINE__
, "sbsb",
801 "not a regular file:", con
->uri
.path
,
802 "->", con
->physical
.path
);
804 return HANDLER_FINISHED
;
807 /* we only handle regular files */
809 if ((sce
->is_symlink
== 1) && !con
->conf
.follow_symlink
) {
810 return HANDLER_GO_ON
;
813 if (!S_ISREG(sce
->st
.st_mode
)) {
814 return HANDLER_GO_ON
;
817 /* don't compress files that are too large as we need to much time to handle them */
818 if (max_fsize
&& (sce
->st
.st_size
>> 10) > max_fsize
) return HANDLER_GO_ON
;
820 /* don't try to compress files less than 128 bytes
822 * - extra overhead for compression
823 * - mmap() fails for st_size = 0 :)
825 if (sce
->st
.st_size
< 128) return HANDLER_GO_ON
;
827 /* check if mimetype is in compress-config */
829 if (sce
->content_type
->ptr
) {
831 if ( (c
= strchr(sce
->content_type
->ptr
, ';')) != NULL
) {
832 content_type
= srv
->tmp_buf
;
833 buffer_copy_string_len(content_type
, sce
->content_type
->ptr
, c
- sce
->content_type
->ptr
);
837 for (m
= 0; m
< p
->conf
.compress
->used
; m
++) {
838 data_string
*compress_ds
= (data_string
*)p
->conf
.compress
->data
[m
];
841 log_error_write(srv
, __FILE__
, __LINE__
, "sbb", "evil", con
->physical
.path
, con
->uri
.path
);
843 return HANDLER_GO_ON
;
846 if (buffer_is_equal(compress_ds
->value
, sce
->content_type
)
847 || (content_type
&& buffer_is_equal(compress_ds
->value
, content_type
))) {
851 /* the response might change according to Accept-Encoding */
852 response_header_insert(srv
, con
, CONST_STR_LEN("Vary"), CONST_STR_LEN("Accept-Encoding"));
854 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->request
.headers
, "Accept-Encoding"))) {
855 int accept_encoding
= 0;
856 char *value
= ds
->value
->ptr
;
857 int matched_encodings
= 0;
858 int use_etag
= sce
->etag
!= NULL
&& sce
->etag
->ptr
!= NULL
;
860 /* get client side support encodings */
862 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("gzip"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_GZIP
;
863 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("x-gzip"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_X_GZIP
;
864 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("deflate"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_DEFLATE
;
865 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("compress"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_COMPRESS
;
868 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("bzip2"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_BZIP2
;
869 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("x-bzip2"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_X_BZIP2
;
871 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("identity"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_IDENTITY
;
873 /* find matching entries */
874 matched_encodings
= accept_encoding
& p
->conf
.allowed_encodings
;
876 if (matched_encodings
) {
877 static const char dflt_gzip
[] = "gzip";
878 static const char dflt_x_gzip
[] = "x-gzip";
879 static const char dflt_deflate
[] = "deflate";
880 static const char dflt_bzip2
[] = "bzip2";
881 static const char dflt_x_bzip2
[] = "x-bzip2";
883 const char *compression_name
= NULL
;
884 int compression_type
= 0;
886 mtime
= strftime_cache_get(srv
, sce
->st
.st_mtime
);
888 /* try matching original etag of uncompressed version */
890 etag_mutate(con
->physical
.etag
, sce
->etag
);
891 if (HANDLER_FINISHED
== http_response_handle_cachable(srv
, con
, mtime
)) {
892 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce
->content_type
));
893 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime
));
894 response_header_overwrite(srv
, con
, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con
->physical
.etag
));
895 return HANDLER_FINISHED
;
899 /* select best matching encoding */
900 if (matched_encodings
& HTTP_ACCEPT_ENCODING_BZIP2
) {
901 compression_type
= HTTP_ACCEPT_ENCODING_BZIP2
;
902 compression_name
= dflt_bzip2
;
903 } else if (matched_encodings
& HTTP_ACCEPT_ENCODING_X_BZIP2
) {
904 compression_type
= HTTP_ACCEPT_ENCODING_X_BZIP2
;
905 compression_name
= dflt_x_bzip2
;
906 } else if (matched_encodings
& HTTP_ACCEPT_ENCODING_GZIP
) {
907 compression_type
= HTTP_ACCEPT_ENCODING_GZIP
;
908 compression_name
= dflt_gzip
;
909 } else if (matched_encodings
& HTTP_ACCEPT_ENCODING_X_GZIP
) {
910 compression_type
= HTTP_ACCEPT_ENCODING_X_GZIP
;
911 compression_name
= dflt_x_gzip
;
913 force_assert(matched_encodings
& HTTP_ACCEPT_ENCODING_DEFLATE
);
914 compression_type
= HTTP_ACCEPT_ENCODING_DEFLATE
;
915 compression_name
= dflt_deflate
;
919 /* try matching etag of compressed version */
920 buffer_copy_buffer(srv
->tmp_buf
, sce
->etag
);
921 buffer_append_string_len(srv
->tmp_buf
, CONST_STR_LEN("-"));
922 buffer_append_string(srv
->tmp_buf
, compression_name
);
923 etag_mutate(con
->physical
.etag
, srv
->tmp_buf
);
926 if (HANDLER_FINISHED
== http_response_handle_cachable(srv
, con
, mtime
)) {
927 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Encoding"), compression_name
, strlen(compression_name
));
928 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce
->content_type
));
929 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime
));
931 response_header_overwrite(srv
, con
, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con
->physical
.etag
));
933 return HANDLER_FINISHED
;
937 if (use_etag
&& !buffer_string_is_empty(p
->conf
.compress_cache_dir
)) {
938 if (0 != deflate_file_to_file(srv
, con
, p
, con
->physical
.path
, sce
, compression_type
))
939 return HANDLER_GO_ON
;
941 if (0 != deflate_file_to_buffer(srv
, con
, p
, con
->physical
.path
, sce
, compression_type
))
942 return HANDLER_GO_ON
;
944 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Encoding"), compression_name
, strlen(compression_name
));
945 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime
));
947 response_header_overwrite(srv
, con
, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con
->physical
.etag
));
949 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce
->content_type
));
950 /* let mod_staticfile handle the cached compressed files, physical path was modified */
951 return (use_etag
&& !buffer_string_is_empty(p
->conf
.compress_cache_dir
)) ? HANDLER_GO_ON
: HANDLER_FINISHED
;
957 return HANDLER_GO_ON
;
960 int mod_compress_plugin_init(plugin
*p
);
961 int mod_compress_plugin_init(plugin
*p
) {
962 p
->version
= LIGHTTPD_VERSION_ID
;
963 p
->name
= buffer_init_string("compress");
965 p
->init
= mod_compress_init
;
966 p
->set_defaults
= mod_compress_setdefaults
;
967 p
->handle_subrequest_start
= mod_compress_physical
;
968 p
->cleanup
= mod_compress_free
;