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