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