Added routing based on request scheme.

Scheme matches exact string “http” or “https”.
This commit is contained in:
Axel Duch
2019-07-24 13:47:35 +03:00
parent b1165d2edc
commit 7785c96c1a
7 changed files with 221 additions and 20 deletions

View File

@@ -72,6 +72,8 @@ static nxt_int_t nxt_conf_vldt_match_patterns_set(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value); nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_match_patterns_set_member( static nxt_int_t nxt_conf_vldt_match_patterns_set_member(
nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value); nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_match_scheme_pattern(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt, static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data); nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt, static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt,
@@ -214,6 +216,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = {
&nxt_conf_vldt_match_patterns, &nxt_conf_vldt_match_patterns,
NULL }, NULL },
{ nxt_string("scheme"),
NXT_CONF_VLDT_STRING,
&nxt_conf_vldt_match_scheme_pattern,
NULL },
{ nxt_string("host"), { nxt_string("host"),
NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
&nxt_conf_vldt_match_patterns, &nxt_conf_vldt_match_patterns,
@@ -819,6 +826,28 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
} }
static nxt_int_t
nxt_conf_vldt_match_scheme_pattern(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
{
nxt_str_t scheme;
static const nxt_str_t http = nxt_string("http");
static const nxt_str_t https = nxt_string("https");
nxt_conf_get_string(value, &scheme);
if (nxt_strcasestr_eq(&scheme, &http)
|| nxt_strcasestr_eq(&scheme, &https))
{
return NXT_OK;
}
return nxt_conf_vldt_error(vldt, "The \"scheme\" can either be "
"\"http\" or \"https\".");
}
static nxt_int_t static nxt_int_t
nxt_conf_vldt_match_patterns_sets(nxt_conf_validation_t *vldt, nxt_conf_vldt_match_patterns_sets(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data) nxt_conf_value_t *value, void *data)

View File

@@ -35,7 +35,6 @@ static void nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r);
static void nxt_h1p_conn_request_body_read(nxt_task_t *task, void *obj, static void nxt_h1p_conn_request_body_read(nxt_task_t *task, void *obj,
void *data); void *data);
static void nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r); static void nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r);
static void nxt_h1p_request_tls(nxt_task_t *task, nxt_http_request_t *r);
static void nxt_h1p_request_header_send(nxt_task_t *task, static void nxt_h1p_request_header_send(nxt_task_t *task,
nxt_http_request_t *r); nxt_http_request_t *r);
static void nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r, static void nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r,
@@ -104,13 +103,6 @@ const nxt_http_proto_local_addr_t nxt_http_proto_local_addr[3] = {
}; };
const nxt_http_proto_tls_t nxt_http_proto_tls[3] = {
nxt_h1p_request_tls,
NULL,
NULL,
};
const nxt_http_proto_header_send_t nxt_http_proto_header_send[3] = { const nxt_http_proto_header_send_t nxt_http_proto_header_send[3] = {
nxt_h1p_request_header_send, nxt_h1p_request_header_send,
NULL, NULL,
@@ -448,6 +440,10 @@ nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data)
r->remote = c->remote; r->remote = c->remote;
#if (NXT_TLS)
r->tls = c->u.tls;
#endif
ret = nxt_http_parse_request_init(&h1p->parser, r->mem_pool); ret = nxt_http_parse_request_init(&h1p->parser, r->mem_pool);
if (nxt_fast_path(ret == NXT_OK)) { if (nxt_fast_path(ret == NXT_OK)) {
@@ -821,15 +817,6 @@ nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r)
} }
static void
nxt_h1p_request_tls(nxt_task_t *task, nxt_http_request_t *r)
{
#if (NXT_TLS)
r->tls = r->proto.h1->conn->u.tls;
#endif
}
#define NXT_HTTP_LAST_SUCCESS \ #define NXT_HTTP_LAST_SUCCESS \
(NXT_HTTP_OK + nxt_nitems(nxt_http_success) - 1) (NXT_HTTP_OK + nxt_nitems(nxt_http_success) - 1)

View File

