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