upstream nginx-0.7.36
[nginx.git] / nginx / src / http / ngx_http_parse.c
1
2 /*
3  * Copyright (C) Igor Sysoev
4  */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12 static uint32_t  usual[] = {
13     0xffffdbfe, /* 1111 1111 1111 1111  1101 1011 1111 1110 */
14
15                 /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
16     0x7fff37d6, /* 0111 1111 1111 1111  0011 0111 1101 0110 */
17
18                 /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
19 #if (NGX_WIN32)
20     0xefffffff, /* 1110 1111 1111 1111  1111 1111 1111 1111 */
21 #else
22     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
23 #endif
24
25                 /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
26     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
27
28     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
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 };
33
34
35 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
36
37 #define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
38     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
39
40 #define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
41     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
42
43 #define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
44     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
45
46 #define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
47     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
48         && m[4] == c4
49
50 #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
51     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
52         && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)
53
54 #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
55     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
56         && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
57
58 #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
59     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
60         && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
61
62 #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
63     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
64         && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)  \
65         && m[8] == c8
66
67 #else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */
68
69 #define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
70     m[0] == c0 && m[1] == c1 && m[2] == c2
71
72 #define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
73     m[0] == c0 && m[2] == c2 && m[3] == c3
74
75 #define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
76     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3
77
78 #define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
79     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
80
81 #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
82     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
83         && m[4] == c4 && m[5] == c5
84
85 #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
86     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
87         && m[4] == c4 && m[5] == c5 && m[6] == c6
88
89 #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
90     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
91         && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7
92
93 #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
94     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
95         && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8
96
97 #endif
98
99
100 /* gcc, icc, msvc and others compile these switches as an jump table */
101
102 ngx_int_t
103 ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
104 {
105     u_char  c, ch, *p, *m;
106     enum {
107         sw_start = 0,
108         sw_method,
109         sw_spaces_before_uri,
110         sw_schema,
111         sw_schema_slash,
112         sw_schema_slash_slash,
113         sw_host,
114         sw_port,
115         sw_after_slash_in_uri,
116         sw_check_uri,
117         sw_uri,
118         sw_http_09,
119         sw_http_H,
120         sw_http_HT,
121         sw_http_HTT,
122         sw_http_HTTP,
123         sw_first_major_digit,
124         sw_major_digit,
125         sw_first_minor_digit,
126         sw_minor_digit,
127         sw_spaces_after_digit,
128         sw_almost_done
129     } state;
130
131     state = r->state;
132
133     for (p = b->pos; p < b->last; p++) {
134         ch = *p;
135
136         switch (state) {
137
138         /* HTTP methods: GET, HEAD, POST */
139         case sw_start:
140             r->request_start = p;
141
142             if (ch == CR || ch == LF) {
143                 break;
144             }
145
146             if (ch < 'A' || ch > 'Z') {
147                 return NGX_HTTP_PARSE_INVALID_METHOD;
148             }
149
150             state = sw_method;
151             break;
152
153         case sw_method:
154             if (ch == ' ') {
155                 r->method_end = p - 1;
156                 m = r->request_start;
157
158                 switch (p - m) {
159
160                 case 3:
161                     if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
162                         r->method = NGX_HTTP_GET;
163                         break;
164                     }
165
166                     if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
167                         r->method = NGX_HTTP_PUT;
168                         break;
169                     }
170
171                     break;
172
173                 case 4:
174                     if (m[1] == 'O') {
175
176                         if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
177                             r->method = NGX_HTTP_POST;
178                             break;
179                         }
180
181                         if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
182                             r->method = NGX_HTTP_COPY;
183                             break;
184                         }
185
186                         if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
187                             r->method = NGX_HTTP_MOVE;
188                             break;
189                         }
190
191                         if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
192                             r->method = NGX_HTTP_LOCK;
193                             break;
194                         }
195
196                     } else {
197
198                         if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
199                             r->method = NGX_HTTP_HEAD;
200                             break;
201                         }
202                     }
203
204                     break;
205
206                 case 5:
207                     if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
208                         r->method = NGX_HTTP_MKCOL;
209                     }
210
211                     if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
212                         r->method = NGX_HTTP_TRACE;
213                     }
214
215                     break;
216
217                 case 6:
218                     if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
219                         r->method = NGX_HTTP_DELETE;
220                         break;
221                     }
222
223                     if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
224                         r->method = NGX_HTTP_UNLOCK;
225                         break;
226                     }
227
228                     break;
229
230                 case 7:
231                     if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
232                     {
233                         r->method = NGX_HTTP_OPTIONS;
234                     }
235
236                     break;
237
238                 case 8:
239                     if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
240                     {
241                         r->method = NGX_HTTP_PROPFIND;
242                     }
243
244                     break;
245
246                 case 9:
247                     if (ngx_str9cmp(m,
248                             'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
249                     {
250                         r->method = NGX_HTTP_PROPPATCH;
251                     }
252
253                     break;
254                 }
255
256                 state = sw_spaces_before_uri;
257                 break;
258             }
259
260             if (ch < 'A' || ch > 'Z') {
261                 return NGX_HTTP_PARSE_INVALID_METHOD;
262             }
263
264             break;
265
266         /* space* before URI */
267         case sw_spaces_before_uri:
268
269             if (ch == '/' ){
270                 r->uri_start = p;
271                 state = sw_after_slash_in_uri;
272                 break;
273             }
274
275             c = (u_char) (ch | 0x20);
276             if (c >= 'a' && c <= 'z') {
277                 r->schema_start = p;
278                 state = sw_schema;
279                 break;
280             }
281
282             switch (ch) {
283             case ' ':
284                 break;
285             default:
286                 return NGX_HTTP_PARSE_INVALID_REQUEST;
287             }
288             break;
289
290         case sw_schema:
291
292             c = (u_char) (ch | 0x20);
293             if (c >= 'a' && c <= 'z') {
294                 break;
295             }
296
297             switch (ch) {
298             case ':':
299                 r->schema_end = p;
300                 state = sw_schema_slash;
301                 break;
302             default:
303                 return NGX_HTTP_PARSE_INVALID_REQUEST;
304             }
305             break;
306
307         case sw_schema_slash:
308             switch (ch) {
309             case '/':
310                 state = sw_schema_slash_slash;
311                 break;
312             default:
313                 return NGX_HTTP_PARSE_INVALID_REQUEST;
314             }
315             break;
316
317         case sw_schema_slash_slash:
318             switch (ch) {
319             case '/':
320                 r->host_start = p + 1;
321                 state = sw_host;
322                 break;
323             default:
324                 return NGX_HTTP_PARSE_INVALID_REQUEST;
325             }
326             break;
327
328         case sw_host:
329
330             c = (u_char) (ch | 0x20);
331             if (c >= 'a' && c <= 'z') {
332                 break;
333             }
334
335             if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
336                 break;
337             }
338
339             r->host_end = p;
340
341             switch (ch) {
342             case ':':
343                 state = sw_port;
344                 break;
345             case '/':
346                 r->uri_start = p;
347                 state = sw_after_slash_in_uri;
348                 break;
349             case ' ':
350                 /*
351                  * use single "/" from request line to preserve pointers,
352                  * if request line will be copied to large client buffer
353                  */
354                 r->uri_start = r->schema_end + 1;
355                 r->uri_end = r->schema_end + 2;
356                 state = sw_http_09;
357                 break;
358             default:
359                 return NGX_HTTP_PARSE_INVALID_REQUEST;
360             }
361             break;
362
363         case sw_port:
364             if (ch >= '0' && ch <= '9') {
365                 break;
366             }
367
368             switch (ch) {
369             case '/':
370                 r->port_end = p;
371                 r->uri_start = p;
372                 state = sw_after_slash_in_uri;
373                 break;
374             case ' ':
375                 r->port_end = p;
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_http_09;
383                 break;
384             default:
385                 return NGX_HTTP_PARSE_INVALID_REQUEST;
386             }
387             break;
388
389         /* check "/.", "//", "%", and "\" (Win32) in URI */
390         case sw_after_slash_in_uri:
391
392             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
393                 state = sw_check_uri;
394                 break;
395             }
396
397             switch (ch) {
398             case ' ':
399                 r->uri_end = p;
400                 state = sw_http_09;
401                 break;
402             case CR:
403                 r->uri_end = p;
404                 r->http_minor = 9;
405                 state = sw_almost_done;
406                 break;
407             case LF:
408                 r->uri_end = p;
409                 r->http_minor = 9;
410                 goto done;
411             case '.':
412                 r->complex_uri = 1;
413                 state = sw_uri;
414                 break;
415             case '%':
416                 r->quoted_uri = 1;
417                 state = sw_uri;
418                 break;
419             case '/':
420                 r->complex_uri = 1;
421                 state = sw_uri;
422                 break;
423 #if (NGX_WIN32)
424             case '\\':
425                 r->complex_uri = 1;
426                 state = sw_uri;
427                 break;
428 #endif
429             case '?':
430                 r->args_start = p + 1;
431                 state = sw_uri;
432                 break;
433             case '#':
434                 r->complex_uri = 1;
435                 state = sw_uri;
436                 break;
437             case '+':
438                 r->plus_in_uri = 1;
439                 break;
440             case '\0':
441                 r->zero_in_uri = 1;
442                 break;
443             default:
444                 state = sw_check_uri;
445                 break;
446             }
447             break;
448
449         /* check "/", "%" and "\" (Win32) in URI */
450         case sw_check_uri:
451
452             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
453                 break;
454             }
455
456             switch (ch) {
457             case '/':
458                 r->uri_ext = NULL;
459                 state = sw_after_slash_in_uri;
460                 break;
461             case '.':
462                 r->uri_ext = p + 1;
463                 break;
464             case ' ':
465                 r->uri_end = p;
466                 state = sw_http_09;
467                 break;
468             case CR:
469                 r->uri_end = p;
470                 r->http_minor = 9;
471                 state = sw_almost_done;
472                 break;
473             case LF:
474                 r->uri_end = p;
475                 r->http_minor = 9;
476                 goto done;
477 #if (NGX_WIN32)
478             case '\\':
479                 r->complex_uri = 1;
480                 state = sw_after_slash_in_uri;
481                 break;
482 #endif
483             case '%':
484                 r->quoted_uri = 1;
485                 state = sw_uri;
486                 break;
487             case '?':
488                 r->args_start = p + 1;
489                 state = sw_uri;
490                 break;
491             case '#':
492                 r->complex_uri = 1;
493                 state = sw_uri;
494                 break;
495             case '+':
496                 r->plus_in_uri = 1;
497                 break;
498             case '\0':
499                 r->zero_in_uri = 1;
500                 break;
501             }
502             break;
503
504         /* URI */
505         case sw_uri:
506
507             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
508                 break;
509             }
510
511             switch (ch) {
512             case ' ':
513                 r->uri_end = p;
514                 state = sw_http_09;
515                 break;
516             case CR:
517                 r->uri_end = p;
518                 r->http_minor = 9;
519                 state = sw_almost_done;
520                 break;
521             case LF:
522                 r->uri_end = p;
523                 r->http_minor = 9;
524                 goto done;
525             case '#':
526                 r->complex_uri = 1;
527                 break;
528             case '\0':
529                 r->zero_in_uri = 1;
530                 break;
531             }
532             break;
533
534         /* space+ after URI */
535         case sw_http_09:
536             switch (ch) {
537             case ' ':
538                 break;
539             case CR:
540                 r->http_minor = 9;
541                 state = sw_almost_done;
542                 break;
543             case LF:
544                 r->http_minor = 9;
545                 goto done;
546             case 'H':
547                 r->http_protocol.data = p;
548                 state = sw_http_H;
549                 break;
550             default:
551                 return NGX_HTTP_PARSE_INVALID_REQUEST;
552             }
553             break;
554
555         case sw_http_H:
556             switch (ch) {
557             case 'T':
558                 state = sw_http_HT;
559                 break;
560             default:
561                 return NGX_HTTP_PARSE_INVALID_REQUEST;
562             }
563             break;
564
565         case sw_http_HT:
566             switch (ch) {
567             case 'T':
568                 state = sw_http_HTT;
569                 break;
570             default:
571                 return NGX_HTTP_PARSE_INVALID_REQUEST;
572             }
573             break;
574
575         case sw_http_HTT:
576             switch (ch) {
577             case 'P':
578                 state = sw_http_HTTP;
579                 break;
580             default:
581                 return NGX_HTTP_PARSE_INVALID_REQUEST;
582             }
583             break;
584
585         case sw_http_HTTP:
586             switch (ch) {
587             case '/':
588                 state = sw_first_major_digit;
589                 break;
590             default:
591                 return NGX_HTTP_PARSE_INVALID_REQUEST;
592             }
593             break;
594
595         /* first digit of major HTTP version */
596         case sw_first_major_digit:
597             if (ch < '1' || ch > '9') {
598                 return NGX_HTTP_PARSE_INVALID_REQUEST;
599             }
600
601             r->http_major = ch - '0';
602             state = sw_major_digit;
603             break;
604
605         /* major HTTP version or dot */
606         case sw_major_digit:
607             if (ch == '.') {
608                 state = sw_first_minor_digit;
609                 break;
610             }
611
612             if (ch < '0' || ch > '9') {
613                 return NGX_HTTP_PARSE_INVALID_REQUEST;
614             }
615
616             r->http_major = r->http_major * 10 + ch - '0';
617             break;
618
619         /* first digit of minor HTTP version */
620         case sw_first_minor_digit:
621             if (ch < '0' || ch > '9') {
622                 return NGX_HTTP_PARSE_INVALID_REQUEST;
623             }
624
625             r->http_minor = ch - '0';
626             state = sw_minor_digit;
627             break;
628
629         /* minor HTTP version or end of request line */
630         case sw_minor_digit:
631             if (ch == CR) {
632                 state = sw_almost_done;
633                 break;
634             }
635
636             if (ch == LF) {
637                 goto done;
638             }
639
640             if (ch == ' ') {
641                 state = sw_spaces_after_digit;
642                 break;
643             }
644
645             if (ch < '0' || ch > '9') {
646                 return NGX_HTTP_PARSE_INVALID_REQUEST;
647             }
648
649             r->http_minor = r->http_minor * 10 + ch - '0';
650             break;
651
652         case sw_spaces_after_digit:
653             switch (ch) {
654             case ' ':
655                 break;
656             case CR:
657                 state = sw_almost_done;
658                 break;
659             case LF:
660                 goto done;
661             default:
662                 return NGX_HTTP_PARSE_INVALID_REQUEST;
663             }
664             break;
665
666         /* end of request line */
667         case sw_almost_done:
668             r->request_end = p - 1;
669             switch (ch) {
670             case LF:
671                 goto done;
672             default:
673                 return NGX_HTTP_PARSE_INVALID_REQUEST;
674             }
675         }
676     }
677
678     b->pos = p;
679     r->state = state;
680
681     return NGX_AGAIN;
682
683 done:
684
685     b->pos = p + 1;
686
687     if (r->request_end == NULL) {
688         r->request_end = p;
689     }
690
691     r->http_version = r->http_major * 1000 + r->http_minor;
692     r->state = sw_start;
693
694     if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
695         return NGX_HTTP_PARSE_INVALID_09_METHOD;
696     }
697
698     return NGX_OK;
699 }
700
701
702 ngx_int_t
703 ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
704     ngx_uint_t allow_underscores)
705 {
706     u_char      c, ch, *p;
707     ngx_uint_t  hash, i;
708     enum {
709         sw_start = 0,
710         sw_name,
711         sw_space_before_value,
712         sw_value,
713         sw_space_after_value,
714         sw_ignore_line,
715         sw_almost_done,
716         sw_header_almost_done
717     } state;
718
719     /* the last '\0' is not needed because string is zero terminated */
720
721     static u_char  lowcase[] =
722         "\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"
723         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
724         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
725         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
726         "\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"
727         "\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"
728         "\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"
729         "\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";
730
731     state = r->state;
732     hash = r->header_hash;
733     i = r->lowcase_index;
734
735     for (p = b->pos; p < b->last; p++) {
736         ch = *p;
737
738         switch (state) {
739
740         /* first char */
741         case sw_start:
742             r->invalid_header = 0;
743
744             switch (ch) {
745             case CR:
746                 r->header_end = p;
747                 state = sw_header_almost_done;
748                 break;
749             case LF:
750                 r->header_end = p;
751                 goto header_done;
752             default:
753                 state = sw_name;
754                 r->header_name_start = p;
755
756                 c = lowcase[ch];
757
758                 if (c) {
759                     hash = ngx_hash(0, c);
760                     r->lowcase_header[0] = c;
761                     i = 1;
762                     break;
763                 }
764
765                 r->invalid_header = 1;
766
767                 break;
768
769             }
770             break;
771
772         /* header name */
773         case sw_name:
774             c = lowcase[ch];
775
776             if (c) {
777                 hash = ngx_hash(hash, c);
778                 r->lowcase_header[i++] = c;
779                 i &= (NGX_HTTP_LC_HEADER_LEN - 1);
780                 break;
781             }
782
783             if (ch == '_') {
784                 if (allow_underscores) {
785                     hash = ngx_hash(hash, ch);
786                     r->lowcase_header[i++] = ch;
787                     i &= (NGX_HTTP_LC_HEADER_LEN - 1);
788
789                 } else {
790                     r->invalid_header = 1;
791                 }
792
793                 break;
794             }
795
796             if (ch == ':') {
797                 r->header_name_end = p;
798                 state = sw_space_before_value;
799                 break;
800             }
801
802             if (ch == CR) {
803                 r->header_name_end = p;
804                 r->header_start = p;
805                 r->header_end = p;
806                 state = sw_almost_done;
807                 break;
808             }
809
810             if (ch == LF) {
811                 r->header_name_end = p;
812                 r->header_start = p;
813                 r->header_end = p;
814                 goto done;
815             }
816
817             /* IIS may send the duplicate "HTTP/1.1 ..." lines */
818             if (ch == '/'
819                 && r->upstream
820                 && p - r->header_name_start == 4
821                 && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
822             {
823                 state = sw_ignore_line;
824                 break;
825             }
826
827             r->invalid_header = 1;
828
829             break;
830
831         /* space* before header value */
832         case sw_space_before_value:
833             switch (ch) {
834             case ' ':
835                 break;
836             case CR:
837                 r->header_start = p;
838                 r->header_end = p;
839                 state = sw_almost_done;
840                 break;
841             case LF:
842                 r->header_start = p;
843                 r->header_end = p;
844                 goto done;
845             default:
846                 r->header_start = p;
847                 state = sw_value;
848                 break;
849             }
850             break;
851
852         /* header value */
853         case sw_value:
854             switch (ch) {
855             case ' ':
856                 r->header_end = p;
857                 state = sw_space_after_value;
858                 break;
859             case CR:
860                 r->header_end = p;
861                 state = sw_almost_done;
862                 break;
863             case LF:
864                 r->header_end = p;
865                 goto done;
866             }
867             break;
868
869         /* space* before end of header line */
870         case sw_space_after_value:
871             switch (ch) {
872             case ' ':
873                 break;
874             case CR:
875                 state = sw_almost_done;
876                 break;
877             case LF:
878                 goto done;
879             default:
880                 state = sw_value;
881                 break;
882             }
883             break;
884
885         /* ignore header line */
886         case sw_ignore_line:
887             switch (ch) {
888             case LF:
889                 state = sw_start;
890                 break;
891             default:
892                 break;
893             }
894             break;
895
896         /* end of header line */
897         case sw_almost_done:
898             switch (ch) {
899             case LF:
900                 goto done;
901             case CR:
902                 break;
903             default:
904                 return NGX_HTTP_PARSE_INVALID_HEADER;
905             }
906             break;
907
908         /* end of header */
909         case sw_header_almost_done:
910             switch (ch) {
911             case LF:
912                 goto header_done;
913             default:
914                 return NGX_HTTP_PARSE_INVALID_HEADER;
915             }
916         }
917     }
918
919     b->pos = p;
920     r->state = state;
921     r->header_hash = hash;
922     r->lowcase_index = i;
923
924     return NGX_AGAIN;
925
926 done:
927
928     b->pos = p + 1;
929     r->state = sw_start;
930     r->header_hash = hash;
931     r->lowcase_index = i;
932
933     return NGX_OK;
934
935 header_done:
936
937     b->pos = p + 1;
938     r->state = sw_start;
939
940     return NGX_HTTP_PARSE_HEADER_DONE;
941 }
942
943
944 ngx_int_t
945 ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
946 {
947     u_char  c, ch, decoded, *p, *u;
948     enum {
949         sw_usual = 0,
950         sw_slash,
951         sw_dot,
952         sw_dot_dot,
953 #if (NGX_WIN32)
954         sw_dot_dot_dot,
955 #endif
956         sw_quoted,
957         sw_quoted_second
958     } state, quoted_state;
959
960 #if (NGX_SUPPRESS_WARN)
961     decoded = '\0';
962     quoted_state = sw_usual;
963 #endif
964
965     state = sw_usual;
966     p = r->uri_start;
967     u = r->uri.data;
968     r->uri_ext = NULL;
969     r->args_start = NULL;
970
971     ch = *p++;
972
973     while (p <= r->uri_end) {
974
975         /*
976          * we use "ch = *p++" inside the cycle, but this operation is safe,
977          * because after the URI there is always at least one charcter:
978          * the line feed
979          */
980
981         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
982                        "s:%d in:'%Xd:%c', out:'%c'", state, ch, ch, *u);
983
984         switch (state) {
985
986         case sw_usual:
987
988             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
989                 *u++ = ch;
990                 ch = *p++;
991                 break;
992             }
993
994             switch(ch) {
995 #if (NGX_WIN32)
996             case '\\':
997                 r->uri_ext = NULL;
998
999                 if (p == r->uri_start + r->uri.len) {
1000
1001                     /*
1002                      * we omit the last "\" to cause redirect because
1003                      * the browsers do not treat "\" as "/" in relative URL path
1004                      */
1005
1006                     break;
1007                 }
1008
1009                 state = sw_slash;
1010                 *u++ = '/';
1011                 break;
1012 #endif
1013             case '/':
1014                 r->uri_ext = NULL;
1015                 state = sw_slash;
1016                 *u++ = ch;
1017                 break;
1018             case '%':
1019                 quoted_state = state;
1020                 state = sw_quoted;
1021                 break;
1022             case '?':
1023                 r->args_start = p;
1024                 goto args;
1025             case '#':
1026                 goto done;
1027             case '.':
1028                 r->uri_ext = u + 1;
1029                 *u++ = ch;
1030                 break;
1031             case '+':
1032                 r->plus_in_uri = 1;
1033             default:
1034                 *u++ = ch;
1035                 break;
1036             }
1037
1038             ch = *p++;
1039             break;
1040
1041         case sw_slash:
1042
1043             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1044                 state = sw_usual;
1045                 *u++ = ch;
1046                 ch = *p++;
1047                 break;
1048             }
1049
1050             switch(ch) {
1051 #if (NGX_WIN32)
1052             case '\\':
1053                 break;
1054 #endif
1055             case '/':
1056                 if (!merge_slashes) {
1057                     *u++ = ch;
1058                 }
1059                 break;
1060             case '.':
1061                 state = sw_dot;
1062                 *u++ = ch;
1063                 break;
1064             case '%':
1065                 quoted_state = state;
1066                 state = sw_quoted;
1067                 break;
1068             case '?':
1069                 r->args_start = p;
1070                 goto args;
1071             case '#':
1072                 goto done;
1073             case '+':
1074                 r->plus_in_uri = 1;
1075             default:
1076                 state = sw_usual;
1077                 *u++ = ch;
1078                 break;
1079             }
1080
1081             ch = *p++;
1082             break;
1083
1084         case sw_dot:
1085
1086             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1087                 state = sw_usual;
1088                 *u++ = ch;
1089                 ch = *p++;
1090                 break;
1091             }
1092
1093             switch(ch) {
1094 #if (NGX_WIN32)
1095             case '\\':
1096 #endif
1097             case '/':
1098                 state = sw_slash;
1099                 u--;
1100                 break;
1101             case '.':
1102                 state = sw_dot_dot;
1103                 *u++ = ch;
1104                 break;
1105             case '%':
1106                 quoted_state = state;
1107                 state = sw_quoted;
1108                 break;
1109             case '?':
1110                 r->args_start = p;
1111                 goto args;
1112             case '#':
1113                 goto done;
1114             case '+':
1115                 r->plus_in_uri = 1;
1116             default:
1117                 state = sw_usual;
1118                 *u++ = ch;
1119                 break;
1120             }
1121
1122             ch = *p++;
1123             break;
1124
1125         case sw_dot_dot:
1126
1127             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1128                 state = sw_usual;
1129                 *u++ = ch;
1130                 ch = *p++;
1131                 break;
1132             }
1133
1134             switch(ch) {
1135 #if (NGX_WIN32)
1136             case '\\':
1137 #endif
1138             case '/':
1139                 state = sw_slash;
1140                 u -= 4;
1141                 if (u < r->uri.data) {
1142                     return NGX_HTTP_PARSE_INVALID_REQUEST;
1143                 }
1144                 while (*(u - 1) != '/') {
1145                     u--;
1146                 }
1147                 break;
1148             case '%':
1149                 quoted_state = state;
1150                 state = sw_quoted;
1151                 break;
1152             case '?':
1153                 r->args_start = p;
1154                 goto args;
1155             case '#':
1156                 goto done;
1157 #if (NGX_WIN32)
1158             case '.':
1159                 state = sw_dot_dot_dot;
1160                 *u++ = ch;
1161                 break;
1162 #endif
1163             case '+':
1164                 r->plus_in_uri = 1;
1165             default:
1166                 state = sw_usual;
1167                 *u++ = ch;
1168                 break;
1169             }
1170
1171             ch = *p++;
1172             break;
1173
1174 #if (NGX_WIN32)
1175         case sw_dot_dot_dot:
1176
1177             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1178                 state = sw_usual;
1179                 *u++ = ch;
1180                 ch = *p++;
1181                 break;
1182             }
1183
1184             switch(ch) {
1185             case '\\':
1186             case '/':
1187                 state = sw_slash;
1188                 u -= 5;
1189                 if (u < r->uri.data) {
1190                     return NGX_HTTP_PARSE_INVALID_REQUEST;
1191                 }
1192                 while (*u != '/') {
1193                     u--;
1194                 }
1195                 if (u < r->uri.data) {
1196                     return NGX_HTTP_PARSE_INVALID_REQUEST;
1197                 }
1198                 while (*(u - 1) != '/') {
1199                     u--;
1200                 }
1201                 break;
1202             case '%':
1203                 quoted_state = state;
1204                 state = sw_quoted;
1205                 break;
1206             case '?':
1207                 r->args_start = p;
1208                 goto args;
1209             case '#':
1210                 goto done;
1211             case '+':
1212                 r->plus_in_uri = 1;
1213             default:
1214                 state = sw_usual;
1215                 *u++ = ch;
1216                 break;
1217             }
1218
1219             ch = *p++;
1220             break;
1221 #endif
1222
1223         case sw_quoted:
1224             r->quoted_uri = 1;
1225
1226             if (ch >= '0' && ch <= '9') {
1227                 decoded = (u_char) (ch - '0');
1228                 state = sw_quoted_second;
1229                 ch = *p++;
1230                 break;
1231             }
1232
1233             c = (u_char) (ch | 0x20);
1234             if (c >= 'a' && c <= 'f') {
1235                 decoded = (u_char) (c - 'a' + 10);
1236                 state = sw_quoted_second;
1237                 ch = *p++;
1238                 break;
1239             }
1240
1241             return NGX_HTTP_PARSE_INVALID_REQUEST;
1242
1243         case sw_quoted_second:
1244             if (ch >= '0' && ch <= '9') {
1245                 ch = (u_char) ((decoded << 4) + ch - '0');
1246
1247                 if (ch == '%') {
1248                     state = sw_usual;
1249                     *u++ = ch;
1250                     ch = *p++;
1251                     break;
1252                 }
1253
1254                 if (ch == '#') {
1255                     *u++ = ch;
1256                     ch = *p++;
1257
1258                 } else if (ch == '\0') {
1259                     r->zero_in_uri = 1;
1260                 }
1261
1262                 state = quoted_state;
1263                 break;
1264             }
1265
1266             c = (u_char) (ch | 0x20);
1267             if (c >= 'a' && c <= 'f') {
1268                 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
1269
1270                 if (ch == '?') {
1271                     *u++ = ch;
1272                     ch = *p++;
1273
1274                 } else if (ch == '+') {
1275                     r->plus_in_uri = 1;
1276                 }
1277
1278                 state = quoted_state;
1279                 break;
1280             }
1281
1282             return NGX_HTTP_PARSE_INVALID_REQUEST;
1283         }
1284     }
1285
1286 done:
1287
1288     r->uri.len = u - r->uri.data;
1289
1290     if (r->uri_ext) {
1291         r->exten.len = u - r->uri_ext;
1292         r->exten.data = r->uri_ext;
1293     }
1294
1295     r->uri_ext = NULL;
1296
1297     return NGX_OK;
1298
1299 args:
1300
1301     while (p < r->uri_end) {
1302         if (*p++ != '#') {
1303             continue;
1304         }
1305
1306         r->args.len = p - 1 - r->args_start;
1307         r->args.data = r->args_start;
1308         r->args_start = NULL;
1309
1310         break;
1311     }
1312
1313     r->uri.len = u - r->uri.data;
1314
1315     if (r->uri_ext) {
1316         r->exten.len = u - r->uri_ext;
1317         r->exten.data = r->uri_ext;
1318     }
1319
1320     r->uri_ext = NULL;
1321
1322     return NGX_OK;
1323 }
1324
1325
1326 ngx_int_t
1327 ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
1328     ngx_str_t *args, ngx_uint_t *flags)
1329 {
1330     u_char  ch, *p;
1331     size_t  len;
1332
1333     len = uri->len;
1334     p = uri->data;
1335
1336     if (len == 0 || p[0] == '?') {
1337         goto unsafe;
1338     }
1339
1340     if (p[0] == '.' && len == 3 && p[1] == '.' && (p[2] == '/'
1341 #if (NGX_WIN32)
1342                                                    || p[2] == '\\'
1343 #endif
1344         ))
1345     {
1346         goto unsafe;
1347     }
1348
1349     for ( /* void */ ; len; len--) {
1350
1351         ch = *p++;
1352
1353         if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1354             continue;
1355         }
1356
1357         if (ch == '?') {
1358             args->len = len - 1;
1359             args->data = p;
1360             uri->len -= len;
1361
1362             return NGX_OK;
1363         }
1364
1365         if (ch == '\0') {
1366             *flags |= NGX_HTTP_ZERO_IN_URI;
1367             continue;
1368         }
1369
1370         if ((ch == '/'
1371 #if (NGX_WIN32)
1372              || ch == '\\'
1373 #endif
1374             ) && len > 2)
1375         {
1376             /* detect "/../" */
1377
1378             if (p[0] == '.' && p[1] == '.' && p[2] == '/') {
1379                 goto unsafe;
1380             }
1381
1382 #if (NGX_WIN32)
1383
1384             if (p[2] == '\\') {
1385                 goto unsafe;
1386             }
1387
1388             if (len > 3) {
1389
1390                 /* detect "/.../" */
1391
1392                 if (p[0] == '.' && p[1] == '.' && p[2] == '.'
1393                     && (p[3] == '/' || p[3] == '\\'))
1394                 {
1395                     goto unsafe;
1396                 }
1397             }
1398 #endif
1399         }
1400     }
1401
1402     return NGX_OK;
1403
1404 unsafe:
1405
1406     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1407                   "unsafe URI \"%V\" was detected", uri);
1408
1409     return NGX_ERROR;
1410 }
1411
1412
1413 ngx_int_t
1414 ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,
1415     ngx_str_t *value)
1416 {
1417     ngx_uint_t         i;
1418     u_char            *start, *last, *end, ch;
1419     ngx_table_elt_t  **h;
1420
1421     h = headers->elts;
1422
1423     for (i = 0; i < headers->nelts; i++) {
1424
1425         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
1426                        "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
1427
1428         if (name->len > h[i]->value.len) {
1429             continue;
1430         }
1431
1432         start = h[i]->value.data;
1433         end = h[i]->value.data + h[i]->value.len;
1434
1435         while (start < end) {
1436
1437             if (ngx_strncasecmp(start, name->data, name->len) != 0) {
1438                 goto skip;
1439             }
1440
1441             for (start += name->len; start < end && *start == ' '; start++) {
1442                 /* void */
1443             }
1444
1445             if (value == NULL) {
1446                 if (start == end || *start == ',') {
1447                     return i;
1448                 }
1449
1450                 goto skip;
1451             }
1452
1453             if (start == end || *start++ != '=') {
1454                 /* the invalid header value */
1455                 goto skip;
1456             }
1457
1458             while (start < end && *start == ' ') { start++; }
1459
1460             for (last = start; last < end && *last != ';'; last++) {
1461                 /* void */
1462             }
1463
1464             value->len = last - start;
1465             value->data = start;
1466
1467             return i;
1468
1469         skip:
1470
1471             while (start < end) {
1472                 ch = *start++;
1473                 if (ch == ';' || ch == ',') {
1474                     break;
1475                 }
1476             }
1477
1478             while (start < end && *start == ' ') { start++; }
1479         }
1480     }
1481
1482     return NGX_DECLINED;
1483 }
1484
1485
1486 ngx_int_t
1487 ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
1488 {
1489     u_char  *p;
1490
1491     if (r->args.len == 0) {
1492         return NGX_DECLINED;
1493     }
1494
1495     for (p = r->args.data; *p && *p != ' '; p++) {
1496
1497         /*
1498          * although r->args.data is not null-terminated by itself,
1499          * however, there is null in the end of request line
1500          */
1501
1502         p = ngx_strcasestrn(p, (char *) name, len - 1);
1503
1504         if (p == NULL) {
1505             return NGX_DECLINED;
1506         }
1507
1508         if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
1509
1510             value->data = p + len + 1;
1511
1512             p = (u_char *) ngx_strchr(p, '&');
1513
1514             if (p == NULL) {
1515                 p = r->args.data + r->args.len;
1516             }
1517
1518             value->len = p - value->data;
1519
1520             return NGX_OK;
1521         }
1522     }
1523
1524     return NGX_DECLINED;
1525 }