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