Coverage Report

Created: 2023-11-19 06:57

/src/nginx/src/http/ngx_http_parse.c
Line
Count
Source (jump to first uncovered line)
1
2
/*
3
 * Copyright (C) Igor Sysoev
4
 * Copyright (C) Nginx, Inc.
5
 */
6
7
8
#include <ngx_config.h>
9
#include <ngx_core.h>
10
#include <ngx_http.h>
11
12
13
static uint32_t  usual[] = {
14
    0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
15
16
                /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
17
    0x7fff37d6, /* 0111 1111 1111 1111  0011 0111 1101 0110 */
18
19
                /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
20
#if (NGX_WIN32)
21
    0xefffffff, /* 1110 1111 1111 1111  1111 1111 1111 1111 */
22
#else
23
    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
24
#endif
25
26
                /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
27
    0x7fffffff, /* 0111 1111 1111 1111  1111 1111 1111 1111 */
28
29
    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
30
    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
31
    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
32
    0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
33
};
34
35
36
#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
37
38
#define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
39
4
    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
40
41
#define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
42
0
    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
43
44
#define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
45
0
    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
46
47
#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
48
18
    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
49
18
        && m[4] == c4
50
51
#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
52
4
    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
53
4
        && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)
54
55
#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
56
0
    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
57
0
        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
58
59
#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
60
0
    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
61
0
        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
62
63
#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
64
0
    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
65
0
        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)  \
66
0
        && m[8] == c8
67
68
#else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */
69
70
#define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
71
    m[0] == c0 && m[1] == c1 && m[2] == c2
72
73
#define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
74
    m[0] == c0 && m[2] == c2 && m[3] == c3
75
76
#define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
77
    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3
78
79
#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
80
    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
81
82
#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
83
    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
84
        && m[4] == c4 && m[5] == c5
85
86
#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
87
    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
88
        && m[4] == c4 && m[5] == c5 && m[6] == c6
89
90
#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
91
    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
92
        && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7
93
94
#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
95
    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
96
        && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8
97
98
#endif
99
100
101
/* gcc, icc, msvc and others compile these switches as an jump table */
102
103
ngx_int_t
104
ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
105
83
{
106
83
    u_char  c, ch, *p, *m;
107
83
    enum {
108
83
        sw_start = 0,
109
83
        sw_method,
110
83
        sw_spaces_before_uri,
111
83
        sw_schema,
112
83
        sw_schema_slash,
113
83
        sw_schema_slash_slash,
114
83
        sw_host_start,
115
83
        sw_host,
116
83
        sw_host_end,
117
83
        sw_host_ip_literal,
118
83
        sw_port,
119
83
        sw_after_slash_in_uri,
120
83
        sw_check_uri,
121
83
        sw_uri,
122
83
        sw_http_09,
123
83
        sw_http_H,
124
83
        sw_http_HT,
125
83
        sw_http_HTT,
126
83
        sw_http_HTTP,
127
83
        sw_first_major_digit,
128
83
        sw_major_digit,
129
83
        sw_first_minor_digit,
130
83
        sw_minor_digit,
131
83
        sw_spaces_after_digit,
132
83
        sw_almost_done
133
83
    } state;
134
135
83
    state = r->state;
136
137
68.1k
    for (p = b->pos; p < b->last; p++) {
138
68.0k
        ch = *p;
139
140
68.0k
        switch (state) {
141
142
        /* HTTP methods: GET, HEAD, POST */
143
65.0k
        case sw_start:
144
65.0k
            r->request_start = p;
145
146
65.0k
            if (ch == CR || ch == LF) {
147
65.0k
                break;
148
65.0k
            }
149
150
19
            if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
151
0
                return NGX_HTTP_PARSE_INVALID_METHOD;
152
0
            }
153
154
19
            state = sw_method;
155
19
            break;
156
157
333
        case sw_method:
158
333
            if (ch == ' ') {
159
18
                r->method_end = p - 1;
160
18
                m = r->request_start;
161
162
18
                switch (p - m) {
163
164
3
                case 3:
165
3
                    if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
166
2
                        r->method = NGX_HTTP_GET;
167
2
                        break;
168
2
                    }
169
170
1
                    if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
171
0
                        r->method = NGX_HTTP_PUT;
172
0
                        break;
173
0
                    }
174
175
1
                    break;
176
177
1
                case 4:
178
0
                    if (m[1] == 'O') {
179
180
0
                        if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
181
0
                            r->method = NGX_HTTP_POST;
182
0
                            break;
183
0
                        }
184
185
0
                        if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
186
0
                            r->method = NGX_HTTP_COPY;
187
0
                            break;
188
0
                        }
189
190
0
                        if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
191
0
                            r->method = NGX_HTTP_MOVE;
192
0
                            break;
193
0
                        }
194
195
0
                        if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
196
0
                            r->method = NGX_HTTP_LOCK;
197
0
                            break;
198
0
                        }
199
200
0
                    } else {
201
202
0
                        if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
203
0
                            r->method = NGX_HTTP_HEAD;
204
0
                            break;
205
0
                        }
206
0
                    }
207
208
0
                    break;
209
210
6
                case 5:
211
6
                    if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
212
0
                        r->method = NGX_HTTP_MKCOL;
213
0
                        break;
214
0
                    }
215
216
6
                    if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
217
0
                        r->method = NGX_HTTP_PATCH;
218
0
                        break;
219
0
                    }
220
221
6
                    if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
222
4
                        r->method = NGX_HTTP_TRACE;
223
4
                        break;
224
4
                    }
225
226
2
                    break;
227
228
2
                case 6:
229
2
                    if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
230
0
                        r->method = NGX_HTTP_DELETE;
231
0
                        break;
232
0
                    }
233
234
2
                    if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
235
0
                        r->method = NGX_HTTP_UNLOCK;
236
0
                        break;
237
0
                    }
238
239
2
                    break;
240
241
2
                case 7:
242
0
                    if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
243
0
                    {
244
0
                        r->method = NGX_HTTP_OPTIONS;
245
0
                    }
246
247
0
                    if (ngx_str7_cmp(m, 'C', 'O', 'N', 'N', 'E', 'C', 'T', ' '))
248
0
                    {
249
0
                        r->method = NGX_HTTP_CONNECT;
250
0
                    }
251
252
0
                    break;
253
254
0
                case 8:
255
0
                    if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
256
0
                    {
257
0
                        r->method = NGX_HTTP_PROPFIND;
258
0
                    }
259
260
0
                    break;
261
262
0
                case 9:
263
0
                    if (ngx_str9cmp(m,
264
0
                            'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
265
0
                    {
266
0
                        r->method = NGX_HTTP_PROPPATCH;
267
0
                    }
268
269
0
                    break;
270
18
                }
271
272
18
                state = sw_spaces_before_uri;
273
18
                break;
274
18
            }
275
276
315
            if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
277
1
                return NGX_HTTP_PARSE_INVALID_METHOD;
278
1
            }
279
280
314
            break;
281
282
        /* space* before URI */
283
314
        case sw_spaces_before_uri:
284
285
18
            if (ch == '/') {
286
16
                r->uri_start = p;
287
16
                state = sw_after_slash_in_uri;
288
16
                break;
289
16
            }
290
291
2
            c = (u_char) (ch | 0x20);
292
2
            if (c >= 'a' && c <= 'z') {
293
2
                r->schema_start = p;
294
2
                state = sw_schema;
295
2
                break;
296
2
            }
297
298
0
            switch (ch) {
299
0
            case ' ':
300
0
                break;
301
0
            default:
302
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
303
0
            }
304
0
            break;
305
306
8
        case sw_schema:
307
308
8
            c = (u_char) (ch | 0x20);
309
8
            if (c >= 'a' && c <= 'z') {
310
6
                break;
311
6
            }
312
313
2
            if ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')
314
0
            {
315
0
                break;
316
0
            }
317
318
2
            switch (ch) {
319
2
            case ':':
320
2
                r->schema_end = p;
321
2
                state = sw_schema_slash;
322
2
                break;
323
0
            default:
324
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
325
2
            }
326
2
            break;
327
328
2
        case sw_schema_slash:
329
2
            switch (ch) {
330
2
            case '/':
331
2
                state = sw_schema_slash_slash;
332
2
                break;
333
0
            default:
334
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
335
2
            }
336
2
            break;
337
338
2
        case sw_schema_slash_slash:
339
2
            switch (ch) {
340
2
            case '/':
341
2
                state = sw_host_start;
342
2
                break;
343
0
            default:
344
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
345
2
            }
346
2
            break;
347
348
2
        case sw_host_start:
349
350
2
            r->host_start = p;
