source: nginx/trunk/src/http/ngx_http_parse.c @ 4573

Revision 4573, 44.3 KB checked in by Ruslan Ermilov, 7 weeks ago (diff)

Fixed spelling in multiline C comments.

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
13static uint32_t  usual[] = {
14    0xffffdbfe, /* 1111 1111 1111 1111  1101 1011 1111 1110 */
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    0xffffffff, /* 1111 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    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
40
41#define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
42    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
43
44#define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
45    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
46
47#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
48    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
49        && m[4] == c4
50
51#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
52    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
53        && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)
54
55#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
56    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
57        && ((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    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
61        && ((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    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
65        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)  \
66        && 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
103ngx_int_t
104ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
105{
106    u_char  c, ch, *p, *m;
107    enum {
108        sw_start = 0,
109        sw_method,
110        sw_spaces_before_uri,
111        sw_schema,
112        sw_schema_slash,
113        sw_schema_slash_slash,
114        sw_host_start,
115        sw_host,
116        sw_host_end,
117        sw_host_ip_literal,
118        sw_port,
119        sw_host_http_09,
120        sw_after_slash_in_uri,
121        sw_check_uri,
122        sw_check_uri_http_09,
123        sw_uri,
124        sw_http_09,
125        sw_http_H,
126        sw_http_HT,
127        sw_http_HTT,
128        sw_http_HTTP,
129        sw_first_major_digit,
130        sw_major_digit,
131        sw_first_minor_digit,
132        sw_minor_digit,
133        sw_spaces_after_digit,
134        sw_almost_done
135    } state;
136
137    state = r->state;
138
139    for (p = b->pos; p < b->last; p++) {
140        ch = *p;
141
142        switch (state) {
143
144        /* HTTP methods: GET, HEAD, POST */
145        case sw_start:
146            r->request_start = p;
147
148            if (ch == CR || ch == LF) {
149                break;
150            }
151
152            if ((ch < 'A' || ch > 'Z') && ch != '_') {
153                return NGX_HTTP_PARSE_INVALID_METHOD;
154            }
155
156            state = sw_method;
157            break;
158
159        case sw_method:
160            if (ch == ' ') {
161                r->method_end = p - 1;
162                m = r->request_start;
163
164                switch (p - m) {
165
166                case 3:
167                    if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
168                        r->method = NGX_HTTP_GET;
169                        break;
170                    }
171
172                    if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
173                        r->method = NGX_HTTP_PUT;
174                        break;
175                    }
176
177                    break;
178
179                case 4:
180                    if (m[1] == 'O') {
181
182                        if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
183                            r->method = NGX_HTTP_POST;
184                            break;
185                        }
186
187                        if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
188                            r->method = NGX_HTTP_COPY;
189                            break;
190                        }
191
192                        if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
193                            r->method = NGX_HTTP_MOVE;
194                            break;
195                        }
196
197                        if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
198                            r->method = NGX_HTTP_LOCK;
199                            break;
200                        }
201
202                    } else {
203
204                        if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
205                            r->method = NGX_HTTP_HEAD;
206                            break;
207                        }
208                    }
209
210                    break;
211
212                case 5:
213                    if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
214                        r->method = NGX_HTTP_MKCOL;
215                    }
216
217                    if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
218                        r->method = NGX_HTTP_PATCH;
219                    }
220
221                    if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
222                        r->method = NGX_HTTP_TRACE;
223                    }
224
225                    break;
226
227                case 6:
228                    if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
229                        r->method = NGX_HTTP_DELETE;
230                        break;
231                    }
232
233                    if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
234                        r->method = NGX_HTTP_UNLOCK;
235                        break;
236                    }
237
238                    break;
239
240                case 7:
241                    if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
242                    {
243                        r->method = NGX_HTTP_OPTIONS;
244                    }
245
246                    break;
247
248                case 8:
249                    if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
250                    {
251                        r->method = NGX_HTTP_PROPFIND;
252                    }
253
254                    break;
255
256                case 9:
257                    if (ngx_str9cmp(m,
258                            'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
259                    {
260                        r->method = NGX_HTTP_PROPPATCH;
261                    }
262
263                    break;
264                }
265
266                state = sw_spaces_before_uri;
267                break;
268            }
269
270            if ((ch < 'A' || ch > 'Z') && ch != '_') {
271                return NGX_HTTP_PARSE_INVALID_METHOD;
272            }
273
274            break;
275
276        /* space* before URI */
277        case sw_spaces_before_uri:
278
279            if (ch == '/') {
280                r->uri_start = p;
281                state = sw_after_slash_in_uri;
282                break;
283            }
284
285            c = (u_char) (ch | 0x20);
286            if (c >= 'a' && c <= 'z') {
287                r->schema_start = p;
288                state = sw_schema;
289                break;
290            }
291
292            switch (ch) {
293            case ' ':
294                break;
295            default:
296                return NGX_HTTP_PARSE_INVALID_REQUEST;
297            }
298            break;
299
300        case sw_schema:
301
302            c = (u_char) (ch | 0x20);
303            if (c >= 'a' && c <= 'z') {
304                break;
305            }
306
307            switch (ch) {
308            case ':':
309                r->schema_end = p;
310                state = sw_schema_slash;
311                break;
312            default:
313                return NGX_HTTP_PARSE_INVALID_REQUEST;
314            }
315            break;
316
317        case sw_schema_slash:
318            switch (ch) {
319            case '/':
320                state = sw_schema_slash_slash;
321                break;
322            default:
323                return NGX_HTTP_PARSE_INVALID_REQUEST;
324            }
325            break;
326
327        case sw_schema_slash_slash:
328            switch (ch) {
329            case '/':
330                state = sw_host_start;
331                break;
332            default:
333                return NGX_HTTP_PARSE_INVALID_REQUEST;
334            }
335            break;
336
337        case sw_host_start:
338
339            r->host_start = p;
340
341            if (ch == '[') {
342                state = sw_host_ip_literal;
343                break;
344            }
345
346            state = sw_host;
347
348            /* fall through */
349
350        case sw_host:
351
352            c = (u_char) (ch | 0x20);
353            if (c >= 'a' && c <= 'z') {
354                break;
355            }
356
357            if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
358                break;
359            }
360
361            /* fall through */
362
363        case sw_host_end:
364
365            r->host_end = p;
366
367            switch (ch) {
368            case ':':
369                state = sw_port;
370                break;
371            case '/':
372                r->uri_start = p;
373                state = sw_after_slash_in_uri;
374                break;
375            case ' ':
376                /*
377                 * use single "/" from request line to preserve pointers,
378                 * if request line will be copied to large client buffer
379                 */
380                r->uri_start = r->schema_end + 1;
381                r->uri_end = r->schema_end + 2;
382                state = sw_host_http_09;
383                break;
384            default:
385                return NGX_HTTP_PARSE_INVALID_REQUEST;
386            }
387            break;
388
389        case sw_host_ip_literal:
390
391            if (ch >= '0' && ch <= '9') {
392                break;
393            }
394
395            c = (u_char) (ch | 0x20);
396            if (c >= 'a' && c <= 'z') {
397                break;
398            }
399
400            switch (ch) {
401            case ':':
402                break;
403            case ']':
404                state = sw_host_end;
405                break;
406            case '-':
407            case '.':
408            case '_':
409            case '~':
410                /* unreserved */
411                break;
412            case '!':
413            case '$':
414            case '&':
415            case '\'':
416            case '(':
417            case ')':
418            case '*':
419            case '+':
420            case ',':
421            case ';':
422            case '=':
423                /* sub-delims */
424                break;
425            default:
426                return NGX_HTTP_PARSE_INVALID_REQUEST;
427            }
428            break;
429
430        case sw_port:
431            if (ch >= '0' && ch <= '9') {
432                break;
433            }
434
435            switch (ch) {
436            case '/':
437                r->port_end = p;
438                r->uri_start = p;
439                state = sw_after_slash_in_uri;
440                break;
441            case ' ':
442                r->port_end = p;
443                /*
444                 * use single "/" from request line to preserve pointers,
445                 * if request line will be copied to large client buffer
446                 */
447                r->uri_start = r->schema_end + 1;
448                r->uri_end = r->schema_end + 2;
449                state = sw_host_http_09;
450                break;
451            default:
452                return NGX_HTTP_PARSE_INVALID_REQUEST;
453            }
454            break;
455
456        /* space+ after "http://host[:port] " */
457        case sw_host_http_09:
458            switch (ch) {
459            case ' ':
460                break;
461            case CR:
462                r->http_minor = 9;
463                state = sw_almost_done;
464                break;
465            case LF:
466                r->http_minor = 9;
467                goto done;
468            case 'H':
469                r->http_protocol.data = p;
470                state = sw_http_H;
471                break;
472            default:
473                return NGX_HTTP_PARSE_INVALID_REQUEST;
474            }
475            break;
476
477
478        /* check "/.", "//", "%", and "\" (Win32) in URI */
479        case sw_after_slash_in_uri:
480
481            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
482                state = sw_check_uri;
483                break;
484            }
485
486            switch (ch) {
487            case ' ':
488                r->uri_end = p;
489                state = sw_check_uri_http_09;
490                break;
491            case CR:
492                r->uri_end = p;
493                r->http_minor = 9;
494                state = sw_almost_done;
495                break;
496            case LF:
497                r->uri_end = p;
498                r->http_minor = 9;
499                goto done;
500            case '.':
501                r->complex_uri = 1;
502                state = sw_uri;
503                break;
504            case '%':
505                r->quoted_uri = 1;
506                state = sw_uri;
507                break;
508            case '/':
509                r->complex_uri = 1;
510                state = sw_uri;
511                break;
512#if (NGX_WIN32)
513            case '\\':
514                r->complex_uri = 1;
515                state = sw_uri;
516                break;
517#endif
518            case '?':
519                r->args_start = p + 1;
520                state = sw_uri;
521                break;
522            case '#':
523                r->complex_uri = 1;
524                state = sw_uri;
525                break;
526            case '+':
527                r->plus_in_uri = 1;
528                break;
529            case '\0':
530                return NGX_HTTP_PARSE_INVALID_REQUEST;
531            default:
532                state = sw_check_uri;
533                break;
534            }
535            break;
536
537        /* check "/", "%" and "\" (Win32) in URI */
538        case sw_check_uri:
539
540            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
541                break;
542            }
543
544            switch (ch) {
545            case '/':
546                r->uri_ext = NULL;
547                state = sw_after_slash_in_uri;
548                break;
549            case '.':
550                r->uri_ext = p + 1;
551                break;
552            case ' ':
553                r->uri_end = p;
554                state = sw_check_uri_http_09;
555                break;
556            case CR:
557                r->uri_end = p;
558                r->http_minor = 9;
559                state = sw_almost_done;
560                break;
561            case LF:
562                r->uri_end = p;
563                r->http_minor = 9;
564                goto done;
565#if (NGX_WIN32)
566            case '\\':
567                r->complex_uri = 1;
568                state = sw_after_slash_in_uri;
569                break;
570#endif
571            case '%':
572                r->quoted_uri = 1;
573                state = sw_uri;
574                break;
575            case '?':
576                r->args_start = p + 1;
577                state = sw_uri;
578                break;
579            case '#':
580                r->complex_uri = 1;
581                state = sw_uri;
582                break;
583            case '+':
584                r->plus_in_uri = 1;
585                break;
586            case '\0':
587                return NGX_HTTP_PARSE_INVALID_REQUEST;
588            }
589            break;
590
591        /* space+ after URI */
592        case sw_check_uri_http_09:
593            switch (ch) {
594            case ' ':
595                break;
596            case CR:
597                r->http_minor = 9;
598                state = sw_almost_done;
599                break;
600            case LF:
601                r->http_minor = 9;
602                goto done;
603            case 'H':
604                r->http_protocol.data = p;
605                state = sw_http_H;
606                break;
607            default:
608                r->space_in_uri = 1;
609                state = sw_check_uri;
610                break;
611            }
612            break;
613
614
615        /* URI */
616        case sw_uri:
617
618            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
619                break;
620            }
621
622            switch (ch) {
623            case ' ':
624                r->uri_end = p;
625                state = sw_http_09;
626                break;
627            case CR:
628                r->uri_end = p;
629                r->http_minor = 9;
630                state = sw_almost_done;
631                break;
632            case LF:
633                r->uri_end = p;
634                r->http_minor = 9;
635                goto done;
636            case '#':
637                r->complex_uri = 1;
638                break;
639            case '\0':
640                return NGX_HTTP_PARSE_INVALID_REQUEST;
641            }
642            break;
643
644        /* space+ after URI */
645        case sw_http_09:
646            switch (ch) {
647            case ' ':
648                break;
649            case CR:
650                r->http_minor = 9;
651                state = sw_almost_done;
652                break;
653            case LF:
654                r->http_minor = 9;
655                goto done;
656            case 'H':
657                r->http_protocol.data = p;
658                state = sw_http_H;
659                break;
660            default:
661                r->space_in_uri = 1;
662                state = sw_uri;
663                break;
664            }
665            break;
666
667        case sw_http_H:
668            switch (ch) {
669            case 'T':
670                state = sw_http_HT;
671                break;
672            default:
673                return NGX_HTTP_PARSE_INVALID_REQUEST;
674            }
675            break;
676
677        case sw_http_HT:
678            switch (ch) {
679            case 'T':
680                state = sw_http_HTT;
681                break;
682            default:
683                return NGX_HTTP_PARSE_INVALID_REQUEST;
684            }
685            break;
686
687        case sw_http_HTT:
688            switch (ch) {
689            case 'P':
690                state = sw_http_HTTP;
691                break;
692            default:
693                return NGX_HTTP_PARSE_INVALID_REQUEST;
694            }
695            break;
696
697        case sw_http_HTTP:
698            switch (ch) {
699            case '/':
700                state = sw_first_major_digit;
701                break;
702            default:
703                return NGX_HTTP_PARSE_INVALID_REQUEST;
704            }
705            break;
706
707        /* first digit of major HTTP version */
708        case sw_first_major_digit:
709            if (ch < '1' || ch > '9') {
710                return NGX_HTTP_PARSE_INVALID_REQUEST;
711            }
712
713            r->http_major = ch - '0';
714            state = sw_major_digit;
715            break;
716
717        /* major HTTP version or dot */
718        case sw_major_digit:
719            if (ch == '.') {
720                state = sw_first_minor_digit;
721                break;
722            }
723
724            if (ch < '0' || ch > '9') {
725                return NGX_HTTP_PARSE_INVALID_REQUEST;
726            }
727
728            r->http_major = r->http_major * 10 + ch - '0';
729            break;
730
731        /* first digit of minor HTTP version */
732        case sw_first_minor_digit:
733            if (ch < '0' || ch > '9') {
734                return NGX_HTTP_PARSE_INVALID_REQUEST;
735            }
736
737            r->http_minor = ch - '0';
738            state = sw_minor_digit;
739            break;
740
741        /* minor HTTP version or end of request line */
742        case sw_minor_digit:
743            if (ch == CR) {
744                state = sw_almost_done;
745                break;
746            }
747
748            if (ch == LF) {
749                goto done;
750            }
751
752            if (ch == ' ') {
753                state = sw_spaces_after_digit;
754                break;
755            }
756
757            if (ch < '0' || ch > '9') {
758                return NGX_HTTP_PARSE_INVALID_REQUEST;
759            }
760
761            r->http_minor = r->http_minor * 10 + ch - '0';
762            break;
763
764        case sw_spaces_after_digit:
765            switch (ch) {
766            case ' ':
767                break;
768            case CR:
769                state = sw_almost_done;
770                break;
771            case LF:
772                goto done;
773            default:
774                return NGX_HTTP_PARSE_INVALID_REQUEST;
775            }
776            break;
777
778        /* end of request line */
779        case sw_almost_done:
780            r->request_end = p - 1;
781            switch (ch) {
782            case LF:
783                goto done;
784            default:
785                return NGX_HTTP_PARSE_INVALID_REQUEST;
786            }
787        }
788    }
789
790    b->pos = p;
791    r->state = state;
792
793    return NGX_AGAIN;
794
795done:
796
797    b->pos = p + 1;
798
799    if (r->request_end == NULL) {
800        r->request_end = p;
801    }
802
803    r->http_version = r->http_major * 1000 + r->http_minor;
804    r->state = sw_start;
805
806    if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
807        return NGX_HTTP_PARSE_INVALID_09_METHOD;
808    }
809
810    return NGX_OK;
811}
812
813
814ngx_int_t
815ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
816    ngx_uint_t allow_underscores)
817{
818    u_char      c, ch, *p;
819    ngx_uint_t  hash, i;
820    enum {
821        sw_start = 0,
822        sw_name,
823        sw_space_before_value,
824        sw_value,
825        sw_space_after_value,
826        sw_ignore_line,
827        sw_almost_done,
828        sw_header_almost_done
829    } state;
830
831    /* the last '\0' is not needed because string is zero terminated */
832
833    static u_char  lowcase[] =
834        "\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"
835        "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
836        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
837        "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
838        "\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        "\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"
840        "\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"
841        "\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";
842
843    state = r->state;
844    hash = r->header_hash;
845    i = r->lowcase_index;
846
847    for (p = b->pos; p < b->last; p++) {
848        ch = *p;
849
850        switch (state) {
851
852        /* first char */
853        case sw_start:
854            r->header_name_start = p;
855            r->invalid_header = 0;
856
857            switch (ch) {
858            case CR:
859                r->header_end = p;
860                state = sw_header_almost_done;
861                break;
862            case LF:
863                r->header_end = p;
864                goto header_done;
865            default:
866                state = sw_name;
867
868                c = lowcase[ch];
869
870                if (c) {
871                    hash = ngx_hash(0, c);
872                    r->lowcase_header[0] = c;
873                    i = 1;
874                    break;
875                }
876
877                if (ch == '\0') {
878                    return NGX_HTTP_PARSE_INVALID_HEADER;
879                }
880
881                r->invalid_header = 1;
882
883                break;
884
885            }
886            break;
887
888        /* header name */
889        case sw_name:
890            c = lowcase[ch];
891
892            if (c) {
893                hash = ngx_hash(hash, c);
894                r->lowcase_header[i++] = c;
895                i &= (NGX_HTTP_LC_HEADER_LEN - 1);
896                break;
897            }
898
899            if (ch == '_') {
900                if (allow_underscores) {
901                    hash = ngx_hash(hash, ch);
902                    r->lowcase_header[i++] = ch;
903                    i &= (NGX_HTTP_LC_HEADER_LEN - 1);
904
905                } else {
906                    r->invalid_header = 1;
907                }
908
909                break;
910            }
911
912            if (ch == ':') {
913                r->header_name_end = p;
914                state = sw_space_before_value;
915                break;
916            }
917
918            if (ch == CR) {
919                r->header_name_end = p;
920                r->header_start = p;
921                r->header_end = p;
922                state = sw_almost_done;
923                break;
924            }
925
926            if (ch == LF) {
927                r->header_name_end = p;
928                r->header_start = p;
929                r->header_end = p;
930                goto done;
931            }
932
933            /* IIS may send the duplicate "HTTP/1.1 ..." lines */
934            if (ch == '/'
935                && r->upstream
936                && p - r->header_name_start == 4
937                && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
938            {
939                state = sw_ignore_line;
940                break;
941            }
942
943            if (ch == '\0') {
944                return NGX_HTTP_PARSE_INVALID_HEADER;
945            }
946
947            r->invalid_header = 1;
948
949            break;
950
951        /* space* before header value */
952        case sw_space_before_value:
953            switch (ch) {
954            case ' ':
955                break;
956            case CR:
957                r->header_start = p;
958                r->header_end = p;
959                state = sw_almost_done;
960                break;
961            case LF:
962                r->header_start = p;
963                r->header_end = p;
964                goto done;
965            case '\0':
966                return NGX_HTTP_PARSE_INVALID_HEADER;
967            default:
968                r->header_start = p;
969                state = sw_value;
970                break;
971            }
972            break;
973
974        /* header value */
975        case sw_value:
976            switch (ch) {
977            case ' ':
978                r->header_end = p;
979                state = sw_space_after_value;
980                break;
981            case CR:
982                r->header_end = p;
983                state = sw_almost_done;
984                break;
985            case LF:
986                r->header_end = p;
987                goto done;
988            case '\0':
989                return NGX_HTTP_PARSE_INVALID_HEADER;
990            }
991            break;
992
993        /* space* before end of header line */
994        case sw_space_after_value:
995            switch (ch) {
996            case ' ':
997                break;
998            case CR:
999                state = sw_almost_done;
1000                break;
1001            case LF:
1002                goto done;
1003            case '\0':
1004                return NGX_HTTP_PARSE_INVALID_HEADER;
1005            default:
1006                state = sw_value;
1007                break;
1008            }
1009            break;
1010
1011        /* ignore header line */
1012        case sw_ignore_line:
1013            switch (ch) {
1014            case LF:
1015                state = sw_start;
1016                break;
1017            default:
1018                break;
1019            }
1020            break;
1021
1022        /* end of header line */
1023        case sw_almost_done:
1024            switch (ch) {
1025            case LF:
1026                goto done;
1027            case CR:
1028                break;
1029            default:
1030                return NGX_HTTP_PARSE_INVALID_HEADER;
1031            }
1032            break;
1033
1034        /* end of header */
1035        case sw_header_almost_done:
1036            switch (ch) {
1037            case LF:
1038                goto header_done;
1039            default:
1040                return NGX_HTTP_PARSE_INVALID_HEADER;
1041            }
1042        }
1043    }
1044
1045    b->pos = p;
1046    r->state = state;
1047    r->header_hash = hash;
1048    r->lowcase_index = i;
1049
1050    return NGX_AGAIN;
1051
1052done:
1053
1054    b->pos = p + 1;
1055    r->state = sw_start;
1056    r->header_hash = hash;
1057    r->lowcase_index = i;
1058
1059    return NGX_OK;
1060
1061header_done:
1062
1063    b->pos = p + 1;
1064    r->state = sw_start;
1065
1066    return NGX_HTTP_PARSE_HEADER_DONE;
1067}
1068
1069
1070ngx_int_t
1071ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
1072{
1073    u_char  c, ch, decoded, *p, *u;
1074    enum {
1075        sw_usual = 0,
1076        sw_slash,
1077        sw_dot,
1078        sw_dot_dot,
1079        sw_quoted,
1080        sw_quoted_second
1081    } state, quoted_state;
1082
1083#if (NGX_SUPPRESS_WARN)
1084    decoded = '\0';
1085    quoted_state = sw_usual;
1086#endif
1087
1088    state = sw_usual;
1089    p = r->uri_start;
1090    u = r->uri.data;
1091    r->uri_ext = NULL;
1092    r->args_start = NULL;
1093
1094    ch = *p++;
1095
1096    while (p <= r->uri_end) {
1097
1098        /*
1099         * we use "ch = *p++" inside the cycle, but this operation is safe,
1100         * because after the URI there is always at least one character:
1101         * the line feed
1102         */
1103
1104        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1105                       "s:%d in:'%Xd:%c', out:'%c'", state, ch, ch, *u);
1106
1107        switch (state) {
1108
1109        case sw_usual:
1110
1111            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1112                *u++ = ch;
1113                ch = *p++;
1114                break;
1115            }
1116
1117            switch(ch) {
1118#if (NGX_WIN32)
1119            case '\\':
1120                r->uri_ext = NULL;
1121
1122                if (p == r->uri_start + r->uri.len) {
1123
1124                    /*
1125                     * we omit the last "\" to cause redirect because
1126                     * the browsers do not treat "\" as "/" in relative URL path
1127                     */
1128
1129                    break;
1130                }
1131
1132                state = sw_slash;
1133                *u++ = '/';
1134                break;
1135#endif
1136            case '/':
1137                r->uri_ext = NULL;
1138                state = sw_slash;
1139                *u++ = ch;
1140                break;
1141            case '%':
1142                quoted_state = state;
1143                state = sw_quoted;
1144                break;
1145            case '?':
1146                r->args_start = p;
1147                goto args;
1148            case '#':
1149                goto done;
1150            case '.':
1151                r->uri_ext = u + 1;
1152                *u++ = ch;
1153                break;
1154            case '+':
1155                r->plus_in_uri = 1;
1156                /* fall through */
1157            default:
1158                *u++ = ch;
1159                break;
1160            }
1161
1162            ch = *p++;
1163            break;
1164
1165        case sw_slash:
1166
1167            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1168                state = sw_usual;
1169                *u++ = ch;
1170                ch = *p++;
1171                break;
1172            }
1173
1174            switch(ch) {
1175#if (NGX_WIN32)
1176            case '\\':
1177                break;
1178#endif
1179            case '/':
1180                if (!merge_slashes) {
1181                    *u++ = ch;
1182                }
1183                break;
1184            case '.':
1185                state = sw_dot;
1186                *u++ = ch;
1187                break;
1188            case '%':
1189                quoted_state = state;
1190                state = sw_quoted;
1191                break;
1192            case '?':
1193                r->args_start = p;
1194                goto args;
1195            case '#':
1196                goto done;
1197            case '+':
1198                r->plus_in_uri = 1;
1199            default:
1200                state = sw_usual;
1201                *u++ = ch;
1202                break;
1203            }
1204
1205            ch = *p++;
1206            break;
1207
1208        case sw_dot:
1209
1210            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1211                state = sw_usual;
1212                *u++ = ch;
1213                ch = *p++;
1214                break;
1215            }
1216
1217            switch(ch) {
1218#if (NGX_WIN32)
1219            case '\\':
1220#endif
1221            case '/':
1222                state = sw_slash;
1223                u--;
1224                break;
1225            case '.':
1226                state = sw_dot_dot;
1227                *u++ = ch;
1228                break;
1229            case '%':
1230                quoted_state = state;
1231                state = sw_quoted;
1232                break;
1233            case '?':
1234                r->args_start = p;
1235                goto args;
1236            case '#':
1237                goto done;
1238            case '+':
1239                r->plus_in_uri = 1;
1240            default:
1241                state = sw_usual;
1242                *u++ = ch;
1243                break;
1244            }
1245
1246            ch = *p++;
1247            break;
1248
1249        case sw_dot_dot:
1250
1251            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1252                state = sw_usual;
1253                *u++ = ch;
1254                ch = *p++;
1255                break;
1256            }
1257
1258            switch(ch) {
1259#if (NGX_WIN32)
1260            case '\\':
1261#endif
1262            case '/':
1263                state = sw_slash;
1264                u -= 5;
1265                for ( ;; ) {
1266                    if (u < r->uri.data) {
1267                        return NGX_HTTP_PARSE_INVALID_REQUEST;
1268                    }
1269                    if (*u == '/') {
1270                        u++;
1271                        break;
1272                    }
1273                    u--;
1274                }
1275                break;
1276            case '%':
1277                quoted_state = state;
1278                state = sw_quoted;
1279                break;
1280            case '?':
1281                r->args_start = p;
1282                goto args;
1283            case '#':
1284                goto done;
1285            case '+':
1286                r->plus_in_uri = 1;
1287            default:
1288                state = sw_usual;
1289                *u++ = ch;
1290                break;
1291            }
1292
1293            ch = *p++;
1294            break;
1295
1296        case sw_quoted:
1297            r->quoted_uri = 1;
1298
1299            if (ch >= '0' && ch <= '9') {
1300                decoded = (u_char) (ch - '0');
1301                state = sw_quoted_second;
1302                ch = *p++;
1303                break;
1304            }
1305
1306            c = (u_char) (ch | 0x20);
1307            if (c >= 'a' && c <= 'f') {
1308                decoded = (u_char) (c - 'a' + 10);
1309                state = sw_quoted_second;
1310                ch = *p++;
1311                break;
1312            }
1313
1314            return NGX_HTTP_PARSE_INVALID_REQUEST;
1315
1316        case sw_quoted_second:
1317            if (ch >= '0' && ch <= '9') {
1318                ch = (u_char) ((decoded << 4) + ch - '0');
1319
1320                if (ch == '%' || ch == '#') {
1321                    state = sw_usual;
1322                    *u++ = ch;
1323                    ch = *p++;
1324                    break;
1325
1326                } else if (ch == '\0') {
1327                    return NGX_HTTP_PARSE_INVALID_REQUEST;
1328                }
1329
1330                state = quoted_state;
1331                break;
1332            }
1333
1334            c = (u_char) (ch | 0x20);
1335            if (c >= 'a' && c <= 'f') {
1336                ch = (u_char) ((decoded << 4) + c - 'a' + 10);
1337
1338                if (ch == '?') {
1339                    state = sw_usual;
1340                    *u++ = ch;
1341                    ch = *p++;
1342                    break;
1343
1344                } else if (ch == '+') {
1345                    r->plus_in_uri = 1;
1346                }
1347
1348                state = quoted_state;
1349                break;
1350            }
1351
1352            return NGX_HTTP_PARSE_INVALID_REQUEST;
1353        }
1354    }
1355
1356done:
1357
1358    r->uri.len = u - r->uri.data;
1359
1360    if (r->uri_ext) {
1361        r->exten.len = u - r->uri_ext;
1362        r->exten.data = r->uri_ext;
1363    }
1364
1365    r->uri_ext = NULL;
1366
1367    return NGX_OK;
1368
1369args:
1370
1371    while (p < r->uri_end) {
1372        if (*p++ != '#') {
1373            continue;
1374        }
1375
1376        r->args.len = p - 1 - r->args_start;
1377        r->args.data = r->args_start;
1378        r->args_start = NULL;
1379
1380        break;
1381    }
1382
1383    r->uri.len = u - r->uri.data;
1384
1385    if (r->uri_ext) {
1386        r->exten.len = u - r->uri_ext;
1387        r->exten.data = r->uri_ext;
1388    }
1389
1390    r->uri_ext = NULL;
1391
1392    return NGX_OK;
1393}
1394
1395
1396ngx_int_t
1397ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
1398    ngx_http_status_t *status)
1399{
1400    u_char   ch;
1401    u_char  *p;
1402    enum {
1403        sw_start = 0,
1404        sw_H,
1405        sw_HT,
1406        sw_HTT,
1407        sw_HTTP,
1408        sw_first_major_digit,
1409        sw_major_digit,
1410        sw_first_minor_digit,
1411        sw_minor_digit,
1412        sw_status,
1413        sw_space_after_status,
1414        sw_status_text,
1415        sw_almost_done
1416    } state;
1417
1418    state = r->state;
1419
1420    for (p = b->pos; p < b->last; p++) {
1421        ch = *p;
1422
1423        switch (state) {
1424
1425        /* "HTTP/" */
1426        case sw_start:
1427            switch (ch) {
1428            case 'H':
1429                state = sw_H;
1430                break;
1431            default:
1432                return NGX_ERROR;
1433            }
1434            break;
1435
1436        case sw_H:
1437            switch (ch) {
1438            case 'T':
1439                state = sw_HT;
1440                break;
1441            default:
1442                return NGX_ERROR;
1443            }
1444            break;
1445
1446        case sw_HT:
1447            switch (ch) {
1448            case 'T':
1449                state = sw_HTT;
1450                break;
1451            default:
1452                return NGX_ERROR;
1453            }
1454            break;
1455
1456        case sw_HTT:
1457            switch (ch) {
1458            case 'P':
1459                state = sw_HTTP;
1460                break;
1461            default:
1462                return NGX_ERROR;
1463            }
1464            break;
1465
1466        case sw_HTTP:
1467            switch (ch) {
1468            case '/':
1469                state = sw_first_major_digit;
1470                break;
1471            default:
1472                return NGX_ERROR;
1473            }
1474            break;
1475
1476        /* the first digit of major HTTP version */
1477        case sw_first_major_digit:
1478            if (ch < '1' || ch > '9') {
1479                return NGX_ERROR;
1480            }
1481
1482            r->http_major = ch - '0';
1483            state = sw_major_digit;
1484            break;
1485
1486        /* the major HTTP version or dot */
1487        case sw_major_digit:
1488            if (ch == '.') {
1489                state = sw_first_minor_digit;
1490                break;
1491            }
1492
1493            if (ch < '0' || ch > '9') {
1494                return NGX_ERROR;
1495            }
1496
1497            r->http_major = r->http_major * 10 + ch - '0';
1498            break;
1499
1500        /* the first digit of minor HTTP version */
1501        case sw_first_minor_digit:
1502            if (ch < '0' || ch > '9') {
1503                return NGX_ERROR;
1504            }
1505
1506            r->http_minor = ch - '0';
1507            state = sw_minor_digit;
1508            break;
1509
1510        /* the minor HTTP version or the end of the request line */
1511        case sw_minor_digit:
1512            if (ch == ' ') {
1513                state = sw_status;
1514                break;
1515            }
1516
1517            if (ch < '0' || ch > '9') {
1518                return NGX_ERROR;
1519            }
1520
1521            r->http_minor = r->http_minor * 10 + ch - '0';
1522            break;
1523
1524        /* HTTP status code */
1525        case sw_status:
1526            if (ch == ' ') {
1527                break;
1528            }
1529
1530            if (ch < '0' || ch > '9') {
1531                return NGX_ERROR;
1532            }
1533
1534            status->code = status->code * 10 + ch - '0';
1535
1536            if (++status->count == 3) {
1537                state = sw_space_after_status;
1538                status->start = p - 2;
1539            }
1540
1541            break;
1542
1543        /* space or end of line */
1544        case sw_space_after_status:
1545            switch (ch) {
1546            case ' ':
1547                state = sw_status_text;
1548                break;
1549            case '.':                    /* IIS may send 403.1, 403.2, etc */
1550                state = sw_status_text;
1551                break;
1552            case CR:
1553                state = sw_almost_done;
1554                break;
1555            case LF:
1556                goto done;
1557            default:
1558                return NGX_ERROR;
1559            }
1560            break;
1561
1562        /* any text until end of line */
1563        case sw_status_text:
1564            switch (ch) {
1565            case CR:
1566                state = sw_almost_done;
1567
1568                break;
1569            case LF:
1570                goto done;
1571            }
1572            break;
1573
1574        /* end of status line */
1575        case sw_almost_done:
1576            status->end = p - 1;
1577            switch (ch) {
1578            case LF:
1579                goto done;
1580            default:
1581                return NGX_ERROR;
1582            }
1583        }
1584    }
1585
1586    b->pos = p;
1587    r->state = state;
1588
1589    return NGX_AGAIN;
1590
1591done:
1592
1593    b->pos = p + 1;
1594
1595    if (status->end == NULL) {
1596        status->end = p;
1597    }
1598
1599    status->http_version = r->http_major * 1000 + r->http_minor;
1600    r->state = sw_start;
1601
1602    return NGX_OK;
1603}
1604
1605
1606ngx_int_t
1607ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
1608    ngx_str_t *args, ngx_uint_t *flags)
1609{
1610    u_char  ch, *p;
1611    size_t  len;
1612
1613    len = uri->len;
1614    p = uri->data;
1615
1616    if (len == 0 || p[0] == '?') {
1617        goto unsafe;
1618    }
1619
1620    if (p[0] == '.' && len == 3 && p[1] == '.' && (ngx_path_separator(p[2]))) {
1621        goto unsafe;
1622    }
1623
1624    for ( /* void */ ; len; len--) {
1625
1626        ch = *p++;
1627
1628        if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1629            continue;
1630        }
1631
1632        if (ch == '?') {
1633            args->len = len - 1;
1634            args->data = p;
1635            uri->len -= len;
1636
1637            return NGX_OK;
1638        }
1639
1640        if (ch == '\0') {
1641            goto unsafe;
1642        }
1643
1644        if (ngx_path_separator(ch) && len > 2) {
1645
1646            /* detect "/../" */
1647
1648            if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) {
1649                goto unsafe;
1650            }
1651        }
1652    }
1653
1654    return NGX_OK;
1655
1656unsafe:
1657
1658    if (*flags & NGX_HTTP_LOG_UNSAFE) {
1659        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1660                      "unsafe URI \"%V\" was detected", uri);
1661    }
1662
1663    return NGX_ERROR;
1664}
1665
1666
1667ngx_int_t
1668ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,
1669    ngx_str_t *value)
1670{
1671    ngx_uint_t         i;
1672    u_char            *start, *last, *end, ch;
1673    ngx_table_elt_t  **h;
1674
1675    h = headers->elts;
1676
1677    for (i = 0; i < headers->nelts; i++) {
1678
1679        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
1680                       "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
1681
1682        if (name->len > h[i]->value.len) {
1683            continue;
1684        }
1685
1686        start = h[i]->value.data;
1687        end = h[i]->value.data + h[i]->value.len;
1688
1689        while (start < end) {
1690
1691            if (ngx_strncasecmp(start, name->data, name->len) != 0) {
1692                goto skip;
1693            }
1694
1695            for (start += name->len; start < end && *start == ' '; start++) {
1696                /* void */
1697            }
1698
1699            if (value == NULL) {
1700                if (start == end || *start == ',') {
1701                    return i;
1702                }
1703
1704                goto skip;
1705            }
1706
1707            if (start == end || *start++ != '=') {
1708                /* the invalid header value */
1709                goto skip;
1710            }
1711
1712            while (start < end && *start == ' ') { start++; }
1713
1714            for (last = start; last < end && *last != ';'; last++) {
1715                /* void */
1716            }
1717
1718            value->len = last - start;
1719            value->data = start;
1720
1721            return i;
1722
1723        skip:
1724
1725            while (start < end) {
1726                ch = *start++;
1727                if (ch == ';' || ch == ',') {
1728                    break;
1729                }
1730            }
1731
1732            while (start < end && *start == ' ') { start++; }
1733        }
1734    }
1735
1736    return NGX_DECLINED;
1737}
1738
1739
1740ngx_int_t
1741ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
1742{
1743    u_char  *p, *last;
1744
1745    if (r->args.len == 0) {
1746        return NGX_DECLINED;
1747    }
1748
1749    p = r->args.data;
1750    last = p + r->args.len;
1751
1752    for ( /* void */ ; p < last; p++) {
1753
1754        /* we need '=' after name, so drop one char from last */
1755
1756        p = ngx_strlcasestrn(p, last - 1, name, len - 1);
1757
1758        if (p == NULL) {
1759            return NGX_DECLINED;
1760        }
1761
1762        if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
1763
1764            value->data = p + len + 1;
1765
1766            p = ngx_strlchr(p, last, '&');
1767
1768            if (p == NULL) {
1769                p = r->args.data + r->args.len;
1770            }
1771
1772            value->len = p - value->data;
1773
1774            return NGX_OK;
1775        }
1776    }
1777
1778    return NGX_DECLINED;
1779}
1780
1781
1782void
1783ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
1784{
1785    u_char  *p, *last;
1786
1787    last = uri->data + uri->len;
1788
1789    p = ngx_strlchr(uri->data, last, '?');
1790
1791    if (p) {
1792        uri->len = p - uri->data;
1793        p++;
1794        args->len = last - p;
1795        args->data = p;
1796
1797    } else {
1798        args->len = 0;
1799    }
1800}
Note: See TracBrowser for help on using the repository browser.