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.
lighttpd2/src/main/plugin_core.c

2031 lines
58 KiB

#include <lighttpd/base.h>
#include <lighttpd/pattern.h>
#include <lighttpd/plugin_core.h>
#include <lighttpd/version.h>
#include <lighttpd/http_range_parser.h>
#include <sys/stat.h>
#include <fcntl.h>
static liAction* core_list(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
liAction *a;
guint i;
UNUSED(wrk); UNUSED(p); UNUSED(userdata);
if (!val) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (val->type == LI_VALUE_ACTION) {
a = val->data.val_action.action;
li_action_acquire(a);
return a;
}
if (val->type != LI_VALUE_LIST) {
ERROR(srv, "expected list, got %s", li_value_type_string(val->type));
return NULL;
}
a = li_action_new_list();
for (i = 0; i < val->data.list->len; i++) {
liValue *oa = g_array_index(val->data.list, liValue*, i);
if (oa->type != LI_VALUE_ACTION) {
ERROR(srv, "expected action at entry %u of list, got %s", i, li_value_type_string(oa->type));
li_action_release(srv, a);
return NULL;
}
assert(srv == oa->data.val_action.srv);
li_action_acquire(oa->data.val_action.action);
g_array_append_val(a->data.list, oa->data.val_action.action);
}
return a;
}
static liAction* core_when(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
liValue *val_cond, *val_act, *val_act_else;
liAction *a, *act = NULL, *act_else = NULL;
UNUSED(wrk); UNUSED(p); UNUSED(userdata);
if (!val) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (val->type != LI_VALUE_LIST) {
ERROR(srv, "expected list, got %s", li_value_type_string(val->type));
return NULL;
}
if (val->data.list->len == 2) {
val_act_else = NULL;
act_else = NULL;
} else if (val->data.list->len == 3) {
val_act_else = g_array_index(val->data.list, liValue*, 2);
} else {
ERROR(srv, "expected list with length 2 or 3, has length %u", val->data.list->len);
return NULL;
}
val_cond = g_array_index(val->data.list, liValue*, 0);
val_act = g_array_index(val->data.list, liValue*, 1);
if (NULL == val_cond || val_cond->type != LI_VALUE_CONDITION) {
ERROR(srv, "expected condition as first parameter, got %s", NULL == val_cond ? "NULL" : li_value_type_string(val_cond->type));
return NULL;
}
if (NULL == val_act || val_act->type == LI_VALUE_NONE) {
act = NULL;
} else if (val_act->type == LI_VALUE_ACTION) {
act = val_act->data.val_action.action;
} else {
ERROR(srv, "expected action as second parameter, got %s", li_value_type_string(val_act->type));
return NULL;
}
if (NULL != val_act_else) {
if (val_act_else->type == LI_VALUE_NONE) {
act_else = NULL;
} else if (val_act_else->type == LI_VALUE_ACTION) {
act_else = val_act_else->data.val_action.action;
} else {
ERROR(srv, "expected action as third parameter, got %s", li_value_type_string(val_act_else->type));
return NULL;
}
}
li_condition_acquire(val_cond->data.val_cond.cond);
if (NULL != act) li_action_acquire(act);
if (NULL != act_else) li_action_acquire(act_else);
a = li_action_new_condition(val_cond->data.val_cond.cond, act, act_else);
return a;
}
static liAction* core_set(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
liValue *val_val, *val_name;
liAction *a;
UNUSED(p); UNUSED(userdata);
if (!val) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (val->type != LI_VALUE_LIST) {
ERROR(srv, "expected list, got %s", li_value_type_string(val->type));
return NULL;
}
if (val->data.list->len != 2) {
ERROR(srv, "expected list with length 2, has length %u", val->data.list->len);
return NULL;
}
val_name = g_array_index(val->data.list, liValue*, 0);
val_val = g_array_index(val->data.list, liValue*, 1);
if (val_name->type != LI_VALUE_STRING) {
ERROR(srv, "expected string as first parameter, got %s", li_value_type_string(val_name->type));
return NULL;
}
a = li_option_action(srv, wrk, val_name->data.string->str, val_val);
return a;
}
static gboolean core_setup_set(liServer *srv, liPlugin* p, liValue *val, gpointer userdata) {
liValue *val_val, *val_name;
UNUSED(p); UNUSED(userdata);
if (!val) {
ERROR(srv, "%s", "need parameter");
return FALSE;
}
if (val->type != LI_VALUE_LIST) {
ERROR(srv, "expected list, got %s", li_value_type_string(val->type));
return FALSE;
}
if (val->data.list->len != 2) {
ERROR(srv, "expected list with length 2, has length %u", val->data.list->len);
return FALSE;
}
val_name = g_array_index(val->data.list, liValue*, 0);
val_val = g_array_index(val->data.list, liValue*, 1);
if (val_name->type != LI_VALUE_STRING) {
ERROR(srv, "expected string as first parameter, got %s", li_value_type_string(val_name->type));
return FALSE;
}
return li_plugin_set_default_option(srv, val_name->data.string->str, val_val);
}
typedef struct docroot_split docroot_split;
struct docroot_split {
GString *hostname;
gchar** splits;
guint split_len;
};
static void core_docroot_nth_cb(GString *pattern_result, guint to, guint from, gpointer data) {
/* $n means n-th part of hostname from end divided by dots */
/* range is interpreted reversed !!! */
gboolean first = TRUE;
guint i;
docroot_split *ctx = data;
if (0 == ctx->hostname->len) return;
/* ranges including 0 will only get the complete hostname */
if (0 == from || 0 == to) {
g_string_append_len(pattern_result, GSTR_LEN(ctx->hostname));
return;
}
if (NULL == ctx->splits) {
ctx->splits = g_strsplit_set(ctx->hostname->str, ".", 31);
ctx->split_len = g_strv_length(ctx->splits);
}
if (0 == ctx->split_len) return;
from = MIN(from, ctx->split_len);
to = MIN(to, ctx->split_len);
if (from <= to) {
for (i = from; i <= to; i++) {
if (first) {
first = FALSE;
} else {
g_string_append_len(pattern_result, CONST_STR_LEN("."));
}
g_string_append(pattern_result, ctx->splits[ctx->split_len - i]);
}
} else {
for (i = from; i >= to; i--) { /* to > 0, so no underflow in i possible */
if (first) {
first = FALSE;
} else {
g_string_append_len(pattern_result, CONST_STR_LEN("."));
}
g_string_append(pattern_result, ctx->splits[ctx->split_len - i]);
}
}
}
static liHandlerResult core_handle_docroot(liVRequest *vr, gpointer param, gpointer *context) {
guint i;
GMatchInfo *match_info = NULL;
GArray *arr = param;
docroot_split dsplit = { vr->request.uri.host, NULL, 0 };
g_string_truncate(vr->physical.doc_root, 0);
if (vr->action_stack.regex_stack->len) {
GArray *rs = vr->action_stack.regex_stack;
match_info = g_array_index(rs, liActionRegexStackElement, rs->len - 1).match_info;
}
/* resume from last stat check */
if (*context) {
i = GPOINTER_TO_INT(*context);
} else {
i = 0;
}
*context = NULL;
/* loop over all the patterns until we find an existing path */
for (; i < arr->len; i++) {
struct stat st;
gint err;
g_string_truncate(vr->physical.doc_root, 0);
li_pattern_eval(vr, vr->physical.doc_root, g_array_index(arr, liPattern*, i), core_docroot_nth_cb, &dsplit, li_pattern_regex_cb, match_info);
if (i == arr->len - 1) break; /* don't stat, we'll use the last entry anyway */
/* check if path exists */
switch (li_stat_cache_get(vr, vr->physical.doc_root, &st, &err, NULL)) {
case LI_HANDLER_GO_ON: break;
case LI_HANDLER_WAIT_FOR_EVENT:
*context = GINT_TO_POINTER(i);
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "docroot: waiting for async: \"%s\"", vr->physical.doc_root->str);
}
g_strfreev(dsplit.splits);
return LI_HANDLER_WAIT_FOR_EVENT;
default:
/* not found, try next pattern */
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "docroot: not found: \"%s\", trying next", vr->physical.doc_root->str);
}
continue;
}
break;
}
g_strfreev(dsplit.splits);
/* build physical path: docroot + uri.path */
g_string_truncate(vr->physical.path, 0);
g_string_append_len(vr->physical.path, GSTR_LEN(vr->physical.doc_root));
if (vr->request.uri.path->len == 0 || vr->request.uri.path->str[0] != '/')
li_path_append_slash(vr->physical.path);
g_string_append_len(vr->physical.path, GSTR_LEN(vr->request.uri.path));
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "docroot: \"%s\"", vr->physical.doc_root->str);
VR_DEBUG(vr, "physical path: \"%s\"", vr->physical.path->str);
}
return LI_HANDLER_GO_ON;
}
static void core_docroot_free(liServer *srv, gpointer param) {
guint i;
GArray *arr = param;
UNUSED(srv);
for (i = 0; i < arr->len; i++) {
li_pattern_free(g_array_index(arr, liPattern*, i));
}
g_array_free(arr, TRUE);
}
static liAction* core_docroot(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
GArray *arr;
guint i;
liValue *v;
liPattern *pattern;
UNUSED(wrk); UNUSED(p); UNUSED(userdata);
if (!val || (val->type != LI_VALUE_STRING && val->type != LI_VALUE_LIST)) {
ERROR(srv, "%s", "docroot action expects a string or list of strings as parameter");
return NULL;
}
arr = g_array_new(FALSE, TRUE, sizeof(liPattern*));
if (val->type == LI_VALUE_STRING) {
pattern = li_pattern_new(srv, val->data.string->str);
if (NULL == pattern) return FALSE;
g_array_append_val(arr, pattern);
} else {
for (i = 0; i < val->data.list->len; i++) {
v = g_array_index(val->data.list, liValue*, i);
if (v->type != LI_VALUE_STRING) {
core_docroot_free(srv, arr);
return NULL;
}
pattern = li_pattern_new(srv, v->data.string->str);
if (NULL == pattern) {
core_docroot_free(srv, arr);
return FALSE;
}
g_array_append_val(arr, pattern);
}
}
return li_action_new_function(core_handle_docroot, NULL, core_docroot_free, arr);
}
13 years ago
typedef struct {
GString *prefix, *path;
} core_alias_config;
static liHandlerResult core_handle_alias(liVRequest *vr, gpointer _param, gpointer *context) {
GArray *param = _param;
guint i;
UNUSED(context);
for (i = 0; i < param->len; i++) {
core_alias_config ac = g_array_index(param, core_alias_config, i);
gsize preflen = ac.prefix->len;
gboolean isdir = FALSE;
if (preflen > 0 && ac.prefix->str[preflen-1] == '/') {
preflen--;
isdir = TRUE;
}
if (li_string_prefix(vr->request.uri.path, ac.prefix->str, preflen)) {
/* check if url has the form "prefix" or "prefix/.*" */
if (isdir && vr->request.uri.path->str[preflen] != '\0' && vr->request.uri.path->str[preflen] != '/') continue;
/* prefix matched */
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "alias path: %s", ac.path->str);
}
g_string_truncate(vr->physical.doc_root, 0);
g_string_append_len(vr->physical.doc_root, GSTR_LEN(ac.path));
/* build physical path: docroot + uri.path */
g_string_truncate(vr->physical.path, 0);
g_string_append_len(vr->physical.path, GSTR_LEN(ac.path));
g_string_append_len(vr->physical.path, vr->request.uri.path->str + preflen, vr->request.uri.path->len - preflen);
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "alias physical path: %s", vr->physical.path->str);
}
return LI_HANDLER_GO_ON;
}
}
return LI_HANDLER_GO_ON;
}
static void core_alias_free(liServer *srv, gpointer _param) {
GArray *param = _param;
guint i;
UNUSED(srv);
for (i = 0; i < param->len; i++) {
core_alias_config ac = g_array_index(param, core_alias_config, i);
g_string_free(ac.prefix, TRUE);
g_string_free(ac.path, TRUE);
}
g_array_free(param, TRUE);
}
static liAction* core_alias(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
13 years ago
GArray *a = NULL;
GArray *vl, *vl1;
core_alias_config ac;
UNUSED(wrk); UNUSED(p); UNUSED(userdata);
13 years ago
if (!val || val->type != LI_VALUE_LIST) {
ERROR(srv, "%s", "unexpected or no parameter for alias action");
return NULL;
}
vl = val->data.list;
if (vl->len == 2 && g_array_index(vl, liValue*, 0)->type == LI_VALUE_STRING && g_array_index(vl, liValue*, 1)->type == LI_VALUE_STRING) {
13 years ago
a = g_array_sized_new(FALSE, TRUE, sizeof(core_alias_config), 1);
ac.prefix = li_value_extract_string(g_array_index(vl, liValue*, 0));
ac.path = li_value_extract_string(g_array_index(vl, liValue*, 1));
13 years ago
g_array_append_val(a, ac);
} else {
guint i;
a = g_array_sized_new(FALSE, TRUE, sizeof(core_alias_config), vl->len);
for (i = 0; i < vl->len; i++) {
if (g_array_index(vl, liValue*, i)->type != LI_VALUE_LIST) {
ERROR(srv, "%s", "unexpected entry in parameter for alias action");
goto error_free;
}
vl1 = g_array_index(vl, liValue*, i)->data.list;
if (g_array_index(vl1, liValue*, 0)->type == LI_VALUE_STRING && g_array_index(vl1, liValue*, 1)->type == LI_VALUE_STRING) {
ac.prefix = li_value_extract_string(g_array_index(vl1, liValue*, 0));
ac.path = li_value_extract_string(g_array_index(vl1, liValue*, 1));
13 years ago
g_array_append_val(a, ac);
} else {
ERROR(srv, "%s", "unexpected entry in parameter for alias action");
goto error_free;
}
}
}
return li_action_new_function(core_handle_alias, NULL, core_alias_free, a);
error_free:
core_alias_free(srv, a);
return NULL;
}
static liHandlerResult core_handle_index(liVRequest *vr, gpointer param, gpointer *context) {
liHandlerResult res;
guint i;
struct stat st;
gint err;
GString *file, *tmp_docroot, *tmp_path;
GArray *files = param;
UNUSED(context);
if (!vr->physical.doc_root->len) {
VR_ERROR(vr, "%s", "no docroot specified yet but index action called");
return LI_HANDLER_ERROR;
}
res = li_stat_cache_get(vr, vr->physical.path, &st, &err, NULL);
if (res == LI_HANDLER_WAIT_FOR_EVENT)
return LI_HANDLER_WAIT_FOR_EVENT;
if (res == LI_HANDLER_ERROR) {
/* we ignore errors here so a later action can deal with it (e.g. 'static') */
return LI_HANDLER_GO_ON;
}
if (!S_ISDIR(st.st_mode))
return LI_HANDLER_GO_ON;
/* need trailing slash */
if (vr->request.uri.path->len == 0 || vr->request.uri.path->str[vr->request.uri.path->len-1] != '/') {
li_vrequest_redirect_directory(vr);
return LI_HANDLER_GO_ON;
}
/* we use two temporary strings here, one to append to docroot and one to append to physical path */
tmp_docroot = vr->wrk->tmp_str;
g_string_truncate(tmp_docroot, 0);
g_string_append_len(vr->wrk->tmp_str, GSTR_LEN(vr->physical.doc_root));
tmp_path = g_string_new_len(GSTR_LEN(vr->physical.path));
/* loop through the list to find a possible index file */
for (i = 0; i < files->len; i++) {
file = g_array_index(files, liValue*, i)->data.string;
if (file->str[0] == '/') {
/* entries beginning with a slash shall be looked up directly at the docroot */
g_string_truncate(tmp_docroot, vr->physical.doc_root->len);
g_string_append_len(tmp_docroot, GSTR_LEN(file));
res = li_stat_cache_get(vr, tmp_docroot, &st, &err, NULL);
} else {
g_string_truncate(tmp_path, vr->physical.path->len);
g_string_append_len(tmp_path, GSTR_LEN(file));
res = li_stat_cache_get(vr, tmp_path, &st, &err, NULL);
}
if (res == LI_HANDLER_WAIT_FOR_EVENT) {
g_string_free(tmp_path, TRUE);
return LI_HANDLER_WAIT_FOR_EVENT;
}
if (res == LI_HANDLER_GO_ON) {
/* file exists. change physical path */
if (file->str[0] == '/') {
g_string_truncate(vr->physical.path, vr->physical.doc_root->len);
g_string_truncate(vr->request.uri.path, 0);
}
g_string_append_len(vr->physical.path, GSTR_LEN(file));
g_string_append_len(vr->request.uri.path, GSTR_LEN(file));
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "using index file: '%s'", file->str);
}
g_string_free(tmp_path, TRUE);
return LI_HANDLER_GO_ON;
}
}
g_string_free(tmp_path, TRUE);
return LI_HANDLER_GO_ON;
}
static void core_index_free(liServer *srv, gpointer param) {
guint i;
GArray *files = param;
UNUSED(srv);
for (i = 0; i < files->len; i++)
li_value_free(g_array_index(files, liValue*, i));
g_array_free(files, TRUE);
}
static liAction* core_index(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
GArray *files;
guint i;
UNUSED(wrk); UNUSED(p); UNUSED(userdata);
if (!val || val->type != LI_VALUE_LIST) {
ERROR(srv, "%s", "index action expects a list of strings as parameter");
return NULL;
}
files = val->data.list;
for (i = 0; i < files->len; i++) {
if (g_array_index(files, liValue*, i)->type != LI_VALUE_STRING) {
ERROR(srv, "%s", "index action expects a list of strings as parameter");
return NULL;
}
}
return li_action_new_function(core_handle_index, NULL, core_index_free, li_value_extract_list(val));
}
static liHandlerResult core_handle_static(liVRequest *vr, gpointer param, gpointer *context) {
13 years ago
int fd = -1;
14 years ago
struct stat st;
int err;
liHandlerResult res;
GArray *exclude_arr = CORE_OPTIONPTR(LI_CORE_OPTION_STATIC_FILE_EXCLUDE_EXTENSIONS).list;
static const gchar boundary[] = "fkj49sn38dcn3";
gboolean no_fail = GPOINTER_TO_INT(param);
UNUSED(param);
UNUSED(context);
if (li_vrequest_is_handled(vr))
return LI_HANDLER_GO_ON;
switch (vr->request.http_method) {
case LI_HTTP_METHOD_GET:
case LI_HTTP_METHOD_HEAD:
break;
default:
if (no_fail) return LI_HANDLER_GO_ON;
if (!li_vrequest_handle_direct(vr)) {
return LI_HANDLER_ERROR;
}
vr->response.http_status = 405;
li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Allow"), CONST_STR_LEN("GET, HEAD"));
return LI_HANDLER_GO_ON;
}
if (vr->physical.path->len == 0) return LI_HANDLER_GO_ON;
if (vr->physical.path->str[vr->physical.path->len-1] == '/') return LI_HANDLER_GO_ON;
if (exclude_arr) {
guint i;
GString *tmp_str = vr->wrk->tmp_str;
gchar *basep = g_path_get_basename(vr->physical.path->str);
g_string_assign(tmp_str, basep);
g_free(basep);
for (i = 0; i < exclude_arr->len; i++) {
liValue *v = g_array_index(exclude_arr, liValue*, i);
if (li_string_suffix(tmp_str, GSTR_LEN(v->data.string))) {
if (no_fail) return LI_HANDLER_GO_ON;
if (!li_vrequest_handle_direct(vr)) {
return LI_HANDLER_ERROR;
}
vr->response.http_status = 403;
return LI_HANDLER_GO_ON;
}
}
}
res = li_stat_cache_get(vr, vr->physical.path, &st, &err, &fd);
if (res == LI_HANDLER_WAIT_FOR_EVENT)
14 years ago
return res;
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "try serving static file: '%s'", vr->physical.path->str);
}
if (res == LI_HANDLER_ERROR) {
14 years ago
/* open or fstat failed */
14 years ago
if (fd != -1)
close(fd);
if (no_fail) return LI_HANDLER_GO_ON;
if (!li_vrequest_handle_direct(vr)) {
return LI_HANDLER_ERROR;
14 years ago
}
14 years ago
switch (err) {
case ENOENT:
case ENOTDIR:
14 years ago
vr->response.http_status = 404;
return LI_HANDLER_GO_ON;
case EACCES:
vr->response.http_status = 403;
return LI_HANDLER_GO_ON;
default:
14 years ago
VR_ERROR(vr, "stat() or open() for '%s' failed: %s", vr->physical.path->str, g_strerror(err));
return LI_HANDLER_ERROR;
}
14 years ago
} else if (S_ISDIR(st.st_mode)) {
14 years ago
if (fd != -1)
close(fd);
return LI_HANDLER_GO_ON;
14 years ago
} else if (!S_ISREG(st.st_mode)) {
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "not a regular file: '%s'", vr->physical.path->str);
}
14 years ago
if (fd != -1)
close(fd);
if (no_fail) return LI_HANDLER_GO_ON;
if (!li_vrequest_handle_direct(vr)) {
return LI_HANDLER_ERROR;
}
vr->response.http_status = 403;
return LI_HANDLER_GO_ON;
} else {
const GString *mime_str;
gboolean cachable;
gboolean ranged_response = FALSE;
liHttpHeader *hh_range;
liChunkFile *cf;
static const GString default_mime_str = { CONST_STR_LEN("application/octet-stream"), 0 };
#ifdef FD_CLOEXEC
fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif
if (!li_vrequest_handle_direct(vr)) {
close(fd);
return LI_HANDLER_ERROR;
}
li_etag_set_header(vr, &st, &cachable);
if (cachable) {
vr->response.http_status = 304;
close(fd);
return LI_HANDLER_GO_ON;
}
cf = li_chunkfile_new(NULL, fd, FALSE);
mime_str = li_mimetype_get(vr, vr->physical.path);
if (!mime_str) mime_str = &default_mime_str;
if (CORE_OPTION(LI_CORE_OPTION_STATIC_RANGE_REQUESTS).boolean) {
li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes"));
hh_range = li_http_header_lookup(vr->request.headers, CONST_STR_LEN("range"));
if (hh_range) {
/* TODO: Check If-Range: header */
const GString range_str = li_const_gstring(LI_HEADER_VALUE_LEN(hh_range));
liParseHttpRangeState rs;
gboolean is_multipart = FALSE, done = FALSE;
li_parse_http_range_init(&rs, &range_str, st.st_size);
do {
switch (li_parse_http_range_next(&rs)) {
case LI_PARSE_HTTP_RANGE_OK:
if (!is_multipart && !rs.last_range) {
is_multipart = TRUE;
}
g_string_printf(vr->wrk->tmp_str, "bytes %"G_GINT64_FORMAT"-%"G_GINT64_FORMAT"/%"G_GINT64_FORMAT, rs.range_start, rs.range_end, (goffset) st.st_size);
if (is_multipart) {
GString *subheader = g_string_sized_new(1023);
g_string_append_printf(subheader, "\r\n--%s\r\nContent-Type: %s\r\nContent-Range: %s\r\n\r\n", boundary, mime_str->str, vr->wrk->tmp_str->str);
li_chunkqueue_append_string(vr->direct_out, subheader);
li_chunkqueue_append_chunkfile(vr->direct_out, cf, rs.range_start, rs.range_length);
} else {
li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Range"), GSTR_LEN(vr->wrk->tmp_str));
li_chunkqueue_append_chunkfile(vr->direct_out, cf, rs.range_start, rs.range_length);
}
break;
case LI_PARSE_HTTP_RANGE_DONE:
ranged_response = TRUE;
done = TRUE;
vr->response.http_status = 206;
if (is_multipart) {
GString *subheader = g_string_sized_new(1023);
g_string_append_printf(subheader, "\r\n--%s--\r\n", boundary);
li_chunkqueue_append_string(vr->direct_out, subheader);
g_string_printf(vr->wrk->tmp_str, "multipart/byteranges; boundary=%s", boundary);
li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), GSTR_LEN(vr->wrk->tmp_str));
} else {
li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), GSTR_LEN(mime_str));
}
break;
case LI_PARSE_HTTP_RANGE_INVALID:
done = TRUE;
/* indirect handing: out cq is already "closed" */
li_chunkqueue_reset(vr->direct_out); vr->direct_out->is_closed = TRUE;
break;
case LI_PARSE_HTTP_RANGE_NOT_SATISFIABLE:
ranged_response = TRUE;
done = TRUE;
li_chunkqueue_reset(vr->direct_out);
g_string_printf(vr->wrk->tmp_str, "bytes */%"G_GINT64_FORMAT, (goffset) st.st_size);
li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Range"), GSTR_LEN(vr->wrk->tmp_str));
vr->response.http_status = 416;
break;
}
} while (!done);
li_parse_http_range_clear(&rs);
}
}
if (!ranged_response) {
vr->response.http_status = 200;
li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), GSTR_LEN(mime_str));
li_chunkqueue_append_chunkfile(vr->direct_out, cf, 0, st.st_size);
}
li_chunkfile_release(cf);
}
return LI_HANDLER_GO_ON;
}
static liAction* core_static(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
UNUSED(wrk); UNUSED(p); UNUSED(userdata);
if (val) {
ERROR(srv, "%s", "static action doesn't have parameters");
return NULL;
}
return li_action_new_function(core_handle_static, NULL, NULL, GINT_TO_POINTER(0));
}