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