[pattern] Add ranges (and fix match-info handling)

personal/stbuehler/wip
Stefan Bühler 12 years ago
parent eecf0d90be
commit 52584c99bb
  1. 10
      include/lighttpd/pattern.h
  2. 24
      src/main/condition.c
  3. 171
      src/main/pattern.c
  4. 60
      src/main/plugin_core.c

@ -6,8 +6,10 @@
/* liPattern is a GArray in disguise */
typedef GArray liPattern;
/* a pattern callback receives an integer index and a data pointer (usually an array) and must return a GString* which gets inserted into the pattern result */
typedef void (*liPatternCB) (GString *pattern_result, guint8 nth_ndx, gpointer data);
/* a pattern callback receives an integer index range [from-to] and a data pointer (usually an array) and must return a GString* which gets inserted into the pattern result
* "from" doesn't have to be smaller than "to" (allows reverse ranges)!
*/
typedef void (*liPatternCB) (GString *pattern_result, guint from, guint to, gpointer data);
/* constructs a new liPattern* by parsing the given string, returns NULL on error */
LI_API liPattern *li_pattern_new(liServer *srv, const gchar* str);
@ -16,8 +18,8 @@ LI_API void li_pattern_free(liPattern *pattern);
LI_API void li_pattern_eval(liVRequest *vr, GString *dest, liPattern *pattern, liPatternCB nth_callback, gpointer nth_data, liPatternCB nth_prev_callback, gpointer nth_prev_data);
/* default array callback, expects a GArray* containing GString* elements */
LI_API void li_pattern_array_cb(GString *pattern_result, guint8 nth_ndx, gpointer data);
LI_API void li_pattern_array_cb(GString *pattern_result, guint from, guint to, gpointer data);
/* default regex callback, expects a GMatchInfo* */
LI_API void li_pattern_regex_cb(GString *pattern_result, guint8 nth_ndx, gpointer data);
LI_API void li_pattern_regex_cb(GString *pattern_result, guint from, guint to, gpointer data);
#endif

