Coverage Report

Created: 2024-09-11 06:19

/src/unit/src/nxt_http_parse.c
Line
Count
Source (jump to first uncovered line)
1
2
/*
3
 * Copyright (C) NGINX, Inc.
4
 * Copyright (C) Valentin V. Bartenev
5
 */
6
7
#include <nxt_main.h>
8
9
10
static nxt_int_t nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp,
11
    u_char **pos, const u_char *end);
12
static nxt_int_t nxt_http_parse_request_line(nxt_http_request_parse_t *rp,
13
    u_char **pos, const u_char *end);
14
static nxt_int_t nxt_http_parse_field_name(nxt_http_request_parse_t *rp,
15
    u_char **pos, const u_char *end);
16
static nxt_int_t nxt_http_parse_field_value(nxt_http_request_parse_t *rp,
17
    u_char **pos, const u_char *end);
18
static u_char *nxt_http_lookup_field_end(u_char *p, const u_char *end);
19
static nxt_int_t nxt_http_parse_field_end(nxt_http_request_parse_t *rp,
20
    u_char **pos, const u_char *end);
21
22
static nxt_int_t nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
23
24
static nxt_int_t nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq,
25
    void *data);
26
27
28
#define NXT_HTTP_MAX_FIELD_NAME         0xFF
29
#define NXT_HTTP_MAX_FIELD_VALUE        NXT_INT32_T_MAX
30
31
0
#define NXT_HTTP_FIELD_LVLHSH_SHIFT     5
32
33
34
typedef enum {
35
    NXT_HTTP_TARGET_SPACE = 1,   /* \s  */
36
    NXT_HTTP_TARGET_HASH,        /*  #  */
37
    NXT_HTTP_TARGET_AGAIN,
38
    NXT_HTTP_TARGET_BAD,         /* \0\r\n */
39
40
    /* traps below are used for extended check only */
41
42
    NXT_HTTP_TARGET_SLASH = 5,   /*  /  */
43
    NXT_HTTP_TARGET_DOT,         /*  .  */
44
    NXT_HTTP_TARGET_ARGS_MARK,   /*  ?  */
45
    NXT_HTTP_TARGET_QUOTE_MARK,  /*  %  */
46
} nxt_http_target_traps_e;
47
48
49
static const uint8_t  nxt_http_target_chars[256] nxt_aligned(64) = {
50
    /* \0                               \n        \r       */
51
        4, 0, 0, 0,  0, 0, 0, 0,   0, 0, 4, 0,  0, 4, 0, 0,
52
        0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 0,
53
54
    /* \s  !  "  #   $  %  &  '    (  )  *  +   ,  -  .  / */
55
        1, 0, 0, 2,  0, 8, 0, 0,   0, 0, 0, 0,  0, 0, 6, 5,
56
57
    /*  0  1  2  3   4  5  6  7    8  9  :  ;   <  =  >  ? */
58
        0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 7,
59
};
60
61
62
nxt_inline nxt_http_target_traps_e
63
nxt_http_parse_target(u_char **pos, const u_char *end)
64
62.9k
{
65
62.9k
    u_char      *p;
66
62.9k
    nxt_uint_t  trap;
67
68
62.9k
    p = *pos;
69
70
62.9k
    while (nxt_fast_path(end - p >= 10)) {
71
72
61.5k
#define nxt_target_test_char(ch)                                              \
73
61.5k
                                                                              \
74
148k
        trap = nxt_http_target_chars[ch];                                     \
75
148k
                                                                              \
76
148k
        if (nxt_slow_path(trap != 0)) {                                       \
77
62.0k
            *pos = &(ch);                                                     \
78
62.0k
            return trap;                                                      \
79
62.0k
        }
80
81
/* enddef */
82
83
61.5k
        nxt_target_test_char(p[0]);
84
26.8k
        nxt_target_test_char(p[1]);
85
19.6k
        nxt_target_test_char(p[2]);
86
8.83k
        nxt_target_test_char(p[3]);
87
88
7.39k
        nxt_target_test_char(p[4]);
89
5.92k
        nxt_target_test_char(p[5]);
90
4.63k
        nxt_target_test_char(p[6]);
91
3.33k
        nxt_target_test_char(p[7]);
92
93
2.75k
        nxt_target_test_char(p[8]);
94
2.22k
        nxt_target_test_char(p[9]);
95
96
1.75k
        p += 10;
97
1.75k
    }
98
99
5.74k
    while (p != end) {
100
4.85k
        nxt_target_test_char(*p); p++;
101
2.61k
    }
102
103
888
    return NXT_HTTP_TARGET_AGAIN;
104
3.13k
}
105
106
107
nxt_int_t
108
nxt_http_parse_request_init(nxt_http_request_parse_t *rp, nxt_mp_t *mp)
109
7.43k
{
110
7.43k
    rp->mem_pool = mp;
111
112
7.43k
    rp->fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t));
113
7.43k
    if (nxt_slow_path(rp->fields == NULL)) {
114
0
        return NXT_ERROR;
115
0
    }
116
117
7.43k
    rp->field_hash = NXT_HTTP_FIELD_HASH_INIT;
118
119
7.43k
    return NXT_OK;
