Annotation of embedaddon/nginx/src/http/ngx_http_parse.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (C) Igor Sysoev
4: * Copyright (C) Nginx, Inc.
5: */
6:
7:
8: #include <ngx_config.h>
9: #include <ngx_core.h>
10: #include <ngx_http.h>
11:
12:
13: static uint32_t usual[] = {
14: 0xffffdbfe, /* 1111 1111 1111 1111 1101 1011 1111 1110 */
15:
16: /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
17: 0x7fff37d6, /* 0111 1111 1111 1111 0011 0111 1101 0110 */
18:
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
25:
26: /* ~}| {zyx wvut srqp onml kjih gfed cba` */
27: 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
28:
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 */
33: };
34:
35:
36: #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
37:
38: #define ngx_str3_cmp(m, c0, c1, c2, c3) \
39: *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
40:
41: #define ngx_str3Ocmp(m, c0, c1, c2, c3) \
42: *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
43:
44: #define ngx_str4cmp(m, c0, c1, c2, c3) \
45: *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
46:
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
50:
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)
54:
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)
58:
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)
62:
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
67:
68: #else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */
69:
70: #define ngx_str3_cmp(m, c0, c1, c2, c3) \
71: m[0] == c0 && m[1] == c1 && m[2] == c2
72:
73: #define ngx_str3Ocmp(m, c0, c1, c2, c3) \
74: m[0] == c0 && m[2] == c2 && m[3] == c3
75:
76: #define ngx_str4cmp(m, c0, c1, c2, c3) \
77: m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3
78:
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
81:
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
85:
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
89:
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
93:
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
97:
98: #endif
99:
100:
101: /* gcc, icc, msvc and others compile these switches as an jump table */
102:
103: ngx_int_t
104: ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
105: {
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;
136:
137: state = r->state;
138:
139: for (p = b->pos; p < b->last; p++) {
140: ch = *p;
141:
142: switch (state) {
143:
144: /* HTTP methods: GET, HEAD, POST */
145: case sw_start:
146: r->request_start = p;
147:
148: if (ch == CR || ch == LF) {
149: break;
150: }
151:
152: if ((ch < 'A' || ch > 'Z') && ch != '_') {
153: return NGX_HTTP_PARSE_INVALID_METHOD;
154: }
155:
156: state = sw_method;
157: break;
158:
159: case sw_method:
160: if (ch == ' ') {
161: r->method_end = p - 1;
162: m = r->request_start;
163:
164: switch (p - m) {
165:
166: case 3:
167: if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
168: r->method = NGX_HTTP_GET;
169: break;
170: }
171:
172: if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
173: r->method = NGX_HTTP_PUT;
174: break;
175: }
176:
177: break;
178:
179: case 4:
180: if (m[1] == 'O') {
181:
182: if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
183: r->method = NGX_HTTP_POST;
184: break;
185: }
186:
187: if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
188: r->method = NGX_HTTP_COPY;
189: break;
190: }
191:
192: if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
193: r->method = NGX_HTTP_MOVE;
194: break;
195: }
196:
197: if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
198: r->method = NGX_HTTP_LOCK;
199: break;
200: }
201:
202: } else {
203:
204: if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
205: r->method = NGX_HTTP_HEAD;
206: break;
207: }
208: }
209:
210: break;
211:
212: case 5:
213: if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
214: r->method = NGX_HTTP_MKCOL;
215: }
216:
217: if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
218: r->method = NGX_HTTP_PATCH;
219: }
220:
221: if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
222: r->method = NGX_HTTP_TRACE;
223: }
224:
225: break;
226:
227: case 6:
228: if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
229: r->method = NGX_HTTP_DELETE;
230: break;
231: }
232:
233: if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
234: r->method = NGX_HTTP_UNLOCK;
235: break;
236: }
237:
238: break;
239:
240: case 7:
241: if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
242: {
243: r->method = NGX_HTTP_OPTIONS;
244: }
245:
246: break;
247:
248: case 8:
249: if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
250: {
251: r->method = NGX_HTTP_PROPFIND;
252: }
253:
254: break;
255:
256: case 9:
257: if (ngx_str9cmp(m,
258: 'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
259: {
260: r->method = NGX_HTTP_PROPPATCH;
261: }
262:
263: break;
264: }
265:
266: state = sw_spaces_before_uri;
267: break;
268: }
269:
270: if ((ch < 'A' || ch > 'Z') && ch != '_') {
271: return NGX_HTTP_PARSE_INVALID_METHOD;
272: }
273:
274: break;
275:
276: /* space* before URI */
277: case sw_spaces_before_uri:
278:
279: if (ch == '/') {
280: r->uri_start = p;
281: state = sw_after_slash_in_uri;
282: break;
283: }
284:
285: c = (u_char) (ch | 0x20);
286: if (c >= 'a' && c <= 'z') {
287: r->schema_start = p;
288: state = sw_schema;
289: break;
290: }
291:
292: switch (ch) {
293: case ' ':
294: break;
295: default:
296: return NGX_HTTP_PARSE_INVALID_REQUEST;
297: }
298: break;
299:
300: case sw_schema:
301:
302: c = (u_char) (ch | 0x20);
303: if (c >= 'a' && c <= 'z') {
304: break;
305: }
306:
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;
314: }
315: break;
316:
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;
324: }
325: break;
326:
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;
334: }
335: break;
336:
337: case sw_host_start:
338:
339: r->host_start = p;
340:
341: if (ch == '[') {
342: state = sw_host_ip_literal;
343: break;
344: }
345:
346: state = sw_host;
347:
348: /* fall through */
349:
350: case sw_host:
351:
352: c = (u_char) (ch | 0x20);
353: if (c >= 'a' && c <= 'z') {
354: break;
355: }
356:
357: if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
358: break;
359: }
360:
361: /* fall through */
362:
363: case sw_host_end:
364:
365: r->host_end = p;
366:
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 ' ':
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_host_http_09;
383: break;
384: default:
385: return NGX_HTTP_PARSE_INVALID_REQUEST;
386: }
387: break;
388:
389: case sw_host_ip_literal:
390:
391: if (ch >= '0' && ch <= '9') {
392: break;
393: }
394:
395: c = (u_char) (ch | 0x20);
396: if (c >= 'a' && c <= 'z') {
397: break;
398: }
399:
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;
427: }
428: break;
429:
430: case sw_port:
431: if (ch >= '0' && ch <= '9') {
432: break;
433: }
434:
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;
443: /*
444: * use single "/" from request line to preserve pointers,
445: * if request line will be copied to large client buffer
446: */
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;
453: }
454: break;
455:
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;
474: }
475: break;
476:
477:
478: /* check "/.", "//", "%", and "\" (Win32) in URI */
479: case sw_after_slash_in_uri:
480:
481: if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
482: state = sw_check_uri;
483: break;
484: }
485:
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;
534: }
535: break;
536:
537: /* check "/", "%" and "\" (Win32) in URI */
538: case sw_check_uri:
539:
540: if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
541: break;
542: }
543:
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;
551: }
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;
595: }
596: break;
597:
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;
618: }
619: break;
620:
621:
622: /* URI */
623: case sw_uri:
624:
625: if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
626: break;
627: }
628:
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;
648: }
649: break;
650:
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;
671: }
672: break;
673:
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;
681: }
682: break;
683:
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;
691: }
692: break;
693:
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;
701: }
702: break;
703:
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;
711: }
712: break;
713:
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;
718: }
719:
720: r->http_major = ch - '0';
721: state = sw_major_digit;
722: break;
723:
724: /* major HTTP version or dot */
725: case sw_major_digit:
726: if (ch == '.') {
727: state = sw_first_minor_digit;
728: break;
729: }
730:
731: if (ch < '0' || ch > '9') {
732: return NGX_HTTP_PARSE_INVALID_REQUEST;
733: }
734:
735: r->http_major = r->http_major * 10 + ch - '0';
736: break;
737:
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;
742: }
743:
744: r->http_minor = ch - '0';
745: state = sw_minor_digit;
746: break;
747:
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;
753: }
754:
755: if (ch == LF) {
756: goto done;
757: }
758:
759: if (ch == ' ') {
760: state = sw_spaces_after_digit;
761: break;
762: }
763:
764: if (ch < '0' || ch > '9') {
765: return NGX_HTTP_PARSE_INVALID_REQUEST;
766: }
767:
768: r->http_minor = r->http_minor * 10 + ch - '0';
769: break;
770:
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;
782: }
783: break;
784:
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;
793: }
794: }
795: }
796:
797: b->pos = p;
798: r->state = state;
799:
800: return NGX_AGAIN;
801:
802: done:
803:
804: b->pos = p + 1;
805:
806: if (r->request_end == NULL) {
807: r->request_end = p;
808: }
809:
810: r->http_version = r->http_major * 1000 + r->http_minor;
811: r->state = sw_start;
812:
813: if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
814: return NGX_HTTP_PARSE_INVALID_09_METHOD;
815: }
816:
817: return NGX_OK;
818: }
819:
820:
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)
824: {
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;
837:
838: /* the last '\0' is not needed because string is zero terminated */
839:
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";
849:
850: state = r->state;
851: hash = r->header_hash;
852: i = r->lowcase_index;
853:
854: for (p = b->pos; p < b->last; p++) {
855: ch = *p;
856:
857: switch (state) {
858:
859: /* first char */
860: case sw_start:
861: r->header_name_start = p;
862: r->invalid_header = 0;
863:
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;
874:
875: c = lowcase[ch];
876:
877: if (c) {
878: hash = ngx_hash(0, c);
879: r->lowcase_header[0] = c;
880: i = 1;
881: break;
882: }
883:
884: if (ch == '\0') {
885: return NGX_HTTP_PARSE_INVALID_HEADER;
886: }
887:
888: r->invalid_header = 1;
889:
890: break;
891:
892: }
893: break;
894:
895: /* header name */
896: case sw_name:
897: c = lowcase[ch];
898:
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;
904: }
905:
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);
911:
912: } else {
913: r->invalid_header = 1;
914: }
915:
916: break;
917: }
918:
919: if (ch == ':') {
920: r->header_name_end = p;
921: state = sw_space_before_value;
922: break;
923: }
924:
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;
931: }
932:
933: if (ch == LF) {
934: r->header_name_end = p;
935: r->header_start = p;
936: r->header_end = p;
937: goto done;
938: }
939:
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)
945: {
946: state = sw_ignore_line;
947: break;
948: }
949:
950: if (ch == '\0') {
951: return NGX_HTTP_PARSE_INVALID_HEADER;
952: }
953:
954: r->invalid_header = 1;
955:
956: break;
957:
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;
978: }
979: break;
980:
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;
997: }
998: break;
999:
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;
1015: }
1016: break;
1017:
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;
1026: }
1027: break;
1028:
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;
1038: }
1039: break;
1040:
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;
1048: }
1049: }
1050: }
1051:
1052: b->pos = p;
1053: r->state = state;
1054: r->header_hash = hash;
1055: r->lowcase_index = i;
1056:
1057: return NGX_AGAIN;
1058:
1059: done:
1060:
1061: b->pos = p + 1;
1062: r->state = sw_start;
1063: r->header_hash = hash;
1064: r->lowcase_index = i;
1065:
1066: return NGX_OK;
1067:
1068: header_done:
1069:
1070: b->pos = p + 1;
1071: r->state = sw_start;
1072:
1073: return NGX_HTTP_PARSE_HEADER_DONE;
1074: }
1075:
1076:
1077: ngx_int_t
1078: ngx_http_parse_uri(ngx_http_request_t *r)
1079: {
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;
1087:
1088: state = sw_start;
1089:
1090: for (p = r->uri_start; p != r->uri_end; p++) {
1091:
1092: ch = *p;
1093:
1094: switch (state) {
1095:
1096: case sw_start:
1097:
1098: if (ch != '/') {
1099: return NGX_ERROR;
1100: }
1101:
1102: state = sw_after_slash_in_uri;
1103: break;
1104:
1105: /* check "/.", "//", "%", and "\" (Win32) in URI */
1106: case sw_after_slash_in_uri:
1107:
1108: if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1109: state = sw_check_uri;
1110: break;
1111: }
1112:
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;
1150: }
1151: break;
1152:
1153: /* check "/", "%" and "\" (Win32) in URI */
1154: case sw_check_uri:
1155:
1156: if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1157: break;
1158: }
1159:
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;
1167: }
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;
1199: }
1200: break;
1201:
1202: /* URI */
1203: case sw_uri:
1204:
1205: if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1206: break;
1207: }
1208:
1209: switch (ch) {
1210: case ' ':
1211: r->space_in_uri = 1;
1212: break;
1213: case '#':
1214: r->complex_uri = 1;
1215: break;
1216: }
1217: break;
1218: }
1219: }
1220:
1221: return NGX_OK;
1222: }
1223:
1224:
1225: ngx_int_t
1226: ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
1227: {
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;
1237:
1238: #if (NGX_SUPPRESS_WARN)
1239: decoded = '\0';
1240: quoted_state = sw_usual;
1241: #endif
1242:
1243: state = sw_usual;
1244: p = r->uri_start;
1245: u = r->uri.data;
1246: r->uri_ext = NULL;
1247: r->args_start = NULL;
1248:
1249: ch = *p++;
1250:
1251: while (p <= r->uri_end) {
1252:
1253: /*
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
1257: */
1258:
1259: ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1260: "s:%d in:'%Xd:%c', out:'%c'", state, ch, ch, *u);
1261:
1262: switch (state) {
1263:
1264: case sw_usual:
1265:
1266: if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1267: *u++ = ch;
1268: ch = *p++;
1269: break;
1270: }
1271:
1272: switch(ch) {
1273: #if (NGX_WIN32)
1274: case '\\':
1275: if (u - 2 >= r->uri.data
1276: && *(u - 1) == '.' && *(u - 2) != '.')
1277: {
1278: u--;
1279: }
1280:
1281: r->uri_ext = NULL;
1282:
1283: if (p == r->uri_start + r->uri.len) {
1284:
1285: /*
1286: * we omit the last "\" to cause redirect because
1287: * the browsers do not treat "\" as "/" in relative URL path
1288: */
1289:
1290: break;
1291: }
1292:
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) != '.')
1301: {
1302: u--;
1303: }
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;
1328: }
1329:
1330: ch = *p++;
1331: break;
1332:
1333: case sw_slash:
1334:
1335: if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1336: state = sw_usual;
1337: *u++ = ch;
1338: ch = *p++;
1339: break;
1340: }
1341:
1342: switch(ch) {
1343: #if (NGX_WIN32)
1344: case '\\':
1345: break;
1346: #endif
1347: case '/':
1348: if (!merge_slashes) {
1349: *u++ = ch;
1350: }
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;
1371: }
1372:
1373: ch = *p++;
1374: break;
1375:
1376: case sw_dot:
1377:
1378: if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1379: state = sw_usual;
1380: *u++ = ch;
1381: ch = *p++;
1382: break;
1383: }
1384:
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;
1412: }
1413:
1414: ch = *p++;
1415: break;
1416:
1417: case sw_dot_dot:
1418:
1419: if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1420: state = sw_usual;
1421: *u++ = ch;
1422: ch = *p++;
1423: break;
1424: }
1425:
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;
1436: }
1437: if (*u == '/') {
1438: u++;
1439: break;
1440: }
1441: u--;
1442: }
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;
1459: }
1460:
1461: ch = *p++;
1462: break;
1463:
1464: case sw_quoted:
1465: r->quoted_uri = 1;
1466:
1467: if (ch >= '0' && ch <= '9') {
1468: decoded = (u_char) (ch - '0');
1469: state = sw_quoted_second;
1470: ch = *p++;
1471: break;
1472: }
1473:
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;
1480: }
1481:
1482: return NGX_HTTP_PARSE_INVALID_REQUEST;
1483:
1484: case sw_quoted_second:
1485: if (ch >= '0' && ch <= '9') {
1486: ch = (u_char) ((decoded << 4) + ch - '0');
1487:
1488: if (ch == '%' || ch == '#') {
1489: state = sw_usual;
1490: *u++ = ch;
1491: ch = *p++;
1492: break;
1493:
1494: } else if (ch == '\0') {
1495: return NGX_HTTP_PARSE_INVALID_REQUEST;
1496: }
1497:
1498: state = quoted_state;
1499: break;
1500: }
1501:
1502: c = (u_char) (ch | 0x20);
1503: if (c >= 'a' && c <= 'f') {
1504: ch = (u_char) ((decoded << 4) + c - 'a' + 10);
1505:
1506: if (ch == '?') {
1507: state = sw_usual;
1508: *u++ = ch;
1509: ch = *p++;
1510: break;
1511:
1512: } else if (ch == '+') {
1513: r->plus_in_uri = 1;
1514: }
1515:
1516: state = quoted_state;
1517: break;
1518: }
1519:
1520: return NGX_HTTP_PARSE_INVALID_REQUEST;
1521: }
1522: }
1523:
1524: done:
1525:
1526: r->uri.len = u - r->uri.data;
1527:
1528: if (r->uri_ext) {
1529: r->exten.len = u - r->uri_ext;
1530: r->exten.data = r->uri_ext;
1531: }
1532:
1533: r->uri_ext = NULL;
1534:
1535: return NGX_OK;
1536:
1537: args:
1538:
1539: while (p < r->uri_end) {
1540: if (*p++ != '#') {
1541: continue;
1542: }
1543:
1544: r->args.len = p - 1 - r->args_start;
1545: r->args.data = r->args_start;
1546: r->args_start = NULL;
1547:
1548: break;
1549: }
1550:
1551: r->uri.len = u - r->uri.data;
1552:
1553: if (r->uri_ext) {
1554: r->exten.len = u - r->uri_ext;
1555: r->exten.data = r->uri_ext;
1556: }
1557:
1558: r->uri_ext = NULL;
1559:
1560: return NGX_OK;
1561: }
1562:
1563:
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)
1567: {
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;
1585:
1586: state = r->state;
1587:
1588: for (p = b->pos; p < b->last; p++) {
1589: ch = *p;
1590:
1591: switch (state) {
1592:
1593: /* "HTTP/" */
1594: case sw_start:
1595: switch (ch) {
1596: case 'H':
1597: state = sw_H;
1598: break;
1599: default:
1600: return NGX_ERROR;
1601: }
1602: break;
1603:
1604: case sw_H:
1605: switch (ch) {
1606: case 'T':
1607: state = sw_HT;
1608: break;
1609: default:
1610: return NGX_ERROR;
1611: }
1612: break;
1613:
1614: case sw_HT:
1615: switch (ch) {
1616: case 'T':
1617: state = sw_HTT;
1618: break;
1619: default:
1620: return NGX_ERROR;
1621: }
1622: break;
1623:
1624: case sw_HTT:
1625: switch (ch) {
1626: case 'P':
1627: state = sw_HTTP;
1628: break;
1629: default:
1630: return NGX_ERROR;
1631: }
1632: break;
1633:
1634: case sw_HTTP:
1635: switch (ch) {
1636: case '/':
1637: state = sw_first_major_digit;
1638: break;
1639: default:
1640: return NGX_ERROR;
1641: }
1642: break;
1643:
1644: /* the first digit of major HTTP version */
1645: case sw_first_major_digit:
1646: if (ch < '1' || ch > '9') {
1647: return NGX_ERROR;
1648: }
1649:
1650: r->http_major = ch - '0';
1651: state = sw_major_digit;
1652: break;
1653:
1654: /* the major HTTP version or dot */
1655: case sw_major_digit:
1656: if (ch == '.') {
1657: state = sw_first_minor_digit;
1658: break;
1659: }
1660:
1661: if (ch < '0' || ch > '9') {
1662: return NGX_ERROR;
1663: }
1664:
1665: r->http_major = r->http_major * 10 + ch - '0';
1666: break;
1667:
1668: /* the first digit of minor HTTP version */
1669: case sw_first_minor_digit:
1670: if (ch < '0' || ch > '9') {
1671: return NGX_ERROR;
1672: }
1673:
1674: r->http_minor = ch - '0';
1675: state = sw_minor_digit;
1676: break;
1677:
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;
1683: }
1684:
1685: if (ch < '0' || ch > '9') {
1686: return NGX_ERROR;
1687: }
1688:
1689: r->http_minor = r->http_minor * 10 + ch - '0';
1690: break;
1691:
1692: /* HTTP status code */
1693: case sw_status:
1694: if (ch == ' ') {
1695: break;
1696: }
1697:
1698: if (ch < '0' || ch > '9') {
1699: return NGX_ERROR;
1700: }
1701:
1702: status->code = status->code * 10 + ch - '0';
1703:
1704: if (++status->count == 3) {
1705: state = sw_space_after_status;
1706: status->start = p - 2;
1707: }
1708:
1709: break;
1710:
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;
1727: }
1728: break;
1729:
1730: /* any text until end of line */
1731: case sw_status_text:
1732: switch (ch) {
1733: case CR:
1734: state = sw_almost_done;
1735:
1736: break;
1737: case LF:
1738: goto done;
1739: }
1740: break;
1741:
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;
1750: }
1751: }
1752: }
1753:
1754: b->pos = p;
1755: r->state = state;
1756:
1757: return NGX_AGAIN;
1758:
1759: done:
1760:
1761: b->pos = p + 1;
1762:
1763: if (status->end == NULL) {
1764: status->end = p;
1765: }
1766:
1767: status->http_version = r->http_major * 1000 + r->http_minor;
1768: r->state = sw_start;
1769:
1770: return NGX_OK;
1771: }
1772:
1773:
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)
1777: {
1778: u_char ch, *p;
1779: size_t len;
1780:
1781: len = uri->len;
1782: p = uri->data;
1783:
1784: if (len == 0 || p[0] == '?') {
1785: goto unsafe;
1786: }
1787:
1788: if (p[0] == '.' && len == 3 && p[1] == '.' && (ngx_path_separator(p[2]))) {
1789: goto unsafe;
1790: }
1791:
1792: for ( /* void */ ; len; len--) {
1793:
1794: ch = *p++;
1795:
1796: if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1797: continue;
1798: }
1799:
1800: if (ch == '?') {
1801: args->len = len - 1;
1802: args->data = p;
1803: uri->len -= len;
1804:
1805: return NGX_OK;
1806: }
1807:
1808: if (ch == '\0') {
1809: goto unsafe;
1810: }
1811:
1812: if (ngx_path_separator(ch) && len > 2) {
1813:
1814: /* detect "/../" */
1815:
1816: if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) {
1817: goto unsafe;
1818: }
1819: }
1820: }
1821:
1822: return NGX_OK;
1823:
1824: unsafe:
1825:
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);
1829: }
1830:
1831: return NGX_ERROR;
1832: }
1833:
1834:
1835: ngx_int_t
1836: ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,
1837: ngx_str_t *value)
1838: {
1839: ngx_uint_t i;
1840: u_char *start, *last, *end, ch;
1841: ngx_table_elt_t **h;
1842:
1843: h = headers->elts;
1844:
1845: for (i = 0; i < headers->nelts; i++) {
1846:
1847: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
1848: "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
1849:
1850: if (name->len > h[i]->value.len) {
1851: continue;
1852: }
1853:
1854: start = h[i]->value.data;
1855: end = h[i]->value.data + h[i]->value.len;
1856:
1857: while (start < end) {
1858:
1859: if (ngx_strncasecmp(start, name->data, name->len) != 0) {
1860: goto skip;
1861: }
1862:
1863: for (start += name->len; start < end && *start == ' '; start++) {
1864: /* void */
1865: }
1866:
1867: if (value == NULL) {
1868: if (start == end || *start == ',') {
1869: return i;
1870: }
1871:
1872: goto skip;
1873: }
1874:
1875: if (start == end || *start++ != '=') {
1876: /* the invalid header value */
1877: goto skip;
1878: }
1879:
1880: while (start < end && *start == ' ') { start++; }
1881:
1882: for (last = start; last < end && *last != ';'; last++) {
1883: /* void */
1884: }
1885:
1886: value->len = last - start;
1887: value->data = start;
1888:
1889: return i;
1890:
1891: skip:
1892:
1893: while (start < end) {
1894: ch = *start++;
1895: if (ch == ';' || ch == ',') {
1896: break;
1897: }
1898: }
1899:
1900: while (start < end && *start == ' ') { start++; }
1901: }
1902: }
1903:
1904: return NGX_DECLINED;
1905: }
1906:
1907:
1908: ngx_int_t
1909: ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
1910: {
1911: u_char *p, *last;
1912:
1913: if (r->args.len == 0) {
1914: return NGX_DECLINED;
1915: }
1916:
1917: p = r->args.data;
1918: last = p + r->args.len;
1919:
1920: for ( /* void */ ; p < last; p++) {
1921:
1922: /* we need '=' after name, so drop one char from last */
1923:
1924: p = ngx_strlcasestrn(p, last - 1, name, len - 1);
1925:
1926: if (p == NULL) {
1927: return NGX_DECLINED;
1928: }
1929:
1930: if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
1931:
1932: value->data = p + len + 1;
1933:
1934: p = ngx_strlchr(p, last, '&');
1935:
1936: if (p == NULL) {
1937: p = r->args.data + r->args.len;
1938: }
1939:
1940: value->len = p - value->data;
1941:
1942: return NGX_OK;
1943: }
1944: }
1945:
1946: return NGX_DECLINED;
1947: }
1948:
1949:
1950: void
1951: ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
1952: {
1953: u_char *p, *last;
1954:
1955: last = uri->data + uri->len;
1956:
1957: p = ngx_strlchr(uri->data, last, '?');
1958:
1959: if (p) {
1960: uri->len = p - uri->data;
1961: p++;
1962: args->len = last - p;
1963: args->data = p;
1964:
1965: } else {
1966: args->len = 0;
1967: }
1968: }
1969:
1970:
1971: ngx_int_t
1972: ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
1973: ngx_http_chunked_t *ctx)
1974: {
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;
1992:
1993: state = ctx->state;
1994:
1995: if (state == sw_chunk_data && ctx->size == 0) {
1996: state = sw_after_data;
1997: }
1998:
1999: rc = NGX_AGAIN;
2000:
2001: for (pos = b->pos; pos < b->last; pos++) {
2002:
2003: ch = *pos;
2004:
2005: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2006: "http chunked byte: %02Xd s:%d", ch, state);
2007:
2008: switch (state) {
2009:
2010: case sw_chunk_start:
2011: if (ch >= '0' && ch <= '9') {
2012: state = sw_chunk_size;
2013: ctx->size = ch - '0';
2014: break;
2015: }
2016:
2017: c = (u_char) (ch | 0x20);
2018:
2019: if (c >= 'a' && c <= 'f') {
2020: state = sw_chunk_size;
2021: ctx->size = c - 'a' + 10;
2022: break;
2023: }
2024:
2025: goto invalid;
2026:
2027: case sw_chunk_size:
2028: if (ch >= '0' && ch <= '9') {
2029: ctx->size = ctx->size * 16 + (ch - '0');
2030: break;
2031: }
2032:
2033: c = (u_char) (ch | 0x20);
2034:
2035: if (c >= 'a' && c <= 'f') {
2036: ctx->size = ctx->size * 16 + (c - 'a' + 10);
2037: break;
2038: }
2039:
2040: if (ctx->size == 0) {
2041:
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;
2056: }
2057:
2058: break;
2059: }
2060:
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;
2075: }
2076:
2077: break;
2078:
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;
2086: }
2087: break;
2088:
2089: case sw_chunk_extension_almost_done:
2090: if (ch == LF) {
2091: state = sw_chunk_data;
2092: break;
2093: }
2094: goto invalid;
2095:
2096: case sw_chunk_data:
2097: rc = NGX_OK;
2098: goto data;
2099:
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;
2107: }
2108: break;
2109:
2110: case sw_after_data_almost_done:
2111: if (ch == LF) {
2112: state = sw_chunk_start;
2113: break;
2114: }
2115: goto invalid;
2116:
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;
2124: }
2125: break;
2126:
2127: case sw_last_chunk_extension_almost_done:
2128: if (ch == LF) {
2129: state = sw_trailer;
2130: break;
2131: }
2132: goto invalid;
2133:
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;
2143: }
2144: break;
2145:
2146: case sw_trailer_almost_done:
2147: if (ch == LF) {
2148: goto done;
2149: }
2150: goto invalid;
2151:
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;
2159: }
2160: break;
2161:
2162: case sw_trailer_header_almost_done:
2163: if (ch == LF) {
2164: state = sw_trailer;
2165: break;
2166: }
2167: goto invalid;
2168:
2169: }
2170: }
2171:
2172: data:
2173:
2174: ctx->state = state;
2175: b->pos = pos;
2176:
2177: switch (state) {
2178:
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;
2209:
2210: }
2211:
2212: if (ctx->size < 0 || ctx->length < 0) {
2213: goto invalid;
2214: }
2215:
2216: return rc;
2217:
2218: done:
2219:
2220: ctx->state = 0;
2221: b->pos = pos + 1;
2222:
2223: return NGX_DONE;
2224:
2225: invalid:
2226:
2227: return NGX_ERROR;
2228: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>