351
352
2
            if (ch == '[') {
353
2
                state = sw_host_ip_literal;
354
2
                break;
355
2
            }
356
357
0
            state = sw_host;
358
359
            /* fall through */
360
361
0
        case sw_host:
362
363
0
            c = (u_char) (ch | 0x20);
364
0
            if (c >= 'a' && c <= 'z') {
365
0
                break;
366
0
            }
367
368
0
            if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
369
0
                break;
370
0
            }
371
372
            /* fall through */
373
374
0
        case sw_host_end:
375
376
0
            r->host_end = p;
377
378
0
            switch (ch) {
379
0
            case ':':
380
0
                state = sw_port;
381
0
                break;
382
0
            case '/':
383
0
                r->uri_start = p;
384
0
                state = sw_after_slash_in_uri;
385
0
                break;
386
0
            case '?':
387
0
                r->uri_start = p;
388
0
                r->args_start = p + 1;
389
0
                r->empty_path_in_uri = 1;
390
0
                state = sw_uri;
391
0
                break;
392
0
            case ' ':
393
                /*
394
                 * use single "/" from request line to preserve pointers,
395
                 * if request line will be copied to large client buffer
396
                 */
397
0
                r->uri_start = r->schema_end + 1;
398
0
                r->uri_end = r->schema_end + 2;
399
0
                state = sw_http_09;
400
0
                break;
401
0
            default:
402
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
403
0
            }
404
0
            break;
405
406
377
        case sw_host_ip_literal:
407
408
377
            if (ch >= '0' && ch <= '9') {
409
38
                break;
410
38
            }
411
412
339
            c = (u_char) (ch | 0x20);
413
339
            if (c >= 'a' && c <= 'z') {
414
156
                break;
415
156
            }
416
417
183
            switch (ch) {
418
3
            case ':':
419
3
                break;
420
0
            case ']':
421
0
                state = sw_host_end;
422
0
                break;
423
7
            case '-':
424
10
            case '.':
425
10
            case '_':
426
11
            case '~':
427
                /* unreserved */
428
11
                break;
429
0
            case '!':
430
0
            case '$':
431
0
            case '&':
432
0
            case '\'':
433
42
            case '(':
434
42
            case ')':
435
42
            case '*':
436
42
            case '+':
437
42
            case ',':
438
167
            case ';':
439
167
            case '=':
440
                /* sub-delims */
441
167
                break;
442
2
            default:
443
2
                return NGX_HTTP_PARSE_INVALID_REQUEST;
444
183
            }
445
181
            break;
446
447
181
        case sw_port:
448
0
            if (ch >= '0' && ch <= '9') {
449
0
                break;
450
0
            }
451
452
0
            switch (ch) {
453
0
            case '/':
454
0
                r->port_end = p;
455
0
                r->uri_start = p;
456
0
                state = sw_after_slash_in_uri;
457
0
                break;
458
0
            case '?':
459
0
                r->port_end = p;
460
0
                r->uri_start = p;
461
0
                r->args_start = p + 1;
462
0
                r->empty_path_in_uri = 1;
463
0
                state = sw_uri;
464
0
                break;
465
0
            case ' ':
466
0
                r->port_end = p;
467
                /*
468
                 * use single "/" from request line to preserve pointers,
469
                 * if request line will be copied to large client buffer
470
                 */
471
0
                r->uri_start = r->schema_end + 1;
472
0
                r->uri_end = r->schema_end + 2;
473
0
                state = sw_http_09;
474
0
                break;
475
0
            default:
476
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
477
0
            }
478
0
            break;
479
480
        /* check "/.", "//", "%", and "\" (Win32) in URI */
481
16
        case sw_after_slash_in_uri:
482
483
16
            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
484
8
                state = sw_check_uri;
485
8
                break;
486
8
            }
487
488
8
            switch (ch) {
489
3
            case ' ':
490
3
                r->uri_end = p;
491
3
                state = sw_http_09;
492
3
                break;
493
0
            case CR:
494
0
                r->uri_end = p;
495
0
                r->http_minor = 9;
496
0
                state = sw_almost_done;
497
0
                break;
498
1
            case LF:
499
1
                r->uri_end = p;
500
1
                r->http_minor = 9;
501
1
                goto done;
502
0
            case '.':
503
0
                r->complex_uri = 1;
504
0
                state = sw_uri;
505
0
                break;
506
0
            case '%':
507
0
                r->quoted_uri = 1;
508
0
                state = sw_uri;
509
0
                break;
510
4
            case '/':
511
4
                r->complex_uri = 1;
512
4
                state = sw_uri;
513
4
                break;
514
#if (NGX_WIN32)
515
            case '\\':
516
                r->complex_uri = 1;
517
                state = sw_uri;
518
                break;
519
#endif
520
0
            case '?':
521
0
                r->args_start = p + 1;
522
0
                state = sw_uri;
523
0
                break;
524
0
            case '#':
525
0
                r->complex_uri = 1;
526
0
                state = sw_uri;
527
0
                break;
528
0
            case '+':
529
0
                r->plus_in_uri = 1;
530
0
                break;
531
0
            default:
532
0
                if (ch < 0x20 || ch == 0x7f) {
533
0
                    return NGX_HTTP_PARSE_INVALID_REQUEST;
534
0
                }
535
0
                state = sw_check_uri;
536
0
                break;
537
8
            }
538
7
            break;
539
540
        /* check "/", "%" and "\" (Win32) in URI */
541
765
        case sw_check_uri:
542
543
765
            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
544
748
                break;
545
748
            }
546
547
17
            switch (ch) {
548
0
            case '/':
549
#if (NGX_WIN32)
550
                if (r->uri_ext == p) {
551
                    r->complex_uri = 1;
552
                    state = sw_uri;
553
                    break;
554
                }
555
#endif
556
0
                r->uri_ext = NULL;
557
0
                state = sw_after_slash_in_uri;
558
0
                break;
559
9
            case '.':
560
9
                r->uri_ext = p + 1;
561
9
                break;
562
6
            case ' ':
563
6
                r->uri_end = p;
564
6
                state = sw_http_09;
565
6
                break;
566
0
            case CR:
567
0
                r->uri_end = p;
568
0
                r->http_minor = 9;
569
0
                state = sw_almost_done;
570
0
                break;
571
1
            case LF:
572
1
                r->uri_end = p;
573
1
                r->http_minor = 9;
574
1
                goto done;
575
#if (NGX_WIN32)
576
            case '\\':
577
                r->complex_uri = 1;
578
                state = sw_after_slash_in_uri;
579
                break;
580
#endif
581
1
            case '%':
582
1
                r->quoted_uri = 1;
583
1
                state = sw_uri;
584
1
                break;
585
0
            case '?':
586
0
                r->args_start = p + 1;
587
0
                state = sw_uri;
588
0
                break;
589
0
            case '#':
590
0
                r->complex_uri = 1;
591
0
                state = sw_uri;
592
0
                break;
593
0
            case '+':
594
0
                r->plus_in_uri = 1;
595
0
                break;
596
0
            default:
597
0
                if (ch < 0x20 || ch == 0x7f) {
598
0
                    return NGX_HTTP_PARSE_INVALID_REQUEST;
599
0
                }
600
0
                break;
601
17
            }
602
16
            break;
603
604
        /* URI */
605
1.34k
        case sw_uri:
606
607
1.34k
            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
608
1.32k
                break;
609
1.32k
            }
610
611
15
            switch (ch) {
612
4
            case ' ':
613
4
                r->uri_end = p;
614
4
                state = sw_http_09;
615
4
                break;
616
0
            case CR:
617
0
                r->uri_end = p;
618
0
                r->http_minor = 9;
619
0
                state = sw_almost_done;
620
0
                break;
621
0
            case LF:
622
0
                r->uri_end = p;
623
0
                r->http_minor = 9;
624
0
                goto done;
625
0
            case '#':
626
0
                r->complex_uri = 1;
627
0
                break;
628
11
            default:
629
11
                if (ch < 0x20 || ch == 0x7f) {
630
0
                    return NGX_HTTP_PARSE_INVALID_REQUEST;
631
0
                }
632
11
                break;
633
15
            }
634
15
            break;
635
636
        /* space+ after URI */
637
17
        case sw_http_09:
638
17
            switch (ch) {
639
4
            case ' ':
640
4
                break;
641
0
            case CR:
642
0
                r->http_minor = 9;
643
0
                state = sw_almost_done;
644
0
                break;
645
0
            case LF:
646
0
                r->http_minor = 9;
647
0
                goto done;
648
13
            case 'H':
649
13
                r->http_protocol.data = p;
650
13
                state = sw_http_H;
651
13
                break;
652
0
            default:
653
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
654
17
            }