120
7.43k
}
121
122
123
nxt_int_t
124
nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b)
125
7.43k
{
126
7.43k
    nxt_int_t  rc;
127
128
7.43k
    if (rp->handler == NULL) {
129
7.43k
        rp->handler = &nxt_http_parse_request_line;
130
7.43k
    }
131
132
49.3k
    do {
133
49.3k
        rc = rp->handler(rp, &b->pos, b->free);
134
49.3k
    } while (rc == NXT_OK);
135
136
7.43k
    return rc;
137
7.43k
}
138
139
140
nxt_int_t
141
nxt_http_parse_fields(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b)
142
0
{
143
0
    nxt_int_t  rc;
144
145
0
    if (rp->handler == NULL) {
146
0
        rp->handler = &nxt_http_parse_field_name;
147
0
    }
148
149
0
    do {
150
0
        rc = rp->handler(rp, &b->pos, b->free);
151
0
    } while (rc == NXT_OK);
152
153
0
    return rc;
154
0
}
155
156
157
static nxt_int_t
158
nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
159
    const u_char *end)
160
7.43k
{
161
7.43k
    u_char                   *p, ch, *after_slash, *args;
162
7.43k
    nxt_int_t                rc;
163
7.43k
    nxt_bool_t               rest;
164
7.43k
    nxt_http_ver_t           ver;
165
7.43k
    nxt_http_target_traps_e  trap;
166
167
7.43k
    static const nxt_http_ver_t  http11 = { "HTTP/1.1" };
168
7.43k
    static const nxt_http_ver_t  http10 = { "HTTP/1.0" };
169
170
7.43k
    p = *pos;
171
172
7.43k
    rp->method.start = p;
173
174
18.6k
    for ( ;; ) {
175
176
18.6k
        while (nxt_fast_path(end - p >= 8)) {
177
178
17.8k
#define nxt_method_test_char(ch)                                              \
179
17.8k
                                                                              \
180
54.6k
            if (nxt_slow_path((ch) < 'A' || (ch) > 'Z')) {                    \
181
18.3k
                p = &(ch);                                                    \
182
18.3k
                goto method_unusual_char;                                     \
183
18.3k
            }
184
185
/* enddef */
186
187
17.8k
            nxt_method_test_char(p[0]);
188
8.61k
            nxt_method_test_char(p[1]);
189
7.40k
            nxt_method_test_char(p[2]);
190
6.20k
            nxt_method_test_char(p[3]);
191
192
5.01k
            nxt_method_test_char(p[4]);
193
3.81k
            nxt_method_test_char(p[5]);
194
2.61k
            nxt_method_test_char(p[6]);
195
1.41k
            nxt_method_test_char(p[7]);
196
197
755
            p += 8;
198
755
        }
199
200
2.03k
        while (p != end) {
201
1.75k
            nxt_method_test_char(*p); p++;
202
421
        }
203
204
281
        rp->method.length = p - rp->method.start;
205
206
281
        return NXT_AGAIN;
207
208
18.3k
    method_unusual_char:
209
210
18.3k
        ch = *p;
211
212
18.3k
        if (nxt_fast_path(ch == ' ')) {
213
6.57k
            rp->method.length = p - rp->method.start;
214
6.57k
            break;
215
6.57k
        }
216
217
11.8k
        if (ch == '_' || ch == '-') {
218
9.51k
            p++;
219
9.51k
            continue;
220
9.51k
        }
221
222
2.29k
        if (rp->method.start == p && (ch == '\r' || ch == '\n')) {
223
1.71k
            rp->method.start++;
224
1.71k
            p++;
225
1.71k
            continue;
226
1.71k
        }
227
228
580
        rp->method.length = p - rp->method.start;
229
230
580
        return NXT_HTTP_PARSE_INVALID;
231
2.29k
    }
232
233
6.57k
    p++;
234
235
6.57k
    if (nxt_slow_path(p == end)) {
236
10
        return NXT_AGAIN;
237
10
    }
238
239
    /* target */
240
241
6.56k
    ch = *p;
242
243
6.56k
    if (nxt_slow_path(ch != '/')) {
244
125
        rc = nxt_http_parse_unusual_target(rp, &p, end);
245
246
125
        if (nxt_slow_path(rc != NXT_OK)) {
247
122
            return rc;
248
122
        }
249
125
    }
250
251
6.44k
    rp->target_start = p;
252
253
6.44k
    after_slash = p + 1;
254
6.44k
    args = NULL;
255
6.44k
    rest = 0;
256
257
10.4k
continue_target:
258
259
16.9k
    for ( ;; ) {
260
16.9k
        p++;
261
262
16.9k
        trap = nxt_http_parse_target(&p, end);
263
264
16.9k
        switch (trap) {
265
4.92k
        case NXT_HTTP_TARGET_SLASH:
266
4.92k
            if (nxt_slow_path(after_slash == p)) {
267
272
                rp->complex_target = 1;
268
272
                goto rest_of_target;
269
272
            }
270
271
4.64k
            after_slash = p + 1;
272
4.64k
            continue;
273
274
2.43k
        case NXT_HTTP_TARGET_DOT:
275
2.43k
            if (nxt_slow_path(after_slash == p)) {
276
575
                rp->complex_target = 1;
277
575
                goto rest_of_target;
278
575
            }
279
280
1.85k
            continue;
281
282
1.85k
        case NXT_HTTP_TARGET_ARGS_MARK:
283
126
            args = p + 1;
284
126
            goto rest_of_target;
285
286
8.12k
        case NXT_HTTP_TARGET_SPACE:
287
8.12k
            rp->target_end = p;
288
8.12k
            goto space_after_target;
289
290
606
        case NXT_HTTP_TARGET_QUOTE_MARK:
291
606
            rp->quoted_target = 1;
292
606
            goto rest_of_target;
293
294
81
        case NXT_HTTP_TARGET_HASH:
295
81
            rp->complex_target = 1;
296
81
            goto rest_of_target;
297
298
551
        case NXT_HTTP_TARGET_AGAIN:
299
551
            rp->target_end = p;
300
551
            return NXT_AGAIN;
301
302
128
        case NXT_HTTP_TARGET_BAD:
303
128
            rp->target_end = p;
304
128
            return NXT_HTTP_PARSE_INVALID;
305
16.9k
        }
306
307
16.9k
        nxt_unreachable();
308
16.9k
    }
309
310
7.25k
rest_of_target:
311
312
7.25k
    rest = 1;
313
314
45.9k
    for ( ;; ) {
315
45.9k
        p++;
316
317
45.9k
        trap = nxt_http_parse_target(&p, end);
318
319
45.9k
        switch (trap) {
320
6.79k
        case NXT_HTTP_TARGET_SPACE:
321
6.79k
            rp->target_end = p;
322
6.79k
            goto space_after_target;
323
324
923
        case NXT_HTTP_TARGET_HASH:
325
923
            rp->complex_target = 1;
326
923
            continue;
327
328
337
        case NXT_HTTP_TARGET_AGAIN:
329
337
            rp->target_end = p;
330
337
            return NXT_AGAIN;
331
332
124
        case NXT_HTTP_TARGET_BAD:
333
124
            rp->target_end = p;
334
124
            return NXT_HTTP_PARSE_INVALID;
335
336
37.8k
        default:
337
37.8k
            continue;
338
45.9k
        }
339
340
45.9k
        nxt_unreachable();
341
45.9k
    }
342
343
16.1k
space_after_target:
344
345
16.1k
    if (nxt_slow_path(end - p < 10)) {
346
347
1.20k
        do {
348
1.20k
            p++;
349
350
1.20k
            if (p == end) {
351
122
                return NXT_AGAIN;
352
122
            }
353
354
1.20k
        } while (*p == ' ');
355
356
934
        if (memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) {
357
358
158
            switch (end - p) {
359
60
            case 8:
360
60
                if (p[7] < '0' || p[7] > '9') {
361
40
                    break;
362
40
                }
363
                /* Fall through. */
364
55
            case 7:
365
55
                if (p[6] != '.') {
366
40
                    break;
367
40
                }
368
                /* Fall through. */
369
67
            case 6:
370
67
                if (p[5] < '0' || p[5] > '9') {
371
41
                    break;
372
41
                }
373
                /* Fall through. */
374
37
            default:
375
37
                return NXT_AGAIN;
376
158
            }
377
158
        }
378
379
        //rp->space_in_target = 1;
380
381
897
        if (rest) {
382
253
            goto rest_of_target;
383
253
        }
384
385
644
        goto continue_target;
386
897
    }
387
388
    /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */
389
390
15.0k
    if (nxt_slow_path(p[9] != '\r' && p[9] != '\n')) {
391
392
9.94k
        if (p[1] == ' ') {
393
            /* surplus space after tartet */
394
1.22k
            p++;
395
1.22k
            goto space_after_target;
396
1.22k
        }
397
398
        //rp->space_in_target = 1;
399
400
8.72k
        if (rest) {
401
5.34k
            goto rest_of_target;
402
5.34k
        }
403
404
3.37k
        goto continue_target;
405
8.72k
    }
406
407
5.14k
    nxt_memcpy(ver.str, &p[1], 8);
408
409
5.14k
    if (nxt_fast_path(ver.ui64 == http11.ui64
410
5.14k
                      || ver.ui64 == http10.ui64
411
5.14k
                      || (memcmp(ver.str, "HTTP/1.", 7) == 0
412
5.14k
                          && ver.s.minor >= '0' && ver.s.minor <= '9')))
413
4.53k
    {
414
4.53k
        rp->version.ui64 = ver.ui64;
415
416
4.53k
        p += 9;
417
4.53k
        if (nxt_fast_path(*p == '\r')) {
418
419
46
            if (nxt_slow_path(p + 1 == end)) {
420
12
                return NXT_AGAIN;
421
12
            }
422
423
34
            if (nxt_slow_path(p[1] != '\n')) {
424
29
                return NXT_HTTP_PARSE_INVALID;
425
29
            }
426
427
5
            *pos = p + 2;
428
429
4.49k
        } else {
430
4.49k
            *pos = p + 1;
431
4.49k
        }
432
433
4.49k
        rp->request_line_end = p;
434
435
4.49k
        if (rp->complex_target || rp->quoted_target) {
436
1.14k
            rc = nxt_http_parse_complex_target(rp);
437
438
1.14k
            if (nxt_slow_path(rc != NXT_OK)) {
439
375
                return rc;
440
375
            }
441
442
768
            return nxt_http_parse_field_name(rp, pos, end);
443
1.14k
        }
444
445
3.35k
        rp->path.start = rp->target_start;
446
447
3.35k
        if (args != NULL) {
448
3
            rp->path.length = args - rp->target_start - 1;
449
450
3
            rp->args.length = rp->target_end - args;
451
3
            rp->args.start = args;
452
453
3.35k
        } else {
454
3.35k
            rp->path.length = rp->target_end - rp->target_start;
455
3.35k
        }
456
457
3.35k
        return nxt_http_parse_field_name(rp, pos, end);
458
4.49k
    }
459
460
607
    if (memcmp(ver.s.prefix, "HTTP/", 5) == 0
461
607
        && ver.s.major >= '0' && ver.s.major <= '9'
462
607
        && ver.s.point == '.'
463
607
        && ver.s.minor >= '0' && ver.s.minor <= '9')
464
19
    {
465
19
        rp->version.ui64 = ver.ui64;
466
19
        return NXT_HTTP_PARSE_UNSUPPORTED_VERSION;
467
19
    }
468
469
588
    return NXT_HTTP_PARSE_INVALID;
470
607
}
471
472
473
static nxt_int_t
474
nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos,
475
    const u_char *end)
