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