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