476
125
{
477
125
    u_char  *p, ch;
478
479
125
    p = *pos;
480
481
125
    ch = *p;
482
483
125
    if (ch == ' ') {
484
        /* skip surplus spaces before target */
485
486
652
        do {
487
652
            p++;
488
489
652
            if (nxt_slow_path(p == end)) {
490
28
                return NXT_AGAIN;
491
28
            }
492
493
624
            ch = *p;
494
495
624
        } while (ch == ' ');
496
497
37
        if (ch == '/') {
498
3
            *pos = p;
499
3
            return NXT_OK;
500
3
        }
501
37
    }
502
503
    /* absolute path or '*' */
504
505
    /* TODO */
506
507
94
    return NXT_HTTP_PARSE_INVALID;
508
125
}
509
510
511
static nxt_int_t
512
nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
513
    const u_char *end)
514
45.9k
{
515
45.9k
    u_char    *p, c;
516
45.9k
    size_t    len;
517
45.9k
    uint32_t  hash;
518
519
45.9k
    static const u_char  normal[256]  nxt_aligned(64) =
520
45.9k
        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
521
    /*   \s ! " # $ % & ' ( ) * + ,        . /                 : ; < = > ?   */
522
45.9k
        "\0\1\0\1\1\1\1\1\0\0\1\1\0" "-" "\1\0" "0123456789" "\0\0\0\0\0\0"
523
524
    /*    @                                 [ \ ] ^ _                        */
525
45.9k
        "\0" "abcdefghijklmnopqrstuvwxyz" "\0\0\0\1\1"
526
    /*    `                                 { | } ~                          */
527
45.9k
        "\1" "abcdefghijklmnopqrstuvwxyz" "\0\1\0\1\0"
528
529
45.9k
        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
530
45.9k
        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
531
45.9k
        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
532
45.9k
        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
533
534
45.9k
    p = *pos + rp->field_name.length;
535
45.9k
    hash = rp->field_hash;
536
537
48.5k
    while (nxt_fast_path(end - p >= 8)) {
538
539
48.5k
#define nxt_field_name_test_char(ch)                                          \
540
48.5k
                                                                              \
541
171k
        c = normal[ch];                                                       \
542
171k
                                                                              \
543
171k
        if (nxt_slow_path(c <= '\1')) {                                       \
544
59.6k
            if (c == '\0') {                                                  \
545
44.8k
                p = &(ch);                                                    \
546
44.8k
                goto name_end;                                                \
547
44.8k
            }                                                                 \
548
59.6k
                                                                              \
549
59.6k
            rp->skip_field = rp->discard_unsafe_fields;                       \
550
14.8k
            c = ch;                                                           \
551
14.8k
        }                                                                     \
552
171k
                                                                              \
553
171k
        hash = nxt_http_field_hash_char(hash, c);
554
555
/* enddef */
556
557
48.5k
        nxt_field_name_test_char(p[0]);
558
48.2k
        nxt_field_name_test_char(p[1]);
559
16.2k
        nxt_field_name_test_char(p[2]);
560
14.1k
        nxt_field_name_test_char(p[3]);
561
562
12.8k
        nxt_field_name_test_char(p[4]);
563
10.6k
        nxt_field_name_test_char(p[5]);
564
8.93k
        nxt_field_name_test_char(p[6]);
565
7.13k
        nxt_field_name_test_char(p[7]);
566
567
6.68k
        p += 8;
568
6.68k
    }
569
570
4.87k
    while (nxt_fast_path(p != end)) {
571
4.87k
        nxt_field_name_test_char(*p); p++;
572
1.91k
    }
573
574
1.18k
    len = p - *pos;
575
576
1.18k
    if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) {
577
14
        return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
578
14
    }
