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