Initial routing implementation.

This commit is contained in:
Igor Sysoev
2019-02-27 16:41:11 +03:00
parent 95c9bba33b
commit d4ccaae900
6 changed files with 979 additions and 40 deletions

View File

@@ -82,6 +82,7 @@ NXT_LIB_SRCS=" \
src/nxt_http_request.c \ src/nxt_http_request.c \
src/nxt_http_response.c \ src/nxt_http_response.c \
src/nxt_http_error.c \ src/nxt_http_error.c \
src/nxt_http_route.c \
src/nxt_application.c \ src/nxt_application.c \
src/nxt_external.c \ src/nxt_external.c \
src/nxt_port_hash.c \ src/nxt_port_hash.c \

View File

@@ -21,6 +21,7 @@ typedef enum {
NXT_HTTP_NOT_MODIFIED = 304, NXT_HTTP_NOT_MODIFIED = 304,
NXT_HTTP_BAD_REQUEST = 400, NXT_HTTP_BAD_REQUEST = 400,
NXT_HTTP_NOT_FOUND = 404,
NXT_HTTP_REQUEST_TIMEOUT = 408, NXT_HTTP_REQUEST_TIMEOUT = 408,
NXT_HTTP_LENGTH_REQUIRED = 411, NXT_HTTP_LENGTH_REQUIRED = 411,
NXT_HTTP_PAYLOAD_TOO_LARGE = 413, NXT_HTTP_PAYLOAD_TOO_LARGE = 413,
@@ -112,6 +113,7 @@ struct nxt_http_request_s {
nxt_buf_t *out; nxt_buf_t *out;
const nxt_http_request_state_t *state; const nxt_http_request_state_t *state;
nxt_str_t host;
nxt_str_t target; nxt_str_t target;
nxt_str_t version; nxt_str_t version;
nxt_str_t *method; nxt_str_t *method;
@@ -124,7 +126,6 @@ struct nxt_http_request_s {
nxt_http_field_t *cookie; nxt_http_field_t *cookie;
nxt_http_field_t *referer; nxt_http_field_t *referer;
nxt_http_field_t *user_agent; nxt_http_field_t *user_agent;
nxt_str_t host;
nxt_off_t content_length_n; nxt_off_t content_length_n;
nxt_sockaddr_t *remote; nxt_sockaddr_t *remote;
@@ -136,6 +137,7 @@ struct nxt_http_request_s {
nxt_http_status_t status:16; nxt_http_status_t status:16;
uint8_t pass_count; /* 8 bits */
uint8_t protocol; /* 2 bits */ uint8_t protocol; /* 2 bits */
uint8_t logged; /* 1 bit */ uint8_t logged; /* 1 bit */
uint8_t header_sent; /* 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, typedef void (*nxt_http_proto_body_read_t)(nxt_task_t *task,
nxt_http_request_t *r); nxt_http_request_t *r);
typedef void (*nxt_http_proto_local_addr_t)(nxt_task_t *task, 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, nxt_int_t nxt_http_request_content_length(void *ctx, nxt_http_field_t *field,
uintptr_t data); 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; extern nxt_time_string_t nxt_http_date_cache;

View File

@@ -10,7 +10,7 @@
static nxt_int_t nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp); 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_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, static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj,
void *data); void *data);
static void nxt_http_request_done(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 static const nxt_http_request_state_t nxt_http_request_body_state
nxt_aligned(64) = nxt_aligned(64) =
{ {
.ready_handler = nxt_http_app_request, .ready_handler = nxt_http_request_pass,
.error_handler = nxt_http_request_close_handler, .error_handler = nxt_http_request_close_handler,
}; };
static void 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_int_t ret;
nxt_event_engine_t *engine; nxt_event_engine_t *engine;
nxt_http_request_t *r;
nxt_app_parse_ctx_t *ar; 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)); ar = nxt_mp_zget(r->mem_pool, sizeof(nxt_app_parse_ctx_t));
if (nxt_slow_path(ar == NULL)) { if (nxt_slow_path(ar == NULL)) {
nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
return; return NULL;
} }
ar->request = r; 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); ret = nxt_http_parse_request_init(&ar->resp_parser, r->mem_pool);
if (nxt_slow_path(ret != NXT_OK)) { if (nxt_slow_path(ret != NXT_OK)) {
nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); 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;
} }

849
src/nxt_http_route.c Normal file
View File

@@ -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);
}

View File

@@ -31,7 +31,8 @@ typedef struct {
typedef struct { typedef struct {
nxt_str_t application; nxt_str_t pass;
nxt_str_t application;
} nxt_router_listener_conf_t; } 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, static nxt_int_t nxt_router_conf_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, u_char *start, u_char *end); 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_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, static void nxt_router_listen_socket_rpc_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_socket_conf_t *skcf); nxt_router_temp_conf_t *tmcf, nxt_socket_conf_t *skcf);
static void nxt_router_listen_socket_ready(nxt_task_t *task, 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->pending);
nxt_queue_add(&new_socket_confs, &tmcf->creating); 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) { nxt_queue_each(skcf, &new_socket_confs, nxt_socket_conf_t, link) {
if (skcf->application != NULL) { if (skcf->pass != NULL) {
nxt_router_app_use(task, skcf->application, -1); nxt_http_pass_cleanup(task, skcf->pass);
skcf->application = NULL;
} }
} nxt_queue_loop; } nxt_queue_loop;
@@ -1189,7 +1191,6 @@ nxt_router_conf_error(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
} nxt_queue_loop; } nxt_queue_loop;
rtcf = tmcf->router_conf;
router = rtcf->router; router = rtcf->router;
nxt_queue_add(&router->sockets, &tmcf->keeping); nxt_queue_add(&router->sockets, &tmcf->keeping);
@@ -1298,9 +1299,15 @@ static nxt_conf_map_t nxt_router_app_processes_conf[] = {
static nxt_conf_map_t nxt_router_listener_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_string("application"),
NXT_CONF_MAP_STR, NXT_CONF_MAP_STR_COPY,
offsetof(nxt_router_listener_conf_t, application), 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 *conf, *http, *value;
nxt_conf_value_t *applications, *application; nxt_conf_value_t *applications, *application;
nxt_conf_value_t *listeners, *listener; nxt_conf_value_t *listeners, *listener;
nxt_conf_value_t *routes_conf;
nxt_socket_conf_t *skcf; nxt_socket_conf_t *skcf;
nxt_http_routes_t *routes;
nxt_event_engine_t *engine; nxt_event_engine_t *engine;
nxt_app_lang_module_t *lang; nxt_app_lang_module_t *lang;
nxt_router_app_conf_t apcf; 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 http_path = nxt_string("/settings/http");
static nxt_str_t applications_path = nxt_string("/applications"); static nxt_str_t applications_path = nxt_string("/applications");
static nxt_str_t listeners_path = nxt_string("/listeners"); 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"); static nxt_str_t access_log_path = nxt_string("/access_log");
#if (NXT_TLS) #if (NXT_TLS)
static nxt_str_t certificate_path = nxt_string("/tls/certificate"); 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; 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); http = nxt_conf_get_path(conf, &http_path);
#if 0 #if 0
if (http == NULL) { 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 = tmcf->router_conf;
skcf->router_conf->count++; skcf->router_conf->count++;
if (lscf.application.length > 0) { if (lscf.pass.length != 0) {
skcf->application = nxt_router_listener_application(tmcf, skcf->pass = nxt_http_pass_create(task, tmcf, &lscf.pass);
&lscf.application);
nxt_router_app_use(task, skcf->application, 1); /* 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; tmcf->router_conf->access_log = access_log;
} }
nxt_http_routes_resolve(task, tmcf);
nxt_queue_add(&tmcf->deleting, &router->sockets); nxt_queue_add(&tmcf->deleting, &router->sockets);
nxt_queue_init(&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_router_listener_application(nxt_router_temp_conf_t *tmcf, nxt_str_t *name)
{ {
nxt_app_t *app; nxt_app_t *app;
@@ -2885,7 +2909,6 @@ nxt_router_listen_event_release(nxt_task_t *task, nxt_listen_event_t *lev,
void void
nxt_router_conf_release(nxt_task_t *task, nxt_socket_conf_joint_t *joint) nxt_router_conf_release(nxt_task_t *task, nxt_socket_conf_joint_t *joint)
{ {
nxt_app_t *app;
nxt_socket_conf_t *skcf; nxt_socket_conf_t *skcf;
nxt_router_conf_t *rtcf; nxt_router_conf_t *rtcf;
nxt_thread_spinlock_t *lock; 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. * be already destroyed by another thread.
*/ */
skcf = joint->socket_conf; skcf = joint->socket_conf;
app = skcf->application;
rtcf = skcf->router_conf; rtcf = skcf->router_conf;
lock = &rtcf->router->lock; 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) { if (--skcf->count != 0) {
skcf = NULL; skcf = NULL;
rtcf = NULL; rtcf = NULL;
app = NULL;
} else { } else {
nxt_queue_remove(&skcf->link); 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); nxt_thread_spin_unlock(lock);
if (skcf != NULL) { if (skcf != NULL) {
if (skcf->pass != NULL) {
nxt_http_pass_cleanup(task, skcf->pass);
}
#if (NXT_TLS) #if (NXT_TLS)
if (skcf->tls != NULL) { if (skcf->tls != NULL) {
task->thread->runtime->tls->server_free(task, skcf->tls); 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 #endif
} }
if (app != NULL) {
nxt_router_app_use(task, app, -1);
}
/* TODO remove engine->port */ /* TODO remove engine->port */
/* TODO excude from connected ports */ /* TODO excude from connected ports */
if (rtcf != NULL) { if (rtcf != NULL) {
nxt_debug(task, "old router conf is destroyed"); 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_router_access_log_release(task, lock, rtcf->access_log);
nxt_mp_thread_adopt(rtcf->mem_pool); 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 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_int_t res;
nxt_app_t *app;
nxt_port_t *port; nxt_port_t *port;
nxt_event_engine_t *engine; nxt_event_engine_t *engine;
nxt_http_request_t *r; 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; nxt_req_conn_link_t *rc;
r = ar->request; 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; engine = task->thread->engine;
rc = nxt_port_rpc_register_handler_ex(task, engine->port, rc = nxt_port_rpc_register_handler_ex(task, engine->port,

View File

@@ -16,6 +16,8 @@ typedef struct nxt_http_request_s nxt_http_request_t;
#include <nxt_application.h> #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; typedef struct nxt_router_access_log_s nxt_router_access_log_t;
@@ -34,6 +36,7 @@ typedef struct {
uint32_t count; uint32_t count;
uint32_t threads; uint32_t threads;
nxt_router_t *router; nxt_router_t *router;
nxt_http_routes_t *routes;
nxt_mp_t *mem_pool; nxt_mp_t *mem_pool;
nxt_router_access_log_t *access_log; nxt_router_access_log_t *access_log;
@@ -137,7 +140,7 @@ typedef struct {
nxt_queue_link_t link; nxt_queue_link_t link;
nxt_router_conf_t *router_conf; 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 * 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, void nxt_router_access_log_reopen_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg); 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); 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_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, void nxt_router_listen_event_release(nxt_task_t *task, nxt_listen_event_t *lev,
nxt_socket_conf_joint_t *joint); nxt_socket_conf_joint_t *joint);