1 module yu.tools.http1xparser.parser; 2 3 import yu.tools.http1xparser.default_; 4 import yu.tools.http1xparser.url; 5 6 /** ubyte[] 为传过去字段里的位置引用,没有数据拷贝,自己使用的时候注意拷贝数据, 7 bool 此段数据是否完结,可能只是数据的一部分。 8 */ 9 10 alias CallBackData = void delegate(ref HTTP1xParser, ubyte[], bool); 11 alias CallBackNotify = void delegate(ref HTTP1xParser); 12 13 @trusted struct HTTP1xParser 14 { 15 @disable this(this); 16 17 this(HTTPType ty, uint maxHeaderSize = 4096) nothrow 18 { 19 rest(ty, maxHeaderSize); 20 } 21 22 pragma(inline, true) @property type() nothrow 23 { 24 return _type; 25 } 26 27 pragma(inline, true) @property isUpgrade() nothrow 28 { 29 return _upgrade; 30 } 31 32 pragma(inline, true) @property contentLength() nothrow 33 { 34 return _contentLength; 35 } 36 37 pragma(inline, true) @property isChunked() nothrow 38 { 39 return (_flags & HTTPParserFlags.F_CHUNKED) == 0 ? false : true; 40 } 41 42 pragma(inline, true) @property method() nothrow 43 { 44 return _method; 45 } 46 47 pragma(inline, true) @property methodString() nothrow 48 { 49 return method_strings[_method]; 50 } 51 52 //版本号首位 53 pragma(inline, true) @property major() nothrow 54 { 55 return _httpMajor; 56 } 57 58 //版本号末尾 59 pragma(inline, true) @property minor() nothrow 60 { 61 return _httpMinor; 62 } 63 64 pragma(inline, true) @property statusCode()nothrow {return _statusCode;} 65 66 pragma(inline, true) @property handleIng() nothrow 67 { 68 return _isHandle; 69 } 70 71 pragma(inline, true) //will throw Http1xParserStopExcetion 72 @property stopNow() nothrow 73 { 74 _isHandle = false; 75 } 76 77 pragma(inline, true) @property skipBody() nothrow 78 { 79 return _skipBody; 80 } 81 82 pragma(inline) @property skipBody(bool skip) nothrow 83 { 84 return _skipBody = skip; 85 } 86 87 pragma(inline, true) @property keepalive() nothrow 88 { 89 return _keepAlive; 90 } 91 92 /** 回调函数指定 */ 93 pragma(inline, true) @property onMessageBegin(CallBackNotify cback) nothrow 94 { 95 _onMessageBegin = cback; 96 } 97 98 pragma(inline, true) @property onMessageComplete(CallBackNotify cback) nothrow 99 { 100 _onMessageComplete = cback; 101 } 102 103 pragma(inline, true) @property onHeaderComplete(CallBackNotify cback) nothrow 104 { 105 _onHeadersComplete = cback; 106 } 107 108 pragma(inline, true) @property onChunkHeader(CallBackNotify cback) nothrow 109 { 110 _onChunkHeader = cback; 111 } 112 113 pragma(inline, true) @property onChunkComplete(CallBackNotify cback) nothrow 114 { 115 _onChunkComplete = cback; 116 } 117 118 pragma(inline, true) @property onUrl(CallBackData cback) nothrow 119 { 120 _onUrl = cback; 121 } 122 123 pragma(inline, true) @property onStatus(CallBackData cback) nothrow 124 { 125 _onStatus = cback; 126 } 127 128 pragma(inline, true) @property onHeaderField(CallBackData cback) nothrow 129 { 130 _onHeaderField = cback; 131 } 132 133 pragma(inline, true) @property onHeaderValue(CallBackData cback) nothrow 134 { 135 _onHeaderValue = cback; 136 } 137 138 pragma(inline, true) @property onBody(CallBackData cback) nothrow 139 { 140 _onBody = cback; 141 } 142 143 void rest(HTTPType ty, uint maxHeaderSize = 4096) nothrow 144 { 145 type = ty; 146 _maxHeaderSize = maxHeaderSize; 147 _state = (type == HTTPType.REQUEST ? HTTPParserState.s_start_req : (type == HTTPType.RESPONSE 148 ? HTTPParserState.s_start_res : HTTPParserState.s_start_req_or_res)); 149 _httpErrno = HTTPParserErrno.HPE_OK; 150 _flags = HTTPParserFlags.F_ZERO; 151 _isHandle = false; 152 _skipBody = false; 153 _keepAlive = 0x00; 154 } 155 156 public: 157 158 pragma(inline, true) bool bodyIsFinal() nothrow 159 { 160 return _state == HTTPParserState.s_message_done; 161 } 162 163 ulong httpParserExecute(ubyte[] data) 164 { 165 _isHandle = true; 166 scope (exit) 167 _isHandle = false; 168 ubyte c, ch; 169 byte unhexVal; 170 size_t mHeaderFieldMark = size_t.max; 171 size_t mHeaderValueMark = size_t.max; 172 size_t mUrlMark = size_t.max; 173 size_t mBodyMark = size_t.max; 174 size_t mStatusMark = size_t.max; 175 size_t maxP = cast(long) data.length; 176 size_t p = 0; 177 if (_httpErrno != HTTPParserErrno.HPE_OK) 178 return 0; 179 if (data.length == 0) 180 { 181 switch (_state) with (HTTPParserState) 182 { 183 case s_body_identity_eof: 184 /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if 185 * we got paused. 186 */ 187 mixin(CALLBACK_NOTIFY_NOADVANCE("MessageComplete")); 188 return 0; 189 190 case s_dead: 191 case s_start_req_or_res: 192 case s_start_res: 193 case s_start_req: 194 return 0; 195 196 default: 197 _httpErrno = HTTPParserErrno.HPE_INVALID_EOF_STATE; 198 return 1; 199 } 200 } 201 202 if (_state == HTTPParserState.s_header_field) 203 mHeaderFieldMark = 0; 204 if (_state == HTTPParserState.s_header_value) 205 mHeaderValueMark = 0; 206 switch (_state) with (HTTPParserState) 207 { 208 case s_req_path: 209 case s_req_schema: 210 case s_req_schema_slash: 211 case s_req_schema_slash_slash: 212 case s_req_server_start: 213 case s_req_server: 214 case s_req_server_with_at: 215 case s_req_query_string_start: 216 case s_req_query_string: 217 case s_req_fragment_start: 218 case s_req_fragment: 219 mUrlMark = 0; 220 break; 221 case s_res_status: 222 mStatusMark = 0; 223 break; 224 default: 225 break; 226 } 227 for (; p < maxP; ++p) 228 with (HTTPParserErrno) 229 { 230 ch = data[p]; 231 if (_state <= HTTPParserState.s_headers_done) 232 { 233 _nread += 1; 234 if (_nread > _maxHeaderSize) 235 throw new Http1xParserExcetion(HPE_HEADER_OVERFLOW); 236 } 237 while (true) 238 { 239 switch (_state) with (HTTPParserState) 240 { 241 case s_dead: 242 /* this _state is used after a 'Connection: close' message 243 * the parser will error out if it reads another message 244 */ 245 if (ch == CR || ch == LF) 246 break; 247 else 248 throw new Http1xParserExcetion(HPE_CLOSED_CONNECTION); 249 case s_start_req_or_res: 250 { 251 if (ch == CR || ch == LF) 252 break; 253 _flags = HTTPParserFlags.F_ZERO; 254 _contentLength = ulong.max; 255 256 if (ch == 'H') 257 { 258 _state = s_res_or_resp_H; 259 mixin(CALLBACK_NOTIFY("MessageBegin")); // 开始处理 260 } 261 else 262 { 263 type = HTTPType.REQUEST; 264 _state = s_start_req; 265 continue; 266 } 267 break; 268 } 269 case s_res_or_resp_H: 270 if (ch == 'T') 271 { 272 type = HTTPType.RESPONSE; 273 _state = s_res_HT; 274 } 275 else 276 { 277 if (ch != 'E') 278 throw new Http1xParserExcetion(HPE_INVALID_CONSTANT); 279 280 type = HTTPType.REQUEST; 281 _method = HTTPMethod.HEAD; 282 _index = 2; 283 _state = s_req_method; 284 } 285 break; 286 287 case s_start_res: 288 { 289 _flags = HTTPParserFlags.F_ZERO; 290 _contentLength = ulong.max; 291 switch (ch) 292 { 293 case 'H': 294 _state = s_res_H; 295 break; 296 297 case CR: 298 case LF: 299 break; 300 301 default: 302 throw new Http1xParserExcetion(HPE_INVALID_CONSTANT); 303 } 304 mixin(CALLBACK_NOTIFY("MessageBegin")); 305 } 306 break; 307 case s_res_H: 308 STRICT_CHECK(ch != 'T'); 309 _state = s_res_HT; 310 break; 311 312 case s_res_HT: 313 STRICT_CHECK(ch != 'T'); 314 _state = s_res_HTT; 315 break; 316 317 case s_res_HTT: 318 STRICT_CHECK(ch != 'P'); 319 _state = s_res_HTTP; 320 break; 321 322 case s_res_HTTP: 323 STRICT_CHECK(ch != '/'); 324 _state = s_res_first_http_major; 325 break; 326 327 case s_res_first_http_major: 328 if (ch < '0' || ch > '9') 329 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 330 331 _httpMajor = cast(ushort)(ch - '0'); 332 _state = HTTPParserState.s_res_http_major; 333 break; 334 335 /* major HTTP version or dot */ 336 case s_res_http_major: 337 { 338 if (ch == '.') 339 { 340 _state = s_res_first_http_minor; 341 break; 342 } 343 if (!mixin(IS_NUM("ch"))) 344 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 345 346 _httpMajor *= 10; 347 _httpMajor += ch - '0'; 348 349 if (_httpMajor > 999) 350 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 351 } 352 break; 353 354 /* first digit of minor HTTP version */ 355 case s_res_first_http_minor: 356 if (!mixin(IS_NUM("ch"))) 357 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 358 359 _httpMinor = cast(ushort)(ch - '0'); 360 _state = s_res_http_minor; 361 break; 362 363 /* minor HTTP version or end of request line */ 364 case s_res_http_minor: 365 { 366 if (ch == ' ') 367 { 368 _state = s_res_first_status_code; 369 break; 370 } 371 372 if (!mixin(IS_NUM("ch"))) 373 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 374 _httpMinor *= 10; 375 _httpMinor += ch - '0'; 376 377 if (_httpMinor > 999) 378 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 379 } 380 break; 381 382 case s_res_first_status_code: 383 { 384 if (!mixin(IS_NUM("ch"))) 385 { 386 if (ch == ' ') 387 break; 388 throw new Http1xParserExcetion(HPE_INVALID_STATUS); 389 } 390 _statusCode = ch - '0'; 391 _state = s_res_status_code; 392 } 393 break; 394 395 case s_res_status_code: 396 { 397 if (!mixin(IS_NUM("ch"))) 398 { 399 switch (ch) 400 { 401 case ' ': 402 _state = s_res_status_start; 403 break; 404 case CR: 405 _state = s_res_line_almost_done; 406 break; 407 case LF: 408 _state = s_header_field_start; 409 break; 410 default: 411 throw new Http1xParserExcetion(HPE_INVALID_STATUS); 412 } 413 break; 414 } 415 416 _statusCode *= 10; 417 _statusCode += ch - '0'; 418 419 if (_statusCode > 999) 420 throw new Http1xParserExcetion(HPE_INVALID_STATUS); 421 } 422 break; 423 424 case s_res_status_start: 425 { 426 if (ch == CR) 427 { 428 _state = s_res_line_almost_done; 429 break; 430 } 431 432 if (ch == LF) 433 { 434 _state = s_header_field_start; 435 break; 436 } 437 438 //MARK(status); 439 if (mStatusMark == size_t.max) 440 mStatusMark = p; 441 _state = s_res_status; 442 _index = 0; 443 } 444 break; 445 446 case s_res_status: 447 if (ch == CR) 448 { 449 _state = s_res_line_almost_done; 450 mixin(CALLBACK_DATA("Status")); 451 break; 452 } 453 454 if (ch == LF) 455 { 456 _state = s_header_field_start; 457 mixin(CALLBACK_DATA("Status")); 458 break; 459 } 460 461 break; 462 463 case s_res_line_almost_done: 464 STRICT_CHECK(ch != LF); 465 _state = s_header_field_start; 466 break; 467 468 case s_start_req: 469 { 470 if (ch == CR || ch == LF) 471 break; 472 _flags = HTTPParserFlags.F_ZERO; 473 _contentLength = ulong.max; 474 475 if (!mixin(IS_ALPHA("ch"))) 476 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 477 478 _index = 1; 479 switch (ch) with (HTTPMethod) 480 { 481 case 'A': 482 _method = ACL; 483 break; 484 case 'B': 485 _method = BIND; 486 break; 487 case 'C': 488 _method = CONNECT; /* or COPY, CHECKOUT */ break; 489 case 'D': 490 _method = DELETE; 491 break; 492 case 'G': 493 _method = GET; 494 break; 495 case 'H': 496 _method = HEAD; 497 break; 498 case 'L': 499 _method = LOCK; /* or LINK */ break; 500 case 'M': 501 _method = MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; 502 case 'N': 503 _method = NOTIFY; 504 break; 505 case 'O': 506 _method = OPTIONS; 507 break; 508 case 'P': 509 _method = POST; 510 /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ 511 break; 512 case 'R': 513 _method = REPORT; /* or REBIND */ break; 514 case 'S': 515 _method = SUBSCRIBE; /* or SEARCH */ break; 516 case 'T': 517 _method = TRACE; 518 break; 519 case 'U': 520 _method = UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break; 521 default: 522 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 523 } 524 _state = HTTPParserState.s_req_method; 525 mixin(CALLBACK_NOTIFY("MessageBegin")); 526 } 527 break; 528 529 case s_req_method: 530 { 531 if (ch == '\0') 532 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 533 534 string matcher = method_strings[_method]; 535 if (ch == ' ' && matcher.length == _index) 536 _state = HTTPParserState.s_req_spaces_before_url; 537 else if (ch == matcher[_index]) 538 { 539 /* nada */ 540 } 541 else if (_method == HTTPMethod.CONNECT) 542 { 543 if (_index == 1 && ch == 'H') 544 _method = HTTPMethod.CHECKOUT; 545 else if (_index == 2 && ch == 'P') 546 _method = HTTPMethod.COPY; 547 else 548 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 549 } 550 else if (_method == HTTPMethod.MKCOL) 551 { 552 if (_index == 1 && ch == 'O') 553 _method = HTTPMethod.MOVE; 554 else if (_index == 1 && ch == 'E') 555 _method = HTTPMethod.MERGE; 556 else if (_index == 1 && ch == '-') 557 _method = HTTPMethod.MSEARCH; 558 else if (_index == 2 && ch == 'A') 559 _method = HTTPMethod.MKACTIVITY; 560 else if (_index == 3 && ch == 'A') 561 _method = HTTPMethod.MKCALENDAR; 562 else 563 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 564 } 565 else if (_method == HTTPMethod.SUBSCRIBE) 566 { 567 if (_index == 1 && ch == 'E') 568 _method = HTTPMethod.SEARCH; 569 else 570 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 571 } 572 else if (_method == HTTPMethod.REPORT) 573 { 574 if (_index == 2 && ch == 'B') 575 _method = HTTPMethod.REBIND; 576 else 577 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 578 } 579 else if (_index == 1) 580 { 581 if (_method == HTTPMethod.POST) 582 { 583 if (ch == 'R') 584 _method = HTTPMethod.PROPFIND; /* or HTTP_PROPPATCH */ 585 else if (ch == 'U') 586 _method = HTTPMethod.PUT; /* or HTTP_PURGE */ 587 else if (ch == 'A') 588 _method = HTTPMethod.PATCH; 589 else 590 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 591 } 592 else if (_method == HTTPMethod.LOCK) 593 { 594 if (ch == 'I') 595 _method = HTTPMethod.LINK; 596 else 597 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 598 } 599 } 600 else if (_index == 2) 601 { 602 if (_method == HTTPMethod.PUT) 603 { 604 if (ch == 'R') 605 _method = HTTPMethod.PURGE; 606 else 607 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 608 } 609 else if (_method == HTTPMethod.UNLOCK) 610 { 611 if (ch == 'S') 612 _method = HTTPMethod.UNSUBSCRIBE; 613 else if (ch == 'B') 614 _method = HTTPMethod.UNBIND; 615 else 616 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 617 } 618 else 619 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 620 } 621 else if (_index == 4 && _method == HTTPMethod.PROPFIND && ch == 'P') 622 { 623 _method = HTTPMethod.PROPPATCH; 624 } 625 else if (_index == 3 && _method == HTTPMethod.UNLOCK && ch == 'I') 626 { 627 _method = HTTPMethod.UNLINK; 628 } 629 else 630 throw new Http1xParserExcetion(HPE_INVALID_METHOD); 631 ++_index; 632 } 633 break; 634 635 case s_req_spaces_before_url: 636 { 637 if (ch == ' ') 638 break; 639 if (mUrlMark == size_t.max) 640 mUrlMark = p; 641 if (_method == HTTPMethod.CONNECT) 642 _state = s_req_server_start; 643 _state = parseURLchar(_state, ch); 644 if (_state == s_dead) 645 throw new Http1xParserExcetion(HPE_INVALID_URL); 646 } 647 break; 648 case s_req_schema: 649 case s_req_schema_slash: 650 case s_req_schema_slash_slash: 651 case s_req_server_start: 652 { 653 switch (ch) 654 { 655 case ' ': /* No whitespace allowed here */ 656 case CR: 657 case LF: 658 throw new Http1xParserExcetion(HPE_INVALID_URL); 659 default: 660 _state = parseURLchar(_state, ch); 661 if (_state == s_dead) 662 throw new Http1xParserExcetion(HPE_INVALID_URL); 663 } 664 } 665 break; 666 case s_req_server: 667 case s_req_server_with_at: 668 case s_req_path: 669 case s_req_query_string_start: 670 case s_req_query_string: 671 case s_req_fragment_start: 672 case s_req_fragment: 673 { 674 switch (ch) 675 { 676 case ' ': 677 _state = s_req_http_start; 678 mixin(CALLBACK_DATA("Url")); 679 break; 680 case CR: 681 case LF: 682 _httpMajor = 0; 683 _httpMinor = 9; 684 _state = (ch == CR) ? s_req_line_almost_done : s_header_field_start; 685 mixin(CALLBACK_DATA("Url")); 686 break; 687 default: 688 _state = parseURLchar(_state, ch); 689 if (_state == s_dead) 690 throw new Http1xParserExcetion(HPE_INVALID_URL); 691 } 692 } 693 break; 694 case s_req_http_start: 695 switch (ch) 696 { 697 case 'H': 698 _state = s_req_http_H; 699 break; 700 case ' ': 701 break; 702 default: 703 throw new Http1xParserExcetion(HPE_INVALID_CONSTANT); 704 } 705 break; 706 707 case s_req_http_H: 708 STRICT_CHECK(ch != 'T'); 709 _state = s_req_http_HT; 710 break; 711 712 case s_req_http_HT: 713 STRICT_CHECK(ch != 'T'); 714 _state = s_req_http_HTT; 715 break; 716 717 case s_req_http_HTT: 718 STRICT_CHECK(ch != 'P'); 719 _state = s_req_http_HTTP; 720 break; 721 722 case s_req_http_HTTP: 723 STRICT_CHECK(ch != '/'); 724 _state = s_req_first_http_major; 725 break; 726 727 /* first digit of major HTTP version */ 728 case s_req_first_http_major: 729 if (ch < '1' || ch > '9') 730 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 731 732 _httpMajor = cast(ushort)(ch - '0'); 733 _state = s_req_http_major; 734 break; 735 736 /* major HTTP version or dot */ 737 case s_req_http_major: 738 { 739 if (ch == '.') 740 { 741 _state = s_req_first_http_minor; 742 break; 743 } 744 745 if (!mixin(IS_NUM("ch"))) 746 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 747 748 _httpMajor *= 10; 749 _httpMajor += ch - '0'; 750 751 if (_httpMajor > 999) 752 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 753 } 754 break; 755 /* first digit of minor HTTP version */ 756 case s_req_first_http_minor: 757 if (!mixin(IS_NUM("ch"))) 758 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 759 760 _httpMinor = cast(ushort)(ch - '0'); 761 _state = s_req_http_minor; 762 break; 763 764 /* minor HTTP version or end of request line */ 765 case s_req_http_minor: 766 { 767 if (ch == CR) 768 { 769 _state = s_req_line_almost_done; 770 break; 771 } 772 773 if (ch == LF) 774 { 775 _state = s_header_field_start; 776 break; 777 } 778 779 /* XXX allow spaces after digit? */ 780 781 if (!mixin(IS_NUM("ch"))) 782 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 783 784 _httpMinor *= 10; 785 _httpMinor += ch - '0'; 786 787 if (_httpMinor > 999) 788 throw new Http1xParserExcetion(HPE_INVALID_VERSION); 789 } 790 break; 791 792 /* end of request line */ 793 case s_req_line_almost_done: 794 { 795 if (ch != LF) 796 throw new Http1xParserExcetion(HPE_LF_EXPECTED); 797 798 _state = s_header_field_start; 799 } 800 break; 801 case s_header_field_start: 802 { 803 if (ch == CR) 804 { 805 _state = s_headers_almost_done; 806 break; 807 } 808 809 if (ch == LF) 810 { 811 /* they might be just sending \n instead of \r\n so this would be 812 * the second \n to denote the end of headers*/ 813 _state = s_headers_almost_done; 814 continue; 815 } 816 817 c = tokens[ch]; 818 819 if (!c) 820 throw new Http1xParserExcetion(HPE_INVALID_HEADER_TOKEN); 821 822 if (mHeaderFieldMark == size_t.max) 823 mHeaderFieldMark = p; 824 825 _index = 0; 826 _state = s_header_field; 827 828 switch (c) with (HTTPParserHeaderstates) 829 { 830 case 'c': 831 _headerState = h_C; 832 break; 833 834 case 'p': 835 _headerState = h_matching_proxy_connection; 836 break; 837 838 case 't': 839 _headerState = h_matching_transfer_encoding; 840 break; 841 842 case 'u': 843 _headerState = h_matching_upgrade; 844 break; 845 846 default: 847 _headerState = h_general; 848 break; 849 } 850 } 851 break; 852 case s_header_field: 853 { 854 const long start = p; 855 for (; p < maxP; p++) 856 { 857 ch = data[p]; 858 c = tokens[ch]; 859 if (!c) 860 break; 861 862 switch (_headerState) with (HTTPParserHeaderstates) 863 { 864 case h_general: 865 break; 866 867 case h_C: 868 _index++; 869 _headerState = (c == 'o' ? h_CO : h_general); 870 break; 871 872 case h_CO: 873 _index++; 874 _headerState = (c == 'n' ? h_CON : h_general); 875 break; 876 877 case h_CON: 878 _index++; 879 switch (c) 880 { 881 case 'n': 882 _headerState = h_matching_connection; 883 break; 884 case 't': 885 _headerState = h_matching_content_length; 886 break; 887 default: 888 _headerState = h_general; 889 break; 890 } 891 break; 892 /* connection */ 893 case h_matching_connection: 894 _index++; 895 if (_index > CONNECTION.length || c != CONNECTION[_index]) 896 { 897 _headerState = HTTPParserHeaderstates.h_general; 898 } 899 else if (_index == CONNECTION.length - 1) 900 { 901 _headerState = h_connection; 902 } 903 break; 904 /* proxy-connection */ 905 case h_matching_proxy_connection: 906 _index++; 907 if (_index > PROXY_CONNECTION.length 908 || c != PROXY_CONNECTION[_index]) 909 { 910 _headerState = h_general; 911 } 912 else if (_index == PROXY_CONNECTION.length) 913 { 914 _headerState = h_connection; 915 } 916 break; 917 /* content-length */ 918 case h_matching_content_length: 919 _index++; 920 if (_index > CONTENT_LENGTH.length || c != CONTENT_LENGTH[ 921 _index]) 922 { 923 _headerState = h_general; 924 } 925 else if (_index == CONTENT_LENGTH.length - 1) 926 { 927 if (_flags & HTTPParserFlags.F_CONTENTLENGTH) 928 throw new Http1xParserExcetion( 929 HPE_UNEXPECTED_CONTENT_LENGTH); 930 _headerState = HTTPParserHeaderstates.h_content_length; 931 _flags |= HTTPParserFlags.F_CONTENTLENGTH; 932 } 933 break; 934 /* transfer-encoding */ 935 case h_matching_transfer_encoding: 936 _index++; 937 if (_index > TRANSFER_ENCODING.length 938 || c != TRANSFER_ENCODING[_index]) 939 { 940 _headerState = h_general; 941 } 942 else if (_index == TRANSFER_ENCODING.length - 1) 943 { 944 _headerState = h_transfer_encoding; 945 } 946 break; 947 948 /* upgrade */ 949 950 case h_matching_upgrade: 951 _index++; 952 if (_index > UPGRADE.length || c != UPGRADE[_index]) 953 { 954 _headerState = h_general; 955 } 956 else if (_index == UPGRADE.length - 1) 957 { 958 _headerState = h_upgrade; 959 } 960 break; 961 962 case h_connection: 963 case h_content_length: 964 case h_transfer_encoding: 965 case h_upgrade: 966 if (ch != ' ') 967 _headerState = HTTPParserHeaderstates.h_general; 968 break; 969 default: 970 assert(false, "Unknown _headerState"); 971 // break; 972 } 973 } 974 _nread += (p - start); 975 if (_nread > _maxHeaderSize) 976 throw new Http1xParserExcetion(HTTPParserErrno.HPE_HEADER_OVERFLOW); 977 if (p == maxP) 978 { 979 --p; 980 } 981 else if (ch == ':') 982 { 983 _state = HTTPParserState.s_header_value_discard_ws; 984 mixin(CALLBACK_DATA("HeaderField")); 985 } 986 else 987 throw new Http1xParserExcetion(HPE_INVALID_HEADER_TOKEN); 988 } 989 break; 990 case s_header_value_discard_ws: 991 if (ch == ' ' || ch == '\t') 992 break; 993 if (ch == CR) 994 { 995 _state = s_header_value_discard_ws_almost_done; 996 break; 997 } 998 if (ch == LF) 999 { 1000 _state = s_header_value_discard_lws; 1001 break; 1002 } 1003 goto case; 1004 /* FALLTHROUGH */ 1005 case s_header_value_start: 1006 { 1007 //MARK(header_value); 1008 if (mHeaderValueMark == size_t.max) 1009 mHeaderValueMark = p; 1010 _state = s_header_value; 1011 _index = 0; 1012 1013 c = ch | 0x20; //LOWER(ch); 1014 switch (_headerState) with (HTTPParserHeaderstates) 1015 { 1016 case h_upgrade: 1017 _flags |= HTTPParserFlags.F_UPGRADE; 1018 _headerState = h_general; 1019 break; 1020 1021 case h_transfer_encoding: 1022 /* looking for 'Transfer-Encoding: chunked' */ 1023 if ('c' == c) 1024 { 1025 _headerState = h_matching_transfer_encoding_chunked; 1026 } 1027 else 1028 { 1029 _headerState = h_general; 1030 } 1031 break; 1032 1033 case h_content_length: 1034 if (!mixin(IS_NUM("ch"))) 1035 throw new Http1xParserExcetion(HPE_INVALID_CONTENT_LENGTH); 1036 _contentLength = ch - '0'; 1037 break; 1038 1039 case h_connection: 1040 /* looking for 'Connection: keep-alive' */ 1041 if (c == 'k') 1042 { 1043 _headerState = h_matching_connection_keep_alive; 1044 _keepAlive = 0x01; 1045 /* looking for 'Connection: close' */ 1046 } 1047 else if (c == 'c') 1048 { 1049 _headerState = h_matching_connection_close; 1050 _keepAlive = 0x02; 1051 } 1052 else if (c == 'u') 1053 { 1054 _headerState = h_matching_connection_upgrade; 1055 _keepAlive = 0x03; 1056 } 1057 else 1058 { 1059 _headerState = h_matching_connection_token; 1060 _keepAlive = 0x04; 1061 } 1062 break; 1063 1064 /* Multi-value `Connection` header */ 1065 case h_matching_connection_token_start: 1066 break; 1067 1068 default: 1069 _headerState = h_general; 1070 break; 1071 } 1072 } 1073 break; 1074 case s_header_value: 1075 { 1076 const long start = p; 1077 auto h_state = _headerState; 1078 for (; p < maxP; p++) 1079 { 1080 ch = data[p]; 1081 if (ch == CR) 1082 { 1083 _state = s_header_almost_done; 1084 _headerState = h_state; 1085 mixin(CALLBACK_DATA("HeaderValue")); 1086 break; 1087 } 1088 1089 if (ch == LF) 1090 { 1091 _state = s_header_almost_done; 1092 //COUNT_HEADER_SIZE(p - start); 1093 _nread += (p - start); 1094 if (_nread > _maxHeaderSize) 1095 throw new Http1xParserExcetion(HPE_HEADER_OVERFLOW); 1096 _headerState = h_state; 1097 mixin(CALLBACK_DATA_NOADVANCE("HeaderValue")); 1098 continue; 1099 } 1100 1101 if (!_lenientHttpHeaders && !(ch == CR 1102 || ch == LF || ch == 9 || (ch > 31 && ch != 127))) 1103 throw new Http1xParserExcetion(HPE_INVALID_HEADER_TOKEN); 1104 c = ch | 0x20; //LOWER(ch); 1105 switch (h_state) with (HTTPParserHeaderstates) 1106 { 1107 case h_general: 1108 { 1109 //import std.string; 1110 import core.stdc.string; 1111 1112 size_t limit = maxP - p; 1113 limit = (limit < _maxHeaderSize ? limit : _maxHeaderSize); //MIN(limit, TTPConfig.instance.MaxHeaderSize); 1114 auto str = data[p .. maxP]; 1115 auto tptr = cast(ubyte*) memchr(str.ptr, CR, str.length); 1116 auto p_cr = tptr - str.ptr; //str._indexOf(CR); // memchr(p, CR, limit); 1117 tptr = cast(ubyte*) memchr(str.ptr, LF, str.length); 1118 auto p_lf = tptr - str.ptr; //str._indexOf(LF); // memchr(p, LF, limit); 1119 ++p_cr; 1120 ++p_lf; 1121 if (p_cr > 0) 1122 { 1123 if (p_lf > 0 && p_cr >= p_lf) 1124 p += p_lf; 1125 else 1126 p += p_cr; 1127 } 1128 else if (p_lf > 0) 1129 { 1130 p += p_lf; 1131 } 1132 else 1133 { 1134 p = maxP; 1135 } 1136 p -= 2; 1137 } 1138 break; 1139 case h_connection: 1140 case h_transfer_encoding: 1141 assert(0, "Shouldn't get here."); 1142 //break; 1143 1144 case h_content_length: 1145 { 1146 ulong t; 1147 1148 if (ch == ' ') 1149 break; 1150 1151 if (!mixin(IS_NUM("ch"))) 1152 throw new Http1xParserExcetion( 1153 HPE_INVALID_CONTENT_LENGTH); 1154 1155 t = _contentLength; 1156 t *= 10; 1157 t += ch - '0'; 1158 1159 /* Overflow? Test against a conservative limit for simplicity. */ 1160 if ((ulong.max - 10) / 10 < _contentLength) 1161 throw new Http1xParserExcetion( 1162 HPE_INVALID_CONTENT_LENGTH); 1163 _contentLength = t; 1164 } 1165 break; 1166 /* Transfer-Encoding: chunked */ 1167 case h_matching_transfer_encoding_chunked: { 1168 _index++; 1169 if (_index > CHUNKED.length || c != CHUNKED[_index]) 1170 h_state = h_general; 1171 else if (_index == CHUNKED.length - 1) 1172 h_state = h_transfer_encoding_chunked; 1173 } 1174 break; 1175 1176 case HTTPParserHeaderstates.h_matching_connection_token_start: 1177 /* looking for 'Connection: keep-alive' */ 1178 if (c == 'k') 1179 h_state = h_matching_connection_keep_alive; 1180 /* looking for 'Connection: close' */ 1181 else if (c == 'c') 1182 h_state = h_matching_connection_close; 1183 else if (c == 'u') 1184 h_state = h_matching_connection_upgrade; 1185 else if (tokens[c]) 1186 h_state = h_matching_connection_token; 1187 else if (c == ' ' || c == '\t') 1188 { 1189 /* Skip lws */ 1190 } 1191 else 1192 { 1193 h_state = h_general; 1194 } 1195 break; 1196 /* looking for 'Connection: keep-alive' */ 1197 case h_matching_connection_keep_alive: 1198 _index++; 1199 if (_index > KEEP_ALIVE.length || c != KEEP_ALIVE[_index]) 1200 h_state = h_matching_connection_token; 1201 else if (_index == KEEP_ALIVE.length - 1) 1202 h_state = h_connection_keep_alive; 1203 break; 1204 1205 /* looking for 'Connection: close' */ 1206 case h_matching_connection_close: 1207 _index++; 1208 if (_index > CLOSE.length || c != CLOSE[_index]) 1209 h_state = h_matching_connection_token; 1210 else if (_index == CLOSE.length - 1) 1211 h_state = h_connection_close; 1212 break; 1213 1214 /* looking for 'Connection: upgrade' */ 1215 case h_matching_connection_upgrade: 1216 _index++; 1217 if (_index > UPGRADE.length || c != UPGRADE[_index]) 1218 h_state = h_matching_connection_token; 1219 else if (_index == UPGRADE.length - 1) 1220 h_state = h_connection_upgrade; 1221 break; 1222 1223 case h_matching_connection_token: 1224 if (ch == ',') 1225 { 1226 h_state = h_matching_connection_token_start; 1227 _index = 0; 1228 } 1229 break; 1230 1231 case h_transfer_encoding_chunked: 1232 if (ch != ' ') 1233 h_state = h_general; 1234 break; 1235 1236 case h_connection_keep_alive: 1237 case h_connection_close: 1238 case h_connection_upgrade: 1239 if (ch == ',') 1240 { 1241 if (h_state == h_connection_keep_alive) 1242 _flags |= HTTPParserFlags.F_CONNECTION_KEEP_ALIVE; 1243 else if (h_state == h_connection_close) 1244 _flags |= HTTPParserFlags.F_CONNECTION_CLOSE; 1245 else if (h_state == h_connection_upgrade) 1246 _flags |= HTTPParserFlags.F_CONNECTION_UPGRADE; 1247 h_state = h_matching_connection_token_start; 1248 _index = 0; 1249 } 1250 else if (ch != ' ') 1251 h_state = h_matching_connection_token; 1252 break; 1253 1254 default: 1255 _state = s_header_value; 1256 h_state = h_general; 1257 break; 1258 } 1259 } 1260 _headerState = h_state; 1261 1262 //COUNT_HEADER_SIZE(p - start); 1263 _nread += (p - start); 1264 if (_nread > _maxHeaderSize) 1265 throw new Http1xParserExcetion(HPE_HEADER_OVERFLOW); 1266 if (p == maxP) 1267 --p; 1268 break; 1269 } 1270 1271 case s_header_almost_done: 1272 if (ch != LF) 1273 throw new Http1xParserExcetion(HPE_LF_EXPECTED); 1274 _state = s_header_value_lws; 1275 break; 1276 case s_header_value_lws: 1277 { 1278 if (ch == ' ' || ch == '\t') 1279 { 1280 _state = s_header_value_start; 1281 continue; 1282 } 1283 1284 /* finished the header */ 1285 switch (_headerState) with (HTTPParserHeaderstates) 1286 { 1287 case h_connection_keep_alive: 1288 _flags |= HTTPParserFlags.F_CONNECTION_KEEP_ALIVE; 1289 break; 1290 case h_connection_close: 1291 _flags |= HTTPParserFlags.F_CONNECTION_CLOSE; 1292 break; 1293 case h_transfer_encoding_chunked: 1294 _flags |= HTTPParserFlags.F_CHUNKED; 1295 break; 1296 case h_connection_upgrade: 1297 _flags |= HTTPParserFlags.F_CONNECTION_UPGRADE; 1298 break; 1299 default: 1300 break; 1301 } 1302 1303 _state = s_header_field_start; 1304 continue; 1305 } 1306 1307 case s_header_value_discard_ws_almost_done: 1308 { 1309 STRICT_CHECK(ch != LF); 1310 _state = s_header_value_discard_lws; 1311 } 1312 break; 1313 1314 case s_header_value_discard_lws: 1315 { 1316 if (ch == ' ' || ch == '\t') 1317 { 1318 _state = s_header_value_discard_ws; 1319 break; 1320 } 1321 else 1322 { 1323 switch (_headerState) with (HTTPParserHeaderstates) 1324 { 1325 case h_connection_keep_alive: 1326 _flags |= HTTPParserFlags.F_CONNECTION_KEEP_ALIVE; 1327 break; 1328 case h_connection_close: 1329 _flags |= HTTPParserFlags.F_CONNECTION_CLOSE; 1330 break; 1331 case h_connection_upgrade: 1332 _flags |= HTTPParserFlags.F_CONNECTION_UPGRADE; 1333 break; 1334 case h_transfer_encoding_chunked: 1335 _flags |= HTTPParserFlags.F_CHUNKED; 1336 break; 1337 default: 1338 break; 1339 } 1340 1341 /* header value was empty */ 1342 //MARK(header_value); 1343 if (mHeaderValueMark == size_t.max) 1344 { 1345 mHeaderValueMark = p; 1346 } 1347 _state = s_header_field_start; 1348 mixin(CALLBACK_DATA_NOADVANCE("HeaderValue")); 1349 continue; 1350 } 1351 } 1352 //TODO 1353 case s_headers_almost_done: 1354 { 1355 STRICT_CHECK(ch != LF); 1356 1357 if (_flags & HTTPParserFlags.F_TRAILING) 1358 { 1359 /* End of a chunked request */ 1360 _state = s_message_done; 1361 mixin(CALLBACK_NOTIFY_NOADVANCE("ChunkComplete")); 1362 continue; 1363 } 1364 1365 /* Cannot use chunked encoding and a content-length header together 1366 per the HTTP specification. */ 1367 if ((_flags & HTTPParserFlags.F_CHUNKED) 1368 && (_flags & HTTPParserFlags.F_CONTENTLENGTH)) 1369 throw new Http1xParserExcetion(HPE_UNEXPECTED_CONTENT_LENGTH); 1370 _state = s_headers_done; 1371 1372 /* Set this here so that on_headers_complete() callbacks can see it */ 1373 _upgrade = ((_flags & (HTTPParserFlags.F_UPGRADE | HTTPParserFlags.F_CONNECTION_UPGRADE)) == ( 1374 HTTPParserFlags.F_UPGRADE | HTTPParserFlags.F_CONNECTION_UPGRADE) 1375 || _method == HTTPMethod.CONNECT); 1376 { 1377 if (_keepAlive == 0x00 && _httpMinor == 0 && _httpMajor == 1) 1378 { 1379 _keepAlive = 0x02; 1380 } 1381 else 1382 { 1383 _keepAlive = 0x01; 1384 } 1385 if (_onHeadersComplete !is null) 1386 { 1387 _onHeadersComplete(this); 1388 if (!handleIng) 1389 throw new Http1xParserStopExcetion(HPE_CB_HeadersComplete, 1390 p + 1); 1391 if (skipBody) 1392 _flags |= HTTPParserFlags.F_SKIPBODY; 1393 } 1394 } 1395 continue; 1396 } 1397 case s_headers_done: 1398 { 1399 int hasBody; 1400 STRICT_CHECK(ch != LF); 1401 1402 _nread = 0; 1403 //int chunked = _flags & HTTPParserFlags.F_CHUNKED ; 1404 //error("s_headers_done is chunked : ", chunked); 1405 hasBody = _flags & HTTPParserFlags.F_CHUNKED 1406 || (_contentLength > 0 && _contentLength != ULLONG_MAX); 1407 if (_upgrade && (_method == HTTPMethod.CONNECT 1408 || (_flags & HTTPParserFlags.F_SKIPBODY) || !hasBody)) 1409 { 1410 /* Exit, the rest of the message is in a different protocol. */ 1411 _state = mixin(NEW_MESSAGE); 1412 mixin(CALLBACK_NOTIFY("MessageComplete")); 1413 return (p + 1); 1414 } 1415 1416 if (_flags & HTTPParserFlags.F_SKIPBODY) 1417 { 1418 _state = mixin(NEW_MESSAGE); 1419 mixin(CALLBACK_NOTIFY("MessageComplete")); 1420 } 1421 else if (_flags & HTTPParserFlags.F_CHUNKED) 1422 { 1423 /* chunked encoding - ignore Content-Length header */ 1424 _state = s_chunk_size_start; 1425 } 1426 else 1427 { 1428 if (_contentLength == 0) 1429 { 1430 /* Content-Length header given but zero: Content-Length: 0\r\n */ 1431 _state = mixin(NEW_MESSAGE); 1432 mixin(CALLBACK_NOTIFY("MessageComplete")); 1433 } 1434 else if (_contentLength != ULLONG_MAX) 1435 { 1436 /* Content-Length header given and non-zero */ 1437 _state = s_body_identity; 1438 } 1439 else 1440 { 1441 if (!httpMessageNeedsEof()) 1442 { 1443 /* Assume content-length 0 - read the next */ 1444 _state = mixin(NEW_MESSAGE); 1445 mixin(CALLBACK_NOTIFY("MessageComplete")); 1446 } 1447 else 1448 { 1449 /* Read body until EOF */ 1450 _state = s_body_identity_eof; 1451 } 1452 } 1453 } 1454 break; 1455 } 1456 1457 case s_body_identity: 1458 { 1459 ulong to_read = _contentLength < cast(ulong)(maxP - p) ? _contentLength 1460 : cast(ulong)(maxP - p); 1461 1462 assert(_contentLength != 0 && _contentLength != ULLONG_MAX); 1463 1464 /* The difference between advancing _contentLength and p is because 1465 * the latter will automaticaly advance on the next loop iteration. 1466 * Further, if _contentLength ends up at 0, we want to see the last 1467 * byte again for our message complete callback. 1468 */ 1469 //MARK(body); 1470 1471 if (mBodyMark == size_t.max) 1472 mBodyMark = p; 1473 _contentLength -= to_read; 1474 p += to_read - 1; 1475 1476 if (_contentLength == 0) 1477 { 1478 _state = s_message_done; 1479 1480 /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. 1481 * 1482 * The alternative to doing this is to wait for the next byte to 1483 * trigger the data callback, just as in every other case. The 1484 * problem with this is that this makes it difficult for the test 1485 * harness to distinguish between complete-on-EOF and 1486 * complete-on-length. It's not clear that this distinction is 1487 * important for applications, but let's keep it for now. 1488 */ 1489 if (mBodyMark != size_t.max && _onBody !is null) 1490 { 1491 ubyte[] _data = data[mBodyMark .. p + 1]; 1492 _onBody(this, _data, true); 1493 if (!handleIng) 1494 throw new Http1xParserStopExcetion(HPE_CB_Body, p + 1); 1495 } 1496 mBodyMark = size_t.max; 1497 continue; 1498 } 1499 } 1500 break; 1501 /* read until EOF */ 1502 case s_body_identity_eof: 1503 //MARK(body); 1504 if (mBodyMark == size_t.max) 1505 mBodyMark = p; 1506 p = maxP - 1; 1507 break; 1508 1509 case s_message_done: 1510 _state = mixin(NEW_MESSAGE); 1511 mixin(CALLBACK_NOTIFY("MessageComplete")); 1512 if (_upgrade) /* Exit, the rest of the message is in a different protocol. */ 1513 return (p + 1); 1514 break; 1515 1516 case s_chunk_size_start: 1517 { 1518 assert(_nread == 1); 1519 assert(_flags & HTTPParserFlags.F_CHUNKED); 1520 1521 unhexVal = unhex[ch]; 1522 if (unhexVal == -1) 1523 throw new Http1xParserExcetion(HPE_INVALID_CHUNK_SIZE); 1524 1525 _contentLength = unhexVal; 1526 _state = s_chunk_size; 1527 } 1528 break; 1529 case s_chunk_size: 1530 { 1531 ulong t; 1532 1533 assert(_flags & HTTPParserFlags.F_CHUNKED); 1534 1535 if (ch == CR) 1536 { 1537 _state = s_chunk_size_almost_done; 1538 break; 1539 } 1540 1541 unhexVal = unhex[ch]; 1542 1543 if (unhexVal == -1) 1544 { 1545 if (ch == ';' || ch == ' ') 1546 { 1547 _state = s_chunk_parameters; 1548 break; 1549 } 1550 throw new Http1xParserExcetion(HPE_INVALID_CHUNK_SIZE); 1551 } 1552 1553 t = _contentLength; 1554 t *= 16; 1555 t += unhexVal; 1556 1557 /* Overflow? Test against a conservative limit for simplicity. */ 1558 if ((ULLONG_MAX - 16) / 16 < _contentLength) 1559 throw new Http1xParserExcetion(HPE_INVALID_CONTENT_LENGTH); 1560 1561 _contentLength = t; 1562 break; 1563 } 1564 1565 case s_chunk_parameters: 1566 { 1567 assert(_flags & HTTPParserFlags.F_CHUNKED); 1568 /* just ignore this shit. TODO check for overflow */ 1569 if (ch == CR) 1570 { 1571 _state = s_chunk_size_almost_done; 1572 break; 1573 } 1574 } 1575 break; 1576 1577 case s_chunk_size_almost_done: 1578 { 1579 assert(_flags & HTTPParserFlags.F_CHUNKED); 1580 STRICT_CHECK(ch != LF); 1581 1582 _nread = 0; 1583 1584 if (_contentLength == 0) 1585 { 1586 _flags |= HTTPParserFlags.F_TRAILING; 1587 _state = s_header_field_start; 1588 } 1589 else 1590 { 1591 _state = s_chunk_data; 1592 } 1593 mixin(CALLBACK_NOTIFY("ChunkHeader")); 1594 } 1595 break; 1596 case s_chunk_data: 1597 { 1598 ulong to_read = _contentLength < cast(ulong)(maxP - p) ? _contentLength 1599 : cast(ulong)(maxP - p); 1600 1601 assert(_flags & HTTPParserFlags.F_CHUNKED); 1602 assert(_contentLength != 0 && _contentLength != ULLONG_MAX); 1603 1604 /* See the explanation in s_body_identity for why the content 1605 * length and data pointers are managed this way. 1606 */ 1607 //MARK(body); 1608 if (mBodyMark == size_t.max) 1609 mBodyMark = p; 1610 _contentLength -= to_read; 1611 p += to_read - 1; 1612 1613 if (_contentLength == 0) 1614 _state = HTTPParserState.s_chunk_data_almost_done; 1615 } 1616 break; 1617 case s_chunk_data_almost_done: 1618 assert(_flags & HTTPParserFlags.F_CHUNKED); 1619 assert(_contentLength == 0); 1620 STRICT_CHECK(ch != CR); 1621 _state = s_chunk_data_done; 1622 mixin(CALLBACK_DATA("Body")); 1623 break; 1624 1625 case HTTPParserState.s_chunk_data_done: 1626 assert(_flags & HTTPParserFlags.F_CHUNKED); 1627 STRICT_CHECK(ch != LF); 1628 _nread = 0; 1629 _state = HTTPParserState.s_chunk_size_start; 1630 mixin(CALLBACK_NOTIFY("ChunkComplete")); 1631 break; 1632 1633 default: 1634 throw new Http1xParserExcetion(HPE_INVALID_INTERNAL_STATE); 1635 } 1636 break; 1637 } 1638 } 1639 1640 assert(((mHeaderFieldMark != size_t.max ? 1 1641 : 0) + (mHeaderValueMark != size_t.max ? 1 1642 : 0) + (mUrlMark != size_t.max ? 1 : 0) + (mBodyMark != size_t.max 1643 ? 1 : 0) + (mStatusMark != size_t.max ? 1 : 0)) <= 1); 1644 1645 mixin(CALLBACK_DATA_NOADVANCE("HeaderField")); //最后没找到 1646 mixin(CALLBACK_DATA_NOADVANCE("HeaderValue")); 1647 mixin(CALLBACK_DATA_NOADVANCE("Url")); 1648 mixin(CALLBACK_DATA_NOADVANCE("Body")); 1649 mixin(CALLBACK_DATA_NOADVANCE("Status")); 1650 1651 return data.length; 1652 } 1653 1654 protected: 1655 pragma(inline, true) @property type(HTTPType ty) 1656 { 1657 _type = ty; 1658 } 1659 1660 pragma(inline) bool httpMessageNeedsEof() 1661 { 1662 if (type == HTTPType.REQUEST) 1663 return false; 1664 1665 /* See RFC 2616 section 4.4 */ 1666 if (_statusCode / 100 == 1 || /* 1xx e.g. Continue */ 1667 _statusCode == 204 || /* No Content */ 1668 _statusCode == 304 1669 || /* Not Modified */ 1670 _flags & HTTPParserFlags.F_SKIPBODY) 1671 return false; /* response to a HEAD request */ 1672 1673 if ((_flags & HTTPParserFlags.F_CHUNKED) || _contentLength != ULLONG_MAX) 1674 return false; 1675 return true; 1676 } 1677 1678 pragma(inline) bool httpShouldKeepAlive() 1679 { 1680 if (_httpMajor > 0 && _httpMinor > 0) 1681 { 1682 /* HTTP/1.1 */ 1683 if (_flags & HTTPParserFlags.F_CONNECTION_CLOSE) 1684 return false; 1685 } 1686 else 1687 { 1688 /* HTTP/1.0 or earlier */ 1689 if (!(_flags & HTTPParserFlags.F_CONNECTION_KEEP_ALIVE)) 1690 return false; 1691 } 1692 return !httpMessageNeedsEof(); 1693 } 1694 1695 protected: 1696 CallBackNotify _onMessageBegin; 1697 1698 CallBackNotify _onHeadersComplete; 1699 1700 CallBackNotify _onMessageComplete; 1701 1702 CallBackNotify _onChunkHeader; 1703 1704 CallBackNotify _onChunkComplete; 1705 1706 CallBackData _onUrl; 1707 1708 CallBackData _onStatus; 1709 1710 CallBackData _onHeaderField; 1711 1712 CallBackData _onHeaderValue; 1713 1714 CallBackData _onBody; 1715 1716 private: 1717 HTTPType _type = HTTPType.BOTH; 1718 HTTPParserFlags _flags = HTTPParserFlags.F_ZERO; 1719 HTTPParserState _state = HTTPParserState.s_start_req_or_res; 1720 HTTPParserHeaderstates _headerState; 1721 uint _index; 1722 uint _lenientHttpHeaders; 1723 uint _nread; 1724 ulong _contentLength; 1725 ushort _httpMajor; 1726 ushort _httpMinor; 1727 uint _statusCode; /* responses only */ 1728 HTTPMethod _method; /* requests only */ 1729 HTTPParserErrno _httpErrno = HTTPParserErrno.HPE_OK; 1730 /* 1 = Upgrade header was present and the parser has exited because of that. 1731 * 0 = No upgrade header present. 1732 * Should be checked when http_parser_execute() returns in addition to 1733 * error checking. 1734 */ 1735 bool _upgrade; 1736 1737 bool _isHandle = false; 1738 1739 bool _skipBody = false; 1740 1741 ubyte _keepAlive = 0x00; 1742 1743 uint _maxHeaderSize = 4096; 1744 } 1745 1746 1747 1748 unittest 1749 { 1750 import std.stdio; 1751 import std.functional; 1752 import yu.exception; 1753 1754 writeln("\n\n\n"); 1755 1756 void on_message_begin(ref HTTP1xParser) 1757 { 1758 writeln("_onMessageBegin"); 1759 writeln(" "); 1760 } 1761 1762 void on_url(ref HTTP1xParser par, ubyte[] data, bool adv) 1763 { 1764 writeln("_onUrl, is NOADVANCE = ", adv); 1765 writeln("\" ", cast(string) data, " \""); 1766 writeln("HTTPMethod is = ", par.methodString); 1767 writeln(" "); 1768 } 1769 1770 void on_status(ref HTTP1xParser par, ubyte[] data, bool adv) 1771 { 1772 writeln("_onStatus, is NOADVANCE = ", adv); 1773 writeln("\" ", cast(string) data, " \""); 1774 writeln(" "); 1775 } 1776 1777 void on_header_field(ref HTTP1xParser par, ubyte[] data, bool adv) 1778 { 1779 static bool frist = true; 1780 writeln("_onHeaderField, is NOADVANCE = ", adv); 1781 writeln("len = ", data.length); 1782 writeln("\" ", cast(string) data, " \""); 1783 if (frist) 1784 { 1785 writeln("\t _httpMajor", par.major); 1786 writeln("\t _httpMinor", par.minor); 1787 frist = false; 1788 } 1789 writeln(" "); 1790 } 1791 1792 void on_header_value(ref HTTP1xParser par, ubyte[] data, bool adv) 1793 { 1794 writeln("_onHeaderValue, is NOADVANCE = ", adv); 1795 writeln("\" ", cast(string) data, " \""); 1796 writeln(" "); 1797 } 1798 1799 void on_headers_complete(ref HTTP1xParser par) 1800 { 1801 writeln("_onHeadersComplete"); 1802 writeln(" "); 1803 } 1804 1805 void on_body(ref HTTP1xParser par, ubyte[] data, bool adv) 1806 { 1807 writeln("_onBody, is NOADVANCE = ", adv); 1808 writeln("\" ", cast(string) data, " \""); 1809 writeln(" "); 1810 } 1811 1812 void on_message_complete(ref HTTP1xParser par) 1813 { 1814 writeln("_onMessageComplete"); 1815 writeln(" "); 1816 } 1817 1818 void on_chunk_header(ref HTTP1xParser par) 1819 { 1820 writeln("_onChunkHeader"); 1821 writeln(" "); 1822 } 1823 1824 void on_chunk_complete(ref HTTP1xParser par) 1825 { 1826 writeln("_onChunkComplete"); 1827 writeln(" "); 1828 } 1829 1830 string data = "GET /test HTTP/1.1\r\nUser-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\nHost:0.0.0.0=5000\r\nAccept: */*\r\n\r\n"; 1831 HTTP1xParser par = HTTP1xParser(); 1832 par.onMessageBegin = toDelegate(&on_message_begin); 1833 par.onMessageComplete = toDelegate(&on_message_complete); 1834 par.onUrl = toDelegate(&on_url); 1835 par.onStatus = toDelegate(&on_status); 1836 par.onHeaderField = toDelegate(&on_header_field); 1837 par.onHeaderValue = toDelegate(&on_header_value); 1838 par.onChunkHeader = toDelegate(&on_chunk_header); 1839 par.onChunkComplete = toDelegate(&on_chunk_complete); 1840 par.onBody = toDelegate(&on_body); 1841 1842 showException(yuCathException(par.httpParserExecute(cast(ubyte[]) data))); 1843 1844 par.rest(HTTPType.BOTH); 1845 data = "POST /post_chunked_all_your_base HTTP/1.1\r\nHost:0.0.0.0=5000\r\nTransfer-Encoding:chunked\r\n\r\n5\r\nhello\r\n"; 1846 1847 auto data2 = "0\r\n\r\n"; 1848 1849 showException(yuCathException(par.httpParserExecute(cast(ubyte[]) data))); 1850 writeln("data 1 is over!"); 1851 showException(yuCathException(par.httpParserExecute(cast(ubyte[]) data2))); 1852 }