[pattern] Add ranges (and fix match-info handling)
parent
eecf0d90be
commit
52584c99bb
|
@ -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 (nth_ndx < a->len) {
|
||||
GString *str = g_array_index(a, GString*, nth_ndx);
|
||||
g_string_append_len(pattern_result, GSTR_LEN(str));
|
||||
if (NULL == a || 0 == a->len) return;
|
||||
|
||||
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) {
|
||||
/* $n means n-th part of hostname from end divided by dots */
|
||||
gchar *c, *end;
|
||||
guint i = 0;
|
||||
GString *str = data;
|
||||
typedef struct docroot_split docroot_split;
|
||||
struct docroot_split {
|
||||
GString *hostname;
|
||||
gchar** splits;
|
||||
guint split_len;
|
||||
};
|
||||
|
||||
if (nth_ndx == 0) {
|
||||
g_string_append_len(pattern_result, GSTR_LEN(str));
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (from <= to) {
|
||||
for (i = from; i <= to; i++) {
|
||||
if (first) {
|
||||
first = FALSE;
|
||||
g_string_append_len(pattern_result, CONST_STR_LEN("."));
|
||||
}
|
||||
|
||||
end = c-1;
|
||||
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…
Reference in New Issue