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