xref: /unit/src/nxt_http_route.c (revision 2161:f8e608f69800)
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 *
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.var = nxt_var_compile(&pass, mp, rtcf->var_fields, 0);
677     if (nxt_slow_path(action->u.var == 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 = nxt_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_var_is_const(action->u.var)) {
1276         nxt_var_raw(action->u.var, &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_var_t  *var;
1299 
1300     var = action->u.var;
1301 
1302     nxt_var_raw(var, &str);
1303 
1304     nxt_debug(task, "http pass: \"%V\"", &str);
1305 
1306     ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
1307     if (nxt_slow_path(ret != NXT_OK)) {
1308         goto fail;
1309     }
1310 
1311     action = nxt_mp_get(r->mem_pool,
1312                         sizeof(nxt_http_action_t) + sizeof(nxt_str_t));
1313     if (nxt_slow_path(action == NULL)) {
1314         goto fail;
1315     }
1316 
1317     action->u.pass = nxt_pointer_to(action, sizeof(nxt_http_action_t));
1318 
1319     nxt_var_query(task, r->var_query, var, action->u.pass);
1320     nxt_var_query_resolve(task, r->var_query, action,
1321                           nxt_http_pass_var_ready,
1322                           nxt_http_pass_var_error);
1323     return NULL;
1324 
1325 fail:
1326 
1327     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
1328     return NULL;
1329 }
1330 
1331 
1332 static void
1333 nxt_http_pass_var_ready(nxt_task_t *task, void *obj, void *data)
1334 {
1335     nxt_int_t           ret;
1336     nxt_router_conf_t   *rtcf;
1337     nxt_http_action_t   *action;
1338     nxt_http_status_t   status;
1339     nxt_http_request_t  *r;
1340 
1341     r = obj;
1342     action = data;
1343     rtcf = r->conf->socket_conf->router_conf;
1344 
1345     nxt_debug(task, "http pass lookup: %V", action->u.pass);
1346 
1347     ret = nxt_http_pass_find(r->mem_pool, rtcf, action->u.pass, action);
1348 
1349     if (ret != NXT_OK) {
1350         status = (ret == NXT_DECLINED) ? NXT_HTTP_NOT_FOUND
1351                                        : NXT_HTTP_INTERNAL_SERVER_ERROR;
1352 
1353         nxt_http_request_error(task, r, status);
1354         return;
1355     }
1356 
1357     nxt_http_request_action(task, r, action);
1358 }
1359 
1360 
1361 static void
1362 nxt_http_pass_var_error(nxt_task_t *task, void *obj, void *data)
1363 {
1364     nxt_http_request_t  *r;
1365 
1366     r = obj;
1367 
1368     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
1369 }
1370 
1371 
1372 static nxt_int_t
1373 nxt_http_pass_find(nxt_mp_t *mp, nxt_router_conf_t *rtcf, nxt_str_t *pass,
1374     nxt_http_action_t *action)
1375 {
1376     nxt_int_t  ret;
1377     nxt_str_t  segments[3];
1378 
1379     ret = nxt_http_pass_segments(mp, pass, segments, 3);
1380     if (nxt_slow_path(ret != NXT_OK)) {
1381         return ret;
1382     }
1383 
1384     if (nxt_str_eq(&segments[0], "applications", 12)) {
1385         return nxt_router_application_init(rtcf, &segments[1], &segments[2],
1386                                            action);
1387     }
1388 
1389     if (segments[2].length == 0) {
1390         if (nxt_str_eq(&segments[0], "upstreams", 9)) {
1391             return nxt_upstream_find(rtcf->upstreams, &segments[1], action);
1392         }
1393 
1394         if (nxt_str_eq(&segments[0], "routes", 6)) {
1395             return nxt_http_route_find(rtcf->routes, &segments[1], action);
1396         }
1397     }
1398 
1399     return NXT_DECLINED;
1400 }
1401 
1402 
1403 nxt_int_t
1404 nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments,
1405     nxt_uint_t n)
1406 {
1407     u_char     *p;
1408     nxt_str_t  rest;
1409 
1410     if (nxt_slow_path(nxt_str_dup(mp, &rest, pass) == NULL)) {
1411         return NXT_ERROR;
1412     }
1413 
1414     nxt_memzero(segments, n * sizeof(nxt_str_t));
1415 
1416     do {
1417         p = nxt_memchr(rest.start, '/', rest.length);
1418 
1419         if (p != NULL) {
1420             n--;
1421 
1422             if (n == 0) {
1423                 return NXT_DECLINED;
1424             }
1425 
1426             segments->length = p - rest.start;
1427             segments->start = rest.start;
1428 
1429             rest.length -= segments->length + 1;
1430             rest.start = p + 1;
1431 
1432         } else {
1433             n = 0;
1434             *segments = rest;
1435         }
1436 
1437         if (segments->length == 0) {
1438             return NXT_DECLINED;
1439         }
1440 
1441         p = nxt_decode_uri(segments->start, segments->start, segments->length);
1442         if (p == NULL) {
1443             return NXT_DECLINED;
1444         }
1445 
1446         segments->length = p - segments->start;
1447         segments++;
1448 
1449     } while (n);
1450 
1451     return NXT_OK;
1452 }
1453 
1454 
1455 static nxt_int_t
1456 nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name,
1457     nxt_http_action_t *action)
1458 {
1459     nxt_http_route_t  **route, **end;
1460 
1461     if (routes == NULL) {
1462         return NXT_DECLINED;
1463     }
1464 
1465     route = &routes->route[0];
1466     end = route + routes->items;
1467 
1468     while (route < end) {
1469         if (nxt_strstr_eq(&(*route)->name, name)) {
1470             action->u.route = *route;
1471             action->handler = nxt_http_route_handler;
1472 
1473             return NXT_OK;
1474         }
1475 
1476         route++;
1477     }
1478 
1479     return NXT_DECLINED;
1480 }
1481 
1482 
1483 nxt_http_action_t *
1484 nxt_http_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1485     nxt_str_t *pass)
1486 {
1487     nxt_mp_t           *mp;
1488     nxt_int_t          ret;
1489     nxt_router_conf_t  *rtcf;
1490     nxt_http_action_t  *action;
1491 
1492     rtcf = tmcf->router_conf;
1493     mp = rtcf->mem_pool;
1494 
1495     action = nxt_mp_alloc(mp, sizeof(nxt_http_action_t));
1496     if (nxt_slow_path(action == NULL)) {
1497         return NULL;
1498     }
1499 
1500     action->u.var = nxt_var_compile(pass, mp, rtcf->var_fields, 0);
1501     if (nxt_slow_path(action->u.var == NULL)) {
1502         return NULL;
1503     }
1504 
1505     action->handler = NULL;
1506 
1507     ret = nxt_http_action_resolve(task, tmcf, action);
1508     if (nxt_slow_path(ret != NXT_OK)) {
1509         return NULL;
1510     }
1511 
1512     return action;
1513 }
1514 
1515 
1516 /* COMPATIBILITY: listener application. */
1517 
1518 nxt_http_action_t *
1519 nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf,
1520     nxt_str_t *name)
1521 {
1522     nxt_http_action_t  *action;
1523 
1524     action = nxt_mp_alloc(rtcf->mem_pool, sizeof(nxt_http_action_t));
1525     if (nxt_slow_path(action == NULL)) {
1526         return NULL;
1527     }
1528 
1529     (void) nxt_router_application_init(rtcf, name, NULL, action);
1530 
1531     return action;
1532 }
1533 
1534 
1535 static nxt_http_action_t *
1536 nxt_http_route_handler(nxt_task_t *task, nxt_http_request_t *r,
1537     nxt_http_action_t *start)
1538 {
1539     nxt_http_route_t        *route;
1540     nxt_http_action_t       *action;
1541     nxt_http_route_match_t  **match, **end;
1542 
1543     route = start->u.route;
1544     match = &route->match[0];
1545     end = match + route->items;
1546 
1547     while (match < end) {
1548         action = nxt_http_route_match(task, r, *match);
1549         if (action != NULL) {
1550             return action;
1551         }
1552 
1553         match++;
1554     }
1555 
1556     nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND);
1557 
1558     return NULL;
1559 }
1560 
1561 
1562 static nxt_http_action_t *
1563 nxt_http_route_match(nxt_task_t *task, nxt_http_request_t *r,
1564     nxt_http_route_match_t *match)
1565 {
1566     nxt_int_t              ret;
1567     nxt_http_route_test_t  *test, *end;
1568 
1569     test = &match->test[0];
1570     end = test + match->items;
1571 
1572     while (test < end) {
1573         switch (test->rule->object) {
1574         case NXT_HTTP_ROUTE_TABLE:
1575             ret = nxt_http_route_table(r, test->table);
1576             break;
1577         case NXT_HTTP_ROUTE_SOURCE:
1578             ret = nxt_http_route_addr_rule(r, test->addr_rule, r->remote);
1579             break;
1580         case NXT_HTTP_ROUTE_DESTINATION:
1581             if (r->local == NULL && nxt_fast_path(r->proto.any != NULL)) {
1582                 nxt_http_proto[r->protocol].local_addr(task, r);
1583             }
1584 
1585             ret = nxt_http_route_addr_rule(r, test->addr_rule, r->local);
1586             break;
1587         default:
1588             ret = nxt_http_route_rule(r, test->rule);
1589             break;
1590         }
1591 
1592         if (ret <= 0) {
1593             /* 0 => NULL, -1 => NXT_HTTP_ACTION_ERROR. */
1594             return (nxt_http_action_t *) (intptr_t) ret;
1595         }
1596 
1597         test++;
1598     }
1599 
1600     return &match->action;
1601 }
1602 
1603 
1604 static nxt_int_t
1605 nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table)
1606 {
1607     nxt_int_t                 ret;
1608     nxt_http_route_ruleset_t  **ruleset, **end;
1609 
1610     ret = 1;
1611     ruleset = &table->ruleset[0];
1612     end = ruleset + table->items;
1613 
1614     while (ruleset < end) {
1615         ret = nxt_http_route_ruleset(r, *ruleset);
1616 
1617         if (ret != 0) {
1618             return ret;
1619         }
1620 
1621         ruleset++;
1622     }
1623 
1624     return ret;
1625 }
1626 
1627 
1628 static nxt_int_t
1629 nxt_http_route_ruleset(nxt_http_request_t *r, nxt_http_route_ruleset_t *ruleset)
1630 {
1631     nxt_int_t              ret;
1632     nxt_http_route_rule_t  **rule, **end;
1633 
1634     rule = &ruleset->rule[0];
1635     end = rule + ruleset->items;
1636 
1637     while (rule < end) {
1638         ret = nxt_http_route_rule(r, *rule);
1639 
1640         if (ret <= 0) {
1641             return ret;
1642         }
1643 
1644         rule++;
1645     }
1646 
1647     return 1;
1648 }
1649 
1650 
1651 static nxt_int_t
1652 nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1653 {
1654     void       *p, **pp;
1655     u_char     *start;
1656     size_t     length;
1657     nxt_str_t  *s;
1658 
1659     switch (rule->object) {
1660 
1661     case NXT_HTTP_ROUTE_HEADER:
1662         return nxt_http_route_header(r, rule);
1663 
1664     case NXT_HTTP_ROUTE_ARGUMENT:
1665         return nxt_http_route_arguments(r, rule);
1666 
1667     case NXT_HTTP_ROUTE_COOKIE:
1668         return nxt_http_route_cookies(r, rule);
1669 
1670     case NXT_HTTP_ROUTE_SCHEME:
1671         return nxt_http_route_scheme(r, rule);
1672 
1673     case NXT_HTTP_ROUTE_QUERY:
1674         return nxt_http_route_query(r, rule);
1675 
1676     default:
1677         break;
1678     }
1679 
1680     p = nxt_pointer_to(r, rule->u.offset);
1681 
1682     if (rule->object == NXT_HTTP_ROUTE_STRING) {
1683         s = p;
1684 
1685     } else {
1686         /* NXT_HTTP_ROUTE_STRING_PTR */
1687         pp = p;
1688         s = *pp;
1689 
1690         if (s == NULL) {
1691             return 0;
1692         }
1693     }
1694 
1695     length = s->length;
1696     start = s->start;
1697 
1698     return nxt_http_route_test_rule(r, rule, start, length);
1699 }
1700 
1701 
1702 static nxt_int_t
1703 nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p,
1704     nxt_sockaddr_t *sa)
1705 {
1706 #if (NXT_INET6)
1707     uint32_t                    i;
1708 #endif
1709     in_port_t                   in_port;
1710     nxt_int_t                   match;
1711     struct sockaddr_in          *sin;
1712 #if (NXT_INET6)
1713     struct sockaddr_in6         *sin6;
1714 #endif
1715     nxt_http_route_addr_base_t  *base;
1716 
1717     base = &p->base;
1718 
1719     switch (sa->u.sockaddr.sa_family) {
1720 
1721     case AF_INET:
1722 
1723         match = (base->addr_family == AF_INET
1724                  || base->addr_family == AF_UNSPEC);
1725         if (!match) {
1726             break;
1727         }
1728 
1729         sin = &sa->u.sockaddr_in;
1730         in_port = ntohs(sin->sin_port);
1731 
1732         match = (in_port >= base->port.start && in_port <= base->port.end);
1733         if (!match) {
1734             break;
1735         }
1736 
1737         switch (base->match_type) {
1738 
1739         case NXT_HTTP_ROUTE_ADDR_ANY:
1740             break;
1741 
1742         case NXT_HTTP_ROUTE_ADDR_EXACT:
1743             match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
1744                                 sizeof(struct in_addr))
1745                      == 0);
1746             break;
1747 
1748         case NXT_HTTP_ROUTE_ADDR_RANGE:
1749             match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
1750                                 sizeof(struct in_addr)) >= 0
1751                      && nxt_memcmp(&sin->sin_addr, &p->addr.v4.end,
1752                                    sizeof(struct in_addr)) <= 0);
1753             break;
1754 
1755         case NXT_HTTP_ROUTE_ADDR_CIDR:
1756             match = ((sin->sin_addr.s_addr & p->addr.v4.end)
1757                      == p->addr.v4.start);
1758             break;
1759 
1760         default:
1761             nxt_unreachable();
1762         }
1763 
1764         break;
1765 
1766 #if (NXT_INET6)
1767     case AF_INET6:
1768 
1769         match = (base->addr_family == AF_INET6
1770                  || base->addr_family == AF_UNSPEC);
1771         if (!match) {
1772             break;
1773         }
1774 
1775         sin6 = &sa->u.sockaddr_in6;
1776         in_port = ntohs(sin6->sin6_port);
1777 
1778         match = (in_port >= base->port.start && in_port <= base->port.end);
1779         if (!match) {
1780             break;
1781         }
1782 
1783         switch (base->match_type) {
1784 
1785         case NXT_HTTP_ROUTE_ADDR_ANY:
1786             break;
1787 
1788         case NXT_HTTP_ROUTE_ADDR_EXACT:
1789             match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
1790                                 sizeof(struct in6_addr))
1791                      == 0);
1792             break;
1793 
1794         case NXT_HTTP_ROUTE_ADDR_RANGE:
1795             match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
1796                                 sizeof(struct in6_addr)) >= 0
1797                      && nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.end,
1798                                    sizeof(struct in6_addr)) <= 0);
1799             break;
1800 
1801         case NXT_HTTP_ROUTE_ADDR_CIDR:
1802             for (i = 0; i < 16; i++) {
1803                 match = ((sin6->sin6_addr.s6_addr[i]
1804                           & p->addr.v6.end.s6_addr[i])
1805                          == p->addr.v6.start.s6_addr[i]);
1806 
1807                 if (!match) {
1808                     break;
1809                 }
1810             }
1811 
1812             break;
1813 
1814         default:
1815             nxt_unreachable();
1816         }
1817 
1818         break;
1819 #endif
1820 
1821 #if (NXT_HAVE_UNIX_DOMAIN)
1822     case AF_UNIX:
1823 
1824         match = (base->addr_family == AF_UNIX);
1825         break;
1826 #endif
1827 
1828     default:
1829         match = 0;
1830         break;
1831     }
1832 
1833     return match ^ base->negative;
1834 }
1835 
1836 
1837 nxt_int_t
1838 nxt_http_route_addr_rule(nxt_http_request_t *r,
1839     nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sa)
1840 {
1841     uint32_t                       n;
1842     nxt_bool_t                     matches;
1843     nxt_http_route_addr_pattern_t  *p;
1844 
1845     n = addr_rule->items;
1846 
1847     if (n == 0) {
1848         return 0;
1849     }
1850 
1851     p = &addr_rule->addr_pattern[0] - 1;
1852 
1853     do {
1854         p++;
1855         n--;
1856 
1857         matches = nxt_http_route_addr_pattern_match(p, sa);
1858 
1859         if (p->base.negative) {
1860             if (matches) {
1861                 continue;
1862             }
1863 
1864             return 0;
1865         }
1866 
1867         if (matches) {
1868             return 1;
1869         }
1870 
1871     } while (n > 0);
1872 
1873     return p->base.negative;
1874 }
1875 
1876 
1877 static nxt_int_t
1878 nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1879 {
1880     nxt_int_t         ret;
1881     nxt_http_field_t  *f;
1882 
1883     ret = 0;
1884 
1885     nxt_list_each(f, r->fields) {
1886 
1887         if (rule->u.name.hash != f->hash
1888             || rule->u.name.length != f->name_length
1889             || nxt_strncasecmp(rule->u.name.start, f->name, f->name_length)
1890                != 0)
1891         {
1892             continue;
1893         }
1894 
1895         ret = nxt_http_route_test_rule(r, rule, f->value, f->value_length);
1896         if (nxt_slow_path(ret == NXT_ERROR)) {
1897             return NXT_ERROR;
1898         }
1899 
1900         if (ret == 0) {
1901             return ret;
1902         }
1903 
1904     } nxt_list_loop;
1905 
1906     return ret;
1907 }
1908 
1909 
1910 static nxt_int_t
1911 nxt_http_route_arguments(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1912 {
1913     nxt_array_t  *arguments;
1914 
1915     arguments = nxt_http_arguments_parse(r);
1916     if (nxt_slow_path(arguments == NULL)) {
1917         return -1;
1918     }
1919 
1920     return nxt_http_route_test_argument(r, rule, arguments);
1921 }
1922 
1923 
1924 static nxt_int_t
1925 nxt_http_route_test_argument(nxt_http_request_t *r,
1926     nxt_http_route_rule_t *rule, nxt_array_t *array)
1927 {
1928     nxt_int_t              ret;
1929     nxt_http_name_value_t  *nv, *end;
1930 
1931     ret = 0;
1932 
1933     nv = array->elts;
1934     end = nv + array->nelts;
1935 
1936     while (nv < end) {
1937 
1938         if (rule->u.name.hash == nv->hash
1939             && rule->u.name.length == nv->name_length
1940             && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
1941         {
1942             ret = nxt_http_route_test_rule(r, rule, nv->value,
1943                                            nv->value_length);
1944             if (nxt_slow_path(ret == NXT_ERROR)) {
1945                 return NXT_ERROR;
1946             }
1947 
1948             if (ret == 0) {
1949                 break;
1950             }
1951         }
1952 
1953         nv++;
1954     }
1955 
1956     return ret;
1957 }
1958 
1959 
1960 static nxt_int_t
1961 nxt_http_route_scheme(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1962 {
1963     nxt_bool_t                      https;
1964     nxt_http_route_pattern_slice_t  *pattern_slice;
1965 
1966     pattern_slice = rule->pattern[0].u.pattern_slices->elts;
1967     https = (pattern_slice->length == nxt_length("https"));
1968 
1969     return (r->tls == https);
1970 }
1971 
1972 
1973 static nxt_int_t
1974 nxt_http_route_query(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1975 {
1976     nxt_array_t  *arguments;
1977 
1978     arguments = nxt_http_arguments_parse(r);
1979     if (nxt_slow_path(arguments == NULL)) {
1980         return -1;
1981     }
1982 
1983     return nxt_http_route_test_rule(r, rule, r->args_decoded.start,
1984                                     r->args_decoded.length);
1985 }
1986 
1987 
1988 static nxt_int_t
1989 nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1990 {
1991     nxt_array_t  *cookies;
1992 
1993     cookies = nxt_http_cookies_parse(r);
1994     if (nxt_slow_path(cookies == NULL)) {
1995         return -1;
1996     }
1997 
1998     return nxt_http_route_test_cookie(r, rule, cookies);
1999 }
2000 
2001 
2002 static nxt_int_t
2003 nxt_http_route_test_cookie(nxt_http_request_t *r,
2004     nxt_http_route_rule_t *rule, nxt_array_t *array)
2005 {
2006     nxt_int_t              ret;
2007     nxt_http_name_value_t  *nv, *end;
2008 
2009     ret = 0;
2010 
2011     nv = array->elts;
2012     end = nv + array->nelts;
2013 
2014     while (nv < end) {
2015 
2016         if (rule->u.name.hash == nv->hash
2017             && rule->u.name.length == nv->name_length
2018             && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
2019         {
2020             ret = nxt_http_route_test_rule(r, rule, nv->value,
2021                                            nv->value_length);
2022             if (nxt_slow_path(ret == NXT_ERROR)) {
2023                 return NXT_ERROR;
2024             }
2025 
2026             if (ret == 0) {
2027                 break;
2028             }
2029         }
2030 
2031         nv++;
2032     }
2033 
2034     return ret;
2035 }
2036 
2037 
2038 nxt_int_t
2039 nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule,
2040     u_char *start, size_t length)
2041 {
2042     nxt_int_t                 ret;
2043     nxt_http_route_pattern_t  *pattern, *end;
2044 
2045     ret = 1;
2046     pattern = &rule->pattern[0];
2047     end = pattern + rule->items;
2048 
2049     while (pattern < end) {
2050         ret = nxt_http_route_pattern(r, pattern, start, length);
2051         if (nxt_slow_path(ret == NXT_ERROR)) {
2052             return NXT_ERROR;
2053         }
2054 
2055         /* nxt_http_route_pattern() returns either 1 or 0. */
2056         ret ^= pattern->negative;
2057 
2058         if (pattern->any == ret) {
2059             return ret;
2060         }
2061 
2062         pattern++;
2063     }
2064 
2065     return ret;
2066 }
2067 
2068 
2069 static nxt_int_t
2070 nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern,
2071     u_char *start, size_t length)
2072 {
2073     u_char                          *p, *end, *test;
2074     size_t                          test_length;
2075     uint32_t                        i;
2076     nxt_array_t                     *pattern_slices;
2077     nxt_http_route_pattern_slice_t  *pattern_slice;
2078 
2079 #if (NXT_HAVE_REGEX)
2080     if (pattern->regex) {
2081         if (r->regex_match == NULL) {
2082             r->regex_match = nxt_regex_match_create(r->mem_pool, 0);
2083             if (nxt_slow_path(r->regex_match == NULL)) {
2084                 return NXT_ERROR;
2085             }
2086         }
2087 
2088         return nxt_regex_match(pattern->u.regex, start, length, r->regex_match);
2089     }
2090 #endif
2091 
2092     if (length < pattern->min_length) {
2093         return 0;
2094     }
2095 
2096     nxt_assert(pattern->u.pattern_slices != NULL);
2097 
2098     pattern_slices = pattern->u.pattern_slices;
2099     pattern_slice = pattern_slices->elts;
2100     end = start + length;
2101 
2102     for (i = 0; i < pattern_slices->nelts; i++, pattern_slice++) {
2103         test = pattern_slice->start;
2104         test_length = pattern_slice->length;
2105 
2106         switch (pattern_slice->type) {
2107         case NXT_HTTP_ROUTE_PATTERN_EXACT:
2108             return ((length == pattern->min_length) &&
2109                     nxt_http_route_memcmp(start, test, test_length,
2110                                           pattern->case_sensitive));
2111 
2112         case NXT_HTTP_ROUTE_PATTERN_BEGIN:
2113             if (nxt_http_route_memcmp(start, test, test_length,
2114                                       pattern->case_sensitive))
2115             {
2116                 start += test_length;
2117                 break;
2118             }
2119 
2120             return 0;
2121 
2122         case NXT_HTTP_ROUTE_PATTERN_END:
2123             p = end - test_length;
2124 
2125             if (nxt_http_route_memcmp(p, test, test_length,
2126                                       pattern->case_sensitive))
2127             {
2128                 end = p;
2129                 break;
2130             }
2131 
2132             return 0;
2133 
2134         case NXT_HTTP_ROUTE_PATTERN_SUBSTRING:
2135             if (pattern->case_sensitive) {
2136                 p = nxt_memstrn(start, end, (char *) test, test_length);
2137 
2138             } else {
2139                 p = nxt_memcasestrn(start, end, (char *) test, test_length);
2140             }
2141 
2142             if (p == NULL) {
2143                 return 0;
2144             }
2145 
2146             start = p + test_length;
2147         }
2148     }
2149 
2150     return 1;
2151 }
2152 
2153 
2154 static nxt_int_t
2155 nxt_http_route_memcmp(u_char *start, u_char *test, size_t test_length,
2156     nxt_bool_t case_sensitive)
2157 {
2158     nxt_int_t  n;
2159 
2160     if (case_sensitive) {
2161         n = nxt_memcmp(start, test, test_length);
2162 
2163     } else {
2164         n = nxt_memcasecmp(start, test, test_length);
2165     }
2166 
2167     return (n == 0);
2168 }
2169