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