comparison src/http/ngx_http_parse.c @ 0:f0b350454894 NGINX_0_1_0

nginx 0.1.0 *) The first public version.
author Igor Sysoev <http://sysoev.ru>
date Mon, 04 Oct 2004 00:00:00 +0400
parents
children cc9f381affaa
comparison
equal deleted inserted replaced
-1:000000000000 0:f0b350454894
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 ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
13 {
14 u_char ch, *p, *m;
15 enum {
16 sw_start = 0,
17 sw_method,
18 sw_space_after_method,
19 sw_spaces_before_uri,
20 sw_schema,
21 sw_schema_slash,
22 sw_schema_slash_slash,
23 sw_host,
24 sw_port,
25 sw_after_slash_in_uri,
26 sw_check_uri,
27 sw_uri,
28 sw_http_09,
29 sw_http_H,
30 sw_http_HT,
31 sw_http_HTT,
32 sw_http_HTTP,
33 sw_first_major_digit,
34 sw_major_digit,
35 sw_first_minor_digit,
36 sw_minor_digit,
37 sw_almost_done,
38 sw_done
39 } state;
40
41 state = r->state;
42 p = b->pos;
43
44 while (p < b->last && state < sw_done) {
45 ch = *p++;
46
47 /* gcc 2.95.2 and msvc 6.0 compile this switch as an jump table */
48
49 switch (state) {
50
51 /* HTTP methods: GET, HEAD, POST */
52 case sw_start:
53 r->request_start = p - 1;
54
55 if (ch == CR || ch == LF) {
56 break;
57 }
58
59 if (ch < 'A' || ch > 'Z') {
60 return NGX_HTTP_PARSE_INVALID_METHOD;
61 }
62
63 state = sw_method;
64 break;
65
66 case sw_method:
67 if (ch == ' ') {
68 r->method_end = p - 1;
69 m = r->request_start;
70
71 if (r->method_end - m == 3) {
72
73 if (m[0] == 'G' && m[1] == 'E' && m[2] == 'T') {
74 r->method = NGX_HTTP_GET;
75 }
76
77 } else if (r->method_end - m == 4) {
78
79 if (m[0] == 'P' && m[1] == 'O'
80 && m[2] == 'T' && m[3] == 'T')
81 {
82 r->method = NGX_HTTP_POST;
83
84 } else if (m[0] == 'H' && m[1] == 'E'
85 && m[2] == 'A' && m[3] == 'D')
86 {
87 r->method = NGX_HTTP_HEAD;
88 }
89 }
90
91 state = sw_spaces_before_uri;
92 break;
93 }
94
95 if (ch < 'A' || ch > 'Z') {
96 return NGX_HTTP_PARSE_INVALID_METHOD;
97 }
98
99 break;
100
101 /* single space after method */
102 case sw_space_after_method:
103 switch (ch) {
104 case ' ':
105 state = sw_spaces_before_uri;
106 break;
107 default:
108 return NGX_HTTP_PARSE_INVALID_METHOD;
109 }
110 break;
111
112 /* space* before URI */
113 case sw_spaces_before_uri:
114 switch (ch) {
115 case '/':
116 r->uri_start = p - 1;
117 state = sw_after_slash_in_uri;
118 break;
119 case ' ':
120 break;
121 default:
122 if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
123 r->schema_start = p - 1;
124 state = sw_schema;
125 break;
126 }
127 return NGX_HTTP_PARSE_INVALID_REQUEST;
128 }
129 break;
130
131 case sw_schema:
132 switch (ch) {
133 case ':':
134 r->schema_end = p - 1;
135 state = sw_schema_slash;
136 break;
137 default:
138 if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
139 break;
140 }
141 return NGX_HTTP_PARSE_INVALID_REQUEST;
142 }
143 break;
144
145 case sw_schema_slash:
146 switch (ch) {
147 case '/':
148 state = sw_schema_slash_slash;
149 break;
150 default:
151 return NGX_HTTP_PARSE_INVALID_REQUEST;
152 }
153 break;
154
155 case sw_schema_slash_slash:
156 switch (ch) {
157 case '/':
158 r->host_start = p - 1;
159 state = sw_host;
160 break;
161 default:
162 return NGX_HTTP_PARSE_INVALID_REQUEST;
163 }
164 break;
165
166 case sw_host:
167 switch (ch) {
168 case ':':
169 r->host_end = p - 1;
170 state = sw_port;
171 break;
172 case '/':
173 r->host_end = p - 1;
174 r->uri_start = p - 1;
175 state = sw_after_slash_in_uri;
176 break;
177 default:
178 if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')
179 || (ch >= '0' && ch <= '9') || ch == '.' || ch == '-')
180 {
181 break;
182 }
183 return NGX_HTTP_PARSE_INVALID_REQUEST;
184 }
185 break;
186
187 case sw_port:
188 switch (ch) {
189 case '/':
190 r->port_end = p - 1;
191 r->uri_start = p - 1;
192 state = sw_after_slash_in_uri;
193 break;
194 default:
195 if (ch < '0' && ch > '9') {
196 return NGX_HTTP_PARSE_INVALID_REQUEST;
197 }
198 break;
199 }
200 break;
201
202 /* check "/.", "//", and "%" in URI */
203 case sw_after_slash_in_uri:
204 switch (ch) {
205 case CR:
206 r->uri_end = p - 1;
207 r->http_minor = 9;
208 state = sw_almost_done;
209 break;
210 case LF:
211 r->uri_end = p - 1;
212 r->http_minor = 9;
213 state = sw_done;
214 break;
215 case ' ':
216 r->uri_end = p - 1;
217 state = sw_http_09;
218 break;
219 case '.':
220 case '%':
221 r->complex_uri = 1;
222 state = sw_uri;
223 break;
224 case '/':
225 r->complex_uri = 1;
226 break;
227 case '?':
228 r->args_start = p;
229 state = sw_uri;
230 break;
231 default:
232 state = sw_check_uri;
233 break;
234 }
235 break;
236
237 /* check "/" and "%" in URI */
238 case sw_check_uri:
239 switch (ch) {
240 case CR:
241 r->uri_end = p - 1;
242 r->http_minor = 9;
243 state = sw_almost_done;
244 break;
245 case LF:
246 r->uri_end = p - 1;
247 r->http_minor = 9;
248 state = sw_done;
249 break;
250 case ' ':
251 r->uri_end = p - 1;
252 state = sw_http_09;
253 break;
254 case '.':
255 r->uri_ext = p;
256 break;
257 case '/':
258 r->uri_ext = NULL;
259 state = sw_after_slash_in_uri;
260 break;
261 case '%':
262 r->complex_uri = 1;
263 state = sw_uri;
264 break;
265 case '?':
266 r->args_start = p;
267 state = sw_uri;
268 break;
269 }
270 break;
271
272 /* URI */
273 case sw_uri:
274 switch (ch) {
275 case CR:
276 r->uri_end = p - 1;
277 r->http_minor = 9;
278 state = sw_almost_done;
279 break;
280 case LF:
281 r->uri_end = p - 1;
282 r->http_minor = 9;
283 state = sw_done;
284 break;
285 case ' ':
286 r->uri_end = p - 1;
287 state = sw_http_09;
288 break;
289 }
290 break;
291
292 /* space+ after URI */
293 case sw_http_09:
294 switch (ch) {
295 case ' ':
296 break;
297 case CR:
298 r->http_minor = 9;
299 state = sw_almost_done;
300 break;
301 case LF:
302 r->http_minor = 9;
303 state = sw_done;
304 break;
305 case 'H':
306 state = sw_http_H;
307 break;
308 default:
309 return NGX_HTTP_PARSE_INVALID_REQUEST;
310 }
311 break;
312
313 case sw_http_H:
314 switch (ch) {
315 case 'T':
316 state = sw_http_HT;
317 break;
318 default:
319 return NGX_HTTP_PARSE_INVALID_REQUEST;
320 }
321 break;
322
323 case sw_http_HT:
324 switch (ch) {
325 case 'T':
326 state = sw_http_HTT;
327 break;
328 default:
329 return NGX_HTTP_PARSE_INVALID_REQUEST;
330 }
331 break;
332
333 case sw_http_HTT:
334 switch (ch) {
335 case 'P':
336 state = sw_http_HTTP;
337 break;
338 default:
339 return NGX_HTTP_PARSE_INVALID_REQUEST;
340 }
341 break;
342
343 case sw_http_HTTP:
344 switch (ch) {
345 case '/':
346 state = sw_first_major_digit;
347 break;
348 default:
349 return NGX_HTTP_PARSE_INVALID_REQUEST;
350 }
351 break;
352
353 /* first digit of major HTTP version */
354 case sw_first_major_digit:
355 if (ch < '1' || ch > '9') {
356 return NGX_HTTP_PARSE_INVALID_REQUEST;
357 }
358
359 r->http_major = ch - '0';
360 state = sw_major_digit;
361 break;
362
363 /* major HTTP version or dot */
364 case sw_major_digit:
365 if (ch == '.') {
366 state = sw_first_minor_digit;
367 break;
368 }
369
370 if (ch < '0' || ch > '9') {
371 return NGX_HTTP_PARSE_INVALID_REQUEST;
372 }
373
374 r->http_major = r->http_major * 10 + ch - '0';
375 break;
376
377 /* first digit of minor HTTP version */
378 case sw_first_minor_digit:
379 if (ch < '0' || ch > '9') {
380 return NGX_HTTP_PARSE_INVALID_REQUEST;
381 }
382
383 r->http_minor = ch - '0';
384 state = sw_minor_digit;
385 break;
386
387 /* minor HTTP version or end of request line */
388 case sw_minor_digit:
389 if (ch == CR) {
390 state = sw_almost_done;
391 break;
392 }
393
394 if (ch == LF) {
395 state = sw_done;
396 break;
397 }
398
399 if (ch < '0' || ch > '9') {
400 return NGX_HTTP_PARSE_INVALID_REQUEST;
401 }
402
403 r->http_minor = r->http_minor * 10 + ch - '0';
404 break;
405
406 /* end of request line */
407 case sw_almost_done:
408 r->request_end = p - 2;
409 switch (ch) {
410 case LF:
411 state = sw_done;
412 break;
413 default:
414 return NGX_HTTP_PARSE_INVALID_REQUEST;
415 }
416 break;
417
418 /* suppress warning */
419 case sw_done:
420 break;
421 }
422 }
423
424 b->pos = p;
425
426 if (state == sw_done) {
427 if (r->request_end == NULL) {
428 r->request_end = p - 1;
429 }
430
431 r->http_version = r->http_major * 1000 + r->http_minor;
432 r->state = sw_start;
433
434 if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
435 return NGX_HTTP_PARSE_INVALID_09_METHOD;
436 }
437
438 return NGX_OK;
439
440 } else {
441 r->state = state;
442 return NGX_AGAIN;
443 }
444 }
445
446
447 ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b)
448 {
449 u_char c, ch, *p;
450 enum {
451 sw_start = 0,
452 sw_name,
453 sw_space_before_value,
454 sw_value,
455 sw_space_after_value,
456 sw_almost_done,
457 sw_header_almost_done,
458 sw_ignore_line,
459 sw_done,
460 sw_header_done
461 } state;
462
463 state = r->state;
464 p = b->pos;
465
466 while (p < b->last && state < sw_done) {
467 ch = *p++;
468
469 switch (state) {
470
471 /* first char */
472 case sw_start:
473 switch (ch) {
474 case CR:
475 r->header_end = p - 1;
476 state = sw_header_almost_done;
477 break;
478 case LF:
479 r->header_end = p - 1;
480 state = sw_header_done;
481 break;
482 default:
483 state = sw_name;
484 r->header_name_start = p - 1;
485
486 c = (u_char) (ch | 0x20);
487 if (c >= 'a' && c <= 'z') {
488 break;
489 }
490
491 if (ch == '-' || ch == '_' || ch == '~' || ch == '.') {
492 break;
493 }
494
495 if (ch >= '0' && ch <= '9') {
496 break;
497 }
498
499 return NGX_HTTP_PARSE_INVALID_HEADER;
500
501 }
502 break;
503
504 /* header name */
505 case sw_name:
506 c = (u_char) (ch | 0x20);
507 if (c >= 'a' && c <= 'z') {
508 break;
509 }
510
511 if (ch == ':') {
512 r->header_name_end = p - 1;
513 state = sw_space_before_value;
514 break;
515 }
516
517 if (ch == '-' || ch == '_' || ch == '~' || ch == '.') {
518 break;
519 }
520
521 if (ch >= '0' && ch <= '9') {
522 break;
523 }
524
525 /* IIS can send duplicate "HTTP/1.1 ..." lines */
526 if (ch == '/'
527 && r->proxy
528 && p - r->header_start == 5
529 && ngx_strncmp(r->header_start, "HTTP", 4) == 0)
530 {
531 state = sw_ignore_line;
532 break;
533 }
534
535 return NGX_HTTP_PARSE_INVALID_HEADER;
536
537 /* space* before header value */
538 case sw_space_before_value:
539 switch (ch) {
540 case ' ':
541 break;
542 case CR:
543 r->header_start = r->header_end = p - 1;
544 state = sw_almost_done;
545 break;
546 case LF:
547 r->header_start = r->header_end = p - 1;
548 state = sw_done;
549 break;
550 default:
551 r->header_start = p - 1;
552 state = sw_value;
553 break;
554 }
555 break;
556
557 /* header value */
558 case sw_value:
559 switch (ch) {
560 case ' ':
561 r->header_end = p - 1;
562 state = sw_space_after_value;
563 break;
564 case CR:
565 r->header_end = p - 1;
566 state = sw_almost_done;
567 break;
568 case LF:
569 r->header_end = p - 1;
570 state = sw_done;
571 break;
572 }
573 break;
574
575 /* space* before end of header line */
576 case sw_space_after_value:
577 switch (ch) {
578 case ' ':
579 break;
580 case CR:
581 state = sw_almost_done;
582 break;
583 case LF:
584 state = sw_done;
585 break;
586 default:
587 state = sw_value;
588 break;
589 }
590 break;
591
592 /* ignore header line */
593 case sw_ignore_line:
594 switch (ch) {
595 case LF:
596 state = sw_start;
597 break;
598 default:
599 break;
600 }
601 break;
602
603 /* end of header line */
604 case sw_almost_done:
605 switch (ch) {
606 case LF:
607 state = sw_done;
608 break;
609 default:
610 return NGX_HTTP_PARSE_INVALID_HEADER;
611 }
612 break;
613
614 /* end of header */
615 case sw_header_almost_done:
616 switch (ch) {
617 case LF:
618 state = sw_header_done;
619 break;
620 default:
621 return NGX_HTTP_PARSE_INVALID_HEADER;
622 }
623 break;
624
625 /* suppress warning */
626 case sw_done:
627 case sw_header_done:
628 break;
629 }
630 }
631
632 b->pos = p;
633
634 if (state == sw_done) {
635 r->state = sw_start;
636 return NGX_OK;
637
638 } else if (state == sw_header_done) {
639 r->state = sw_start;
640 return NGX_HTTP_PARSE_HEADER_DONE;
641
642 } else {
643 r->state = state;
644 return NGX_AGAIN;
645 }
646 }
647
648
649 ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r)
650 {
651 u_char c, ch, decoded, *p, *u;
652 enum {
653 sw_usual = 0,
654 sw_slash,
655 sw_dot,
656 sw_dot_dot,
657 #if (WIN32)
658 sw_dot_dot_dot,
659 #endif
660 sw_quoted,
661 sw_quoted_second
662 } state, quoted_state;
663
664 decoded = '\0';
665 quoted_state = sw_usual;
666
667 state = sw_usual;
668 p = r->uri_start;
669 u = r->uri.data;
670 r->uri_ext = NULL;
671
672 ch = *p++;
673
674 while (p < r->uri_start + r->uri.len + 1) {
675
676 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
677 "s:%d in:'%x:%c', out:'%c'", state, ch, ch, *u);
678
679 switch (state) {
680 case sw_usual:
681 switch(ch) {
682 case '/':
683 r->uri_ext = NULL;
684 state = sw_slash;
685 *u++ = ch;
686 break;
687 case '%':
688 quoted_state = state;
689 state = sw_quoted;
690 break;
691 case '.':
692 r->uri_ext = u + 1;
693 default:
694 *u++ = ch;
695 break;
696 }
697 ch = *p++;
698 break;
699
700 case sw_slash:
701 switch(ch) {
702 case '/':
703 break;
704 case '.':
705 state = sw_dot;
706 *u++ = ch;
707 break;
708 case '%':
709 quoted_state = state;
710 state = sw_quoted;
711 break;
712 default:
713 state = sw_usual;
714 *u++ = ch;
715 break;
716 }
717 ch = *p++;
718 break;
719
720 case sw_dot:
721 switch(ch) {
722 case '/':
723 state = sw_slash;
724 u--;
725 break;
726 case '.':
727 state = sw_dot_dot;
728 *u++ = ch;
729 break;
730 case '%':
731 quoted_state = state;
732 state = sw_quoted;
733 break;
734 default:
735 state = sw_usual;
736 *u++ = ch;
737 break;
738 }
739 ch = *p++;
740 break;
741
742 case sw_dot_dot:
743 switch(ch) {
744 case '/':
745 state = sw_slash;
746 u -= 4;
747 if (u < r->uri.data) {
748 return NGX_HTTP_PARSE_INVALID_REQUEST;
749 }
750 while (*(u - 1) != '/') {
751 u--;
752 }
753 break;
754 case '%':
755 quoted_state = state;
756 state = sw_quoted;
757 break;
758 #if (WIN32)
759 case '.':
760 state = sw_dot_dot_dot;
761 *u++ = ch;
762 break;
763 #endif
764 default:
765 state = sw_usual;
766 *u++ = ch;
767 break;
768 }
769 ch = *p++;
770 break;
771
772 #if (WIN32)
773 case sw_dot_dot_dot:
774 switch(ch) {
775 case '/':
776 state = sw_slash;
777 u -= 5;
778 if (u < r->uri.data) {
779 return NGX_HTTP_PARSE_INVALID_REQUEST;
780 }
781 while (*u != '/') {
782 u--;
783 }
784 if (u < r->uri.data) {
785 return NGX_HTTP_PARSE_INVALID_REQUEST;
786 }
787 while (*(u - 1) != '/') {
788 u--;
789 }
790 break;
791 case '%':
792 quoted_state = state;
793 state = sw_quoted;
794 break;
795 default:
796 state = sw_usual;
797 *u++ = ch;
798 break;
799 }
800 ch = *p++;
801 break;
802 #endif
803
804 case sw_quoted:
805 if (ch >= '0' && ch <= '9') {
806 decoded = (u_char) (ch - '0');
807 state = sw_quoted_second;
808 ch = *p++;
809 break;
810 }
811
812 c = (u_char) (ch | 0x20);
813 if (c >= 'a' && c <= 'f') {
814 decoded = (u_char) (c - 'a' + 10);
815 state = sw_quoted_second;
816 ch = *p++;
817 break;
818 }
819
820 return NGX_HTTP_PARSE_INVALID_REQUEST;
821
822 case sw_quoted_second:
823 if (ch >= '0' && ch <= '9') {
824 ch = (u_char) ((decoded << 4) + ch - '0');
825 if (ch == '%') {
826 state = sw_usual;
827 *u++ = ch;
828 ch = *p++;
829 break;
830 }
831 state = quoted_state;
832 break;
833 }
834
835 c = (u_char) (ch | 0x20);
836 if (c >= 'a' && c <= 'f') {
837 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
838 if (ch == '%') {
839 state = sw_usual;
840 *u++ = ch;
841 ch = *p++;
842 break;
843 }
844 state = quoted_state;
845 break;
846 }
847
848 return NGX_HTTP_PARSE_INVALID_REQUEST;
849 }
850 }
851
852 r->uri.len = u - r->uri.data;
853 r->uri.data[r->uri.len] = '\0';
854
855 if (r->uri_ext) {
856 r->exten.len = u - r->uri_ext;
857
858 if (!(r->exten.data = ngx_palloc(r->pool, r->exten.len + 1))) {
859 return NGX_HTTP_INTERNAL_SERVER_ERROR;
860 }
861
862 ngx_cpystrn(r->exten.data, r->uri_ext, r->exten.len + 1);
863 }
864
865 r->uri_ext = NULL;
866
867 return NGX_OK;
868 }