diff options
author | Igor Sysoev <igor@sysoev.ru> | 2019-05-30 15:33:51 +0300 |
---|---|---|
committer | Igor Sysoev <igor@sysoev.ru> | 2019-05-30 15:33:51 +0300 |
commit | 0ba7cfce7572070892bf54ed69b82a822e3ee0d4 (patch) | |
tree | e285a25d935a95118d051a29e87650d48a66a334 /src | |
parent | 3aaebe4169f80707e495f1111b7ab9c2dcb79b6d (diff) | |
download | unit-0ba7cfce7572070892bf54ed69b82a822e3ee0d4.tar.gz unit-0ba7cfce7572070892bf54ed69b82a822e3ee0d4.tar.bz2 |
Added routing based on header fields.
Diffstat (limited to '')
-rw-r--r-- | src/nxt_conf_validation.c | 68 | ||||
-rw-r--r-- | src/nxt_http_parse.c | 4 | ||||
-rw-r--r-- | src/nxt_http_parse.h | 5 | ||||
-rw-r--r-- | src/nxt_http_route.c | 399 |
4 files changed, 409 insertions, 67 deletions
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index aedcc605..bee82dd4 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -66,6 +66,12 @@ static nxt_int_t nxt_conf_vldt_match_patterns(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_match_patterns_sets(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_match_patterns_set(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_match_patterns_set_member( + nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt, @@ -218,6 +224,21 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = { &nxt_conf_vldt_match_patterns, NULL }, + { nxt_string("arguments"), + NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, + &nxt_conf_vldt_match_patterns_sets, + NULL }, + + { nxt_string("headers"), + NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, + &nxt_conf_vldt_match_patterns_sets, + NULL }, + + { nxt_string("cookies"), + NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, + &nxt_conf_vldt_match_patterns_sets, + NULL }, + NXT_CONF_VLDT_END }; @@ -748,8 +769,8 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, } state; if (nxt_conf_type(value) != NXT_CONF_STRING) { - return nxt_conf_vldt_error(vldt, - "The \"match\" patterns must be strings."); + return nxt_conf_vldt_error(vldt, "The \"match\" patterns for \"host\", " + "\"uri\", and \"method\" must be strings."); } nxt_conf_get_string(value, &pattern); @@ -798,6 +819,49 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, } +static nxt_int_t +nxt_conf_vldt_match_patterns_sets(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + if (nxt_conf_type(value) == NXT_CONF_ARRAY) { + return nxt_conf_vldt_array_iterator(vldt, value, + &nxt_conf_vldt_match_patterns_set); + } + + /* NXT_CONF_OBJECT */ + + return nxt_conf_vldt_match_patterns_set(vldt, value); +} + + +static nxt_int_t +nxt_conf_vldt_match_patterns_set(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value) +{ + if (nxt_conf_type(value) != NXT_CONF_OBJECT) { + return nxt_conf_vldt_error(vldt, "The \"match\" patterns for " + "\"arguments\", \"cookies\", and " + "\"headers\" must be objects."); + } + + return nxt_conf_vldt_object_iterator(vldt, value, + &nxt_conf_vldt_match_patterns_set_member); +} + + +static nxt_int_t +nxt_conf_vldt_match_patterns_set_member(nxt_conf_validation_t *vldt, + nxt_str_t *name, nxt_conf_value_t *value) +{ + if (name->length == 0) { + return nxt_conf_vldt_error(vldt, "The \"match\" pattern objects must " + "not contain empty member names."); + } + + return nxt_conf_vldt_match_patterns(vldt, value, NULL); +} + + #if (NXT_TLS) static nxt_int_t diff --git a/src/nxt_http_parse.c b/src/nxt_http_parse.c index 64e4bf35..05df245e 100644 --- a/src/nxt_http_parse.c +++ b/src/nxt_http_parse.c @@ -34,10 +34,6 @@ static nxt_int_t nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, #define NXT_HTTP_FIELD_LVLHSH_SHIFT 5 -#define NXT_HTTP_FIELD_HASH_INIT 159406 -#define nxt_http_field_hash_char(h, c) (((h) << 4) + (h) + (c)) -#define nxt_http_field_hash_end(h) (((h) >> 16) ^ (h)) - typedef enum { NXT_HTTP_TARGET_SPACE = 1, /* \s */ diff --git a/src/nxt_http_parse.h b/src/nxt_http_parse.h index 0326d45c..6c629936 100644 --- a/src/nxt_http_parse.h +++ b/src/nxt_http_parse.h @@ -93,6 +93,11 @@ struct nxt_http_field_s { }; +#define NXT_HTTP_FIELD_HASH_INIT 159406U +#define nxt_http_field_hash_char(h, c) (((h) << 4) + (h) + (c)) +#define nxt_http_field_hash_end(h) (((h) >> 16) ^ (h)) + + nxt_int_t nxt_http_parse_request_init(nxt_http_request_parse_t *rp, nxt_mp_t *mp); nxt_int_t nxt_http_parse_request(nxt_http_request_parse_t *rp, diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 21e83ecb..e3c3cd9f 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -9,9 +9,9 @@ typedef enum { - NXT_HTTP_ROUTE_STRING = 0, + NXT_HTTP_ROUTE_TABLE = 0, + NXT_HTTP_ROUTE_STRING, NXT_HTTP_ROUTE_STRING_PTR, - NXT_HTTP_ROUTE_FIELD, NXT_HTTP_ROUTE_HEADER, NXT_HTTP_ROUTE_ARGUMENT, NXT_HTTP_ROUTE_COOKIE, @@ -38,6 +38,7 @@ typedef struct { nxt_conf_value_t *host; nxt_conf_value_t *uri; nxt_conf_value_t *method; + nxt_conf_value_t *headers; } nxt_http_route_match_conf_t; @@ -56,17 +57,48 @@ typedef struct { typedef struct { - uintptr_t offset; - uint32_t items; + /* The object must be the first field. */ nxt_http_route_object_t object:8; + uint32_t items; + + union { + uintptr_t offset; + + struct { + u_char *start; + uint16_t hash; + uint16_t length; + } name; + } u; + nxt_http_route_pattern_t pattern[0]; } nxt_http_route_rule_t; typedef struct { uint32_t items; - nxt_http_pass_t pass; nxt_http_route_rule_t *rule[0]; +} nxt_http_route_ruleset_t; + + +typedef struct { + /* The object must be the first field. */ + nxt_http_route_object_t object:8; + uint32_t items; + nxt_http_route_ruleset_t *ruleset[0]; +} nxt_http_route_table_t; + + +typedef union { + nxt_http_route_rule_t *rule; + nxt_http_route_table_t *table; +} nxt_http_route_test_t; + + +typedef struct { + uint32_t items; + nxt_http_pass_t pass; + nxt_http_route_test_t test[0]; } nxt_http_route_match_t; @@ -87,9 +119,15 @@ static nxt_http_route_t *nxt_http_route_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv); static nxt_http_route_match_t *nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv); +static nxt_http_route_table_t *nxt_http_route_table_create(nxt_task_t *task, + nxt_mp_t *mp, nxt_conf_value_t *table_cv); +static nxt_http_route_ruleset_t *nxt_http_route_ruleset_create(nxt_task_t *task, + nxt_mp_t *mp, nxt_conf_value_t *ruleset_cv); +static nxt_http_route_rule_t *nxt_http_route_rule_header_create( + nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *rule_cv, nxt_str_t *name); static nxt_http_route_rule_t *nxt_http_route_rule_create(nxt_task_t *task, - nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, - nxt_bool_t case_sensitive, nxt_http_route_pattern_case_t pattern_case); + nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive, + nxt_http_route_pattern_case_t pattern_case); static int nxt_http_pattern_compare(const void *one, const void *two); static nxt_int_t nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern, @@ -109,8 +147,16 @@ static nxt_http_pass_t *nxt_http_route_pass(nxt_task_t *task, nxt_http_request_t *r, nxt_http_pass_t *start); static nxt_http_pass_t *nxt_http_route_match(nxt_http_request_t *r, nxt_http_route_match_t *match); +static nxt_bool_t nxt_http_route_table(nxt_http_request_t *r, + nxt_http_route_table_t *table); +static nxt_bool_t nxt_http_route_ruleset(nxt_http_request_t *r, + nxt_http_route_ruleset_t *ruleset); static nxt_bool_t nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule); +static nxt_bool_t nxt_http_route_header(nxt_http_request_t *r, + nxt_http_route_rule_t *rule); +static nxt_bool_t nxt_http_route_test_rule(nxt_http_request_t *r, + nxt_http_route_rule_t *rule, u_char *start, size_t length); static nxt_bool_t nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern, u_char *start, size_t length); static nxt_bool_t nxt_http_route_memcmp(u_char *start, u_char *test, @@ -196,6 +242,12 @@ static nxt_conf_map_t nxt_http_route_match_conf[] = { NXT_CONF_MAP_PTR, offsetof(nxt_http_route_match_conf_t, method), }, + + { + nxt_string("headers"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_match_conf_t, headers), + }, }; @@ -241,10 +293,13 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, { size_t size; uint32_t n; + nxt_mp_t *mp; nxt_int_t ret; nxt_str_t pass, *string; nxt_conf_value_t *match_conf, *pass_conf; - nxt_http_route_rule_t *rule, **p; + nxt_http_route_test_t *test; + nxt_http_route_rule_t *rule; + nxt_http_route_table_t *table; nxt_http_route_match_t *match; nxt_http_route_match_conf_t mtcf; @@ -263,7 +318,9 @@ 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_rule_t *); - match = nxt_mp_alloc(tmcf->router_conf->mem_pool, size); + mp = tmcf->router_conf->mem_pool; + + match = nxt_mp_alloc(mp, size); if (nxt_slow_path(match == NULL)) { return NULL; } @@ -272,7 +329,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, match->pass.handler = NULL; match->items = n; - string = nxt_str_dup(tmcf->router_conf->mem_pool, &match->pass.name, &pass); + string = nxt_str_dup(mp, &match->pass.name, &pass); if (nxt_slow_path(string == NULL)) { return NULL; } @@ -290,64 +347,204 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NULL; } - p = &match->rule[0]; + test = &match->test[0]; if (mtcf.host != NULL) { - rule = nxt_http_route_rule_create(task, tmcf, mtcf.host, 1, + rule = nxt_http_route_rule_create(task, mp, mtcf.host, 1, NXT_HTTP_ROUTE_PATTERN_LOWCASE); if (rule == NULL) { return NULL; } - rule->offset = offsetof(nxt_http_request_t, host); + rule->u.offset = offsetof(nxt_http_request_t, host); rule->object = NXT_HTTP_ROUTE_STRING; - *p++ = rule; + test->rule = rule; + test++; } if (mtcf.uri != NULL) { - rule = nxt_http_route_rule_create(task, tmcf, mtcf.uri, 1, + rule = nxt_http_route_rule_create(task, mp, mtcf.uri, 1, NXT_HTTP_ROUTE_PATTERN_NOCASE); if (rule == NULL) { return NULL; } - rule->offset = offsetof(nxt_http_request_t, path); + rule->u.offset = offsetof(nxt_http_request_t, path); rule->object = NXT_HTTP_ROUTE_STRING_PTR; - *p++ = rule; + test->rule = rule; + test++; } if (mtcf.method != NULL) { - rule = nxt_http_route_rule_create(task, tmcf, mtcf.method, 1, + rule = nxt_http_route_rule_create(task, mp, mtcf.method, 1, NXT_HTTP_ROUTE_PATTERN_UPCASE); if (rule == NULL) { return NULL; } - rule->offset = offsetof(nxt_http_request_t, method); + rule->u.offset = offsetof(nxt_http_request_t, method); rule->object = NXT_HTTP_ROUTE_STRING_PTR; - *p++ = rule; + test->rule = rule; + test++; + } + + if (mtcf.headers != NULL) { + table = nxt_http_route_table_create(task, mp, mtcf.headers); + if (table == NULL) { + return NULL; + } + + test->table = table; + test++; } return match; } +static nxt_http_route_table_t * +nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp, + nxt_conf_value_t *table_cv) +{ + size_t size; + uint32_t i, n; + nxt_bool_t array; + nxt_conf_value_t *ruleset_cv; + nxt_http_route_table_t *table; + nxt_http_route_ruleset_t *ruleset; + + array = (nxt_conf_type(table_cv) == NXT_CONF_ARRAY); + n = array ? nxt_conf_array_elements_count(table_cv) : 1; + size = sizeof(nxt_http_route_table_t) + + n * sizeof(nxt_http_route_ruleset_t *); + + table = nxt_mp_alloc(mp, size); + if (nxt_slow_path(table == NULL)) { + return NULL; + } + + table->items = n; + table->object = NXT_HTTP_ROUTE_TABLE; + + if (!array) { + ruleset = nxt_http_route_ruleset_create(task, mp, table_cv); + if (nxt_slow_path(ruleset == NULL)) { + return NULL; + } + + table->ruleset[0] = ruleset; + + return table; + } + + for (i = 0; i < n; i++) { + ruleset_cv = nxt_conf_get_array_element(table_cv, i); + + ruleset = nxt_http_route_ruleset_create(task, mp, ruleset_cv); + if (nxt_slow_path(ruleset == NULL)) { + return NULL; + } + + table->ruleset[i] = ruleset; + } + + return table; +} + + +static nxt_http_route_ruleset_t * +nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp, + nxt_conf_value_t *ruleset_cv) +{ + size_t size; + uint32_t i, n, next; + nxt_str_t name; + nxt_conf_value_t *rule_cv; + nxt_http_route_rule_t *rule; + nxt_http_route_ruleset_t *ruleset; + + n = nxt_conf_object_members_count(ruleset_cv); + size = sizeof(nxt_http_route_ruleset_t) + + n * sizeof(nxt_http_route_rule_t *); + + ruleset = nxt_mp_alloc(mp, size); + if (nxt_slow_path(ruleset == NULL)) { + return NULL; + } + + ruleset->items = n; + + next = 0; + + for (i = 0; i < n; i++) { + rule_cv = nxt_conf_next_object_member(ruleset_cv, &name, &next); + + rule = nxt_http_route_rule_header_create(task, mp, rule_cv, &name); + if (nxt_slow_path(rule == NULL)) { + return NULL; + } + + ruleset->rule[i] = rule; + } + + return ruleset; +} + + static nxt_http_route_rule_t * -nxt_http_route_rule_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, +nxt_http_route_rule_header_create(nxt_task_t *task, nxt_mp_t *mp, + nxt_conf_value_t *rule_cv, nxt_str_t *name) +{ + u_char c, *p; + uint32_t hash; + nxt_uint_t i; + nxt_http_route_rule_t *rule; + + rule = nxt_http_route_rule_create(task, mp, rule_cv, 0, + NXT_HTTP_ROUTE_PATTERN_NOCASE); + if (nxt_slow_path(rule == NULL)) { + return NULL; + } + + rule->u.name.length = name->length; + + p = nxt_mp_nget(mp, name->length); + if (nxt_slow_path(p == NULL)) { + return NULL; + } + + rule->u.name.start = p; + rule->object = NXT_HTTP_ROUTE_HEADER; + + hash = NXT_HTTP_FIELD_HASH_INIT; + + for (i = 0; i < name->length; i++) { + c = name->start[i]; + *p++ = c; + + c = nxt_lowcase(c); + hash = nxt_http_field_hash_char(hash, c); + } + + rule->u.name.hash = nxt_http_field_hash_end(hash) & 0xFFFF; + + return rule; +} + + +static nxt_http_route_rule_t * +nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive, nxt_http_route_pattern_case_t pattern_case) { size_t size; uint32_t i, n; - nxt_mp_t *mp; nxt_int_t ret; nxt_bool_t string; nxt_conf_value_t *value; nxt_http_route_rule_t *rule; nxt_http_route_pattern_t *pattern; - mp = tmcf->router_conf->mem_pool; - string = (nxt_conf_type(cv) != NXT_CONF_ARRAY); n = string ? 1 : nxt_conf_array_elements_count(cv); size = sizeof(nxt_http_route_rule_t) + n * sizeof(nxt_http_route_pattern_t); @@ -543,6 +740,7 @@ nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf) nxt_http_routes_t *routes; routes = tmcf->router_conf->routes; + if (routes != NULL) { route = &routes->route[0]; end = route + routes->items; @@ -744,77 +942,156 @@ nxt_http_route_pass(nxt_task_t *task, nxt_http_request_t *r, static nxt_http_pass_t * nxt_http_route_match(nxt_http_request_t *r, nxt_http_route_match_t *match) { + nxt_bool_t ret; + nxt_http_route_test_t *test, *end; + + test = &match->test[0]; + end = test + match->items; + + while (test < end) { + if (test->rule->object != NXT_HTTP_ROUTE_TABLE) { + ret = nxt_http_route_rule(r, test->rule); + + } else { + ret = nxt_http_route_table(r, test->table); + } + + if (!ret) { + return NULL; + } + + test++; + } + + return &match->pass; +} + + +static nxt_bool_t +nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table) +{ + nxt_bool_t ret; + nxt_http_route_ruleset_t **ruleset, **end; + + ret = 1; + ruleset = &table->ruleset[0]; + end = ruleset + table->items; + + while (ruleset < end) { + ret = nxt_http_route_ruleset(r, *ruleset); + + if (ret) { + return ret; + } + + ruleset++; + } + + return ret; +} + + +static nxt_bool_t +nxt_http_route_ruleset(nxt_http_request_t *r, nxt_http_route_ruleset_t *ruleset) +{ nxt_http_route_rule_t **rule, **end; - rule = &match->rule[0]; - end = rule + match->items; + rule = &ruleset->rule[0]; + end = rule + ruleset->items; while (rule < end) { if (!nxt_http_route_rule(r, *rule)) { - return NULL; + return 0; } rule++; } - return &match->pass; + return 1; } static nxt_bool_t nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule) { - void *p, **pp; - u_char *start; - size_t length; - nxt_str_t *s; - nxt_bool_t ret; - nxt_http_field_t *f; - nxt_http_route_pattern_t *pattern, *end; + void *p, **pp; + u_char *start; + size_t length; + nxt_str_t *s; + + switch (rule->object) { - p = nxt_pointer_to(r, rule->offset); + case NXT_HTTP_ROUTE_HEADER: + return nxt_http_route_header(r, rule); + + case NXT_HTTP_ROUTE_ARGUMENT: + return 0; + + case NXT_HTTP_ROUTE_COOKIE: + return 0; + + default: + break; + } + + p = nxt_pointer_to(r, rule->u.offset); if (rule->object == NXT_HTTP_ROUTE_STRING) { s = p; - length = s->length; - start = s->start; } else { + /* NXT_HTTP_ROUTE_STRING_PTR */ pp = p; - p = *pp; + s = *pp; - if (p == NULL) { + if (s == NULL) { return 0; } + } - switch (rule->object) { + length = s->length; + start = s->start; - case NXT_HTTP_ROUTE_STRING_PTR: - s = p; - length = s->length; - start = s->start; - break; + return nxt_http_route_test_rule(r, rule, start, length); +} - case NXT_HTTP_ROUTE_FIELD: - f = p; - length = f->value_length; - start = f->value; - break; - case NXT_HTTP_ROUTE_HEADER: - return 0; +static nxt_bool_t +nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule) +{ + nxt_bool_t ret; + nxt_http_field_t *f; - case NXT_HTTP_ROUTE_ARGUMENT: - return 0; + ret = 0; - case NXT_HTTP_ROUTE_COOKIE: - return 0; + nxt_list_each(f, r->fields) { - default: - nxt_unreachable(); - return 0; + if (rule->u.name.hash != f->hash + || rule->u.name.length != f->name_length + || nxt_strncasecmp(rule->u.name.start, f->name, f->name_length) + != 0) + { + continue; } - } + + ret = nxt_http_route_test_rule(r, rule, f->value, f->value_length); + + if (!ret) { + return ret; + } + + } nxt_list_loop; + + return ret; +} + + +static nxt_bool_t +nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule, + u_char *start, size_t length) +{ + nxt_bool_t ret; + nxt_http_route_pattern_t *pattern, *end; ret = 1; pattern = &rule->pattern[0]; |