@@ -172,7 +172,6 @@ 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,
nxt_http_request_t *r); nxt_http_request_t *r);
typedef void (*nxt_http_proto_tls_t)(nxt_task_t *task, nxt_http_request_t *r);
typedef void (*nxt_http_proto_header_send_t)(nxt_task_t *task, typedef void (*nxt_http_proto_header_send_t)(nxt_task_t *task,
nxt_http_request_t *r); nxt_http_request_t *r);
typedef void (*nxt_http_proto_send_t)(nxt_task_t *task, nxt_http_request_t *r, typedef void (*nxt_http_proto_send_t)(nxt_task_t *task, nxt_http_request_t *r,
@@ -228,7 +227,6 @@ extern nxt_lvlhsh_t nxt_response_fields_hash;
extern const nxt_http_proto_body_read_t nxt_http_proto_body_read[]; extern const nxt_http_proto_body_read_t nxt_http_proto_body_read[];
extern const nxt_http_proto_local_addr_t nxt_http_proto_local_addr[]; extern const nxt_http_proto_local_addr_t nxt_http_proto_local_addr[];
extern const nxt_http_proto_tls_t nxt_http_proto_tls[];
extern const nxt_http_proto_header_send_t nxt_http_proto_header_send[]; extern const nxt_http_proto_header_send_t nxt_http_proto_header_send[];
extern const nxt_http_proto_send_t nxt_http_proto_send[]; extern const nxt_http_proto_send_t nxt_http_proto_send[];
extern const nxt_http_proto_body_bytes_sent_t nxt_http_proto_body_bytes_sent[]; extern const nxt_http_proto_body_bytes_sent_t nxt_http_proto_body_bytes_sent[];

View File

@@ -357,7 +357,6 @@ nxt_http_request_proto_info(nxt_task_t *task, nxt_http_request_t *r)
{ {
if (r->proto.any != NULL) { if (r->proto.any != NULL) {
nxt_http_proto_local_addr[r->protocol](task, r); nxt_http_proto_local_addr[r->protocol](task, r);
nxt_http_proto_tls[r->protocol](task, r);
} }
} }

View File

@@ -15,6 +15,7 @@ typedef enum {
NXT_HTTP_ROUTE_HEADER, NXT_HTTP_ROUTE_HEADER,
NXT_HTTP_ROUTE_ARGUMENT, NXT_HTTP_ROUTE_ARGUMENT,
NXT_HTTP_ROUTE_COOKIE, NXT_HTTP_ROUTE_COOKIE,
NXT_HTTP_ROUTE_SCHEME,
} nxt_http_route_object_t; } nxt_http_route_object_t;
@@ -41,6 +42,7 @@ typedef struct {
nxt_conf_value_t *headers; nxt_conf_value_t *headers;
nxt_conf_value_t *arguments; nxt_conf_value_t *arguments;
nxt_conf_value_t *cookies; nxt_conf_value_t *cookies;
nxt_conf_value_t *scheme;
} nxt_http_route_match_conf_t; } nxt_http_route_match_conf_t;
@@ -197,6 +199,8 @@ static nxt_http_name_value_t *nxt_http_route_argument(nxt_array_t *array,
u_char *end); u_char *end);
static nxt_int_t nxt_http_route_test_argument(nxt_http_request_t *r, 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_http_route_rule_t *rule, nxt_array_t *array);
static nxt_int_t nxt_http_route_scheme(nxt_http_request_t *r,
nxt_http_route_rule_t *rule);
static nxt_int_t nxt_http_route_cookies(nxt_http_request_t *r, static nxt_int_t nxt_http_route_cookies(nxt_http_request_t *r,
nxt_http_route_rule_t *rule); nxt_http_route_rule_t *rule);
static nxt_array_t *nxt_http_route_cookies_parse(nxt_http_request_t *r); static nxt_array_t *nxt_http_route_cookies_parse(nxt_http_request_t *r);
@@ -276,6 +280,11 @@ nxt_http_routes_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
static nxt_conf_map_t nxt_http_route_match_conf[] = { static nxt_conf_map_t nxt_http_route_match_conf[] = {
{
nxt_string("scheme"),
NXT_CONF_MAP_PTR,
offsetof(nxt_http_route_match_conf_t, scheme)
},
{ {
nxt_string("host"), nxt_string("host"),
NXT_CONF_MAP_PTR, NXT_CONF_MAP_PTR,
@@ -412,6 +421,18 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
test = &match->test[0]; test = &match->test[0];
if (mtcf.scheme != NULL) {
rule = nxt_http_route_rule_create(task, mp, mtcf.scheme, 1,
NXT_HTTP_ROUTE_PATTERN_NOCASE);
if (rule == NULL) {
return NULL;
}
rule->object = NXT_HTTP_ROUTE_SCHEME;
test->rule = rule;
test++;
}
if (mtcf.host != NULL) { if (mtcf.host != NULL) {
rule = nxt_http_route_rule_create(task, mp, mtcf.host, 1, rule = nxt_http_route_rule_create(task, mp, mtcf.host, 1,
NXT_HTTP_ROUTE_PATTERN_LOWCASE); NXT_HTTP_ROUTE_PATTERN_LOWCASE);
@@ -1125,6 +1146,9 @@ nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
case NXT_HTTP_ROUTE_COOKIE: case NXT_HTTP_ROUTE_COOKIE:
return nxt_http_route_cookies(r, rule); return nxt_http_route_cookies(r, rule);
case NXT_HTTP_ROUTE_SCHEME:
return nxt_http_route_scheme(r, rule);
default: default:
break; break;
} }
@@ -1330,6 +1354,18 @@ nxt_http_route_test_argument(nxt_http_request_t *r,
} }
static nxt_int_t
nxt_http_route_scheme(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
{
nxt_bool_t tls, https;
https = (rule->pattern[0].length1 == nxt_length("https"));
tls = (r->tls != NULL);
return (tls == https);
}
static nxt_int_t static nxt_int_t
nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule) nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
{ {

View File

@@ -2641,5 +2641,99 @@ class TestRouting(TestApplicationProto):
'match cookies array 10', 'match cookies array 10',
) )
def test_routes_match_scheme(self):
self.assertIn(
'success',
self.route(
{
"match": {"scheme": "http"},
"action": {"pass": "applications/empty"},
}
),
'match scheme http configure',
)
self.assertIn(
'success',
self.route(
{
"match": {"scheme": "https"},
"action": {"pass": "applications/empty"},
}
),
'match scheme https configure',
)
self.assertIn(
'success',
self.route(
{
"match": {"scheme": "HtTp"},
"action": {"pass": "applications/empty"},
}
),
'match scheme http case insensitive configure',
)
self.assertIn(
'success',
self.route(
{
"match": {"scheme": "HtTpS"},
"action": {"pass": "applications/empty"},
}
),
'match scheme https case insensitive configure',
)
def test_routes_match_scheme_invalid(self):
self.assertIn(
'error',
self.route(
{
"match": {"scheme": ["http"]},
"action": {"pass": "applications/empty"},
}
),
'scheme invalid type no arrays allowed',
)
self.assertIn(
'error',
self.route(
{
"match": {"scheme": "ftp"},
"action": {"pass": "applications/empty"},
}
),
'scheme invalid protocol 1',
)
self.assertIn(
'error',
self.route(
{
"match": {"scheme": "ws"},
"action": {"pass": "applications/empty"},
}
),
'scheme invalid protocol 2',
)
self.assertIn(
'error',
self.route(
{
"match": {"scheme": "*"},
"action": {"pass": "applications/empty"},
}
),
'scheme invalid no wildcard allowed',
)
self.assertIn(
'error',
self.route(
{
"match": {"scheme": ""},
"action": {"pass": "applications/empty"},
}
),
'scheme invalid empty',
)
if __name__ == '__main__': if __name__ == '__main__':
TestRouting.main() TestRouting.main()

58
test/test_routing_tls.py Normal file
View File

@@ -0,0 +1,58 @@
from unit.applications.tls import TestApplicationTLS
class TestRoutingTLS(TestApplicationTLS):
prerequisites = ['python', 'openssl']
def test_routes_match_scheme(self):
self.certificate()
self.assertIn(
'success',
self.conf(
{
"listeners": {
"*:7080": {"pass": "routes"},
"*:7081": {
"pass": "routes",
"tls": {"certificate": 'default'},
},
},
"routes": [
{
"match": {"scheme": "http"},
"action": {"pass": "applications/empty"},
},
{
"match": {"scheme": "https"},
"action": {"pass": "applications/204_no_content"},
},
],
"applications": {
"empty": {
"type": "python",
"processes": {"spare": 0},
"path": self.current_dir + "/python/empty",
"module": "wsgi",
},
"204_no_content": {
"type": "python",
"processes": {"spare": 0},
"path": self.current_dir
+ "/python/204_no_content",
"module": "wsgi",
},
},
}
),
'scheme configure',
)
self.assertEqual(self.get()['status'], 200, 'scheme http')
self.assertEqual(
self.get_ssl(port=7081)['status'], 204, 'scheme https'
)
if __name__ == '__main__':
TestRoutingTLS.main()