[core] Rework mimetype lookup code
This commit is contained in:
parent
9c63e60cb7
commit
582dfa99d7
|
@ -61,6 +61,7 @@
|
|||
#include <lighttpd/throttle.h>
|
||||
#include <lighttpd/pattern.h>
|
||||
#include <lighttpd/encoding.h>
|
||||
#include <lighttpd/mimetype.h>
|
||||
|
||||
#include <lighttpd/connection.h>
|
||||
|
||||
|
|
|
@ -16,7 +16,4 @@ LI_API gchar *li_http_version_string(liHttpVersion method, guint *len);
|
|||
/* converts a given 3 digit http status code to a gchar[3] string. e.g. 403 to {'4','0','3'} */
|
||||
LI_API void li_http_status_to_str(gint status_code, gchar status_str[]);
|
||||
|
||||
/* looks up the mimetype for a filename by comparing suffixes. first match is returned. do not free the result */
|
||||
LI_API GString *li_mimetype_get(liVRequest *vr, GString *filename);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef _LIGHTTPD_MIMETYPE_H_
|
||||
#define _LIGHTTPD_MIMETYPE_H_
|
||||
|
||||
struct liMimetypeNode {
|
||||
gchar cmin;
|
||||
gchar cmax;
|
||||
gpointer *children; /* array of either liMimetypeNode* or GString* */
|
||||
GString *mimetype;
|
||||
};
|
||||
typedef struct liMimetypeNode liMimetypeNode;
|
||||
|
||||
LI_API liMimetypeNode *li_mimetype_node_new(void);
|
||||
LI_API void li_mimetype_node_free(liMimetypeNode *node);
|
||||
LI_API void li_mimetype_insert(liMimetypeNode *node, GString *suffix, GString *mimetype, guint depth);
|
||||
|
||||
/* looks up the mimetype for a filename by comparing suffixes. longest match is returned. do not free the result */
|
||||
LI_API GString *li_mimetype_get(liVRequest *vr, GString *filename);
|
||||
|
||||
#endif
|
|
@ -206,6 +206,7 @@ SET(LIGHTTPD_SHARED_SRC
|
|||
http_response_parser.c
|
||||
lighttpd_glue.c
|
||||
log.c
|
||||
mimetype.c
|
||||
network.c
|
||||
network_write.c network_writev.c
|
||||
network_sendfile.c
|
||||
|
|
|
@ -24,6 +24,7 @@ lighttpd_shared_src= \
|
|||
http_response_parser.c \
|
||||
lighttpd_glue.c \
|
||||
log.c \
|
||||
mimetype.c \
|
||||
network.c \
|
||||
network_write.c network_writev.c \
|
||||
network_sendfile.c \
|
||||
|
|
|
@ -180,39 +180,3 @@ void li_http_status_to_str(gint status_code, gchar status_str[]) {
|
|||
status_code /= 10;
|
||||
status_str[0] = status_code + '0';
|
||||
}
|
||||
|
||||
|
||||
GString *li_mimetype_get(liVRequest *vr, GString *filename) {
|
||||
/* search in mime_types option for the first match */
|
||||
GArray *arr;
|
||||
|
||||
if (!vr || !filename || !filename->len)
|
||||
return NULL;
|
||||
|
||||
arr = CORE_OPTIONPTR(LI_CORE_OPTION_MIME_TYPES).list;
|
||||
|
||||
for (guint i = 0; i < arr->len; i++) {
|
||||
gint k, j;
|
||||
liValue *tuple = g_array_index(arr, liValue*, i);
|
||||
GString *ext = g_array_index(tuple->data.list, liValue*, 0)->data.string;
|
||||
if (ext->len > filename->len)
|
||||
continue;
|
||||
|
||||
/* "" extension matches everything, used for default mimetype */
|
||||
if (!ext->len)
|
||||
return g_array_index(tuple->data.list, liValue*, 1)->data.string;
|
||||
|
||||
k = filename->len - 1;
|
||||
j = ext->len - 1;
|
||||
for (; j >= 0; j--) {
|
||||
if (ext->str[j] != filename->str[k])
|
||||
break;
|
||||
k--;
|
||||
}
|
||||
|
||||
if (j == -1)
|
||||
return g_array_index(tuple->data.list, liValue*, 1)->data.string;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
|
||||
#include <lighttpd/base.h>
|
||||
#include <lighttpd/plugin_core.h>
|
||||
|
||||
#define MIME_COUNT_CHILDREN(x) (x->cmin == 0 ? 0 : ((guint)x->cmax - x->cmin + 1))
|
||||
#define MIME_MARK_NODE(x) ((gpointer)((guintptr)x | 1))
|
||||
#define MIME_UNMARK_NODE(x) ((gpointer)((guintptr)x & (~1)))
|
||||
#define MIME_IS_NODE(x) ((guintptr)x & 1)
|
||||
|
||||
LI_API liMimetypeNode *li_mimetype_node_new(void) {
|
||||
return g_slice_new0(liMimetypeNode);
|
||||
}
|
||||
|
||||
LI_API void li_mimetype_node_free(liMimetypeNode *node) {
|
||||
guint i;
|
||||
gpointer ptr;
|
||||
|
||||
if (node->mimetype)
|
||||
g_string_free(node->mimetype, TRUE);
|
||||
|
||||
for (i = 0; i < MIME_COUNT_CHILDREN(node); i++) {
|
||||
ptr = node->children[i];
|
||||
|
||||
if (!ptr)
|
||||
continue;
|
||||
|
||||
if (!MIME_IS_NODE(ptr)) {
|
||||
g_string_free(ptr, TRUE);
|
||||
} else {
|
||||
li_mimetype_node_free(MIME_UNMARK_NODE(ptr));
|
||||
}
|
||||
}
|
||||
|
||||
if (node->children)
|
||||
g_free(node->children);
|
||||
|
||||
g_slice_free(liMimetypeNode, node);
|
||||
}
|
||||
|
||||
LI_API void li_mimetype_insert(liMimetypeNode *node, GString *suffix, GString *mimetype, guint depth) {
|
||||
gchar c, cdiff;
|
||||
gpointer ptr;
|
||||
liMimetypeNode *node_new;
|
||||
|
||||
/* start of suffix reached */
|
||||
if (depth == suffix->len) {
|
||||
if (node->mimetype)
|
||||
g_string_free(node->mimetype, TRUE);
|
||||
|
||||
node->mimetype = mimetype;
|
||||
return;
|
||||
}
|
||||
|
||||
c = suffix->str[suffix->len - depth - 1];
|
||||
|
||||
if (!node->children) {
|
||||
node->cmin = node->cmax = c;
|
||||
node->children = g_malloc(sizeof(gpointer));
|
||||
node->children[0] = mimetype;
|
||||
return;
|
||||
} else if (c < node->cmin) {
|
||||
cdiff = node->cmin - c; /* how much space we need in front */
|
||||
node->children = g_realloc(node->children, sizeof(gpointer) * (MIME_COUNT_CHILDREN(node) + cdiff)); /* make room for more children */
|
||||
memmove(&node->children[(guint)cdiff], node->children, sizeof(gpointer) * MIME_COUNT_CHILDREN(node)); /* move existing children to the back */
|
||||
memset(node->children, 0, cdiff * sizeof(gpointer));
|
||||
node->cmin = c;
|
||||
} else if (c > node->cmax) {
|
||||
cdiff = c - node->cmax;
|
||||
node->children = g_realloc(node->children, sizeof(gpointer) * (MIME_COUNT_CHILDREN(node) + cdiff)); /* make room for more children */
|
||||
memset(&node->children[MIME_COUNT_CHILDREN(node)], 0, cdiff * sizeof(gpointer));
|
||||
node->cmax = c;
|
||||
}
|
||||
|
||||
ptr = node->children[c - node->cmin];
|
||||
|
||||
/* slot not used yet, just point to mimetype */
|
||||
if (ptr == NULL) {
|
||||
node->children[c - node->cmin] = mimetype;
|
||||
return;
|
||||
}
|
||||
|
||||
/* slot contains another node */
|
||||
if (MIME_IS_NODE(ptr)) {
|
||||
li_mimetype_insert(MIME_UNMARK_NODE(ptr), suffix, mimetype, depth+1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* slot contains a mimetype, split into node */
|
||||
node_new = g_slice_new(liMimetypeNode);
|
||||
node_new->mimetype = ptr;
|
||||
node_new->cmax = node_new->cmin = suffix->str[suffix->len - depth - 1];
|
||||
node_new->children = g_malloc(sizeof(gpointer));
|
||||
node_new->children[0] = mimetype;
|
||||
node->children[c - node->cmin] = MIME_MARK_NODE(node_new);
|
||||
}
|
||||
|
||||
LI_API GString *li_mimetype_get(liVRequest *vr, GString *filename) {
|
||||
/* search in mime_types option for the longest suffix match */
|
||||
GString *mimetype;
|
||||
liMimetypeNode *node;
|
||||
gchar *c;
|
||||
gpointer ptr;
|
||||
|
||||
if (!vr || !filename || !filename->len)
|
||||
return NULL;
|
||||
|
||||
node = CORE_OPTIONPTR(LI_CORE_OPTION_MIME_TYPES).ptr;
|
||||
|
||||
mimetype = node->mimetype;
|
||||
|
||||
for (c = filename->str + filename->len - 1; c > filename->str; c--) {
|
||||
if (*c < node->cmin || *c > node->cmax)
|
||||
return mimetype;
|
||||
|
||||
ptr = node->children[*c - node->cmin];
|
||||
if (!ptr)
|
||||
return mimetype;
|
||||
|
||||
if (!MIME_IS_NODE(ptr))
|
||||
return ptr;
|
||||
|
||||
node = MIME_UNMARK_NODE(ptr);
|
||||
}
|
||||
|
||||
return mimetype;
|
||||
}
|
|
@ -1185,15 +1185,18 @@ static gboolean core_option_static_exclude_exts_parse(liServer *srv, liWorker *w
|
|||
|
||||
static gboolean core_option_mime_types_parse(liServer *srv, liWorker *wrk, liPlugin *p, size_t ndx, liValue *val, gpointer *oval) {
|
||||
GArray *arr;
|
||||
liMimetypeNode *node;
|
||||
|
||||
UNUSED(srv);
|
||||
UNUSED(wrk);
|
||||
UNUSED(p);
|
||||
UNUSED(ndx);
|
||||
|
||||
*oval = node = li_mimetype_node_new();
|
||||
node->mimetype = g_string_new_len(CONST_STR_LEN("application/octet-stream"));
|
||||
|
||||
/* default value */
|
||||
if (!val) {
|
||||
*oval = g_array_new(FALSE, TRUE, sizeof(liValue));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -1204,11 +1207,13 @@ static gboolean core_option_mime_types_parse(liServer *srv, liWorker *wrk, liPlu
|
|||
liValue *v1, *v2;
|
||||
if (v->type != LI_VALUE_LIST) {
|
||||
ERROR(srv, "mime_types option expects a list of string tuples, entry #%u is of type %s", i, li_value_type_string(v->type));
|
||||
li_mimetype_node_free(node);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (v->data.list->len != 2) {
|
||||
ERROR(srv, "mime_types option expects a list of string tuples, entry #%u is not a tuple", i);
|
||||
li_mimetype_node_free(node);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -1216,26 +1221,22 @@ static gboolean core_option_mime_types_parse(liServer *srv, liWorker *wrk, liPlu
|
|||
v2 = g_array_index(v->data.list, liValue*, 1);
|
||||
if (v1->type != LI_VALUE_STRING || v2->type != LI_VALUE_STRING) {
|
||||
ERROR(srv, "mime_types option expects a list of string tuples, entry #%u is a (%s,%s) tuple", i, li_value_type_string(v1->type), li_value_type_string(v2->type));
|
||||
li_mimetype_node_free(node);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* everything ok */
|
||||
*oval = li_value_extract_list(val);
|
||||
li_mimetype_insert(node, v1->data.string, li_value_extract_string(v2), 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void core_option_mime_types_free(liServer *srv, liPlugin *p, size_t ndx, gpointer oval) {
|
||||
GArray *list = oval;
|
||||
UNUSED(srv);
|
||||
UNUSED(p);
|
||||
UNUSED(ndx);
|
||||
|
||||
for (guint i = 0; i < list->len; i++)
|
||||
li_value_free(g_array_index(list, liValue*, i));
|
||||
|
||||
g_array_free(list, TRUE);
|
||||
li_mimetype_node_free(oval);
|
||||
}
|
||||
|
||||
static gboolean core_option_etag_use_parse(liServer *srv, liWorker *wrk, liPlugin *p, size_t ndx, liValue *val, liOptionValue *oval) {
|
||||
|
|
|
@ -38,6 +38,7 @@ def build(bld):
|
|||
lighttpd_glue.c
|
||||
lighttpd_worker.c
|
||||
log.c
|
||||
mimetype.c
|
||||
network.c
|
||||
network_sendfile.c
|
||||
network_write.c
|
||||
|
|
Loading…
Reference in New Issue