diff options
author | Igor Sysoev <igor@sysoev.ru> | 2019-02-27 16:41:11 +0300 |
---|---|---|
committer | Igor Sysoev <igor@sysoev.ru> | 2019-02-27 16:41:11 +0300 |
commit | d4ccaae900f78b13923a9bd9ee7bbaa33c99b18b (patch) | |
tree | 6755a201d8b49f6945a213d76bf40d2b6ea7666d | |
parent | 95c9bba33bcb1d0e8e9e7c64d2591187ce11bab1 (diff) | |
download | unit-d4ccaae900f78b13923a9bd9ee7bbaa33c99b18b.tar.gz unit-d4ccaae900f78b13923a9bd9ee7bbaa33c99b18b.tar.bz2 |
Initial routing implementation.
-rw-r--r-- | auto/sources | 1 | ||||
-rw-r--r-- | src/nxt_http.h | 32 | ||||
-rw-r--r-- | src/nxt_http_request.c | 53 | ||||
-rw-r--r-- | src/nxt_http_route.c | 849 | ||||
-rw-r--r-- | src/nxt_router.c | 74 | ||||
-rw-r--r-- | src/nxt_router.h | 10 |
6 files changed, 979 insertions, 40 deletions
diff --git a/auto/sources b/auto/sources index bc979fe2..4c4fd742 100644 --- a/auto/sources +++ b/auto/sources @@ -82,6 +82,7 @@ NXT_LIB_SRCS=" \ src/nxt_http_request.c \ src/nxt_http_response.c \ src/nxt_http_error.c \ + src/nxt_http_route.c \ src/nxt_application.c \ src/nxt_external.c \ src/nxt_port_hash.c \ diff --git a/src/nxt_http.h b/src/nxt_http.h index af0b2cd6..23c406d3 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -21,6 +21,7 @@ typedef enum { NXT_HTTP_NOT_MODIFIED = 304, NXT_HTTP_BAD_REQUEST = 400, + NXT_HTTP_NOT_FOUND = 404, NXT_HTTP_REQUEST_TIMEOUT = 408, NXT_HTTP_LENGTH_REQUIRED = 411, NXT_HTTP_PAYLOAD_TOO_LARGE = 413, @@ -112,6 +113,7 @@ struct nxt_http_request_s { nxt_buf_t *out; const nxt_http_request_state_t *state; + nxt_str_t host; nxt_str_t target; nxt_str_t version; nxt_str_t *method; @@ -124,7 +126,6 @@ struct nxt_http_request_s { nxt_http_field_t *cookie; nxt_http_field_t *referer; nxt_http_field_t *user_agent; - nxt_str_t host; nxt_off_t content_length_n; nxt_sockaddr_t *remote; @@ -136,6 +137,7 @@ struct nxt_http_request_s { nxt_http_status_t status:16; + uint8_t pass_count; /* 8 bits */ uint8_t protocol; /* 2 bits */ uint8_t logged; /* 1 bit */ uint8_t header_sent; /* 1 bit */ @@ -143,6 +145,22 @@ struct nxt_http_request_s { }; +typedef struct nxt_http_route_s nxt_http_route_t; + + +struct nxt_http_pass_s { + nxt_http_pass_t *(*handler)(nxt_task_t *task, + nxt_http_request_t *r, + nxt_http_pass_t *pass); + union { + nxt_http_route_t *route; + nxt_app_t *application; + } u; + + nxt_str_t name; +}; + + typedef void (*nxt_http_proto_body_read_t)(nxt_task_t *task, nxt_http_request_t *r); typedef void (*nxt_http_proto_local_addr_t)(nxt_task_t *task, @@ -184,6 +202,18 @@ nxt_int_t nxt_http_request_field(void *ctx, nxt_http_field_t *field, nxt_int_t nxt_http_request_content_length(void *ctx, nxt_http_field_t *field, uintptr_t data); +nxt_http_routes_t *nxt_http_routes_create(nxt_task_t *task, + nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *routes_conf); +nxt_http_pass_t *nxt_http_pass_create(nxt_task_t *task, + nxt_router_temp_conf_t *tmcf, nxt_str_t *name); +void nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf); +nxt_http_pass_t *nxt_http_pass_application(nxt_task_t *task, + nxt_router_temp_conf_t *tmcf, nxt_str_t *name); +void nxt_http_routes_cleanup(nxt_task_t *task, nxt_http_routes_t *routes); +void nxt_http_pass_cleanup(nxt_task_t *task, nxt_http_pass_t *pass); + +nxt_http_pass_t *nxt_http_request_application(nxt_task_t *task, + nxt_http_request_t *r, nxt_http_pass_t *pass); extern nxt_time_string_t nxt_http_date_cache; diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index b7d46c72..f6c14df9 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -10,7 +10,7 @@ static nxt_int_t nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp); static void nxt_http_request_start(nxt_task_t *task, void *obj, void *data); -static void nxt_http_app_request(nxt_task_t *task, void *obj, void *data); +static void nxt_http_request_pass(nxt_task_t *task, void *obj, void *data); static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj, void *data); static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data); @@ -278,25 +278,60 @@ nxt_http_request_start(nxt_task_t *task, void *obj, void *data) static const nxt_http_request_state_t nxt_http_request_body_state nxt_aligned(64) = { - .ready_handler = nxt_http_app_request, + .ready_handler = nxt_http_request_pass, .error_handler = nxt_http_request_close_handler, }; static void -nxt_http_app_request(nxt_task_t *task, void *obj, void *data) +nxt_http_request_pass(nxt_task_t *task, void *obj, void *data) +{ + nxt_http_pass_t *pass; + nxt_http_request_t *r; + + r = obj; + + pass = r->conf->socket_conf->pass; + + if (nxt_slow_path(pass == NULL)) { + goto fail; + } + + for ( ;; ) { + nxt_debug(task, "http request route: %V", &pass->name); + + pass = pass->handler(task, r, pass); + if (pass == NULL) { + break; + } + + if (nxt_slow_path(r->pass_count++ == 255)) { + goto fail; + } + } + + return; + +fail: + + nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); +} + + +nxt_http_pass_t * +nxt_http_request_application(nxt_task_t *task, nxt_http_request_t *r, + nxt_http_pass_t *pass) { nxt_int_t ret; nxt_event_engine_t *engine; - nxt_http_request_t *r; nxt_app_parse_ctx_t *ar; - r = obj; + nxt_debug(task, "http request application"); ar = nxt_mp_zget(r->mem_pool, sizeof(nxt_app_parse_ctx_t)); if (nxt_slow_path(ar == NULL)) { nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); - return; + return NULL; } ar->request = r; @@ -370,10 +405,12 @@ nxt_http_app_request(nxt_task_t *task, void *obj, void *data) ret = nxt_http_parse_request_init(&ar->resp_parser, r->mem_pool); if (nxt_slow_path(ret != NXT_OK)) { nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); - return; + return NULL; } - nxt_router_process_http_request(task, ar); + nxt_router_process_http_request(task, ar, pass->u.application); + + return NULL; } diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c new file mode 100644 index 00000000..133c39ab --- /dev/null +++ b/src/nxt_http_route.c @@ -0,0 +1,849 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_router.h> +#include <nxt_http.h> + + +typedef enum { + NXT_HTTP_ROUTE_STRING = 0, + NXT_HTTP_ROUTE_STRING_PTR, + NXT_HTTP_ROUTE_FIELD, + NXT_HTTP_ROUTE_HEADER, + NXT_HTTP_ROUTE_ARGUMENT, + NXT_HTTP_ROUTE_COOKIE, +} nxt_http_route_object_t; + + +typedef enum { + NXT_HTTP_ROUTE_PATTERN_EXACT = 0, + NXT_HTTP_ROUTE_PATTERN_BEGIN, + NXT_HTTP_ROUTE_PATTERN_END, + NXT_HTTP_ROUTE_PATTERN_SUBSTRING, +} nxt_http_route_pattern_type_t; + + +typedef enum { + NXT_HTTP_ROUTE_PATTERN_NOCASE = 0, + NXT_HTTP_ROUTE_PATTERN_LOWCASE, + NXT_HTTP_ROUTE_PATTERN_UPCASE, +} nxt_http_route_pattern_case_t; + + +typedef struct { + nxt_conf_value_t *host; + nxt_conf_value_t *uri; + nxt_conf_value_t *method; +} nxt_http_route_match_conf_t; + + +typedef struct { + nxt_str_t test; + uint32_t min_length; + + nxt_http_route_pattern_type_t type:8; + uint8_t case_sensitive; /* 1 bit */ + uint8_t negative; /* 1 bit */ + uint8_t any; /* 1 bit */ +} nxt_http_route_pattern_t; + + +typedef struct { + uintptr_t offset; + uint32_t items; + nxt_http_route_object_t object:8; + 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_match_t; + + +struct nxt_http_route_s { + nxt_str_t name; + uint32_t items; + nxt_http_route_match_t *match[0]; +}; + + +struct nxt_http_routes_s { + uint32_t items; + nxt_http_route_t *route[0]; +}; + + +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_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); +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 void nxt_http_route_resolve(nxt_task_t *task, + nxt_router_temp_conf_t *tmcf, nxt_http_route_t *route); +static void nxt_http_pass_resolve(nxt_task_t *task, + nxt_router_temp_conf_t *tmcf, nxt_http_pass_t *pass); +static nxt_http_route_t *nxt_http_route_find(nxt_http_routes_t *routes, + nxt_str_t *name); +static void nxt_http_route_cleanup(nxt_task_t *task, nxt_http_route_t *routes); + +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, + nxt_http_route_rule_t *rule); +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); + + +nxt_http_routes_t * +nxt_http_routes_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_conf_value_t *routes_conf) +{ + size_t size; + uint32_t i, n, next; + nxt_mp_t *mp; + nxt_str_t name, *string; + nxt_bool_t object; + nxt_conf_value_t *route_conf; + nxt_http_route_t *route; + nxt_http_routes_t *routes; + + object = (nxt_conf_type(routes_conf) == NXT_CONF_OBJECT); + n = object ? nxt_conf_object_members_count(routes_conf) : 1; + size = sizeof(nxt_http_routes_t) + n * sizeof(nxt_http_route_t *); + + mp = tmcf->router_conf->mem_pool; + + routes = nxt_mp_alloc(mp, size); + if (nxt_slow_path(routes == NULL)) { + return NULL; + } + + routes->items = n; + + if (object) { + next = 0; + + for (i = 0; i < n; i++) { + route_conf = nxt_conf_next_object_member(routes_conf, &name, &next); + + route = nxt_http_route_create(task, tmcf, route_conf); + if (nxt_slow_path(route == NULL)) { + return NULL; + } + + routes->route[i] = route; + + string = nxt_str_dup(mp, &route->name, &name); + if (nxt_slow_path(string == NULL)) { + return NULL; + } + } + + } else { + route = nxt_http_route_create(task, tmcf, routes_conf); + if (nxt_slow_path(route == NULL)) { + return NULL; + } + + routes->route[0] = route; + + route->name.length = 0; + route->name.start = NULL; + } + + return routes; +} + + +static nxt_conf_map_t nxt_http_route_match_conf[] = { + { + nxt_string("host"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_match_conf_t, host), + }, + + { + nxt_string("uri"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_match_conf_t, uri), + }, + + { + nxt_string("method"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_route_match_conf_t, method), + }, +}; + + +static nxt_http_route_t * +nxt_http_route_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_conf_value_t *cv) +{ + size_t size; + uint32_t i, n; + nxt_conf_value_t *value; + nxt_http_route_t *route; + nxt_http_route_match_t *match, **m; + + n = nxt_conf_array_elements_count(cv); + size = sizeof(nxt_http_route_t) + n * sizeof(nxt_http_route_match_t *); + + route = nxt_mp_alloc(tmcf->router_conf->mem_pool, size); + if (nxt_slow_path(route == NULL)) { + return NULL; + } + + route->items = n; + m = &route->match[0]; + + for (i = 0; i < n; i++) { + value = nxt_conf_get_array_element(cv, i); + + match = nxt_http_route_match_create(task, tmcf, value); + if (match == NULL) { + return NULL; + } + + *m++ = match; + } + + return route; +} + + +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) +{ + size_t size; + uint32_t n; + 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_match_t *match; + nxt_http_route_match_conf_t mtcf; + + static nxt_str_t pass_path = nxt_string("/action/pass"); + static nxt_str_t match_path = nxt_string("/match"); + + pass_conf = nxt_conf_get_path(cv, &pass_path); + if (nxt_slow_path(pass_conf == NULL)) { + return NULL; + } + + nxt_conf_get_string(pass_conf, &pass); + + match_conf = nxt_conf_get_path(cv, &match_path); + + 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); + if (nxt_slow_path(match == NULL)) { + return NULL; + } + + match->pass.u.route = NULL; + match->pass.handler = NULL; + match->items = n; + + string = nxt_str_dup(tmcf->router_conf->mem_pool, &match->pass.name, &pass); + if (nxt_slow_path(string == NULL)) { + return NULL; + } + + if (n == 0) { + return match; + } + + nxt_memzero(&mtcf, sizeof(mtcf)); + + ret = nxt_conf_map_object(tmcf->mem_pool, + match_conf, nxt_http_route_match_conf, + nxt_nitems(nxt_http_route_match_conf), &mtcf); + if (ret != NXT_OK) { + return NULL; + } + + p = &match->rule[0]; + + if (mtcf.host != NULL) { + rule = nxt_http_route_rule_create(task, tmcf, mtcf.host, 1, + NXT_HTTP_ROUTE_PATTERN_LOWCASE); + if (rule == NULL) { + return NULL; + } + + rule->offset = offsetof(nxt_http_request_t, host); + rule->object = NXT_HTTP_ROUTE_STRING; + *p++ = rule; + } + + if (mtcf.uri != NULL) { + rule = nxt_http_route_rule_create(task, tmcf, mtcf.uri, 1, + NXT_HTTP_ROUTE_PATTERN_NOCASE); + if (rule == NULL) { + return NULL; + } + + rule->offset = offsetof(nxt_http_request_t, path); + rule->object = NXT_HTTP_ROUTE_STRING_PTR; + *p++ = rule; + } + + if (mtcf.method != NULL) { + rule = nxt_http_route_rule_create(task, tmcf, mtcf.method, 1, + NXT_HTTP_ROUTE_PATTERN_UPCASE); + if (rule == NULL) { + return NULL; + } + + rule->offset = offsetof(nxt_http_request_t, method); + rule->object = NXT_HTTP_ROUTE_STRING_PTR; + *p++ = rule; + } + + return match; +} + + +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) +{ + 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); + + rule = nxt_mp_alloc(mp, size); + if (nxt_slow_path(rule == NULL)) { + return NULL; + } + + rule->items = n; + + pattern = &rule->pattern[0]; + + if (string) { + pattern[0].case_sensitive = case_sensitive; + ret = nxt_http_route_pattern_create(task, mp, cv, &pattern[0], + pattern_case); + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } + + return rule; + } + + nxt_conf_array_qsort(cv, nxt_http_pattern_compare); + + for (i = 0; i < n; i++) { + pattern[i].case_sensitive = case_sensitive; + value = nxt_conf_get_array_element(cv, i); + + ret = nxt_http_route_pattern_create(task, mp, value, &pattern[i], + pattern_case); + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } + } + + return rule; +} + + +static int +nxt_http_pattern_compare(const void *one, const void *two) +{ + nxt_str_t test; + nxt_bool_t negative1, negative2; + nxt_conf_value_t *value; + + value = (nxt_conf_value_t *) one; + nxt_conf_get_string(value, &test); + negative1 = (test.length != 0 && test.start[0] == '!'); + + value = (nxt_conf_value_t *) two; + nxt_conf_get_string(value, &test); + negative2 = (test.length != 0 && test.start[0] == '!'); + + return (negative2 - negative1); +} + + +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) +{ + u_char *start; + nxt_str_t test; + nxt_http_route_pattern_type_t type; + + type = NXT_HTTP_ROUTE_PATTERN_EXACT; + + nxt_conf_get_string(cv, &test); + + pattern->negative = 0; + pattern->any = 1; + + if (test.length != 0) { + + if (test.start[0] == '!') { + test.start++; + test.length--; + + pattern->negative = 1; + pattern->any = 0; + } + + if (test.length != 0) { + + if (test.start[0] == '*') { + test.start++; + test.length--; + + if (test.length != 0) { + if (test.start[test.length - 1] == '*') { + test.length--; + type = NXT_HTTP_ROUTE_PATTERN_SUBSTRING; + + } else { + type = NXT_HTTP_ROUTE_PATTERN_END; + } + + } else { + type = NXT_HTTP_ROUTE_PATTERN_BEGIN; + } + + } else if (test.start[test.length - 1] == '*') { + test.length--; + type = NXT_HTTP_ROUTE_PATTERN_BEGIN; + } + } + } + + pattern->type = type; + pattern->min_length = test.length; + pattern->test.length = test.length; + + start = nxt_mp_nget(mp, test.length); + if (nxt_slow_path(start == NULL)) { + return NXT_ERROR; + } + + pattern->test.start = start; + + switch (pattern_case) { + + case NXT_HTTP_ROUTE_PATTERN_UPCASE: + nxt_memcpy_upcase(start, test.start, test.length); + break; + + case NXT_HTTP_ROUTE_PATTERN_LOWCASE: + nxt_memcpy_lowcase(start, test.start, test.length); + break; + + case NXT_HTTP_ROUTE_PATTERN_NOCASE: + nxt_memcpy(start, test.start, test.length); + break; + } + + return NXT_OK; +} + + +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_routes_t *routes; + + routes = tmcf->router_conf->routes; + if (routes != NULL) { + items = routes->items; + route = &routes->route[0]; + + while (items != 0) { + nxt_http_route_resolve(task, tmcf, *route); + + route++; + items--; + } + } +} + + +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; + + items = route->items; + match = &route->match[0]; + + while (items != 0) { + nxt_http_pass_resolve(task, tmcf, &(*match)->pass); + + match++; + items--; + } +} + + +static void +nxt_http_pass_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_http_pass_t *pass) +{ + nxt_str_t name; + + name = pass->name; + + if (nxt_str_start(&name, "applications/", 13)) { + name.length -= 13; + name.start += 13; + + pass->u.application = nxt_router_listener_application(tmcf, &name); + nxt_router_app_use(task, pass->u.application, 1); + + pass->handler = nxt_http_request_application; + + } else if (nxt_str_start(&name, "routes", 6)) { + + if (name.length == 6) { + name.length = 0; + name.start = NULL; + + } else if (name.start[6] == '/') { + name.length -= 7; + name.start += 7; + } + + pass->u.route = nxt_http_route_find(tmcf->router_conf->routes, &name); + + pass->handler = nxt_http_route_pass; + } +} + + +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; + + items = routes->items; + route = &routes->route[0]; + + do { + if (nxt_strstr_eq(&(*route)->name, name)) { + return *route; + } + + route++; + items--; + + } while (items != 0); + + return NULL; +} + + +nxt_http_pass_t * +nxt_http_pass_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_str_t *name) +{ + nxt_http_pass_t *pass; + + pass = nxt_mp_alloc(tmcf->router_conf->mem_pool, sizeof(nxt_http_pass_t)); + if (nxt_slow_path(pass == NULL)) { + return NULL; + } + + pass->name = *name; + + nxt_http_pass_resolve(task, tmcf, pass); + + return pass; +} + + +/* COMPATIBILITY: listener application. */ + +nxt_http_pass_t * +nxt_http_pass_application(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_str_t *name) +{ + nxt_http_pass_t *pass; + + pass = nxt_mp_alloc(tmcf->router_conf->mem_pool, sizeof(nxt_http_pass_t)); + if (nxt_slow_path(pass == NULL)) { + return NULL; + } + + pass->name = *name; + + pass->u.application = nxt_router_listener_application(tmcf, name); + nxt_router_app_use(task, pass->u.application, 1); + + pass->handler = nxt_http_request_application; + + return pass; +} + + +void +nxt_http_routes_cleanup(nxt_task_t *task, nxt_http_routes_t *routes) +{ + nxt_uint_t items; + nxt_http_route_t **route; + + if (routes != NULL) { + items = routes->items; + route = &routes->route[0]; + + do { + nxt_http_route_cleanup(task, *route); + + route++; + items--; + + } while (items != 0); + } +} + + +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; + + items = route->items; + match = &route->match[0]; + + do { + nxt_http_pass_cleanup(task, &(*match)->pass); + + match++; + items--; + + } while (items != 0); +} + + +void +nxt_http_pass_cleanup(nxt_task_t *task, nxt_http_pass_t *pass) +{ + if (pass->handler == nxt_http_request_application) { + nxt_router_app_use(task, pass->u.application, -1); + } +} + + +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; + + route = start->u.route; + items = route->items; + match = &route->match[0]; + + while (items != 0) { + pass = nxt_http_route_match(r, *match); + if (pass != NULL) { + return pass; + } + + match++; + items--; + } + + nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND); + + return NULL; +} + + +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; + + rule = &match->rule[0]; + items = match->items; + + while (items != 0) { + if (!nxt_http_route_rule(r, *rule)) { + return NULL; + } + + rule++; + items--; + } + + return &match->pass; +} + + +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_uint_t items; + nxt_bool_t ret; + nxt_http_field_t *f; + nxt_http_route_pattern_t *pattern; + + p = nxt_pointer_to(r, rule->offset); + + if (rule->object == NXT_HTTP_ROUTE_STRING) { + s = p; + length = s->length; + start = s->start; + + } else { + pp = p; + p = *pp; + + if (p == NULL) { + return 0; + } + + switch (rule->object) { + + case NXT_HTTP_ROUTE_STRING_PTR: + s = p; + length = s->length; + start = s->start; + break; + + case NXT_HTTP_ROUTE_FIELD: + f = p; + length = f->value_length; + start = f->value; + break; + + case NXT_HTTP_ROUTE_HEADER: + return 0; + + case NXT_HTTP_ROUTE_ARGUMENT: + return 0; + + case NXT_HTTP_ROUTE_COOKIE: + return 0; + + default: + nxt_unreachable(); + return 0; + } + } + + items = rule->items; + pattern = &rule->pattern[0]; + + do { + ret = nxt_http_route_pattern(r, pattern, start, length); + + ret ^= pattern->negative; + + if (pattern->any == ret) { + return ret; + } + + pattern++; + items--; + + } while (items != 0); + + return ret; +} + + +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) +{ + nxt_str_t *test; + + if (length < pattern->min_length) { + return 0; + } + + test = &pattern->test; + + switch (pattern->type) { + + case NXT_HTTP_ROUTE_PATTERN_EXACT: + if (length != test->length) { + return 0; + } + + break; + + case NXT_HTTP_ROUTE_PATTERN_BEGIN: + break; + + case NXT_HTTP_ROUTE_PATTERN_END: + start += length - test->length; + break; + + case NXT_HTTP_ROUTE_PATTERN_SUBSTRING: + if (pattern->case_sensitive) { + return (nxt_memstrn(start, start + length, + (char *) test->start, test->length) + != NULL); + } + + return (nxt_memcasestrn(start, start + length, + (char *) test->start, test->length) + != NULL); + } + + if (pattern->case_sensitive) { + return (nxt_memcmp(start, test->start, test->length) == 0); + } + + return (nxt_memcasecmp(start, test->start, test->length) == 0); +} diff --git a/src/nxt_router.c b/src/nxt_router.c index 5621abec..e81a2f96 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -31,7 +31,8 @@ typedef struct { typedef struct { - nxt_str_t application; + nxt_str_t pass; + nxt_str_t application; } nxt_router_listener_conf_t; @@ -163,8 +164,6 @@ static void nxt_router_conf_send(nxt_task_t *task, static nxt_int_t nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, u_char *start, u_char *end); static nxt_app_t *nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name); -static nxt_app_t *nxt_router_listener_application(nxt_router_temp_conf_t *tmcf, - nxt_str_t *name); static void nxt_router_listen_socket_rpc_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_socket_conf_t *skcf); static void nxt_router_listen_socket_ready(nxt_task_t *task, @@ -1174,11 +1173,14 @@ nxt_router_conf_error(nxt_task_t *task, nxt_router_temp_conf_t *tmcf) nxt_queue_add(&new_socket_confs, &tmcf->pending); nxt_queue_add(&new_socket_confs, &tmcf->creating); + rtcf = tmcf->router_conf; + + nxt_http_routes_cleanup(task, rtcf->routes); + nxt_queue_each(skcf, &new_socket_confs, nxt_socket_conf_t, link) { - if (skcf->application != NULL) { - nxt_router_app_use(task, skcf->application, -1); - skcf->application = NULL; + if (skcf->pass != NULL) { + nxt_http_pass_cleanup(task, skcf->pass); } } nxt_queue_loop; @@ -1189,7 +1191,6 @@ nxt_router_conf_error(nxt_task_t *task, nxt_router_temp_conf_t *tmcf) } nxt_queue_loop; - rtcf = tmcf->router_conf; router = rtcf->router; nxt_queue_add(&router->sockets, &tmcf->keeping); @@ -1299,8 +1300,14 @@ static nxt_conf_map_t nxt_router_app_processes_conf[] = { static nxt_conf_map_t nxt_router_listener_conf[] = { { + nxt_string("pass"), + NXT_CONF_MAP_STR_COPY, + offsetof(nxt_router_listener_conf_t, pass), + }, + + { nxt_string("application"), - NXT_CONF_MAP_STR, + NXT_CONF_MAP_STR_COPY, offsetof(nxt_router_listener_conf_t, application), }, }; @@ -1379,7 +1386,9 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *conf, *http, *value; nxt_conf_value_t *applications, *application; nxt_conf_value_t *listeners, *listener; + nxt_conf_value_t *routes_conf; nxt_socket_conf_t *skcf; + nxt_http_routes_t *routes; nxt_event_engine_t *engine; nxt_app_lang_module_t *lang; nxt_router_app_conf_t apcf; @@ -1392,6 +1401,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, static nxt_str_t http_path = nxt_string("/settings/http"); static nxt_str_t applications_path = nxt_string("/applications"); static nxt_str_t listeners_path = nxt_string("/listeners"); + static nxt_str_t routes_path = nxt_string("/routes"); static nxt_str_t access_log_path = nxt_string("/access_log"); #if (NXT_TLS) static nxt_str_t certificate_path = nxt_string("/tls/certificate"); @@ -1589,6 +1599,15 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, app_joint->free_app_work.obj = app_joint; } + routes_conf = nxt_conf_get_path(conf, &routes_path); + if (nxt_fast_path(routes_conf != NULL)) { + routes = nxt_http_routes_create(task, tmcf, routes_conf); + if (nxt_slow_path(routes == NULL)) { + return NXT_ERROR; + } + tmcf->router_conf->routes = routes; + } + http = nxt_conf_get_path(conf, &http_path); #if 0 if (http == NULL) { @@ -1671,10 +1690,13 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, skcf->router_conf = tmcf->router_conf; skcf->router_conf->count++; - if (lscf.application.length > 0) { - skcf->application = nxt_router_listener_application(tmcf, - &lscf.application); - nxt_router_app_use(task, skcf->application, 1); + if (lscf.pass.length != 0) { + skcf->pass = nxt_http_pass_create(task, tmcf, &lscf.pass); + + /* COMPATIBILITY: listener application. */ + } else if (lscf.application.length > 0) { + skcf->pass = nxt_http_pass_application(task, tmcf, + &lscf.application); } } @@ -1712,6 +1734,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, tmcf->router_conf->access_log = access_log; } + nxt_http_routes_resolve(task, tmcf); + nxt_queue_add(&tmcf->deleting, &router->sockets); nxt_queue_init(&router->sockets); @@ -1752,7 +1776,7 @@ nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name) } -static nxt_app_t * +nxt_app_t * nxt_router_listener_application(nxt_router_temp_conf_t *tmcf, nxt_str_t *name) { nxt_app_t *app; @@ -2885,7 +2909,6 @@ nxt_router_listen_event_release(nxt_task_t *task, nxt_listen_event_t *lev, void nxt_router_conf_release(nxt_task_t *task, nxt_socket_conf_joint_t *joint) { - nxt_app_t *app; nxt_socket_conf_t *skcf; nxt_router_conf_t *rtcf; nxt_thread_spinlock_t *lock; @@ -2904,7 +2927,6 @@ nxt_router_conf_release(nxt_task_t *task, nxt_socket_conf_joint_t *joint) * be already destroyed by another thread. */ skcf = joint->socket_conf; - app = skcf->application; rtcf = skcf->router_conf; lock = &rtcf->router->lock; @@ -2916,7 +2938,6 @@ nxt_router_conf_release(nxt_task_t *task, nxt_socket_conf_joint_t *joint) if (--skcf->count != 0) { skcf = NULL; rtcf = NULL; - app = NULL; } else { nxt_queue_remove(&skcf->link); @@ -2929,6 +2950,10 @@ nxt_router_conf_release(nxt_task_t *task, nxt_socket_conf_joint_t *joint) nxt_thread_spin_unlock(lock); if (skcf != NULL) { + if (skcf->pass != NULL) { + nxt_http_pass_cleanup(task, skcf->pass); + } + #if (NXT_TLS) if (skcf->tls != NULL) { task->thread->runtime->tls->server_free(task, skcf->tls); @@ -2936,16 +2961,14 @@ nxt_router_conf_release(nxt_task_t *task, nxt_socket_conf_joint_t *joint) #endif } - if (app != NULL) { - nxt_router_app_use(task, app, -1); - } - /* TODO remove engine->port */ /* TODO excude from connected ports */ if (rtcf != NULL) { nxt_debug(task, "old router conf is destroyed"); + nxt_http_routes_cleanup(task, rtcf->routes); + nxt_router_access_log_release(task, lock, rtcf->access_log); nxt_mp_thread_adopt(rtcf->mem_pool); @@ -4432,10 +4455,10 @@ nxt_router_app_port(nxt_task_t *task, nxt_app_t *app, nxt_req_app_link_t *ra) void -nxt_router_process_http_request(nxt_task_t *task, nxt_app_parse_ctx_t *ar) +nxt_router_process_http_request(nxt_task_t *task, nxt_app_parse_ctx_t *ar, + nxt_app_t *app) { nxt_int_t res; - nxt_app_t *app; nxt_port_t *port; nxt_event_engine_t *engine; nxt_http_request_t *r; @@ -4443,13 +4466,6 @@ nxt_router_process_http_request(nxt_task_t *task, nxt_app_parse_ctx_t *ar) nxt_req_conn_link_t *rc; r = ar->request; - app = r->conf->socket_conf->application; - - if (app == NULL) { - nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); - return; - } - engine = task->thread->engine; rc = nxt_port_rpc_register_handler_ex(task, engine->port, diff --git a/src/nxt_router.h b/src/nxt_router.h index a2da8ff9..dec56bd5 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -16,6 +16,8 @@ typedef struct nxt_http_request_s nxt_http_request_t; #include <nxt_application.h> +typedef struct nxt_http_pass_s nxt_http_pass_t; +typedef struct nxt_http_routes_s nxt_http_routes_t; typedef struct nxt_router_access_log_s nxt_router_access_log_t; @@ -34,6 +36,7 @@ typedef struct { uint32_t count; uint32_t threads; nxt_router_t *router; + nxt_http_routes_t *routes; nxt_mp_t *mem_pool; nxt_router_access_log_t *access_log; @@ -137,7 +140,7 @@ typedef struct { nxt_queue_link_t link; nxt_router_conf_t *router_conf; - nxt_app_t *application; + nxt_http_pass_t *pass; /* * A listen socket time can be shorter than socket configuration life @@ -189,8 +192,11 @@ void nxt_router_remove_pid_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); void nxt_router_access_log_reopen_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); -void nxt_router_process_http_request(nxt_task_t *task, nxt_app_parse_ctx_t *ar); +void nxt_router_process_http_request(nxt_task_t *task, nxt_app_parse_ctx_t *ar, + nxt_app_t *app); void nxt_router_app_port_close(nxt_task_t *task, nxt_port_t *port); +nxt_app_t *nxt_router_listener_application(nxt_router_temp_conf_t *tmcf, + nxt_str_t *name); void nxt_router_app_use(nxt_task_t *task, nxt_app_t *app, int i); void nxt_router_listen_event_release(nxt_task_t *task, nxt_listen_event_t *lev, nxt_socket_conf_joint_t *joint); |