You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lighttpd1.4/src/t/test_mod_staticfile.c

434 lines
17 KiB
C

#include "first.h"
#undef NDEBUG
#include <sys/types.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "mod_staticfile.c"
#include "fdlog.h"
#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);
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);
#ifdef __COVERITY__ /* Coverity misses that this is set a few lines above */
force_assert(http_header_request_get(r, HTTP_HEADER_IF_MODIFIED_SINCE,
CONST_STR_LEN("If-Modified-Since")));
#endif
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() */
void test_mod_staticfile (void);
void test_mod_staticfile (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 = fdlog_init(NULL, -1, FDLOG_FD);
r.conf.errh->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);
fdlog_free(r.conf.errh);
buffer_free(r.tmp_buf);
chunkqueue_reset(&r.write_queue);
free(r.uri.path.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);
}