Coverage Report

Created: 2024-02-25 06:30

/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
}