655
17
            break;
656
657
17
        case sw_http_H:
658
13
            switch (ch) {
659
13
            case 'T':
660
13
                state = sw_http_HT;
661
13
                break;
662
0
            default:
663
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
664
13
            }
665
13
            break;
666
667
13
        case sw_http_HT:
668
13
            switch (ch) {
669
13
            case 'T':
670
13
                state = sw_http_HTT;
671
13
                break;
672
0
            default:
673
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
674
13
            }
675
13
            break;
676
677
13
        case sw_http_HTT:
678
13
            switch (ch) {
679
13
            case 'P':
680
13
                state = sw_http_HTTP;
681
13
                break;
682
0
            default:
683
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
684
13
            }
685
13
            break;
686
687
13
        case sw_http_HTTP:
688
13
            switch (ch) {
689
13
            case '/':
690
13
                state = sw_first_major_digit;
691
13
                break;
692
0
            default:
693
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
694
13
            }
695
13
            break;
696
697
        /* first digit of major HTTP version */
698
13
        case sw_first_major_digit:
699
13
            if (ch < '1' || ch > '9') {
700
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
701
0
            }
702
703
13
            r->http_major = ch - '0';
704
705
13
            if (r->http_major > 1) {
706
0
                return NGX_HTTP_PARSE_INVALID_VERSION;
707
0
            }
708
709
13
            state = sw_major_digit;
710
13
            break;
711
712
        /* major HTTP version or dot */
713
13
        case sw_major_digit:
714
13
            if (ch == '.') {
715
13
                state = sw_first_minor_digit;
716
13
                break;
717
13
            }
718
719
0
            if (ch < '0' || ch > '9') {
720
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
721
0
            }
722
723
0
            r->http_major = r->http_major * 10 + (ch - '0');
724
725
0
            if (r->http_major > 1) {
726
0
                return NGX_HTTP_PARSE_INVALID_VERSION;
727
0
            }
728
729
0
            break;
730
731
        /* first digit of minor HTTP version */
732
13
        case sw_first_minor_digit:
733
13
            if (ch < '0' || ch > '9') {
734
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
735
0
            }
736
737
13
            r->http_minor = ch - '0';
738
13
            state = sw_minor_digit;
739
13
            break;
740
741
        /* minor HTTP version or end of request line */
742
13
        case sw_minor_digit:
743
13
            if (ch == CR) {
744
0
                state = sw_almost_done;
745
0
                break;
746
0
            }
747
748
13
            if (ch == LF) {
749
9
                goto done;
750
9
            }
751
752
4
            if (ch == ' ') {
753
4
                state = sw_spaces_after_digit;
754
4
                break;
755
4
            }
756
757
0
            if (ch < '0' || ch > '9') {
758
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
759
0
            }
760
761
0
            if (r->http_minor > 99) {
762
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
763
0
            }
764
765
0
            r->http_minor = r->http_minor * 10 + (ch - '0');
766
0
            break;
767
768
8
        case sw_spaces_after_digit:
769
8
            switch (ch) {
770
4
            case ' ':
771
4
                break;
772
0
            case CR:
773
0
                state = sw_almost_done;
774
0
                break;
775
4
            case LF:
776
4
                goto done;
777
0
            default:
778
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
779
8
            }
780
4
            break;
781
782
        /* end of request line */
783
4
        case sw_almost_done:
784
0
            r->request_end = p - 1;
785
0
            switch (ch) {
786
0
            case LF:
787
0
                goto done;
788
0
            default:
789
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
790
0
            }
791
68.0k
        }
792
68.0k
    }
793
794
65
    b->pos = p;
795
65
    r->state = state;
796
797
65
    return NGX_AGAIN;
798
799
15
done:
800
801
15
    b->pos = p + 1;
802
803
15
    if (r->request_end == NULL) {
804
15
        r->request_end = p;
805
15
    }
806
807
15
    r->http_version = r->http_major * 1000 + r->http_minor;
808
15
    r->state = sw_start;
809
810
15
    if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
811
0
        return NGX_HTTP_PARSE_INVALID_09_METHOD;
812
0
    }
813
814
15
    return NGX_OK;
815
15
}
816
817
818
ngx_int_t
819
ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
820
    ngx_uint_t allow_underscores)
