~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/http/ngx_http_parse.c

Version: ~ [ nginx-0.6.26 ] ~ [ nginx-0.5.35 ] ~ [ nginx-0.5.20 ] ~ [ nginx-0.5.19 ] ~

  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 >= '' && 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 >= '' && 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 - '';
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 < '' || ch > '9') {
613                 return NGX_HTTP_PARSE_INVALID_REQUEST;
614             }
615 
616             r->http_major = r->http_major * 10 + ch - '';
617             break;
618 
619         /* first digit of minor HTTP version */
620         case sw_first_minor_digit:
621             if (ch < '' || ch > '9') {
622                 return NGX_HTTP_PARSE_INVALID_REQUEST;
623             }
624 
625             r->http_minor = ch - '';
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 < '' || ch > '9') {
646                 return NGX_HTTP_PARSE_INVALID_REQUEST;
647             }
648 
649             r->http_minor = r->http_minor * 10 + ch - '';
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 {
705     u_char      c, ch, *p;
706     ngx_uint_t  hash, i;
707     enum {
708         sw_start = 0,
709         sw_name,
710         sw_space_before_value,
711         sw_value,
712         sw_space_after_value,
713         sw_ignore_line,
714         sw_almost_done,
715         sw_header_almost_done
716     } state;
717 
718     /* the last '\0' is not needed because string is zero terminated */
719 
720     static u_char  lowcase[] =
721         "\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"
722         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
723         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
724         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
725         "\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"
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";
729 
730     state = r->state;
731     hash = r->header_hash;
732     i = r->lowcase_index;
733 
734     for (p = b->pos; p < b->last; p++) {
735         ch = *p;
736 
737         switch (state) {
738 
739         /* first char */
740         case sw_start:
741             r->invalid_header = 0;
742 
743             switch (ch) {
744             case CR:
745                 r->header_end = p;
746                 state = sw_header_almost_done;
747                 break;
748             case LF:
749                 r->header_end = p;
750                 goto header_done;
751             default:
752                 state = sw_name;
753                 r->header_name_start = p;
754 
755                 c = lowcase[ch];
756 
757                 if (c) {
758                     hash = ngx_hash(0, c);
759                     r->lowcase_header[0] = c;
760                     i = 1;
761                     break;
762                 }
763 
764                 r->invalid_header = 1;
765 
766                 break;
767 
768             }
769             break;
770 
771         /* header name */
772         case sw_name:
773             c = lowcase[ch];
774 
775             if (c) {
776                 hash = ngx_hash(hash, c);
777                 r->lowcase_header[i++] = c;
778                 i &= ~NGX_HTTP_LC_HEADER_LEN;
779                 break;
780             }
781 
782             if (ch == ':') {
783                 r->header_name_end = p;
784                 state = sw_space_before_value;
785                 break;
786             }
787 
788             if (ch == CR) {
789                 r->header_name_end = p;
790                 r->header_start = p;
791                 r->header_end = p;
792                 state = sw_almost_done;
793                 break;
794             }
795 
796             if (ch == LF) {
797                 r->header_name_end = p;
798                 r->header_start = p;
799                 r->header_end = p;
800                 goto done;
801             }
802 
803             /* IIS may send the duplicate "HTTP/1.1 ..." lines */
804             if (ch == '/'
805                 && r->upstream
806                 && p - r->header_name_start == 4
807                 && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
808             {
809                 state = sw_ignore_line;
810                 break;
811             }
812 
813             r->invalid_header = 1;
814 
815             break;
816 
817         /* space* before header value */
818         case sw_space_before_value:
819             switch (ch) {
820             case ' ':
821                 break;
822             case CR:
823                 r->header_start = p;
824                 r->header_end = p;
825                 state = sw_almost_done;
826                 break;
827             case LF:
828                 r->header_start = p;
829                 r->header_end = p;
830                 goto done;
831             default:
832                 r->header_start = p;
833                 state = sw_value;
834                 break;
835             }
836             break;
837 
838         /* header value */
839         case sw_value:
840             switch (ch) {
841             case ' ':
842                 r->header_end = p;
843                 state = sw_space_after_value;
844                 break;
845             case CR:
846                 r->header_end = p;
847                 state = sw_almost_done;
848                 break;
849             case LF:
850                 r->header_end = p;
851                 goto done;
852             }
853             break;
854 
855         /* space* before end of header line */
856         case sw_space_after_value:
857             switch (ch) {
858             case ' ':
859                 break;
860             case CR:
861                 state = sw_almost_done;
862                 break;
863             case LF:
864                 goto done;
865             default:
866                 state = sw_value;
867                 break;
868             }
869             break;
870 
871         /* ignore header line */
872         case sw_ignore_line:
873             switch (ch) {
874             case LF:
875                 state = sw_start;
876                 break;
877             default:
878                 break;
879             }
880             break;
881 
882         /* end of header line */
883         case sw_almost_done:
884             switch (ch) {
885             case CR:
886                 break;
887             case LF:
888                 goto done;
889             default:
890                 return NGX_HTTP_PARSE_INVALID_HEADER;
891             }
892             break;
893 
894         /* end of header */
895         case sw_header_almost_done:
896             switch (ch) {
897             case LF:
898                 goto header_done;
899             default:
900                 return NGX_HTTP_PARSE_INVALID_HEADER;
901             }
902         }
903     }
904 
905     b->pos = p;
906     r->state = state;
907     r->header_hash = hash;
908     r->lowcase_index = i;
909 
910     return NGX_AGAIN;
911 
912 done:
913 
914     b->pos = p + 1;
915     r->state = sw_start;
916     r->header_hash = hash;
917     r->lowcase_index = i;
918 
919     return NGX_OK;
920 
921 header_done:
922 
923     b->pos = p + 1;
924     r->state = sw_start;
925 
926     return NGX_HTTP_PARSE_HEADER_DONE;
927 }
928 
929 
930 ngx_int_t
931 ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
932 {
933     u_char  c, ch, decoded, *p, *u;
934     enum {
935         sw_usual = 0,
936         sw_slash,
937         sw_dot,
938         sw_dot_dot,
939 #if (NGX_WIN32)
940         sw_dot_dot_dot,
941 #endif
942         sw_quoted,
943         sw_quoted_second
944     } state, quoted_state;
945 
946 #if (NGX_SUPPRESS_WARN)
947     decoded = '\0';
948     quoted_state = sw_usual;
949 #endif
950 
951     state = sw_usual;
952     p = r->uri_start;
953     u = r->uri.data;
954     r->uri_ext = NULL;
955     r->args_start = NULL;
956 
957     ch = *p++;
958 
959     while (p <= r->uri_end) {
960 
961         /*
962          * we use "ch = *p++" inside the cycle, but this operation is safe,
963          * because after the URI there is always at least one charcter:
964          * the line feed
965          */
966 
967         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
968                        "s:%d in:'%Xd:%c', out:'%c'", state, ch, ch, *u);
969 
970         switch (state) {
971 
972         case sw_usual:
973 
974             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
975                 *u++ = ch;
976                 ch = *p++;
977                 break;
978             }
979 
980             switch(ch) {
981 #if (NGX_WIN32)
982             case '\\':
983                 r->uri_ext = NULL;
984 
985                 if (p == r->uri_start + r->uri.len) {
986 
987                     /*
988                      * we omit the last "\" to cause redirect because
989                      * the browsers do not treat "\" as "/" in relative URL path
990                      */
991 
992                     break;
993                 }
994 
995                 state = sw_slash;
996                 *u++ = '/';
997                 break;
998 #endif
999             case '/':
1000                 r->uri_ext = NULL;
1001                 state = sw_slash;
1002                 *u++ = ch;
1003                 break;
1004             case '%':
1005                 quoted_state = state;
1006                 state = sw_quoted;
1007                 break;
1008             case '?':
1009                 r->args_start = p;
1010                 goto args;
1011             case '#':
1012                 goto done;
1013             case '.':
1014                 r->uri_ext = u + 1;
1015                 *u++ = ch;
1016                 break;
1017             case '+':
1018                 r->plus_in_uri = 1;
1019             default:
1020                 *u++ = ch;
1021                 break;
1022             }
1023 
1024             ch = *p++;
1025             break;
1026 
1027         case sw_slash:
1028 
1029             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1030                 state = sw_usual;
1031                 *u++ = ch;
1032                 ch = *p++;
1033                 break;
1034             }
1035 
1036             switch(ch) {
1037 #if (NGX_WIN32)
1038             case '\\':
1039                 break;
1040 #endif
1041             case '/':
1042                 if (!merge_slashes) {
1043                     *u++ = ch;
1044                 }
1045                 break;
1046             case '.':
1047                 state = sw_dot;
1048                 *u++ = ch;
1049                 break;
1050             case '%':
1051                 quoted_state = state;
1052                 state = sw_quoted;
1053                 break;
1054             case '?':
1055                 r->args_start = p;
1056                 goto args;
1057             case '#':
1058                 goto done;
1059             case '+':
1060                 r->plus_in_uri = 1;
1061             default:
1062                 state = sw_usual;
1063                 *u++ = ch;
1064                 break;
1065             }
1066 
1067             ch = *p++;
1068             break;
1069 
1070         case sw_dot:
1071 
1072             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1073                 state = sw_usual;
1074                 *u++ = ch;
1075                 ch = *p++;
1076                 break;
1077             }
1078 
1079             switch(ch) {
1080 #if (NGX_WIN32)
1081             case '\\':
1082 #endif
1083             case '/':
1084                 state = sw_slash;
1085                 u--;
1086                 break;
1087             case '.':
1088                 state = sw_dot_dot;
1089                 *u++ = ch;
1090                 break;
1091             case '%':
1092                 quoted_state = state;
1093                 state = sw_quoted;
1094                 break;
1095             case '?':
1096                 r->args_start = p;
1097                 goto args;
1098             case '#':
1099                 goto done;
1100             case '+':
1101                 r->plus_in_uri = 1;
1102             default:
1103                 state = sw_usual;
1104                 *u++ = ch;
1105                 break;
1106             }
1107 
1108             ch = *p++;
1109             break;
1110 
1111         case sw_dot_dot:
1112 
1113             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1114                 state = sw_usual;
1115                 *u++ = ch;
1116                 ch = *p++;
1117                 break;
1118             }
1119 
1120             switch(ch) {
1121 #if (NGX_WIN32)
1122             case '\\':
1123 #endif
1124             case '/':
1125                 state = sw_slash;
1126                 u -= 4;
1127                 if (u < r->uri.data) {
1128                     return NGX_HTTP_PARSE_INVALID_REQUEST;
1129                 }
1130                 while (*(u - 1) != '/') {
1131                     u--;
1132                 }
1133                 break;
1134             case '%':
1135                 quoted_state = state;
1136                 state = sw_quoted;
1137                 break;
1138             case '?':
1139                 r->args_start = p;
1140                 goto args;
1141             case '#':
1142                 goto done;
1143 #if (NGX_WIN32)
1144             case '.':
1145                 state = sw_dot_dot_dot;
1146                 *u++ = ch;
1147                 break;
1148 #endif
1149             case '+':
1150                 r->plus_in_uri = 1;
1151             default:
1152                 state = sw_usual;
1153                 *u++ = ch;
1154                 break;
1155             }
1156 
1157             ch = *p++;
1158             break;
1159 
1160 #if (NGX_WIN32)
1161         case sw_dot_dot_dot:
1162 
1163             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1164                 state = sw_usual;
1165                 *u++ = ch;
1166                 ch = *p++;
1167                 break;
1168             }
1169 
1170             switch(ch) {
1171             case '\\':
1172             case '/':
1173                 state = sw_slash;
1174                 u -= 5;
1175                 if (u < r->uri.data) {
1176                     return NGX_HTTP_PARSE_INVALID_REQUEST;
1177                 }
1178                 while (*u != '/') {
1179                     u--;
1180                 }
1181                 if (u < r->uri.data) {
1182                     return NGX_HTTP_PARSE_INVALID_REQUEST;
1183                 }
1184                 while (*(u - 1) != '/') {
1185                     u--;
1186                 }
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 #endif
1208 
1209         case sw_quoted:
1210             r->quoted_uri = 1;
1211 
1212             if (ch >= '' && ch <= '9') {
1213                 decoded = (u_char) (ch - '');
1214                 state = sw_quoted_second;
1215                 ch = *p++;
1216                 break;
1217             }
1218 
1219             c = (u_char) (ch | 0x20);
1220             if (c >= 'a' && c <= 'f') {
1221                 decoded = (u_char) (c - 'a' + 10);
1222                 state = sw_quoted_second;
1223                 ch = *p++;
1224                 break;
1225             }
1226 
1227             return NGX_HTTP_PARSE_INVALID_REQUEST;
1228 
1229         case sw_quoted_second:
1230             if (ch >= '' && ch <= '9') {
1231                 ch = (u_char) ((decoded << 4) + ch - '');
1232 
1233                 if (ch == '%') {
1234                     state = sw_usual;
1235                     *u++ = ch;
1236                     ch = *p++;
1237                     break;
1238                 }
1239 
1240                 if (ch == '#') {
1241                     *u++ = ch;
1242</