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