821
41
{
822
41
    u_char      c, ch, *p;
823
41
    ngx_uint_t  hash, i;
824
41
    enum {
825
41
        sw_start = 0,
826
41
        sw_name,
827
41
        sw_space_before_value,
828
41
        sw_value,
829
41
        sw_space_after_value,
830
41
        sw_ignore_line,
831
41
        sw_almost_done,
832
41
        sw_header_almost_done
833
41
    } state;
834
835
    /* the last '\0' is not needed because string is zero terminated */
836
837
41
    static u_char  lowcase[] =
838
41
        "\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"
839
41
        "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
840
41
        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
841
41
        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
842
41
        "\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"
843
41
        "\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"
844
41
        "\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"
845
41
        "\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";
846
847
41
    state = r->state;
848
41
    hash = r->header_hash;
849
41
    i = r->lowcase_index;
850
851
621
    for (p = b->pos; p < b->last; p++) {
852
621
        ch = *p;
853
854
621
        switch (state) {
855
856
        /* first char */
857
41
        case sw_start:
858
41
            r->header_name_start = p;
859
41
            r->invalid_header = 0;
860
861
41
            switch (ch) {
862
2
            case CR:
863
2
                r->header_end = p;
864
2
                state = sw_header_almost_done;
865
2
                break;
866
12
            case LF:
867
12
                r->header_end = p;
868
12
                goto header_done;
869
27
            default:
870
27
                state = sw_name;
871
872
27
                c = lowcase[ch];
873
874
27
                if (c) {
875
25
                    hash = ngx_hash(0, c);
876
25
                    r->lowcase_header[0] = c;
877
25
                    i = 1;
878
25
                    break;
879
25
                }
880
881
2
                if (ch == '_') {
882
0
                    if (allow_underscores) {
883
0
                        hash = ngx_hash(0, ch);
884
0
                        r->lowcase_header[0] = ch;
885
0
                        i = 1;
886
887
0
                    } else {
888
0
                        hash = 0;
889
0
                        i = 0;
890
0
                        r->invalid_header = 1;
891
0
                    }
892
893
0
                    break;
894
0
                }
895
896
2
                if (ch <= 0x20 || ch == 0x7f || ch == ':') {
897
0
                    r->header_end = p;
898
0
                    return NGX_HTTP_PARSE_INVALID_HEADER;
899
0
                }
900
901
2
                hash = 0;
902
2
                i = 0;
903
2
                r->invalid_header = 1;
904
905
2
                break;
906
907
41
            }
908
29
            break;
909
910
        /* header name */
911
352
        case sw_name:
912
352
            c = lowcase[ch];
913
914
352
            if (c) {
915
317
                hash = ngx_hash(hash, c);
916
317
                r->lowcase_header[i++] = c;
917
317
                i &= (NGX_HTTP_LC_HEADER_LEN - 1);
918
317
                break;
919
317
            }
920
921
35
            if (ch == '_') {
922
6
                if (allow_underscores) {
923
0
                    hash = ngx_hash(hash, ch);
924
0
                    r->lowcase_header[i++] = ch;
925
0
                    i &= (NGX_HTTP_LC_HEADER_LEN - 1);
926
927
6
                } else {
928
6
                    r->invalid_header = 1;
929
6
                }
930
931
6
                break;
932
6
            }
933
934
29
            if (ch == ':') {
935
23
                r->header_name_end = p;
936
23
                state = sw_space_before_value;
937
23
                break;
938
23
            }
939
940
6
            if (ch == CR) {
941
0
                r->header_name_end = p;
942
0
                r->header_start = p;
943
0
                r->header_end = p;
944
0
                state = sw_almost_done;
945
0
                break;
946
0
            }
947
948
6
            if (ch == LF) {
949
4
                r->header_name_end = p;
950
4
                r->header_start = p;
951
4
                r->header_end = p;
952
4
                goto done;
953
4
            }
954
955
            /* IIS may send the duplicate "HTTP/1.1 ..." lines */
956
2
            if (ch == '/'
957
2
                && r->upstream
958
2
                && p - r->header_name_start == 4
959
2
                && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
960
0
            {
961
0
                state = sw_ignore_line;
962
0
                break;
963
0
            }
964
965
2
            if (ch <= 0x20 || ch == 0x7f) {
966
0
                r->header_end = p;
967
0
                return NGX_HTTP_PARSE_INVALID_HEADER;
968
0
            }
969
970
2
            r->invalid_header = 1;
971
972
2
            break;
973
974
        /* space* before header value */
975
24
        case sw_space_before_value:
976
24
            switch (ch) {
977
1
            case ' ':
978
1
                break;
979
0
            case CR:
980
0
                r->header_start = p;
981
0
                r->header_end = p;
982
0
                state = sw_almost_done;
983
0
                break;
984
0
            case LF:
985
0
                r->header_start = p;
986
0
                r->header_end = p;
987
0
                goto done;
988
0
            case '\0':
989
0
                r->header_end = p;
990
0
                return NGX_HTTP_PARSE_INVALID_HEADER;
991
23
            default:
992
23
                r->header_start = p;
993
23
                state = sw_value;
994
23
                break;
995
24
            }
996
24
            break;
997
998
        /* header value */
999
196
        case sw_value:
1000
196
            switch (ch) {
1001
5
            case ' ':
1002
5
                r->header_end = p;
1003
5
                state = sw_space_after_value;
1004
5
                break;
1005
0
            case CR:
1006
0
                r->header_end = p;
1007
0
                state = sw_almost_done;
1008
0
                break;
1009
19
            case LF:
1010
19
                r->header_end = p;
1011
19
                goto done;
1012
0
            case '\0':
1013
0
                r->header_end = p;
1014
0
                return NGX_HTTP_PARSE_INVALID_HEADER;
1015
196
            }
1016
177
            break;
1017
1018
        /* space* before end of header line */
1019
177
        case sw_space_after_value:
1020
6
            switch (ch) {
1021
1
            case ' ':
1022
1
                break;
1023
0
            case CR:
1024
0
                state = sw_almost_done;
1025
0
                break;
1026
4
            case LF:
1027
4
                goto done;
1028
0
            case '\0':
1029
0
                r->header_end = p;
1030
0
                return NGX_HTTP_PARSE_INVALID_HEADER;
1031
1
            default:
1032
1
                state = sw_value;
1033
1
                break;
1034
6
            }
1035
2
            break;
1036
1037
        /* ignore header line */
1038
2
        case sw_ignore_line:
1039
0
            switch (ch) {
1040
0
            case LF:
1041
0
                state = sw_start;
1042
0
                break;
1043
0
            default:
1044
0
                break;
1045
0
            }
1046
0
            break;
1047
1048
        /* end of header line */
1049
0
        case sw_almost_done:
1050
0
            switch (ch) {
1051
0
            case LF:
1052
0
                goto done;
1053
0
            case CR:
1054
0
                break;
1055
0
            default:
1056
0
                return NGX_HTTP_PARSE_INVALID_HEADER;
1057
0
            }
1058
0
            break;
1059
1060
        /* end of header */
1061
2
        case sw_header_almost_done:
1062
2
            switch (ch) {
1063
2
            case LF:
1064
2
                goto header_done;
1065
0
            default:
1066
0
                return NGX_HTTP_PARSE_INVALID_HEADER;
1067
2
            }
1068
621
        }
1069
621
    }
1070
1071
0
    b->pos = p;
1072
0
    r->state = state;
1073
0
    r->header_hash = hash;
1074
0
    r->lowcase_index = i;
1075
1076
0
    return NGX_AGAIN;
1077
1078
27
done:
1079
1080
27
    b->pos = p + 1;
1081
27
    r->state = sw_start;
1082
27
    r->header_hash = hash;
1083
27
    r->lowcase_index = i;
1084
1085
27
    return NGX_OK;
1086
1087
14
header_done:
1088
1089
14
    b->pos = p + 1;
1090
14
    r->state = sw_start;
1091
1092
14
    return NGX_HTTP_PARSE_HEADER_DONE;
1093
41
}
1094
1095
1096
ngx_int_t
1097
ngx_http_parse_uri(ngx_http_request_t *r)
1098
0
{
1099
0
    u_char  *p, ch;
1100
0
    enum {
1101
0
        sw_start = 0,
1102
0
        sw_after_slash_in_uri,
1103
0
        sw_check_uri,
1104
0
        sw_uri
1105
0
    } state;
1106
1107
0
    state = sw_start;
1108
1109
0
    for (p = r->uri_start; p != r->uri_end; p++) {
1110
1111
0
        ch = *p;
1112
1113
0
        switch (state) {
1114
1115
0
        case sw_start:
1116
1117
0
            if (ch != '/') {
1118
0
                return NGX_ERROR;
1119
0
            }
1120
1121
0
            state = sw_after_slash_in_uri;
1122
0
            break;
1123
1124
        /* check "/.", "//", "%", and "\" (Win32) in URI */
1125
0
        case sw_after_slash_in_uri:
1126
1127
0
            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1128
0
                state = sw_check_uri;
1129
0
                break;
1130
0
            }
1131
1132
0
            switch (ch) {
1133
0
            case '.':
1134
0
                r->complex_uri = 1;
1135
0
                state = sw_uri;
1136
0
                break;
1137
0
            case '%':
1138
0
                r->quoted_uri = 1;
1139
0
                state = sw_uri;
1140
0
                break;
1141
0
            case '/':
1142
0
                r->complex_uri = 1;
1143
0
                state = sw_uri;
1144
0
                break;
1145
#if (NGX_WIN32)
1146
            case '\\':
1147
                r->complex_uri = 1;
1148
                state = sw_uri;
1149
                break;
1150
#endif
1151
0
            case '?':
1152
0
                r->args_start = p + 1;
1153
0
                state = sw_uri;
1154
0
                break;
1155
0
            case '#':
1156
0
                r->complex_uri = 1;
1157
0
                state = sw_uri;
1158
0
                break;
1159
0
            case '+':
1160
0
                r->plus_in_uri = 1;
1161
0
                break;
1162
0
            default:
1163
0
                if (ch <= 0x20 || ch == 0x7f) {
1164
0
                    return NGX_ERROR;
1165
0
                }
1166
0
                state = sw_check_uri;
1167
0
                break;
1168
0
            }
1169
0
            break;
1170
1171
        /* check "/", "%" and "\" (Win32) in URI */
1172
0
        case sw_check_uri:
1173
1174
0
            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1175
0
                break;
1176
0
            }
1177
1178
0
            switch (ch) {
1179
0
            case '/':
1180
#if (NGX_WIN32)
1181
                if (r->uri_ext == p) {
1182
                    r->complex_uri = 1;
1183
                    state = sw_uri;
1184
                    break;
1185
                }
1186
#endif
1187
0
                r->uri_ext = NULL;
1188
0
                state = sw_after_slash_in_uri;
1189
0
                break;
1190
0
            case '.':
1191
0
                r->uri_ext = p + 1;
1192
0
                break;
1193
#if (NGX_WIN32)
1194
            case '\\':
1195
                r->complex_uri = 1;
1196
                state = sw_after_slash_in_uri;
1197
                break;
1198
#endif
1199
0
            case '%':
1200
0
                r->quoted_uri = 1;
1201
0
                state = sw_uri;
1202
0
                break;
1203
0
            case '?':
1204
0
                r->args_start = p + 1;
1205
0
                state = sw_uri;
1206
0
                break;
1207
0
            case '#':
1208
0
                r->complex_uri = 1;
1209
0
                state = sw_uri;
1210
0
                break;
1211
0
            case '+':
1212
0
                r->plus_in_uri = 1;
1213
0
                break;
1214
0
            default:
1215
0
                if (ch <= 0x20 || ch == 0x7f) {
1216
0
                    return NGX_ERROR;
1217
0
                }
1218
0
                break;
1219
0
            }
1220
0
            break;
1221
1222
        /* URI */
1223
0
        case sw_uri:
1224
1225
0
            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1226
0
                break;
1227
0
            }
1228
1229
0
            switch (ch) {
1230
0
            case '#':
1231
0
                r->complex_uri = 1;
1232
0
                break;
1233
0
            default:
1234
0
                if (ch <= 0x20 || ch == 0x7f) {
1235
0
                    return NGX_ERROR;
1236
0
                }
1237
0
                break;
1238
0
            }
1239
0
            break;
1240
0
        }
1241
0
    }
1242
1243
0
    return NGX_OK;