579
580
1.17k
    rp->field_hash = hash;
581
1.17k
    rp->field_name.length = len;
582
583
1.17k
    rp->handler = &nxt_http_parse_field_name;
584
585
1.17k
    return NXT_AGAIN;
586
587
44.8k
name_end:
588
589
44.8k
    if (nxt_fast_path(*p == ':')) {
590
42.8k
        if (nxt_slow_path(p == *pos)) {
591
26
            return NXT_HTTP_PARSE_INVALID;
592
26
        }
593
594
42.8k
        len = p - *pos;
595
596
42.8k
        if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) {
597
6
            return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
598
6
        }
599
600
42.8k
        rp->field_hash = hash;
601
602
42.8k
        rp->field_name.length = len;
603
42.8k
        rp->field_name.start = *pos;
604
605
42.8k
        *pos = p + 1;
606
607
42.8k
        return nxt_http_parse_field_value(rp, pos, end);
608
42.8k
    }
609
610
1.91k
    if (nxt_slow_path(p != *pos)) {
611
167
        return NXT_HTTP_PARSE_INVALID;
612
167
    }
613
614
1.74k
    return nxt_http_parse_field_end(rp, pos, end);
615
1.91k
}
616
617
618
static nxt_int_t
619
nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos,
620
    const u_char *end)
