summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_http_route.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nxt_http_route.c')
-rw-r--r--src/nxt_http_route.c1021
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);
}