xref: /unit/src/nxt_http_route.c (revision 2247:baa6b9879267)
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_query_ready(nxt_task_t *task, void *obj, void *data);
197 static void nxt_http_pass_query_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 *
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 *
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 *
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
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_router_conf_t       *rtcf;
647     nxt_http_action_conf_t  acf;
648 
649     nxt_memzero(&acf, sizeof(acf));
650 
651     ret = nxt_conf_map_object(tmcf->mem_pool, cv, nxt_http_route_action_conf,
652                               nxt_nitems(nxt_http_route_action_conf), &acf);
653     if (ret != NXT_OK) {
654         return ret;
655     }
656 
657     nxt_memzero(action, sizeof(nxt_http_action_t));
658 
659     rtcf = tmcf->router_conf;
660     mp = rtcf->mem_pool;
661 
662     if (acf.ret != NULL) {
663         return nxt_http_return_init(rtcf, action, &acf);
664     }
665 
666     if (acf.share != NULL) {
667         return nxt_http_static_init(task, tmcf, action, &acf);
668     }
669 
670     if (acf.proxy != NULL) {
671         return nxt_http_proxy_init(mp, action, &acf);
672     }
673 
674     nxt_conf_get_string(acf.pass, &pass);
675 
676     action->u.tstr = nxt_tstr_compile(rtcf->tstr_state, &pass, 0);
677     if (nxt_slow_path(action->u.tstr == NULL)) {
678         return NXT_ERROR;
679     }
680 
681     return NXT_OK;
682 }
683 
684 
685 static nxt_http_route_table_t *
686 nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp,
687     nxt_conf_value_t *table_cv, nxt_http_route_object_t object,
688     nxt_bool_t case_sensitive, nxt_http_uri_encoding_t encoding)
689 {
690     size_t                    size;
691     uint32_t                  i, n;
692     nxt_conf_value_t          *ruleset_cv;
693     nxt_http_route_table_t    *table;
694     nxt_http_route_ruleset_t  *ruleset;
695 
696     n = nxt_conf_array_elements_count_or_1(table_cv);
697     size = sizeof(nxt_http_route_table_t)
698            + n * sizeof(nxt_http_route_ruleset_t *);
699 
700     table = nxt_mp_alloc(mp, size);
701     if (nxt_slow_path(table == NULL)) {
702         return NULL;
703     }
704 
705     table->items = n;
706     table->object = NXT_HTTP_ROUTE_TABLE;
707 
708     for (i = 0; i < n; i++) {
709         ruleset_cv = nxt_conf_get_array_element_or_itself(table_cv, i);
710 
711         ruleset = nxt_http_route_ruleset_create(task, mp, ruleset_cv, object,
712                                                 case_sensitive, encoding);
713         if (nxt_slow_path(ruleset == NULL)) {
714             return NULL;
715         }
716 
717         table->ruleset[i] = ruleset;
718     }
719 
720     return table;
721 }
722 
723 
724 static nxt_http_route_ruleset_t *
725 nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp,
726     nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object,
727     nxt_bool_t case_sensitive, nxt_http_uri_encoding_t encoding)
728 {
729     size_t                    size;
730     uint32_t                  i, n, next;
731     nxt_str_t                 name;
732     nxt_conf_value_t          *rule_cv;
733     nxt_http_route_rule_t     *rule;
734     nxt_http_route_ruleset_t  *ruleset;
735 
736     n = nxt_conf_object_members_count(ruleset_cv);
737     size = sizeof(nxt_http_route_ruleset_t)
738            + n * sizeof(nxt_http_route_rule_t *);
739 
740     ruleset = nxt_mp_alloc(mp, size);
741     if (nxt_slow_path(ruleset == NULL)) {
742         return NULL;
743     }
744 
745     ruleset->items = n;
746 
747     next = 0;
748 
749     /*
750      * A workaround for GCC 10 with -flto -O2 flags that warns about "name"
751      * may be uninitialized in nxt_http_route_rule_name_create().
752      */
753     nxt_str_null(&name);
754 
755     for (i = 0; i < n; i++) {
756         rule_cv = nxt_conf_next_object_member(ruleset_cv, &name, &next);
757 
758         rule = nxt_http_route_rule_name_create(task, mp, rule_cv, &name,
759                                                case_sensitive, encoding);
760         if (nxt_slow_path(rule == NULL)) {
761             return NULL;
762         }
763 
764         rule->object = object;
765         ruleset->rule[i] = rule;
766     }
767 
768     return ruleset;
769 }
770 
771 
772 static nxt_http_route_rule_t *
773 nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp,
774     nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive,
775     nxt_http_uri_encoding_t encoding)
776 {
777     int64_t                hash;
778     nxt_http_route_rule_t  *rule;
779 
780     rule = nxt_http_route_rule_create(task, mp, rule_cv, case_sensitive,
781                                       NXT_HTTP_ROUTE_PATTERN_NOCASE,
782                                       encoding);
783     if (nxt_slow_path(rule == NULL)) {
784         return NULL;
785     }
786 
787     hash = nxt_http_field_hash(mp, name, case_sensitive, encoding);
788     if (nxt_slow_path(hash == -1)) {
789         return NULL;
790     }
791 
792     rule->u.name.hash = hash;
793     rule->u.name.start = name->start;
794     rule->u.name.length = name->length;
795 
796     return rule;
797 }
798 
799 
800 static nxt_http_route_rule_t *
801 nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp,
802     nxt_conf_value_t *cv, nxt_bool_t case_sensitive,
803     nxt_http_route_pattern_case_t pattern_case,
804     nxt_http_uri_encoding_t encoding)
805 {
806     size_t                    size;
807     uint32_t                  i, n;
808     nxt_int_t                 ret;
809     nxt_conf_value_t          *value;
810     nxt_http_route_rule_t     *rule;
811     nxt_http_route_pattern_t  *pattern;
812 
813     n = nxt_conf_array_elements_count_or_1(cv);
814     size = sizeof(nxt_http_route_rule_t) + n * sizeof(nxt_http_route_pattern_t);
815 
816     rule = nxt_mp_alloc(mp, size);
817     if (nxt_slow_path(rule == NULL)) {
818         return NULL;
819     }
820 
821     rule->items = n;
822 
823     pattern = &rule->pattern[0];
824 
825     nxt_conf_array_qsort(cv, nxt_http_pattern_compare);
826 
827     for (i = 0; i < n; i++) {
828         pattern[i].case_sensitive = case_sensitive;
829         value = nxt_conf_get_array_element_or_itself(cv, i);
830 
831         ret = nxt_http_route_pattern_create(task, mp, value, &pattern[i],
832                                             pattern_case, encoding);
833         if (nxt_slow_path(ret != NXT_OK)) {
834             return NULL;
835         }
836     }
837 
838     return rule;
839 }
840 
841 
842 nxt_http_route_addr_rule_t *
843 nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp,
844     nxt_conf_value_t *cv)
845 {
846     size_t                         size;
847     uint32_t                       i, n;
848     nxt_conf_value_t               *value;
849     nxt_http_route_addr_rule_t     *addr_rule;
850     nxt_http_route_addr_pattern_t  *pattern;
851 
852     n = nxt_conf_array_elements_count_or_1(cv);
853 
854     size = sizeof(nxt_http_route_addr_rule_t)
855            + n * sizeof(nxt_http_route_addr_pattern_t);
856 
857     addr_rule = nxt_mp_alloc(mp, size);
858     if (nxt_slow_path(addr_rule == NULL)) {
859         return NULL;
860     }
861 
862     addr_rule->items = n;
863 
864     for (i = 0; i < n; i++) {
865         pattern = &addr_rule->addr_pattern[i];
866         value = nxt_conf_get_array_element_or_itself(cv, i);
867 
868         if (nxt_http_route_addr_pattern_parse(mp, pattern, value) != NXT_OK) {
869             return NULL;
870         }
871     }
872 
873     if (n > 1) {
874         nxt_qsort(addr_rule->addr_pattern, addr_rule->items,
875             sizeof(nxt_http_route_addr_pattern_t),
876             nxt_http_addr_pattern_compare);
877     }
878 
879     return addr_rule;
880 }
881 
882 
883 nxt_http_route_rule_t *
884 nxt_http_route_types_rule_create(nxt_task_t *task, nxt_mp_t *mp,
885     nxt_conf_value_t *types)
886 {
887     return nxt_http_route_rule_create(task, mp, types, 0,
888                                       NXT_HTTP_ROUTE_PATTERN_LOWCASE,
889                                       NXT_HTTP_URI_ENCODING_NONE);
890 }
891 
892 
893 static int
894 nxt_http_pattern_compare(const void *one, const void *two)
895 {
896     nxt_str_t         test;
897     nxt_bool_t        negative1, negative2;
898     nxt_conf_value_t  *value;
899 
900     value = (nxt_conf_value_t *) one;
901     nxt_conf_get_string(value, &test);
902     negative1 = (test.length != 0 && test.start[0] == '!');
903 
904     value = (nxt_conf_value_t *) two;
905     nxt_conf_get_string(value, &test);
906     negative2 = (test.length != 0 && test.start[0] == '!');
907 
908     return (negative2 - negative1);
909 }
910 
911 
912 static int
913 nxt_http_addr_pattern_compare(const void *one, const void *two)
914 {
915     const nxt_http_route_addr_pattern_t  *p1, *p2;
916 
917     p1 = one;
918     p2 = two;
919 
920     return (p2->base.negative - p1->base.negative);
921 }
922 
923 
924 static nxt_int_t
925 nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
926     nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern,
927     nxt_http_route_pattern_case_t pattern_case,
928     nxt_http_uri_encoding_t encoding)
929 {
930     u_char                          c, *p, *end;
931     nxt_str_t                       test, tmp;
932     nxt_int_t                       ret;
933     nxt_array_t                     *slices;
934 #if (NXT_HAVE_REGEX)
935     nxt_regex_t                     *re;
936     nxt_regex_err_t                 err;
937 #endif
938     nxt_http_route_pattern_type_t   type;
939     nxt_http_route_pattern_slice_t  *slice;
940 
941     type = NXT_HTTP_ROUTE_PATTERN_EXACT;
942 
943     nxt_conf_get_string(cv, &test);
944 
945     pattern->u.pattern_slices = NULL;
946     pattern->negative = 0;
947     pattern->any = 1;
948     pattern->min_length = 0;
949 #if (NXT_HAVE_REGEX)
950     pattern->regex = 0;
951 #endif
952 
953     if (test.length != 0 && test.start[0] == '!') {
954         test.start++;
955         test.length--;
956 
957         pattern->negative = 1;
958         pattern->any = 0;
959     }
960 
961     if (test.length > 0 && test.start[0] == '~') {
962 #if (NXT_HAVE_REGEX)
963         test.start++;
964         test.length--;
965 
966         re = nxt_regex_compile(mp, &test, &err);
967         if (nxt_slow_path(re == NULL)) {
968             if (err.offset < test.length) {
969                 nxt_alert(task, "nxt_regex_compile(%V) failed: %s at offset %d",
970                           &test, err.msg, (int) err.offset);
971                 return NXT_ERROR;
972             }
973 
974             nxt_alert(task, "nxt_regex_compile(%V) failed %s", &test, err.msg);
975 
976             return NXT_ERROR;
977         }
978 
979         pattern->u.regex = re;
980         pattern->regex = 1;
981 
982         return NXT_OK;
983 
984 #else
985         return NXT_ERROR;
986 #endif
987     }
988 
989     slices = nxt_array_create(mp, 1, sizeof(nxt_http_route_pattern_slice_t));
990     if (nxt_slow_path(slices == NULL)) {
991         return NXT_ERROR;
992     }
993 
994     pattern->u.pattern_slices = slices;
995 
996     if (test.length == 0) {
997         slice = nxt_array_add(slices);
998         if (nxt_slow_path(slice == NULL)) {
999             return NXT_ERROR;
1000         }
1001 
1002         slice->type = NXT_HTTP_ROUTE_PATTERN_EXACT;
1003         slice->start = NULL;
1004         slice->length = 0;
1005 
1006         return NXT_OK;
1007     }
1008 
1009     if (test.start[0] == '*') {
1010         /* 'type' is no longer 'EXACT', assume 'END'. */
1011         type = NXT_HTTP_ROUTE_PATTERN_END;
1012         test.start++;
1013         test.length--;
1014     }
1015 
1016     if (type == NXT_HTTP_ROUTE_PATTERN_EXACT) {
1017         tmp.start = test.start;
1018 
1019         p = memchr(test.start, '*', test.length);
1020 
1021         if (p == NULL) {
1022             /* No '*' found - EXACT pattern. */
1023             tmp.length = test.length;
1024             type = NXT_HTTP_ROUTE_PATTERN_EXACT;
1025 
1026             test.start += test.length;
1027             test.length = 0;
1028 
1029         } else {
1030             /* '*' found - BEGIN pattern. */
1031             tmp.length = p - test.start;
1032             type = NXT_HTTP_ROUTE_PATTERN_BEGIN;
1033 
1034             test.start = p + 1;
1035             test.length -= tmp.length + 1;
1036         }
1037 
1038         ret = nxt_http_route_pattern_slice(slices, &tmp, type, encoding,
1039                                            pattern_case);
1040         if (nxt_slow_path(ret != NXT_OK)) {
1041             return ret;
1042         }
1043 
1044         pattern->min_length += tmp.length;
1045     }
1046 
1047     end = test.start + test.length;
1048 
1049     if (test.length != 0 && end[-1] != '*') {
1050         p = end - 1;
1051 
1052         while (p != test.start) {
1053             c = *p--;
1054 
1055             if (c == '*') {
1056                 p += 2;
1057                 break;
1058             }
1059         }
1060 
1061         tmp.start = p;
1062         tmp.length = end - p;
1063 
1064         test.length -= tmp.length;
1065         end = p;
1066 
1067         ret = nxt_http_route_pattern_slice(slices, &tmp,
1068                                            NXT_HTTP_ROUTE_PATTERN_END,
1069                                            encoding, pattern_case);
1070         if (nxt_slow_path(ret != NXT_OK)) {
1071             return ret;
1072         }
1073 
1074         pattern->min_length += tmp.length;
1075     }
1076 
1077     tmp.start = test.start;
1078     tmp.length = 0;
1079 
1080     p = tmp.start;
1081 
1082     while (p != end) {
1083         c = *p++;
1084 
1085         if (c != '*') {
1086             tmp.length++;
1087             continue;
1088         }
1089 
1090         if (tmp.length == 0) {
1091             tmp.start = p;
1092             continue;
1093         }
1094 
1095         ret = nxt_http_route_pattern_slice(slices, &tmp,
1096                                            NXT_HTTP_ROUTE_PATTERN_SUBSTRING,
1097                                            encoding, pattern_case);
1098         if (nxt_slow_path(ret != NXT_OK)) {
1099             return ret;
1100         }
1101 
1102         pattern->min_length += tmp.length;
1103 
1104         tmp.start = p;
1105         tmp.length = 0;
1106     }
1107 
1108     if (tmp.length != 0) {
1109         ret = nxt_http_route_pattern_slice(slices, &tmp,
1110                                            NXT_HTTP_ROUTE_PATTERN_SUBSTRING,
1111                                            encoding, pattern_case);
1112         if (nxt_slow_path(ret != NXT_OK)) {
1113             return ret;
1114         }
1115 
1116         pattern->min_length += tmp.length;
1117     }
1118 
1119     return NXT_OK;
1120 }
1121 
1122 
1123 static nxt_int_t
1124 nxt_http_route_decode_str(nxt_str_t *str, nxt_http_uri_encoding_t encoding)
1125 {
1126     u_char  *start, *end;
1127 
1128     switch (encoding) {
1129     case NXT_HTTP_URI_ENCODING_NONE:
1130         break;
1131 
1132     case NXT_HTTP_URI_ENCODING:
1133         start = str->start;
1134 
1135         end = nxt_decode_uri(start, start, str->length);
1136         if (nxt_slow_path(end == NULL)) {
1137             return NXT_ERROR;
1138         }
1139 
1140         str->length = end - start;
1141         break;
1142 
1143     case NXT_HTTP_URI_ENCODING_PLUS:
1144         start = str->start;
1145 
1146         end = nxt_decode_uri_plus(start, start, str->length);
1147         if (nxt_slow_path(end == NULL)) {
1148             return NXT_ERROR;
1149         }
1150 
1151         str->length = end - start;
1152         break;
1153 
1154     default:
1155         nxt_unreachable();
1156     }
1157 
1158     return NXT_OK;
1159 }
1160 
1161 
1162 static nxt_int_t
1163 nxt_http_route_pattern_slice(nxt_array_t *slices,
1164     nxt_str_t *test, nxt_http_route_pattern_type_t type,
1165     nxt_http_uri_encoding_t encoding,
1166     nxt_http_route_pattern_case_t pattern_case)
1167 {
1168     u_char                          *start;
1169     nxt_int_t                       ret;
1170     nxt_http_route_pattern_slice_t  *slice;
1171 
1172     ret = nxt_http_route_decode_str(test, encoding);
1173     if (nxt_slow_path(ret != NXT_OK)) {
1174         return ret;
1175     }
1176 
1177     start = nxt_mp_nget(slices->mem_pool, test->length);
1178     if (nxt_slow_path(start == NULL)) {
1179         return NXT_ERROR;
1180     }
1181 
1182     switch (pattern_case) {
1183 
1184     case NXT_HTTP_ROUTE_PATTERN_UPCASE:
1185         nxt_memcpy_upcase(start, test->start, test->length);
1186         break;
1187 
1188     case NXT_HTTP_ROUTE_PATTERN_LOWCASE:
1189         nxt_memcpy_lowcase(start, test->start, test->length);
1190         break;
1191 
1192     case NXT_HTTP_ROUTE_PATTERN_NOCASE:
1193         nxt_memcpy(start, test->start, test->length);
1194         break;
1195     }
1196 
1197     slice = nxt_array_add(slices);
1198     if (nxt_slow_path(slice == NULL)) {
1199         return NXT_ERROR;
1200     }
1201 
1202     slice->type = type;
1203     slice->start = start;
1204     slice->length = test->length;
1205 
1206     return NXT_OK;
1207 }
1208 
1209 
1210 nxt_int_t
1211 nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
1212 {
1213     nxt_int_t          ret;
1214     nxt_http_route_t   **route, **end;
1215     nxt_http_routes_t  *routes;
1216 
1217     routes = tmcf->router_conf->routes;
1218 
1219     if (routes != NULL) {
1220         route = &routes->route[0];
1221         end = route + routes->items;
1222 
1223         while (route < end) {
1224             ret = nxt_http_route_resolve(task, tmcf, *route);
1225             if (nxt_slow_path(ret != NXT_OK)) {
1226                 return NXT_ERROR;
1227             }
1228 
1229             route++;
1230         }
1231     }
1232 
1233     return NXT_OK;
1234 }
1235 
1236 
1237 static nxt_int_t
1238 nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1239     nxt_http_route_t *route)
1240 {
1241     nxt_int_t               ret;
1242     nxt_http_route_match_t  **match, **end;
1243 
1244     match = &route->match[0];
1245     end = match + route->items;
1246 
1247     while (match < end) {
1248         ret = nxt_http_action_resolve(task, tmcf, &(*match)->action);
1249         if (nxt_slow_path(ret != NXT_OK)) {
1250             return NXT_ERROR;
1251         }
1252 
1253         match++;
1254     }
1255 
1256     return NXT_OK;
1257 }
1258 
1259 
1260 static nxt_int_t
1261 nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1262     nxt_http_action_t *action)
1263 {
1264     nxt_int_t  ret;
1265     nxt_str_t  pass;
1266 
1267     if (action->handler != NULL) {
1268         if (action->fallback != NULL) {
1269             return nxt_http_action_resolve(task, tmcf, action->fallback);
1270         }
1271 
1272         return NXT_OK;
1273     }
1274 
1275     if (nxt_tstr_is_const(action->u.tstr)) {
1276         nxt_tstr_str(action->u.tstr, &pass);
1277 
1278         ret = nxt_http_pass_find(tmcf->mem_pool, tmcf->router_conf, &pass,
1279                                  action);
1280         if (nxt_slow_path(ret != NXT_OK)) {
1281             return NXT_ERROR;
1282         }
1283 
1284     } else {
1285         action->handler = nxt_http_pass_var;
1286     }
1287 
1288     return NXT_OK;
1289 }
1290 
1291 
1292 static nxt_http_action_t *
1293 nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r,
1294     nxt_http_action_t *action)
1295 {
1296     nxt_int_t          ret;
1297     nxt_str_t          str;
1298     nxt_tstr_t         *tstr;
1299     nxt_router_conf_t  *rtcf;
1300 
1301     tstr = action->u.tstr;
1302 
1303     nxt_tstr_str(tstr, &str);
1304 
1305     nxt_debug(task, "http pass: \"%V\"", &str);
1306 
1307     rtcf = r->conf->socket_conf->router_conf;
1308 
1309     ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, &r->tstr_cache,
1310                               r, r->mem_pool);
1311     if (nxt_slow_path(ret != NXT_OK)) {
1312         goto fail;
1313     }
1314 
1315     action = nxt_mp_get(r->mem_pool,
1316                         sizeof(nxt_http_action_t) + sizeof(nxt_str_t));
1317     if (nxt_slow_path(action == NULL)) {
1318         goto fail;
1319     }
1320 
1321     action->u.pass = nxt_pointer_to(action, sizeof(nxt_http_action_t));
1322 
1323     nxt_tstr_query(task, r->tstr_query, tstr, action->u.pass);
1324     nxt_tstr_query_resolve(task, r->tstr_query, action,
1325                            nxt_http_pass_query_ready,
1326                            nxt_http_pass_query_error);
1327     return NULL;
1328 
1329 fail:
1330 
1331     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
1332     return NULL;
1333 }
1334 
1335 
1336 static void
1337 nxt_http_pass_query_ready(nxt_task_t *task, void *obj, void *data)
1338 {
1339     nxt_int_t           ret;
1340     nxt_router_conf_t   *rtcf;
1341     nxt_http_action_t   *action;
1342     nxt_http_status_t   status;
1343     nxt_http_request_t  *r;
1344 
1345     r = obj;
1346     action = data;
1347     rtcf = r->conf->socket_conf->router_conf;
1348 
1349     nxt_debug(task, "http pass lookup: %V", action->u.pass);
1350 
1351     ret = nxt_http_pass_find(r->mem_pool, rtcf, action->u.pass, action);
1352 
1353     if (ret != NXT_OK) {
1354         status = (ret == NXT_DECLINED) ? NXT_HTTP_NOT_FOUND
1355                                        : NXT_HTTP_INTERNAL_SERVER_ERROR;
1356 
1357         nxt_http_request_error(task, r, status);
1358         return;
1359     }
1360 
1361     nxt_http_request_action(task, r, action);
1362 }
1363 
1364 
1365 static void
1366 nxt_http_pass_query_error(nxt_task_t *task, void *obj, void *data)
1367 {
1368     nxt_http_request_t  *r;
1369 
1370     r = obj;
1371 
1372     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
1373 }
1374 
1375 
1376 static nxt_int_t
1377 nxt_http_pass_find(nxt_mp_t *mp, nxt_router_conf_t *rtcf, nxt_str_t *pass,
1378     nxt_http_action_t *action)
1379 {
1380     nxt_int_t  ret;
1381     nxt_str_t  segments[3];
1382 
1383     ret = nxt_http_pass_segments(mp, pass, segments, 3);
1384     if (nxt_slow_path(ret != NXT_OK)) {
1385         return ret;
1386     }
1387 
1388     if (nxt_str_eq(&segments[0], "applications", 12)) {
1389         return nxt_router_application_init(rtcf, &segments[1], &segments[2],
1390                                            action);
1391     }
1392 
1393     if (segments[2].length == 0) {
1394         if (nxt_str_eq(&segments[0], "upstreams", 9)) {
1395             return nxt_upstream_find(rtcf->upstreams, &segments[1], action);
1396         }
1397 
1398         if (nxt_str_eq(&segments[0], "routes", 6)) {
1399             return nxt_http_route_find(rtcf->routes, &segments[1], action);
1400         }
1401     }
1402 
1403     return NXT_DECLINED;
1404 }
1405 
1406 
1407 nxt_int_t
1408 nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments,
1409     nxt_uint_t n)
1410 {
1411     u_char     *p;
1412     nxt_str_t  rest;
1413 
1414     if (nxt_slow_path(nxt_str_dup(mp, &rest, pass) == NULL)) {
1415         return NXT_ERROR;
1416     }
1417 
1418     nxt_memzero(segments, n * sizeof(nxt_str_t));
1419 
1420     do {
1421         p = memchr(rest.start, '/', rest.length);
1422 
1423         if (p != NULL) {
1424             n--;
1425 
1426             if (n == 0) {
1427                 return NXT_DECLINED;
1428             }
1429 
1430             segments->length = p - rest.start;
1431             segments->start = rest.start;
1432 
1433             rest.length -= segments->length + 1;
1434             rest.start = p + 1;
1435 
1436         } else {
1437             n = 0;
1438             *segments = rest;
1439         }
1440 
1441         if (segments->length == 0) {
1442             return NXT_DECLINED;
1443         }
1444 
1445         p = nxt_decode_uri(segments->start, segments->start, segments->length);
1446         if (p == NULL) {
1447             return NXT_DECLINED;
1448         }
1449 
1450         segments->length = p - segments->start;
1451         segments++;
1452 
1453     } while (n);
1454 
1455     return NXT_OK;
1456 }
1457 
1458 
1459 static nxt_int_t
1460 nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name,
1461     nxt_http_action_t *action)
1462 {
1463     nxt_http_route_t  **route, **end;
1464 
1465     if (routes == NULL) {
1466         return NXT_DECLINED;
1467     }
1468 
1469     route = &routes->route[0];
1470     end = route + routes->items;
1471 
1472     while (route < end) {
1473         if (nxt_strstr_eq(&(*route)->name, name)) {
1474             action->u.route = *route;
1475             action->handler = nxt_http_route_handler;
1476 
1477             return NXT_OK;
1478         }
1479 
1480         route++;
1481     }
1482 
1483     return NXT_DECLINED;
1484 }
1485 
1486 
1487 nxt_http_action_t *
1488 nxt_http_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1489     nxt_str_t *pass)
1490 {
1491     nxt_mp_t           *mp;
1492     nxt_int_t          ret;
1493     nxt_router_conf_t  *rtcf;
1494     nxt_http_action_t  *action;
1495 
1496     rtcf = tmcf->router_conf;
1497     mp = rtcf->mem_pool;
1498 
1499     action = nxt_mp_alloc(mp, sizeof(nxt_http_action_t));
1500     if (nxt_slow_path(action == NULL)) {
1501         return NULL;
1502     }
1503 
1504     action->u.tstr = nxt_tstr_compile(rtcf->tstr_state, pass, 0);
1505     if (nxt_slow_path(action->u.tstr == NULL)) {
1506         return NULL;
1507     }
1508 
1509     action->handler = NULL;
1510 
1511     ret = nxt_http_action_resolve(task, tmcf, action);
1512     if (nxt_slow_path(ret != NXT_OK)) {
1513         return NULL;
1514     }
1515 
1516     return action;
1517 }
1518 
1519 
1520 /* COMPATIBILITY: listener application. */
1521 
1522 nxt_http_action_t *
1523 nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf,
1524     nxt_str_t *name)
1525 {
1526     nxt_http_action_t  *action;
1527 
1528     action = nxt_mp_alloc(rtcf->mem_pool, sizeof(nxt_http_action_t));
1529     if (nxt_slow_path(action == NULL)) {
1530         return NULL;
1531     }
1532 
1533     (void) nxt_router_application_init(rtcf, name, NULL, action);
1534 
1535     return action;
1536 }
1537 
1538 
1539 static nxt_http_action_t *
1540 nxt_http_route_handler(nxt_task_t *task, nxt_http_request_t *r,
1541     nxt_http_action_t *start)
1542 {
1543     nxt_http_route_t        *route;
1544     nxt_http_action_t       *action;
1545     nxt_http_route_match_t  **match, **end;
1546 
1547     route = start->u.route;
1548     match = &route->match[0];
1549     end = match + route->items;
1550 
1551     while (match < end) {
1552         action = nxt_http_route_match(task, r, *match);
1553         if (action != NULL) {
1554             return action;
1555         }
1556 
1557         match++;
1558     }
1559 
1560     nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND);
1561 
1562     return NULL;
1563 }
1564 
1565 
1566 static nxt_http_action_t *
1567 nxt_http_route_match(nxt_task_t *task, nxt_http_request_t *r,
1568     nxt_http_route_match_t *match)
1569 {
1570     nxt_int_t              ret;
1571     nxt_http_route_test_t  *test, *end;
1572 
1573     test = &match->test[0];
1574     end = test + match->items;
1575 
1576     while (test < end) {
1577         switch (test->rule->object) {
1578         case NXT_HTTP_ROUTE_TABLE:
1579             ret = nxt_http_route_table(r, test->table);
1580             break;
1581         case NXT_HTTP_ROUTE_SOURCE:
1582             ret = nxt_http_route_addr_rule(r, test->addr_rule, r->remote);
1583             break;
1584         case NXT_HTTP_ROUTE_DESTINATION:
1585             if (r->local == NULL && nxt_fast_path(r->proto.any != NULL)) {
1586                 nxt_http_proto[r->protocol].local_addr(task, r);
1587             }
1588 
1589             ret = nxt_http_route_addr_rule(r, test->addr_rule, r->local);
1590             break;
1591         default:
1592             ret = nxt_http_route_rule(r, test->rule);
1593             break;
1594         }
1595 
1596         if (ret <= 0) {
1597             /* 0 => NULL, -1 => NXT_HTTP_ACTION_ERROR. */
1598             return (nxt_http_action_t *) (intptr_t) ret;
1599         }
1600 
1601         test++;
1602     }
1603 
1604     return &match->action;
1605 }
1606 
1607 
1608 static nxt_int_t
1609 nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table)
1610 {
1611     nxt_int_t                 ret;
1612     nxt_http_route_ruleset_t  **ruleset, **end;
1613 
1614     ret = 1;
1615     ruleset = &table->ruleset[0];
1616     end = ruleset + table->items;
1617 
1618     while (ruleset < end) {
1619         ret = nxt_http_route_ruleset(r, *ruleset);
1620 
1621         if (ret != 0) {
1622             return ret;
1623         }
1624 
1625         ruleset++;
1626     }
1627 
1628     return ret;
1629 }
1630 
1631 
1632 static nxt_int_t
1633 nxt_http_route_ruleset(nxt_http_request_t *r, nxt_http_route_ruleset_t *ruleset)
1634 {
1635     nxt_int_t              ret;
1636     nxt_http_route_rule_t  **rule, **end;
1637 
1638     rule = &ruleset->rule[0];
1639     end = rule + ruleset->items;
1640 
1641     while (rule < end) {
1642         ret = nxt_http_route_rule(r, *rule);
1643 
1644         if (ret <= 0) {
1645             return ret;
1646         }
1647 
1648         rule++;
1649     }
1650 
1651     return 1;
1652 }
1653 
1654 
1655 static nxt_int_t
1656 nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1657 {
1658     void       *p, **pp;
1659     u_char     *start;
1660     size_t     length;
1661     nxt_str_t  *s;
1662 
1663     switch (rule->object) {
1664 
1665     case NXT_HTTP_ROUTE_HEADER:
1666         return nxt_http_route_header(r, rule);
1667 
1668     case NXT_HTTP_ROUTE_ARGUMENT:
1669         return nxt_http_route_arguments(r, rule);
1670 
1671     case NXT_HTTP_ROUTE_COOKIE:
1672         return nxt_http_route_cookies(r, rule);
1673 
1674     case NXT_HTTP_ROUTE_SCHEME:
1675         return nxt_http_route_scheme(r, rule);
1676 
1677     case NXT_HTTP_ROUTE_QUERY:
1678         return nxt_http_route_query(r, rule);
1679 
1680     default:
1681         break;
1682     }
1683 
1684     p = nxt_pointer_to(r, rule->u.offset);
1685 
1686     if (rule->object == NXT_HTTP_ROUTE_STRING) {
1687         s = p;
1688 
1689     } else {
1690         /* NXT_HTTP_ROUTE_STRING_PTR */
1691         pp = p;
1692         s = *pp;
1693 
1694         if (s == NULL) {
1695             return 0;
1696         }
1697     }
1698 
1699     length = s->length;
1700     start = s->start;
1701 
1702     return nxt_http_route_test_rule(r, rule, start, length);
1703 }
1704 
1705 
1706 static nxt_int_t
1707 nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p,
1708     nxt_sockaddr_t *sa)
1709 {
1710 #if (NXT_INET6)
1711     uint32_t                    i;
1712 #endif
1713     in_port_t                   in_port;
1714     nxt_int_t                   match;
1715     struct sockaddr_in          *sin;
1716 #if (NXT_INET6)
1717     struct sockaddr_in6         *sin6;
1718 #endif
1719     nxt_http_route_addr_base_t  *base;
1720 
1721     base = &p->base;
1722 
1723     switch (sa->u.sockaddr.sa_family) {
1724 
1725     case AF_INET:
1726 
1727         match = (base->addr_family == AF_INET
1728                  || base->addr_family == AF_UNSPEC);
1729         if (!match) {
1730             break;
1731         }
1732 
1733         sin = &sa->u.sockaddr_in;
1734         in_port = ntohs(sin->sin_port);
1735 
1736         match = (in_port >= base->port.start && in_port <= base->port.end);
1737         if (!match) {
1738             break;
1739         }
1740 
1741         switch (base->match_type) {
1742 
1743         case NXT_HTTP_ROUTE_ADDR_ANY:
1744             break;
1745 
1746         case NXT_HTTP_ROUTE_ADDR_EXACT:
1747             match = (memcmp(&sin->sin_addr, &p->addr.v4.start,
1748                                 sizeof(struct in_addr))
1749                      == 0);
1750             break;
1751 
1752         case NXT_HTTP_ROUTE_ADDR_RANGE:
1753             match = (memcmp(&sin->sin_addr, &p->addr.v4.start,
1754                                 sizeof(struct in_addr)) >= 0
1755                      && memcmp(&sin->sin_addr, &p->addr.v4.end,
1756                                    sizeof(struct in_addr)) <= 0);
1757             break;
1758 
1759         case NXT_HTTP_ROUTE_ADDR_CIDR:
1760             match = ((sin->sin_addr.s_addr & p->addr.v4.end)
1761                      == p->addr.v4.start);
1762             break;
1763 
1764         default:
1765             nxt_unreachable();
1766         }
1767 
1768         break;
1769 
1770 #if (NXT_INET6)
1771     case AF_INET6:
1772 
1773         match = (base->addr_family == AF_INET6
1774                  || base->addr_family == AF_UNSPEC);
1775         if (!match) {
1776             break;
1777         }
1778 
1779         sin6 = &sa->u.sockaddr_in6;
1780         in_port = ntohs(sin6->sin6_port);
1781 
1782         match = (in_port >= base->port.start && in_port <= base->port.end);
1783         if (!match) {
1784             break;
1785         }
1786 
1787         switch (base->match_type) {
1788 
1789         case NXT_HTTP_ROUTE_ADDR_ANY:
1790             break;
1791 
1792         case NXT_HTTP_ROUTE_ADDR_EXACT:
1793             match = (memcmp(&sin6->sin6_addr, &p->addr.v6.start,
1794                                 sizeof(struct in6_addr))
1795                      == 0);
1796             break;
1797 
1798         case NXT_HTTP_ROUTE_ADDR_RANGE:
1799             match = (memcmp(&sin6->sin6_addr, &p->addr.v6.start,
1800                                 sizeof(struct in6_addr)) >= 0
1801                      && memcmp(&sin6->sin6_addr, &p->addr.v6.end,
1802                                    sizeof(struct in6_addr)) <= 0);
1803             break;
1804 
1805         case NXT_HTTP_ROUTE_ADDR_CIDR:
1806             for (i = 0; i < 16; i++) {
1807                 match = ((sin6->sin6_addr.s6_addr[i]
1808                           & p->addr.v6.end.s6_addr[i])
1809                          == p->addr.v6.start.s6_addr[i]);
1810 
1811                 if (!match) {
1812                     break;
1813                 }
1814             }
1815 
1816             break;
1817 
1818         default:
1819             nxt_unreachable();
1820         }
1821 
1822         break;
1823 #endif
1824 
1825 #if (NXT_HAVE_UNIX_DOMAIN)
1826     case AF_UNIX:
1827 
1828         match = (base->addr_family == AF_UNIX);
1829         break;
1830 #endif
1831 
1832     default:
1833         match = 0;
1834         break;
1835     }
1836 
1837     return match ^ base->negative;
1838 }
1839 
1840 
1841 nxt_int_t
1842 nxt_http_route_addr_rule(nxt_http_request_t *r,
1843     nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sa)
1844 {
1845     uint32_t                       n;
1846     nxt_bool_t                     matches;
1847     nxt_http_route_addr_pattern_t  *p;
1848 
1849     n = addr_rule->items;
1850 
1851     if (n == 0) {
1852         return 0;
1853     }
1854 
1855     p = &addr_rule->addr_pattern[0] - 1;
1856 
1857     do {
1858         p++;
1859         n--;
1860 
1861         matches = nxt_http_route_addr_pattern_match(p, sa);
1862 
1863         if (p->base.negative) {
1864             if (matches) {
1865                 continue;
1866             }
1867 
1868             return 0;
1869         }
1870 
1871         if (matches) {
1872             return 1;
1873         }
1874 
1875     } while (n > 0);
1876 
1877     return p->base.negative;
1878 }
1879 
1880 
1881 static nxt_int_t
1882 nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1883 {
1884     nxt_int_t         ret;
1885     nxt_http_field_t  *f;
1886 
1887     ret = 0;
1888 
1889     nxt_list_each(f, r->fields) {
1890 
1891         if (rule->u.name.hash != f->hash
1892             || rule->u.name.length != f->name_length
1893             || nxt_strncasecmp(rule->u.name.start, f->name, f->name_length)
1894                != 0)
1895         {
1896             continue;
1897         }
1898 
1899         ret = nxt_http_route_test_rule(r, rule, f->value, f->value_length);
1900         if (nxt_slow_path(ret == NXT_ERROR)) {
1901             return NXT_ERROR;
1902         }
1903 
1904         if (ret == 0) {
1905             return ret;
1906         }
1907 
1908     } nxt_list_loop;
1909 
1910     return ret;
1911 }
1912 
1913 
1914 static nxt_int_t
1915 nxt_http_route_arguments(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1916 {
1917     nxt_array_t  *arguments;
1918 
1919     arguments = nxt_http_arguments_parse(r);
1920     if (nxt_slow_path(arguments == NULL)) {
1921         return -1;
1922     }
1923 
1924     return nxt_http_route_test_argument(r, rule, arguments);
1925 }
1926 
1927 
1928 static nxt_int_t
1929 nxt_http_route_test_argument(nxt_http_request_t *r,
1930     nxt_http_route_rule_t *rule, nxt_array_t *array)
1931 {
1932     nxt_int_t              ret;
1933     nxt_http_name_value_t  *nv, *end;
1934 
1935     ret = 0;
1936 
1937     nv = array->elts;
1938     end = nv + array->nelts;
1939 
1940     while (nv < end) {
1941 
1942         if (rule->u.name.hash == nv->hash
1943             && rule->u.name.length == nv->name_length
1944             && memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
1945         {
1946             ret = nxt_http_route_test_rule(r, rule, nv->value,
1947                                            nv->value_length);
1948             if (nxt_slow_path(ret == NXT_ERROR)) {
1949                 return NXT_ERROR;
1950             }
1951 
1952             if (ret == 0) {
1953                 break;
1954             }
1955         }
1956 
1957         nv++;
1958     }
1959 
1960     return ret;
1961 }
1962 
1963 
1964 static nxt_int_t
1965 nxt_http_route_scheme(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1966 {
1967     nxt_bool_t                      https;
1968     nxt_http_route_pattern_slice_t  *pattern_slice;
1969 
1970     pattern_slice = rule->pattern[0].u.pattern_slices->elts;
1971     https = (pattern_slice->length == nxt_length("https"));
1972 
1973     return (r->tls == https);
1974 }
1975 
1976 
1977 static nxt_int_t
1978 nxt_http_route_query(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1979 {
1980     nxt_array_t  *arguments;
1981 
1982     arguments = nxt_http_arguments_parse(r);
1983     if (nxt_slow_path(arguments == NULL)) {
1984         return -1;
1985     }
1986 
1987     return nxt_http_route_test_rule(r, rule, r->args_decoded.start,
1988                                     r->args_decoded.length);
1989 }
1990 
1991 
1992 static nxt_int_t
1993 nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1994 {
1995     nxt_array_t  *cookies;
1996 
1997     cookies = nxt_http_cookies_parse(r);
1998     if (nxt_slow_path(cookies == NULL)) {
1999         return -1;
2000     }
2001 
2002     return nxt_http_route_test_cookie(r, rule, cookies);
2003 }
2004 
2005 
2006 static nxt_int_t
2007 nxt_http_route_test_cookie(nxt_http_request_t *r,
2008     nxt_http_route_rule_t *rule, nxt_array_t *array)
2009 {
2010     nxt_int_t              ret;
2011     nxt_http_name_value_t  *nv, *end;
2012 
2013     ret = 0;
2014 
2015     nv = array->elts;
2016     end = nv + array->nelts;
2017 
2018     while (nv < end) {
2019 
2020         if (rule->u.name.hash == nv->hash
2021             && rule->u.name.length == nv->name_length
2022             && memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
2023         {
2024             ret = nxt_http_route_test_rule(r, rule, nv->value,
2025                                            nv->value_length);
2026             if (nxt_slow_path(ret == NXT_ERROR)) {
2027                 return NXT_ERROR;
2028             }
2029 
2030             if (ret == 0) {
2031                 break;
2032             }
2033         }
2034 
2035         nv++;
2036     }
2037 
2038     return ret;
2039 }
2040 
2041 
2042 nxt_int_t
2043 nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule,
2044     u_char *start, size_t length)
2045 {
2046     nxt_int_t                 ret;
2047     nxt_http_route_pattern_t  *pattern, *end;
2048 
2049     ret = 1;
2050     pattern = &rule->pattern[0];
2051     end = pattern + rule->items;
2052 
2053     while (pattern < end) {
2054         ret = nxt_http_route_pattern(r, pattern, start, length);
2055         if (nxt_slow_path(ret == NXT_ERROR)) {
2056             return NXT_ERROR;
2057         }
2058 
2059         /* nxt_http_route_pattern() returns either 1 or 0. */
2060         ret ^= pattern->negative;
2061 
2062         if (pattern->any == ret) {
2063             return ret;
2064         }
2065 
2066         pattern++;
2067     }
2068 
2069     return ret;
2070 }
2071 
2072 
2073 static nxt_int_t
2074 nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern,
2075     u_char *start, size_t length)
2076 {
2077     u_char                          *p, *end, *test;
2078     size_t                          test_length;
2079     uint32_t                        i;
2080     nxt_array_t                     *pattern_slices;
2081     nxt_http_route_pattern_slice_t  *pattern_slice;
2082 
2083 #if (NXT_HAVE_REGEX)
2084     if (pattern->regex) {
2085         if (r->regex_match == NULL) {
2086             r->regex_match = nxt_regex_match_create(r->mem_pool, 0);
2087             if (nxt_slow_path(r->regex_match == NULL)) {
2088                 return NXT_ERROR;
2089             }
2090         }
2091 
2092         return nxt_regex_match(pattern->u.regex, start, length, r->regex_match);
2093     }
2094 #endif
2095 
2096     if (length < pattern->min_length) {
2097         return 0;
2098     }
2099 
2100     nxt_assert(pattern->u.pattern_slices != NULL);
2101 
2102     pattern_slices = pattern->u.pattern_slices;
2103     pattern_slice = pattern_slices->elts;
2104     end = start + length;
2105 
2106     for (i = 0; i < pattern_slices->nelts; i++, pattern_slice++) {
2107         test = pattern_slice->start;
2108         test_length = pattern_slice->length;
2109 
2110         switch (pattern_slice->type) {
2111         case NXT_HTTP_ROUTE_PATTERN_EXACT:
2112             return ((length == pattern->min_length) &&
2113                     nxt_http_route_memcmp(start, test, test_length,
2114                                           pattern->case_sensitive));
2115 
2116         case NXT_HTTP_ROUTE_PATTERN_BEGIN:
2117             if (nxt_http_route_memcmp(start, test, test_length,
2118                                       pattern->case_sensitive))
2119             {
2120                 start += test_length;
2121                 break;
2122             }
2123 
2124             return 0;
2125 
2126         case NXT_HTTP_ROUTE_PATTERN_END:
2127             p = end - test_length;
2128 
2129             if (nxt_http_route_memcmp(p, test, test_length,
2130                                       pattern->case_sensitive))
2131             {
2132                 end = p;
2133                 break;
2134             }
2135 
2136             return 0;
2137 
2138         case NXT_HTTP_ROUTE_PATTERN_SUBSTRING:
2139             if (pattern->case_sensitive) {
2140                 p = nxt_memstrn(start, end, (char *) test, test_length);
2141 
2142             } else {
2143                 p = nxt_memcasestrn(start, end, (char *) test, test_length);
2144             }
2145 
2146             if (p == NULL) {
2147                 return 0;
2148             }
2149 
2150             start = p + test_length;
2151         }
2152     }
2153 
2154     return 1;
2155 }
2156 
2157 
2158 static nxt_int_t
2159 nxt_http_route_memcmp(u_char *start, u_char *test, size_t test_length,
2160     nxt_bool_t case_sensitive)
2161 {
2162     nxt_int_t  n;
2163 
2164     if (case_sensitive) {
2165         n = memcmp(start, test, test_length);
2166 
2167     } else {
2168         n = nxt_memcasecmp(start, test, test_length);
2169     }
2170 
2171     return (n == 0);
2172 }
2173