lighttpd 1.4.x
https://www.lighttpd.net/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
458 lines
18 KiB
458 lines
18 KiB
#include "first.h" |
|
|
|
#undef NDEBUG |
|
#include <sys/types.h> |
|
#include <assert.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
|
|
#include "mod_staticfile.c" |
|
#include "http_date.h" |
|
#include "http_etag.h" |
|
#include "http_header.h" |
|
|
|
__attribute_noinline__ |
|
static void test_mod_staticfile_reset (request_st * const r) |
|
{ |
|
r->http_status = 0; |
|
r->resp_htags = 0; |
|
array_reset_data_strings(&r->resp_headers); |
|
http_response_body_clear(r, 0); |
|
buffer_clear(&r->physical.etag); |
|
r->conf.etag_flags = ETAG_USE_INODE | ETAG_USE_MTIME | ETAG_USE_SIZE; |
|
} |
|
|
|
__attribute_noinline__ |
|
static void |
|
run_http_response_send_file (request_st * const r, int line, int status, const char *desc) |
|
{ |
|
http_response_send_file(r, &r->physical.path, NULL); |
|
if (r->http_status != status) { |
|
fprintf(stderr, |
|
"%s.%d: %s() failed: expected '%d', got '%d' for test %s\n", |
|
__FILE__, line, "http_response_send_file", status, |
|
r->http_status, desc); |
|
fflush(stderr); |
|
abort(); |
|
} |
|
} |
|
|
|
static void |
|
test_http_response_send_file (request_st * const r, time_t lmtime) |
|
{ |
|
test_mod_staticfile_reset(r); |
|
const buffer *vb; |
|
|
|
/*(mismatch test must be first, else stat_cache will have cached mimetype)*/ |
|
array * const mimetypes_empty = array_init(0); |
|
const array * const mimetypes_orig = r->conf.mimetypes; |
|
r->conf.mimetypes = mimetypes_empty; |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"basic static file (w/o mimetype match)"); |
|
vb = http_header_response_get(r, HTTP_HEADER_CONTENT_TYPE, |
|
CONST_STR_LEN("Content-Type")); |
|
assert(vb && buffer_eq_slen(vb, CONST_STR_LEN("application/octet-stream"))); |
|
test_mod_staticfile_reset(r); |
|
r->conf.mimetypes = mimetypes_orig; |
|
array_free(mimetypes_empty); |
|
|
|
run_http_response_send_file(r, __LINE__, 200, |
|
"basic static file (w/ mimetype match)"); |
|
vb = http_header_response_get(r, HTTP_HEADER_CONTENT_TYPE, |
|
CONST_STR_LEN("Content-Type")); |
|
assert(vb && buffer_eq_slen(vb, CONST_STR_LEN("text/plain"))); |
|
vb = http_header_response_get(r, HTTP_HEADER_ETAG, |
|
CONST_STR_LEN("ETag")); |
|
assert(vb && vb->ptr[0] == '"' && vb->ptr[buffer_clen(vb)-1] == '"'); |
|
vb = http_header_response_get(r, HTTP_HEADER_LAST_MODIFIED, |
|
CONST_STR_LEN("Last-Modified")); |
|
assert(vb); |
|
test_mod_staticfile_reset(r); |
|
|
|
const uint32_t plen = buffer_clen(&r->physical.path); |
|
buffer_append_string_len(&r->physical.path, CONST_STR_LEN("-nonexistent")); |
|
run_http_response_send_file(r, __LINE__, 404, |
|
"non-existent file"); |
|
test_mod_staticfile_reset(r); |
|
buffer_truncate(&r->physical.path, plen); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since"), |
|
CONST_STR_LEN("")); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-modified-since invalid (empty)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since"), |
|
CONST_STR_LEN("foobar")); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-modified-since invalid (not time string)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since"), |
|
CONST_STR_LEN("this string is too long to be a valid timestamp")); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-modified-since invalid (too long to be valid time string)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
char lmtime_str[HTTP_DATE_SZ]; |
|
uint32_t lmtime_len; |
|
|
|
lmtime_len = http_date_time_to_str(lmtime_str,sizeof(lmtime_str), |
|
lmtime ? lmtime-1 : lmtime); |
|
http_header_request_set(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since"), |
|
lmtime_str, lmtime_len); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-modified-since older than st_mtime"); |
|
test_mod_staticfile_reset(r); |
|
|
|
lmtime_len = http_date_time_to_str(lmtime_str,sizeof(lmtime_str),lmtime); |
|
http_header_request_set(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since"), |
|
lmtime_str, lmtime_len); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-modified-since matches st_mtime"); |
|
test_mod_staticfile_reset(r); |
|
|
|
lmtime_len = http_date_time_to_str(lmtime_str,sizeof(lmtime_str),lmtime+1); |
|
http_header_request_set(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since"), |
|
lmtime_str, lmtime_len); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-modified-since newer than st_mtime"); |
|
test_mod_staticfile_reset(r); |
|
|
|
buffer_append_string_len( |
|
http_header_request_get(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since")), |
|
CONST_STR_LEN("; foo")); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-modified-since newer but overload (invalid)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_unset(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since")); |
|
|
|
buffer *etag = buffer_init(); |
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
CONST_STR_LEN("foo")); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-none-match (etag mismatch)"); |
|
vb = http_header_response_get(r, HTTP_HEADER_ETAG, |
|
CONST_STR_LEN("ETag")); |
|
assert(vb); |
|
buffer_copy_buffer(etag, vb); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
CONST_BUF_LEN(etag)); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-none-match (etag match)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
r->conf.etag_flags = 0; |
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
CONST_BUF_LEN(etag)); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-none-match (etag would match, but etags disabled in config)"); |
|
test_mod_staticfile_reset(r); |
|
r->conf.etag_flags = ETAG_USE_INODE | ETAG_USE_MTIME | ETAG_USE_SIZE; |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
CONST_BUF_LEN(etag)); |
|
lmtime_len = http_date_time_to_str(lmtime_str,sizeof(lmtime_str), |
|
lmtime ? lmtime-1 : lmtime); |
|
http_header_request_set(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since"), |
|
lmtime_str, lmtime_len); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-none-match (etag match), " |
|
"if-modified-since (old) (should be ignored)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
CONST_BUF_LEN(etag)); |
|
lmtime_len = http_date_time_to_str(lmtime_str,sizeof(lmtime_str),lmtime); |
|
http_header_request_set(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since"), |
|
lmtime_str, lmtime_len); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-none-match (etag match), " |
|
"if-modified-since (now) (should be ignored)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
CONST_BUF_LEN(etag)); |
|
http_header_request_set(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since"), |
|
CONST_STR_LEN("Sun, 01 Jan 1970 00:00:01 GMT foo")); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-none-match (etag match), " |
|
"if-modified-since (overlong; invalid) (should be ignored)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
CONST_STR_LEN("foo")); |
|
lmtime_len = http_date_time_to_str(lmtime_str,sizeof(lmtime_str), |
|
lmtime ? lmtime-1 : lmtime); |
|
http_header_request_set(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since"), |
|
lmtime_str, lmtime_len); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-none-match (etag mismatch), " |
|
"if-modified-since (old) (should be ignored)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
CONST_STR_LEN("foo")); |
|
lmtime_len = http_date_time_to_str(lmtime_str,sizeof(lmtime_str),lmtime); |
|
http_header_request_set(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since"), |
|
lmtime_str, lmtime_len); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-none-match (etag mismatch), " |
|
"if-modified-since (now) (should be ignored)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_unset(r, HTTP_HEADER_IF_MODIFIED_SINCE, |
|
CONST_STR_LEN("If-Modified-Since")); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
etag->ptr, buffer_clen(etag)-1); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-none-match (etag invalid; mismatched quotes)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
etag->ptr+1, buffer_clen(etag)-2); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-none-match (etag invalid; no quotes)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
CONST_STR_LEN("*")); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-none-match (etag * (unquoted) matches any ETag)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_set(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match"), |
|
CONST_STR_LEN("\"*\"")); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-none-match (etag \"*\" (quoted) is a regular ETag)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
buffer * const rqst_etag = |
|
http_header_request_set_ptr(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match")); |
|
|
|
buffer_copy_string_len(rqst_etag, CONST_STR_LEN("W/")); |
|
buffer_append_buffer(rqst_etag, etag); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-none-match (weak etag) matches like ETag for GET and HEAD)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
/*(200 expected here instead of 206 since Range is handled later)*/ |
|
http_header_request_set(r, HTTP_HEADER_RANGE, |
|
CONST_STR_LEN("Range"), |
|
CONST_STR_LEN("bytes=0-0")); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-none-match (weak etag) does not match for Range request)"); |
|
test_mod_staticfile_reset(r); |
|
http_header_request_unset(r, HTTP_HEADER_RANGE, CONST_STR_LEN("Range")); |
|
|
|
buffer_copy_string_len(rqst_etag, CONST_STR_LEN("W/\"12345\"")); |
|
run_http_response_send_file(r, __LINE__, 200, |
|
"if-none-match (weak etag no match)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
buffer_append_string_len(rqst_etag, CONST_STR_LEN(", ")); |
|
buffer_append_buffer(rqst_etag, etag); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-none-match (etag list, second etag matches)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
buffer_append_string_len(rqst_etag, CONST_STR_LEN(", W/")); |
|
buffer_append_buffer(rqst_etag, etag); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-none-match (etag list, second etag matches weakly)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
buffer_copy_string_len(rqst_etag, CONST_STR_LEN("\"12345\",, ,, , ")); |
|
buffer_append_buffer(rqst_etag, etag); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-none-match (etag list non-normalized, ending with etag match)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
buffer_copy_string_len(rqst_etag, CONST_STR_LEN("\"1234\", ")); |
|
buffer_append_buffer(rqst_etag, etag); |
|
buffer_append_string_len(rqst_etag, CONST_STR_LEN(", \"brokentrailing")); |
|
run_http_response_send_file(r, __LINE__, 304, |
|
"if-none-match (etag list with etag match then invalid trailing data)"); |
|
test_mod_staticfile_reset(r); |
|
|
|
http_header_request_unset(r, HTTP_HEADER_IF_NONE_MATCH, |
|
CONST_STR_LEN("If-None-Match")); |
|
|
|
buffer_free(etag); |
|
} |
|
|
|
__attribute_noinline__ |
|
static void |
|
run_mod_staticfile_process (request_st * const r, plugin_config * const pconf, int line, int status, const char *desc) |
|
{ |
|
handler_t rc = mod_staticfile_process(r, pconf); |
|
if (r->http_status != status |
|
|| rc != (status ? HANDLER_FINISHED : HANDLER_GO_ON)) { |
|
fprintf(stderr, |
|
"%s.%d: %s() failed: expected '%d', got '%d' for test %s\n", |
|
__FILE__, line, "mod_staticfile_process", status, |
|
r->http_status, desc); |
|
fflush(stderr); |
|
abort(); |
|
} |
|
} |
|
|
|
static void |
|
test_mod_staticfile_process (request_st * const r, plugin_config * const pconf) |
|
{ |
|
test_mod_staticfile_reset(r); |
|
|
|
pconf->disable_pathinfo = 0; |
|
buffer_copy_string_len(&r->pathinfo, CONST_STR_LEN("/pathinfo")); |
|
run_mod_staticfile_process(r, pconf, __LINE__, 200, |
|
"pathinfo allowed and present"); |
|
test_mod_staticfile_reset(r); |
|
pconf->disable_pathinfo = 1; |
|
run_mod_staticfile_process(r, pconf, __LINE__, 0, |
|
"pathinfo denied and present"); |
|
test_mod_staticfile_reset(r); |
|
buffer_clear(&r->pathinfo); |
|
run_mod_staticfile_process(r, pconf, __LINE__, 200, |
|
"pathinfo denied and not present"); |
|
test_mod_staticfile_reset(r); |
|
pconf->disable_pathinfo = 0; |
|
|
|
array * const a = array_init(1); |
|
array_insert_value(a, CONST_STR_LEN(".exe")); |
|
pconf->exclude_ext = a; |
|
run_mod_staticfile_process(r, pconf, __LINE__, 200, |
|
"extension disallowed (no match)"); |
|
test_mod_staticfile_reset(r); |
|
buffer_append_string_len(&r->physical.path, CONST_STR_LEN(".exe")); |
|
run_mod_staticfile_process(r, pconf, __LINE__, 0, |
|
"extension disallowed (match)"); |
|
test_mod_staticfile_reset(r); |
|
pconf->exclude_ext = NULL; |
|
array_free(a); |
|
} |
|
|
|
#include <unistd.h> /* unlink() */ |
|
|
|
int main (void) |
|
{ |
|
char fn[] = "/tmp/lighttpd_mod_staticfile.XXXXXX"; |
|
#ifdef __COVERITY__ |
|
/* POSIX-2008 requires mkstemp create file with 0600 perms */ |
|
umask(0600); |
|
#endif |
|
/* coverity[secure_temp : FALSE] */ |
|
int fd = mkstemp(fn); |
|
if (fd < 0) { |
|
perror("mkstemp()"); |
|
exit(1); |
|
} |
|
struct stat st; |
|
if (0 != fstat(fd, &st)) { |
|
perror("fstat()"); |
|
exit(1); |
|
} |
|
|
|
plugin_data * const p = mod_staticfile_init(); |
|
assert(NULL != p); |
|
p->conf.etags_used = 1; |
|
|
|
request_st r; |
|
|
|
memset(&r, 0, sizeof(request_st)); |
|
r.http_method = HTTP_METHOD_GET; |
|
r.http_version = HTTP_VERSION_1_1; |
|
r.tmp_buf = buffer_init(); |
|
r.conf.errh = log_error_st_init(); |
|
r.conf.errh->errorlog_fd = -1; /* (disable) */ |
|
r.conf.follow_symlink = 1; |
|
buffer_copy_string_len(&r.uri.path, CONST_STR_LEN("/")); |
|
array * const mimetypes = array_init(1); |
|
r.conf.mimetypes = mimetypes; |
|
array_set_key_value(mimetypes, fn+sizeof(fn)-8, 7, |
|
CONST_STR_LEN("text/plain")); |
|
|
|
strftime_cache_reset(); |
|
|
|
buffer_copy_string_len(&r.physical.path, fn, sizeof(fn)-1); |
|
test_http_response_send_file(&r, st.st_mtime); |
|
|
|
r.rqst_htags = 0; |
|
array_reset_data_strings(&r.rqst_headers); |
|
|
|
buffer_copy_string_len(&r.physical.path, fn, sizeof(fn)-1); |
|
test_mod_staticfile_process(&r, &p->conf); |
|
|
|
array_free(mimetypes); |
|
log_error_st_free(r.conf.errh); |
|
buffer_free(r.tmp_buf); |
|
chunkqueue_reset(&r.write_queue); |
|
|
|
free(r.uri.path.ptr); |
|
free(r.physical.etag.ptr); |
|
free(r.physical.path.ptr); |
|
free(r.physical.rel_path.ptr); |
|
free(r.physical.doc_root.ptr); |
|
|
|
free(p); |
|
stat_cache_free(); |
|
unlink(fn); |
|
return 0; |
|
} |
|
|
|
|
|
/* |
|
* stub functions |
|
*/ |
|
|
|
#include "fdevent_impl.h" |
|
int fdevent_select_init(struct fdevents *ev) { return NULL == ev; } |
|
int fdevent_poll_init(struct fdevents *ev) { return NULL == ev; } |
|
int fdevent_linux_sysepoll_init(struct fdevents *ev) { return NULL == ev; } |
|
int fdevent_solaris_devpoll_init(struct fdevents *ev) { return NULL == ev; } |
|
int fdevent_solaris_port_init(struct fdevents *ev) { return NULL == ev; } |
|
int fdevent_freebsd_kqueue_init(struct fdevents *ev) { return NULL == ev; } |
|
int fdevent_libev_init(struct fdevents *ev) { return NULL == ev; } |
|
|
|
int config_plugin_values_init(server *srv, void *p_d, const config_plugin_keys_t *cpk, const char *mname) { |
|
UNUSED(srv); |
|
UNUSED(p_d); |
|
UNUSED(cpk); |
|
UNUSED(mname); |
|
return 0; |
|
} |
|
|
|
int config_check_cond(request_st *r, int context_ndx) { |
|
UNUSED(r); |
|
UNUSED(context_ndx); |
|
return 0; |
|
}
|
|
|