Annotation of embedaddon/nginx/src/http/ngx_http_parse.c, revision 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>