xref: /unit/src/nxt_http_route.c (revision 1854:aebe76640568)
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("proxy"),
631         NXT_CONF_MAP_PTR,
632         offsetof(nxt_http_route_action_conf_t, proxy)
633     },
634     {
635         nxt_string("share"),
636         NXT_CONF_MAP_PTR,
637         offsetof(nxt_http_route_action_conf_t, share)
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     if (accf.share != NULL) {
704         conf = accf.share;
705 
706     } else if (accf.proxy != NULL) {
707         conf = accf.proxy;
708 
709     } else {
710         conf = accf.pass;
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.share != NULL) {
721         action->handler = nxt_http_static_handler;
722 
723         if (accf.fallback != NULL) {
724             action->u.share.fallback = nxt_mp_alloc(mp,
725                                                     sizeof(nxt_http_action_t));
726             if (nxt_slow_path(action->u.share.fallback == NULL)) {
727                 return NXT_ERROR;
728             }
729 
730             return nxt_http_route_action_create(tmcf, accf.fallback,
731                                                 action->u.share.fallback);
732         }
733 
734         return NXT_OK;
735     }
736 
737     if (accf.proxy != NULL) {
738         return nxt_http_proxy_create(mp, action);
739     }
740 
741     return NXT_OK;
742 }
743 
744 
745 static nxt_http_route_table_t *
746 nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp,
747     nxt_conf_value_t *table_cv, nxt_http_route_object_t object,
748     nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding)
749 {
750     size_t                    size;
751     uint32_t                  i, n;
752     nxt_bool_t                array;
753     nxt_conf_value_t          *ruleset_cv;
754     nxt_http_route_table_t    *table;
755     nxt_http_route_ruleset_t  *ruleset;
756 
757     array = (nxt_conf_type(table_cv) == NXT_CONF_ARRAY);
758     n = array ? nxt_conf_array_elements_count(table_cv) : 1;
759     size = sizeof(nxt_http_route_table_t)
760            + n * sizeof(nxt_http_route_ruleset_t *);
761 
762     table = nxt_mp_alloc(mp, size);
763     if (nxt_slow_path(table == NULL)) {
764         return NULL;
765     }
766 
767     table->items = n;
768     table->object = NXT_HTTP_ROUTE_TABLE;
769 
770     if (!array) {
771         ruleset = nxt_http_route_ruleset_create(task, mp, table_cv, object,
772                                                 case_sensitive, encoding);
773         if (nxt_slow_path(ruleset == NULL)) {
774             return NULL;
775         }
776 
777         table->ruleset[0] = ruleset;
778 
779         return table;
780     }
781 
782     for (i = 0; i < n; i++) {
783         ruleset_cv = nxt_conf_get_array_element(table_cv, i);
784 
785         ruleset = nxt_http_route_ruleset_create(task, mp, ruleset_cv, object,
786                                                 case_sensitive, encoding);
787         if (nxt_slow_path(ruleset == NULL)) {
788             return NULL;
789         }
790 
791         table->ruleset[i] = ruleset;
792     }
793 
794     return table;
795 }
796 
797 
798 static nxt_http_route_ruleset_t *
799 nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp,
800     nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object,
801     nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding)
802 {
803     size_t                    size;
804     uint32_t                  i, n, next;
805     nxt_str_t                 name;
806     nxt_conf_value_t          *rule_cv;
807     nxt_http_route_rule_t     *rule;
808     nxt_http_route_ruleset_t  *ruleset;
809 
810     n = nxt_conf_object_members_count(ruleset_cv);
811     size = sizeof(nxt_http_route_ruleset_t)
812            + n * sizeof(nxt_http_route_rule_t *);
813 
814     ruleset = nxt_mp_alloc(mp, size);
815     if (nxt_slow_path(ruleset == NULL)) {
816         return NULL;
817     }
818 
819     ruleset->items = n;
820 
821     next = 0;
822 
823     /*
824      * A workaround for GCC 10 with -flto -O2 flags that warns about "name"
825      * may be uninitialized in nxt_http_route_rule_name_create().
826      */
827     nxt_str_null(&name);
828 
829     for (i = 0; i < n; i++) {
830         rule_cv = nxt_conf_next_object_member(ruleset_cv, &name, &next);
831 
832         rule = nxt_http_route_rule_name_create(task, mp, rule_cv, &name,
833                                                case_sensitive, encoding);
834         if (nxt_slow_path(rule == NULL)) {
835             return NULL;
836         }
837 
838         rule->object = object;
839         ruleset->rule[i] = rule;
840     }
841 
842     return ruleset;
843 }
844 
845 
846 static nxt_http_route_rule_t *
847 nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp,
848     nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive,
849     nxt_http_route_encoding_t encoding)
850 {
851     u_char                 c, *p, *src, *start, *end, plus;
852     uint8_t                d0, d1;
853     uint32_t               hash;
854     nxt_uint_t             i;
855     nxt_http_route_rule_t  *rule;
856 
857     rule = nxt_http_route_rule_create(task, mp, rule_cv, case_sensitive,
858                                       NXT_HTTP_ROUTE_PATTERN_NOCASE,
859                                       encoding);
860     if (nxt_slow_path(rule == NULL)) {
861         return NULL;
862     }
863 
864     rule->u.name.length = name->length;
865 
866     p = nxt_mp_nget(mp, name->length);
867     if (nxt_slow_path(p == NULL)) {
868         return NULL;
869     }
870 
871     hash = NXT_HTTP_FIELD_HASH_INIT;
872     rule->u.name.start = p;
873 
874     if (encoding == NXT_HTTP_ROUTE_ENCODING_NONE) {
875         for (i = 0; i < name->length; i++) {
876             c = name->start[i];
877             *p++ = c;
878 
879             c = case_sensitive ? c : nxt_lowcase(c);
880             hash = nxt_http_field_hash_char(hash, c);
881         }
882 
883         goto end;
884     }
885 
886     plus = (encoding == NXT_HTTP_ROUTE_ENCODING_URI_PLUS) ? ' ' : '+';
887 
888     start = name->start;
889     end = start + name->length;
890 
891     for (src = start; src < end; src++) {
892         c = *src;
893 
894         switch (c) {
895         case '%':
896             if (nxt_slow_path(end - src <= 2)) {
897                 return NULL;
898             }
899 
900             d0 = nxt_hex2int[src[1]];
901             d1 = nxt_hex2int[src[2]];
902             src += 2;
903 
904             if (nxt_slow_path((d0 | d1) >= 16)) {
905                 return NULL;
906             }
907 
908             c = (d0 << 4) + d1;
909             *p++ = c;
910             break;
911 
912         case '+':
913             c = plus;
914             *p++ = c;
915             break;
916 
917         default:
918             *p++ = c;
919             break;
920         }
921 
922         c = case_sensitive ? c : nxt_lowcase(c);
923         hash = nxt_http_field_hash_char(hash, c);
924     }
925 
926     rule->u.name.length = p - rule->u.name.start;
927 
928 end:
929 
930     rule->u.name.hash = nxt_http_field_hash_end(hash) & 0xFFFF;
931 
932     return rule;
933 }
934 
935 
936 static nxt_http_route_rule_t *
937 nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp,
938     nxt_conf_value_t *cv, nxt_bool_t case_sensitive,
939     nxt_http_route_pattern_case_t pattern_case,
940     nxt_http_route_encoding_t encoding)
941 {
942     size_t                    size;
943     uint32_t                  i, n;
944     nxt_int_t                 ret;
945     nxt_bool_t                string;
946     nxt_conf_value_t          *value;
947     nxt_http_route_rule_t     *rule;
948     nxt_http_route_pattern_t  *pattern;
949 
950     string = (nxt_conf_type(cv) != NXT_CONF_ARRAY);
951     n = string ? 1 : nxt_conf_array_elements_count(cv);
952     size = sizeof(nxt_http_route_rule_t) + n * sizeof(nxt_http_route_pattern_t);
953 
954     rule = nxt_mp_alloc(mp, size);
955     if (nxt_slow_path(rule == NULL)) {
956         return NULL;
957     }
958 
959     rule->items = n;
960 
961     pattern = &rule->pattern[0];
962 
963     if (string) {
964         pattern[0].case_sensitive = case_sensitive;
965         ret = nxt_http_route_pattern_create(task, mp, cv, &pattern[0],
966                                             pattern_case, encoding);
967         if (nxt_slow_path(ret != NXT_OK)) {
968             return NULL;
969         }
970 
971         return rule;
972     }
973 
974     nxt_conf_array_qsort(cv, nxt_http_pattern_compare);
975 
976     for (i = 0; i < n; i++) {
977         pattern[i].case_sensitive = case_sensitive;
978         value = nxt_conf_get_array_element(cv, i);
979 
980         ret = nxt_http_route_pattern_create(task, mp, value, &pattern[i],
981                                             pattern_case, encoding);
982         if (nxt_slow_path(ret != NXT_OK)) {
983             return NULL;
984         }
985     }
986 
987     return rule;
988 }
989 
990 
991 static nxt_http_route_addr_rule_t *
992 nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp,
993      nxt_conf_value_t *cv)
994 {
995     size_t                         size;
996     uint32_t                       i, n;
997     nxt_bool_t                     array;
998     nxt_conf_value_t               *value;
999     nxt_http_route_addr_rule_t     *addr_rule;
1000     nxt_http_route_addr_pattern_t  *pattern;
1001 
1002     array = (nxt_conf_type(cv) == NXT_CONF_ARRAY);
1003     n = array ? nxt_conf_array_elements_count(cv) : 1;
1004 
1005     size = sizeof(nxt_http_route_addr_rule_t)
1006            + n * sizeof(nxt_http_route_addr_pattern_t);
1007 
1008     addr_rule = nxt_mp_alloc(mp, size);
1009     if (nxt_slow_path(addr_rule == NULL)) {
1010         return NULL;
1011     }
1012 
1013     addr_rule->items = n;
1014 
1015     if (!array) {
1016         pattern = &addr_rule->addr_pattern[0];
1017 
1018         if (nxt_http_route_addr_pattern_parse(mp, pattern, cv) != NXT_OK) {
1019             return NULL;
1020         }
1021 
1022         return addr_rule;
1023     }
1024 
1025     for (i = 0; i < n; i++) {
1026         pattern = &addr_rule->addr_pattern[i];
1027         value = nxt_conf_get_array_element(cv, i);
1028 
1029         if (nxt_http_route_addr_pattern_parse(mp, pattern, value) != NXT_OK) {
1030             return NULL;
1031         }
1032     }
1033 
1034     if (n > 1) {
1035         nxt_qsort(addr_rule->addr_pattern, addr_rule->items,
1036             sizeof(nxt_http_route_addr_pattern_t),
1037             nxt_http_addr_pattern_compare);
1038     }
1039 
1040     return addr_rule;
1041 }
1042 
1043 
1044 static int
1045 nxt_http_pattern_compare(const void *one, const void *two)
1046 {
1047     nxt_str_t         test;
1048     nxt_bool_t        negative1, negative2;
1049     nxt_conf_value_t  *value;
1050 
1051     value = (nxt_conf_value_t *) one;
1052     nxt_conf_get_string(value, &test);
1053     negative1 = (test.length != 0 && test.start[0] == '!');
1054 
1055     value = (nxt_conf_value_t *) two;
1056     nxt_conf_get_string(value, &test);
1057     negative2 = (test.length != 0 && test.start[0] == '!');
1058 
1059     return (negative2 - negative1);
1060 }
1061 
1062 
1063 static int
1064 nxt_http_addr_pattern_compare(const void *one, const void *two)
1065 {
1066     const nxt_http_route_addr_pattern_t  *p1, *p2;
1067 
1068     p1 = one;
1069     p2 = two;
1070 
1071     return (p2->base.negative - p1->base.negative);
1072 }
1073 
1074 
1075 static nxt_int_t
1076 nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
1077     nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern,
1078     nxt_http_route_pattern_case_t pattern_case,
1079     nxt_http_route_encoding_t encoding)
1080 {
1081     u_char                          c, *p, *end;
1082     nxt_str_t                       test, tmp;
1083     nxt_int_t                       ret;
1084     nxt_array_t                     *slices;
1085 #if (NXT_HAVE_REGEX)
1086     nxt_regex_t                     *re;
1087     nxt_regex_err_t                 err;
1088 #endif
1089     nxt_http_route_pattern_type_t   type;
1090     nxt_http_route_pattern_slice_t  *slice;
1091 
1092     type = NXT_HTTP_ROUTE_PATTERN_EXACT;
1093 
1094     nxt_conf_get_string(cv, &test);
1095 
1096     pattern->u.pattern_slices = NULL;
1097     pattern->negative = 0;
1098     pattern->any = 1;
1099     pattern->min_length = 0;
1100 #if (NXT_HAVE_REGEX)
1101     pattern->regex = 0;
1102 #endif
1103 
1104     if (test.length != 0 && test.start[0] == '!') {
1105         test.start++;
1106         test.length--;
1107 
1108         pattern->negative = 1;
1109         pattern->any = 0;
1110     }
1111 
1112     if (test.length > 0 && test.start[0] == '~') {
1113 #if (NXT_HAVE_REGEX)
1114         test.start++;
1115         test.length--;
1116 
1117         re = nxt_regex_compile(mp, &test, &err);
1118         if (nxt_slow_path(re == NULL)) {
1119             if (err.offset < test.length) {
1120                 nxt_alert(task, "nxt_regex_compile(%V) failed: %s at offset %d",
1121                           &test, err.msg, (int) err.offset);
1122                 return NXT_ERROR;
1123             }
1124 
1125             nxt_alert(task, "nxt_regex_compile(%V) failed %s", &test, err.msg);
1126 
1127             return NXT_ERROR;
1128         }
1129 
1130         pattern->u.regex = re;
1131         pattern->regex = 1;
1132 
1133         return NXT_OK;
1134 
1135 #else
1136         return NXT_ERROR;
1137 #endif
1138     }
1139 
1140     slices = nxt_array_create(mp, 1, sizeof(nxt_http_route_pattern_slice_t));
1141     if (nxt_slow_path(slices == NULL)) {
1142         return NXT_ERROR;
1143     }
1144 
1145     pattern->u.pattern_slices = slices;
1146 
1147     if (test.length == 0) {
1148         slice = nxt_array_add(slices);
1149         if (nxt_slow_path(slice == NULL)) {
1150             return NXT_ERROR;
1151         }
1152 
1153         slice->type = NXT_HTTP_ROUTE_PATTERN_EXACT;
1154         slice->start = NULL;
1155         slice->length = 0;
1156 
1157         return NXT_OK;
1158     }
1159 
1160     if (test.start[0] == '*') {
1161         /* 'type' is no longer 'EXACT', assume 'END'. */
1162         type = NXT_HTTP_ROUTE_PATTERN_END;
1163         test.start++;
1164         test.length--;
1165     }
1166 
1167     if (type == NXT_HTTP_ROUTE_PATTERN_EXACT) {
1168         tmp.start = test.start;
1169 
1170         p = nxt_memchr(test.start, '*', test.length);
1171 
1172         if (p == NULL) {
1173             /* No '*' found - EXACT pattern. */
1174             tmp.length = test.length;
1175             type = NXT_HTTP_ROUTE_PATTERN_EXACT;
1176 
1177             test.start += test.length;
1178             test.length = 0;
1179 
1180         } else {
1181             /* '*' found - BEGIN pattern. */
1182             tmp.length = p - test.start;
1183             type = NXT_HTTP_ROUTE_PATTERN_BEGIN;
1184 
1185             test.start = p + 1;
1186             test.length -= tmp.length + 1;
1187         }
1188 
1189         ret = nxt_http_route_pattern_slice(slices, &tmp, type, encoding,
1190                                            pattern_case);
1191         if (nxt_slow_path(ret != NXT_OK)) {
1192             return ret;
1193         }
1194 
1195         pattern->min_length += tmp.length;
1196     }
1197 
1198     end = test.start + test.length;
1199 
1200     if (test.length != 0 && end[-1] != '*') {
1201         p = end - 1;
1202 
1203         while (p != test.start) {
1204             c = *p--;
1205 
1206             if (c == '*') {
1207                 p += 2;
1208                 break;
1209             }
1210         }
1211 
1212         tmp.start = p;
1213         tmp.length = end - p;
1214 
1215         test.length -= tmp.length;
1216         end = p;
1217 
1218         ret = nxt_http_route_pattern_slice(slices, &tmp,
1219                                            NXT_HTTP_ROUTE_PATTERN_END,
1220                                            encoding, pattern_case);
1221         if (nxt_slow_path(ret != NXT_OK)) {
1222             return ret;
1223         }
1224 
1225         pattern->min_length += tmp.length;
1226     }
1227 
1228     tmp.start = test.start;
1229     tmp.length = 0;
1230 
1231     p = tmp.start;
1232 
1233     while (p != end) {
1234         c = *p++;
1235 
1236         if (c != '*') {
1237             tmp.length++;
1238             continue;
1239         }
1240 
1241         if (tmp.length == 0) {
1242             tmp.start = p;
1243             continue;
1244         }
1245 
1246         ret = nxt_http_route_pattern_slice(slices, &tmp,
1247                                            NXT_HTTP_ROUTE_PATTERN_SUBSTRING,
1248                                            encoding, pattern_case);
1249         if (nxt_slow_path(ret != NXT_OK)) {
1250             return ret;
1251         }
1252 
1253         pattern->min_length += tmp.length;
1254 
1255         tmp.start = p;
1256         tmp.length = 0;
1257     }
1258 
1259     if (tmp.length != 0) {
1260         ret = nxt_http_route_pattern_slice(slices, &tmp,
1261                                            NXT_HTTP_ROUTE_PATTERN_SUBSTRING,
1262                                            encoding, pattern_case);
1263         if (nxt_slow_path(ret != NXT_OK)) {
1264             return ret;
1265         }
1266 
1267         pattern->min_length += tmp.length;
1268     }
1269 
1270     return NXT_OK;
1271 }
1272 
1273 
1274 static nxt_int_t
1275 nxt_http_route_decode_str(nxt_str_t *str, nxt_http_route_encoding_t encoding)
1276 {
1277     u_char  *start, *end;
1278 
1279     switch (encoding) {
1280     case NXT_HTTP_ROUTE_ENCODING_NONE:
1281         break;
1282 
1283     case NXT_HTTP_ROUTE_ENCODING_URI:
1284         start = str->start;
1285 
1286         end = nxt_decode_uri(start, start, str->length);
1287         if (nxt_slow_path(end == NULL)) {
1288             return NXT_ERROR;
1289         }
1290 
1291         str->length = end - start;
1292         break;
1293 
1294     case NXT_HTTP_ROUTE_ENCODING_URI_PLUS:
1295         start = str->start;
1296 
1297         end = nxt_decode_uri_plus(start, start, str->length);
1298         if (nxt_slow_path(end == NULL)) {
1299             return NXT_ERROR;
1300         }
1301 
1302         str->length = end - start;
1303         break;
1304 
1305     default:
1306         nxt_unreachable();
1307     }
1308 
1309     return NXT_OK;
1310 }
1311 
1312 
1313 static nxt_int_t
1314 nxt_http_route_pattern_slice(nxt_array_t *slices,
1315     nxt_str_t *test,
1316     nxt_http_route_pattern_type_t type,
1317     nxt_http_route_encoding_t encoding,
1318     nxt_http_route_pattern_case_t pattern_case)
1319 {
1320     u_char                          *start;
1321     nxt_int_t                       ret;
1322     nxt_http_route_pattern_slice_t  *slice;
1323 
1324     ret = nxt_http_route_decode_str(test, encoding);
1325     if (nxt_slow_path(ret != NXT_OK)) {
1326         return ret;
1327     }
1328 
1329     start = nxt_mp_nget(slices->mem_pool, test->length);
1330     if (nxt_slow_path(start == NULL)) {
1331         return NXT_ERROR;
1332     }
1333 
1334     switch (pattern_case) {
1335 
1336     case NXT_HTTP_ROUTE_PATTERN_UPCASE:
1337         nxt_memcpy_upcase(start, test->start, test->length);
1338         break;
1339 
1340     case NXT_HTTP_ROUTE_PATTERN_LOWCASE:
1341         nxt_memcpy_lowcase(start, test->start, test->length);
1342         break;
1343 
1344     case NXT_HTTP_ROUTE_PATTERN_NOCASE:
1345         nxt_memcpy(start, test->start, test->length);
1346         break;
1347     }
1348 
1349     slice = nxt_array_add(slices);
1350     if (nxt_slow_path(slice == NULL)) {
1351         return NXT_ERROR;
1352     }
1353 
1354     slice->type = type;
1355     slice->start = start;
1356     slice->length = test->length;
1357 
1358     return NXT_OK;
1359 }
1360 
1361 
1362 nxt_int_t
1363 nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
1364 {
1365     nxt_int_t          ret;
1366     nxt_http_route_t   **route, **end;
1367     nxt_http_routes_t  *routes;
1368 
1369     routes = tmcf->router_conf->routes;
1370 
1371     if (routes != NULL) {
1372         route = &routes->route[0];
1373         end = route + routes->items;
1374 
1375         while (route < end) {
1376             ret = nxt_http_route_resolve(task, tmcf, *route);
1377             if (nxt_slow_path(ret != NXT_OK)) {
1378                 return NXT_ERROR;
1379             }
1380 
1381             route++;
1382         }
1383     }
1384 
1385     return NXT_OK;
1386 }
1387 
1388 
1389 static nxt_int_t
1390 nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1391     nxt_http_route_t *route)
1392 {
1393     nxt_int_t               ret;
1394     nxt_http_route_match_t  **match, **end;
1395 
1396     match = &route->match[0];
1397     end = match + route->items;
1398 
1399     while (match < end) {
1400         ret = nxt_http_action_resolve(task, tmcf, &(*match)->action);
1401         if (nxt_slow_path(ret != NXT_OK)) {
1402             return NXT_ERROR;
1403         }
1404 
1405         match++;
1406     }
1407 
1408     return NXT_OK;
1409 }
1410 
1411 
1412 static nxt_int_t
1413 nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1414     nxt_http_action_t *action)
1415 {
1416     nxt_var_t  *var;
1417     nxt_int_t  ret;
1418 
1419     if (action->handler != NULL) {
1420         if (action->handler == nxt_http_static_handler
1421             && action->u.share.fallback != NULL)
1422         {
1423             return nxt_http_action_resolve(task, tmcf,
1424                                            action->u.share.fallback);
1425         }
1426 
1427         return NXT_OK;
1428     }
1429 
1430     if (nxt_is_var(&action->name)) {
1431         var = nxt_var_compile(&action->name, tmcf->router_conf->mem_pool);
1432         if (nxt_slow_path(var == NULL)) {
1433             return NXT_ERROR;
1434         }
1435 
1436         action->u.var = var;
1437         action->handler = nxt_http_action_pass_var;
1438         return NXT_OK;
1439     }
1440 
1441     ret = nxt_http_pass_find(task, tmcf->mem_pool, tmcf->router_conf, action);
1442     if (nxt_slow_path(ret != NXT_OK)) {
1443         return NXT_ERROR;
1444     }
1445 
1446     return NXT_OK;
1447 }
1448 
1449 
1450 static nxt_http_action_t *
1451 nxt_http_action_pass_var(nxt_task_t *task, nxt_http_request_t *r,
1452     nxt_http_action_t *action)
1453 {
1454     nxt_var_t  *var;
1455     nxt_int_t  ret;
1456 
1457     ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
1458     if (nxt_slow_path(ret != NXT_OK)) {
1459         goto fail;
1460     }
1461 
1462     var = action->u.var;
1463 
1464     action = nxt_mp_get(r->mem_pool, sizeof(nxt_http_action_t));
1465     if (nxt_slow_path(action == NULL)) {
1466         goto fail;
1467     }
1468 
1469     nxt_var_query(task, r->var_query, var, &action->name);
1470     nxt_var_query_resolve(task, r->var_query, action,
1471                           nxt_http_action_pass_var_ready,
1472                           nxt_http_action_pass_var_error);
1473     return NULL;
1474 
1475 fail:
1476 
1477     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
1478     return NULL;
1479 }
1480 
1481 
1482 static void
1483 nxt_http_action_pass_var_ready(nxt_task_t *task, void *obj, void *data)
1484 {
1485     nxt_int_t           ret;
1486     nxt_router_conf_t   *rtcf;
1487     nxt_http_action_t   *action;
1488     nxt_http_status_t   status;
1489     nxt_http_request_t  *r;
1490 
1491     r = obj;
1492     action = data;
1493     rtcf = r->conf->socket_conf->router_conf;
1494 
1495     nxt_debug(task, "http pass lookup: %V", &action->name);
1496 
1497     ret = nxt_http_pass_find(task, r->mem_pool, rtcf, action);
1498 
1499     if (ret != NXT_OK) {
1500         status = (ret == NXT_DECLINED) ? NXT_HTTP_NOT_FOUND
1501                                        : NXT_HTTP_INTERNAL_SERVER_ERROR;
1502 
1503         nxt_http_request_error(task, r, status);
1504         return;
1505     }
1506 
1507     nxt_http_request_action(task, r, action);
1508 }
1509 
1510 
1511 static void
1512 nxt_http_action_pass_var_error(nxt_task_t *task, void *obj, void *data)
1513 {
1514     nxt_http_request_t  *r;
1515 
1516     r = obj;
1517 
1518     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
1519 }
1520 
1521 
1522 static nxt_int_t
1523 nxt_http_pass_find(nxt_task_t *task, nxt_mp_t *mp, nxt_router_conf_t *rtcf,
1524     nxt_http_action_t *action)
1525 {
1526     nxt_str_t   *targets;
1527     nxt_int_t   ret;
1528     nxt_uint_t  i;
1529     nxt_str_t   segments[3];
1530 
1531     ret = nxt_http_pass_segments(mp, &action->name, segments, 3);
1532     if (nxt_slow_path(ret != NXT_OK)) {
1533         return ret;
1534     }
1535 
1536     if (nxt_str_eq(&segments[0], "applications", 12)) {
1537         ret = nxt_router_listener_application(rtcf, &segments[1], action);
1538 
1539         if (ret != NXT_OK) {
1540             return ret;
1541         }
1542 
1543         if (segments[2].length != 0) {
1544             targets = action->u.app.application->targets;
1545 
1546             for (i = 0; !nxt_strstr_eq(&segments[2], &targets[i]); i++);
1547 
1548             action->u.app.target = i;
1549 
1550         } else {
1551             action->u.app.target = 0;
1552         }
1553 
1554         return NXT_OK;
1555     }
1556 
1557     if (segments[2].length == 0) {
1558         if (nxt_str_eq(&segments[0], "upstreams", 9)) {
1559             return nxt_upstream_find(rtcf->upstreams, &segments[1], action);
1560         }
1561 
1562         if (nxt_str_eq(&segments[0], "routes", 6)) {
1563             return nxt_http_route_find(rtcf->routes, &segments[1], action);
1564         }
1565     }
1566 
1567     return NXT_DECLINED;
1568 }
1569 
1570 
1571 nxt_int_t
1572 nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments,
1573     nxt_uint_t n)
1574 {
1575     u_char     *p;
1576     nxt_str_t  rest;
1577 
1578     if (nxt_slow_path(nxt_str_dup(mp, &rest, pass) == NULL)) {
1579         return NXT_ERROR;
1580     }
1581 
1582     nxt_memzero(segments, n * sizeof(nxt_str_t));
1583 
1584     do {
1585         p = nxt_memchr(rest.start, '/', rest.length);
1586 
1587         if (p != NULL) {
1588             n--;
1589 
1590             if (n == 0) {
1591                 return NXT_DECLINED;
1592             }
1593 
1594             segments->length = p - rest.start;
1595             segments->start = rest.start;
1596 
1597             rest.length -= segments->length + 1;
1598             rest.start = p + 1;
1599 
1600         } else {
1601             n = 0;
1602             *segments = rest;
1603         }
1604 
1605         if (segments->length == 0) {
1606             return NXT_DECLINED;
1607         }
1608 
1609         p = nxt_decode_uri(segments->start, segments->start, segments->length);
1610         if (p == NULL) {
1611             return NXT_DECLINED;
1612         }
1613 
1614         segments->length = p - segments->start;
1615         segments++;
1616 
1617     } while (n);
1618 
1619     return NXT_OK;
1620 }
1621 
1622 
1623 static nxt_int_t
1624 nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name,
1625     nxt_http_action_t *action)
1626 {
1627     nxt_http_route_t  **route, **end;
1628 
1629     route = &routes->route[0];
1630     end = route + routes->items;
1631 
1632     while (route < end) {
1633         if (nxt_strstr_eq(&(*route)->name, name)) {
1634             action->u.route = *route;
1635             action->handler = nxt_http_route_handler;
1636 
1637             return NXT_OK;
1638         }
1639 
1640         route++;
1641     }
1642 
1643     return NXT_DECLINED;
1644 }
1645 
1646 
1647 nxt_http_action_t *
1648 nxt_http_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1649     nxt_str_t *name)
1650 {
1651     nxt_int_t          ret;
1652     nxt_http_action_t  *action;
1653 
1654     action = nxt_mp_alloc(tmcf->router_conf->mem_pool,
1655                           sizeof(nxt_http_action_t));
1656     if (nxt_slow_path(action == NULL)) {
1657         return NULL;
1658     }
1659 
1660     action->name = *name;
1661     action->handler = NULL;
1662 
1663     ret = nxt_http_action_resolve(task, tmcf, action);
1664     if (nxt_slow_path(ret != NXT_OK)) {
1665         return NULL;
1666     }
1667 
1668     return action;
1669 }
1670 
1671 
1672 /* COMPATIBILITY: listener application. */
1673 
1674 nxt_http_action_t *
1675 nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf,
1676     nxt_str_t *name)
1677 {
1678     nxt_http_action_t  *action;
1679 
1680     action = nxt_mp_alloc(rtcf->mem_pool, sizeof(nxt_http_action_t));
1681     if (nxt_slow_path(action == NULL)) {
1682         return NULL;
1683     }
1684 
1685     action->name = *name;
1686 
1687     (void) nxt_router_listener_application(rtcf, name, action);
1688 
1689     action->u.app.target = 0;
1690 
1691     return action;
1692 }
1693 
1694 
1695 static nxt_http_action_t *
1696 nxt_http_route_handler(nxt_task_t *task, nxt_http_request_t *r,
1697     nxt_http_action_t *start)
1698 {
1699     nxt_http_route_t        *route;
1700     nxt_http_action_t       *action;
1701     nxt_http_route_match_t  **match, **end;
1702 
1703     route = start->u.route;
1704     match = &route->match[0];
1705     end = match + route->items;
1706 
1707     while (match < end) {
1708         action = nxt_http_route_match(task, r, *match);
1709         if (action != NULL) {
1710             return action;
1711         }
1712 
1713         match++;
1714     }
1715 
1716     nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND);
1717 
1718     return NULL;
1719 }
1720 
1721 
1722 static nxt_http_action_t *
1723 nxt_http_route_match(nxt_task_t *task, nxt_http_request_t *r,
1724     nxt_http_route_match_t *match)
1725 {
1726     nxt_int_t              ret;
1727     nxt_http_route_test_t  *test, *end;
1728 
1729     test = &match->test[0];
1730     end = test + match->items;
1731 
1732     while (test < end) {
1733         switch (test->rule->object) {
1734         case NXT_HTTP_ROUTE_TABLE:
1735             ret = nxt_http_route_table(r, test->table);
1736             break;
1737         case NXT_HTTP_ROUTE_SOURCE:
1738             ret = nxt_http_route_addr_rule(r, test->addr_rule, r->remote);
1739             break;
1740         case NXT_HTTP_ROUTE_DESTINATION:
1741             if (r->local == NULL && nxt_fast_path(r->proto.any != NULL)) {
1742                 nxt_http_proto[r->protocol].local_addr(task, r);
1743             }
1744 
1745             ret = nxt_http_route_addr_rule(r, test->addr_rule, r->local);
1746             break;
1747         default:
1748             ret = nxt_http_route_rule(r, test->rule);
1749             break;
1750         }
1751 
1752         if (ret <= 0) {
1753             /* 0 => NULL, -1 => NXT_HTTP_ACTION_ERROR. */
1754             return (nxt_http_action_t *) (intptr_t) ret;
1755         }
1756 
1757         test++;
1758     }
1759 
1760     return &match->action;
1761 }
1762 
1763 
1764 static nxt_int_t
1765 nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table)
1766 {
1767     nxt_int_t                 ret;
1768     nxt_http_route_ruleset_t  **ruleset, **end;
1769 
1770     ret = 1;
1771     ruleset = &table->ruleset[0];
1772     end = ruleset + table->items;
1773 
1774     while (ruleset < end) {
1775         ret = nxt_http_route_ruleset(r, *ruleset);
1776 
1777         if (ret != 0) {
1778             return ret;
1779         }
1780 
1781         ruleset++;
1782     }
1783 
1784     return ret;
1785 }
1786 
1787 
1788 static nxt_int_t
1789 nxt_http_route_ruleset(nxt_http_request_t *r, nxt_http_route_ruleset_t *ruleset)
1790 {
1791     nxt_int_t              ret;
1792     nxt_http_route_rule_t  **rule, **end;
1793 
1794     rule = &ruleset->rule[0];
1795     end = rule + ruleset->items;
1796 
1797     while (rule < end) {
1798         ret = nxt_http_route_rule(r, *rule);
1799 
1800         if (ret <= 0) {
1801             return ret;
1802         }
1803 
1804         rule++;
1805     }
1806 
1807     return 1;
1808 }
1809 
1810 
1811 static nxt_int_t
1812 nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1813 {
1814     void       *p, **pp;
1815     u_char     *start;
1816     size_t     length;
1817     nxt_str_t  *s;
1818 
1819     switch (rule->object) {
1820 
1821     case NXT_HTTP_ROUTE_HEADER:
1822         return nxt_http_route_header(r, rule);
1823 
1824     case NXT_HTTP_ROUTE_ARGUMENT:
1825         return nxt_http_route_arguments(r, rule);
1826 
1827     case NXT_HTTP_ROUTE_COOKIE:
1828         return nxt_http_route_cookies(r, rule);
1829 
1830     case NXT_HTTP_ROUTE_SCHEME:
1831         return nxt_http_route_scheme(r, rule);
1832 
1833     default:
1834         break;
1835     }
1836 
1837     p = nxt_pointer_to(r, rule->u.offset);
1838 
1839     if (rule->object == NXT_HTTP_ROUTE_STRING) {
1840         s = p;
1841 
1842     } else {
1843         /* NXT_HTTP_ROUTE_STRING_PTR */
1844         pp = p;
1845         s = *pp;
1846 
1847         if (s == NULL) {
1848             return 0;
1849         }
1850     }
1851 
1852     length = s->length;
1853     start = s->start;
1854 
1855     return nxt_http_route_test_rule(r, rule, start, length);
1856 }
1857 
1858 
1859 static nxt_int_t
1860 nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p,
1861     nxt_sockaddr_t *sa)
1862 {
1863 #if (NXT_INET6)
1864     uint32_t                    i;
1865 #endif
1866     in_port_t                   in_port;
1867     nxt_int_t                   match;
1868     struct sockaddr_in          *sin;
1869 #if (NXT_INET6)
1870     struct sockaddr_in6         *sin6;
1871 #endif
1872     nxt_http_route_addr_base_t  *base;
1873 
1874     base = &p->base;
1875 
1876     switch (sa->u.sockaddr.sa_family) {
1877 
1878     case AF_INET:
1879 
1880         match = (base->addr_family == AF_INET
1881                  || base->addr_family == AF_UNSPEC);
1882         if (!match) {
1883             break;
1884         }
1885 
1886         sin = &sa->u.sockaddr_in;
1887         in_port = ntohs(sin->sin_port);
1888 
1889         match = (in_port >= base->port.start && in_port <= base->port.end);
1890         if (!match) {
1891             break;
1892         }
1893 
1894         switch (base->match_type) {
1895 
1896         case NXT_HTTP_ROUTE_ADDR_ANY:
1897             break;
1898 
1899         case NXT_HTTP_ROUTE_ADDR_EXACT:
1900             match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
1901                                 sizeof(struct in_addr))
1902                      == 0);
1903             break;
1904 
1905         case NXT_HTTP_ROUTE_ADDR_RANGE:
1906             match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
1907                                 sizeof(struct in_addr)) >= 0
1908                      && nxt_memcmp(&sin->sin_addr, &p->addr.v4.end,
1909                                    sizeof(struct in_addr)) <= 0);
1910             break;
1911 
1912         case NXT_HTTP_ROUTE_ADDR_CIDR:
1913             match = ((sin->sin_addr.s_addr & p->addr.v4.end)
1914                      == p->addr.v4.start);
1915             break;
1916 
1917         default:
1918             nxt_unreachable();
1919         }
1920 
1921         break;
1922 
1923 #if (NXT_INET6)
1924     case AF_INET6:
1925 
1926         match = (base->addr_family == AF_INET6
1927                  || base->addr_family == AF_UNSPEC);
1928         if (!match) {
1929             break;
1930         }
1931 
1932         sin6 = &sa->u.sockaddr_in6;
1933         in_port = ntohs(sin6->sin6_port);
1934 
1935         match = (in_port >= base->port.start && in_port <= base->port.end);
1936         if (!match) {
1937             break;
1938         }
1939 
1940         switch (base->match_type) {
1941 
1942         case NXT_HTTP_ROUTE_ADDR_ANY:
1943             break;
1944 
1945         case NXT_HTTP_ROUTE_ADDR_EXACT:
1946             match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
1947                                 sizeof(struct in6_addr))
1948                      == 0);
1949             break;
1950 
1951         case NXT_HTTP_ROUTE_ADDR_RANGE:
1952             match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
1953                                 sizeof(struct in6_addr)) >= 0
1954                      && nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.end,
1955                                    sizeof(struct in6_addr)) <= 0);
1956             break;
1957 
1958         case NXT_HTTP_ROUTE_ADDR_CIDR:
1959             for (i = 0; i < 16; i++) {
1960                 match = ((sin6->sin6_addr.s6_addr[i]
1961                           & p->addr.v6.end.s6_addr[i])
1962                          == p->addr.v6.start.s6_addr[i]);
1963 
1964                 if (!match) {
1965                     break;
1966                 }
1967             }
1968 
1969             break;
1970 
1971         default:
1972             nxt_unreachable();
1973         }
1974 
1975         break;
1976 #endif
1977 
1978     default:
1979         match = 0;
1980         break;
1981     }
1982 
1983     return match ^ base->negative;
1984 }
1985 
1986 
1987 static nxt_int_t
1988 nxt_http_route_addr_rule(nxt_http_request_t *r,
1989     nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sa)
1990 {
1991     uint32_t                       n;
1992     nxt_bool_t                     matches;
1993     nxt_http_route_addr_pattern_t  *p;
1994 
1995     n = addr_rule->items;
1996     p = &addr_rule->addr_pattern[0] - 1;
1997 
1998     do {
1999         p++;
2000         n--;
2001 
2002         matches = nxt_http_route_addr_pattern_match(p, sa);
2003 
2004         if (p->base.negative) {
2005             if (matches) {
2006                 continue;
2007             }
2008 
2009             return 0;
2010         }
2011 
2012         if (matches) {
2013             return 1;
2014         }
2015 
2016     } while (n > 0);
2017 
2018     return p->base.negative;
2019 }
2020 
2021 
2022 static nxt_int_t
2023 nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
2024 {
2025     nxt_int_t         ret;
2026     nxt_http_field_t  *f;
2027 
2028     ret = 0;
2029 
2030     nxt_list_each(f, r->fields) {
2031 
2032         if (rule->u.name.hash != f->hash
2033             || rule->u.name.length != f->name_length
2034             || nxt_strncasecmp(rule->u.name.start, f->name, f->name_length)
2035                != 0)
2036         {
2037             continue;
2038         }
2039 
2040         ret = nxt_http_route_test_rule(r, rule, f->value, f->value_length);
2041         if (nxt_slow_path(ret == NXT_ERROR)) {
2042             return NXT_ERROR;
2043         }
2044 
2045         if (ret == 0) {
2046             return ret;
2047         }
2048 
2049     } nxt_list_loop;
2050 
2051     return ret;
2052 }
2053 
2054 
2055 static nxt_int_t
2056 nxt_http_route_arguments(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
2057 {
2058     nxt_array_t  *arguments;
2059 
2060     if (r->args == NULL) {
2061         return 0;
2062     }
2063 
2064     arguments = nxt_http_route_arguments_parse(r);
2065     if (nxt_slow_path(arguments == NULL)) {
2066         return -1;
2067     }
2068 
2069     return nxt_http_route_test_argument(r, rule, arguments);
2070 }
2071 
2072 
2073 static nxt_array_t *
2074 nxt_http_route_arguments_parse(nxt_http_request_t *r)
2075 {
2076     size_t                 name_length;
2077     u_char                 c, *p, *dst, *dst_start, *start, *end, *name;
2078     uint8_t                d0, d1;
2079     uint32_t               hash;
2080     nxt_bool_t             valid;
2081     nxt_array_t            *args;
2082     nxt_http_name_value_t  *nv;
2083 
2084     if (r->arguments != NULL) {
2085         return r->arguments;
2086     }
2087 
2088     args = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t));
2089     if (nxt_slow_path(args == NULL)) {
2090         return NULL;
2091     }
2092 
2093     hash = NXT_HTTP_FIELD_HASH_INIT;
2094     valid = 1;
2095     name = NULL;
2096     name_length = 0;
2097 
2098     dst_start = nxt_mp_nget(r->mem_pool, r->args->length);
2099     if (nxt_slow_path(dst_start == NULL)) {
2100         return NULL;
2101     }
2102 
2103     start = r->args->start;
2104     end = start + r->args->length;
2105 
2106     for (p = start, dst = dst_start; p < end; p++, dst++) {
2107         c = *p;
2108         *dst = c;
2109 
2110         switch (c) {
2111         case '=':
2112             if (name != NULL) {
2113                 break;
2114             }
2115 
2116             name_length = dst - dst_start;
2117             valid = (name_length != 0);
2118             name = dst_start;
2119             dst_start = dst + 1;
2120 
2121             continue;
2122 
2123         case '&':
2124             if (valid) {
2125                 nv = nxt_http_route_argument(args, name, name_length, hash,
2126                                              dst_start, dst);
2127                 if (nxt_slow_path(nv == NULL)) {
2128                     return NULL;
2129                 }
2130             }
2131 
2132             hash = NXT_HTTP_FIELD_HASH_INIT;
2133             name_length = 0;
2134             valid = 1;
2135             name = NULL;
2136             dst_start = dst + 1;
2137 
2138             continue;
2139 
2140         case '+':
2141             c = ' ';
2142             *dst = ' ';
2143 
2144             break;
2145 
2146         case '%':
2147             if (nxt_slow_path(end - p <= 2)) {
2148                 break;
2149             }
2150 
2151             d0 = nxt_hex2int[p[1]];
2152             d1 = nxt_hex2int[p[2]];
2153 
2154             if (nxt_slow_path((d0 | d1) >= 16)) {
2155                 break;
2156             }
2157 
2158             p += 2;
2159             c = (d0 << 4) + d1;
2160             *dst = c;
2161 
2162             break;
2163         }
2164 
2165         if (name == NULL) {
2166             hash = nxt_http_field_hash_char(hash, c);
2167         }
2168     }
2169 
2170     if (valid) {
2171         nv = nxt_http_route_argument(args, name, name_length, hash, dst_start,
2172                                      dst);
2173         if (nxt_slow_path(nv == NULL)) {
2174             return NULL;
2175         }
2176     }
2177 
2178     r->arguments = args;
2179 
2180     return args;
2181 }
2182 
2183 
2184 static nxt_http_name_value_t *
2185 nxt_http_route_argument(nxt_array_t *array, u_char *name, size_t name_length,
2186     uint32_t hash, u_char *start, u_char *end)
2187 {
2188     size_t                 length;
2189     nxt_http_name_value_t  *nv;
2190 
2191     nv = nxt_array_add(array);
2192     if (nxt_slow_path(nv == NULL)) {
2193         return NULL;
2194     }
2195 
2196     nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF;
2197 
2198     length = end - start;
2199 
2200     if (name == NULL) {
2201         name_length = length;
2202         name = start;
2203         length = 0;
2204     }
2205 
2206     nv->name_length = name_length;
2207     nv->value_length = length;
2208     nv->name = name;
2209     nv->value = start;
2210 
2211     return nv;
2212 }
2213 
2214 
2215 static nxt_int_t
2216 nxt_http_route_test_argument(nxt_http_request_t *r,
2217     nxt_http_route_rule_t *rule, nxt_array_t *array)
2218 {
2219     nxt_int_t              ret;
2220     nxt_http_name_value_t  *nv, *end;
2221 
2222     ret = 0;
2223 
2224     nv = array->elts;
2225     end = nv + array->nelts;
2226 
2227     while (nv < end) {
2228 
2229         if (rule->u.name.hash == nv->hash
2230             && rule->u.name.length == nv->name_length
2231             && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
2232         {
2233             ret = nxt_http_route_test_rule(r, rule, nv->value,
2234                                            nv->value_length);
2235             if (nxt_slow_path(ret == NXT_ERROR)) {
2236                 return NXT_ERROR;
2237             }
2238 
2239             if (ret == 0) {
2240                 break;
2241             }
2242         }
2243 
2244         nv++;
2245     }
2246 
2247     return ret;
2248 }
2249 
2250 
2251 static nxt_int_t
2252 nxt_http_route_scheme(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
2253 {
2254     nxt_bool_t                      tls, https;
2255     nxt_http_route_pattern_slice_t  *pattern_slice;
2256 
2257     pattern_slice = rule->pattern[0].u.pattern_slices->elts;
2258     https = (pattern_slice->length == nxt_length("https"));
2259     tls = (r->tls != NULL);
2260 
2261     return (tls == https);
2262 }
2263 
2264 
2265 static nxt_int_t
2266 nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
2267 {
2268     nxt_array_t  *cookies;
2269 
2270     cookies = nxt_http_route_cookies_parse(r);
2271     if (nxt_slow_path(cookies == NULL)) {
2272         return -1;
2273     }
2274 
2275     return nxt_http_route_test_cookie(r, rule, cookies);
2276 }
2277 
2278 
2279 static nxt_array_t *
2280 nxt_http_route_cookies_parse(nxt_http_request_t *r)
2281 {
2282     nxt_int_t         ret;
2283     nxt_array_t       *cookies;
2284     nxt_http_field_t  *f;
2285 
2286     if (r->cookies != NULL) {
2287         return r->cookies;
2288     }
2289 
2290     cookies = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_name_value_t));
2291     if (nxt_slow_path(cookies == NULL)) {
2292         return NULL;
2293     }
2294 
2295     nxt_list_each(f, r->fields) {
2296 
2297         if (f->hash != NXT_COOKIE_HASH
2298             || f->name_length != 6
2299             || nxt_strncasecmp(f->name, (u_char *) "Cookie", 6) != 0)
2300         {
2301             continue;
2302         }
2303 
2304         ret = nxt_http_route_cookie_parse(cookies, f->value,
2305                                           f->value + f->value_length);
2306         if (ret != NXT_OK) {
2307             return NULL;
2308         }
2309 
2310     } nxt_list_loop;
2311 
2312     r->cookies = cookies;
2313 
2314     return cookies;
2315 }
2316 
2317 
2318 static nxt_int_t
2319 nxt_http_route_cookie_parse(nxt_array_t *cookies, u_char *start, u_char *end)
2320 {
2321     size_t                 name_length;
2322     u_char                 c, *p, *name;
2323     nxt_http_name_value_t  *nv;
2324 
2325     name = NULL;
2326     name_length = 0;
2327 
2328     for (p = start; p < end; p++) {
2329         c = *p;
2330 
2331         if (c == '=') {
2332             while (start[0] == ' ') { start++; }
2333 
2334             name_length = p - start;
2335 
2336             if (name_length != 0) {
2337                 name = start;
2338             }
2339 
2340             start = p + 1;
2341 
2342         } else if (c == ';') {
2343             if (name != NULL) {
2344                 nv = nxt_http_route_cookie(cookies, name, name_length,
2345                                            start, p);
2346                 if (nxt_slow_path(nv == NULL)) {
2347                     return NXT_ERROR;
2348                 }
2349             }
2350 
2351             name = NULL;
2352             start = p + 1;
2353          }
2354     }
2355 
2356     if (name != NULL) {
2357         nv = nxt_http_route_cookie(cookies, name, name_length, start, p);
2358         if (nxt_slow_path(nv == NULL)) {
2359             return NXT_ERROR;
2360         }
2361     }
2362 
2363     return NXT_OK;
2364 }
2365 
2366 
2367 static nxt_http_name_value_t *
2368 nxt_http_route_cookie(nxt_array_t *array, u_char *name, size_t name_length,
2369     u_char *start, u_char *end)
2370 {
2371     u_char                 c, *p;
2372     uint32_t               hash;
2373     nxt_http_name_value_t  *nv;
2374 
2375     nv = nxt_array_add(array);
2376     if (nxt_slow_path(nv == NULL)) {
2377         return NULL;
2378     }
2379 
2380     nv->name_length = name_length;
2381     nv->name = name;
2382 
2383     hash = NXT_HTTP_FIELD_HASH_INIT;
2384 
2385     for (p = name; p < name + name_length; p++) {
2386         c = *p;
2387         hash = nxt_http_field_hash_char(hash, c);
2388     }
2389 
2390     nv->hash = nxt_http_field_hash_end(hash) & 0xFFFF;
2391 
2392     while (start < end && end[-1] == ' ') { end--; }
2393 
2394     nv->value_length = end - start;
2395     nv->value = start;
2396 
2397     return nv;
2398 }
2399 
2400 
2401 static nxt_int_t
2402 nxt_http_route_test_cookie(nxt_http_request_t *r,
2403     nxt_http_route_rule_t *rule, nxt_array_t *array)
2404 {
2405     nxt_int_t              ret;
2406     nxt_http_name_value_t  *nv, *end;
2407 
2408     ret = 0;
2409 
2410     nv = array->elts;
2411     end = nv + array->nelts;
2412 
2413     while (nv < end) {
2414 
2415         if (rule->u.name.hash == nv->hash
2416             && rule->u.name.length == nv->name_length
2417             && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
2418         {
2419             ret = nxt_http_route_test_rule(r, rule, nv->value,
2420                                            nv->value_length);
2421             if (nxt_slow_path(ret == NXT_ERROR)) {
2422                 return NXT_ERROR;
2423             }
2424 
2425             if (ret == 0) {
2426                 break;
2427             }
2428         }
2429 
2430         nv++;
2431     }
2432 
2433     return ret;
2434 }
2435 
2436 
2437 static nxt_int_t
2438 nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule,
2439     u_char *start, size_t length)
2440 {
2441     nxt_int_t                 ret;
2442     nxt_http_route_pattern_t  *pattern, *end;
2443 
2444     ret = 1;
2445     pattern = &rule->pattern[0];
2446     end = pattern + rule->items;
2447 
2448     while (pattern < end) {
2449         ret = nxt_http_route_pattern(r, pattern, start, length);
2450         if (nxt_slow_path(ret == NXT_ERROR)) {
2451             return NXT_ERROR;
2452         }
2453 
2454         /* nxt_http_route_pattern() returns either 1 or 0. */
2455         ret ^= pattern->negative;
2456 
2457         if (pattern->any == ret) {
2458             return ret;
2459         }
2460 
2461         pattern++;
2462     }
2463 
2464     return ret;
2465 }
2466 
2467 
2468 static nxt_int_t
2469 nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern,
2470     u_char *start, size_t length)
2471 {
2472     u_char                          *p, *end, *test;
2473     size_t                          test_length;
2474     uint32_t                        i;
2475     nxt_array_t                     *pattern_slices;
2476     nxt_http_route_pattern_slice_t  *pattern_slice;
2477 
2478 #if (NXT_HAVE_REGEX)
2479     if (pattern->regex) {
2480         if (r->regex_match == NULL) {
2481             r->regex_match = nxt_regex_match_create(r->mem_pool, 0);
2482             if (nxt_slow_path(r->regex_match == NULL)) {
2483                 return NXT_ERROR;
2484             }
2485         }
2486 
2487         return nxt_regex_match(pattern->u.regex, start, length, r->regex_match);
2488     }
2489 #endif
2490 
2491     if (length < pattern->min_length) {
2492         return 0;
2493     }
2494 
2495     nxt_assert(pattern->u.pattern_slices != NULL);
2496 
2497     pattern_slices = pattern->u.pattern_slices;
2498     pattern_slice = pattern_slices->elts;
2499     end = start + length;
2500 
2501     for (i = 0; i < pattern_slices->nelts; i++, pattern_slice++) {
2502         test = pattern_slice->start;
2503         test_length = pattern_slice->length;
2504 
2505         switch (pattern_slice->type) {
2506         case NXT_HTTP_ROUTE_PATTERN_EXACT:
2507             return ((length == pattern->min_length) &&
2508                     nxt_http_route_memcmp(start, test, test_length,
2509                                           pattern->case_sensitive));
2510 
2511         case NXT_HTTP_ROUTE_PATTERN_BEGIN:
2512             if (nxt_http_route_memcmp(start, test, test_length,
2513                                       pattern->case_sensitive))
2514             {
2515                 start += test_length;
2516                 break;
2517             }
2518 
2519             return 0;
2520 
2521         case NXT_HTTP_ROUTE_PATTERN_END:
2522             p = end - test_length;
2523 
2524             if (nxt_http_route_memcmp(p, test, test_length,
2525                                       pattern->case_sensitive))
2526             {
2527                 end = p;
2528                 break;
2529             }
2530 
2531             return 0;
2532 
2533         case NXT_HTTP_ROUTE_PATTERN_SUBSTRING:
2534             if (pattern->case_sensitive) {
2535                 p = nxt_memstrn(start, end, (char *) test, test_length);
2536 
2537             } else {
2538                 p = nxt_memcasestrn(start, end, (char *) test, test_length);
2539             }
2540 
2541             if (p == NULL) {
2542                 return 0;
2543             }
2544 
2545             start = p + test_length;
2546         }
2547     }
2548 
2549     return 1;
2550 }
2551 
2552 
2553 static nxt_int_t
2554 nxt_http_route_memcmp(u_char *start, u_char *test, size_t test_length,
2555     nxt_bool_t case_sensitive)
2556 {
2557     nxt_int_t  n;
2558 
2559     if (case_sensitive) {
2560         n = nxt_memcmp(start, test, test_length);
2561 
2562     } else {
2563         n = nxt_memcasecmp(start, test, test_length);
2564     }
2565 
2566     return (n == 0);
2567 }
2568