621
42.8k
{
622
42.8k
    u_char  *p, *start, ch;
623
42.8k
    size_t  len;
624
625
42.8k
    p = *pos;
626
627
44.7k
    for ( ;; ) {
628
44.7k
        if (nxt_slow_path(p == end)) {
629
98
            *pos = p;
630
98
            rp->handler = &nxt_http_parse_field_value;
631
98
            return NXT_AGAIN;
632
98
        }
633
634
44.6k
        ch = *p;
635
636
44.6k
        if (ch != ' ' && ch != '\t') {
637
42.7k
            break;
638
42.7k
        }
639
640
1.84k
        p++;
641
1.84k
    }
642
643
42.7k
    start = p;
644
645
42.7k
    p += rp->field_value.length;
646
647
51.1k
    for ( ;; ) {
648
51.1k
        p = nxt_http_lookup_field_end(p, end);
649
650
51.1k
        if (nxt_slow_path(p == end)) {
651
361
            *pos = start;
652
653
361
            len = p - start;
654
655
361
            if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) {
656
0
                return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
657
0
            }
658
659
361
            rp->field_value.length = len;
660
361
            rp->handler = &nxt_http_parse_field_value;
661
361
            return NXT_AGAIN;
662
361
        }
663
664
50.8k
        ch = *p;
665
666
50.8k
        if (nxt_fast_path(ch == '\r' || ch == '\n')) {
667
41.9k
            break;
668
41.9k
        }
669
670
8.87k
        if (ch != '\t') {
671
443
            return NXT_HTTP_PARSE_INVALID;
672
443
        }
673
674
8.43k
        p++;
675
8.43k
    }
676
677
41.9k
    *pos = p;
678
679
41.9k
    if (nxt_fast_path(p != start)) {
680
681
5.20k
        while (p[-1] == ' ' || p[-1] == '\t') {
682
1.56k
            p--;
683
1.56k
        }
684
3.64k
    }
685
686
41.9k
    len = p - start;
687
688
41.9k
    if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) {
689
0
        return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
690
0
    }
691
692
41.9k
    rp->field_value.length = len;
693
41.9k
    rp->field_value.start = start;
694
695
41.9k
    return nxt_http_parse_field_end(rp, pos, end);
696
41.9k
}
697
698
699
static u_char *
700
nxt_http_lookup_field_end(u_char *p, const u_char *end)
701
51.1k
{
702
51.1k
    while (nxt_fast_path(end - p >= 16)) {
703
704
48.2k
#define nxt_field_end_test_char(ch)                                           \
705
48.2k
                                                                              \
706
146k
        if (nxt_slow_path((ch) < 0x20)) {                                     \
707
50.8k
            return &(ch);                                                     \
708
50.8k
        }
709
710
/* enddef */
711
712
48.2k
        nxt_field_end_test_char(p[0]);
713
11.1k
        nxt_field_end_test_char(p[1]);
714
9.51k
        nxt_field_end_test_char(p[2]);
715
8.58k
        nxt_field_end_test_char(p[3]);
716
717
7.77k
        nxt_field_end_test_char(p[4]);
718
7.10k
        nxt_field_end_test_char(p[5]);
719
6.21k
        nxt_field_end_test_char(p[6]);
720
5.48k
        nxt_field_end_test_char(p[7]);
721
722
4.86k
        nxt_field_end_test_char(p[8]);
723
4.59k
        nxt_field_end_test_char(p[9]);
724
4.16k
        nxt_field_end_test_char(p[10]);
725
3.79k
        nxt_field_end_test_char(p[11]);
726
727
3.55k
        nxt_field_end_test_char(p[12]);
728
3.29k
        nxt_field_end_test_char(p[13]);
729
3.00k
        nxt_field_end_test_char(p[14]);
730
2.75k
        nxt_field_end_test_char(p[15]);
731
732
2.47k
        p += 16;
733
2.47k
    }
734
735
5.39k
    while (nxt_fast_path(end - p >= 4)) {
736
737
4.38k
        nxt_field_end_test_char(p[0]);
738
2.07k
        nxt_field_end_test_char(p[1]);
739
1.85k
        nxt_field_end_test_char(p[2]);
740
1.54k
        nxt_field_end_test_char(p[3]);
741
742
948
        p += 4;
743
948
    }
744
745
1.95k
    switch (end - p) {
746
475
    case 3:
747
475
        nxt_field_end_test_char(*p); p++;
748
        /* Fall through. */
749
1.26k
    case 2:
750
1.26k
        nxt_field_end_test_char(*p); p++;
751
        /* Fall through. */
752
645
    case 1:
753
645
        nxt_field_end_test_char(*p); p++;
754
        /* Fall through. */
755
361
    case 0:
756
361
        break;
757
0
    default:
758
0
        nxt_unreachable();
759
1.95k
    }
760
761
361
    return p;
762
1.95k
}
763
764
765
static nxt_int_t
766
nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos,
767
    const u_char *end)
