Update and clean Tomato RAF files
[tomato.git] / release / src / router / nginx / src / http / ngx_http_parse.c
blob34b3b85d060d27837febd0cd0e65dcdc35b17a69
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
13 static uint32_t usual[] = {
14 0xffffdbfe, /* 1111 1111 1111 1111 1101 1011 1111 1110 */
16 /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
17 0x7fff37d6, /* 0111 1111 1111 1111 0011 0111 1101 0110 */
19 /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
20 #if (NGX_WIN32)
21 0xefffffff, /* 1110 1111 1111 1111 1111 1111 1111 1111 */
22 #else
23 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
24 #endif
26 /* ~}| {zyx wvut srqp onml kjih gfed cba` */
27 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 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
36 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
38 #define ngx_str3_cmp(m, c0, c1, c2, c3) \
39 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
41 #define ngx_str3Ocmp(m, c0, c1, c2, c3) \
42 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
44 #define ngx_str4cmp(m, c0, c1, c2, c3) \
45 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
47 #define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
48 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
49 && m[4] == c4
51 #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5) \
52 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
53 && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)
55 #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
56 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
57 && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
59 #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
60 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
61 && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
63 #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \
64 *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
65 && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) \
66 && m[8] == c8
68 #else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */
70 #define ngx_str3_cmp(m, c0, c1, c2, c3) \
71 m[0] == c0 && m[1] == c1 && m[2] == c2
73 #define ngx_str3Ocmp(m, c0, c1, c2, c3) \
74 m[0] == c0 && m[2] == c2 && m[3] == c3
76 #define ngx_str4cmp(m, c0, c1, c2, c3) \
77 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3
79 #define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
80 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
82 #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5) \
83 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
84 && m[4] == c4 && m[5] == c5
86 #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
87 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
88 && m[4] == c4 && m[5] == c5 && m[6] == c6
90 #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
91 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
92 && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7
94 #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \
95 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
96 && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8
98 #endif
101 /* gcc, icc, msvc and others compile these switches as an jump table */
103 ngx_int_t
104 ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
106 u_char c, ch, *p, *m;
107 enum {
108 sw_start = 0,
109 sw_method,
110 sw_spaces_before_uri,
111 sw_schema,
112 sw_schema_slash,
113 sw_schema_slash_slash,
114 sw_host_start,
115 sw_host,
116 sw_host_end,
117 sw_host_ip_literal,
118 sw_port,
119 sw_host_http_09,
120 sw_after_slash_in_uri,
121 sw_check_uri,
122 sw_check_uri_http_09,
123 sw_uri,
124 sw_http_09,
125 sw_http_H,
126 sw_http_HT,
127 sw_http_HTT,
128 sw_http_HTTP,
129 sw_first_major_digit,
130 sw_major_digit,
131 sw_first_minor_digit,
132 sw_minor_digit,
133 sw_spaces_after_digit,
134 sw_almost_done
135 } state;
137 state = r->state;
139 for (p = b->pos; p < b->last; p++) {
140 ch = *p;
142 switch (state) {
144 /* HTTP methods: GET, HEAD, POST */
145 case sw_start:
146 r->request_start = p;
148 if (ch == CR || ch == LF) {
149 break;
152 if ((ch < 'A' || ch > 'Z') && ch != '_') {
153 return NGX_HTTP_PARSE_INVALID_METHOD;
156 state = sw_method;
157 break;
159 case sw_method:
160 if (ch == ' ') {
161 r->method_end = p - 1;
162 m = r->request_start;
164 switch (p - m) {
166 case 3:
167 if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
168 r->method = NGX_HTTP_GET;
169 break;
172 if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
173 r->method = NGX_HTTP_PUT;
174 break;
177 break;
179 case 4:
180 if (m[1] == 'O') {
182 if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
183 r->method = NGX_HTTP_POST;
184 break;
187 if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
188 r->method = NGX_HTTP_COPY;
189 break;
192 if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
193 r->method = NGX_HTTP_MOVE;
194 break;
197 if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
198 r->method = NGX_HTTP_LOCK;
199 break;
202 } else {
204 if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
205 r->method = NGX_HTTP_HEAD;
206 break;
210 break;
212 case 5:
213 if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
214 r->method = NGX_HTTP_MKCOL;
217 if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
218 r->method = NGX_HTTP_PATCH;
221 if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
222 r->method = NGX_HTTP_TRACE;
225 break;
227 case 6:
228 if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
229 r->method = NGX_HTTP_DELETE;
230 break;
233 if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
234 r->method = NGX_HTTP_UNLOCK;
235 break;
238 break;
240 case 7:
241 if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
243 r->method = NGX_HTTP_OPTIONS;
246 break;
248 case 8:
249 if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
251 r->method = NGX_HTTP_PROPFIND;
254 break;
256 case 9:
257 if (ngx_str9cmp(m,
258 'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
260 r->method = NGX_HTTP_PROPPATCH;
263 break;
266 state = sw_spaces_before_uri;
267 break;
270 if ((ch < 'A' || ch > 'Z') && ch != '_') {
271 return NGX_HTTP_PARSE_INVALID_METHOD;
274 break;
276 /* space* before URI */
277 case sw_spaces_before_uri:
279 if (ch == '/') {
280 r->uri_start = p;
281 state = sw_after_slash_in_uri;
282 break;
285 c = (u_char) (ch | 0x20);
286 if (c >= 'a' && c <= 'z') {
287 r->schema_start = p;
288 state = sw_schema;
289 break;
292 switch (ch) {
293 case ' ':
294 break;
295 default:
296 return NGX_HTTP_PARSE_INVALID_REQUEST;
298 break;
300 case sw_schema:
302 c = (u_char) (ch | 0x20);
303 if (c >= 'a' && c <= 'z') {
304 break;
307 switch (ch) {
308 case ':':
309 r->schema_end = p;
310 state = sw_schema_slash;
311 break;
312 default:
313 return NGX_HTTP_PARSE_INVALID_REQUEST;
315 break;
317 case sw_schema_slash:
318 switch (ch) {
319 case '/':
320 state = sw_schema_slash_slash;
321 break;
322 default:
323 return NGX_HTTP_PARSE_INVALID_REQUEST;
325 break;
327 case sw_schema_slash_slash:
328 switch (ch) {
329 case '/':
330 state = sw_host_start;
331 break;
332 default:
333 return NGX_HTTP_PARSE_INVALID_REQUEST;
335 break;
337 case sw_host_start:
339 r->host_start = p;
341 if (ch == '[') {
342 state = sw_host_ip_literal;
343 break;
346 state = sw_host;
348 /* fall through */
350 case sw_host:
352 c = (u_char) (ch | 0x20);
353 if (c >= 'a' && c <= 'z') {
354 break;
357 if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
358 break;
361 /* fall through */
363 case sw_host_end:
365 r->host_end = p;
367 switch (ch) {
368 case ':':
369 state = sw_port;
370 break;
371 case '/':
372 r->uri_start = p;
373 state = sw_after_slash_in_uri;
374 break;
375 case ' ':
377 * use single "/" from request line to preserve pointers,
378 * if request line will be copied to large client buffer
380 r->uri_start = r->schema_end + 1;
381 r->uri_end = r->schema_end + 2;
382 state = sw_host_http_09;
383 break;
384 default:
385 return NGX_HTTP_PARSE_INVALID_REQUEST;
387 break;
389 case sw_host_ip_literal:
391 if (ch >= '0' && ch <= '9') {
392 break;
395 c = (u_char) (ch | 0x20);
396 if (c >= 'a' && c <= 'z') {
397 break;
400 switch (ch) {
401 case ':':
402 break;
403 case ']':
404 state = sw_host_end;
405 break;
406 case '-':
407 case '.':
408 case '_':
409 case '~':
410 /* unreserved */
411 break;
412 case '!':
413 case '$':
414 case '&':
415 case '\'':
416 case '(':
417 case ')':
418 case '*':
419 case '+':
420 case ',':
421 case ';':
422 case '=':
423 /* sub-delims */
424 break;
425 default:
426 return NGX_HTTP_PARSE_INVALID_REQUEST;
428 break;
430 case sw_port:
431 if (ch >= '0' && ch <= '9') {
432 break;
435 switch (ch) {
436 case '/':
437 r->port_end = p;
438 r->uri_start = p;
439 state = sw_after_slash_in_uri;
440 break;
441 case ' ':
442 r->port_end = p;
444 * use single "/" from request line to preserve pointers,
445 * if request line will be copied to large client buffer
447 r->uri_start = r->schema_end + 1;
448 r->uri_end = r->schema_end + 2;
449 state = sw_host_http_09;
450 break;
451 default:
452 return NGX_HTTP_PARSE_INVALID_REQUEST;
454 break;
456 /* space+ after "http://host[:port] " */
457 case sw_host_http_09:
458 switch (ch) {
459 case ' ':
460 break;
461 case CR:
462 r->http_minor = 9;
463 state = sw_almost_done;
464 break;
465 case LF:
466 r->http_minor = 9;
467 goto done;
468 case 'H':
469 r->http_protocol.data = p;
470 state = sw_http_H;
471 break;
472 default:
473 return NGX_HTTP_PARSE_INVALID_REQUEST;
475 break;
478 /* check "/.", "//", "%", and "\" (Win32) in URI */
479 case sw_after_slash_in_uri:
481 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
482 state = sw_check_uri;
483 break;
486 switch (ch) {
487 case ' ':
488 r->uri_end = p;
489 state = sw_check_uri_http_09;
490 break;
491 case CR:
492 r->uri_end = p;
493 r->http_minor = 9;
494 state = sw_almost_done;
495 break;
496 case LF:
497 r->uri_end = p;
498 r->http_minor = 9;
499 goto done;
500 case '.':
501 r->complex_uri = 1;
502 state = sw_uri;
503 break;
504 case '%':
505 r->quoted_uri = 1;
506 state = sw_uri;
507 break;
508 case '/':
509 r->complex_uri = 1;
510 state = sw_uri;
511 break;
512 #if (NGX_WIN32)
513 case '\\':
514 r->complex_uri = 1;
515 state = sw_uri;
516 break;
517 #endif
518 case '?':
519 r->args_start = p + 1;
520 state = sw_uri;
521 break;
522 case '#':
523 r->complex_uri = 1;
524 state = sw_uri;
525 break;
526 case '+':
527 r->plus_in_uri = 1;
528 break;
529 case '\0':
530 return NGX_HTTP_PARSE_INVALID_REQUEST;
531 default:
532 state = sw_check_uri;
533 break;
535 break;
537 /* check "/", "%" and "\" (Win32) in URI */
538 case sw_check_uri:
540 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
541 break;
544 switch (ch) {
545 case '/':
546 #if (NGX_WIN32)
547 if (r->uri_ext == p) {
548 r->complex_uri = 1;
549 state = sw_uri;
550 break;
552 #endif
553 r->uri_ext = NULL;
554 state = sw_after_slash_in_uri;
555 break;
556 case '.':
557 r->uri_ext = p + 1;
558 break;
559 case ' ':
560 r->uri_end = p;
561 state = sw_check_uri_http_09;
562 break;
563 case CR:
564 r->uri_end = p;
565 r->http_minor = 9;
566 state = sw_almost_done;
567 break;
568 case LF:
569 r->uri_end = p;
570 r->http_minor = 9;
571 goto done;
572 #if (NGX_WIN32)
573 case '\\':
574 r->complex_uri = 1;
575 state = sw_after_slash_in_uri;
576 break;
577 #endif
578 case '%':
579 r->quoted_uri = 1;
580 state = sw_uri;
581 break;
582 case '?':
583 r->args_start = p + 1;
584 state = sw_uri;
585 break;
586 case '#':
587 r->complex_uri = 1;
588 state = sw_uri;
589 break;
590 case '+':
591 r->plus_in_uri = 1;
592 break;
593 case '\0':
594 return NGX_HTTP_PARSE_INVALID_REQUEST;
596 break;
598 /* space+ after URI */
599 case sw_check_uri_http_09:
600 switch (ch) {
601 case ' ':
602 break;
603 case CR:
604 r->http_minor = 9;
605 state = sw_almost_done;
606 break;
607 case LF:
608 r->http_minor = 9;
609 goto done;
610 case 'H':
611 r->http_protocol.data = p;
612 state = sw_http_H;
613 break;
614 default:
615 r->space_in_uri = 1;
616 state = sw_check_uri;
617 break;
619 break;
622 /* URI */
623 case sw_uri:
625 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
626 break;
629 switch (ch) {
630 case ' ':
631 r->uri_end = p;
632 state = sw_http_09;
633 break;
634 case CR:
635 r->uri_end = p;
636 r->http_minor = 9;
637 state = sw_almost_done;
638 break;
639 case LF:
640 r->uri_end = p;
641 r->http_minor = 9;
642 goto done;
643 case '#':
644 r->complex_uri = 1;
645 break;
646 case '\0':
647 return NGX_HTTP_PARSE_INVALID_REQUEST;
649 break;
651 /* space+ after URI */
652 case sw_http_09:
653 switch (ch) {
654 case ' ':
655 break;
656 case CR:
657 r->http_minor = 9;
658 state = sw_almost_done;
659 break;
660 case LF:
661 r->http_minor = 9;
662 goto done;
663 case 'H':
664 r->http_protocol.data = p;
665 state = sw_http_H;
666 break;
667 default:
668 r->space_in_uri = 1;
669 state = sw_uri;
670 break;
672 break;
674 case sw_http_H:
675 switch (ch) {
676 case 'T':
677 state = sw_http_HT;
678 break;
679 default:
680 return NGX_HTTP_PARSE_INVALID_REQUEST;
682 break;
684 case sw_http_HT:
685 switch (ch) {
686 case 'T':
687 state = sw_http_HTT;
688 break;
689 default:
690 return NGX_HTTP_PARSE_INVALID_REQUEST;
692 break;
694 case sw_http_HTT:
695 switch (ch) {
696 case 'P':
697 state = sw_http_HTTP;
698 break;
699 default:
700 return NGX_HTTP_PARSE_INVALID_REQUEST;
702 break;
704 case sw_http_HTTP:
705 switch (ch) {
706 case '/':
707 state = sw_first_major_digit;
708 break;
709 default:
710 return NGX_HTTP_PARSE_INVALID_REQUEST;
712 break;
714 /* first digit of major HTTP version */
715 case sw_first_major_digit:
716 if (ch < '1' || ch > '9') {
717 return NGX_HTTP_PARSE_INVALID_REQUEST;
720 r->http_major = ch - '0';
721 state = sw_major_digit;
722 break;
724 /* major HTTP version or dot */
725 case sw_major_digit:
726 if (ch == '.') {
727 state = sw_first_minor_digit;
728 break;
731 if (ch < '0' || ch > '9') {
732 return NGX_HTTP_PARSE_INVALID_REQUEST;
735 r->http_major = r->http_major * 10 + ch - '0';
736 break;
738 /* first digit of minor HTTP version */
739 case sw_first_minor_digit:
740 if (ch < '0' || ch > '9') {
741 return NGX_HTTP_PARSE_INVALID_REQUEST;
744 r->http_minor = ch - '0';
745 state = sw_minor_digit;
746 break;
748 /* minor HTTP version or end of request line */
749 case sw_minor_digit:
750 if (ch == CR) {
751 state = sw_almost_done;
752 break;
755 if (ch == LF) {
756 goto done;
759 if (ch == ' ') {
760 state = sw_spaces_after_digit;
761 break;
764 if (ch < '0' || ch > '9') {
765 return NGX_HTTP_PARSE_INVALID_REQUEST;
768 r->http_minor = r->http_minor * 10 + ch - '0';
769 break;
771 case sw_spaces_after_digit:
772 switch (ch) {
773 case ' ':
774 break;
775 case CR:
776 state = sw_almost_done;
777 break;
778 case LF:
779 goto done;
780 default:
781 return NGX_HTTP_PARSE_INVALID_REQUEST;
783 break;
785 /* end of request line */
786 case sw_almost_done:
787 r->request_end = p - 1;
788 switch (ch) {
789 case LF:
790 goto done;
791 default:
792 return NGX_HTTP_PARSE_INVALID_REQUEST;
797 b->pos = p;
798 r->state = state;
800 return NGX_AGAIN;
802 done:
804 b->pos = p + 1;
806 if (r->request_end == NULL) {
807 r->request_end = p;
810 r->http_version = r->http_major * 1000 + r->http_minor;
811 r->state = sw_start;
813 if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
814 return NGX_HTTP_PARSE_INVALID_09_METHOD;
817 return NGX_OK;
821 ngx_int_t
822 ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
823 ngx_uint_t allow_underscores)
825 u_char c, ch, *p;
826 ngx_uint_t hash, i;
827 enum {
828 sw_start = 0,
829 sw_name,
830 sw_space_before_value,
831 sw_value,
832 sw_space_after_value,
833 sw_ignore_line,
834 sw_almost_done,
835 sw_header_almost_done
836 } state;
838 /* the last '\0' is not needed because string is zero terminated */
840 static u_char lowcase[] =
841 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
842 "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
843 "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
844 "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
845 "\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"
846 "\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"
847 "\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"
848 "\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";
850 state = r->state;
851 hash = r->header_hash;
852 i = r->lowcase_index;
854 for (p = b->pos; p < b->last; p++) {
855 ch = *p;
857 switch (state) {
859 /* first char */
860 case sw_start:
861 r->header_name_start = p;
862 r->invalid_header = 0;
864 switch (ch) {
865 case CR:
866 r->header_end = p;
867 state = sw_header_almost_done;
868 break;
869 case LF:
870 r->header_end = p;
871 goto header_done;
872 default:
873 state = sw_name;
875 c = lowcase[ch];
877 if (c) {
878 hash = ngx_hash(0, c);
879 r->lowcase_header[0] = c;
880 i = 1;
881 break;
884 if (ch == '\0') {
885 return NGX_HTTP_PARSE_INVALID_HEADER;
888 r->invalid_header = 1;
890 break;
893 break;
895 /* header name */
896 case sw_name:
897 c = lowcase[ch];
899 if (c) {
900 hash = ngx_hash(hash, c);
901 r->lowcase_header[i++] = c;
902 i &= (NGX_HTTP_LC_HEADER_LEN - 1);
903 break;
906 if (ch == '_') {
907 if (allow_underscores) {
908 hash = ngx_hash(hash, ch);
909 r->lowcase_header[i++] = ch;
910 i &= (NGX_HTTP_LC_HEADER_LEN - 1);
912 } else {
913 r->invalid_header = 1;
916 break;
919 if (ch == ':') {
920 r->header_name_end = p;
921 state = sw_space_before_value;
922 break;
925 if (ch == CR) {
926 r->header_name_end = p;
927 r->header_start = p;
928 r->header_end = p;
929 state = sw_almost_done;
930 break;
933 if (ch == LF) {
934 r->header_name_end = p;
935 r->header_start = p;
936 r->header_end = p;
937 goto done;
940 /* IIS may send the duplicate "HTTP/1.1 ..." lines */
941 if (ch == '/'
942 && r->upstream
943 && p - r->header_name_start == 4
944 && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
946 state = sw_ignore_line;
947 break;
950 if (ch == '\0') {
951 return NGX_HTTP_PARSE_INVALID_HEADER;
954 r->invalid_header = 1;
956 break;
958 /* space* before header value */
959 case sw_space_before_value:
960 switch (ch) {
961 case ' ':
962 break;
963 case CR:
964 r->header_start = p;
965 r->header_end = p;
966 state = sw_almost_done;
967 break;
968 case LF:
969 r->header_start = p;
970 r->header_end = p;
971 goto done;
972 case '\0':
973 return NGX_HTTP_PARSE_INVALID_HEADER;
974 default:
975 r->header_start = p;
976 state = sw_value;
977 break;
979 break;
981 /* header value */
982 case sw_value:
983 switch (ch) {
984 case ' ':
985 r->header_end = p;
986 state = sw_space_after_value;
987 break;
988 case CR:
989 r->header_end = p;
990 state = sw_almost_done;
991 break;
992 case LF:
993 r->header_end = p;
994 goto done;
995 case '\0':
996 return NGX_HTTP_PARSE_INVALID_HEADER;
998 break;
1000 /* space* before end of header line */
1001 case sw_space_after_value:
1002 switch (ch) {
1003 case ' ':
1004 break;
1005 case CR:
1006 state = sw_almost_done;
1007 break;
1008 case LF:
1009 goto done;
1010 case '\0':
1011 return NGX_HTTP_PARSE_INVALID_HEADER;
1012 default:
1013 state = sw_value;
1014 break;
1016 break;
1018 /* ignore header line */
1019 case sw_ignore_line:
1020 switch (ch) {
1021 case LF:
1022 state = sw_start;
1023 break;
1024 default:
1025 break;
1027 break;
1029 /* end of header line */
1030 case sw_almost_done:
1031 switch (ch) {
1032 case LF:
1033 goto done;
1034 case CR:
1035 break;
1036 default:
1037 return NGX_HTTP_PARSE_INVALID_HEADER;
1039 break;
1041 /* end of header */
1042 case sw_header_almost_done:
1043 switch (ch) {
1044 case LF:
1045 goto header_done;
1046 default:
1047 return NGX_HTTP_PARSE_INVALID_HEADER;
1052 b->pos = p;
1053 r->state = state;
1054 r->header_hash = hash;
1055 r->lowcase_index = i;
1057 return NGX_AGAIN;
1059 done:
1061 b->pos = p + 1;
1062 r->state = sw_start;
1063 r->header_hash = hash;
1064 r->lowcase_index = i;
1066 return NGX_OK;
1068 header_done:
1070 b->pos = p + 1;
1071 r->state = sw_start;
1073 return NGX_HTTP_PARSE_HEADER_DONE;
1077 ngx_int_t
1078 ngx_http_parse_uri(ngx_http_request_t *r)
1080 u_char *p, ch;
1081 enum {
1082 sw_start = 0,
1083 sw_after_slash_in_uri,
1084 sw_check_uri,
1085 sw_uri
1086 } state;
1088 state = sw_start;
1090 for (p = r->uri_start; p != r->uri_end; p++) {
1092 ch = *p;
1094 switch (state) {
1096 case sw_start:
1098 if (ch != '/') {
1099 return NGX_ERROR;
1102 state = sw_after_slash_in_uri;
1103 break;
1105 /* check "/.", "//", "%", and "\" (Win32) in URI */
1106 case sw_after_slash_in_uri:
1108 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1109 state = sw_check_uri;
1110 break;
1113 switch (ch) {
1114 case ' ':
1115 r->space_in_uri = 1;
1116 state = sw_check_uri;
1117 break;
1118 case '.':
1119 r->complex_uri = 1;
1120 state = sw_uri;
1121 break;
1122 case '%':
1123 r->quoted_uri = 1;
1124 state = sw_uri;
1125 break;
1126 case '/':
1127 r->complex_uri = 1;
1128 state = sw_uri;
1129 break;
1130 #if (NGX_WIN32)
1131 case '\\':
1132 r->complex_uri = 1;
1133 state = sw_uri;
1134 break;
1135 #endif
1136 case '?':
1137 r->args_start = p + 1;
1138 state = sw_uri;
1139 break;
1140 case '#':
1141 r->complex_uri = 1;
1142 state = sw_uri;
1143 break;
1144 case '+':
1145 r->plus_in_uri = 1;
1146 break;
1147 default:
1148 state = sw_check_uri;
1149 break;
1151 break;
1153 /* check "/", "%" and "\" (Win32) in URI */
1154 case sw_check_uri:
1156 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1157 break;
1160 switch (ch) {
1161 case '/':
1162 #if (NGX_WIN32)
1163 if (r->uri_ext == p) {
1164 r->complex_uri = 1;
1165 state = sw_uri;
1166 break;
1168 #endif
1169 r->uri_ext = NULL;
1170 state = sw_after_slash_in_uri;
1171 break;
1172 case '.':
1173 r->uri_ext = p + 1;
1174 break;
1175 case ' ':
1176 r->space_in_uri = 1;
1177 break;
1178 #if (NGX_WIN32)
1179 case '\\':
1180 r->complex_uri = 1;
1181 state = sw_after_slash_in_uri;
1182 break;
1183 #endif
1184 case '%':
1185 r->quoted_uri = 1;
1186 state = sw_uri;
1187 break;
1188 case '?':
1189 r->args_start = p + 1;
1190 state = sw_uri;
1191 break;
1192 case '#':
1193 r->complex_uri = 1;
1194 state = sw_uri;
1195 break;
1196 case '+':
1197 r->plus_in_uri = 1;
1198 break;
1200 break;
1202 /* URI */
1203 case sw_uri:
1205 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1206 break;
1209 switch (ch) {
1210 case ' ':
1211 r->space_in_uri = 1;
1212 break;
1213 case '#':
1214 r->complex_uri = 1;
1215 break;
1217 break;
1221 return NGX_OK;
1225 ngx_int_t
1226 ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
1228 u_char c, ch, decoded, *p, *u;
1229 enum {
1230 sw_usual = 0,
1231 sw_slash,
1232 sw_dot,
1233 sw_dot_dot,
1234 sw_quoted,
1235 sw_quoted_second
1236 } state, quoted_state;
1238 #if (NGX_SUPPRESS_WARN)
1239 decoded = '\0';
1240 quoted_state = sw_usual;
1241 #endif
1243 state = sw_usual;
1244 p = r->uri_start;
1245 u = r->uri.data;
1246 r->uri_ext = NULL;
1247 r->args_start = NULL;
1249 ch = *p++;
1251 while (p <= r->uri_end) {
1254 * we use "ch = *p++" inside the cycle, but this operation is safe,
1255 * because after the URI there is always at least one character:
1256 * the line feed
1259 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1260 "s:%d in:'%Xd:%c', out:'%c'", state, ch, ch, *u);
1262 switch (state) {
1264 case sw_usual:
1266 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1267 *u++ = ch;
1268 ch = *p++;
1269 break;
1272 switch(ch) {
1273 #if (NGX_WIN32)
1274 case '\\':
1275 if (u - 2 >= r->uri.data
1276 && *(u - 1) == '.' && *(u - 2) != '.')
1278 u--;
1281 r->uri_ext = NULL;
1283 if (p == r->uri_start + r->uri.len) {
1286 * we omit the last "\" to cause redirect because
1287 * the browsers do not treat "\" as "/" in relative URL path
1290 break;
1293 state = sw_slash;
1294 *u++ = '/';
1295 break;
1296 #endif
1297 case '/':
1298 #if (NGX_WIN32)
1299 if (u - 2 >= r->uri.data
1300 && *(u - 1) == '.' && *(u - 2) != '.')
1302 u--;
1304 #endif
1305 r->uri_ext = NULL;
1306 state = sw_slash;
1307 *u++ = ch;
1308 break;
1309 case '%':
1310 quoted_state = state;
1311 state = sw_quoted;
1312 break;
1313 case '?':
1314 r->args_start = p;
1315 goto args;
1316 case '#':
1317 goto done;
1318 case '.':
1319 r->uri_ext = u + 1;
1320 *u++ = ch;
1321 break;
1322 case '+':
1323 r->plus_in_uri = 1;
1324 /* fall through */
1325 default:
1326 *u++ = ch;
1327 break;
1330 ch = *p++;
1331 break;
1333 case sw_slash:
1335 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1336 state = sw_usual;
1337 *u++ = ch;
1338 ch = *p++;
1339 break;
1342 switch(ch) {
1343 #if (NGX_WIN32)
1344 case '\\':
1345 break;
1346 #endif
1347 case '/':
1348 if (!merge_slashes) {
1349 *u++ = ch;
1351 break;
1352 case '.':
1353 state = sw_dot;
1354 *u++ = ch;
1355 break;
1356 case '%':
1357 quoted_state = state;
1358 state = sw_quoted;
1359 break;
1360 case '?':
1361 r->args_start = p;
1362 goto args;
1363 case '#':
1364 goto done;
1365 case '+':
1366 r->plus_in_uri = 1;
1367 default:
1368 state = sw_usual;
1369 *u++ = ch;
1370 break;
1373 ch = *p++;
1374 break;
1376 case sw_dot:
1378 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1379 state = sw_usual;
1380 *u++ = ch;
1381 ch = *p++;
1382 break;
1385 switch(ch) {
1386 #if (NGX_WIN32)
1387 case '\\':
1388 #endif
1389 case '/':
1390 state = sw_slash;
1391 u--;
1392 break;
1393 case '.':
1394 state = sw_dot_dot;
1395 *u++ = ch;
1396 break;
1397 case '%':
1398 quoted_state = state;
1399 state = sw_quoted;
1400 break;
1401 case '?':
1402 r->args_start = p;
1403 goto args;
1404 case '#':
1405 goto done;
1406 case '+':
1407 r->plus_in_uri = 1;
1408 default:
1409 state = sw_usual;
1410 *u++ = ch;
1411 break;
1414 ch = *p++;
1415 break;
1417 case sw_dot_dot:
1419 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1420 state = sw_usual;
1421 *u++ = ch;
1422 ch = *p++;
1423 break;
1426 switch(ch) {
1427 #if (NGX_WIN32)
1428 case '\\':
1429 #endif
1430 case '/':
1431 state = sw_slash;
1432 u -= 5;
1433 for ( ;; ) {
1434 if (u < r->uri.data) {
1435 return NGX_HTTP_PARSE_INVALID_REQUEST;
1437 if (*u == '/') {
1438 u++;
1439 break;
1441 u--;
1443 break;
1444 case '%':
1445 quoted_state = state;
1446 state = sw_quoted;
1447 break;
1448 case '?':
1449 r->args_start = p;
1450 goto args;
1451 case '#':
1452 goto done;
1453 case '+':
1454 r->plus_in_uri = 1;
1455 default:
1456 state = sw_usual;
1457 *u++ = ch;
1458 break;
1461 ch = *p++;
1462 break;
1464 case sw_quoted:
1465 r->quoted_uri = 1;
1467 if (ch >= '0' && ch <= '9') {
1468 decoded = (u_char) (ch - '0');
1469 state = sw_quoted_second;
1470 ch = *p++;
1471 break;
1474 c = (u_char) (ch | 0x20);
1475 if (c >= 'a' && c <= 'f') {
1476 decoded = (u_char) (c - 'a' + 10);
1477 state = sw_quoted_second;
1478 ch = *p++;
1479 break;
1482 return NGX_HTTP_PARSE_INVALID_REQUEST;
1484 case sw_quoted_second:
1485 if (ch >= '0' && ch <= '9') {
1486 ch = (u_char) ((decoded << 4) + ch - '0');
1488 if (ch == '%' || ch == '#') {
1489 state = sw_usual;
1490 *u++ = ch;
1491 ch = *p++;
1492 break;
1494 } else if (ch == '\0') {
1495 return NGX_HTTP_PARSE_INVALID_REQUEST;
1498 state = quoted_state;
1499 break;
1502 c = (u_char) (ch | 0x20);
1503 if (c >= 'a' && c <= 'f') {
1504 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
1506 if (ch == '?') {
1507 state = sw_usual;
1508 *u++ = ch;
1509 ch = *p++;
1510 break;
1512 } else if (ch == '+') {
1513 r->plus_in_uri = 1;
1516 state = quoted_state;
1517 break;
1520 return NGX_HTTP_PARSE_INVALID_REQUEST;
1524 done:
1526 r->uri.len = u - r->uri.data;
1528 if (r->uri_ext) {
1529 r->exten.len = u - r->uri_ext;
1530 r->exten.data = r->uri_ext;
1533 r->uri_ext = NULL;
1535 return NGX_OK;
1537 args:
1539 while (p < r->uri_end) {
1540 if (*p++ != '#') {
1541 continue;
1544 r->args.len = p - 1 - r->args_start;
1545 r->args.data = r->args_start;
1546 r->args_start = NULL;
1548 break;
1551 r->uri.len = u - r->uri.data;
1553 if (r->uri_ext) {
1554 r->exten.len = u - r->uri_ext;
1555 r->exten.data = r->uri_ext;
1558 r->uri_ext = NULL;
1560 return NGX_OK;
1564 ngx_int_t
1565 ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
1566 ngx_http_status_t *status)
1568 u_char ch;
1569 u_char *p;
1570 enum {
1571 sw_start = 0,
1572 sw_H,
1573 sw_HT,
1574 sw_HTT,
1575 sw_HTTP,
1576 sw_first_major_digit,
1577 sw_major_digit,
1578 sw_first_minor_digit,
1579 sw_minor_digit,
1580 sw_status,
1581 sw_space_after_status,
1582 sw_status_text,
1583 sw_almost_done
1584 } state;
1586 state = r->state;
1588 for (p = b->pos; p < b->last; p++) {
1589 ch = *p;
1591 switch (state) {
1593 /* "HTTP/" */
1594 case sw_start:
1595 switch (ch) {
1596 case 'H':
1597 state = sw_H;
1598 break;
1599 default:
1600 return NGX_ERROR;
1602 break;
1604 case sw_H:
1605 switch (ch) {
1606 case 'T':
1607 state = sw_HT;
1608 break;
1609 default:
1610 return NGX_ERROR;
1612 break;
1614 case sw_HT:
1615 switch (ch) {
1616 case 'T':
1617 state = sw_HTT;
1618 break;
1619 default:
1620 return NGX_ERROR;
1622 break;
1624 case sw_HTT:
1625 switch (ch) {
1626 case 'P':
1627 state = sw_HTTP;
1628 break;
1629 default:
1630 return NGX_ERROR;
1632 break;
1634 case sw_HTTP:
1635 switch (ch) {
1636 case '/':
1637 state = sw_first_major_digit;
1638 break;
1639 default:
1640 return NGX_ERROR;
1642 break;
1644 /* the first digit of major HTTP version */
1645 case sw_first_major_digit:
1646 if (ch < '1' || ch > '9') {
1647 return NGX_ERROR;
1650 r->http_major = ch - '0';
1651 state = sw_major_digit;
1652 break;
1654 /* the major HTTP version or dot */
1655 case sw_major_digit:
1656 if (ch == '.') {
1657 state = sw_first_minor_digit;
1658 break;
1661 if (ch < '0' || ch > '9') {
1662 return NGX_ERROR;
1665 r->http_major = r->http_major * 10 + ch - '0';
1666 break;
1668 /* the first digit of minor HTTP version */
1669 case sw_first_minor_digit:
1670 if (ch < '0' || ch > '9') {
1671 return NGX_ERROR;
1674 r->http_minor = ch - '0';
1675 state = sw_minor_digit;
1676 break;
1678 /* the minor HTTP version or the end of the request line */
1679 case sw_minor_digit:
1680 if (ch == ' ') {
1681 state = sw_status;
1682 break;
1685 if (ch < '0' || ch > '9') {
1686 return NGX_ERROR;
1689 r->http_minor = r->http_minor * 10 + ch - '0';
1690 break;
1692 /* HTTP status code */
1693 case sw_status:
1694 if (ch == ' ') {
1695 break;
1698 if (ch < '0' || ch > '9') {
1699 return NGX_ERROR;
1702 status->code = status->code * 10 + ch - '0';
1704 if (++status->count == 3) {
1705 state = sw_space_after_status;
1706 status->start = p - 2;
1709 break;
1711 /* space or end of line */
1712 case sw_space_after_status:
1713 switch (ch) {
1714 case ' ':
1715 state = sw_status_text;
1716 break;
1717 case '.': /* IIS may send 403.1, 403.2, etc */
1718 state = sw_status_text;
1719 break;
1720 case CR:
1721 state = sw_almost_done;
1722 break;
1723 case LF:
1724 goto done;
1725 default:
1726 return NGX_ERROR;
1728 break;
1730 /* any text until end of line */
1731 case sw_status_text:
1732 switch (ch) {
1733 case CR:
1734 state = sw_almost_done;
1736 break;
1737 case LF:
1738 goto done;
1740 break;
1742 /* end of status line */
1743 case sw_almost_done:
1744 status->end = p - 1;
1745 switch (ch) {
1746 case LF:
1747 goto done;
1748 default:
1749 return NGX_ERROR;
1754 b->pos = p;
1755 r->state = state;
1757 return NGX_AGAIN;
1759 done:
1761 b->pos = p + 1;
1763 if (status->end == NULL) {
1764 status->end = p;
1767 status->http_version = r->http_major * 1000 + r->http_minor;
1768 r->state = sw_start;
1770 return NGX_OK;
1774 ngx_int_t
1775 ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
1776 ngx_str_t *args, ngx_uint_t *flags)
1778 u_char ch, *p;
1779 size_t len;
1781 len = uri->len;
1782 p = uri->data;
1784 if (len == 0 || p[0] == '?') {
1785 goto unsafe;
1788 if (p[0] == '.' && len == 3 && p[1] == '.' && (ngx_path_separator(p[2]))) {
1789 goto unsafe;
1792 for ( /* void */ ; len; len--) {
1794 ch = *p++;
1796 if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1797 continue;
1800 if (ch == '?') {
1801 args->len = len - 1;
1802 args->data = p;
1803 uri->len -= len;
1805 return NGX_OK;
1808 if (ch == '\0') {
1809 goto unsafe;
1812 if (ngx_path_separator(ch) && len > 2) {
1814 /* detect "/../" */
1816 if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) {
1817 goto unsafe;
1822 return NGX_OK;
1824 unsafe:
1826 if (*flags & NGX_HTTP_LOG_UNSAFE) {
1827 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1828 "unsafe URI \"%V\" was detected", uri);
1831 return NGX_ERROR;
1835 ngx_int_t
1836 ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,
1837 ngx_str_t *value)
1839 ngx_uint_t i;
1840 u_char *start, *last, *end, ch;
1841 ngx_table_elt_t **h;
1843 h = headers->elts;
1845 for (i = 0; i < headers->nelts; i++) {
1847 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
1848 "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
1850 if (name->len > h[i]->value.len) {
1851 continue;
1854 start = h[i]->value.data;
1855 end = h[i]->value.data + h[i]->value.len;
1857 while (start < end) {
1859 if (ngx_strncasecmp(start, name->data, name->len) != 0) {
1860 goto skip;
1863 for (start += name->len; start < end && *start == ' '; start++) {
1864 /* void */
1867 if (value == NULL) {
1868 if (start == end || *start == ',') {
1869 return i;
1872 goto skip;
1875 if (start == end || *start++ != '=') {
1876 /* the invalid header value */
1877 goto skip;
1880 while (start < end && *start == ' ') { start++; }
1882 for (last = start; last < end && *last != ';'; last++) {
1883 /* void */
1886 value->len = last - start;
1887 value->data = start;
1889 return i;
1891 skip:
1893 while (start < end) {
1894 ch = *start++;
1895 if (ch == ';' || ch == ',') {
1896 break;
1900 while (start < end && *start == ' ') { start++; }
1904 return NGX_DECLINED;
1908 ngx_int_t
1909 ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
1911 u_char *p, *last;
1913 if (r->args.len == 0) {
1914 return NGX_DECLINED;
1917 p = r->args.data;
1918 last = p + r->args.len;
1920 for ( /* void */ ; p < last; p++) {
1922 /* we need '=' after name, so drop one char from last */
1924 p = ngx_strlcasestrn(p, last - 1, name, len - 1);
1926 if (p == NULL) {
1927 return NGX_DECLINED;
1930 if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
1932 value->data = p + len + 1;
1934 p = ngx_strlchr(p, last, '&');
1936 if (p == NULL) {
1937 p = r->args.data + r->args.len;
1940 value->len = p - value->data;
1942 return NGX_OK;
1946 return NGX_DECLINED;
1950 void
1951 ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
1953 u_char *p, *last;
1955 last = uri->data + uri->len;
1957 p = ngx_strlchr(uri->data, last, '?');
1959 if (p) {
1960 uri->len = p - uri->data;
1961 p++;
1962 args->len = last - p;
1963 args->data = p;
1965 } else {
1966 args->len = 0;
1971 ngx_int_t
1972 ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
1973 ngx_http_chunked_t *ctx)
1975 u_char *pos, ch, c;
1976 ngx_int_t rc;
1977 enum {
1978 sw_chunk_start = 0,
1979 sw_chunk_size,
1980 sw_chunk_extension,
1981 sw_chunk_extension_almost_done,
1982 sw_chunk_data,
1983 sw_after_data,
1984 sw_after_data_almost_done,
1985 sw_last_chunk_extension,
1986 sw_last_chunk_extension_almost_done,
1987 sw_trailer,
1988 sw_trailer_almost_done,
1989 sw_trailer_header,
1990 sw_trailer_header_almost_done
1991 } state;
1993 state = ctx->state;
1995 if (state == sw_chunk_data && ctx->size == 0) {
1996 state = sw_after_data;
1999 rc = NGX_AGAIN;
2001 for (pos = b->pos; pos < b->last; pos++) {
2003 ch = *pos;
2005 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2006 "http chunked byte: %02Xd s:%d", ch, state);
2008 switch (state) {
2010 case sw_chunk_start:
2011 if (ch >= '0' && ch <= '9') {
2012 state = sw_chunk_size;
2013 ctx->size = ch - '0';
2014 break;
2017 c = (u_char) (ch | 0x20);
2019 if (c >= 'a' && c <= 'f') {
2020 state = sw_chunk_size;
2021 ctx->size = c - 'a' + 10;
2022 break;
2025 goto invalid;
2027 case sw_chunk_size:
2028 if (ch >= '0' && ch <= '9') {
2029 ctx->size = ctx->size * 16 + (ch - '0');
2030 break;
2033 c = (u_char) (ch | 0x20);
2035 if (c >= 'a' && c <= 'f') {
2036 ctx->size = ctx->size * 16 + (c - 'a' + 10);
2037 break;
2040 if (ctx->size == 0) {
2042 switch (ch) {
2043 case CR:
2044 state = sw_last_chunk_extension_almost_done;
2045 break;
2046 case LF:
2047 state = sw_trailer;
2048 break;
2049 case ';':
2050 case ' ':
2051 case '\t':
2052 state = sw_last_chunk_extension;
2053 break;
2054 default:
2055 goto invalid;
2058 break;
2061 switch (ch) {
2062 case CR:
2063 state = sw_chunk_extension_almost_done;
2064 break;
2065 case LF:
2066 state = sw_chunk_data;
2067 break;
2068 case ';':
2069 case ' ':
2070 case '\t':
2071 state = sw_chunk_extension;
2072 break;
2073 default:
2074 goto invalid;
2077 break;
2079 case sw_chunk_extension:
2080 switch (ch) {
2081 case CR:
2082 state = sw_chunk_extension_almost_done;
2083 break;
2084 case LF:
2085 state = sw_chunk_data;
2087 break;
2089 case sw_chunk_extension_almost_done:
2090 if (ch == LF) {
2091 state = sw_chunk_data;
2092 break;
2094 goto invalid;
2096 case sw_chunk_data:
2097 rc = NGX_OK;
2098 goto data;
2100 case sw_after_data:
2101 switch (ch) {
2102 case CR:
2103 state = sw_after_data_almost_done;
2104 break;
2105 case LF:
2106 state = sw_chunk_start;
2108 break;
2110 case sw_after_data_almost_done:
2111 if (ch == LF) {
2112 state = sw_chunk_start;
2113 break;
2115 goto invalid;
2117 case sw_last_chunk_extension:
2118 switch (ch) {
2119 case CR:
2120 state = sw_last_chunk_extension_almost_done;
2121 break;
2122 case LF:
2123 state = sw_trailer;
2125 break;
2127 case sw_last_chunk_extension_almost_done:
2128 if (ch == LF) {
2129 state = sw_trailer;
2130 break;
2132 goto invalid;
2134 case sw_trailer:
2135 switch (ch) {
2136 case CR:
2137 state = sw_trailer_almost_done;
2138 break;
2139 case LF:
2140 goto done;
2141 default:
2142 state = sw_trailer_header;
2144 break;
2146 case sw_trailer_almost_done:
2147 if (ch == LF) {
2148 goto done;
2150 goto invalid;
2152 case sw_trailer_header:
2153 switch (ch) {
2154 case CR:
2155 state = sw_trailer_header_almost_done;
2156 break;
2157 case LF:
2158 state = sw_trailer;
2160 break;
2162 case sw_trailer_header_almost_done:
2163 if (ch == LF) {
2164 state = sw_trailer;
2165 break;
2167 goto invalid;
2172 data:
2174 ctx->state = state;
2175 b->pos = pos;
2177 switch (state) {
2179 case sw_chunk_start:
2180 ctx->length = 3 /* "0" LF LF */;
2181 break;
2182 case sw_chunk_size:
2183 ctx->length = 2 /* LF LF */
2184 + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */ : 0);
2185 break;
2186 case sw_chunk_extension:
2187 case sw_chunk_extension_almost_done:
2188 ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */;
2189 break;
2190 case sw_chunk_data:
2191 ctx->length = ctx->size + 4 /* LF "0" LF LF */;
2192 break;
2193 case sw_after_data:
2194 case sw_after_data_almost_done:
2195 ctx->length = 4 /* LF "0" LF LF */;
2196 break;
2197 case sw_last_chunk_extension:
2198 case sw_last_chunk_extension_almost_done:
2199 ctx->length = 2 /* LF LF */;
2200 break;
2201 case sw_trailer:
2202 case sw_trailer_almost_done:
2203 ctx->length = 1 /* LF */;
2204 break;
2205 case sw_trailer_header:
2206 case sw_trailer_header_almost_done:
2207 ctx->length = 2 /* LF LF */;
2208 break;
2212 return rc;
2214 done:
2216 ctx->state = 0;
2217 b->pos = pos + 1;
2219 return NGX_DONE;
2221 invalid:
2223 return NGX_ERROR;