1244
0
}
1245
1246
1247
ngx_int_t
1248
ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
1249
4
{
1250
4
    u_char  c, ch, decoded, *p, *u;
1251
4
    enum {
1252
4
        sw_usual = 0,
1253
4
        sw_slash,
1254
4
        sw_dot,
1255
4
        sw_dot_dot,
1256
4
        sw_quoted,
1257
4
        sw_quoted_second
1258
4
    } state, quoted_state;
1259
1260
4
#if (NGX_SUPPRESS_WARN)
1261
4
    decoded = '\0';
1262
4
    quoted_state = sw_usual;
1263
4
#endif
1264
1265
4
    state = sw_usual;
1266
4
    p = r->uri_start;
1267
4
    u = r->uri.data;
1268
4
    r->uri_ext = NULL;
1269
4
    r->args_start = NULL;
1270
1271
4
    if (r->empty_path_in_uri) {
1272
0
        *u++ = '/';
1273
0
    }
1274
1275
4
    ch = *p++;
1276
1277
998
    while (p <= r->uri_end) {
1278
1279
        /*
1280
         * we use "ch = *p++" inside the cycle, but this operation is safe,
1281
         * because after the URI there is always at least one character:
1282
         * the line feed
1283
         */
1284
1285
994
        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1286
994
                       "s:%d in:'%Xd:%c'", state, ch, ch);
1287
1288
994
        switch (state) {
1289
1290
979
        case sw_usual:
1291
1292
979
            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1293
971
                *u++ = ch;
1294
971
                ch = *p++;
1295
971
                break;
1296
971
            }
1297
1298
8
            switch (ch) {
1299
#if (NGX_WIN32)
1300
            case '\\':
1301
                if (u - 2 >= r->uri.data
1302
                    && *(u - 1) == '.' && *(u - 2) != '.')
1303
                {
1304
                    u--;
1305
                }
1306
1307
                r->uri_ext = NULL;
1308
1309
                if (p == r->uri_start + r->uri.len) {
1310
1311
                    /*
1312
                     * we omit the last "\" to cause redirect because
1313
                     * the browsers do not treat "\" as "/" in relative URL path
1314
                     */
1315
1316
                    break;
1317
                }
1318
1319
                state = sw_slash;
1320
                *u++ = '/';
1321
                break;
1322
#endif
1323
7
            case '/':
1324
#if (NGX_WIN32)
1325
                if (u - 2 >= r->uri.data
1326
                    && *(u - 1) == '.' && *(u - 2) != '.')
1327
                {
1328
                    u--;
1329
                }
1330
#endif
1331
7
                r->uri_ext = NULL;
1332
7
                state = sw_slash;
1333
7
                *u++ = ch;
1334
7
                break;
1335
0
            case '%':
1336
0
                quoted_state = state;
1337
0
                state = sw_quoted;
1338
0
                break;
1339
0
            case '?':
1340
0
                r->args_start = p;
1341
0
                goto args;
1342
0
            case '#':
1343
0
                goto done;
1344
1
            case '.':
1345
1
                r->uri_ext = u + 1;
1346
1
                *u++ = ch;
1347
1
                break;
1348
0
            case '+':
1349
0
                r->plus_in_uri = 1;
1350
                /* fall through */
1351
0
            default:
1352
0
                *u++ = ch;
1353
0
                break;
1354
8
            }
1355
1356
8
            ch = *p++;
1357
8
            break;
1358
1359
15
        case sw_slash:
1360
1361
15
            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1362
4
                state = sw_usual;
1363
4
                *u++ = ch;
1364
4
                ch = *p++;
1365
4
                break;
1366
4
            }
1367
1368
11
            switch (ch) {
1369
#if (NGX_WIN32)
1370
            case '\\':
1371
                break;
1372
#endif
1373
11
            case '/':
1374
11
                if (!merge_slashes) {
1375
0
                    *u++ = ch;
1376
0
                }
1377
11
                break;
1378
0
            case '.':
1379
0
                state = sw_dot;
1380
0
                *u++ = ch;
1381
0
                break;
1382
0
            case '%':
1383
0
                quoted_state = state;
1384
0
                state = sw_quoted;
1385
0
                break;
1386
0
            case '?':
1387
0
                r->args_start = p;
1388
0
                goto args;
1389
0
            case '#':
1390
0
                goto done;
1391
0
            case '+':
1392
0
                r->plus_in_uri = 1;
1393
                /* fall through */
1394
0
            default:
1395
0
                state = sw_usual;
1396
0
                *u++ = ch;
1397
0
                break;
1398
11
            }
1399
1400
11
            ch = *p++;
1401
11
            break;
1402
1403
0
        case sw_dot:
1404
1405
0
            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1406
0
                state = sw_usual;
1407
0
                *u++ = ch;
1408
0
                ch = *p++;
1409
0
                break;
1410
0
            }
1411
1412
0
            switch (ch) {
1413
#if (NGX_WIN32)
1414
            case '\\':
1415
#endif
1416
0
            case '/':
1417
0
                state = sw_slash;
1418
0
                u--;
1419
0
                break;
1420
0
            case '.':
1421
0
                state = sw_dot_dot;
1422
0
                *u++ = ch;
1423
0
                break;
1424
0
            case '%':
1425
0
                quoted_state = state;
1426
0
                state = sw_quoted;
1427
0
                break;
1428
0
            case '?':
1429
0
                u--;
1430
0
                r->args_start = p;
1431
0
                goto args;
1432
0
            case '#':
1433
0
                u--;
1434
0
                goto done;
1435
0
            case '+':
1436
0
                r->plus_in_uri = 1;
1437
                /* fall through */
1438
0
            default:
1439
0
                state = sw_usual;
1440
0
                *u++ = ch;
1441
0
                break;
1442
0
            }
1443
1444
0
            ch = *p++;
1445
0
            break;
1446
1447
0
        case sw_dot_dot:
1448
1449
0
            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1450
0
                state = sw_usual;
1451
0
                *u++ = ch;
1452
0
                ch = *p++;
1453
0
                break;
1454
0
            }
1455
1456
0
            switch (ch) {
1457
#if (NGX_WIN32)
1458
            case '\\':
1459
#endif
1460
0
            case '/':
1461
0
            case '?':
1462
0
            case '#':
1463
0
                u -= 4;
1464
0
                for ( ;; ) {
1465
0
                    if (u < r->uri.data) {
1466
0
                        return NGX_HTTP_PARSE_INVALID_REQUEST;
1467
0
                    }
1468
0
                    if (*u == '/') {
1469
0
                        u++;
1470
0
                        break;
1471
0
                    }
1472
0
                    u--;
1473
0
                }
1474
0
                if (ch == '?') {
1475
0
                    r->args_start = p;
1476
0
                    goto args;
1477
0
                }
1478
0
                if (ch == '#') {
1479
0
                    goto done;
1480
0
                }
1481
0
                state = sw_slash;
1482
0
                break;
1483
0
            case '%':
1484
0
                quoted_state = state;
1485
0
                state = sw_quoted;
1486
0
                break;
1487
0
            case '+':
1488
0
                r->plus_in_uri = 1;
1489
                /* fall through */
1490
0
            default:
1491
0
                state = sw_usual;
1492
0
                *u++ = ch;
1493
0
                break;
1494
0
            }
1495
1496
0
            ch = *p++;
1497
0
            break;
1498
1499
0
        case sw_quoted:
1500
0
            r->quoted_uri = 1;
1501
1502
0
            if (ch >= '0' && ch <= '9') {
1503
0
                decoded = (u_char) (ch - '0');
1504
0
                state = sw_quoted_second;
1505
0
                ch = *p++;
1506
0
                break;
1507
0
            }
1508
1509
0
            c = (u_char) (ch | 0x20);
1510
0
            if (c >= 'a' && c <= 'f') {
1511
0
                decoded = (u_char) (c - 'a' + 10);
1512
0
                state = sw_quoted_second;
1513
0
                ch = *p++;
1514
0
                break;
1515
0
            }
1516
1517
0
            return NGX_HTTP_PARSE_INVALID_REQUEST;
1518
1519
0
        case sw_quoted_second:
1520
0
            if (ch >= '0' && ch <= '9') {
1521
0
                ch = (u_char) ((decoded << 4) + (ch - '0'));
1522
1523
0
                if (ch == '%' || ch == '#') {
1524
0
                    state = sw_usual;
1525
0
                    *u++ = ch;
1526
0
                    ch = *p++;
1527
0
                    break;
1528
1529
0
                } else if (ch == '\0') {
1530
0
                    return NGX_HTTP_PARSE_INVALID_REQUEST;
1531
0
                }
1532
1533
0
                state = quoted_state;
1534
0
                break;
1535
0
            }
1536
1537
0
            c = (u_char) (ch | 0x20);
1538
0
            if (c >= 'a' && c <= 'f') {
1539
0
                ch = (u_char) ((decoded << 4) + (c - 'a') + 10);
1540
1541
0
                if (ch == '?') {
1542
0
                    state = sw_usual;
1543
0
                    *u++ = ch;
1544
0
                    ch = *p++;
1545
0
                    break;
1546
1547
0
                } else if (ch == '+') {
1548
0
                    r->plus_in_uri = 1;
1549
0
                }
1550
1551
0
                state = quoted_state;
1552
0
                break;
1553
0
            }
