1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7 #include <nxt_router.h>
8 #include <nxt_http.h>
9 #include <nxt_sockaddr.h>
10 #include <nxt_http_route_addr.h>
11 #include <nxt_regex.h>
12
13
14 typedef enum {
15 NXT_HTTP_ROUTE_TABLE = 0,
16 NXT_HTTP_ROUTE_STRING,
17 NXT_HTTP_ROUTE_STRING_PTR,
18 NXT_HTTP_ROUTE_HEADER,
19 NXT_HTTP_ROUTE_ARGUMENT,
20 NXT_HTTP_ROUTE_COOKIE,
21 NXT_HTTP_ROUTE_SCHEME,
22 NXT_HTTP_ROUTE_QUERY,
23 NXT_HTTP_ROUTE_SOURCE,
24 NXT_HTTP_ROUTE_DESTINATION,
25 } nxt_http_route_object_t;
26
27
28 typedef enum {
29 NXT_HTTP_ROUTE_PATTERN_EXACT = 0,
30 NXT_HTTP_ROUTE_PATTERN_BEGIN,
31 NXT_HTTP_ROUTE_PATTERN_END,
32 NXT_HTTP_ROUTE_PATTERN_SUBSTRING,
33 } nxt_http_route_pattern_type_t;
34
35
36 typedef enum {
37 NXT_HTTP_ROUTE_PATTERN_NOCASE = 0,
38 NXT_HTTP_ROUTE_PATTERN_LOWCASE,
39 NXT_HTTP_ROUTE_PATTERN_UPCASE,
40 } nxt_http_route_pattern_case_t;
41
42
43 typedef enum {
44 NXT_HTTP_ROUTE_ENCODING_NONE = 0,
45 NXT_HTTP_ROUTE_ENCODING_URI,
46 NXT_HTTP_ROUTE_ENCODING_URI_PLUS
47 } nxt_http_route_encoding_t;
48
49
50 typedef struct {
51 nxt_conf_value_t *host;
52 nxt_conf_value_t *uri;
53 nxt_conf_value_t *method;
54 nxt_conf_value_t *headers;
55 nxt_conf_value_t *arguments;
56 nxt_conf_value_t *cookies;
57 nxt_conf_value_t *scheme;
58 nxt_conf_value_t *query;
59 nxt_conf_value_t *source;
60 nxt_conf_value_t *destination;
61 } nxt_http_route_match_conf_t;
62
63
64 typedef struct {
65 u_char *start;
66 uint32_t length;
67 nxt_http_route_pattern_type_t type:8;
68 } nxt_http_route_pattern_slice_t;
69
70
71 typedef struct {
72 union {
73 nxt_array_t *pattern_slices;
74 #if (NXT_HAVE_REGEX)
75 nxt_regex_t *regex;
76 #endif
77 } u;
78 uint32_t min_length;
79
80 uint8_t case_sensitive; /* 1 bit */
81 uint8_t negative; /* 1 bit */
82 uint8_t any; /* 1 bit */
83 #if (NXT_HAVE_REGEX)
84 uint8_t regex; /* 1 bit */
85 #endif
86 } nxt_http_route_pattern_t;
87
88
89 typedef struct {
90 uint16_t hash;
91 uint16_t name_length;
92 uint32_t value_length;
93 u_char *name;
94 u_char *value;
95 } nxt_http_name_value_t;
96
97
98 typedef struct {
99 uint16_t hash;
100 uint16_t name_length;
101 uint32_t value_length;
102 u_char *name;
103 u_char *value;
104 } nxt_http_cookie_t;
105
106
107 struct nxt_http_route_rule_s {
108 /* The object must be the first field. */
109 nxt_http_route_object_t object:8;
110 uint32_t items;
111
112 union {
113 uintptr_t offset;
114
115 struct {
116 u_char *start;
117 uint16_t hash;
118 uint16_t length;
119 } name;
120 } u;
121
122 nxt_http_route_pattern_t pattern[0];
123 };
124
125
126 typedef struct {
127 uint32_t items;
128 nxt_http_route_rule_t *rule[0];
129 } nxt_http_route_ruleset_t;
130
131
132 typedef struct {
133 /* The object must be the first field. */
134 nxt_http_route_object_t object:8;
135 uint32_t items;
136 nxt_http_route_ruleset_t *ruleset[0];
137 } nxt_http_route_table_t;
138
139
140 struct nxt_http_route_addr_rule_s {
141 /* The object must be the first field. */
142 nxt_http_route_object_t object:8;
143 uint32_t items;
144 nxt_http_route_addr_pattern_t addr_pattern[0];
145 };
146
147
148 typedef union {
149 nxt_http_route_rule_t *rule;
150 nxt_http_route_table_t *table;
151 nxt_http_route_addr_rule_t *addr_rule;
152 } nxt_http_route_test_t;
153
154
155 typedef struct {
156 uint32_t items;
157 nxt_http_action_t action;
158 nxt_http_route_test_t test[0];
159 } nxt_http_route_match_t;
160
161
162 struct nxt_http_route_s {
163 nxt_str_t name;
164 uint32_t items;
165 nxt_http_route_match_t *match[0];
166 };
167
168
169 struct nxt_http_routes_s {
170 uint32_t items;
171 nxt_http_route_t *route[0];
172 };
173
174
175 #define NXT_COOKIE_HASH \
176 (nxt_http_field_hash_end( \
177 nxt_http_field_hash_char( \
178 nxt_http_field_hash_char( \
179 nxt_http_field_hash_char( \
180 nxt_http_field_hash_char( \
181 nxt_http_field_hash_char( \
182 nxt_http_field_hash_char(NXT_HTTP_FIELD_HASH_INIT, \
183 'c'), 'o'), 'o'), 'k'), 'i'), 'e')) & 0xFFFF)
184
185
186 static nxt_http_route_t *nxt_http_route_create(nxt_task_t *task,
187 nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv);
188 static nxt_http_route_match_t *nxt_http_route_match_create(nxt_task_t *task,
189 nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv);
190 static nxt_http_route_table_t *nxt_http_route_table_create(nxt_task_t *task,
191 nxt_mp_t *mp, nxt_conf_value_t *table_cv, nxt_http_route_object_t object,
192 nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding);
193 static nxt_http_route_ruleset_t *nxt_http_route_ruleset_create(nxt_task_t *task,
194 nxt_mp_t *mp, nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object,
195 nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding);
196 static nxt_http_route_rule_t *nxt_http_route_rule_name_create(nxt_task_t *task,
197 nxt_mp_t *mp, nxt_conf_value_t *rule_cv, nxt_str_t *name,
198 nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding);
199 static nxt_http_route_rule_t *nxt_http_route_rule_create(nxt_task_t *task,
200 nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive,
201 nxt_http_route_pattern_case_t pattern_case,
202 nxt_http_route_encoding_t encoding);
203 static int nxt_http_pattern_compare(const void *one, const void *two);
204 static int nxt_http_addr_pattern_compare(const void *one, const void *two);
205 static nxt_int_t nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
206 nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern,
207 nxt_http_route_pattern_case_t pattern_case,
208 nxt_http_route_encoding_t encoding);
209 static nxt_int_t nxt_http_route_decode_str(nxt_str_t *str,
210 nxt_http_route_encoding_t encoding);
211 static nxt_int_t nxt_http_route_pattern_slice(nxt_array_t *slices,
212 nxt_str_t *test,
213 nxt_http_route_pattern_type_t type,
214 nxt_http_route_encoding_t encoding,
215 nxt_http_route_pattern_case_t pattern_case);
216
217 static nxt_int_t nxt_http_route_resolve(nxt_task_t *task,
218 nxt_router_temp_conf_t *tmcf, nxt_http_route_t *route);
219 static nxt_int_t nxt_http_action_resolve(nxt_task_t *task,
220 nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action);
221 static nxt_http_action_t *nxt_http_pass_var(nxt_task_t *task,
222 nxt_http_request_t *r, nxt_http_action_t *action);
223 static void nxt_http_pass_var_ready(nxt_task_t *task, void *obj, void *data);
224 static void nxt_http_pass_var_error(nxt_task_t *task, void *obj, void *data);
225 static nxt_int_t nxt_http_pass_find(nxt_mp_t *mp, nxt_router_conf_t *rtcf,
226 nxt_str_t *pass, nxt_http_action_t *action);
227 static nxt_int_t nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name,
228 nxt_http_action_t *action);
229
230 static nxt_http_action_t *nxt_http_route_handler(nxt_task_t *task,
231 nxt_http_request_t *r, nxt_http_action_t *start);
232 static nxt_http_action_t *nxt_http_route_match(nxt_task_t *task,
233 nxt_http_request_t *r, nxt_http_route_match_t *match);
234 static nxt_int_t nxt_http_route_table(nxt_http_request_t *r,
235 nxt_http_route_table_t *table);
236 static nxt_int_t nxt_http_route_ruleset(nxt_http_request_t *r,
237 nxt_http_route_ruleset_t *ruleset);
238 static nxt_int_t nxt_http_route_rule(nxt_http_request_t *r,
239 nxt_http_route_rule_t *rule);
240 static nxt_int_t nxt_http_route_header(nxt_http_request_t *r,
241 nxt_http_route_rule_t *rule);
242 static nxt_int_t nxt_http_route_arguments(nxt_http_request_t *r,
243 nxt_http_route_rule_t *rule);
244 static nxt_array_t *nxt_http_route_arguments_parse(nxt_http_request_t *r);
245 static nxt_http_name_value_t *nxt_http_route_argument(nxt_array_t *array,
246 u_char *name, size_t name_length, uint32_t hash, u_char *start,
247 u_char *end);
248 static nxt_int_t nxt_http_route_test_argument(nxt_http_request_t *r,
249 nxt_http_route_rule_t *rule, nxt_array_t *array);
250 static nxt_int_t nxt_http_route_scheme(nxt_http_request_t *r,
251 nxt_http_route_rule_t *rule);
252 static nxt_int_t nxt_http_route_query(nxt_http_request_t *r,
253 nxt_http_route_rule_t *rule);
254 static nxt_int_t nxt_http_route_cookies(nxt_http_request_t *r,
255 nxt_http_route_rule_t *rule);
256 static nxt_array_t *nxt_http_route_cookies_parse(nxt_http_request_t *r);
257 static nxt_int_t nxt_http_route_cookie_parse(nxt_array_t *cookies,
258 u_char *start, u_char *end);
259 static nxt_http_name_value_t *nxt_http_route_cookie(nxt_array_t *array,
260 u_char *name, size_t name_length, u_char *start, u_char *end);
261 static nxt_int_t nxt_http_route_test_cookie(nxt_http_request_t *r,
262 nxt_http_route_rule_t *rule, nxt_array_t *array);
263 static nxt_int_t nxt_http_route_pattern(nxt_http_request_t *r,
264 nxt_http_route_pattern_t *pattern, u_char *start, size_t length);
265 static nxt_int_t nxt_http_route_memcmp(u_char *start, u_char *test,
266 size_t length, nxt_bool_t case_sensitive);
267
268
269 nxt_http_routes_t *
nxt_http_routes_create(nxt_task_t * task,nxt_router_temp_conf_t * tmcf,nxt_conf_value_t * routes_conf)270 nxt_http_routes_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
271 nxt_conf_value_t *routes_conf)
272 {
273 size_t size;
274 uint32_t i, n, next;
275 nxt_mp_t *mp;
276 nxt_str_t name, *string;
277 nxt_bool_t object;
278 nxt_conf_value_t *route_conf;
279 nxt_http_route_t *route;
280 nxt_http_routes_t *routes;
281
282 object = (nxt_conf_type(routes_conf) == NXT_CONF_OBJECT);
283 n = object ? nxt_conf_object_members_count(routes_conf) : 1;
284 size = sizeof(nxt_http_routes_t) + n * sizeof(nxt_http_route_t *);
285
286 mp = tmcf->router_conf->mem_pool;
287
288 routes = nxt_mp_alloc(mp, size);
289 if (nxt_slow_path(routes == NULL)) {
290 return NULL;
291 }
292
293 routes->items = n;
294
295 if (object) {
296 next = 0;
297
298 for (i = 0; i < n; i++) {
299 route_conf = nxt_conf_next_object_member(routes_conf, &name, &next);
300
301 route = nxt_http_route_create(task, tmcf, route_conf);
302 if (nxt_slow_path(route == NULL)) {
303 return NULL;
304 }
305
306 routes->route[i] = route;
307
308 string = nxt_str_dup(mp, &route->name, &name);
309 if (nxt_slow_path(string == NULL)) {
310 return NULL;
311 }
312 }
313
314 } else {
315 route = nxt_http_route_create(task, tmcf, routes_conf);
316 if (nxt_slow_path(route == NULL)) {
317 return NULL;
318 }
319
320 routes->route[0] = route;
321
322 route->name.length = 0;
323 route->name.start = NULL;
324 }
325
326 return routes;
327 }
328
329
330 static nxt_conf_map_t nxt_http_route_match_conf[] = {
331 {
332 nxt_string("scheme"),
333 NXT_CONF_MAP_PTR,
334 offsetof(nxt_http_route_match_conf_t, scheme)
335 },
336 {
337 nxt_string("host"),
338 NXT_CONF_MAP_PTR,
339 offsetof(nxt_http_route_match_conf_t, host),
340 },
341
342 {
343 nxt_string("uri"),
344 NXT_CONF_MAP_PTR,
345 offsetof(nxt_http_route_match_conf_t, uri),
346 },
347
348 {
349 nxt_string("method"),
350 NXT_CONF_MAP_PTR,
351 offsetof(nxt_http_route_match_conf_t, method),
352 },
353
354 {
355 nxt_string("headers"),
356 NXT_CONF_MAP_PTR,
357 offsetof(nxt_http_route_match_conf_t, headers),
358 },
359
360 {
361 nxt_string("arguments"),
362 NXT_CONF_MAP_PTR,
363 offsetof(nxt_http_route_match_conf_t, arguments),
364 },
365
366 {
367 nxt_string("cookies"),
368 NXT_CONF_MAP_PTR,
369 offsetof(nxt_http_route_match_conf_t, cookies),
370 },
371
372 {
373 nxt_string("query"),
374 NXT_CONF_MAP_PTR,
375 offsetof(nxt_http_route_match_conf_t, query),
376 },
377
378 {
379 nxt_string("source"),
380 NXT_CONF_MAP_PTR,
381 offsetof(nxt_http_route_match_conf_t, source),
382 },
383
384 {
385 nxt_string("destination"),
386 NXT_CONF_MAP_PTR,
387 offsetof(nxt_http_route_match_conf_t, destination),
388 },
389 };
390
391
392 static nxt_http_route_t *
nxt_http_route_create(nxt_task_t * task,nxt_router_temp_conf_t * tmcf,nxt_conf_value_t * cv)393 nxt_http_route_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
394 nxt_conf_value_t *cv)
395 {
396 size_t size;
397 uint32_t i, n;
398 nxt_conf_value_t *value;
399 nxt_http_route_t *route;
400 nxt_http_route_match_t *match, **m;
401
402 n = nxt_conf_array_elements_count(cv);
403 size = sizeof(nxt_http_route_t) + n * sizeof(nxt_http_route_match_t *);
404
405 route = nxt_mp_alloc(tmcf->router_conf->mem_pool, size);
406 if (nxt_slow_path(route == NULL)) {
407 return NULL;
408 }
409
410 route->items = n;
411 m = &route->match[0];
412
413 for (i = 0; i < n; i++) {
414 value = nxt_conf_get_array_element(cv, i);
415
416 match = nxt_http_route_match_create(task, tmcf, value);
417 if (match == NULL) {
418 return NULL;
419 }
420
421 *m++ = match;
422 }
423
424 return route;
425 }
426
427
428 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)429 nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
430 nxt_conf_value_t *cv)
431 {
432 size_t size;
433 uint32_t n;
434 nxt_mp_t *mp;
435 nxt_int_t ret;
436 nxt_conf_value_t *match_conf, *action_conf;
437 nxt_http_route_test_t *test;
438 nxt_http_route_rule_t *rule;
439 nxt_http_route_table_t *table;
440 nxt_http_route_match_t *match;
441 nxt_http_route_addr_rule_t *addr_rule;
442 nxt_http_route_match_conf_t mtcf;
443
444 static nxt_str_t match_path = nxt_string("/match");
445 static nxt_str_t action_path = nxt_string("/action");
446
447 match_conf = nxt_conf_get_path(cv, &match_path);
448
449 n = (match_conf != NULL) ? nxt_conf_object_members_count(match_conf) : 0;
450 size = sizeof(nxt_http_route_match_t) + n * sizeof(nxt_http_route_test_t *);
451
452 mp = tmcf->router_conf->mem_pool;
453
454 match = nxt_mp_alloc(mp, size);
455 if (nxt_slow_path(match == NULL)) {
456 return NULL;
457 }
458
459 match->items = n;
460
461 action_conf = nxt_conf_get_path(cv, &action_path);
462 if (nxt_slow_path(action_conf == NULL)) {
463 return NULL;
464 }
465
466 ret = nxt_http_action_init(task, tmcf, action_conf, &match->action);
467 if (nxt_slow_path(ret != NXT_OK)) {
468 return NULL;
469 }
470
471 if (n == 0) {
472 return match;
473 }
474
475 nxt_memzero(&mtcf, sizeof(mtcf));
476
477 ret = nxt_conf_map_object(tmcf->mem_pool,
478 match_conf, nxt_http_route_match_conf,
479 nxt_nitems(nxt_http_route_match_conf), &mtcf);
480 if (ret != NXT_OK) {
481 return NULL;
482 }
483
484 test = &match->test[0];
485
486 if (mtcf.scheme != NULL) {
487 rule = nxt_http_route_rule_create(task, mp, mtcf.scheme, 1,
488 NXT_HTTP_ROUTE_PATTERN_NOCASE,
489 NXT_HTTP_ROUTE_ENCODING_NONE);
490 if (rule == NULL) {
491 return NULL;
492 }
493
494 rule->object = NXT_HTTP_ROUTE_SCHEME;
495 test->rule = rule;
496 test++;
497 }
498
499 if (mtcf.host != NULL) {
500 rule = nxt_http_route_rule_create(task, mp, mtcf.host, 1,
501 NXT_HTTP_ROUTE_PATTERN_LOWCASE,
502 NXT_HTTP_ROUTE_ENCODING_NONE);
503 if (rule == NULL) {
504 return NULL;
505 }
506
507 rule->u.offset = offsetof(nxt_http_request_t, host);
508 rule->object = NXT_HTTP_ROUTE_STRING;
509 test->rule = rule;
510 test++;
511 }
512
513 if (mtcf.uri != NULL) {
514 rule = nxt_http_route_rule_create(task, mp, mtcf.uri, 1,
515 NXT_HTTP_ROUTE_PATTERN_NOCASE,
516 NXT_HTTP_ROUTE_ENCODING_URI);
517 if (rule == NULL) {
518 return NULL;
519 }
520
521 rule->u.offset = offsetof(nxt_http_request_t, path);
522 rule->object = NXT_HTTP_ROUTE_STRING_PTR;
523 test->rule = rule;
524 test++;
525 }
526
527 if (mtcf.method != NULL) {
528 rule = nxt_http_route_rule_create(task, mp, mtcf.method, 1,
529 NXT_HTTP_ROUTE_PATTERN_UPCASE,
530 NXT_HTTP_ROUTE_ENCODING_NONE);
531 if (rule == NULL) {
532 return NULL;
533 }
534
535 rule->u.offset = offsetof(nxt_http_request_t, method);
536 rule->object = NXT_HTTP_ROUTE_STRING_PTR;
537 test->rule = rule;
538 test++;
539 }
540
541 if (mtcf.headers != NULL) {
542 table = nxt_http_route_table_create(task, mp, mtcf.headers,
543 NXT_HTTP_ROUTE_HEADER, 0,
544 NXT_HTTP_ROUTE_ENCODING_NONE);
545 if (table == NULL) {
546 return NULL;
547 }
548
549 test->table = table;
550 test++;
551 }
552
553 if (mtcf.arguments != NULL) {
554 table = nxt_http_route_table_create(task, mp, mtcf.arguments,
555 NXT_HTTP_ROUTE_ARGUMENT, 1,
556 NXT_HTTP_ROUTE_ENCODING_URI_PLUS);
557 if (table == NULL) {
558 return NULL;
559 }
560
561 test->table = table;
562 test++;
563 }
564
565 if (mtcf.cookies != NULL) {
566 table = nxt_http_route_table_create(task, mp, mtcf.cookies,
567 NXT_HTTP_ROUTE_COOKIE, 1,
568 NXT_HTTP_ROUTE_ENCODING_NONE);
569 if (table == NULL) {
570 return NULL;
571 }
572
573 test->table = table;
574 test++;
575 }
576
577 if (mtcf.query != NULL) {
578 rule = nxt_http_route_rule_create(task, mp, mtcf.query, 1,
579 NXT_HTTP_ROUTE_PATTERN_NOCASE,
580 NXT_HTTP_ROUTE_ENCODING_URI_PLUS);
581 if (rule == NULL) {
582 return NULL;
583 }
584
585 rule->object = NXT_HTTP_ROUTE_QUERY;
586 test->rule = rule;
587 test++;
588 }
589
590 if (mtcf.source != NULL) {
591 addr_rule = nxt_http_route_addr_rule_create(task, mp, mtcf.source);
592 if (addr_rule == NULL) {
593 return NULL;
594 }
595
596 addr_rule->object = NXT_HTTP_ROUTE_SOURCE;
597 test->addr_rule = addr_rule;
598 test++;
599 }
600
601 if (mtcf.destination != NULL) {
602 addr_rule = nxt_http_route_addr_rule_create(task, mp, mtcf.destination);
603 if (addr_rule == NULL) {
604 return NULL;
605 }
606
607 addr_rule->object = NXT_HTTP_ROUTE_DESTINATION;
608 test->addr_rule = addr_rule;
609 test++;
610 }
611
612 return match;
613 }
614
615
616 static nxt_conf_map_t nxt_http_route_action_conf[] = {
617 {
618 nxt_string("pass"),
619 NXT_CONF_MAP_PTR,
620 offsetof(nxt_http_action_conf_t, pass)
621 },
622 {
623 nxt_string("return"),
624 NXT_CONF_MAP_PTR,
625 offsetof(nxt_http_action_conf_t, ret)
626 },
627 {
628 nxt_string("location"),
629 NXT_CONF_MAP_STR,
630 offsetof(nxt_http_action_conf_t, location)
631 },
632 {
633 nxt_string("proxy"),
634 NXT_CONF_MAP_PTR,
635 offsetof(nxt_http_action_conf_t, proxy)
636 },
637 {
638 nxt_string("share"),
639 NXT_CONF_MAP_PTR,
640 offsetof(nxt_http_action_conf_t, share)
641 },
642 {
643 nxt_string("chroot"),
644 NXT_CONF_MAP_STR,
645 offsetof(nxt_http_action_conf_t, chroot)
646 },
647 {
648 nxt_string("follow_symlinks"),
649 NXT_CONF_MAP_PTR,
650 offsetof(nxt_http_action_conf_t, follow_symlinks)
651 },
652 {
653 nxt_string("traverse_mounts"),
654 NXT_CONF_MAP_PTR,
655 offsetof(nxt_http_action_conf_t, traverse_mounts)
656 },
657 {
658 nxt_string("types"),
659 NXT_CONF_MAP_PTR,
660 offsetof(nxt_http_action_conf_t, types)
661 },
662 {
663 nxt_string("fallback"),
664 NXT_CONF_MAP_PTR,
665 offsetof(nxt_http_action_conf_t, fallback)
666 },
667 };
668
669
670 nxt_int_t
nxt_http_action_init(nxt_task_t * task,nxt_router_temp_conf_t * tmcf,nxt_conf_value_t * cv,nxt_http_action_t * action)671 nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
672 nxt_conf_value_t *cv, nxt_http_action_t *action)
673 {
674 nxt_mp_t *mp;
675 nxt_int_t ret;
676 nxt_str_t pass;
677 nxt_http_action_conf_t acf;
678
679 nxt_memzero(&acf, sizeof(acf));
680
681 ret = nxt_conf_map_object(tmcf->mem_pool, cv, nxt_http_route_action_conf,
682 nxt_nitems(nxt_http_route_action_conf), &acf);
683 if (ret != NXT_OK) {
684 return ret;
685 }
686
687 nxt_memzero(action, sizeof(nxt_http_action_t));
688
689 mp = tmcf->router_conf->mem_pool;
690
691 if (acf.ret != NULL) {
692 return nxt_http_return_init(mp, action, &acf);
693 }
694
695 if (acf.share != NULL) {
696 return nxt_http_static_init(task, tmcf, action, &acf);
697 }
698
699 if (acf.proxy != NULL) {
700 return nxt_http_proxy_init(mp, action, &acf);
701 }
702
703 nxt_conf_get_string(acf.pass, &pass);
704
705 action->u.var = nxt_var_compile(&pass, mp, 0);
706 if (nxt_slow_path(action->u.var == NULL)) {
707 return NXT_ERROR;
708 }
709
710 return NXT_OK;
711 }
712
713
714 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,nxt_http_route_encoding_t encoding)715 nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp,
716 nxt_conf_value_t *table_cv, nxt_http_route_object_t object,
717 nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding)
718 {
719 size_t size;
720 uint32_t i, n;
721 nxt_bool_t array;
722 nxt_conf_value_t *ruleset_cv;
723 nxt_http_route_table_t *table;
724 nxt_http_route_ruleset_t *ruleset;
725
726 array = (nxt_conf_type(table_cv) == NXT_CONF_ARRAY);
727 n = array ? nxt_conf_array_elements_count(table_cv) : 1;
728 size = sizeof(nxt_http_route_table_t)
729 + n * sizeof(nxt_http_route_ruleset_t *);
730
731 table = nxt_mp_alloc(mp, size);
732 if (nxt_slow_path(table == NULL)) {
733 return NULL;
734 }
735
736 table->items = n;
737 table->object = NXT_HTTP_ROUTE_TABLE;
738
739 if (!array) {
740 ruleset = nxt_http_route_ruleset_create(task, mp, table_cv, object,
741 case_sensitive, encoding);
742 if (nxt_slow_path(ruleset == NULL)) {
743 return NULL;
744 }
745
746 table->ruleset[0] = ruleset;
747
748 return table;
749 }
750
751 for (i = 0; i < n; i++) {
752 ruleset_cv = nxt_conf_get_array_element(table_cv, i);
753
754 ruleset = nxt_http_route_ruleset_create(task, mp, ruleset_cv, object,
755 case_sensitive, encoding);
756 if (nxt_slow_path(ruleset == NULL)) {
757 return NULL;
758 }
759
760 table->ruleset[i] = ruleset;
761 }
762
763 return table;
764 }
765
766
767 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,nxt_http_route_encoding_t encoding)768 nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp,
769 nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object,
770 nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding)
771 {
772 size_t size;
773 uint32_t i, n, next;
774 nxt_str_t name;
775 nxt_conf_value_t *rule_cv;
776 nxt_http_route_rule_t *rule;
777 nxt_http_route_ruleset_t *ruleset;
778
779 n = nxt_conf_object_members_count(ruleset_cv);
780 size = sizeof(nxt_http_route_ruleset_t)
781 + n * sizeof(nxt_http_route_rule_t *);
782
783 ruleset = nxt_mp_alloc(mp, size);
784 if (nxt_slow_path(ruleset == NULL)) {
785 return NULL;
786 }
787
788 ruleset->items = n;
789
790 next = 0;
791
792 /*
793 * A workaround for GCC 10 with -flto -O2 flags that warns about "name"
794 * may be uninitialized in nxt_http_route_rule_name_create().
795 */
796 nxt_str_null(&name);
797
798 for (i = 0; i < n; i++) {
799 rule_cv = nxt_conf_next_object_member(ruleset_cv, &name, &next);
800
801 rule = nxt_http_route_rule_name_create(task, mp, rule_cv, &name,
802 case_sensitive, encoding);
803 if (nxt_slow_path(rule == NULL)) {
804 return NULL;
805 }
806
807 rule->object = object;
808 ruleset->rule[i] = rule;
809 }
810
811 return ruleset;
812 }
813
814
815 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,nxt_http_route_encoding_t encoding)816 nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp,
817 nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive,
818 nxt_http_route_encoding_t encoding)
819 {
820 u_char c, *p, *src, *start, *end, plus;
821 uint8_t d0, d1;
822 uint32_t hash;
823 nxt_uint_t i;
824 nxt_http_route_rule_t *rule;
825
826 rule = nxt_http_route_rule_create(task, mp, rule_cv, case_sensitive,
827 NXT_HTTP_ROUTE_PATTERN_NOCASE,
828 encoding);
829 if (nxt_slow_path(rule == NULL)) {
830 return NULL;
831 }
832
833 rule->u.name.length = name->length;
834
835 p = nxt_mp_nget(mp, name->length);
836 if (nxt_slow_path(p == NULL)) {
837 return NULL;
838 }
839
840 hash = NXT_HTTP_FIELD_HASH_INIT;
841 rule->u.name.start = p;
842
843 if (encoding == NXT_HTTP_ROUTE_ENCODING_NONE) {
844 for (i = 0; i < name->length; i++) {
845 c = name->start[i];
846 *p++ = c;
847
848 c = case_sensitive ? c : nxt_lowcase(c);
849 hash = nxt_http_field_hash_char(hash, c);
850 }
851
852 goto end;
853 }
854
855 plus = (encoding == NXT_HTTP_ROUTE_ENCODING_URI_PLUS) ? ' ' : '+';
856
857 start = name->start;
858 end = start + name->length;
859
860 for (src = start; src < end; src++) {
861 c = *src;
862
863 switch (c) {
864 case '%':
865 if (nxt_slow_path(end - src <= 2)) {
866 return NULL;
867 }
868
869 d0 = nxt_hex2int[src[1]];
870 d1 = nxt_hex2int[src[2]];
871 src += 2;
872
873 if (nxt_slow_path((d0 | d1) >= 16)) {
874 return NULL;
875 }
876
877 c = (d0 << 4) + d1;
878 *p++ = c;
879 break;
880
881 case '+':
882 c = plus;
883 *p++ = c;
884 break;
885
886 default:
887 *p++ = c;
888 break;
889 }
890
891 c = case_sensitive ? c : nxt_lowcase(c);
892 hash = nxt_http_field_hash_char(hash, c);
893 }
894
895 rule->u.name.length = p - rule->u.name.start;
896
897 end:
898
899 rule->u.name.hash = nxt_http_field_hash_end(hash) & 0xFFFF;
900
901 return rule;
902 }
903
904
905 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,nxt_http_route_encoding_t encoding)906 nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp,
907 nxt_conf_value_t *cv, nxt_bool_t case_sensitive,
908 nxt_http_route_pattern_case_t pattern_case,
909 nxt_http_route_encoding_t encoding)
910 {
911 size_t size;
912 uint32_t i, n;
913 nxt_int_t ret;
914 nxt_bool_t string;
915 nxt_conf_value_t *value;
916 nxt_http_route_rule_t *rule;
917 nxt_http_route_pattern_t *pattern;
918
919 string = (nxt_conf_type(cv) != NXT_CONF_ARRAY);
920 n = string ? 1 : nxt_conf_array_elements_count(cv);
921 size = sizeof(nxt_http_route_rule_t) + n * sizeof(nxt_http_route_pattern_t);
922
923 rule = nxt_mp_alloc(mp, size);
924 if (nxt_slow_path(rule == NULL)) {
925 return NULL;
926 }
927
928 rule->items = n;
929
930 pattern = &rule->pattern[0];
931
932 if (string) {
933 pattern[0].case_sensitive = case_sensitive;
934 ret = nxt_http_route_pattern_create(task, mp, cv, &pattern[0],
935 pattern_case, encoding);
936 if (nxt_slow_path(ret != NXT_OK)) {
937 return NULL;
938 }
939
940 return rule;
941 }
942
943 nxt_conf_array_qsort(cv, nxt_http_pattern_compare);
944
945 for (i = 0; i < n; i++) {
946 pattern[i].case_sensitive = case_sensitive;
947 value = nxt_conf_get_array_element(cv, i);
948
949 ret = nxt_http_route_pattern_create(task, mp, value, &pattern[i],
950 pattern_case, encoding);
951 if (nxt_slow_path(ret != NXT_OK)) {
952 return NULL;
953 }
954 }
955
956 return rule;
957 }
958
959
960 nxt_http_route_addr_rule_t *
nxt_http_route_addr_rule_create(nxt_task_t * task,nxt_mp_t * mp,nxt_conf_value_t * cv)961 nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp,
962 nxt_conf_value_t *cv)
963 {
964 size_t size;
965 uint32_t i, n;
966 nxt_bool_t array;
967 nxt_conf_value_t *value;
968 nxt_http_route_addr_rule_t *addr_rule;
969 nxt_http_route_addr_pattern_t *pattern;
970
971 array = (nxt_conf_type(cv) == NXT_CONF_ARRAY);
972 n = array ? nxt_conf_array_elements_count(cv) : 1;
973
974 size = sizeof(nxt_http_route_addr_rule_t)
975 + n * sizeof(nxt_http_route_addr_pattern_t);
976
977 addr_rule = nxt_mp_alloc(mp, size);
978 if (nxt_slow_path(addr_rule == NULL)) {
979 return NULL;
980 }
981
982 addr_rule->items = n;
983
984 if (!array) {
985 pattern = &addr_rule->addr_pattern[0];
986
987 if (nxt_http_route_addr_pattern_parse(mp, pattern, cv) != NXT_OK) {
988 return NULL;
989 }
990
991 return addr_rule;
992 }
993
994 for (i = 0; i < n; i++) {
995 pattern = &addr_rule->addr_pattern[i];
996 value = nxt_conf_get_array_element(cv, i);
997
998 if (nxt_http_route_addr_pattern_parse(mp, pattern, value) != NXT_OK) {
999 return NULL;
1000 }
1001 }
1002
1003 if (n > 1) {
1004 nxt_qsort(addr_rule->addr_pattern, addr_rule->items,
1005 sizeof(nxt_http_route_addr_pattern_t),
1006 nxt_http_addr_pattern_compare);
1007 }
1008
1009 return addr_rule;
1010 }
1011
1012
1013 nxt_http_route_rule_t *
nxt_http_route_types_rule_create(nxt_task_t * task,nxt_mp_t * mp,nxt_conf_value_t * types)1014 nxt_http_route_types_rule_create(nxt_task_t *task, nxt_mp_t *mp,
1015 nxt_conf_value_t *types)
1016 {
1017 return nxt_http_route_rule_create(task, mp, types, 0,
1018 NXT_HTTP_ROUTE_PATTERN_LOWCASE,
1019 NXT_HTTP_ROUTE_ENCODING_NONE);
1020 }
1021
1022
1023 static int
nxt_http_pattern_compare(const void * one,const void * two)1024 nxt_http_pattern_compare(const void *one, const void *two)
1025 {
1026 nxt_str_t test;
1027 nxt_bool_t negative1, negative2;
1028 nxt_conf_value_t *value;
1029
1030 value = (nxt_conf_value_t *) one;
1031 nxt_conf_get_string(value, &test);
1032 negative1 = (test.length != 0 && test.start[0] == '!');
1033
1034 value = (nxt_conf_value_t *) two;
1035 nxt_conf_get_string(value, &test);
1036 negative2 = (test.length != 0 && test.start[0] == '!');
1037
1038 return (negative2 - negative1);
1039 }
1040
1041
1042 static int
nxt_http_addr_pattern_compare(const void * one,const void * two)1043 nxt_http_addr_pattern_compare(const void *one, const void *two)
1044 {
1045 const nxt_http_route_addr_pattern_t *p1, *p2;
1046
1047 p1 = one;
1048 p2 = two;
1049
1050 return (p2->base.negative - p1->base.negative);
1051 }
1052
1053
1054 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,nxt_http_route_encoding_t encoding)1055 nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
1056 nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern,
1057 nxt_http_route_pattern_case_t pattern_case,
1058 nxt_http_route_encoding_t encoding)
1059 {
1060 u_char c, *p, *end;
1061 nxt_str_t test, tmp;
1062 nxt_int_t ret;
1063 nxt_array_t *slices;
1064 #if (NXT_HAVE_REGEX)
1065 nxt_regex_t *re;
1066 nxt_regex_err_t err;
1067 #endif
1068 nxt_http_route_pattern_type_t type;
1069 nxt_http_route_pattern_slice_t *slice;
1070
1071 type = NXT_HTTP_ROUTE_PATTERN_EXACT;
1072
1073 nxt_conf_get_string(cv, &test);
1074
1075 pattern->u.pattern_slices = NULL;
1076 pattern->negative = 0;
1077 pattern->any = 1;
1078 pattern->min_length = 0;
1079 #if (NXT_HAVE_REGEX)
1080 pattern->regex = 0;
1081 #endif
1082
1083 if (test.length != 0 && test.start[0] == '!') {
1084 test.start++;
1085 test.length--;
1086
1087 pattern->negative = 1;
1088 pattern->any = 0;
1089 }
1090
1091 if (test.length > 0 && test.start[0] == '~') {
1092 #if (NXT_HAVE_REGEX)
1093 test.start++;
1094 test.length--;
1095
1096 re = nxt_regex_compile(mp, &test, &err);
1097 if (nxt_slow_path(re == NULL)) {
1098 if (err.offset < test.length) {
1099 nxt_alert(task, "nxt_regex_compile(%V) failed: %s at offset %d",
1100 &test, err.msg, (int) err.offset);
1101 return NXT_ERROR;
1102 }
1103
1104 nxt_alert(task, "nxt_regex_compile(%V) failed %s", &test, err.msg);
1105
1106 return NXT_ERROR;
1107 }
1108
1109 pattern->u.regex = re;
1110 pattern->regex = 1;
1111
1112 return NXT_OK;
1113
1114 #else
1115 return NXT_ERROR;
1116 #endif
1117 }
1118
1119 slices = nxt_array_create(mp, 1, sizeof(nxt_http_route_pattern_slice_t));
1120 if (nxt_slow_path(slices == NULL)) {
1121 return NXT_ERROR;
1122 }
1123
1124 pattern->u.pattern_slices = slices;
1125
1126 if (test.length == 0) {
1127 slice = nxt_array_add(slices);
1128 if (nxt_slow_path(slice == NULL)) {
1129 return NXT_ERROR;
1130 }
1131
1132 slice->type = NXT_HTTP_ROUTE_PATTERN_EXACT;
1133 slice->start = NULL;
1134 slice->length = 0;
1135
1136 return NXT_OK;
1137 }
1138
1139 if (test.start[0] == '*') {
1140 /* 'type' is no longer 'EXACT', assume 'END'. */
1141 type = NXT_HTTP_ROUTE_PATTERN_END;
1142 test.start++;
1143 test.length--;
1144 }
1145
1146 if (type == NXT_HTTP_ROUTE_PATTERN_EXACT) {
1147 tmp.start = test.start;
1148
1149 p = nxt_memchr(test.start, '*', test.length);
1150
1151 if (p == NULL) {
1152 /* No '*' found - EXACT pattern. */
1153 tmp.length = test.length;
1154 type = NXT_HTTP_ROUTE_PATTERN_EXACT;
1155
1156 test.start += test.length;
1157 test.length = 0;
1158
1159 } else {
1160 /* '*' found - BEGIN pattern. */
1161 tmp.length = p - test.start;
1162 type = NXT_HTTP_ROUTE_PATTERN_BEGIN;
1163
1164 test.start = p + 1;
1165 test.length -= tmp.length + 1;
1166 }
1167
1168 ret = nxt_http_route_pattern_slice(slices, &tmp, type, encoding,
1169 pattern_case);
1170 if (nxt_slow_path(ret != NXT_OK)) {
1171 return ret;
1172 }
1173
1174 pattern->min_length += tmp.length;
1175 }
1176
1177 end = test.start + test.length;
1178
1179 if (test.length != 0 && end[-1] != '*') {
1180 p = end - 1;
1181
1182 while (p != test.start) {
1183 c = *p--;
1184
1185 if (c == '*') {
1186 p += 2;
1187 break;
1188 }
1189 }
1190
1191 tmp.start = p;
1192 tmp.length = end - p;
1193
1194 test.length -= tmp.length;
1195 end = p;
1196
1197 ret = nxt_http_route_pattern_slice(slices, &tmp,
1198 NXT_HTTP_ROUTE_PATTERN_END,
1199 encoding, pattern_case);
1200 if (nxt_slow_path(ret != NXT_OK)) {
1201 return ret;
1202 }
1203
1204 pattern->min_length += tmp.length;
1205 }
1206
1207 tmp.start = test.start;
1208 tmp.length = 0;
1209
1210 p = tmp.start;
1211
1212 while (p != end) {
1213 c = *p++;
1214
1215 if (c != '*') {
1216 tmp.length++;
1217 continue;
1218 }
1219
1220 if (tmp.length == 0) {
1221 tmp.start = p;
1222 continue;
1223 }
1224
1225 ret = nxt_http_route_pattern_slice(slices, &tmp,
1226 NXT_HTTP_ROUTE_PATTERN_SUBSTRING,
1227 encoding, pattern_case);
1228 if (nxt_slow_path(ret != NXT_OK)) {
1229 return ret;
1230 }
1231
1232 pattern->min_length += tmp.length;
1233
1234 tmp.start = p;
1235 tmp.length = 0;
1236 }
1237
1238 if (tmp.length != 0) {
1239 ret = nxt_http_route_pattern_slice(slices, &tmp,
1240 NXT_HTTP_ROUTE_PATTERN_SUBSTRING,
1241 encoding, pattern_case);
1242 if (nxt_slow_path(ret != NXT_OK)) {
1243 return ret;
1244 }
1245
1246 pattern->min_length += tmp.length;
1247 }
1248
1249 return NXT_OK;
1250 }
1251
1252
1253 static nxt_int_t
nxt_http_route_decode_str(nxt_str_t * str,nxt_http_route_encoding_t encoding)1254 nxt_http_route_decode_str(nxt_str_t *str, nxt_http_route_encoding_t encoding)
1255 {
1256 u_char *start, *end;
1257
1258 switch (encoding) {
1259 case NXT_HTTP_ROUTE_ENCODING_NONE:
1260 break;
1261
1262 case NXT_HTTP_ROUTE_ENCODING_URI:
1263 start = str->start;
1264
1265 end = nxt_decode_uri(start, start, str->length);
1266 if (nxt_slow_path(end == NULL)) {
1267 return NXT_ERROR;
1268 }
1269
1270 str->length = end - start;
1271 break;
1272
1273 case NXT_HTTP_ROUTE_ENCODING_URI_PLUS:
1274 start = str->start;
1275
1276 end = nxt_decode_uri_plus(start, start, str->length);
1277 if (nxt_slow_path(end == NULL)) {
1278 return NXT_ERROR;
1279 }
1280
1281 str->length = end - start;
1282 break;
1283
1284 default:
1285 nxt_unreachable();
1286 }
1287
1288 return NXT_OK;
1289 }
1290
1291
1292 static nxt_int_t
nxt_http_route_pattern_slice(nxt_array_t * slices,nxt_str_t * test,nxt_http_route_pattern_type_t type,nxt_http_route_encoding_t encoding,nxt_http_route_pattern_case_t pattern_case)1293 nxt_http_route_pattern_slice(nxt_array_t *slices,
1294 nxt_str_t *test,
1295 nxt_http_route_pattern_type_t type,
1296 nxt_http_route_encoding_t encoding,
1297 nxt_http_route_pattern_case_t pattern_case)
1298 {
1299 u_char *start;
1300 nxt_int_t ret;
1301 nxt_http_route_pattern_slice_t *slice;
1302
1303 ret = nxt_http_route_decode_str(test, encoding);
1304 if (nxt_slow_path(ret != NXT_OK)) {
1305 return ret;
1306 }
1307
1308 start = nxt_mp_nget(slices->mem_pool, test->length);
1309 if (nxt_slow_path(start == NULL)) {
1310 return NXT_ERROR;
1311 }
1312
1313 switch (pattern_case) {
1314
1315 case NXT_HTTP_ROUTE_PATTERN_UPCASE:
1316 nxt_memcpy_upcase(start, test->start, test->length);
1317 break;
1318
1319 case NXT_HTTP_ROUTE_PATTERN_LOWCASE:
1320 nxt_memcpy_lowcase(start, test->start, test->length);
1321 break;
1322
1323 case NXT_HTTP_ROUTE_PATTERN_NOCASE:
1324 nxt_memcpy(start, test->start, test->length);
1325 break;
1326 }
1327
1328 slice = nxt_array_add(slices);
1329 if (nxt_slow_path(slice == NULL)) {
1330 return NXT_ERROR;
1331 }
1332
1333 slice->type = type;
1334 slice->start = start;
1335 slice->length = test->length;
1336
1337 return NXT_OK;
1338 }
1339
1340
1341 nxt_int_t
nxt_http_routes_resolve(nxt_task_t * task,nxt_router_temp_conf_t * tmcf)1342 nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
1343 {
1344 nxt_int_t ret;
1345 nxt_http_route_t **route, **end;
1346 nxt_http_routes_t *routes;
1347
1348 routes = tmcf->router_conf->routes;
1349
1350 if (routes != NULL) {
1351 route = &routes->route[0];
1352 end = route + routes->items;
1353
1354 while (route < end) {
1355 ret = nxt_http_route_resolve(task, tmcf, *route);
1356 if (nxt_slow_path(ret != NXT_OK)) {
1357 return NXT_ERROR;
1358 }
1359
1360 route++;
1361 }
1362 }
1363
1364 return NXT_OK;
1365 }
1366
1367
1368 static nxt_int_t
nxt_http_route_resolve(nxt_task_t * task,nxt_router_temp_conf_t * tmcf,nxt_http_route_t * route)1369 nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1370 nxt_http_route_t *route)
1371 {
1372 nxt_int_t ret;
1373 nxt_http_route_match_t **match, **end;
1374
1375 match = &route->match[0];
1376 end = match + route->items;
1377
1378 while (match < end) {
1379 ret = nxt_http_action_resolve(task, tmcf, &(*match)->action);
1380 if (nxt_slow_path(ret != NXT_OK)) {
1381 return NXT_ERROR;
1382 }
1383
1384 match++;
1385 }
1386
1387 return NXT_OK;
1388 }
1389
1390
1391 static nxt_int_t
nxt_http_action_resolve(nxt_task_t * task,nxt_router_temp_conf_t * tmcf,nxt_http_action_t * action)1392 nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1393 nxt_http_action_t *action)
1394 {
1395 nxt_int_t ret;
1396 nxt_str_t pass;
1397
1398 if (action->handler != NULL) {
1399 if (action->fallback != NULL) {
1400 return nxt_http_action_resolve(task, tmcf, action->fallback);
1401 }
1402
1403 return NXT_OK;
1404 }
1405
1406 if (nxt_var_is_const(action->u.var)) {
1407 nxt_var_raw(action->u.var, &pass);
1408
1409 ret = nxt_http_pass_find(tmcf->mem_pool, tmcf->router_conf, &pass,
1410 action);
1411 if (nxt_slow_path(ret != NXT_OK)) {
1412 return NXT_ERROR;
1413 }
1414
1415 } else {
1416 action->handler = nxt_http_pass_var;
1417 }
1418
1419 return NXT_OK;
1420 }
1421
1422
1423 static nxt_http_action_t *
nxt_http_pass_var(nxt_task_t * task,nxt_http_request_t * r,nxt_http_action_t * action)1424 nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r,
1425 nxt_http_action_t *action)
1426 {
1427 nxt_int_t ret;
1428 nxt_str_t str;
1429 nxt_var_t *var;
1430
1431 var = action->u.var;
1432
1433 nxt_var_raw(var, &str);
1434
1435 nxt_debug(task, "http pass: \"%V\"", &str);
1436
1437 ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
1438 if (nxt_slow_path(ret != NXT_OK)) {
1439 goto fail;
1440 }
1441
1442 action = nxt_mp_get(r->mem_pool,
1443 sizeof(nxt_http_action_t) + sizeof(nxt_str_t));
1444 if (nxt_slow_path(action == NULL)) {
1445 goto fail;
1446 }
1447
1448 action->u.pass = nxt_pointer_to(action, sizeof(nxt_http_action_t));
1449
1450 nxt_var_query(task, r->var_query, var, action->u.pass);
1451 nxt_var_query_resolve(task, r->var_query, action,
1452 nxt_http_pass_var_ready,
1453 nxt_http_pass_var_error);
1454 return NULL;
1455
1456 fail:
1457
1458 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
1459 return NULL;
1460 }
1461
1462
1463 static void
nxt_http_pass_var_ready(nxt_task_t * task,void * obj,void * data)1464 nxt_http_pass_var_ready(nxt_task_t *task, void *obj, void *data)
1465 {
1466 nxt_int_t ret;
1467 nxt_router_conf_t *rtcf;
1468 nxt_http_action_t *action;
1469 nxt_http_status_t status;
1470 nxt_http_request_t *r;
1471
1472 r = obj;
1473 action = data;
1474 rtcf = r->conf->socket_conf->router_conf;
1475
1476 nxt_debug(task, "http pass lookup: %V", action->u.pass);
1477
1478 ret = nxt_http_pass_find(r->mem_pool, rtcf, action->u.pass, action);
1479
1480 if (ret != NXT_OK) {
1481 status = (ret == NXT_DECLINED) ? NXT_HTTP_NOT_FOUND
1482 : NXT_HTTP_INTERNAL_SERVER_ERROR;
1483
1484 nxt_http_request_error(task, r, status);
1485 return;
1486 }
1487
1488 nxt_http_request_action(task, r, action);
1489 }
1490
1491
1492 static void
nxt_http_pass_var_error(nxt_task_t * task,void * obj,void * data)1493 nxt_http_pass_var_error(nxt_task_t *task, void *obj, void *data)
1494 {
1495 nxt_http_request_t *r;
1496
1497 r = obj;
1498
1499 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
1500 }
1501
1502
1503 static nxt_int_t
nxt_http_pass_find(nxt_mp_t * mp,nxt_router_conf_t * rtcf,nxt_str_t * pass,nxt_http_action_t * action)1504 nxt_http_pass_find(nxt_mp_t *mp, nxt_router_conf_t *rtcf, nxt_str_t *pass,
1505 nxt_http_action_t *action)
1506 {
1507 nxt_int_t ret;
1508 nxt_str_t segments[3];
1509
1510 ret = nxt_http_pass_segments(mp, pass, segments, 3);
1511 if (nxt_slow_path(ret != NXT_OK)) {
1512 return ret;
1513 }
1514
1515 if (nxt_str_eq(&segments[0], "applications", 12)) {
1516 return nxt_router_application_init(rtcf, &segments[1], &segments[2],
1517 action);
1518 }
1519
1520 if (segments[2].length == 0) {
1521 if (nxt_str_eq(&segments[0], "upstreams", 9)) {
1522 return nxt_upstream_find(rtcf->upstreams, &segments[1], action);
1523 }
1524
1525 if (nxt_str_eq(&segments[0], "routes", 6)) {
1526 return nxt_http_route_find(rtcf->routes, &segments[1], action);
1527 }
1528 }
1529
1530 return NXT_DECLINED;
1531 }
1532
1533
1534 nxt_int_t
nxt_http_pass_segments(nxt_mp_t * mp,nxt_str_t * pass,nxt_str_t * segments,nxt_uint_t n)1535 nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments,
1536 nxt_uint_t n)
1537 {
1538 u_char *p;
1539 nxt_str_t rest;
1540
1541 if (nxt_slow_path(nxt_str_dup(mp, &rest, pass) == NULL)) {
1542 return NXT_ERROR;
1543 }
1544
1545 nxt_memzero(segments, n * sizeof(nxt_str_t));
1546
1547 do {
1548 p = nxt_memchr(rest.start, '/', rest.length);
1549
1550 if (p != NULL) {
1551 n--;
1552
1553 if (n == 0) {
1554 return NXT_DECLINED;
1555 }
1556
1557 segments->length = p - rest.start;
1558 segments->start = rest.start;
1559
1560 rest.length -= segments->length + 1;
1561 rest.start = p + 1;
1562
1563 } else {
1564 n = 0;
1565 *segments = rest;
1566 }
1567
1568 if (segments->length == 0) {
1569 return NXT_DECLINED;
1570 }
1571
1572 p = nxt_decode_uri(segments->start, segments->start, segments->length);
1573 if (p == NULL) {
1574 return NXT_DECLINED;
1575 }
1576
1577 segments->length = p - segments->start;
1578 segments++;
1579
1580 } while (n);
1581
1582 return NXT_OK;
1583 }
1584
1585
1586 static nxt_int_t
nxt_http_route_find(nxt_http_routes_t * routes,nxt_str_t * name,nxt_http_action_t * action)1587 nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name,
1588 nxt_http_action_t *action)
1589 {
1590 nxt_http_route_t **route, **end;
1591
1592 if (routes == NULL) {
1593 return NXT_DECLINED;
1594 }
1595
1596 route = &routes->route[0];
1597 end = route + routes->items;
1598
1599 while (route < end) {
1600 if (nxt_strstr_eq(&(*route)->name, name)) {
1601 action->u.route = *route;
1602 action->handler = nxt_http_route_handler;
1603
1604 return NXT_OK;
1605 }
1606
1607 route++;
1608 }
1609
1610 return NXT_DECLINED;
1611 }
1612
1613
1614 nxt_http_action_t *
nxt_http_action_create(nxt_task_t * task,nxt_router_temp_conf_t * tmcf,nxt_str_t * pass)1615 nxt_http_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1616 nxt_str_t *pass)
1617 {
1618 nxt_mp_t *mp;
1619 nxt_int_t ret;
1620 nxt_http_action_t *action;
1621
1622 mp = tmcf->router_conf->mem_pool;
1623
1624 action = nxt_mp_alloc(mp, sizeof(nxt_http_action_t));
1625 if (nxt_slow_path(action == NULL)) {
1626 return NULL;
1627 }
1628
1629 action->u.var = nxt_var_compile(pass, mp, 0);
1630 if (nxt_slow_path(action->u.var == NULL)) {
1631 return NULL;
1632 }
1633
1634 action->handler = NULL;
1635
1636 ret = nxt_http_action_resolve(task, tmcf, action);
1637 if (nxt_slow_path(ret != NXT_OK)) {
1638 return NULL;
1639 }
1640
1641 return action;
1642 }
1643
1644
1645 /* COMPATIBILITY: listener application. */
1646
1647 nxt_http_action_t *
nxt_http_pass_application(nxt_task_t * task,nxt_router_conf_t * rtcf,nxt_str_t * name)1648 nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf,
1649 nxt_str_t *name)
1650 {
1651 nxt_http_action_t *action;
1652
1653 action = nxt_mp_alloc(rtcf->mem_pool, sizeof(nxt_http_action_t));
1654 if (nxt_slow_path(action == NULL)) {
1655 return NULL;
1656 }
1657
1658 (void) nxt_router_application_init(rtcf, name, NULL, action);
1659
1660 return action;
1661 }
1662
1663
1664 static nxt_http_action_t *
nxt_http_route_handler(nxt_task_t * task,nxt_http_request_t * r,nxt_http_action_t * start)1665 nxt_http_route_handler(nxt_task_t *task, nxt_http_request_t *r,
1666 nxt_http_action_t *start)
1667 {
1668 nxt_http_route_t *route;
1669 nxt_http_action_t *action;
1670 nxt_http_route_match_t **match, **end;
1671
1672 route = start->u.route;
1673 match = &route->match[0];
1674 end = match + route->items;
1675
1676 while (match < end) {
1677 action = nxt_http_route_match(task, r, *match);
1678 if (action != NULL) {
1679 return action;
1680 }
1681
1682 match++;
1683 }
1684
1685 nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND);
1686
1687 return NULL;
1688 }
1689
1690
1691 static nxt_http_action_t *
nxt_http_route_match(nxt_task_t * task,nxt_http_request_t * r,nxt_http_route_match_t * match)1692 nxt_http_route_match(nxt_task_t *task, nxt_http_request_t *r,
1693 nxt_http_route_match_t *match)
1694 {
1695 nxt_int_t ret;
1696 nxt_http_route_test_t *test, *end;
1697
1698 test = &match->test[0];
1699 end = test + match->items;
1700
1701 while (test < end) {
1702 switch (test->rule->object) {
1703 case NXT_HTTP_ROUTE_TABLE:
1704 ret = nxt_http_route_table(r, test->table);
1705 break;
1706 case NXT_HTTP_ROUTE_SOURCE:
1707 ret = nxt_http_route_addr_rule(r, test->addr_rule, r->remote);
1708 break;
1709 case NXT_HTTP_ROUTE_DESTINATION:
1710 if (r->local == NULL && nxt_fast_path(r->proto.any != NULL)) {
1711 nxt_http_proto[r->protocol].local_addr(task, r);
1712 }
1713
1714 ret = nxt_http_route_addr_rule(r, test->addr_rule, r->local);
1715 break;
1716 default:
1717 ret = nxt_http_route_rule(r, test->rule);
1718 break;
1719 }
1720
1721 if (ret <= 0) {
1722 /* 0 => NULL, -1 => NXT_HTTP_ACTION_ERROR. */
1723 return (nxt_http_action_t *) (intptr_t) ret;
1724 }
1725
1726 test++;
1727 }
1728
1729 return &match->action;
1730 }
1731
1732
1733 static nxt_int_t
nxt_http_route_table(nxt_http_request_t * r,nxt_http_route_table_t * table)1734 nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table)
1735 {
1736 nxt_int_t ret;
1737 nxt_http_route_ruleset_t **ruleset, **end;
1738
1739 ret = 1;
1740 ruleset = &table->ruleset[0];
1741 end = ruleset + table->items;
1742
1743 while (ruleset < end) {
1744 ret = nxt_http_route_ruleset(r, *ruleset);
1745
1746 if (ret != 0) {
1747 return ret;
1748 }
1749
1750 ruleset++;
1751 }
1752
1753 return ret;
1754 }
1755
1756
1757 static nxt_int_t
nxt_http_route_ruleset(nxt_http_request_t * r,nxt_http_route_ruleset_t * ruleset)1758 nxt_http_route_ruleset(nxt_http_request_t *r, nxt_http_route_ruleset_t *ruleset)
1759 {
1760 nxt_int_t ret;
1761 nxt_http_route_rule_t **rule, **end;
1762
1763 rule = &ruleset->rule[0];
1764 end = rule + ruleset->items;
1765
1766 while (rule < end) {
1767 ret = nxt_http_route_rule(r, *rule);
1768
1769 if (ret <= 0) {
1770 return ret;
1771 }
1772
1773 rule++;
1774 }
1775
1776 return 1;
1777 }
1778
1779
1780 static nxt_int_t
nxt_http_route_rule(nxt_http_request_t * r,nxt_http_route_rule_t * rule)1781 nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1782 {
1783 void *p, **pp;
1784 u_char *start;
1785 size_t length;
1786 nxt_str_t *s;
1787
1788 switch (rule->object) {
1789
1790 case NXT_HTTP_ROUTE_HEADER:
1791 return nxt_http_route_header(r, rule);
1792
1793 case NXT_HTTP_ROUTE_ARGUMENT:
1794 return nxt_http_route_arguments(r, rule);
1795
1796 case NXT_HTTP_ROUTE_COOKIE:
1797 return nxt_http_route_cookies(r, rule);
1798
1799 case NXT_HTTP_ROUTE_SCHEME:
1800 return nxt_http_route_scheme(r, rule);
1801
1802 case NXT_HTTP_ROUTE_QUERY:
1803 return nxt_http_route_query(r, rule);
1804
1805 default:
1806 break;
1807 }
1808
1809 p = nxt_pointer_to(r, rule->u.offset);
1810
1811 if (rule->object == NXT_HTTP_ROUTE_STRING) {
1812 s = p;
1813
1814 } else {
1815 /* NXT_HTTP_ROUTE_STRING_PTR */
1816 pp = p;
1817 s = *pp;
1818
1819 if (s == NULL) {
1820 return 0;
1821 }
1822 }
1823
1824 length = s->length;
1825 start = s->start;
1826
1827 return nxt_http_route_test_rule(r, rule, start, length);
1828 }
1829
1830
1831 static nxt_int_t
nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t * p,nxt_sockaddr_t * sa)1832 nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p,
1833 nxt_sockaddr_t *sa)
1834 {
1835 #if (NXT_INET6)
1836 uint32_t i;
1837 #endif
1838 in_port_t in_port;
1839 nxt_int_t match;
1840 struct sockaddr_in *sin;
1841 #if (NXT_INET6)
1842 struct sockaddr_in6 *sin6;
1843 #endif
1844 nxt_http_route_addr_base_t *base;
1845
1846 base = &p->base;
1847
1848 switch (sa->u.sockaddr.sa_family) {
1849
1850 case AF_INET:
1851
1852 match = (base->addr_family == AF_INET
1853 || base->addr_family == AF_UNSPEC);
1854 if (!match) {
1855 break;
1856 }
1857
1858 sin = &sa->u.sockaddr_in;
1859 in_port = ntohs(sin->sin_port);
1860
1861 match = (in_port >= base->port.start && in_port <= base->port.end);
1862 if (!match) {
1863 break;
1864 }
1865
1866 switch (base->match_type) {
1867
1868 case NXT_HTTP_ROUTE_ADDR_ANY:
1869 break;
1870
1871 case NXT_HTTP_ROUTE_ADDR_EXACT:
1872 match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
1873 sizeof(struct in_addr))
1874 == 0);
1875 break;
1876
1877 case NXT_HTTP_ROUTE_ADDR_RANGE:
1878 match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
1879 sizeof(struct in_addr)) >= 0
1880 && nxt_memcmp(&sin->sin_addr, &p->addr.v4.end,
1881 sizeof(struct in_addr)) <= 0);
1882 break;
1883
1884 case NXT_HTTP_ROUTE_ADDR_CIDR:
1885 match = ((sin->sin_addr.s_addr & p->addr.v4.end)
1886 == p->addr.v4.start);
1887 break;
1888
1889 default:
1890 nxt_unreachable();
1891 }
1892
1893 break;
1894
1895 #if (NXT_INET6)
1896 case AF_INET6:
1897
1898 match = (base->addr_family == AF_INET6
1899 || base->addr_family == AF_UNSPEC);
1900 if (!match) {
1901 break;
1902 }
1903
1904 sin6 = &sa->u.sockaddr_in6;
1905 in_port = ntohs(sin6->sin6_port);
1906
1907 match = (in_port >= base->port.start && in_port <= base->port.end);
1908 if (!match) {
1909 break;
1910 }
1911
1912 switch (base->match_type) {
1913
1914 case NXT_HTTP_ROUTE_ADDR_ANY:
1915 break;
1916
1917 case NXT_HTTP_ROUTE_ADDR_EXACT:
1918 match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
1919 sizeof(struct in6_addr))
1920 == 0);
1921 break;
1922
1923 case NXT_HTTP_ROUTE_ADDR_RANGE:
1924 match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
1925 sizeof(struct in6_addr)) >= 0
1926 && nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.end,
1927 sizeof(struct in6_addr)) <= 0);
1928 break;
1929
1930 case NXT_HTTP_ROUTE_ADDR_CIDR:
1931 for (i = 0; i < 16; i++) {
1932 match = ((sin6->sin6_addr.s6_addr[i]
1933 & p->addr.v6.end.s6_addr[i])
1934 == p->addr.v6.start.s6_addr[i]);
1935
1936 if (!match) {
1937 break;
1938 }
1939 }
1940
1941 break;
1942
1943 default:
1944 nxt_unreachable();
1945 }
1946
1947 break;
1948 #endif
1949
1950 default:
1951 match = 0;
1952 break;
1953 }
1954
1955 return match ^ base->negative;
1956 }
1957
1958
1959 nxt_int_t
nxt_http_route_addr_rule(nxt_http_request_t * r,nxt_http_route_addr_rule_t * addr_rule,nxt_sockaddr_t * sa)1960 nxt_http_route_addr_rule(nxt_http_request_t *r,
1961 nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sa)
1962 {
1963 uint32_t n;
1964 nxt_bool_t matches;
1965 nxt_http_route_addr_pattern_t *p;
1966
1967 n = addr_rule->items;
1968
1969 if (n == 0) {
1970 return 0;
1971 }
1972
1973 p = &addr_rule->addr_pattern[0] - 1;
1974
1975 do {
1976 p++;
1977 n--;
1978
1979 matches = nxt_http_route_addr_pattern_match(p, sa);
1980
1981 if (p->base.negative) {
1982 if (matches) {
1983 continue;
1984 }
1985
1986 return 0;
1987 }
1988
1989 if (matches) {
1990 return 1;
1991 }
1992
1993 } while (n > 0);
1994
1995 return p->base.negative;
1996 }
1997
1998
1999 static nxt_int_t
nxt_http_route_header(nxt_http_request_t * r,nxt_http_route_rule_t * rule)2000 nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
2001 {
2002 nxt_int_t ret;
2003 nxt_http_field_t *f;
2004
2005 ret = 0;
2006
2007 nxt_list_each(f, r->fields) {
2008
2009 if (rule->u.name.hash != f->hash
2010 || rule->u.name.length != f->name_length
2011 || nxt_strncasecmp(rule->u.name.start, f->name, f->name_length)
2012 != 0)
2013 {
2014 continue;
2015 }
2016
2017 ret = nxt_http_route_test_rule(r, rule, f->value, f->value_length);
2018 if (nxt_slow_path(ret == NXT_ERROR)) {
2019 return NXT_ERROR;
2020 }
2021
2022 if (ret == 0) {
2023 return ret;
2024 }
2025
2026 } nxt_list_loop;
2027
2028 return ret;
2029 }
2030
2031
2032 static nxt_int_t
nxt_http_route_arguments(nxt_http_request_t * r,nxt_http_route_rule_t * rule)2033 nxt_http_route_arguments(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
2034 {
2035 nxt_array_t *arguments;
2036
2037 arguments = nxt_http_route_arguments_parse(r);
2038 if (nxt_slow_path(arguments == NULL)) {
2039 return -1;
2040 }
2041
2042 return nxt_http_route_test_argument(r, rule, arguments);
2043 }
2044
2045
2046 static nxt_array_t *
nxt_http_route_arguments_parse(nxt_http_request_t * r)2047 nxt_http_route_arguments_parse(nxt_http_request_t *r)
2048 {
2049 size_t name_length;
2050 u_char *p, *dst, *dst_start, *start, *end, *name;
2051 uint8_t d0, d1;
2052 uint32_t hash;
2053 nxt_array_t *args;
2054 nxt_http_name_value_t *nv;
2055
2056 if (r->arguments != NULL) {
2057 return r->arguments;
2058 }
2059
2060 args = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t));
2061 if (nxt_slow_path(args == NULL)) {
2062 return NULL;
2063 }
2064
2065 hash = NXT_HTTP_FIELD_HASH_INIT;
2066 name = NULL;
2067 name_length = 0;
2068
2069 dst_start = nxt_mp_nget(r->mem_pool, r->args->length);
2070 if (nxt_slow_path(dst_start == NULL)) {
2071 return NULL;
2072 }
2073
2074 r->args_decoded.start = dst_start;
2075
2076 start = r->args->start;
2077 end = start + r->args->length;
2078
2079 for (p = start, dst = dst_start; p < end; p++, dst++) {
2080 *dst = *p;
2081
2082 switch (*p) {
2083 case '=':
2084 if (name == NULL) {
2085 name_length = dst - dst_start;
2086 name = dst_start;
2087 dst_start = dst + 1;
2088 }
2089
2090 continue;
2091
2092 case '&':
2093 if (name_length != 0 || dst != dst_start) {
2094 nv = nxt_http_route_argument(args, name, name_length, hash,
2095 dst_start, dst);
2096 if (nxt_slow_path(nv == NULL)) {
2097 return NULL;
2098 }
2099 }
2100
2101 hash = NXT_HTTP_FIELD_HASH_INIT;
2102 name_length = 0;
2103 name = NULL;
2104 dst_start = dst + 1;
2105
2106 continue;
2107
2108 case '+':
2109 *dst = ' ';
2110
2111 break;
2112
2113 case '%':
2114 if (nxt_slow_path(end - p <= 2)) {
2115 break;
2116 }
2117
2118 d0 = nxt_hex2int[p[1]];
2119 d1 = nxt_hex2int[p[2]];
2120
2121 if (nxt_slow_path((d0 | d1) >= 16)) {
2122 break;
2123 }
2124
2125 p += 2;
2126 *dst = (d0 << 4) + d1;
2127
2128 break;
2129 }
2130
2131 if (name == NULL) {
2132 hash = nxt_http_field_hash_char(hash, *dst);
2133 }
2134 }
2135
2136 r->args_decoded.length = dst - r->args_decoded.start;
2137
2138 if (name_length != 0 || dst != dst_start) {
2139 nv = nxt_http_route_argument(args, name, name_length, hash, dst_start,
2140 dst);
2141 if (nxt_slow_path(nv == NULL)) {
2142 return NULL;
2143 }
2144 }
2145
2146 r->arguments = args;
2147
2148 return args;
2149 }
2150
2151
2152 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)2153 nxt_http_route_argument(nxt_array_t *array, u_char *name, size_t name_length,
2154 uint32_t hash, u_char *start, u_char *end)
2155 {
2156 size_t length;
2157 nxt_http_name_value_t *nv;
2158
2159 nv = nxt_array_add(array);
2160 if (nxt_slow_path(nv == NULL)) {
2161 return NULL;
2162 }
2163
2164 nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF;
2165
2166 length = end - start;
2167
2168 if (name == NULL) {
2169 name_length = length;
2170 name = start;
2171 length = 0;
2172 }
2173
2174 nv->name_length = name_length;
2175 nv->value_length = length;
2176 nv->name = name;
2177 nv->value = start;
2178
2179 return nv;
2180 }
2181
2182
2183 static nxt_int_t
nxt_http_route_test_argument(nxt_http_request_t * r,nxt_http_route_rule_t * rule,nxt_array_t * array)2184 nxt_http_route_test_argument(nxt_http_request_t *r,
2185 nxt_http_route_rule_t *rule, nxt_array_t *array)
2186 {
2187 nxt_int_t ret;
2188 nxt_http_name_value_t *nv, *end;
2189
2190 ret = 0;
2191
2192 nv = array->elts;
2193 end = nv + array->nelts;
2194
2195 while (nv < end) {
2196
2197 if (rule->u.name.hash == nv->hash
2198 && rule->u.name.length == nv->name_length
2199 && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
2200 {
2201 ret = nxt_http_route_test_rule(r, rule, nv->value,
2202 nv->value_length);
2203 if (nxt_slow_path(ret == NXT_ERROR)) {
2204 return NXT_ERROR;
2205 }
2206
2207 if (ret == 0) {
2208 break;
2209 }
2210 }
2211
2212 nv++;
2213 }
2214
2215 return ret;
2216 }
2217
2218
2219 static nxt_int_t
nxt_http_route_scheme(nxt_http_request_t * r,nxt_http_route_rule_t * rule)2220 nxt_http_route_scheme(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
2221 {
2222 nxt_bool_t tls, https;
2223 nxt_http_route_pattern_slice_t *pattern_slice;
2224
2225 pattern_slice = rule->pattern[0].u.pattern_slices->elts;
2226 https = (pattern_slice->length == nxt_length("https"));
2227 tls = (r->tls != NULL);
2228
2229 return (tls == https);
2230 }
2231
2232
2233 static nxt_int_t
nxt_http_route_query(nxt_http_request_t * r,nxt_http_route_rule_t * rule)2234 nxt_http_route_query(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
2235 {
2236 nxt_array_t *arguments;
2237
2238 arguments = nxt_http_route_arguments_parse(r);
2239 if (nxt_slow_path(arguments == NULL)) {
2240 return -1;
2241 }
2242
2243 return nxt_http_route_test_rule(r, rule, r->args_decoded.start,
2244 r->args_decoded.length);
2245 }
2246
2247
2248 static nxt_int_t
nxt_http_route_cookies(nxt_http_request_t * r,nxt_http_route_rule_t * rule)2249 nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
2250 {
2251 nxt_array_t *cookies;
2252
2253 cookies = nxt_http_route_cookies_parse(r);
2254 if (nxt_slow_path(cookies == NULL)) {
2255 return -1;
2256 }
2257
2258 return nxt_http_route_test_cookie(r, rule, cookies);
2259 }
2260
2261
2262 static nxt_array_t *
nxt_http_route_cookies_parse(nxt_http_request_t * r)2263 nxt_http_route_cookies_parse(nxt_http_request_t *r)
2264 {
2265 nxt_int_t ret;
2266 nxt_array_t *cookies;
2267 nxt_http_field_t *f;
2268
2269 if (r->cookies != NULL) {
2270 return r->cookies;
2271 }
2272
2273 cookies = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t));
2274 if (nxt_slow_path(cookies == NULL)) {
2275 return NULL;
2276 }
2277
2278 nxt_list_each(f, r->fields) {
2279
2280 if (f->hash != NXT_COOKIE_HASH
2281 || f->name_length != 6
2282 || nxt_strncasecmp(f->name, (u_char *) "Cookie", 6) != 0)
2283 {
2284 continue;
2285 }
2286
2287 ret = nxt_http_route_cookie_parse(cookies, f->value,
2288 f->value + f->value_length);
2289 if (ret != NXT_OK) {
2290 return NULL;
2291 }
2292
2293 } nxt_list_loop;
2294
2295 r->cookies = cookies;
2296
2297 return cookies;
2298 }
2299
2300
2301 static nxt_int_t
nxt_http_route_cookie_parse(nxt_array_t * cookies,u_char * start,u_char * end)2302 nxt_http_route_cookie_parse(nxt_array_t *cookies, u_char *start, u_char *end)
2303 {
2304 size_t name_length;
2305 u_char c, *p, *name;
2306 nxt_http_name_value_t *nv;
2307
2308 name = NULL;
2309 name_length = 0;
2310
2311 for (p = start; p < end; p++) {
2312 c = *p;
2313
2314 if (c == '=') {
2315 while (start[0] == ' ') { start++; }
2316
2317 name_length = p - start;
2318
2319 if (name_length != 0) {
2320 name = start;
2321 }
2322
2323 start = p + 1;
2324
2325 } else if (c == ';') {
2326 if (name != NULL) {
2327 nv = nxt_http_route_cookie(cookies, name, name_length,
2328 start, p);
2329 if (nxt_slow_path(nv == NULL)) {
2330 return NXT_ERROR;
2331 }
2332 }
2333
2334 name = NULL;
2335 start = p + 1;
2336 }
2337 }
2338
2339 if (name != NULL) {
2340 nv = nxt_http_route_cookie(cookies, name, name_length, start, p);
2341 if (nxt_slow_path(nv == NULL)) {
2342 return NXT_ERROR;
2343 }
2344 }
2345
2346 return NXT_OK;
2347 }
2348
2349
2350 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)2351 nxt_http_route_cookie(nxt_array_t *array, u_char *name, size_t name_length,
2352 u_char *start, u_char *end)
2353 {
2354 u_char c, *p;
2355 uint32_t hash;
2356 nxt_http_name_value_t *nv;
2357
2358 nv = nxt_array_add(array);
2359 if (nxt_slow_path(nv == NULL)) {
2360 return NULL;
2361 }
2362
2363 nv->name_length = name_length;
2364 nv->name = name;
2365
2366 hash = NXT_HTTP_FIELD_HASH_INIT;
2367
2368 for (p = name; p < name + name_length; p++) {
2369 c = *p;
2370 hash = nxt_http_field_hash_char(hash, c);
2371 }
2372
2373 nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF;
2374
2375 while (start < end && end[-1] == ' ') { end--; }
2376
2377 nv->value_length = end - start;
2378 nv->value = start;
2379
2380 return nv;
2381 }
2382
2383
2384 static nxt_int_t
nxt_http_route_test_cookie(nxt_http_request_t * r,nxt_http_route_rule_t * rule,nxt_array_t * array)2385 nxt_http_route_test_cookie(nxt_http_request_t *r,
2386 nxt_http_route_rule_t *rule, nxt_array_t *array)
2387 {
2388 nxt_int_t ret;
2389 nxt_http_name_value_t *nv, *end;
2390
2391 ret = 0;
2392
2393 nv = array->elts;
2394 end = nv + array->nelts;
2395
2396 while (nv < end) {
2397
2398 if (rule->u.name.hash == nv->hash
2399 && rule->u.name.length == nv->name_length
2400 && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
2401 {
2402 ret = nxt_http_route_test_rule(r, rule, nv->value,
2403 nv->value_length);
2404 if (nxt_slow_path(ret == NXT_ERROR)) {
2405 return NXT_ERROR;
2406 }
2407
2408 if (ret == 0) {
2409 break;
2410 }
2411 }
2412
2413 nv++;
2414 }
2415
2416 return ret;
2417 }
2418
2419
2420 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)2421 nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule,
2422 u_char *start, size_t length)
2423 {
2424 nxt_int_t ret;
2425 nxt_http_route_pattern_t *pattern, *end;
2426
2427 ret = 1;
2428 pattern = &rule->pattern[0];
2429 end = pattern + rule->items;
2430
2431 while (pattern < end) {
2432 ret = nxt_http_route_pattern(r, pattern, start, length);
2433 if (nxt_slow_path(ret == NXT_ERROR)) {
2434 return NXT_ERROR;
2435 }
2436
2437 /* nxt_http_route_pattern() returns either 1 or 0. */
2438 ret ^= pattern->negative;
2439
2440 if (pattern->any == ret) {
2441 return ret;
2442 }
2443
2444 pattern++;
2445 }
2446
2447 return ret;
2448 }
2449
2450
2451 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)2452 nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern,
2453 u_char *start, size_t length)
2454 {
2455 u_char *p, *end, *test;
2456 size_t test_length;
2457 uint32_t i;
2458 nxt_array_t *pattern_slices;
2459 nxt_http_route_pattern_slice_t *pattern_slice;
2460
2461 #if (NXT_HAVE_REGEX)
2462 if (pattern->regex) {
2463 if (r->regex_match == NULL) {
2464 r->regex_match = nxt_regex_match_create(r->mem_pool, 0);
2465 if (nxt_slow_path(r->regex_match == NULL)) {
2466 return NXT_ERROR;
2467 }
2468 }
2469
2470 return nxt_regex_match(pattern->u.regex, start, length, r->regex_match);
2471 }
2472 #endif
2473
2474 if (length < pattern->min_length) {
2475 return 0;
2476 }
2477
2478 nxt_assert(pattern->u.pattern_slices != NULL);
2479
2480 pattern_slices = pattern->u.pattern_slices;
2481 pattern_slice = pattern_slices->elts;
2482 end = start + length;
2483
2484 for (i = 0; i < pattern_slices->nelts; i++, pattern_slice++) {
2485 test = pattern_slice->start;
2486 test_length = pattern_slice->length;
2487
2488 switch (pattern_slice->type) {
2489 case NXT_HTTP_ROUTE_PATTERN_EXACT:
2490 return ((length == pattern->min_length) &&
2491 nxt_http_route_memcmp(start, test, test_length,
2492 pattern->case_sensitive));
2493
2494 case NXT_HTTP_ROUTE_PATTERN_BEGIN:
2495 if (nxt_http_route_memcmp(start, test, test_length,
2496 pattern->case_sensitive))
2497 {
2498 start += test_length;
2499 break;
2500 }
2501
2502 return 0;
2503
2504 case NXT_HTTP_ROUTE_PATTERN_END:
2505 p = end - test_length;
2506
2507 if (nxt_http_route_memcmp(p, test, test_length,
2508 pattern->case_sensitive))
2509 {
2510 end = p;
2511 break;
2512 }
2513
2514 return 0;
2515
2516 case NXT_HTTP_ROUTE_PATTERN_SUBSTRING:
2517 if (pattern->case_sensitive) {
2518 p = nxt_memstrn(start, end, (char *) test, test_length);
2519
2520 } else {
2521 p = nxt_memcasestrn(start, end, (char *) test, test_length);
2522 }
2523
2524 if (p == NULL) {
2525 return 0;
2526 }
2527
2528 start = p + test_length;
2529 }
2530 }
2531
2532 return 1;
2533 }
2534
2535
2536 static nxt_int_t
nxt_http_route_memcmp(u_char * start,u_char * test,size_t test_length,nxt_bool_t case_sensitive)2537 nxt_http_route_memcmp(u_char *start, u_char *test, size_t test_length,
2538 nxt_bool_t case_sensitive)
2539 {
2540 nxt_int_t n;
2541
2542 if (case_sensitive) {
2543 n = nxt_memcmp(start, test, test_length);
2544
2545 } else {
2546 n = nxt_memcasecmp(start, test, test_length);
2547 }
2548
2549 return (n == 0);
2550 }
2551