xref: /unit/src/nxt_http_route.c (revision 2448:243735980417)
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("pass"),
588         NXT_CONF_MAP_PTR,
589         offsetof(nxt_http_action_conf_t, pass)
590     },
591     {
592         nxt_string("return"),
593         NXT_CONF_MAP_PTR,
594         offsetof(nxt_http_action_conf_t, ret)
595     },
596     {
597         nxt_string("location"),
598         NXT_CONF_MAP_PTR,
599         offsetof(nxt_http_action_conf_t, location)
600     },
601     {
602         nxt_string("proxy"),
603         NXT_CONF_MAP_PTR,
604         offsetof(nxt_http_action_conf_t, proxy)
605     },
606     {
607         nxt_string("share"),
608         NXT_CONF_MAP_PTR,
609         offsetof(nxt_http_action_conf_t, share)
610     },
611     {
612         nxt_string("index"),
613         NXT_CONF_MAP_PTR,
614         offsetof(nxt_http_action_conf_t, index)
615     },
616     {
617         nxt_string("chroot"),
618         NXT_CONF_MAP_STR,
619         offsetof(nxt_http_action_conf_t, chroot)
620     },
621     {
622         nxt_string("follow_symlinks"),
623         NXT_CONF_MAP_PTR,
624         offsetof(nxt_http_action_conf_t, follow_symlinks)
625     },
626     {
627         nxt_string("traverse_mounts"),
628         NXT_CONF_MAP_PTR,
629         offsetof(nxt_http_action_conf_t, traverse_mounts)
630     },
631     {
632         nxt_string("types"),
633         NXT_CONF_MAP_PTR,
634         offsetof(nxt_http_action_conf_t, types)
635     },
636     {
637         nxt_string("fallback"),
638         NXT_CONF_MAP_PTR,
639         offsetof(nxt_http_action_conf_t, fallback)
640     },
641 };
642 
643 
644 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)645 nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
646     nxt_conf_value_t *cv, nxt_http_action_t *action)
647 {
648     nxt_mp_t                *mp;
649     nxt_int_t               ret;
650     nxt_str_t               pass;
651     nxt_router_conf_t       *rtcf;
652     nxt_http_action_conf_t  acf;
653 
654     nxt_memzero(&acf, sizeof(acf));
655 
656     ret = nxt_conf_map_object(tmcf->mem_pool, cv, nxt_http_route_action_conf,
657                               nxt_nitems(nxt_http_route_action_conf), &acf);
658     if (ret != NXT_OK) {
659         return ret;
660     }
661 
662     nxt_memzero(action, sizeof(nxt_http_action_t));
663 
664     rtcf = tmcf->router_conf;
665     mp = rtcf->mem_pool;
666 
667     if (acf.rewrite != NULL) {
668         ret = nxt_http_rewrite_init(rtcf, action, &acf);
669         if (nxt_slow_path(ret != NXT_OK)) {
670             return ret;
671         }
672     }
673 
674     if (acf.ret != NULL) {
675         return nxt_http_return_init(rtcf, action, &acf);
676     }
677 
678     if (acf.share != NULL) {
679         return nxt_http_static_init(task, tmcf, action, &acf);
680     }
681 
682     if (acf.proxy != NULL) {
683         return nxt_http_proxy_init(mp, action, &acf);
684     }
685 
686     nxt_conf_get_string(acf.pass, &pass);
687 
688     action->u.tstr = nxt_tstr_compile(rtcf->tstr_state, &pass, 0);
689     if (nxt_slow_path(action->u.tstr == NULL)) {
690         return NXT_ERROR;
691     }
692 
693     return NXT_OK;
694 }
695 
696 
697 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)698 nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp,
699     nxt_conf_value_t *table_cv, nxt_http_route_object_t object,
700     nxt_bool_t case_sensitive, nxt_http_uri_encoding_t encoding)
701 {
702     size_t                    size;
703     uint32_t                  i, n;
704     nxt_conf_value_t          *ruleset_cv;
705     nxt_http_route_table_t    *table;
706     nxt_http_route_ruleset_t  *ruleset;
707 
708     n = nxt_conf_array_elements_count_or_1(table_cv);
709     size = sizeof(nxt_http_route_table_t)
710            + n * sizeof(nxt_http_route_ruleset_t *);
711 
712     table = nxt_mp_alloc(mp, size);
713     if (nxt_slow_path(table == NULL)) {
714         return NULL;
715     }
716 
717     table->items = n;
718     table->object = NXT_HTTP_ROUTE_TABLE;
719 
720     for (i = 0; i < n; i++) {
721         ruleset_cv = nxt_conf_get_array_element_or_itself(table_cv, i);
722 
723         ruleset = nxt_http_route_ruleset_create(task, mp, ruleset_cv, object,
724                                                 case_sensitive, encoding);
725         if (nxt_slow_path(ruleset == NULL)) {
726             return NULL;
727         }
728 
729         table->ruleset[i] = ruleset;
730     }
731 
732     return table;
733 }
734 
735 
736 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)737 nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp,
738     nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object,
739     nxt_bool_t case_sensitive, nxt_http_uri_encoding_t encoding)
740 {
741     size_t                    size;
742     uint32_t                  i, n, next;
743     nxt_str_t                 name;
744     nxt_conf_value_t          *rule_cv;
745     nxt_http_route_rule_t     *rule;
746     nxt_http_route_ruleset_t  *ruleset;
747 
748     n = nxt_conf_object_members_count(ruleset_cv);
749     size = sizeof(nxt_http_route_ruleset_t)
750            + n * sizeof(nxt_http_route_rule_t *);
751 
752     ruleset = nxt_mp_alloc(mp, size);
753     if (nxt_slow_path(ruleset == NULL)) {
754         return NULL;
755     }
756 
757     ruleset->items = n;
758 
759     next = 0;
760 
761     /*
762      * A workaround for GCC 10 with -flto -O2 flags that warns about "name"
763      * may be uninitialized in nxt_http_route_rule_name_create().
764      */
765     nxt_str_null(&name);
766 
767     for (i = 0; i < n; i++) {
768         rule_cv = nxt_conf_next_object_member(ruleset_cv, &name, &next);
769 
770         rule = nxt_http_route_rule_name_create(task, mp, rule_cv, &name,
771                                                case_sensitive, encoding);
772         if (nxt_slow_path(rule == NULL)) {
773             return NULL;
774         }
775 
776         rule->object = object;
777         ruleset->rule[i] = rule;
778     }
779 
780     return ruleset;
781 }
782 
783 
784 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)785 nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp,
786     nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive,
787     nxt_http_uri_encoding_t encoding)
788 {
789     int64_t                hash;
790     nxt_http_route_rule_t  *rule;
791 
792     rule = nxt_http_route_rule_create(task, mp, rule_cv, case_sensitive,
793                                       NXT_HTTP_ROUTE_PATTERN_NOCASE,
794                                       encoding);
795     if (nxt_slow_path(rule == NULL)) {
796         return NULL;
797     }
798 
799     hash = nxt_http_field_hash(mp, name, case_sensitive, encoding);
800     if (nxt_slow_path(hash == -1)) {
801         return NULL;
802     }
803 
804     rule->u.name.hash = hash;
805     rule->u.name.start = name->start;
806     rule->u.name.length = name->length;
807 
808     return rule;
809 }
810 
811 
812 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)813 nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp,
814     nxt_conf_value_t *cv, nxt_bool_t case_sensitive,
815     nxt_http_route_pattern_case_t pattern_case,
816     nxt_http_uri_encoding_t encoding)
817 {
818     size_t                    size;
819     uint32_t                  i, n;
820     nxt_int_t                 ret;
821     nxt_conf_value_t          *value;
822     nxt_http_route_rule_t     *rule;
823     nxt_http_route_pattern_t  *pattern;
824 
825     n = nxt_conf_array_elements_count_or_1(cv);
826     size = sizeof(nxt_http_route_rule_t) + n * sizeof(nxt_http_route_pattern_t);
827 
828     rule = nxt_mp_alloc(mp, size);
829     if (nxt_slow_path(rule == NULL)) {
830         return NULL;
831     }
832 
833     rule->items = n;
834 
835     pattern = &rule->pattern[0];
836 
837     nxt_conf_array_qsort(cv, nxt_http_pattern_compare);
838 
839     for (i = 0; i < n; i++) {
840         pattern[i].case_sensitive = case_sensitive;
841         value = nxt_conf_get_array_element_or_itself(cv, i);
842 
843         ret = nxt_http_route_pattern_create(task, mp, value, &pattern[i],
844                                             pattern_case, encoding);
845         if (nxt_slow_path(ret != NXT_OK)) {
846             return NULL;
847         }
848     }
849 
850     return rule;
851 }
852 
853 
854 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)855 nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp,
856     nxt_conf_value_t *cv)
857 {
858     size_t                         size;
859     uint32_t                       i, n;
860     nxt_conf_value_t               *value;
861     nxt_http_route_addr_rule_t     *addr_rule;
862     nxt_http_route_addr_pattern_t  *pattern;
863 
864     n = nxt_conf_array_elements_count_or_1(cv);
865 
866     size = sizeof(nxt_http_route_addr_rule_t)
867            + n * sizeof(nxt_http_route_addr_pattern_t);
868 
869     addr_rule = nxt_mp_alloc(mp, size);
870     if (nxt_slow_path(addr_rule == NULL)) {
871         return NULL;
872     }
873 
874     addr_rule->items = n;
875 
876     for (i = 0; i < n; i++) {
877         pattern = &addr_rule->addr_pattern[i];
878         value = nxt_conf_get_array_element_or_itself(cv, i);
879 
880         if (nxt_http_route_addr_pattern_parse(mp, pattern, value) != NXT_OK) {
881             return NULL;
882         }
883     }
884 
885     if (n > 1) {
886         nxt_qsort(addr_rule->addr_pattern, addr_rule->items,
887             sizeof(nxt_http_route_addr_pattern_t),
888             nxt_http_addr_pattern_compare);
889     }
890 
891     return addr_rule;
892 }
893 
894 
895 nxt_http_route_rule_t *
nxt_http_route_types_rule_create(nxt_task_t * task,nxt_mp_t * mp,nxt_conf_value_t * types)896 nxt_http_route_types_rule_create(nxt_task_t *task, nxt_mp_t *mp,
897     nxt_conf_value_t *types)
898 {
899     return nxt_http_route_rule_create(task, mp, types, 0,
900                                       NXT_HTTP_ROUTE_PATTERN_LOWCASE,
901                                       NXT_HTTP_URI_ENCODING_NONE);
902 }
903 
904 
905 static int
nxt_http_pattern_compare(const void * one,const void * two)906 nxt_http_pattern_compare(const void *one, const void *two)
907 {
908     nxt_str_t         test;
909     nxt_bool_t        negative1, negative2;
910     nxt_conf_value_t  *value;
911 
912     value = (nxt_conf_value_t *) one;
913     nxt_conf_get_string(value, &test);
914     negative1 = (test.length != 0 && test.start[0] == '!');
915 
916     value = (nxt_conf_value_t *) two;
917     nxt_conf_get_string(value, &test);
918     negative2 = (test.length != 0 && test.start[0] == '!');
919 
920     return (negative2 - negative1);
921 }
922 
923 
924 static int
nxt_http_addr_pattern_compare(const void * one,const void * two)925 nxt_http_addr_pattern_compare(const void *one, const void *two)
926 {
927     const nxt_http_route_addr_pattern_t  *p1, *p2;
928 
929     p1 = one;
930     p2 = two;
931 
932     return (p2->base.negative - p1->base.negative);
933 }
934 
935 
936 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)937 nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
938     nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern,
939     nxt_http_route_pattern_case_t pattern_case,
940     nxt_http_uri_encoding_t encoding)
941 {
942     u_char                          c, *p, *end;
943     nxt_str_t                       test, tmp;
944     nxt_int_t                       ret;
945     nxt_array_t                     *slices;
946 #if (NXT_HAVE_REGEX)
947     nxt_regex_t                     *re;
948     nxt_regex_err_t                 err;
949 #endif
950     nxt_http_route_pattern_type_t   type;
951     nxt_http_route_pattern_slice_t  *slice;
952 
953     type = NXT_HTTP_ROUTE_PATTERN_EXACT;
954 
955     nxt_conf_get_string(cv, &test);
956 
957     pattern->u.pattern_slices = NULL;
958     pattern->negative = 0;
959     pattern->any = 1;
960     pattern->min_length = 0;
961 #if (NXT_HAVE_REGEX)
962     pattern->regex = 0;
963 #endif
964 
965     if (test.length != 0 && test.start[0] == '!') {
966         test.start++;
967         test.length--;
968 
969         pattern->negative = 1;
970         pattern->any = 0;
971     }
972 
973     if (test.length > 0 && test.start[0] == '~') {
974 #if (NXT_HAVE_REGEX)
975         test.start++;
976         test.length--;
977 
978         re = nxt_regex_compile(mp, &test, &err);
979         if (nxt_slow_path(re == NULL)) {
980             if (err.offset < test.length) {
981                 nxt_alert(task, "nxt_regex_compile(%V) failed: %s at offset %d",
982                           &test, err.msg, (int) err.offset);
983                 return NXT_ERROR;
984             }
985 
986             nxt_alert(task, "nxt_regex_compile(%V) failed %s", &test, err.msg);
987 
988             return NXT_ERROR;
989         }
990 
991         pattern->u.regex = re;
992         pattern->regex = 1;
993 
994         return NXT_OK;
995 
996 #else
997         return NXT_ERROR;
998 #endif
999     }
1000 
1001     slices = nxt_array_create(mp, 1, sizeof(nxt_http_route_pattern_slice_t));
1002     if (nxt_slow_path(slices == NULL)) {
1003         return NXT_ERROR;
1004     }
1005 
1006     pattern->u.pattern_slices = slices;
1007 
1008     if (test.length == 0) {
1009         slice = nxt_array_add(slices);
1010         if (nxt_slow_path(slice == NULL)) {
1011             return NXT_ERROR;
1012         }
1013 
1014         slice->type = NXT_HTTP_ROUTE_PATTERN_EXACT;
1015         slice->start = NULL;
1016         slice->length = 0;
1017 
1018         return NXT_OK;
1019     }
1020 
1021     if (test.start[0] == '*') {
1022         /* 'type' is no longer 'EXACT', assume 'END'. */
1023         type = NXT_HTTP_ROUTE_PATTERN_END;
1024         test.start++;
1025         test.length--;
1026     }
1027 
1028     if (type == NXT_HTTP_ROUTE_PATTERN_EXACT) {
1029         tmp.start = test.start;
1030 
1031         p = memchr(test.start, '*', test.length);
1032 
1033         if (p == NULL) {
1034             /* No '*' found - EXACT pattern. */
1035             tmp.length = test.length;
1036             type = NXT_HTTP_ROUTE_PATTERN_EXACT;
1037 
1038             test.start += test.length;
1039             test.length = 0;
1040 
1041         } else {
1042             /* '*' found - BEGIN pattern. */
1043             tmp.length = p - test.start;
1044             type = NXT_HTTP_ROUTE_PATTERN_BEGIN;
1045 
1046             test.start = p + 1;
1047             test.length -= tmp.length + 1;
1048         }
1049 
1050         ret = nxt_http_route_pattern_slice(slices, &tmp, type, encoding,
1051                                            pattern_case);
1052         if (nxt_slow_path(ret != NXT_OK)) {
1053             return ret;
1054         }
1055 
1056         pattern->min_length += tmp.length;
1057     }
1058 
1059     end = test.start + test.length;
1060 
1061     if (test.length != 0 && end[-1] != '*') {
1062         p = end - 1;
1063 
1064         while (p != test.start) {
1065             c = *p--;
1066 
1067             if (c == '*') {
1068                 p += 2;
1069                 break;
1070             }
1071         }
1072 
1073         tmp.start = p;
1074         tmp.length = end - p;
1075 
1076         test.length -= tmp.length;
1077         end = p;
1078 
1079         ret = nxt_http_route_pattern_slice(slices, &tmp,
1080                                            NXT_HTTP_ROUTE_PATTERN_END,
1081                                            encoding, pattern_case);
1082         if (nxt_slow_path(ret != NXT_OK)) {
1083             return ret;
1084         }
1085 
1086         pattern->min_length += tmp.length;
1087     }
1088 
1089     tmp.start = test.start;
1090     tmp.length = 0;
1091 
1092     p = tmp.start;
1093 
1094     while (p != end) {
1095         c = *p++;
1096 
1097         if (c != '*') {
1098             tmp.length++;
1099             continue;
1100         }
1101 
1102         if (tmp.length == 0) {
1103             tmp.start = p;
1104             continue;
1105         }
1106 
1107         ret = nxt_http_route_pattern_slice(slices, &tmp,
1108                                            NXT_HTTP_ROUTE_PATTERN_SUBSTRING,
1109                                            encoding, pattern_case);
1110         if (nxt_slow_path(ret != NXT_OK)) {
1111             return ret;
1112         }
1113 
1114         pattern->min_length += tmp.length;
1115 
1116         tmp.start = p;
1117         tmp.length = 0;
1118     }
1119 
1120     if (tmp.length != 0) {
1121         ret = nxt_http_route_pattern_slice(slices, &tmp,
1122                                            NXT_HTTP_ROUTE_PATTERN_SUBSTRING,
1123                                            encoding, pattern_case);
1124         if (nxt_slow_path(ret != NXT_OK)) {
1125             return ret;
1126         }
1127 
1128         pattern->min_length += tmp.length;
1129     }
1130 
1131     return NXT_OK;
1132 }
1133 
1134 
1135 static nxt_int_t
nxt_http_route_decode_str(nxt_str_t * str,nxt_http_uri_encoding_t encoding)1136 nxt_http_route_decode_str(nxt_str_t *str, nxt_http_uri_encoding_t encoding)
1137 {
1138     u_char  *start, *end;
1139 
1140     switch (encoding) {
1141     case NXT_HTTP_URI_ENCODING_NONE:
1142         break;
1143 
1144     case NXT_HTTP_URI_ENCODING:
1145         start = str->start;
1146 
1147         end = nxt_decode_uri(start, start, str->length);
1148         if (nxt_slow_path(end == NULL)) {
1149             return NXT_ERROR;
1150         }
1151 
1152         str->length = end - start;
1153         break;
1154 
1155     case NXT_HTTP_URI_ENCODING_PLUS:
1156         start = str->start;
1157 
1158         end = nxt_decode_uri_plus(start, start, str->length);
1159         if (nxt_slow_path(end == NULL)) {
1160             return NXT_ERROR;
1161         }
1162 
1163         str->length = end - start;
1164         break;
1165 
1166     default:
1167         nxt_unreachable();
1168     }
1169 
1170     return NXT_OK;
1171 }
1172 
1173 
1174 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)1175 nxt_http_route_pattern_slice(nxt_array_t *slices,
1176     nxt_str_t *test, nxt_http_route_pattern_type_t type,
1177     nxt_http_uri_encoding_t encoding,
1178     nxt_http_route_pattern_case_t pattern_case)
1179 {
1180     u_char                          *start;
1181     nxt_int_t                       ret;
1182     nxt_http_route_pattern_slice_t  *slice;
1183 
1184     ret = nxt_http_route_decode_str(test, encoding);
1185     if (nxt_slow_path(ret != NXT_OK)) {
1186         return ret;
1187     }
1188 
1189     start = nxt_mp_nget(slices->mem_pool, test->length);
1190     if (nxt_slow_path(start == NULL)) {
1191         return NXT_ERROR;
1192     }
1193 
1194     switch (pattern_case) {
1195 
1196     case NXT_HTTP_ROUTE_PATTERN_UPCASE:
1197         nxt_memcpy_upcase(start, test->start, test->length);
1198         break;
1199 
1200     case NXT_HTTP_ROUTE_PATTERN_LOWCASE:
1201         nxt_memcpy_lowcase(start, test->start, test->length);
1202         break;
1203 
1204     case NXT_HTTP_ROUTE_PATTERN_NOCASE:
1205         nxt_memcpy(start, test->start, test->length);
1206         break;
1207     }
1208 
1209     slice = nxt_array_add(slices);
1210     if (nxt_slow_path(slice == NULL)) {
1211         return NXT_ERROR;
1212     }
1213 
1214     slice->type = type;
1215     slice->start = start;
1216     slice->length = test->length;
1217 
1218     return NXT_OK;
1219 }
1220 
1221 
1222 nxt_int_t
nxt_http_routes_resolve(nxt_task_t * task,nxt_router_temp_conf_t * tmcf)1223 nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
1224 {
1225     nxt_int_t          ret;
1226     nxt_http_route_t   **route, **end;
1227     nxt_http_routes_t  *routes;
1228 
1229     routes = tmcf->router_conf->routes;
1230 
1231     if (routes != NULL) {
1232         route = &routes->route[0];
1233         end = route + routes->items;
1234 
1235         while (route < end) {
1236             ret = nxt_http_route_resolve(task, tmcf, *route);
1237             if (nxt_slow_path(ret != NXT_OK)) {
1238                 return NXT_ERROR;
1239             }
1240 
1241             route++;
1242         }
1243     }
1244 
1245     return NXT_OK;
1246 }
1247 
1248 
1249 static nxt_int_t
nxt_http_route_resolve(nxt_task_t * task,nxt_router_temp_conf_t * tmcf,nxt_http_route_t * route)1250 nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1251     nxt_http_route_t *route)
1252 {
1253     nxt_int_t               ret;
1254     nxt_http_route_match_t  **match, **end;
1255 
1256     match = &route->match[0];
1257     end = match + route->items;
1258 
1259     while (match < end) {
1260         ret = nxt_http_action_resolve(task, tmcf, &(*match)->action);
1261         if (nxt_slow_path(ret != NXT_OK)) {
1262             return NXT_ERROR;
1263         }
1264 
1265         match++;
1266     }
1267 
1268     return NXT_OK;
1269 }
1270 
1271 
1272 static nxt_int_t
nxt_http_action_resolve(nxt_task_t * task,nxt_router_temp_conf_t * tmcf,nxt_http_action_t * action)1273 nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1274     nxt_http_action_t *action)
1275 {
1276     nxt_int_t  ret;
1277     nxt_str_t  pass;
1278 
1279     if (action->handler != NULL) {
1280         if (action->fallback != NULL) {
1281             return nxt_http_action_resolve(task, tmcf, action->fallback);
1282         }
1283 
1284         return NXT_OK;
1285     }
1286 
1287     if (nxt_tstr_is_const(action->u.tstr)) {
1288         nxt_tstr_str(action->u.tstr, &pass);
1289 
1290         ret = nxt_http_pass_find(tmcf->mem_pool, tmcf->router_conf, &pass,
1291                                  action);
1292         if (nxt_slow_path(ret != NXT_OK)) {
1293             return NXT_ERROR;
1294         }
1295 
1296     } else {
1297         action->handler = nxt_http_pass_var;
1298     }
1299 
1300     return NXT_OK;
1301 }
1302 
1303 
1304 static nxt_http_action_t *
nxt_http_pass_var(nxt_task_t * task,nxt_http_request_t * r,nxt_http_action_t * action)1305 nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r,
1306     nxt_http_action_t *action)
1307 {
1308     nxt_int_t          ret;
1309     nxt_str_t          str;
1310     nxt_tstr_t         *tstr;
1311     nxt_router_conf_t  *rtcf;
1312 
1313     tstr = action->u.tstr;
1314 
1315     nxt_tstr_str(tstr, &str);
1316 
1317     nxt_debug(task, "http pass: \"%V\"", &str);
1318 
1319     rtcf = r->conf->socket_conf->router_conf;
1320 
1321     ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, &r->tstr_cache,
1322                               r, r->mem_pool);
1323     if (nxt_slow_path(ret != NXT_OK)) {
1324         goto fail;
1325     }
1326 
1327     action = nxt_mp_zget(r->mem_pool,
1328                          sizeof(nxt_http_action_t) + sizeof(nxt_str_t));
1329     if (nxt_slow_path(action == NULL)) {
1330         goto fail;
1331     }
1332 
1333     action->u.pass = nxt_pointer_to(action, sizeof(nxt_http_action_t));
1334 
1335     nxt_tstr_query(task, r->tstr_query, tstr, action->u.pass);
1336     nxt_tstr_query_resolve(task, r->tstr_query, action,
1337                            nxt_http_pass_query_ready,
1338                            nxt_http_pass_query_error);
1339     return NULL;
1340 
1341 fail:
1342 
1343     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
1344     return NULL;
1345 }
1346 
1347 
1348 static void
nxt_http_pass_query_ready(nxt_task_t * task,void * obj,void * data)1349 nxt_http_pass_query_ready(nxt_task_t *task, void *obj, void *data)
1350 {
1351     nxt_int_t           ret;
1352     nxt_router_conf_t   *rtcf;
1353     nxt_http_action_t   *action;
1354     nxt_http_status_t   status;
1355     nxt_http_request_t  *r;
1356 
1357     r = obj;
1358     action = data;
1359     rtcf = r->conf->socket_conf->router_conf;
1360 
1361     nxt_debug(task, "http pass lookup: %V", action->u.pass);
1362 
1363     ret = nxt_http_pass_find(r->mem_pool, rtcf, action->u.pass, action);
1364 
1365     if (ret != NXT_OK) {
1366         status = (ret == NXT_DECLINED) ? NXT_HTTP_NOT_FOUND
1367                                        : NXT_HTTP_INTERNAL_SERVER_ERROR;
1368 
1369         nxt_http_request_error(task, r, status);
1370         return;
1371     }
1372 
1373     nxt_http_request_action(task, r, action);
1374 }
1375 
1376 
1377 static void
nxt_http_pass_query_error(nxt_task_t * task,void * obj,void * data)1378 nxt_http_pass_query_error(nxt_task_t *task, void *obj, void *data)
1379 {
1380     nxt_http_request_t  *r;
1381 
1382     r = obj;
1383 
1384     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
1385 }
1386 
1387 
1388 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)1389 nxt_http_pass_find(nxt_mp_t *mp, nxt_router_conf_t *rtcf, nxt_str_t *pass,
1390     nxt_http_action_t *action)
1391 {
1392     nxt_int_t  ret;
1393     nxt_str_t  segments[3];
1394 
1395     ret = nxt_http_pass_segments(mp, pass, segments, 3);
1396     if (nxt_slow_path(ret != NXT_OK)) {
1397         return ret;
1398     }
1399 
1400     if (nxt_str_eq(&segments[0], "applications", 12)) {
1401         return nxt_router_application_init(rtcf, &segments[1], &segments[2],
1402                                            action);
1403     }
1404 
1405     if (segments[2].length == 0) {
1406         if (nxt_str_eq(&segments[0], "upstreams", 9)) {
1407             return nxt_upstream_find(rtcf->upstreams, &segments[1], action);
1408         }
1409 
1410         if (nxt_str_eq(&segments[0], "routes", 6)) {
1411             return nxt_http_route_find(rtcf->routes, &segments[1], action);
1412         }
1413     }
1414 
1415     return NXT_DECLINED;
1416 }
1417 
1418 
1419 nxt_int_t
nxt_http_pass_segments(nxt_mp_t * mp,nxt_str_t * pass,nxt_str_t * segments,nxt_uint_t n)1420 nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments,
1421     nxt_uint_t n)
1422 {
1423     u_char     *p;
1424     nxt_str_t  rest;
1425 
1426     if (nxt_slow_path(nxt_str_dup(mp, &rest, pass) == NULL)) {
1427         return NXT_ERROR;
1428     }
1429 
1430     nxt_memzero(segments, n * sizeof(nxt_str_t));
1431 
1432     do {
1433         p = memchr(rest.start, '/', rest.length);
1434 
1435         if (p != NULL) {
1436             n--;
1437 
1438             if (n == 0) {
1439                 return NXT_DECLINED;
1440             }
1441 
1442             segments->length = p - rest.start;
1443             segments->start = rest.start;
1444 
1445             rest.length -= segments->length + 1;
1446             rest.start = p + 1;
1447 
1448         } else {
1449             n = 0;
1450             *segments = rest;
1451         }
1452 
1453         if (segments->length == 0) {
1454             return NXT_DECLINED;
1455         }
1456 
1457         p = nxt_decode_uri(segments->start, segments->start, segments->length);
1458         if (p == NULL) {
1459             return NXT_DECLINED;
1460         }
1461 
1462         segments->length = p - segments->start;
1463         segments++;
1464 
1465     } while (n);
1466 
1467     return NXT_OK;
1468 }
1469 
1470 
1471 static nxt_int_t
nxt_http_route_find(nxt_http_routes_t * routes,nxt_str_t * name,nxt_http_action_t * action)1472 nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name,
1473     nxt_http_action_t *action)
1474 {
1475     nxt_http_route_t  **route, **end;
1476 
1477     if (routes == NULL) {
1478         return NXT_DECLINED;
1479     }
1480 
1481     route = &routes->route[0];
1482     end = route + routes->items;
1483 
1484     while (route < end) {
1485         if (nxt_strstr_eq(&(*route)->name, name)) {
1486             action->u.route = *route;
1487             action->handler = nxt_http_route_handler;
1488 
1489             return NXT_OK;
1490         }
1491 
1492         route++;
1493     }
1494 
1495     return NXT_DECLINED;
1496 }
1497 
1498 
1499 nxt_http_action_t *
nxt_http_action_create(nxt_task_t * task,nxt_router_temp_conf_t * tmcf,nxt_str_t * pass)1500 nxt_http_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
1501     nxt_str_t *pass)
1502 {
1503     nxt_mp_t           *mp;
1504     nxt_int_t          ret;
1505     nxt_router_conf_t  *rtcf;
1506     nxt_http_action_t  *action;
1507 
1508     rtcf = tmcf->router_conf;
1509     mp = rtcf->mem_pool;
1510 
1511     action = nxt_mp_zalloc(mp, sizeof(nxt_http_action_t));
1512     if (nxt_slow_path(action == NULL)) {
1513         return NULL;
1514     }
1515 
1516     action->u.tstr = nxt_tstr_compile(rtcf->tstr_state, pass, 0);
1517     if (nxt_slow_path(action->u.tstr == NULL)) {
1518         return NULL;
1519     }
1520 
1521     action->handler = NULL;
1522 
1523     ret = nxt_http_action_resolve(task, tmcf, action);
1524     if (nxt_slow_path(ret != NXT_OK)) {
1525         return NULL;
1526     }
1527 
1528     return action;
1529 }
1530 
1531 
1532 /* COMPATIBILITY: listener application. */
1533 
1534 nxt_http_action_t *
nxt_http_pass_application(nxt_task_t * task,nxt_router_conf_t * rtcf,nxt_str_t * name)1535 nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf,
1536     nxt_str_t *name)
1537 {
1538     nxt_http_action_t  *action;
1539 
1540     action = nxt_mp_zalloc(rtcf->mem_pool, sizeof(nxt_http_action_t));
1541     if (nxt_slow_path(action == NULL)) {
1542         return NULL;
1543     }
1544 
1545     (void) nxt_router_application_init(rtcf, name, NULL, action);
1546 
1547     return action;
1548 }
1549 
1550 
1551 static nxt_http_action_t *
nxt_http_route_handler(nxt_task_t * task,nxt_http_request_t * r,nxt_http_action_t * start)1552 nxt_http_route_handler(nxt_task_t *task, nxt_http_request_t *r,
1553     nxt_http_action_t *start)
1554 {
1555     size_t                  i;
1556     nxt_http_route_t        *route;
1557     nxt_http_action_t       *action;
1558 
1559     route = start->u.route;
1560 
1561     for (i = 0; i < route->items; i++) {
1562         action = nxt_http_route_match(task, r, route->match[i]);
1563 
1564         if (nxt_slow_path(r->log_route)) {
1565             uint32_t    lvl = (action == NULL) ? NXT_LOG_INFO : NXT_LOG_NOTICE;
1566             const char  *sel = (action == NULL) ? "discarded" : "selected";
1567 
1568             if (route->name.length == 0) {
1569                 nxt_log(task, lvl, "\"routes/%z\" %s", i, sel);
1570             } else {
1571                 nxt_log(task, lvl, "\"routes/%V/%z\" %s", &route->name, i, sel);
1572             }
1573         }
1574 
1575         if (action != NULL) {
1576             return action;
1577         }
1578     }
1579 
1580     nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND);
1581 
1582     return NULL;
1583 }
1584 
1585 
1586 static nxt_http_action_t *
nxt_http_route_match(nxt_task_t * task,nxt_http_request_t * r,nxt_http_route_match_t * match)1587 nxt_http_route_match(nxt_task_t *task, nxt_http_request_t *r,
1588     nxt_http_route_match_t *match)
1589 {
1590     nxt_int_t              ret;
1591     nxt_http_route_test_t  *test, *end;
1592 
1593     test = &match->test[0];
1594     end = test + match->items;
1595 
1596     while (test < end) {
1597         switch (test->rule->object) {
1598         case NXT_HTTP_ROUTE_TABLE:
1599             ret = nxt_http_route_table(r, test->table);
1600             break;
1601         case NXT_HTTP_ROUTE_SOURCE:
1602             ret = nxt_http_route_addr_rule(r, test->addr_rule, r->remote);
1603             break;
1604         case NXT_HTTP_ROUTE_DESTINATION:
1605             if (r->local == NULL && nxt_fast_path(r->proto.any != NULL)) {
1606                 nxt_http_proto[r->protocol].local_addr(task, r);
1607             }
1608 
1609             ret = nxt_http_route_addr_rule(r, test->addr_rule, r->local);
1610             break;
1611         default:
1612             ret = nxt_http_route_rule(r, test->rule);
1613             break;
1614         }
1615 
1616         if (ret <= 0) {
1617             /* 0 => NULL, -1 => NXT_HTTP_ACTION_ERROR. */
1618             return (nxt_http_action_t *) (intptr_t) ret;
1619         }
1620 
1621         test++;
1622     }
1623 
1624     return &match->action;
1625 }
1626 
1627 
1628 static nxt_int_t
nxt_http_route_table(nxt_http_request_t * r,nxt_http_route_table_t * table)1629 nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table)
1630 {
1631     nxt_int_t                 ret;
1632     nxt_http_route_ruleset_t  **ruleset, **end;
1633 
1634     ret = 1;
1635     ruleset = &table->ruleset[0];
1636     end = ruleset + table->items;
1637 
1638     while (ruleset < end) {
1639         ret = nxt_http_route_ruleset(r, *ruleset);
1640 
1641         if (ret != 0) {
1642             return ret;
1643         }
1644 
1645         ruleset++;
1646     }
1647 
1648     return ret;
1649 }
1650 
1651 
1652 static nxt_int_t
nxt_http_route_ruleset(nxt_http_request_t * r,nxt_http_route_ruleset_t * ruleset)1653 nxt_http_route_ruleset(nxt_http_request_t *r, nxt_http_route_ruleset_t *ruleset)
1654 {
1655     nxt_int_t              ret;
1656     nxt_http_route_rule_t  **rule, **end;
1657 
1658     rule = &ruleset->rule[0];
1659     end = rule + ruleset->items;
1660 
1661     while (rule < end) {
1662         ret = nxt_http_route_rule(r, *rule);
1663 
1664         if (ret <= 0) {
1665             return ret;
1666         }
1667 
1668         rule++;
1669     }
1670 
1671     return 1;
1672 }
1673 
1674 
1675 static nxt_int_t
nxt_http_route_rule(nxt_http_request_t * r,nxt_http_route_rule_t * rule)1676 nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1677 {
1678     void       *p, **pp;
1679     u_char     *start;
1680     size_t     length;
1681     nxt_str_t  *s;
1682 
1683     switch (rule->object) {
1684 
1685     case NXT_HTTP_ROUTE_HEADER:
1686         return nxt_http_route_header(r, rule);
1687 
1688     case NXT_HTTP_ROUTE_ARGUMENT:
1689         return nxt_http_route_arguments(r, rule);
1690 
1691     case NXT_HTTP_ROUTE_COOKIE:
1692         return nxt_http_route_cookies(r, rule);
1693 
1694     case NXT_HTTP_ROUTE_SCHEME:
1695         return nxt_http_route_scheme(r, rule);
1696 
1697     case NXT_HTTP_ROUTE_QUERY:
1698         return nxt_http_route_query(r, rule);
1699 
1700     default:
1701         break;
1702     }
1703 
1704     p = nxt_pointer_to(r, rule->u.offset);
1705 
1706     if (rule->object == NXT_HTTP_ROUTE_STRING) {
1707         s = p;
1708 
1709     } else {
1710         /* NXT_HTTP_ROUTE_STRING_PTR */
1711         pp = p;
1712         s = *pp;
1713 
1714         if (s == NULL) {
1715             return 0;
1716         }
1717     }
1718 
1719     length = s->length;
1720     start = s->start;
1721 
1722     return nxt_http_route_test_rule(r, rule, start, length);
1723 }
1724 
1725 
1726 static nxt_int_t
nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t * p,nxt_sockaddr_t * sa)1727 nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p,
1728     nxt_sockaddr_t *sa)
1729 {
1730 #if (NXT_INET6)
1731     uint32_t                    i;
1732 #endif
1733     in_port_t                   in_port;
1734     nxt_int_t                   match;
1735     struct sockaddr_in          *sin;
1736 #if (NXT_INET6)
1737     struct sockaddr_in6         *sin6;
1738 #endif
1739     nxt_http_route_addr_base_t  *base;
1740 
1741     base = &p->base;
1742 
1743     switch (sa->u.sockaddr.sa_family) {
1744 
1745     case AF_INET:
1746 
1747         match = (base->addr_family == AF_INET
1748                  || base->addr_family == AF_UNSPEC);
1749         if (!match) {
1750             break;
1751         }
1752 
1753         sin = &sa->u.sockaddr_in;
1754         in_port = ntohs(sin->sin_port);
1755 
1756         match = (in_port >= base->port.start && in_port <= base->port.end);
1757         if (!match) {
1758             break;
1759         }
1760 
1761         switch (base->match_type) {
1762 
1763         case NXT_HTTP_ROUTE_ADDR_ANY:
1764             break;
1765 
1766         case NXT_HTTP_ROUTE_ADDR_EXACT:
1767             match = (memcmp(&sin->sin_addr, &p->addr.v4.start,
1768                                 sizeof(struct in_addr))
1769                      == 0);
1770             break;
1771 
1772         case NXT_HTTP_ROUTE_ADDR_RANGE:
1773             match = (memcmp(&sin->sin_addr, &p->addr.v4.start,
1774                                 sizeof(struct in_addr)) >= 0
1775                      && memcmp(&sin->sin_addr, &p->addr.v4.end,
1776                                    sizeof(struct in_addr)) <= 0);
1777             break;
1778 
1779         case NXT_HTTP_ROUTE_ADDR_CIDR:
1780             match = ((sin->sin_addr.s_addr & p->addr.v4.end)
1781                      == p->addr.v4.start);
1782             break;
1783 
1784         default:
1785             nxt_unreachable();
1786         }
1787 
1788         break;
1789 
1790 #if (NXT_INET6)
1791     case AF_INET6:
1792 
1793         match = (base->addr_family == AF_INET6
1794                  || base->addr_family == AF_UNSPEC);
1795         if (!match) {
1796             break;
1797         }
1798 
1799         sin6 = &sa->u.sockaddr_in6;
1800         in_port = ntohs(sin6->sin6_port);
1801 
1802         match = (in_port >= base->port.start && in_port <= base->port.end);
1803         if (!match) {
1804             break;
1805         }
1806 
1807         switch (base->match_type) {
1808 
1809         case NXT_HTTP_ROUTE_ADDR_ANY:
1810             break;
1811 
1812         case NXT_HTTP_ROUTE_ADDR_EXACT:
1813             match = (memcmp(&sin6->sin6_addr, &p->addr.v6.start,
1814                                 sizeof(struct in6_addr))
1815                      == 0);
1816             break;
1817 
1818         case NXT_HTTP_ROUTE_ADDR_RANGE:
1819             match = (memcmp(&sin6->sin6_addr, &p->addr.v6.start,
1820                                 sizeof(struct in6_addr)) >= 0
1821                      && memcmp(&sin6->sin6_addr, &p->addr.v6.end,
1822                                    sizeof(struct in6_addr)) <= 0);
1823             break;
1824 
1825         case NXT_HTTP_ROUTE_ADDR_CIDR:
1826             for (i = 0; i < 16; i++) {
1827                 match = ((sin6->sin6_addr.s6_addr[i]
1828                           & p->addr.v6.end.s6_addr[i])
1829                          == p->addr.v6.start.s6_addr[i]);
1830 
1831                 if (!match) {
1832                     break;
1833                 }
1834             }
1835 
1836             break;
1837 
1838         default:
1839             nxt_unreachable();
1840         }
1841 
1842         break;
1843 #endif
1844 
1845 #if (NXT_HAVE_UNIX_DOMAIN)
1846     case AF_UNIX:
1847 
1848         match = (base->addr_family == AF_UNIX);
1849         break;
1850 #endif
1851 
1852     default:
1853         match = 0;
1854         break;
1855     }
1856 
1857     return match ^ base->negative;
1858 }
1859 
1860 
1861 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)1862 nxt_http_route_addr_rule(nxt_http_request_t *r,
1863     nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sa)
1864 {
1865     uint32_t                       n;
1866     nxt_bool_t                     matches;
1867     nxt_http_route_addr_pattern_t  *p;
1868 
1869     n = addr_rule->items;
1870 
1871     if (n == 0) {
1872         return 0;
1873     }
1874 
1875     p = &addr_rule->addr_pattern[0] - 1;
1876 
1877     do {
1878         p++;
1879         n--;
1880 
1881         matches = nxt_http_route_addr_pattern_match(p, sa);
1882 
1883         if (p->base.negative) {
1884             if (matches) {
1885                 continue;
1886             }
1887 
1888             return 0;
1889         }
1890 
1891         if (matches) {
1892             return 1;
1893         }
1894 
1895     } while (n > 0);
1896 
1897     return p->base.negative;
1898 }
1899 
1900 
1901 static nxt_int_t
nxt_http_route_header(nxt_http_request_t * r,nxt_http_route_rule_t * rule)1902 nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1903 {
1904     nxt_int_t         ret;
1905     nxt_http_field_t  *f;
1906 
1907     ret = 0;
1908 
1909     nxt_list_each(f, r->fields) {
1910 
1911         if (rule->u.name.hash != f->hash
1912             || rule->u.name.length != f->name_length
1913             || nxt_strncasecmp(rule->u.name.start, f->name, f->name_length)
1914                != 0)
1915         {
1916             continue;
1917         }
1918 
1919         ret = nxt_http_route_test_rule(r, rule, f->value, f->value_length);
1920         if (nxt_slow_path(ret == NXT_ERROR)) {
1921             return NXT_ERROR;
1922         }
1923 
1924         if (ret == 0) {
1925             return ret;
1926         }
1927 
1928     } nxt_list_loop;
1929 
1930     return ret;
1931 }
1932 
1933 
1934 static nxt_int_t
nxt_http_route_arguments(nxt_http_request_t * r,nxt_http_route_rule_t * rule)1935 nxt_http_route_arguments(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1936 {
1937     nxt_array_t  *arguments;
1938 
1939     arguments = nxt_http_arguments_parse(r);
1940     if (nxt_slow_path(arguments == NULL)) {
1941         return -1;
1942     }
1943 
1944     return nxt_http_route_test_argument(r, rule, arguments);
1945 }
1946 
1947 
1948 static nxt_int_t
nxt_http_route_test_argument(nxt_http_request_t * r,nxt_http_route_rule_t * rule,nxt_array_t * array)1949 nxt_http_route_test_argument(nxt_http_request_t *r,
1950     nxt_http_route_rule_t *rule, nxt_array_t *array)
1951 {
1952     nxt_int_t              ret;
1953     nxt_http_name_value_t  *nv, *end;
1954 
1955     ret = 0;
1956 
1957     nv = array->elts;
1958     end = nv + array->nelts;
1959 
1960     while (nv < end) {
1961 
1962         if (rule->u.name.hash == nv->hash
1963             && rule->u.name.length == nv->name_length
1964             && memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
1965         {
1966             ret = nxt_http_route_test_rule(r, rule, nv->value,
1967                                            nv->value_length);
1968             if (nxt_slow_path(ret == NXT_ERROR)) {
1969                 return NXT_ERROR;
1970             }
1971 
1972             if (ret == 0) {
1973                 break;
1974             }
1975         }
1976 
1977         nv++;
1978     }
1979 
1980     return ret;
1981 }
1982 
1983 
1984 static nxt_int_t
nxt_http_route_scheme(nxt_http_request_t * r,nxt_http_route_rule_t * rule)1985 nxt_http_route_scheme(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1986 {
1987     nxt_bool_t                      https;
1988     nxt_http_route_pattern_slice_t  *pattern_slice;
1989 
1990     pattern_slice = rule->pattern[0].u.pattern_slices->elts;
1991     https = (pattern_slice->length == nxt_length("https"));
1992 
1993     return (r->tls == https);
1994 }
1995 
1996 
1997 static nxt_int_t
nxt_http_route_query(nxt_http_request_t * r,nxt_http_route_rule_t * rule)1998 nxt_http_route_query(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
1999 {
2000     nxt_array_t  *arguments;
2001 
2002     arguments = nxt_http_arguments_parse(r);
2003     if (nxt_slow_path(arguments == NULL)) {
2004         return -1;
2005     }
2006 
2007     return nxt_http_route_test_rule(r, rule, r->args_decoded.start,
2008                                     r->args_decoded.length);
2009 }
2010 
2011 
2012 static nxt_int_t
nxt_http_route_cookies(nxt_http_request_t * r,nxt_http_route_rule_t * rule)2013 nxt_http_route_cookies(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
2014 {
2015     nxt_array_t  *cookies;
2016 
2017     cookies = nxt_http_cookies_parse(r);
2018     if (nxt_slow_path(cookies == NULL)) {
2019         return -1;
2020     }
2021 
2022     return nxt_http_route_test_cookie(r, rule, cookies);
2023 }
2024 
2025 
2026 static nxt_int_t
nxt_http_route_test_cookie(nxt_http_request_t * r,nxt_http_route_rule_t * rule,nxt_array_t * array)2027 nxt_http_route_test_cookie(nxt_http_request_t *r,
2028     nxt_http_route_rule_t *rule, nxt_array_t *array)
2029 {
2030     nxt_int_t              ret;
2031     nxt_http_name_value_t  *nv, *end;
2032 
2033     ret = 0;
2034 
2035     nv = array->elts;
2036     end = nv + array->nelts;
2037 
2038     while (nv < end) {
2039 
2040         if (rule->u.name.hash == nv->hash
2041             && rule->u.name.length == nv->name_length
2042             && memcmp(rule->u.name.start, nv->name, nv->name_length) == 0)
2043         {
2044             ret = nxt_http_route_test_rule(r, rule, nv->value,
2045                                            nv->value_length);
2046             if (nxt_slow_path(ret == NXT_ERROR)) {
2047                 return NXT_ERROR;
2048             }
2049 
2050             if (ret == 0) {
2051                 break;
2052             }
2053         }
2054 
2055         nv++;
2056     }
2057 
2058     return ret;
2059 }
2060 
2061 
2062 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)2063 nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule,
2064     u_char *start, size_t length)
2065 {
2066     nxt_int_t                 ret;
2067     nxt_http_route_pattern_t  *pattern, *end;
2068 
2069     ret = 1;
2070     pattern = &rule->pattern[0];
2071     end = pattern + rule->items;
2072 
2073     while (pattern < end) {
2074         ret = nxt_http_route_pattern(r, pattern, start, length);
2075         if (nxt_slow_path(ret == NXT_ERROR)) {
2076             return NXT_ERROR;
2077         }
2078 
2079         /* nxt_http_route_pattern() returns either 1 or 0. */
2080         ret ^= pattern->negative;
2081 
2082         if (pattern->any == ret) {
2083             return ret;
2084         }
2085 
2086         pattern++;
2087     }
2088 
2089     return ret;
2090 }
2091 
2092 
2093 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)2094 nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern,
2095     u_char *start, size_t length)
2096 {
2097     u_char                          *p, *end, *test;
2098     size_t                          test_length;
2099     uint32_t                        i;
2100     nxt_array_t                     *pattern_slices;
2101     nxt_http_route_pattern_slice_t  *pattern_slice;
2102 
2103 #if (NXT_HAVE_REGEX)
2104     if (pattern->regex) {
2105         if (r->regex_match == NULL) {
2106             r->regex_match = nxt_regex_match_create(r->mem_pool, 0);
2107             if (nxt_slow_path(r->regex_match == NULL)) {
2108                 return NXT_ERROR;
2109             }
2110         }
2111 
2112         return nxt_regex_match(pattern->u.regex, start, length, r->regex_match);
2113     }
2114 #endif
2115 
2116     if (length < pattern->min_length) {
2117         return 0;
2118     }
2119 
2120     nxt_assert(pattern->u.pattern_slices != NULL);
2121 
2122     pattern_slices = pattern->u.pattern_slices;
2123     pattern_slice = pattern_slices->elts;
2124     end = start + length;
2125 
2126     for (i = 0; i < pattern_slices->nelts; i++, pattern_slice++) {
2127         test = pattern_slice->start;
2128         test_length = pattern_slice->length;
2129 
2130         switch (pattern_slice->type) {
2131         case NXT_HTTP_ROUTE_PATTERN_EXACT:
2132             return ((length == pattern->min_length) &&
2133                     nxt_http_route_memcmp(start, test, test_length,
2134                                           pattern->case_sensitive));
2135 
2136         case NXT_HTTP_ROUTE_PATTERN_BEGIN:
2137             if (nxt_http_route_memcmp(start, test, test_length,
2138                                       pattern->case_sensitive))
2139             {
2140                 start += test_length;
2141                 break;
2142             }
2143 
2144             return 0;
2145 
2146         case NXT_HTTP_ROUTE_PATTERN_END:
2147             p = end - test_length;
2148 
2149             if (nxt_http_route_memcmp(p, test, test_length,
2150                                       pattern->case_sensitive))
2151             {
2152                 end = p;
2153                 break;
2154             }
2155 
2156             return 0;
2157 
2158         case NXT_HTTP_ROUTE_PATTERN_SUBSTRING:
2159             if (pattern->case_sensitive) {
2160                 p = nxt_memstrn(start, end, (char *) test, test_length);
2161 
2162             } else {
2163                 p = nxt_memcasestrn(start, end, (char *) test, test_length);
2164             }
2165 
2166             if (p == NULL) {
2167                 return 0;
2168             }
2169 
2170             start = p + test_length;
2171         }
2172     }
2173 
2174     return 1;
2175 }
2176 
2177 
2178 static nxt_int_t
nxt_http_route_memcmp(u_char * start,u_char * test,size_t test_length,nxt_bool_t case_sensitive)2179 nxt_http_route_memcmp(u_char *start, u_char *test, size_t test_length,
2180     nxt_bool_t case_sensitive)
2181 {
2182     nxt_int_t  n;
2183 
2184     if (case_sensitive) {
2185         n = memcmp(start, test, test_length);
2186 
2187     } else {
2188         n = nxt_memcasecmp(start, test, test_length);
2189     }
2190 
2191     return (n == 0);
2192 }
2193