1554
1555
0
            return NGX_HTTP_PARSE_INVALID_REQUEST;
1556
994
        }
1557
994
    }
1558
1559
4
    if (state == sw_quoted || state == sw_quoted_second) {
1560
0
        return NGX_HTTP_PARSE_INVALID_REQUEST;
1561
0
    }
1562
1563
4
    if (state == sw_dot) {
1564
0
        u--;
1565
1566
4
    } else if (state == sw_dot_dot) {
1567
0
        u -= 4;
1568
1569
0
        for ( ;; ) {
1570
0
            if (u < r->uri.data) {
1571
0
                return NGX_HTTP_PARSE_INVALID_REQUEST;
1572
0
            }
1573
1574
0
            if (*u == '/') {
1575
0
                u++;
1576
0
                break;
1577
0
            }
1578
1579
0
            u--;
1580
0
        }
1581
0
    }
1582
1583
4
done:
1584
1585
4
    r->uri.len = u - r->uri.data;
1586
1587
4
    if (r->uri_ext) {
1588
1
        r->exten.len = u - r->uri_ext;
1589
1
        r->exten.data = r->uri_ext;
1590
1
    }
1591
1592
4
    r->uri_ext = NULL;
1593
1594
4
    return NGX_OK;
1595
1596
0
args:
1597
1598
0
    while (p < r->uri_end) {
1599
0
        if (*p++ != '#') {
1600
0
            continue;
1601
0
        }
1602
1603
0
        r->args.len = p - 1 - r->args_start;
1604
0
        r->args.data = r->args_start;
1605
0
        r->args_start = NULL;
1606
1607
0
        break;
1608
0
    }
1609
1610
0
    r->uri.len = u - r->uri.data;
1611
1612
0
    if (r->uri_ext) {
1613
0
        r->exten.len = u - r->uri_ext;
1614
0
        r->exten.data = r->uri_ext;
1615
0
    }
1616
1617
0
    r->uri_ext = NULL;
1618
1619
0
    return NGX_OK;
1620
4
}
1621
1622
1623
ngx_int_t
1624
ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
1625
    ngx_http_status_t *status)
1626
3
{
1627
3
    u_char   ch;
1628
3
    u_char  *p;
1629
3
    enum {
1630
3
        sw_start = 0,
1631
3
        sw_H,
1632
3
        sw_HT,
1633
3
        sw_HTT,
1634
3
        sw_HTTP,
1635
3
        sw_first_major_digit,
1636
3
        sw_major_digit,
1637
3
        sw_first_minor_digit,
1638
3
        sw_minor_digit,
1639
3
        sw_status,
1640
3
        sw_space_after_status,
1641
3
        sw_status_text,
1642
3
        sw_almost_done
1643
3
    } state;
1644
1645
3
    state = r->state;
1646
1647
15
    for (p = b->pos; p < b->last; p++) {
1648
15
        ch = *p;
1649
1650
15
        switch (state) {
1651
1652
        /* "HTTP/" */
1653
3
        case sw_start:
1654
3
            switch (ch) {
1655
1
            case 'H':
1656
1
                state = sw_H;
1657
1
                break;
1658
2
            default:
1659
2
                return NGX_ERROR;
1660
3
            }
1661
1
            break;
1662
1663
1
        case sw_H:
1664
1
            switch (ch) {
1665
1
            case 'T':
1666
1
                state = sw_HT;
1667
1
                break;
1668
0
            default:
1669
0
                return NGX_ERROR;
1670
1
            }
1671
1
            break;
1672
1673
1
        case sw_HT:
1674
1
            switch (ch) {
1675
1
            case 'T':
1676
1
                state = sw_HTT;
1677
1
                break;
1678
0
            default:
1679
0
                return NGX_ERROR;
1680
1
            }
1681
1
            break;
1682
1683
1
        case sw_HTT:
1684
1
            switch (ch) {
1685
1
            case 'P':
1686
1
                state = sw_HTTP;
1687
1
                break;
1688
0
            default:
1689
0
                return NGX_ERROR;
1690
1
            }
1691
1
            break;
1692
1693
1
        case sw_HTTP:
1694
1
            switch (ch) {
1695
1
            case '/':
1696
1
                state = sw_first_major_digit;
1697
1
                break;
1698
0
            default:
1699
0
                return NGX_ERROR;
1700
1
            }
1701
1
            break;
1702
1703
        /* the first digit of major HTTP version */
1704
1
        case sw_first_major_digit:
1705
1
            if (ch < '1' || ch > '9') {
1706
0
                return NGX_ERROR;
1707
0
            }
1708
1709
1
            r->http_major = ch - '0';
1710
1
            state = sw_major_digit;
1711
1
            break;
1712
1713
        /* the major HTTP version or dot */
1714
1
        case sw_major_digit:
1715
1
            if (ch == '.') {
1716
1
                state = sw_first_minor_digit;
1717
1
                break;
1718
1
            }
1719
1720
0
            if (ch < '0' || ch > '9') {
1721
0
                return NGX_ERROR;
1722
0
            }
1723
1724
0
            if (r->http_major > 99) {
1725
0
                return NGX_ERROR;
1726
0
            }
1727
1728
0
            r->http_major = r->http_major * 10 + (ch - '0');
1729
0
            break;
1730
1731
        /* the first digit of minor HTTP version */
1732
1
        case sw_first_minor_digit:
1733
1
            if (ch < '0' || ch > '9') {
1734
0
                return NGX_ERROR;
1735
0
            }
1736
1737
1
            r->http_minor = ch - '0';
1738
1
            state = sw_minor_digit;
1739
1
            break;
1740
1741
        /* the minor HTTP version or the end of the request line */
1742
1
        case sw_minor_digit:
1743
1
            if (ch == ' ') {
1744
1
                state = sw_status;
1745
1
                break;
1746
1
            }
1747
1748
0
            if (ch < '0' || ch > '9') {
1749
0
                return NGX_ERROR;
1750
0
            }
1751
1752
0
            if (r->http_minor > 99) {
1753
0
                return NGX_ERROR;
1754
0
            }
1755
1756
0
            r->http_minor = r->http_minor * 10 + (ch - '0');
1757
0
            break;
1758
1759
        /* HTTP status code */
1760
3
        case sw_status:
1761
3
            if (ch == ' ') {
1762
0
                break;
1763
0
            }
1764
1765
3
            if (ch < '0' || ch > '9') {
1766
0
                return NGX_ERROR;
1767
0
            }
1768
1769
3
            status->code = status->code * 10 + (ch - '0');
1770
1771
3
            if (++status->count == 3) {
1772
1
                state = sw_space_after_status;
1773
1
                status->start = p - 2;
1774
1
            }
1775
1776
3
            break;
1777
1778
        /* space or end of line */
1779
1
        case sw_space_after_status:
1780
1
            switch (ch) {
1781
0
            case ' ':
1782
0
                state = sw_status_text;
1783
0
                break;
1784
0
            case '.':                    /* IIS may send 403.1, 403.2, etc */
1785
0
                state = sw_status_text;
1786
0
                break;
1787
0
            case CR:
1788
0
                state = sw_almost_done;
1789
0
                break;
1790
1
            case LF:
1791
1
                goto done;
1792
0
            default:
1793
0
                return NGX_ERROR;
1794
1
            }
1795
0
            break;
1796
1797
        /* any text until end of line */
1798
0
        case sw_status_text:
1799
0
            switch (ch) {
1800
0
            case CR:
1801
0
                state = sw_almost_done;
1802
1803
0
                break;
1804
0
            case LF:
1805
0
                goto done;
1806
0
            }
1807
0
            break;
1808
1809
        /* end of status line */
1810
0
        case sw_almost_done:
1811
0
            status->end = p - 1;
1812
0
            switch (ch) {
1813
0
            case LF:
1814
0
                goto done;
1815
0
            default:
1816
0
                return NGX_ERROR;
1817
0
            }
1818
15
        }
1819
15
    }
1820
1821
0
    b->pos = p;
1822
0
    r->state = state;
1823
1824
0
    return NGX_AGAIN;
1825
1826
1
done:
1827
1828
1
    b->pos = p + 1;
1829
1830
1
    if (status->end == NULL) {
1831
1
        status->end = p;
1832
1
    }
1833
1834
1
    status->http_version = r->http_major * 1000 + r->http_minor;
1835
1
    r->state = sw_start;
1836
1837
1
    return NGX_OK;
1838
3
}
1839
1840
1841
ngx_int_t
1842
ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
1843
    ngx_str_t *args, ngx_uint_t *flags)
