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
);
601 /* Remove the incomplete cache file, so that later hits aren't served from it */
602 if (-1 == unlink(p
->ofn
->ptr
)) {
603 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "unlinking incomplete cachefile", p
->ofn
, "failed:", strerror(errno
));
609 buffer_copy_buffer(con
->physical
.path
, p
->ofn
);
614 static int deflate_file_to_buffer(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*fn
, stat_cache_entry
*sce
, int type
) {
618 volatile int mapped
= 0;/* quiet warning: might be clobbered by 'longjmp' */
623 if ((off_t
)(sce
->st
.st_size
* 1.1) < sce
->st
.st_size
) return -1;
625 /* don't mmap files > 128M
627 * we could use a sliding window, but currently there is no need for it
630 if (sce
->st
.st_size
> 128 * 1024 * 1024) return -1;
633 if (-1 == (ifd
= open(fn
->ptr
, O_RDONLY
| O_BINARY
))) {
634 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "opening plain-file", fn
, "failed", strerror(errno
));
640 if (MAP_FAILED
!= (start
= mmap(NULL
, sce
->st
.st_size
, PROT_READ
, MAP_SHARED
, ifd
, 0))) {
642 signal(SIGBUS
, sigbus_handler
);
643 sigbus_jmp_valid
= 1;
644 if (0 != sigsetjmp(sigbus_jmp
, 1)) {
645 sigbus_jmp_valid
= 0;
647 log_error_write(srv
, __FILE__
, __LINE__
, "sbd", "SIGBUS in mmap:",
650 munmap(start
, sce
->st
.st_size
);
655 #endif /* FIXME: might attempt to read very large file completely into memory; see compress.max-filesize config option */
656 if (NULL
== (start
= malloc(sce
->st
.st_size
)) || sce
->st
.st_size
!= read(ifd
, start
, sce
->st
.st_size
)) {
657 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "reading", fn
, "failed", strerror(errno
));
666 case HTTP_ACCEPT_ENCODING_GZIP
:
667 case HTTP_ACCEPT_ENCODING_X_GZIP
:
668 ret
= deflate_file_to_buffer_gzip(srv
, con
, p
, start
, sce
->st
.st_size
, sce
->st
.st_mtime
);
670 case HTTP_ACCEPT_ENCODING_DEFLATE
:
671 ret
= deflate_file_to_buffer_deflate(srv
, con
, p
, start
, sce
->st
.st_size
);
675 case HTTP_ACCEPT_ENCODING_BZIP2
:
676 case HTTP_ACCEPT_ENCODING_X_BZIP2
:
677 ret
= deflate_file_to_buffer_bzip2(srv
, con
, p
, start
, sce
->st
.st_size
);
687 sigbus_jmp_valid
= 0;
688 munmap(start
, sce
->st
.st_size
);
695 if (ret
!= 0) return -1;
697 chunkqueue_reset(con
->write_queue
);
698 chunkqueue_append_buffer(con
->write_queue
, p
->b
);
700 buffer_reset(con
->physical
.path
);
702 con
->file_finished
= 1;
703 con
->file_started
= 1;
711 static int mod_compress_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
713 plugin_config
*s
= p
->config_storage
[0];
715 PATCH(compress_cache_dir
);
717 PATCH(compress_max_filesize
);
718 PATCH(allowed_encodings
);
720 /* skip the first, the global context */
721 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
722 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
723 s
= p
->config_storage
[i
];
725 /* condition didn't match */
726 if (!config_check_cond(srv
, con
, dc
)) continue;
729 for (j
= 0; j
< dc
->value
->used
; j
++) {
730 data_unset
*du
= dc
->value
->data
[j
];
732 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("compress.cache-dir"))) {
733 PATCH(compress_cache_dir
);
734 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("compress.filetype"))) {
736 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("compress.max-filesize"))) {
737 PATCH(compress_max_filesize
);
738 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("compress.allowed-encodings"))) {
739 PATCH(allowed_encodings
);
748 static int mod_compress_contains_encoding(const char *headervalue
, const char *encoding
, size_t len
) {
749 const char *m
= headervalue
;
751 while (*m
== ',' || *m
== ' ' || *m
== '\t') {
754 if (0 == strncasecmp(m
, encoding
, len
)) {
755 /*(not a full HTTP field parse: not parsing for q-values and not handling q=0)*/
757 if (*m
== '\0' || *m
== ',' || *m
== ';' || *m
== ' ' || *m
== '\t')
759 } else if (*m
!= '\0') {
762 } while ((m
= strchr(m
, ',')));
766 PHYSICALPATH_FUNC(mod_compress_physical
) {
767 plugin_data
*p
= p_d
;
770 stat_cache_entry
*sce
= NULL
;
771 buffer
*mtime
= NULL
;
772 buffer
*content_type
;
774 if (con
->mode
!= DIRECT
|| con
->http_status
) return HANDLER_GO_ON
;
776 /* only GET and POST can get compressed */
777 if (con
->request
.http_method
!= HTTP_METHOD_GET
&&
778 con
->request
.http_method
!= HTTP_METHOD_POST
) {
779 return HANDLER_GO_ON
;
782 if (buffer_string_is_empty(con
->physical
.path
)) {
783 return HANDLER_GO_ON
;
786 mod_compress_patch_connection(srv
, con
, p
);
788 max_fsize
= p
->conf
.compress_max_filesize
;
790 if (con
->conf
.log_request_handling
) {
791 log_error_write(srv
, __FILE__
, __LINE__
, "s", "-- handling file as static file");
794 if (HANDLER_ERROR
== stat_cache_get_entry(srv
, con
, con
->physical
.path
, &sce
)) {
795 con
->http_status
= 403;
797 log_error_write(srv
, __FILE__
, __LINE__
, "sbsb",
798 "not a regular file:", con
->uri
.path
,
799 "->", con
->physical
.path
);
801 return HANDLER_FINISHED
;
804 /* we only handle regular files */
806 if ((sce
->is_symlink
== 1) && !con
->conf
.follow_symlink
) {
807 return HANDLER_GO_ON
;
810 if (!S_ISREG(sce
->st
.st_mode
)) {
811 return HANDLER_GO_ON
;
814 /* don't compress files that are too large as we need to much time to handle them */
815 if (max_fsize
&& (sce
->st
.st_size
>> 10) > max_fsize
) return HANDLER_GO_ON
;
817 /* don't try to compress files less than 128 bytes
819 * - extra overhead for compression
820 * - mmap() fails for st_size = 0 :)
822 if (sce
->st
.st_size
< 128) return HANDLER_GO_ON
;
824 /* check if mimetype is in compress-config */
826 if (sce
->content_type
->ptr
) {
828 if ( (c
= strchr(sce
->content_type
->ptr
, ';')) != NULL
) {
829 content_type
= srv
->tmp_buf
;
830 buffer_copy_string_len(content_type
, sce
->content_type
->ptr
, c
- sce
->content_type
->ptr
);
834 for (m
= 0; m
< p
->conf
.compress
->used
; m
++) {
835 data_string
*compress_ds
= (data_string
*)p
->conf
.compress
->data
[m
];
838 log_error_write(srv
, __FILE__
, __LINE__
, "sbb", "evil", con
->physical
.path
, con
->uri
.path
);
840 return HANDLER_GO_ON
;
843 if (buffer_is_equal(compress_ds
->value
, sce
->content_type
)
844 || (content_type
&& buffer_is_equal(compress_ds
->value
, content_type
))) {
848 /* the response might change according to Accept-Encoding */
849 response_header_insert(srv
, con
, CONST_STR_LEN("Vary"), CONST_STR_LEN("Accept-Encoding"));
851 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->request
.headers
, "Accept-Encoding"))) {
852 int accept_encoding
= 0;
853 char *value
= ds
->value
->ptr
;
854 int matched_encodings
= 0;
855 int use_etag
= sce
->etag
!= NULL
&& sce
->etag
->ptr
!= NULL
;
857 /* get client side support encodings */
859 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("gzip"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_GZIP
;
860 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("x-gzip"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_X_GZIP
;
861 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("deflate"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_DEFLATE
;
862 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("compress"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_COMPRESS
;
865 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("bzip2"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_BZIP2
;
866 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("x-bzip2"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_X_BZIP2
;
868 if (mod_compress_contains_encoding(value
, CONST_STR_LEN("identity"))) accept_encoding
|= HTTP_ACCEPT_ENCODING_IDENTITY
;
870 /* find matching entries */
871 matched_encodings
= accept_encoding
& p
->conf
.allowed_encodings
;
873 if (matched_encodings
) {
874 static const char dflt_gzip
[] = "gzip";
875 static const char dflt_x_gzip
[] = "x-gzip";
876 static const char dflt_deflate
[] = "deflate";
877 static const char dflt_bzip2
[] = "bzip2";
878 static const char dflt_x_bzip2
[] = "x-bzip2";
880 const char *compression_name
= NULL
;
881 int compression_type
= 0;
883 mtime
= strftime_cache_get(srv
, sce
->st
.st_mtime
);
885 /* try matching original etag of uncompressed version */
887 etag_mutate(con
->physical
.etag
, sce
->etag
);
888 if (HANDLER_FINISHED
== http_response_handle_cachable(srv
, con
, mtime
)) {
889 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce
->content_type
));
890 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime
));
891 response_header_overwrite(srv
, con
, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con
->physical
.etag
));
892 return HANDLER_FINISHED
;
896 /* select best matching encoding */
897 if (matched_encodings
& HTTP_ACCEPT_ENCODING_BZIP2
) {
898 compression_type
= HTTP_ACCEPT_ENCODING_BZIP2
;
899 compression_name
= dflt_bzip2
;
900 } else if (matched_encodings
& HTTP_ACCEPT_ENCODING_X_BZIP2
) {
901 compression_type
= HTTP_ACCEPT_ENCODING_X_BZIP2
;
902 compression_name
= dflt_x_bzip2
;
903 } else if (matched_encodings
& HTTP_ACCEPT_ENCODING_GZIP
) {
904 compression_type
= HTTP_ACCEPT_ENCODING_GZIP
;
905 compression_name
= dflt_gzip
;
906 } else if (matched_encodings
& HTTP_ACCEPT_ENCODING_X_GZIP
) {
907 compression_type
= HTTP_ACCEPT_ENCODING_X_GZIP
;
908 compression_name
= dflt_x_gzip
;
910 force_assert(matched_encodings
& HTTP_ACCEPT_ENCODING_DEFLATE
);
911 compression_type
= HTTP_ACCEPT_ENCODING_DEFLATE
;
912 compression_name
= dflt_deflate
;
916 /* try matching etag of compressed version */
917 buffer_copy_buffer(srv
->tmp_buf
, sce
->etag
);
918 buffer_append_string_len(srv
->tmp_buf
, CONST_STR_LEN("-"));
919 buffer_append_string(srv
->tmp_buf
, compression_name
);
920 etag_mutate(con
->physical
.etag
, srv
->tmp_buf
);
923 if (HANDLER_FINISHED
== http_response_handle_cachable(srv
, con
, mtime
)) {
924 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Encoding"), compression_name
, strlen(compression_name
));
925 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce
->content_type
));
926 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime
));
928 response_header_overwrite(srv
, con
, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con
->physical
.etag
));
930 return HANDLER_FINISHED
;
934 if (use_etag
&& !buffer_string_is_empty(p
->conf
.compress_cache_dir
)) {
935 if (0 != deflate_file_to_file(srv
, con
, p
, con
->physical
.path
, sce
, compression_type
))
936 return HANDLER_GO_ON
;
938 if (0 != deflate_file_to_buffer(srv
, con
, p
, con
->physical
.path
, sce
, compression_type
))
939 return HANDLER_GO_ON
;
941 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Encoding"), compression_name
, strlen(compression_name
));
942 response_header_overwrite(srv
, con
, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime
));
944 response_header_overwrite(srv
, con
, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con
->physical
.etag
));
946 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce
->content_type
));
947 /* let mod_staticfile handle the cached compressed files, physical path was modified */
948 return (use_etag
&& !buffer_string_is_empty(p
->conf
.compress_cache_dir
)) ? HANDLER_GO_ON
: HANDLER_FINISHED
;
954 return HANDLER_GO_ON
;
957 int mod_compress_plugin_init(plugin
*p
);
958 int mod_compress_plugin_init(plugin
*p
) {
959 p
->version
= LIGHTTPD_VERSION_ID
;
960 p
->name
= buffer_init_string("compress");
962 p
->init
= mod_compress_init
;
963 p
->set_defaults
= mod_compress_setdefaults
;
964 p
->handle_subrequest_start
= mod_compress_physical
;
965 p
->cleanup
= mod_compress_free
;