768
43.6k
{
769
43.6k
    u_char            *p;
770
43.6k
    nxt_http_field_t  *field;
771
772
43.6k
    p = *pos;
773
774
43.6k
    if (nxt_fast_path(*p == '\r')) {
775
1.05k
        p++;
776
777
1.05k
        if (nxt_slow_path(p == end)) {
778
36
            rp->handler = &nxt_http_parse_field_end;
779
36
            return NXT_AGAIN;
780
36
        }
781
1.05k
    }
782
783
43.6k
    if (nxt_fast_path(*p == '\n')) {
784
43.5k
        *pos = p + 1;
785
786
43.5k
        if (rp->field_name.length != 0) {
787
41.8k
            if (rp->skip_field) {
788
0
                rp->skip_field = 0;
789
790
41.8k
            } else {
791
41.8k
                field = nxt_list_add(rp->fields);
792
793
41.8k
                if (nxt_slow_path(field == NULL)) {
794
0
                    return NXT_ERROR;
795
0
                }
796
797
41.8k
                field->hash = nxt_http_field_hash_end(rp->field_hash);
798
41.8k
                field->skip = 0;
799
41.8k
                field->hopbyhop = 0;
800
801
41.8k
                field->name_length = rp->field_name.length;
802
41.8k
                field->value_length = rp->field_value.length;
803
41.8k
                field->name = rp->field_name.start;
804
41.8k
                field->value = rp->field_value.start;
805
41.8k
            }
806
807
41.8k
            rp->field_hash = NXT_HTTP_FIELD_HASH_INIT;
808
809
41.8k
            rp->field_name.length = 0;
810
41.8k
            rp->field_value.length = 0;
811
812
41.8k
            rp->handler = &nxt_http_parse_field_name;
813
41.8k
            return NXT_OK;
814
41.8k
        }
815
816
1.65k
        return NXT_DONE;
817
43.5k
    }
818
819
140
    return NXT_HTTP_PARSE_INVALID;
820
43.6k
}
821
822
823
#define nxt_http_is_normal(c)                                                 \
824
56.4k
    (nxt_fast_path((nxt_http_normal[c / 8] & (1 << (c & 7))) != 0))
