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