diff options
Diffstat (limited to 'src/nxt_http_route.c')
-rw-r--r-- | src/nxt_http_route.c | 1021 |
1 files changed, 889 insertions, 132 deletions
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 133c39ab..d6749acb 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, @@ -21,6 +21,7 @@ typedef enum { typedef enum { NXT_HTTP_ROUTE_PATTERN_EXACT = 0, NXT_HTTP_ROUTE_PATTERN_BEGIN, + NXT_HTTP_ROUTE_PATTERN_MIDDLE, NXT_HTTP_ROUTE_PATTERN_END, NXT_HTTP_ROUTE_PATTERN_SUBSTRING, } nxt_http_route_pattern_type_t; @@ -37,11 +38,17 @@ typedef struct { nxt_conf_value_t *host; nxt_conf_value_t *uri; nxt_conf_value_t *method; + nxt_conf_value_t *headers; + nxt_conf_value_t *arguments; + nxt_conf_value_t *cookies; } nxt_http_route_match_conf_t; typedef struct { - nxt_str_t test; + u_char *start1; + u_char *start2; + uint32_t length1; + uint32_t length2; uint32_t min_length; nxt_http_route_pattern_type_t type:8; @@ -52,17 +59,66 @@ typedef struct { typedef struct { - uintptr_t offset; - uint32_t items; + uint16_t hash; + uint16_t name_length; + uint32_t value_length; + u_char *name; + u_char *value; +} nxt_http_name_value_t; + + +typedef struct { + uint16_t hash; + uint16_t name_length; + uint32_t value_length; + u_char *name; + u_char *value; +} nxt_http_cookie_t; + + +typedef struct { + /* 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; @@ -79,17 +135,39 @@ struct nxt_http_routes_s { }; +#define NJS_COOKIE_HASH \ + (nxt_http_field_hash_end( \ + nxt_http_field_hash_char( \ + nxt_http_field_hash_char( \ + nxt_http_field_hash_char( \ + nxt_http_field_hash_char( \ + nxt_http_field_hash_char( \ + nxt_http_field_hash_char(NXT_HTTP_FIELD_HASH_INIT, \ + 'c'), 'o'), 'o'), 'k'), 'i'), 'e')) & 0xFFFF) + + 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, nxt_http_route_object_t object, + nxt_bool_t case_sensitive); +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, nxt_http_route_object_t object, + nxt_bool_t case_sensitive); +static nxt_http_route_rule_t *nxt_http_route_rule_name_create(nxt_task_t *task, + nxt_mp_t *mp, nxt_conf_value_t *rule_cv, nxt_str_t *name, + nxt_bool_t case_sensitive); 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, nxt_http_route_pattern_case_t pattern_case); +static u_char *nxt_http_route_pattern_copy(nxt_mp_t *mp, nxt_str_t *test, + nxt_http_route_pattern_case_t pattern_case); static void nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_route_t *route); @@ -103,10 +181,37 @@ 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_rule(nxt_http_request_t *r, +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, + nxt_http_route_ruleset_t *ruleset); +static nxt_int_t nxt_http_route_rule(nxt_http_request_t *r, + nxt_http_route_rule_t *rule); +static nxt_int_t nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule); -static nxt_bool_t nxt_http_route_pattern(nxt_http_request_t *r, +static nxt_int_t nxt_http_route_arguments(nxt_http_request_t *r, + nxt_http_route_rule_t *rule); +static nxt_array_t *nxt_http_route_arguments_parse(nxt_http_request_t *r); +static nxt_http_name_value_t *nxt_http_route_argument(nxt_array_t *array, + u_char *name, size_t name_length, uint32_t hash, u_char *start, + u_char *end); +static nxt_int_t nxt_http_route_test_argument(nxt_http_request_t *r, + nxt_http_route_rule_t *rule, nxt_array_t *array); +static nxt_int_t nxt_http_route_cookies(nxt_http_request_t *r, + nxt_http_route_rule_t *rule); +static nxt_array_t *nxt_http_route_cookies_parse(nxt_http_request_t *r); +static nxt_int_t nxt_http_route_cookie_parse(nxt_array_t *cookies, + u_char *start, u_char *end); +static nxt_http_name_value_t *nxt_http_route_cookie(nxt_array_t *array, + u_char *name, size_t name_length, u_char *start, u_char *end); +static nxt_int_t nxt_http_route_test_cookie(nxt_http_request_t *r, + nxt_http_route_rule_t *rule, nxt_array_t *array); +static nxt_int_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_int_t nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern, u_char *start, size_t length); +static nxt_int_t nxt_http_route_memcmp(u_char *start, u_char *test, + size_t length, nxt_bool_t case_sensitive); nxt_http_routes_t * @@ -188,6 +293,24 @@ 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), + }, + + { + nxt_string("arguments"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_match_conf_t, arguments), + }, + + { + nxt_string("cookies"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_match_conf_t, cookies), + }, }; @@ -233,10 +356,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; @@ -255,7 +381,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; } @@ -264,7 +392,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; } @@ -282,64 +410,232 @@ 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, + NXT_HTTP_ROUTE_HEADER, 0); + if (table == NULL) { + return NULL; + } + + test->table = table; + test++; + } + + if (mtcf.arguments != NULL) { + table = nxt_http_route_table_create(task, mp, mtcf.arguments, + NXT_HTTP_ROUTE_ARGUMENT, 1); + if (table == NULL) { + return NULL; + } + + test->table = table; + test++; + } + + if (mtcf.cookies != NULL) { + table = nxt_http_route_table_create(task, mp, mtcf.cookies, + NXT_HTTP_ROUTE_COOKIE, 0); + 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, nxt_http_route_object_t object, + nxt_bool_t case_sensitive) +{ + 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, + object, case_sensitive); + 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, + object, case_sensitive); + 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, nxt_http_route_object_t object, + nxt_bool_t case_sensitive) +{ + 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_name_create(task, mp, rule_cv, &name, + case_sensitive); + if (nxt_slow_path(rule == NULL)) { + return NULL; + } + + rule->object = object; + 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_name_create(nxt_task_t *task, nxt_mp_t *mp, + nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive) +{ + 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, case_sensitive, + 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; + + 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); @@ -407,8 +703,12 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, { u_char *start; nxt_str_t test; + nxt_uint_t n, length; nxt_http_route_pattern_type_t type; + /* Suppress warning about uninitialized variable. */ + length = 0; + type = NXT_HTTP_ROUTE_PATTERN_EXACT; nxt_conf_get_string(cv, &test); @@ -448,57 +748,98 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, } else if (test.start[test.length - 1] == '*') { test.length--; type = NXT_HTTP_ROUTE_PATTERN_BEGIN; + + } else { + length = test.length - 1; + + for (n = 1; n < length; n++) { + if (test.start[n] == '*') { + test.length = n; + type = NXT_HTTP_ROUTE_PATTERN_MIDDLE; + break; + } + } } } } pattern->type = type; pattern->min_length = test.length; - pattern->test.length = test.length; + pattern->length1 = test.length; - start = nxt_mp_nget(mp, test.length); + start = nxt_http_route_pattern_copy(mp, &test, pattern_case); if (nxt_slow_path(start == NULL)) { return NXT_ERROR; } - pattern->test.start = start; + pattern->start1 = start; + + if (type == NXT_HTTP_ROUTE_PATTERN_MIDDLE) { + length -= test.length; + pattern->length2 = length; + pattern->min_length += length; + + test.start = &test.start[test.length + 1]; + test.length = length; + + start = nxt_http_route_pattern_copy(mp, &test, pattern_case); + if (nxt_slow_path(start == NULL)) { + return NXT_ERROR; + } + + pattern->start2 = start; + } + + return NXT_OK; +} + + +static u_char * +nxt_http_route_pattern_copy(nxt_mp_t *mp, nxt_str_t *test, + nxt_http_route_pattern_case_t pattern_case) +{ + u_char *start; + + start = nxt_mp_nget(mp, test->length); + if (nxt_slow_path(start == NULL)) { + return start; + } switch (pattern_case) { case NXT_HTTP_ROUTE_PATTERN_UPCASE: - nxt_memcpy_upcase(start, test.start, test.length); + nxt_memcpy_upcase(start, test->start, test->length); break; case NXT_HTTP_ROUTE_PATTERN_LOWCASE: - nxt_memcpy_lowcase(start, test.start, test.length); + nxt_memcpy_lowcase(start, test->start, test->length); break; case NXT_HTTP_ROUTE_PATTERN_NOCASE: - nxt_memcpy(start, test.start, test.length); + nxt_memcpy(start, test->start, test->length); break; } - return NXT_OK; + return start; } void nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf) { - nxt_uint_t items; - nxt_http_route_t **route; + nxt_http_route_t **route, **end; nxt_http_routes_t *routes; routes = tmcf->router_conf->routes; + if (routes != NULL) { - items = routes->items; route = &routes->route[0]; + end = route + routes->items; - while (items != 0) { + while (route < end) { nxt_http_route_resolve(task, tmcf, *route); route++; - items--; } } } @@ -508,17 +849,15 @@ static void nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_route_t *route) { - nxt_uint_t items; - nxt_http_route_match_t **match; + nxt_http_route_match_t **match, **end; - items = route->items; match = &route->match[0]; + end = match + route->items; - while (items != 0) { + while (match < end) { nxt_http_pass_resolve(task, tmcf, &(*match)->pass); match++; - items--; } } @@ -561,21 +900,18 @@ nxt_http_pass_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, static nxt_http_route_t * nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name) { - nxt_uint_t items; - nxt_http_route_t **route; + nxt_http_route_t **route, **end; - items = routes->items; route = &routes->route[0]; + end = route + routes->items; - do { + while (route < end) { if (nxt_strstr_eq(&(*route)->name, name)) { return *route; } route++; - items--; - - } while (items != 0); + } return NULL; } @@ -627,20 +963,17 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, void nxt_http_routes_cleanup(nxt_task_t *task, nxt_http_routes_t *routes) { - nxt_uint_t items; - nxt_http_route_t **route; + nxt_http_route_t **route, **end; if (routes != NULL) { - items = routes->items; route = &routes->route[0]; + end = route + routes->items; - do { + while (route < end) { nxt_http_route_cleanup(task, *route); route++; - items--; - - } while (items != 0); + } } } @@ -648,19 +981,16 @@ nxt_http_routes_cleanup(nxt_task_t *task, nxt_http_routes_t *routes) static void nxt_http_route_cleanup(nxt_task_t *task, nxt_http_route_t *route) { - nxt_uint_t items; - nxt_http_route_match_t **match; + nxt_http_route_match_t **match, **end; - items = route->items; match = &route->match[0]; + end = match + route->items; - do { + while (match < end) { nxt_http_pass_cleanup(task, &(*match)->pass); match++; - items--; - - } while (items != 0); + } } @@ -677,23 +1007,21 @@ static nxt_http_pass_t * nxt_http_route_pass(nxt_task_t *task, nxt_http_request_t *r, nxt_http_pass_t *start) { - nxt_uint_t items; nxt_http_pass_t *pass; nxt_http_route_t *route; - nxt_http_route_match_t **match; + nxt_http_route_match_t **match, **end; route = start->u.route; - items = route->items; match = &route->match[0]; + end = match + route->items; - while (items != 0) { + while (match < end) { pass = nxt_http_route_match(r, *match); if (pass != NULL) { return pass; } match++; - items--; } nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND); @@ -705,87 +1033,488 @@ 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_uint_t items; - nxt_http_route_rule_t **rule; + nxt_int_t ret; + nxt_http_route_test_t *test, *end; - rule = &match->rule[0]; - items = match->items; + test = &match->test[0]; + end = test + match->items; - while (items != 0) { - if (!nxt_http_route_rule(r, *rule)) { - return NULL; + 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); } - rule++; - items--; + if (ret <= 0) { + /* 0 => NULL, -1 => NXT_HTTP_PASS_ERROR. */ + return (nxt_http_pass_t *) (intptr_t) ret; + } + + test++; } return &match->pass; } -static nxt_bool_t +static nxt_int_t +nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table) +{ + nxt_int_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 != 0) { + return ret; + } + + ruleset++; + } + + return ret; +} + + +static nxt_int_t +nxt_http_route_ruleset(nxt_http_request_t *r, nxt_http_route_ruleset_t *ruleset) +{ + nxt_int_t ret; + nxt_http_route_rule_t **rule, **end; + + rule = &ruleset->rule[0]; + end = rule + ruleset->items; + + while (rule < end) { + ret = nxt_http_route_rule(r, *rule); + + if (ret <= 0) { + return ret; + } + + rule++; + } + + return 1; +} + + +static nxt_int_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_uint_t items; - nxt_bool_t ret; - nxt_http_field_t *f; - nxt_http_route_pattern_t *pattern; + void *p, **pp; + u_char *start; + size_t length; + nxt_str_t *s; + + switch (rule->object) { + + case NXT_HTTP_ROUTE_HEADER: + return nxt_http_route_header(r, rule); - p = nxt_pointer_to(r, rule->offset); + case NXT_HTTP_ROUTE_ARGUMENT: + return nxt_http_route_arguments(r, rule); + + case NXT_HTTP_ROUTE_COOKIE: + return nxt_http_route_cookies(r, rule); + + 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_int_t +nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule) +{ + nxt_int_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 == 0) { + return ret; + } + + } nxt_list_loop; + + return ret; +} + + +static nxt_int_t +nxt_http_route_arguments(nxt_http_request_t *r, nxt_http_route_rule_t *rule) +{ + nxt_array_t *arguments; + + if (r->args == NULL) { + return 0; + } + + arguments = nxt_http_route_arguments_parse(r); + if (nxt_slow_path(arguments == NULL)) { + return -1; + } + + return nxt_http_route_test_argument(r, rule, arguments); +} + + +static nxt_array_t * +nxt_http_route_arguments_parse(nxt_http_request_t *r) +{ + size_t name_length; + u_char c, *p, *start, *end, *name; + uint32_t hash; + nxt_bool_t valid; + nxt_array_t *args; + nxt_http_name_value_t *nv; + + if (r->arguments != NULL) { + return r->arguments; + } + + args = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t)); + if (nxt_slow_path(args == NULL)) { + return NULL; + } + + hash = NXT_HTTP_FIELD_HASH_INIT; + valid = 1; + name = NULL; + name_length = 0; + + start = r->args->start; + end = start + r->args->length; + + for (p = start; p < end; p++) { + c = *p; + + if (c == '=') { + name_length = p - start; + name = start; + start = p + 1; + valid = (name_length != 0); + + } else if (c == '&') { + if (valid) { + nv = nxt_http_route_argument(args, name, name_length, hash, + start, p); + if (nxt_slow_path(nv == NULL)) { + return NULL; + } + } + + hash = NXT_HTTP_FIELD_HASH_INIT; + valid = 1; + name = NULL; + start = p + 1; + + } else if (name == NULL) { + hash = nxt_http_field_hash_char(hash, c); + } + } + + if (valid) { + nv = nxt_http_route_argument(args, name, name_length, hash, start, p); + if (nxt_slow_path(nv == NULL)) { + return NULL; + } + } + + r->arguments = args; + + return args; +} + + +static nxt_http_name_value_t * +nxt_http_route_argument(nxt_array_t *array, u_char *name, size_t name_length, + uint32_t hash, u_char *start, u_char *end) +{ + size_t length; + nxt_http_name_value_t *nv; + + nv = nxt_array_add(array); + if (nxt_slow_path(nv == NULL)) { + return NULL; + } + + nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF; + + length = end - start; + + if (name == NULL) { + name_length = length; + name = start; + length = 0; + } + + nv->name_length = name_length; + nv->value_length = length; + nv->name = name; + nv->value = start; + + return nv; +} + + +static nxt_int_t +nxt_http_route_test_argument(nxt_http_request_t *r, + nxt_http_route_rule_t *rule, nxt_array_t *array) +{ + nxt_bool_t ret; + nxt_http_name_value_t *nv, *end; + + ret = 0; + + nv = array->elts; + end = nv + array->nelts; + + while (nv < end) { + + if (rule->u.name.hash == nv->hash + && rule->u.name.length == nv->name_length + && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0) + { + ret = nxt_http_route_test_rule(r, rule, nv->value, + nv->value_length); + if (ret == 0) { + break; + } + } + + nv++; + } + + return ret; +} + + +static nxt_int_t +nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule) +{ + nxt_array_t *cookies; + + cookies = nxt_http_route_cookies_parse(r); + if (nxt_slow_path(cookies == NULL)) { + return -1; + } + + return nxt_http_route_test_cookie(r, rule, cookies); +} + + +static nxt_array_t * +nxt_http_route_cookies_parse(nxt_http_request_t *r) +{ + nxt_int_t ret; + nxt_array_t *cookies; + nxt_http_field_t *f; + + if (r->cookies != NULL) { + return r->cookies; + } + + cookies = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t)); + if (nxt_slow_path(cookies == NULL)) { + return NULL; + } + + nxt_list_each(f, r->fields) { + + if (f->hash != NJS_COOKIE_HASH + || f->name_length != 6 + || nxt_strncasecmp(f->name, (u_char *) "Cookie", 6) != 0) + { + continue; + } + + ret = nxt_http_route_cookie_parse(cookies, f->value, + f->value + f->value_length); + if (ret != NXT_OK) { + return NULL; + } + + } nxt_list_loop; + + r->cookies = cookies; + + return cookies; +} + + +static nxt_int_t +nxt_http_route_cookie_parse(nxt_array_t *cookies, u_char *start, u_char *end) +{ + size_t name_length; + u_char c, *p, *name; + nxt_http_name_value_t *nv; + + name = NULL; + name_length = 0; + + for (p = start; p < end; p++) { + c = *p; + + if (c == '=') { + while (start[0] == ' ') { start++; } + + name_length = p - start; + + if (name_length != 0) { + name = start; + } + + start = p + 1; + + } else if (c == ';') { + if (name != NULL) { + nv = nxt_http_route_cookie(cookies, name, name_length, + start, p); + if (nxt_slow_path(nv == NULL)) { + return NXT_ERROR; + } + } + + name = NULL; + start = p + 1; + } + } + + if (name != NULL) { + nv = nxt_http_route_cookie(cookies, name, name_length, start, p); + if (nxt_slow_path(nv == NULL)) { + return NXT_ERROR; + } + } + + return NXT_OK; +} + + +static nxt_http_name_value_t * +nxt_http_route_cookie(nxt_array_t *array, u_char *name, size_t name_length, + u_char *start, u_char *end) +{ + u_char c, *p; + uint32_t hash; + nxt_http_name_value_t *nv; + + nv = nxt_array_add(array); + if (nxt_slow_path(nv == NULL)) { + return NULL; + } + + nv->name_length = name_length; + nv->name = name; + + hash = NXT_HTTP_FIELD_HASH_INIT; + + for (p = name; p < name + name_length; p++) { + c = *p; + c = nxt_lowcase(c); + hash = nxt_http_field_hash_char(hash, c); + } + + nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF; + + while (start < end && end[-1] == ' ') { end--; } + + nv->value_length = end - start; + nv->value = start; + + return nv; +} + + +static nxt_int_t +nxt_http_route_test_cookie(nxt_http_request_t *r, + nxt_http_route_rule_t *rule, nxt_array_t *array) +{ + nxt_bool_t ret; + nxt_http_name_value_t *nv, *end; + + ret = 0; + + nv = array->elts; + end = nv + array->nelts; + + while (nv < end) { + + if (rule->u.name.hash == nv->hash + && rule->u.name.length == nv->name_length + && nxt_strncasecmp(rule->u.name.start, nv->name, nv->name_length) + == 0) + { + ret = nxt_http_route_test_rule(r, rule, nv->value, + nv->value_length); + if (ret == 0) { + break; + } } + + nv++; } - items = rule->items; + return ret; +} + + +static nxt_int_t +nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule, + u_char *start, size_t length) +{ + nxt_int_t ret; + nxt_http_route_pattern_t *pattern, *end; + + ret = 1; pattern = &rule->pattern[0]; + end = pattern + rule->items; - do { + while (pattern < end) { ret = nxt_http_route_pattern(r, pattern, start, length); + /* nxt_http_route_pattern() returns either 1 or 0. */ ret ^= pattern->negative; if (pattern->any == ret) { @@ -793,30 +1522,31 @@ nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule) } pattern++; - items--; - - } while (items != 0); + } return ret; } -static nxt_bool_t +static nxt_int_t nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern, u_char *start, size_t length) { - nxt_str_t *test; + u_char *p, *end, *test; + size_t test_length; + nxt_int_t ret; if (length < pattern->min_length) { return 0; } - test = &pattern->test; + test = pattern->start1; + test_length = pattern->length1; switch (pattern->type) { case NXT_HTTP_ROUTE_PATTERN_EXACT: - if (length != test->length) { + if (length != test_length) { return 0; } @@ -825,25 +1555,52 @@ nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern, case NXT_HTTP_ROUTE_PATTERN_BEGIN: break; + case NXT_HTTP_ROUTE_PATTERN_MIDDLE: + ret = nxt_http_route_memcmp(start, test, test_length, + pattern->case_sensitive); + if (!ret) { + return ret; + } + + test = pattern->start2; + test_length = pattern->length2; + + /* Fall through. */ + case NXT_HTTP_ROUTE_PATTERN_END: - start += length - test->length; + start += length - test_length; break; case NXT_HTTP_ROUTE_PATTERN_SUBSTRING: + end = start + length; + if (pattern->case_sensitive) { - return (nxt_memstrn(start, start + length, - (char *) test->start, test->length) - != NULL); + p = nxt_memstrn(start, end, (char *) test, test_length); + + } else { + p = nxt_memcasestrn(start, end, (char *) test, test_length); } - return (nxt_memcasestrn(start, start + length, - (char *) test->start, test->length) - != NULL); + return (p != NULL); } - if (pattern->case_sensitive) { - return (nxt_memcmp(start, test->start, test->length) == 0); + return nxt_http_route_memcmp(start, test, test_length, + pattern->case_sensitive); +} + + +static nxt_int_t +nxt_http_route_memcmp(u_char *start, u_char *test, size_t test_length, + nxt_bool_t case_sensitive) +{ + nxt_int_t n; + + if (case_sensitive) { + n = nxt_memcmp(start, test, test_length); + + } else { + n = nxt_memcasecmp(start, test, test_length); } - return (nxt_memcasecmp(start, test->start, test->length) == 0); + return (n == 0); } |