825
826
827
static const uint8_t  nxt_http_normal[32]  nxt_aligned(32) = {
828
829
                             /*        \0   \r  \n                         */
830
    0xFE, 0xDB, 0xFF, 0xFF,  /* 1111 1110  1101 1011  1111 1111  1111 1111 */
831
832
                             /* '&%$ #"!   /.-, |*)(  7654 3210  ?>=< ;:98 */
833
    0xD6, 0x37, 0xFF, 0x7F,  /* 1101 0110  0011 0111  1111 1111  0111 1111 */
834
835
                             /* GFED CBA@  ONML KJIH  WVUT SRQP  _^]\ [ZYX */
836
    0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
837
838
                             /* gfed cba`  onml kjih  wvut srqp   ~}| {zyx */
839
    0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
840
841
    0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
842
    0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
843
    0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
844
    0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
845
};
846
847
848
nxt_int_t
849
nxt_http_parse_complex_target(nxt_http_request_parse_t *rp)
850
1.14k
{
851
1.14k
    u_char  *p, *u, c, ch, high, *args;
852
853
1.14k
    enum {
854
1.14k
        sw_normal = 0,
855
1.14k
        sw_slash,
856
1.14k
        sw_dot,
857
1.14k
        sw_dot_dot,
858
1.14k
        sw_quoted,
859
1.14k
        sw_quoted_second,
860
1.14k
    } state, saved_state;
861
862
1.14k
    nxt_prefetch(nxt_http_normal);
863
864
1.14k
    state = sw_normal;
865
1.14k
    saved_state = sw_normal;
866
1.14k
    p = rp->target_start;
867
868
1.14k
    u = nxt_mp_alloc(rp->mem_pool, rp->target_end - p + 1);
869
1.14k
    if (nxt_slow_path(u == NULL)) {
870
0
        return NXT_ERROR;
871
0
    }
872
873
1.14k
    rp->path.length = 0;
874
1.14k
    rp->path.start = u;
875
876
1.14k
    high = '\0';
877
1.14k
    args = NULL;
878
879
68.9k
    while (p < rp->target_end) {
880
881
67.9k
        ch = *p++;
882
883
73.2k
    again:
884
885
73.2k
        switch (state) {
886
887
27.8k
        case sw_normal:
888
889
27.8k
            if (nxt_http_is_normal(ch)) {
890
12.0k
                *u++ = ch;
891
12.0k
                continue;
892
12.0k
            }
893
894
15.8k
            switch (ch) {
895
8.69k
            case '/':
896
8.69k
                state = sw_slash;
897
8.69k
                *u++ = ch;
898
8.69k
                continue;
899
5.47k
            case '%':
900
5.47k
                saved_state = state;
901
5.47k
                state = sw_quoted;
902
5.47k
                continue;
903
12
            case '?':
904
12
                args = p;
905
12
                goto args;
906
20
            case '#':
907
20
                goto done;
908
1.63k
            default:
909
1.63k
                *u++ = ch;
910
1.63k
                continue;
911
15.8k
            }
912
913
0
            break;
914
915
15.4k
        case sw_slash:
916
917
15.4k
            if (nxt_http_is_normal(ch)) {
918
2.17k
                state = sw_normal;
919
2.17k
                *u++ = ch;
920
2.17k
                continue;
921
2.17k
            }
922
923
13.3k
            switch (ch) {
924
2.18k
            case '/':
925
2.18k
                continue;
926
7.77k
            case '.':
927
7.77k
                state = sw_dot;
928
7.77k
                *u++ = ch;
929
7.77k
                continue;
930
1.66k
            case '%':
931
1.66k
                saved_state = state;
932
1.66k
                state = sw_quoted;
933
1.66k
                continue;
934
45
            case '?':
935
45
                args = p;
936
45
                goto args;
937
4
            case '#':
938
4
                goto done;
939
1.62k
            default:
940
1.62k
                state = sw_normal;
941
1.62k
                *u++ = ch;
942
1.62k
                continue;
943
13.3k
            }
944
945
0
            break;
946
947
8.53k
        case sw_dot:
948
949
8.53k
            if (nxt_http_is_normal(ch)) {
950
990
                state = sw_normal;
951
990
                *u++ = ch;
952
990
                continue;
953
990
            }
954
955
7.54k
            switch (ch) {
956
1.88k
            case '/':
957
1.88k
                state = sw_slash;
958
1.88k
                u--;
959
1.88k
                continue;
960
3.92k
            case '.':
961
3.92k
                state = sw_dot_dot;
962
3.92k
                *u++ = ch;
963
3.92k
                continue;
964
789
            case '%':
965
789
                saved_state = state;
966
789
                state = sw_quoted;
967
789
                continue;
968
17
            case '?':
969
17
                u--;
970
17
                args = p;
971
17
                goto args;
972
4
            case '#':
973
4
                u--;
974
4
                goto done;
975
925
            default:
976
925
                state = sw_normal;
977
925
                *u++ = ch;
978
925
                continue;
979
7.54k
            }
980
981
0
            break;
982
983
4.54k
        case sw_dot_dot:
984
985
4.54k
            if (nxt_http_is_normal(ch)) {
986
1.15k
                state = sw_normal;
987
1.15k
                *u++ = ch;
988
1.15k
                continue;
989
1.15k
            }
990
991
3.38k
            switch (ch) {
992
993
1.64k
            case '/':
994
1.65k
            case '?':
995
1.65k
            case '#':
996
1.65k
                u -= 5;
997
998
4.78k
                for ( ;; ) {
999
4.78k
                    if (u < rp->path.start) {
1000
28
                        return NXT_HTTP_PARSE_INVALID;
1001
28
                    }
1002
1003
4.75k
                    if (*u == '/') {
1004
1.62k
                        u++;
1005
1.62k
                        break;
1006
1.62k
                    }
1007
1008
3.12k
                    u--;
1009
3.12k
                }
1010
1011
1.62k
                if (ch == '?') {
1012
5
                    args = p;
1013
5
                    goto args;
1014
5
                }
1015
1016
1.62k
                if (ch == '#') {
1017
3
                    goto done;
1018
3
                }
1019
1020
1.62k
                state = sw_slash;
1021
1.62k
                break;
1022
1023
691
            case '%':
1024
691
                saved_state = state;
1025
691
                state = sw_quoted;
1026
691
                continue;
1027
1028
1.03k
            default:
1029
1.03k
                state = sw_normal;
1030
1.03k
                *u++ = ch;
1031
1.03k
                continue;
1032
3.38k
            }
1033
1034
1.62k
            break;
1035
1036
8.49k
        case sw_quoted:
1037
8.49k
            rp->quoted_target = 1;
1038
1039
8.49k
            if (ch >= '0' && ch <= '9') {
1040
6.27k
                high = (u_char) (ch - '0');
1041
6.27k
                state = sw_quoted_second;
1042
6.27k
                continue;
1043
6.27k
            }
1044
1045
2.22k
            c = (u_char) (ch | 0x20);
1046
2.22k
            if (c >= 'a' && c <= 'f') {
1047
2.14k
                high = (u_char) (c - 'a' + 10);
1048
2.14k
                state = sw_quoted_second;
1049
2.14k
                continue;
1050
2.14k
            }
1051
1052
81
            return NXT_HTTP_PARSE_INVALID;
1053
1054
8.35k
        case sw_quoted_second:
1055
8.35k
            if (ch >= '0' && ch <= '9') {
1056
3.71k
                ch = (u_char) ((high << 4) + ch - '0');
1057
1058
3.71k
                if (ch == '%') {
1059
1.03k
                    state = sw_normal;
1060
1.03k
                    *u++ = '%';
1061
1062
1.03k
                    if (rp->encoded_slashes) {
1063
0
                        *u++ = '2';
1064
0
                        *u++ = '5';
1065
0
                    }
1066
1067
1.03k
                    continue;
1068
1.03k
                }
1069
1070
2.67k
                if (ch == '#') {
1071
1.10k
                    state = sw_normal;
1072
1.10k
                    *u++ = '#';
1073
1.10k
                    continue;
1074
1.10k
                }
1075
1076
1.57k
                if (ch == '\0') {
1077
3
                    return NXT_HTTP_PARSE_INVALID;
1078
3
                }
1079
1080
1.56k
                state = saved_state;
1081
1.56k
                goto again;
1082
1.57k
            }
1083
1084
4.63k
            c = (u_char) (ch | 0x20);
1085
4.63k
            if (c >= 'a' && c <= 'f') {
1086
4.56k
                ch = (u_char) ((high << 4) + c - 'a' + 10);
1087
1088
4.56k
                if (ch == '?') {
1089
997
                    state = sw_normal;
1090
997
                    *u++ = ch;
1091
997
                    continue;
1092
997
                }
1093
1094
3.56k
                if (ch == '/' && rp->encoded_slashes) {
1095
0
                    state = sw_normal;
1096
0
                    *u++ = '%';
1097
0
                    *u++ = '2';
1098
0
                    *u++ = p[-1];  /* 'f' or 'F' */
1099
0
                    continue;
1100
0
                }
1101
1102
3.56k
                state = saved_state;
1103
3.56k
                goto again;
1104
3.56k
            }
1105
1106
79
            return NXT_HTTP_PARSE_INVALID;
1107
73.2k
        }
1108
73.2k
    }
1109
1110
1.03k
    if (state >= sw_dot) {
1111
378
        if (state >= sw_quoted) {
1112
184
            return NXT_HTTP_PARSE_INVALID;
1113
184
        }
1114
1115
        /* "/." and "/.." must be normalized similar to "/./" and "/../". */
1116
194
        ch = '/';
1117
194
        goto again;
1118
378
    }
1119
1120
737
args:
1121
1122
3.50k
    for (/* void */; p < rp->target_end; p++) {
1123
2.80k
        if (*p == '#') {
1124
38
            break;
1125
38
        }
1126
2.80k
    }
1127
1128
737
    if (args != NULL) {
1129
79
        rp->args.length = p - args;
1130
79
        rp->args.start = args;
1131
79
    }
1132
1133
768
done:
1134
1135
768
    rp->path.length = u - rp->path.start;
1136
1137
768
    return NXT_OK;
1138
737
}
1139
1140
1141
const nxt_lvlhsh_proto_t  nxt_http_fields_hash_proto  nxt_aligned(64) = {
1142
    NXT_LVLHSH_BUCKET_SIZE(64),
1143
    { NXT_HTTP_FIELD_LVLHSH_SHIFT, 0, 0, 0, 0, 0, 0, 0 },
1144
    nxt_http_field_hash_test,
1145
    nxt_lvlhsh_alloc,
1146
    nxt_lvlhsh_free,
1147
};
1148
1149
1150
static nxt_int_t
1151
nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
1152
5.18k
{
1153
5.18k
    nxt_http_field_proc_t  *field;
1154
1155
5.18k
    field = data;
1156
1157
5.18k
    if (nxt_strcasestr_eq(&lhq->key, &field->name)) {
1158
3.50k
        return NXT_OK;
1159
3.50k
    }
1160
1161
1.68k
    return NXT_DECLINED;
1162
5.18k
}
1163
1164
1165
static nxt_int_t
1166
nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, void *data)
1167
0
{
1168
0
    return NXT_OK;
1169
0
}
1170
1171
1172
nxt_int_t
1173
nxt_http_fields_hash(nxt_lvlhsh_t *hash,
1174
    nxt_http_field_proc_t items[], nxt_uint_t count)
