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