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