@ -620,14 +620,26 @@ static liHandlerResult li_condition_check_eval_string(liVRequest *vr, liConditio
*res = !g_str_has_suffix(val, cond->rvalue.string->str);
break;
case LI_CONFIG_COND_MATCH:
*res = g_regex_match(cond->rvalue.regex, val, 0, &arse.match_info);
arse.string = (*res) ? g_string_new(val) : NULL;
g_array_append_val(vr->action_stack.regex_stack, arse);
arse.match_info = NULL;
arse.string = g_string_new(val); /* we have to copy the value, as match-info references it */
*res = g_regex_match(cond->rvalue.regex, arse.string->str, 0, &arse.match_info);
if (*res) {
g_array_append_val(vr->action_stack.regex_stack, arse);
} else {
g_match_info_free(arse.match_info);
g_string_free(arse.string, TRUE);
}
break;
case LI_CONFIG_COND_NOMATCH:
*res = !g_regex_match(cond->rvalue.regex, val, 0, &arse.match_info);
arse.string = NULL;
g_array_append_val(vr->action_stack.regex_stack, arse);
arse.match_info = NULL;
arse.string = g_string_new(val); /* we have to copy the value, as match-info references it */
*res = !g_regex_match(cond->rvalue.regex, arse.string->str, 0, &arse.match_info);
if (*res) {
g_match_info_free(arse.match_info);
g_string_free(arse.string, TRUE);
} else {
g_array_append_val(vr->action_stack.regex_stack, arse);
}
break;
case LI_CONFIG_COND_IP:
case LI_CONFIG_COND_NOTIP:

@ -3,37 +3,105 @@
typedef struct {
enum {
PATTERN_STRING, /* literal */
PATTERN_NTH, /* $n */
PATTERN_NTH_PREV, /* %n */
PATTERN_VAR, /* %{req.foo} */
PATTERN_VAR_ENCODED /* %{enc:req.foo} */
PATTERN_STRING, /* literal */
PATTERN_NTH, /* $n */
PATTERN_NTH_PREV, /* %n */
PATTERN_VAR, /* %{req.foo} */
PATTERN_VAR_ENCODED /* %{enc:req.foo} */
} type;
union {
GString *str; /* PATTERN_STRING */
guint8 ndx; /* PATTERN_NTH and PATTERN_NTH_PREV */
liConditionLValue *lvalue; /* PATTERN_VAR and PATTERN_VAR_ENCODED */
/* PATTERN_STRING */
GString *str;
/* PATTERN_NTH and PATTERN_NTH_PREV */
struct {
guint from, to;
} range;
/* PATTERN_VAR and PATTERN_VAR_ENCODED */
liConditionLValue *lvalue;
} data;
} liPatternPart;
static gboolean parse_range(liServer *srv, liPatternPart *part, const gchar **str, const gchar *origstr) {
guint64 val;
gchar *endc = NULL;
const gchar *c = *str;
c++; /* skip '[' */
if (*c == '-') {
if (c[1] == ']') {
/* parse error */
ERROR(srv, "could not parse pattern, empty range %%[-]: \"%s\"", origstr);
return FALSE;
}
part->data.range.from = G_MAXUINT;
} else if (*c == ']') {
/* parse error */
ERROR(srv, "could not parse pattern, empty range %%[]: \"%s\"", origstr);
return FALSE;
} else {
errno = 0;
val = g_ascii_strtoull(c, &endc, 10);
if (0 != errno || val > G_MAXUINT) {
ERROR(srv, "could not parse pattern, range overflow: \"%s\"", origstr);
return FALSE;
} else if (endc == c || (*endc != '-' && *endc != ']')) {
ERROR(srv, "could not parse pattern, invalid range: \"%s\"", origstr);
return FALSE;
}
part->data.range.from = val;
}
part->data.range.to = part->data.range.from;
if (*c == '-') {
c++;
if (*c == ']') {
part->data.range.to = G_MAXUINT;
} else {
errno = 0;
val = g_ascii_strtoull(c, &endc, 10);
if (0 != errno || val > G_MAXUINT) {
ERROR(srv, "could not parse pattern, range overflow: \"%s\"", origstr);
return FALSE;
} else if (endc == c || (*endc != ']')) {
ERROR(srv, "could not parse pattern, invalid range: \"%s\"", origstr);
return FALSE;
}
part->data.range.to = val;
}
}
c++; /* skip ']' */
*str = c;
return TRUE;
}
liPattern *li_pattern_new(liServer *srv, const gchar* str) {
GArray *pattern;
liPatternPart part;
gchar *c;
const gchar *c;
gboolean encoded;
pattern = g_array_new(FALSE, TRUE, sizeof(liPatternPart));
for (c = (gchar*)str; *c;) {
for (c = str; *c;) {
if (*c == '$') {
/* $n, PATTERN_NTH */
c++;
if (*c >= '0' && *c <= '9') {
part.type = PATTERN_NTH;
part.data.ndx = *c - '0';
part.data.range.from = part.data.range.to = *c - '0';
g_array_append_val(pattern, part);
c++;
} else if ('[' == *c) {
part.type = PATTERN_NTH;
if (!parse_range(srv, &part, &c, str)) {
li_pattern_free((liPattern*)pattern);
return NULL;
}
g_array_append_val(pattern, part);
} else {
/* parse error */
ERROR(srv, "could not parse pattern: \"%s\"", str);
@ -45,12 +113,19 @@ liPattern *li_pattern_new(liServer *srv, const gchar* str) {
if (*c >= '0' && *c <= '9') {
/* %n, PATTERN_NTH_PREV */
part.type = PATTERN_NTH_PREV;
part.data.ndx = *c - '0';
part.data.range.from = part.data.range.to = *c - '0';
g_array_append_val(pattern, part);
c++;
} else if ('[' == *c) {
part.type = PATTERN_NTH_PREV;
if (!parse_range(srv, &part, &c, str)) {
li_pattern_free((liPattern*)pattern);
return NULL;
}
g_array_append_val(pattern, part);
} else if (*c == '{') {
/* %{var}, PATTERN_VAR */
gchar *lval_c, *lval_start;
const gchar *lval_c, *lval_start;
guint lval_len = 0;
GString *key = NULL;
@ -67,7 +142,7 @@ liPattern *li_pattern_new(liServer *srv, const gchar* str) {
for (lval_start = lval_c; *lval_c != '\0' && *lval_c != '}'; lval_c++) {
/* got a key */
if (*lval_c == '[') {
gchar *key_c, *key_start;
const gchar *key_c, *key_start;
guint key_len = 0;
/* search for closing ']' */
@ -120,7 +195,7 @@ liPattern *li_pattern_new(liServer *srv, const gchar* str) {
}
} else {
/* string */
gchar *first;
const gchar *first;
part.type = PATTERN_STRING;
part.data.str= g_string_sized_new(0);
@ -187,23 +262,28 @@ void li_pattern_eval(liVRequest *vr, GString *dest, liPattern *pattern, liPatter
g_string_append_len(dest, GSTR_LEN(part->data.str));
break;
case PATTERN_NTH:
if (nth_callback)
nth_callback(dest, part->data.ndx, nth_data);
if (NULL != nth_callback) {
nth_callback(dest, part->data.range.from, part->data.range.to, nth_data);
}
break;
case PATTERN_NTH_PREV:
if (nth_prev_callback)
nth_prev_callback(dest, part->data.ndx, nth_prev_data);
if (NULL != nth_prev_callback) {
nth_prev_callback(dest, part->data.range.from, part->data.range.to, nth_prev_data);
}
break;
case PATTERN_VAR_ENCODED:
encoded = TRUE;
/* fall through */
case PATTERN_VAR:
if (vr == NULL) continue;
res = li_condition_get_value(vr, part->data.lvalue, &cond_val, LI_COND_VALUE_HINT_STRING);
if (res == LI_HANDLER_GO_ON) {
if (encoded)
if (encoded) {
li_string_encode_append(li_condition_value_to_string(vr, &cond_val), dest, LI_ENCODING_URI);
else
} else {
g_string_append(dest, li_condition_value_to_string(vr, &cond_val));
}
}
break;
@ -211,22 +291,51 @@ void li_pattern_eval(liVRequest *vr, GString *dest, liPattern *pattern, liPatter
}
}
void li_pattern_array_cb(GString *pattern_result, guint8 nth_ndx, gpointer data) {
void li_pattern_array_cb(GString *pattern_result, guint from, guint to, gpointer data) {
GArray *a = data;
guint i;
if (NULL == a || 0 == a->len) return;
if (nth_ndx < a->len) {
GString *str = g_array_index(a, GString*, nth_ndx);
g_string_append_len(pattern_result, GSTR_LEN(str));
if (G_LIKELY(from <= to)) {
to = MIN(to, a->len - 1);
for (i = from; i <= to; i++) {
GString *str = g_array_index(a, GString*, i);
if (NULL != str) {
g_string_append_len(pattern_result, GSTR_LEN(str));
}
}
} else {
from = MIN(from, a->len - 1); /* => from+1 is defined */
for (i = from + 1; i-- >= to; ) {
GString *str = g_array_index(a, GString*, i);
if (NULL != str) {
g_string_append_len(pattern_result, GSTR_LEN(str));
}
}
}
}
void li_pattern_regex_cb(GString *pattern_result, guint8 nth_ndx, gpointer data) {
gint start_pos, end_pos;
void li_pattern_regex_cb(GString *pattern_result, guint from, guint to, gpointer data) {
GMatchInfo *match_info = data;
guint i;
gint start_pos, end_pos;
if (!match_info)
return;
if (NULL == match_info) return;
if (g_match_info_fetch_pos(match_info, (gint)nth_ndx, &start_pos, &end_pos))
g_string_append_len(pattern_result, g_match_info_get_string(match_info) + start_pos, end_pos - start_pos);
if (G_LIKELY(from <= to)) {
to = MIN(to, G_MAXINT);
for (i = from; i <= to; i++) {
if (g_match_info_fetch_pos(match_info, (gint) i, &start_pos, &end_pos)) {
g_string_append_len(pattern_result, g_match_info_get_string(match_info) + start_pos, end_pos - start_pos);
}
}
} else {
from = MIN(from, G_MAXINT); /* => from+1 is defined */
for (i = from + 1; i-- >= to; ) {
if (g_match_info_fetch_pos(match_info, (gint) i, &start_pos, &end_pos)) {
g_string_append_len(pattern_result, g_match_info_get_string(match_info) + start_pos, end_pos - start_pos);
}
}
}
}

@ -151,29 +151,53 @@ static gboolean core_setup_set(liServer *srv, liPlugin* p, liValue *val, gpointe
return li_plugin_set_default_option(srv, val_name->data.string->str, val_val);
}
static void core_docroot_nth_cb(GString *pattern_result, guint8 nth_ndx, gpointer data) {
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 */
gchar *c, *end;
guint i = 0;
GString *str = data;
/* range is interpreted reversed !!! */
gboolean first = TRUE;
guint i;
docroot_split *ctx = data;
if (nth_ndx == 0) {
g_string_append_len(pattern_result, GSTR_LEN(str));
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;
}
end = str->str + str->len - 1;
if (NULL == ctx->splits) {
ctx->splits = g_strsplit_set(ctx->hostname->str, ".", 31);
ctx->split_len = g_strv_length(ctx->splits);
}
for (c = end; c > str->str; c--) {
if (*c == '.') {
i++;
if (0 == ctx->split_len) return;
if (i == nth_ndx) {
g_string_append_len(pattern_result, c+1, end - c);
return;
}
from = MAX(from, ctx->split_len);
to = MAX(to, ctx->split_len);
end = c-1;
if (from <= to) {
for (i = from; i <= to; i++) {
if (first) {
first = FALSE;
g_string_append_len(pattern_result, CONST_STR_LEN("."));
}
g_string_append(pattern_result, ctx->splits[ctx->split_len - i]);
}
} else {
for (i = from+1; i-- >= to; ) {
if (first) {
first = FALSE;
g_string_append_len(pattern_result, CONST_STR_LEN("."));
}
g_string_append(pattern_result, ctx->splits[ctx->split_len - i]);
}
}
}
@ -182,6 +206,7 @@ static liHandlerResult core_handle_docroot(liVRequest *vr, gpointer param, gpoin
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);
@ -205,7 +230,7 @@ static liHandlerResult core_handle_docroot(liVRequest *vr, gpointer param, gpoin
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, vr->request.uri.host, li_pattern_regex_cb, match_info);
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 */
@ -217,6 +242,7 @@ static liHandlerResult core_handle_docroot(liVRequest *vr, gpointer param, gpoin
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 */
@ -227,6 +253,8 @@ static liHandlerResult core_handle_docroot(liVRequest *vr, gpointer param, gpoin
}
}
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));

Loading…
Cancel
Save