From 03985fd2a464e85ea457db38c4c551299e443533 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Wed, 10 Jul 2024 14:40:07 +0800 Subject: [PATCH] http: Add "if" option to the "match" object This feature allows users to specify conditions to check if one route is matched. It is used the same way as the "if" option in the access log. Example: { "match": { "if": "`${headers['User-Agent'].split('/')[0] == 'curl'}`" }, "action": { "return": 204 } } --- src/nxt_conf_validation.c | 4 ++ src/nxt_http_route.c | 97 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 04091745c..fa8027992 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -684,6 +684,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = { .type = NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, .validator = nxt_conf_vldt_match_patterns_sets, .u.string = "cookies" + }, { + .name = nxt_string("if"), + .type = NXT_CONF_VLDT_STRING, + .validator = nxt_conf_vldt_if, }, NXT_CONF_VLDT_END diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index a82518a4a..cfdf3b4a7 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -51,6 +51,7 @@ typedef struct { nxt_conf_value_t *query; nxt_conf_value_t *source; nxt_conf_value_t *destination; + nxt_conf_value_t *condition; } nxt_http_route_match_conf_t; @@ -138,6 +139,8 @@ typedef union { typedef struct { uint32_t items; + nxt_tstr_t *condition; + uint8_t cond_negate; /* 1 bit */ nxt_http_action_t action; nxt_http_route_test_t test[]; } nxt_http_route_match_t; @@ -204,6 +207,8 @@ static nxt_http_action_t *nxt_http_route_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *start); static nxt_http_action_t *nxt_http_route_match(nxt_task_t *task, nxt_http_request_t *r, nxt_http_route_match_t *match); +static nxt_int_t nxt_http_route_match_if(nxt_task_t *task, + nxt_http_request_t *r, nxt_http_route_match_t *match); static nxt_int_t nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table); static nxt_int_t nxt_http_route_ruleset(nxt_http_request_t *r, @@ -350,6 +355,12 @@ static nxt_conf_map_t nxt_http_route_match_conf[] = { NXT_CONF_MAP_PTR, offsetof(nxt_http_route_match_conf_t, destination), }, + + { + nxt_string("if"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_match_conf_t, condition), + }, }; @@ -397,7 +408,9 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, uint32_t n; nxt_mp_t *mp; nxt_int_t ret; - nxt_conf_value_t *match_conf, *action_conf; + nxt_str_t str; + nxt_conf_value_t *match_conf, *action_conf, *condition; + nxt_router_conf_t *rtcf; nxt_http_route_test_t *test; nxt_http_route_rule_t *rule; nxt_http_route_table_t *table; @@ -405,6 +418,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_route_addr_rule_t *addr_rule; nxt_http_route_match_conf_t mtcf; + static const nxt_str_t if_path = nxt_string("/if"); static const nxt_str_t match_path = nxt_string("/match"); static const nxt_str_t action_path = nxt_string("/action"); @@ -413,9 +427,21 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, n = (match_conf != NULL) ? nxt_conf_object_members_count(match_conf) : 0; size = sizeof(nxt_http_route_match_t) + n * sizeof(nxt_http_route_test_t *); - mp = tmcf->router_conf->mem_pool; + rtcf = tmcf->router_conf; + mp = rtcf->mem_pool; + + condition = NULL; + + if (match_conf != NULL) { + condition = nxt_conf_get_path(match_conf, &if_path); + + if (condition != NULL) { + n--; + size -= sizeof(nxt_http_route_test_t *); + } + } - match = nxt_mp_alloc(mp, size); + match = nxt_mp_zalloc(mp, size); if (nxt_slow_path(match == NULL)) { return NULL; } @@ -432,7 +458,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NULL; } - if (n == 0) { + if (n == 0 && condition == NULL) { return match; } @@ -445,6 +471,22 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NULL; } + if (condition != NULL) { + nxt_conf_get_string(condition, &str); + + if (str.length > 0 && str.start[0] == '!') { + match->cond_negate = 1; + + str.start++; + str.length--; + } + + match->condition = nxt_tstr_compile(rtcf->tstr_state, &str, 0); + if (nxt_slow_path(match->condition == NULL)) { + return NULL; + } + } + test = &match->test[0]; if (mtcf.scheme != NULL) { @@ -1594,8 +1636,17 @@ nxt_http_route_match(nxt_task_t *task, nxt_http_request_t *r, nxt_http_route_match_t *match) { nxt_int_t ret; + nxt_http_action_t *action; nxt_http_route_test_t *test, *end; + if (match->condition != NULL) { + ret = nxt_http_route_match_if(task, r, match); + if (ret <= 0) { + action = (nxt_http_action_t *) (intptr_t) ret; + return action; + } + } + test = &match->test[0]; end = test + match->items; @@ -1631,6 +1682,44 @@ nxt_http_route_match(nxt_task_t *task, nxt_http_request_t *r, } +static nxt_int_t +nxt_http_route_match_if(nxt_task_t *task, nxt_http_request_t *r, + nxt_http_route_match_t *match) +{ + nxt_int_t ret, expr; + nxt_str_t str; + nxt_router_conf_t *rtcf; + + expr = 1; + + if (match->condition != NULL) { + rtcf = r->conf->socket_conf->router_conf; + + ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, + &r->tstr_cache, r, r->mem_pool); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + ret = nxt_tstr_query(task, r->tstr_query, match->condition, &str); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + if (str.length == 0 + || nxt_str_eq(&str, "0", 1) + || nxt_str_eq(&str, "false", 5) + || nxt_str_eq(&str, "null", 4) + || nxt_str_eq(&str, "undefined", 9)) + { + expr = 0; + } + } + + return match->cond_negate ^ expr; +} + + static nxt_int_t nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table) {