2
0
Fork 0

[mod_vhost] drop vhost.simple / vhost.pattern

personal/stbuehler/wip
Stefan Bühler 2010-09-26 22:18:25 +02:00
parent 8a2fce79e8
commit 9a7dc4ecd6
1 changed files with 5 additions and 391 deletions

View File

@ -3,7 +3,7 @@
*
* Description:
* mod_vhost offers various ways to implement virtual webhosts.
* It can map hostnames to document roots or even actions and offers multiple ways to do so.
* It can map hostnames to actions and offers multiple ways to do so.
* These ways differ in the flexibility of mapping (what to map and what to map to) as well as performance.
*
* Setups:
@ -11,10 +11,6 @@
* Options:
* vhost.debug = <true|false> - enable debug output
* Actions:
* vhost.simple ("server-root" => string, "docroot" => string, "default" => string);
* - builds the document root by concatinating server-root + hostname + docroot
* - if the newly build docroot does not exist, repeat with "default" hostname
* - not very flexible but fast (use symlinks for some limited flexibility)
* vhost.map ["host1": action1, "host2": action2, "default": action0];
* - lookup action by using the hostname as the key of the hashtable
* - if not found, use default action
@ -24,26 +20,13 @@
* - if no match, use default action
* - slowest method but the most flexible one
* - somewhat optimized internally and automatically to speed up lookup of frequently accessed hosts
* vhost.pattern string;
* - builds document root by substituting $0..$9 with parts of the hostname
* - parts are defined by splitting the hostname at each dot
* - $0 is the whole hostname, $1 the last part aka the tld, $2 the second last and so on
* - ${n-} is part n and all others, concatinated by dots (0 < n <= 9)
* - ${n-m} is parts n to m, concatinated by dots (0 < n < m <= 9)
*
* Example config:
* vhost.simple ("server-root" => "/var/www/vhosts/", "docroot" => "/pub", "default" => "localhost");
* - maps test.lighttpd.net to /var/www/vhosts/test.lighttpd.net/pub/
* and lighttpd.net to /var/www/vhosts/lighttpd.net/pub/
*
*
* mydom1 {...} mydom2 {...} defaultdom {...}
* vhost.map ["dom1.com": mydom1, "dom2.tld": mydom2, "default": defaultdom];
* vhost.map_regex ["^(.+\.)?dom1\.com$": mydom1, "^dom2\.(com|net|org)$": mydom2, "default": defaultdom];
*
* vhost.pattern "/var/www/vhosts/$2.$1/$0/pub/";
* - maps test.lighttpd.net to /var/www/vhosts/lighttpd.net/test.lighttpd.net/pub/
* and lighttpd.net to /var/www/vhosts/lighttpd.net/lighttpd.net/pub/
*
* Tip:
* You can combine vhost.map and vhost.map_regex to create a reasonably fast and flexible vhost mapping mechanism.
* Just use a vhost.map_regex action as the default fallback action in vhost.map.
@ -63,21 +46,14 @@
LI_API gboolean mod_vhost_init(liModules *mods, liModule *mod);
LI_API gboolean mod_vhost_free(liModules *mods, liModule *mod);
struct vhost_simple_data {
liPlugin *plugin;
GString *default_vhost;
GString *docroot;
GString *server_root;
};
typedef struct vhost_simple_data vhost_simple_data;
typedef struct vhost_map_data vhost_map_data;
struct vhost_map_data {
liPlugin *plugin;
GHashTable *hash;
liValue *default_action;
};
typedef struct vhost_map_data vhost_map_data;
typedef struct vhost_map_regex_entry vhost_map_regex_entry;
struct vhost_map_regex_entry {
GRegex *regex;
liValue *action;
@ -85,177 +61,13 @@ struct vhost_map_regex_entry {
guint hits;
guint hits_30s;
};
typedef struct vhost_map_regex_entry vhost_map_regex_entry;
typedef struct vhost_map_regex_data vhost_map_regex_data;
struct vhost_map_regex_data {
liPlugin *plugin;
GArray *lists; /* array of array of vhost_map_regex_entry */
liValue *default_action;
};
typedef struct vhost_map_regex_data vhost_map_regex_data;
struct vhost_pattern_part {
enum {
VHOST_PATTERN_STRING,
VHOST_PATTERN_PART,
VHOST_PATTERN_RANGE
} type;
union {
GString *str;
guint8 idx;
struct {
guint8 n;
guint8 m;
} range;
} data;
};
typedef struct vhost_pattern_part vhost_pattern_part;
struct vhost_pattern_hostpart {
gchar *str;
guint len;
};
typedef struct vhost_pattern_hostpart vhost_pattern_hostpart;
struct vhost_pattern_data {
GArray *parts;
guint max_idx;
liPlugin *plugin;
};
typedef struct vhost_pattern_data vhost_pattern_data;
static liHandlerResult vhost_simple(liVRequest *vr, gpointer param, gpointer *context) {
struct stat st;
gint err;
gboolean debug;
vhost_simple_data *sd = param;
debug = _OPTION(vr, sd->plugin, 0).boolean;
/* we use context to check if the physical path was already built */
if (*context == NULL) {
/* build document root: sd->server_root + req.host + sd->docroot */
g_string_truncate(vr->physical.doc_root, 0);
g_string_append_len(vr->physical.doc_root, GSTR_LEN(sd->server_root));
g_string_append_len(vr->physical.doc_root, GSTR_LEN(vr->request.uri.host));
g_string_append_len(vr->physical.doc_root, GSTR_LEN(sd->docroot));
}
/* check if directory exists. if not, fall back to default host */
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(1);
return LI_HANDLER_WAIT_FOR_EVENT;
default:
if (debug)
VR_DEBUG(vr, "vhost.simple: docroot for vhost \"%s\" does not exist, falling back to default", vr->request.uri.host->str);
g_string_truncate(vr->physical.doc_root, sd->server_root->len);
g_string_append_len(vr->physical.doc_root, GSTR_LEN(sd->default_vhost));
g_string_append_len(vr->physical.doc_root, GSTR_LEN(sd->docroot));
}
if (debug)
VR_DEBUG(vr, "vhost.simple: docroot now \"%s\"", vr->physical.doc_root->str);
/* 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));
g_string_append_len(vr->physical.path, GSTR_LEN(vr->request.uri.path));
return LI_HANDLER_GO_ON;
}
static void vhost_simple_free(liServer *srv, gpointer param) {
vhost_simple_data *sd = param;
UNUSED(srv);
if (sd->default_vhost)
g_string_free(sd->default_vhost, TRUE);
if (sd->docroot)
g_string_free(sd->docroot, TRUE);
if (sd->server_root)
g_string_free(sd->server_root, TRUE);
g_slice_free(vhost_simple_data, sd);
}
static liAction* vhost_simple_create(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
guint i;
GArray *arr;
liValue *k, *v;
vhost_simple_data *sd;
GString **setting;
UNUSED(wrk);
UNUSED(userdata);
WARNING(srv, "%s", "vhost.simple is deprecated, 'docroot' now has the same power");
if (!val || val->type != LI_VALUE_LIST) {
ERROR(srv, "%s", "vhost.simple expects a list if string tuples as parameter");
return NULL;
}
sd = g_slice_new0(vhost_simple_data);
sd->plugin = p;
arr = val->data.list;
for (i = 0; i < arr->len; i++) {
val = g_array_index(arr, liValue*, i);
if (val->type != LI_VALUE_LIST || val->data.list->len != 2) {
vhost_simple_free(srv, sd);
ERROR(srv, "%s", "vhost.simple expects a list if string tuples as parameter");
return NULL;
}
k = g_array_index(val->data.list, liValue*, 0);
v = g_array_index(val->data.list, liValue*, 1);
if (k->type != LI_VALUE_STRING || v->type != LI_VALUE_STRING) {
vhost_simple_free(srv, sd);
ERROR(srv, "%s", "vhost.simple expects a list if string tuples as parameter");
return NULL;
}
if (g_str_equal(k->data.string->str, "server-root")) {
setting = &sd->server_root;
} else if (g_str_equal(k->data.string->str, "docroot")) {
setting = &sd->docroot;
} else if (g_str_equal(k->data.string->str, "default")) {
setting = &sd->default_vhost;
} else {
vhost_simple_free(srv, sd);
ERROR(srv, "unkown setting \"%s\" for vhost.simple", k->data.string->str);
return NULL;
}
if (*setting) {
vhost_simple_free(srv, sd);
ERROR(srv, "%s", "parameter set twice for vhost.simple");
return NULL;
}
*setting = li_value_extract_string(v);
}
if (!sd->server_root || !sd->docroot || !sd->default_vhost) {
vhost_simple_free(srv, sd);
ERROR(srv, "%s", "missing parameter for vhost.simple. need \"server-root\", \"docroot\" and \"default\"");
return NULL;
}
/* make sure server_root has a trailing slash (or whatever this OS uses as dir seperator */
if (sd->server_root->len == 0 || sd->server_root->str[sd->server_root->len-1] != G_DIR_SEPARATOR)
g_string_append_c(sd->server_root, G_DIR_SEPARATOR);
/* make sure docroot begins with a slash (or whatever this OS uses as dir seperator */
if (sd->docroot->len == 0 || sd->docroot->str[0] != G_DIR_SEPARATOR)
g_string_prepend_c(sd->docroot, G_DIR_SEPARATOR);
return li_action_new_function(vhost_simple, NULL, vhost_simple_free, sd);
}
static liHandlerResult vhost_map(liVRequest *vr, gpointer param, gpointer *context) {
liValue *v;
@ -493,202 +305,6 @@ static liAction* vhost_map_regex_create(liServer *srv, liWorker *wrk, liPlugin*
return li_action_new_function(vhost_map_regex, NULL, vhost_map_regex_free, mrd);
}
static liHandlerResult vhost_pattern(liVRequest *vr, gpointer param, gpointer *context) {
GArray *parts = g_array_sized_new(FALSE, TRUE, sizeof(vhost_pattern_hostpart), 6);
vhost_pattern_data *pattern = param;
gboolean debug = _OPTION(vr, pattern->plugin, 0).boolean;
guint i, j;
gchar *c, *c_last;
vhost_pattern_hostpart hp;
UNUSED(context);
if (!vr->request.uri.host->len) {
if (debug)
VR_DEBUG(vr, "%s", "vhost_pattern: no host given");
return LI_HANDLER_GO_ON;
}
/* parse host. we traverse the host in reverse order */
/* foo.bar.baz. */
c = &vr->request.uri.host->str[vr->request.uri.host->len-1];
c_last = c+1;
for (i = 0; i < pattern->max_idx && c >= vr->request.uri.host->str; c--) {
if (*c == '.') {
hp.str = c+1;
hp.len = c_last - c - 1;
g_array_append_val(parts, hp);
i++;
c_last = c;
}
}
if (i < pattern->max_idx) {
/* the last part */
hp.str = vr->request.uri.host->str;
hp.len = c_last - hp.str;
g_array_append_val(parts, hp);
}
/* now construct the new docroot */
g_string_truncate(vr->physical.doc_root, 0);
for (i = 0; i < pattern->parts->len; i++) {
vhost_pattern_part *p = &g_array_index(pattern->parts, vhost_pattern_part, i);
switch (p->type) {
case VHOST_PATTERN_STRING:
g_string_append_len(vr->physical.doc_root, GSTR_LEN(p->data.str));
break;
case VHOST_PATTERN_PART:
if (p->data.idx == 0) {
/* whole hostname */
g_string_append_len(vr->physical.doc_root, GSTR_LEN(vr->request.uri.host));
} else if (p->data.idx <= parts->len) {
/* specific part */
g_string_append_len(vr->physical.doc_root, g_array_index(parts, vhost_pattern_hostpart, p->data.idx-1).str, g_array_index(parts, vhost_pattern_hostpart, p->data.idx-1).len);
}
break;
case VHOST_PATTERN_RANGE:
if (p->data.range.n > parts->len)
continue;
for (j = MIN(p->data.range.m, parts->len); j >= p->data.range.n; j--) {
if (j < MIN(p->data.range.m, parts->len))
g_string_append_c(vr->physical.doc_root, '.');
g_string_append_len(vr->physical.doc_root, g_array_index(parts, vhost_pattern_hostpart, j-1).str, g_array_index(parts, vhost_pattern_hostpart, j-1).len);
}
break;
}
}
/* 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));
g_string_append_len(vr->physical.path, GSTR_LEN(vr->request.uri.path));
if (debug)
VR_DEBUG(vr, "vhost.pattern: mapped host \"%s\" to docroot \"%s\"", vr->request.uri.host->str, vr->physical.doc_root->str);
g_array_free(parts, TRUE);
return LI_HANDLER_GO_ON;
}
static void vhost_pattern_free(liServer *srv, gpointer param) {
vhost_pattern_data *pd = param;
guint i;
UNUSED(srv);
for (i = 0; i < pd->parts->len; i++) {
vhost_pattern_part *p = &g_array_index(pd->parts, vhost_pattern_part, i);
if (p->type == VHOST_PATTERN_STRING) {
g_string_free(p->data.str, TRUE);
}
}
g_array_free(pd->parts, TRUE);
g_slice_free(vhost_pattern_data, pd);
}
static liAction* vhost_pattern_create(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
vhost_pattern_data *pd;
GString *str;
gchar *c, *c_last;
vhost_pattern_part part;
UNUSED(wrk); UNUSED(userdata);
WARNING(srv, "%s", "vhost.pattern is deprecated, 'docroot' now has the same power");
if (!val || val->type != LI_VALUE_STRING) {
ERROR(srv, "%s", "vhost.pattern expects a string as parameter");
return NULL;
}
str = li_value_extract_string(val);
pd = g_slice_new0(vhost_pattern_data);
pd->parts = g_array_sized_new(FALSE, TRUE, sizeof(vhost_pattern_part), 6);
pd->plugin = p;
/* parse pattern */
for (c_last = c = str->str; *c; c++) {
if (*c == '$') {
if (c - c_last > 0) {
/* normal string */
part.type = VHOST_PATTERN_STRING;
part.data.str = g_string_new_len(c_last, c - c_last);
g_array_append_val(pd->parts, part);
}
c++;
c_last = c+1;
if (*c == '$') {
/* $$ */
if (pd->parts->len && g_array_index(pd->parts, vhost_pattern_part, pd->parts->len - 1).type == VHOST_PATTERN_STRING) {
g_string_append_c(g_array_index(pd->parts, vhost_pattern_part, pd->parts->len - 1).data.str, '$');
continue;
} else if (!pd->parts->len) {
continue;
} else {
part.type = VHOST_PATTERN_STRING;
part.data.str = g_string_new_len(CONST_STR_LEN("$"));
}
g_array_append_val(pd->parts, part);
}
else if (*c >= '0' && *c <= '9') {
/* $n */
part.type = VHOST_PATTERN_PART;
part.data.idx = *c - '0';
pd->max_idx = MAX(pd->max_idx, part.data.idx);
g_array_append_val(pd->parts, part);
} else if (*c == '{') {
/* ${n-} or ${n-m} */
c++;
if (!(*c > '0' && *c <= '9') || *(c+1) != '-') {
vhost_pattern_free(srv, pd);
ERROR(srv, "vhost.pattern: malformed pattern \"%s\"", str->str);
return NULL;
}
part.type = VHOST_PATTERN_RANGE;
if (*(c+2) == '}') {
/* ${n-} */
part.data.range.n = *c - '0';
part.data.range.m = 9;
c_last += 3;
pd->max_idx = 9;
} else if (*(c+2) > '0' && *(c+2) <= '9' && *c < *(c+2) && *(c+3) == '}') {
/* ${n-m} */
part.data.range.n = *c - '0';
part.data.range.m = *(c+2) - '0';
c_last += 4;
pd->max_idx = MAX(pd->max_idx, part.data.range.m);
} else {
vhost_pattern_free(srv, pd);
ERROR(srv, "vhost.pattern: malformed pattern \"%s\"", str->str);
return NULL;
}
g_array_append_val(pd->parts, part);
} else {
vhost_pattern_free(srv, pd);
ERROR(srv, "vhost.pattern: malformed pattern \"%s\"", str->str);
return NULL;
}
}
}
if (c - c_last > 0) {
part.type = VHOST_PATTERN_STRING;
part.data.str = g_string_new_len(c_last, c - c_last);
g_array_append_val(pd->parts, part);
}
return li_action_new_function(vhost_pattern, NULL, vhost_pattern_free, pd);
}
static const liPluginOption options[] = {
{ "vhost.debug", LI_VALUE_BOOLEAN, FALSE, NULL },
@ -697,10 +313,8 @@ static const liPluginOption options[] = {
};
static const liPluginAction actions[] = {
{ "vhost.simple", vhost_simple_create, NULL },
{ "vhost.map", vhost_map_create, NULL },
{ "vhost.map_regex", vhost_map_regex_create, NULL },
{ "vhost.pattern", vhost_pattern_create, NULL },
{ NULL, NULL, NULL }
};