1175
6
{
1176
6
    u_char              ch;
1177
6
    uint32_t            key;
1178
6
    nxt_str_t           *name;
1179
6
    nxt_int_t           ret;
1180
6
    nxt_uint_t          i, j;
1181
6
    nxt_lvlhsh_query_t  lhq;
1182
1183
6
    lhq.replace = 0;
1184
6
    lhq.proto = &nxt_http_fields_hash_proto;
1185
6
    lhq.pool = NULL;
1186
1187
42
    for (i = 0; i < count; i++) {
1188
36
        key = NXT_HTTP_FIELD_HASH_INIT;
1189
36
        name = &items[i].name;
1190
1191
442
        for (j = 0; j < name->length; j++) {
1192
406
            ch = nxt_lowcase(name->start[j]);
1193
406
            key = nxt_http_field_hash_char(key, ch);
1194
406
        }
1195
1196
36
        lhq.key_hash = nxt_http_field_hash_end(key) & 0xFFFF;
1197
36
        lhq.key = *name;
1198
36
        lhq.value = &items[i];
1199
1200
36
        ret = nxt_lvlhsh_insert(hash, &lhq);
1201
1202
36
        if (nxt_slow_path(ret != NXT_OK)) {
1203
0
            return NXT_ERROR;
1204
0
        }
1205
36
    }
1206
1207
6
    return NXT_OK;
1208
6
}
1209
1210
1211
nxt_uint_t
1212
nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash,
1213
    nxt_http_field_proc_t items[], nxt_uint_t count, nxt_bool_t level)
1214
0
{
1215
0
    u_char              ch;
1216
0
    uint32_t            key, mask;
1217
0
    nxt_str_t           *name;
1218
0
    nxt_uint_t          colls, i, j;
1219
0
    nxt_lvlhsh_proto_t  proto;
1220
0
    nxt_lvlhsh_query_t  lhq;
1221
1222
0
    proto = nxt_http_fields_hash_proto;
1223
0
    proto.test = nxt_http_field_hash_collision;
1224
1225
0
    lhq.replace = 0;
1226
0
    lhq.proto = &proto;
1227
1228
0
    mask = level ? (1 << NXT_HTTP_FIELD_LVLHSH_SHIFT) - 1 : 0xFFFF;
1229
1230
0
    colls = 0;
1231
1232
0
    for (i = 0; i < count; i++) {
1233
0
        key = NXT_HTTP_FIELD_HASH_INIT;
1234
0
        name = &items[i].name;
1235
1236
0
        for (j = 0; j < name->length; j++) {
1237
0
            ch = nxt_lowcase(name->start[j]);
1238
0
            key = nxt_http_field_hash_char(key, ch);
1239
0
        }
1240
1241
0
        lhq.key_hash = nxt_http_field_hash_end(key) & mask;
1242
0
        lhq.value = &items[i];
1243
1244
0
        if (nxt_lvlhsh_insert(hash, &lhq) == NXT_DECLINED) {
1245
0
            colls++;
1246
0
        }
1247
0
    }
1248
1249
0
    return colls;
1250
0
}
1251
1252
1253
nxt_int_t
1254
nxt_http_fields_process(nxt_list_t *fields, nxt_lvlhsh_t *hash, void *ctx)
1255
1.65k
{
1256
1.65k
    nxt_int_t         ret;
1257
1.65k
    nxt_http_field_t  *field;
1258
1259
21.5k
    nxt_list_each(field, fields) {
1260
1261
21.5k
        ret = nxt_http_field_process(field, hash, ctx);
1262
21.5k
        if (nxt_slow_path(ret != NXT_OK)) {
1263
339
            return ret;
1264
339
        }
1265
1266
21.5k
    } nxt_list_loop;
1267
1268
1.31k
    return NXT_OK;
1269
1.65k
}