1844
0
{
1845
0
    u_char      ch, *p, *src, *dst;
1846
0
    size_t      len;
1847
0
    ngx_uint_t  quoted;
1848
1849
0
    len = uri->len;
1850
0
    p = uri->data;
1851
0
    quoted = 0;
1852
1853
0
    if (len == 0 || p[0] == '?') {
1854
0
        goto unsafe;
1855
0
    }
1856
1857
0
    if (p[0] == '.' && len > 1 && p[1] == '.'
1858
0
        && (len == 2 || ngx_path_separator(p[2])))
1859
0
    {
1860
0
        goto unsafe;
1861
0
    }
1862
1863
0
    for ( /* void */ ; len; len--) {
1864
1865
0
        ch = *p++;
1866
1867
0
        if (ch == '%') {
1868
0
            quoted = 1;
1869
0
            continue;
1870
0
        }
1871
1872
0
        if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1873
0
            continue;
1874
0
        }
1875
1876
0
        if (ch == '?') {
1877
0
            args->len = len - 1;
1878
0
            args->data = p;
1879
0
            uri->len -= len;
1880
1881
0
            break;
1882
0
        }
1883
1884
0
        if (ch == '\0') {
1885
0
            goto unsafe;
1886
0
        }
1887
1888
0
        if (ngx_path_separator(ch) && len > 2) {
1889
1890
            /* detect "/../" and "/.." */
1891
1892
0
            if (p[0] == '.' && p[1] == '.'
1893
0
                && (len == 3 || ngx_path_separator(p[2])))
1894
0
            {
1895
0
                goto unsafe;
1896
0
            }
1897
0
        }
1898
0
    }
1899
1900
0
    if (quoted) {
1901
0
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1902
0
                       "escaped URI: \"%V\"", uri);
1903
1904
0
        src = uri->data;
1905
1906
0
        dst = ngx_pnalloc(r->pool, uri->len);
1907
0
        if (dst == NULL) {
1908
0
            return NGX_ERROR;
1909
0
        }
1910
1911
0
        uri->data = dst;
1912
1913
0
        ngx_unescape_uri(&dst, &src, uri->len, 0);
1914
1915
0
        uri->len = dst - uri->data;
1916
1917
0
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1918
0
                       "unescaped URI: \"%V\"", uri);
1919
1920
0
        len = uri->len;
1921
0
        p = uri->data;
1922
1923
0
        if (p[0] == '.' && len > 1 && p[1] == '.'
1924
0
            && (len == 2 || ngx_path_separator(p[2])))
1925
0
        {
1926
0
            goto unsafe;
1927
0
        }
1928
1929
0
        for ( /* void */ ; len; len--) {
1930
1931
0
            ch = *p++;
1932
1933
0
            if (ch == '\0') {
1934
0
                goto unsafe;
1935
0
            }
1936
1937
0
            if (ngx_path_separator(ch) && len > 2) {
1938
1939
                /* detect "/../" and "/.." */
1940
1941
0
                if (p[0] == '.' && p[1] == '.'
1942
0
                    && (len == 3 || ngx_path_separator(p[2])))
1943
0
                {
1944
0
                    goto unsafe;
1945
0
                }
1946
0
            }
1947
0
        }
1948
0
    }
1949
1950
0
    return NGX_OK;
1951
1952
0
unsafe:
1953
1954
0
    if (*flags & NGX_HTTP_LOG_UNSAFE) {
1955
0
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1956
0
                      "unsafe URI \"%V\" was detected", uri);
1957
0
    }
1958
1959
0
    return NGX_ERROR;
1960
0
}
1961
1962
1963
ngx_table_elt_t *
1964
ngx_http_parse_multi_header_lines(ngx_http_request_t *r,
1965
    ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value)
1966
0
{
1967
0
    u_char           *start, *last, *end, ch;
1968
0
    ngx_table_elt_t  *h;
1969
1970
0
    for (h = headers; h; h = h->next) {
1971
1972
0
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1973
0
                       "parse header: \"%V: %V\"", &h->key, &h->value);
1974
1975
0
        if (name->len > h->value.len) {
1976
0
            continue;
1977
0
        }
1978
1979
0
        start = h->value.data;
1980
0
        end = h->value.data + h->value.len;
1981
1982
0
        while (start < end) {
1983
1984
0
            if (ngx_strncasecmp(start, name->data, name->len) != 0) {
1985
0
                goto skip;
1986
0
            }
1987
1988
0
            for (start += name->len; start < end && *start == ' '; start++) {
1989
                /* void */
1990
0
            }
1991
1992
0
            if (value == NULL) {
1993
0
                if (start == end || *start == ',') {
1994
0
                    return h;
1995
0
                }
1996
1997
0
                goto skip;
1998
0
            }
1999
2000
0
            if (start == end || *start++ != '=') {
2001
                /* the invalid header value */
2002
0
                goto skip;
2003
0
            }
2004
2005
0
            while (start < end && *start == ' ') { start++; }
2006
2007
0
            for (last = start; last < end && *last != ';'; last++) {
2008
                /* void */
2009
0
            }
2010
2011
0
            value->len = last - start;
2012
0
            value->data = start;
2013
2014
0
            return h;
2015
2016
0
        skip:
2017
2018
0
            while (start < end) {
2019
0
                ch = *start++;
2020
0
                if (ch == ';' || ch == ',') {
2021
0
                    break;
2022
0
                }
2023
0
            }
2024
2025
0
            while (start < end && *start == ' ') { start++; }
2026
0
        }
2027
0
    }
2028
2029
0
    return NULL;
2030
0
}
2031
2032
2033
ngx_table_elt_t *
2034
ngx_http_parse_set_cookie_lines(ngx_http_request_t *r,
2035
    ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value)
2036
0
{
2037
0
    u_char           *start, *last, *end;
2038
0
    ngx_table_elt_t  *h;
2039
2040
0
    for (h = headers; h; h = h->next) {
2041
2042
0
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2043
0
                       "parse header: \"%V: %V\"", &h->key, &h->value);
2044
2045
0
        if (name->len >= h->value.len) {
2046
0
            continue;
2047
0
        }
2048
2049
0
        start = h->value.data;
2050
0
        end = h->value.data + h->value.len;
2051
2052
0
        if (ngx_strncasecmp(start, name->data, name->len) != 0) {
2053
0
            continue;
2054
0
        }
2055
2056
0
        for (start += name->len; start < end && *start == ' '; start++) {
2057
            /* void */
2058
0
        }
2059
2060
0
        if (start == end || *start++ != '=') {
2061
            /* the invalid header value */
2062
0
            continue;
2063
0
        }
2064
2065
0
        while (start < end && *start == ' ') { start++; }
2066
2067
0
        for (last = start; last < end && *last != ';'; last++) {
2068
            /* void */
2069
0
        }
2070
2071
0
        value->len = last - start;
2072
0
        value->data = start;
2073
2074
0
        return h;
2075
0
    }
2076
2077
0
    return NULL;
2078
0
}
2079
2080
2081
ngx_int_t
2082
ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
2083
0
{
2084
0
    u_char  *p, *last;
2085
2086
0
    if (r->args.len == 0) {
2087
0
        return NGX_DECLINED;
2088
0
    }
2089
2090
0
    p = r->args.data;
2091
0
    last = p + r->args.len;
2092
2093
0
    for ( /* void */ ; p < last; p++) {
2094
2095
        /* we need '=' after name, so drop one char from last */
2096
2097
0
        p = ngx_strlcasestrn(p, last - 1, name, len - 1);
2098
2099
0
        if (p == NULL) {
2100
0
            return NGX_DECLINED;
2101
0
        }
2102
2103
0
        if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
2104
2105
0
            value->data = p + len + 1;
2106
2107
0
            p = ngx_strlchr(p, last, '&');
2108
2109
0
            if (p == NULL) {
2110
0
                p = r->args.data + r->args.len;
2111
0
            }
2112
2113
0
            value->len = p - value->data;
2114
2115
0
            return NGX_OK;
2116
0
        }
2117
0
    }
2118
2119
0
    return NGX_DECLINED;
2120
0
}
2121
2122
2123
void
2124
ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
2125
0
{
2126
0
    u_char  *p, *last;
2127
2128
0
    last = uri->data + uri->len;
2129
2130
0
    p = ngx_strlchr(uri->data, last, '?');
2131
2132
0
    if (p) {
2133
0
        uri->len = p - uri->data;
2134
0
        p++;
2135
0
        args->len = last - p;
2136
0
        args->data = p;
2137
2138
0
    } else {
2139
0
        args->len = 0;
2140
0
    }
2141
0
}
2142
2143
2144
ngx_int_t
2145
ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
2146
    ngx_http_chunked_t *ctx)
2147
193
{
2148
193
    u_char     *pos, ch, c;
2149
193
    ngx_int_t   rc;
2150
193
    enum {
2151
193
        sw_chunk_start = 0,
2152
193
        sw_chunk_size,
2153
193
        sw_chunk_extension,
2154
193
        sw_chunk_extension_almost_done,
2155
193
        sw_chunk_data,
2156
193
        sw_after_data,
2157
193
        sw_after_data_almost_done,
2158
193
        sw_last_chunk_extension,
2159
193
        sw_last_chunk_extension_almost_done,
2160
193
        sw_trailer,
2161
193
        sw_trailer_almost_done,
2162
193
        sw_trailer_header,
2163
193
        sw_trailer_header_almost_done
2164
193
    } state;
2165
2166
193
    state = ctx->state;
2167
2168
193
    if (state == sw_chunk_data && ctx->size == 0) {
2169
26
        state = sw_after_data;
2170
26
    }
2171
2172
193
    rc = NGX_AGAIN;
2173
2174
831k
    for (pos = b->pos; pos < b->last; pos++) {
2175
2176
831k
        ch = *pos;
2177
2178
831k
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2179
831k
                       "http chunked byte: %02Xd s:%d", ch, state);
2180
2181
831k
        switch (state) {
2182
2183
33
        case sw_chunk_start:
2184
33
            if (ch >= '0' && ch <= '9') {
2185
31
                state = sw_chunk_size;
2186
31
                ctx->size = ch - '0';
2187
31
                break;
2188
31
            }
2189
2190
2
            c = (u_char) (ch | 0x20);
2191
2192
2
            if (c >= 'a' && c <= 'f') {
2193
2
                state = sw_chunk_size;
2194
2
                ctx->size = c - 'a' + 10;
2195
2
                break;
2196
2
            }
2197
2198
0
            goto invalid;
2199
2200
66.2k
        case sw_chunk_size:
2201
66.2k
            if (ctx->size > NGX_MAX_OFF_T_VALUE / 16) {
2202
0
                goto invalid;
2203
0
            }
2204
2205
66.2k
            if (ch >= '0' && ch <= '9') {
2206
66.2k
                ctx->size = ctx->size * 16 + (ch - '0');
2207
66.2k
                break;
2208
66.2k
            }
2209
2210
33
            c = (u_char) (ch | 0x20);
2211
2212
33
            if (c >= 'a' && c <= 'f') {
2213
1
                ctx->size = ctx->size * 16 + (c - 'a' + 10);
2214
1
                break;
2215
1
            }
2216
2217
32
            if (ctx->size == 0) {
2218
2219
2
                switch (ch) {
2220
0
                case CR:
2221
0
                    state = sw_last_chunk_extension_almost_done;
2222
0
                    break;
2223
0
                case LF:
2224
0
                    state = sw_trailer;
2225
0
                    break;
2226
0
                case ';':
2227
2
                case ' ':
2228
2
                case '\t':
2229
2
                    state = sw_last_chunk_extension;
2230
2
                    break;
2231
0
                default:
2232
0
                    goto invalid;
2233
2
                }
2234
2235
2
                break;
2236
2
            }
2237
2238
30
            switch (ch) {
2239
0
            case CR:
2240
0
                state = sw_chunk_extension_almost_done;
2241
0
                break;
2242
3
            case LF:
2243
3
                state = sw_chunk_data;
2244
3
                break;
2245
0
            case ';':
2246
27
            case ' ':
2247
27
            case '\t':
2248
27
                state = sw_chunk_extension;
2249
27
                break;
2250
0
            default:
2251
0
                goto invalid;
2252
30
            }
2253
2254
30
            break;
2255
2256
625k
        case sw_chunk_extension:
2257
625k
            switch (ch) {
2258
0
            case CR:
2259
0
                state = sw_chunk_extension_almost_done;
2260
0
                break;
2261
25
            case LF:
2262
25
                state = sw_chunk_data;
2263
625k
            }
2264
625k
            break;
2265
2266
625k
        case sw_chunk_extension_almost_done:
2267
0
            if (ch == LF) {
2268
0
                state = sw_chunk_data;
2269
0
                break;
2270
0
            }
2271
0
            goto invalid;
2272
2273
46
        case sw_chunk_data:
2274
46
            rc = NGX_OK;
2275
46
            goto data;
2276
2277
26
        case sw_after_data:
2278
26
            switch (ch) {
2279
12
            case CR:
2280
12
                state = sw_after_data_almost_done;
2281
12
                break;
2282
12
            case LF:
2283
12
                state = sw_chunk_start;
2284
12
                break;
2285
2
            default:
2286
2
                goto invalid;
2287
26
            }
2288
24
            break;
2289
2290
24
        case sw_after_data_almost_done:
2291
12
            if (ch == LF) {
2292
12
                state = sw_chunk_start;
2293
12
                break;
2294
12
            }
2295
0
            goto invalid;
2296
2297
65.7k
        case sw_last_chunk_extension:
2298
65.7k
            switch (ch) {
2299
0
            case CR:
2300
0
                state = sw_last_chunk_extension_almost_done;
2301
0
                break;
2302
1
            case LF:
2303
1
                state = sw_trailer;
2304
65.7k
            }
2305
65.7k
            break;
2306
2307
65.7k
        case sw_last_chunk_extension_almost_done:
2308
0
            if (ch == LF) {
2309
0
                state = sw_trailer;
2310
0
                break;
2311
0
            }
2312
0
            goto invalid;
2313
2314
2
        case sw_trailer:
2315
2
            switch (ch) {
2316
0
            case CR:
2317
0
                state = sw_trailer_almost_done;
2318
0
                break;
2319
0
            case LF:
2320
0
                goto done;
2321
2
            default:
2322
2
                state = sw_trailer_header;
2323
2
            }
2324
2
            break;
2325
2326
2
        case sw_trailer_almost_done:
2327
0
            if (ch == LF) {
2328
0
                goto done;
2329
0
            }
2330
0
            goto invalid;
2331
2332
73.7k
        case sw_trailer_header:
2333
73.7k
            switch (ch) {
2334
0
            case CR:
2335
0
                state = sw_trailer_header_almost_done;
2336
0
                break;
2337
1
            case LF:
2338
1
                state = sw_trailer;
2339
73.7k
            }
2340
73.7k
            break;
2341
2342
73.7k
        case sw_trailer_header_almost_done:
2343
0
            if (ch == LF) {
2344
0
                state = sw_trailer;
2345
0
                break;
2346
0
            }
2347
0
            goto invalid;
2348
2349
831k
        }
2350
831k
    }
2351
2352
191
data:
2353
2354
191
    ctx->state = state;
2355
191
    b->pos = pos;
2356
2357
191
    if (ctx->size > NGX_MAX_OFF_T_VALUE - 5) {
2358
0
        goto invalid;
2359
0
    }
2360
2361
191
    switch (state) {
2362
2363
0
    case sw_chunk_start:
2364
0
        ctx->length = 3 /* "0" LF LF */;
2365
0
        break;
2366
17
    case sw_chunk_size:
2367
17
        ctx->length = 1 /* LF */
2368
17
                      + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */
2369
17
                                   : 1 /* LF */);
2370
17
        break;
2371
87
    case sw_chunk_extension:
2372
87
    case sw_chunk_extension_almost_done:
2373
87
        ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */;
2374
87
        break;
2375
66
    case sw_chunk_data:
2376
66
        ctx->length = ctx->size + 4 /* LF "0" LF LF */;
2377
66
        break;
2378
0
    case sw_after_data:
2379
0
    case sw_after_data_almost_done:
2380
0
        ctx->length = 4 /* LF "0" LF LF */;
2381
0
        break;
2382
17
    case sw_last_chunk_extension:
2383
17
    case sw_last_chunk_extension_almost_done:
2384
17
        ctx->length = 2 /* LF LF */;
2385
17
        break;
2386
0
    case sw_trailer:
2387
0
    case sw_trailer_almost_done:
2388
0
        ctx->length = 1 /* LF */;
2389
0
        break;
2390
4
    case sw_trailer_header:
2391
4
    case sw_trailer_header_almost_done:
2392
4
        ctx->length = 2 /* LF LF */;
2393
4
        break;
2394
2395
191
    }
2396
2397
191
    return rc;
2398
2399
0
done:
2400
2401
0
    ctx->state = 0;
2402
0
    b->pos = pos + 1;
2403
2404
0
    return NGX_DONE;
2405
2406
2
invalid:
2407
2408
2
    return NGX_ERROR;
2409
191
}