Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(36)

Side by Side Diff: sdk/lib/io/http_parser.dart

Issue 173683002: Slightly speed up http-parser and http-header-writing. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sdk/lib/io/http_impl.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.io; 5 part of dart.io;
6 6
7 // Global constants. 7 // Global constants.
8 class _Const { 8 class _Const {
9 // Bytes for "HTTP". 9 // Bytes for "HTTP".
10 static const HTTP = const [72, 84, 84, 80]; 10 static const HTTP = const [72, 84, 84, 80];
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 240
241 // The data that is currently being parsed. 241 // The data that is currently being parsed.
242 Uint8List _buffer; 242 Uint8List _buffer;
243 int _index; 243 int _index;
244 244
245 final bool _requestParser; 245 final bool _requestParser;
246 int _state; 246 int _state;
247 int _httpVersionIndex; 247 int _httpVersionIndex;
248 int _messageType; 248 int _messageType;
249 int _statusCode = 0; 249 int _statusCode = 0;
250 List _method_or_status_code; 250 final List<int> _method = [];
251 List _uri_or_reason_phrase; 251 final List<int> _uri_or_reason_phrase = [];
252 List _headerField; 252 final List<int> _headerField = [];
253 List _headerValue; 253 final List<int> _headerValue = [];
254 254
255 int _httpVersion; 255 int _httpVersion;
256 int _transferLength = -1; 256 int _transferLength = -1;
257 bool _persistentConnection; 257 bool _persistentConnection;
258 bool _connectionUpgrade; 258 bool _connectionUpgrade;
259 bool _chunked; 259 bool _chunked;
260 260
261 bool _noMessageBody; 261 bool _noMessageBody = false;
262 String _responseToMethod; // Indicates the method used for the request.
263 int _remainingContent = -1; 262 int _remainingContent = -1;
264 263
265 _HttpHeaders _headers; 264 _HttpHeaders _headers;
266 265
267 // The current incoming connection. 266 // The current incoming connection.
268 _HttpIncoming _incoming; 267 _HttpIncoming _incoming;
269 StreamSubscription _socketSubscription; 268 StreamSubscription _socketSubscription;
270 bool _paused = true; 269 bool _paused = true;
271 bool _bodyPaused = false; 270 bool _bodyPaused = false;
272 StreamController<_HttpIncoming> _controller; 271 StreamController<_HttpIncoming> _controller;
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
367 case _State.START: 366 case _State.START:
368 if (byte == _Const.HTTP[0]) { 367 if (byte == _Const.HTTP[0]) {
369 // Start parsing method or HTTP version. 368 // Start parsing method or HTTP version.
370 _httpVersionIndex = 1; 369 _httpVersionIndex = 1;
371 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION; 370 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION;
372 } else { 371 } else {
373 // Start parsing method. 372 // Start parsing method.
374 if (!_isTokenChar(byte)) { 373 if (!_isTokenChar(byte)) {
375 throw new HttpException("Invalid request method"); 374 throw new HttpException("Invalid request method");
376 } 375 }
377 _method_or_status_code.add(byte); 376 _method.add(byte);
378 if (!_requestParser) { 377 if (!_requestParser) {
379 throw new HttpException("Invalid response line"); 378 throw new HttpException("Invalid response line");
380 } 379 }
381 _state = _State.REQUEST_LINE_METHOD; 380 _state = _State.REQUEST_LINE_METHOD;
382 } 381 }
383 break; 382 break;
384 383
385 case _State.METHOD_OR_RESPONSE_HTTP_VERSION: 384 case _State.METHOD_OR_RESPONSE_HTTP_VERSION:
386 if (_httpVersionIndex < _Const.HTTP.length && 385 if (_httpVersionIndex < _Const.HTTP.length &&
387 byte == _Const.HTTP[_httpVersionIndex]) { 386 byte == _Const.HTTP[_httpVersionIndex]) {
388 // Continue parsing HTTP version. 387 // Continue parsing HTTP version.
389 _httpVersionIndex++; 388 _httpVersionIndex++;
390 } else if (_httpVersionIndex == _Const.HTTP.length && 389 } else if (_httpVersionIndex == _Const.HTTP.length &&
391 byte == _CharCode.SLASH) { 390 byte == _CharCode.SLASH) {
392 // HTTP/ parsed. As method is a token this cannot be a 391 // HTTP/ parsed. As method is a token this cannot be a
393 // method anymore. 392 // method anymore.
394 _httpVersionIndex++; 393 _httpVersionIndex++;
395 if (_requestParser) { 394 if (_requestParser) {
396 throw new HttpException("Invalid request line"); 395 throw new HttpException("Invalid request line");
397 } 396 }
398 _state = _State.RESPONSE_HTTP_VERSION; 397 _state = _State.RESPONSE_HTTP_VERSION;
399 } else { 398 } else {
400 // Did not parse HTTP version. Expect method instead. 399 // Did not parse HTTP version. Expect method instead.
401 for (int i = 0; i < _httpVersionIndex; i++) { 400 for (int i = 0; i < _httpVersionIndex; i++) {
402 _method_or_status_code.add(_Const.HTTP[i]); 401 _method.add(_Const.HTTP[i]);
403 } 402 }
404 if (byte == _CharCode.SP) { 403 if (byte == _CharCode.SP) {
405 _state = _State.REQUEST_LINE_URI; 404 _state = _State.REQUEST_LINE_URI;
406 } else { 405 } else {
407 _method_or_status_code.add(byte); 406 _method.add(byte);
408 _httpVersion = _HttpVersion.UNDETERMINED; 407 _httpVersion = _HttpVersion.UNDETERMINED;
409 if (!_requestParser) { 408 if (!_requestParser) {
410 throw new HttpException("Invalid response line"); 409 throw new HttpException("Invalid response line");
411 } 410 }
412 _state = _State.REQUEST_LINE_METHOD; 411 _state = _State.REQUEST_LINE_METHOD;
413 } 412 }
414 } 413 }
415 break; 414 break;
416 415
417 case _State.RESPONSE_HTTP_VERSION: 416 case _State.RESPONSE_HTTP_VERSION:
(...skipping 24 matching lines...) Expand all
442 441
443 case _State.REQUEST_LINE_METHOD: 442 case _State.REQUEST_LINE_METHOD:
444 if (byte == _CharCode.SP) { 443 if (byte == _CharCode.SP) {
445 _state = _State.REQUEST_LINE_URI; 444 _state = _State.REQUEST_LINE_URI;
446 } else { 445 } else {
447 if (_Const.SEPARATOR_MAP[byte] || 446 if (_Const.SEPARATOR_MAP[byte] ||
448 byte == _CharCode.CR || 447 byte == _CharCode.CR ||
449 byte == _CharCode.LF) { 448 byte == _CharCode.LF) {
450 throw new HttpException("Invalid request method"); 449 throw new HttpException("Invalid request method");
451 } 450 }
452 _method_or_status_code.add(byte); 451 _method.add(byte);
453 } 452 }
454 break; 453 break;
455 454
456 case _State.REQUEST_LINE_URI: 455 case _State.REQUEST_LINE_URI:
457 if (byte == _CharCode.SP) { 456 if (byte == _CharCode.SP) {
458 if (_uri_or_reason_phrase.length == 0) { 457 if (_uri_or_reason_phrase.length == 0) {
459 throw new HttpException("Invalid request URI"); 458 throw new HttpException("Invalid request URI");
460 } 459 }
461 _state = _State.REQUEST_LINE_HTTP_VERSION; 460 _state = _State.REQUEST_LINE_HTTP_VERSION;
462 _httpVersionIndex = 0; 461 _httpVersionIndex = 0;
(...skipping 30 matching lines...) Expand all
493 break; 492 break;
494 493
495 case _State.REQUEST_LINE_ENDING: 494 case _State.REQUEST_LINE_ENDING:
496 _expect(byte, _CharCode.LF); 495 _expect(byte, _CharCode.LF);
497 _messageType = _MessageType.REQUEST; 496 _messageType = _MessageType.REQUEST;
498 _state = _State.HEADER_START; 497 _state = _State.HEADER_START;
499 break; 498 break;
500 499
501 case _State.RESPONSE_LINE_STATUS_CODE: 500 case _State.RESPONSE_LINE_STATUS_CODE:
502 if (byte == _CharCode.SP) { 501 if (byte == _CharCode.SP) {
503 if (_method_or_status_code.length != 3) {
504 throw new HttpException("Invalid response status code");
505 }
506 _state = _State.RESPONSE_LINE_REASON_PHRASE; 502 _state = _State.RESPONSE_LINE_REASON_PHRASE;
507 } else if (byte == _CharCode.CR) { 503 } else if (byte == _CharCode.CR) {
508 // Some HTTP servers does not follow the spec. and send 504 // Some HTTP servers does not follow the spec. and send
509 // \r\n right after the status code. 505 // \r\n right after the status code.
510 _state = _State.RESPONSE_LINE_ENDING; 506 _state = _State.RESPONSE_LINE_ENDING;
511 } else { 507 } else {
512 if (byte < 0x30 && 0x39 < byte) { 508 if (byte < 0x30 && 0x39 < byte) {
513 throw new HttpException("Invalid response status code"); 509 throw new HttpException("Invalid response status code");
514 } else { 510 } else {
515 _method_or_status_code.add(byte); 511 _statusCode = _statusCode * 10 + byte - 0x30;
Søren Gjesse 2014/02/20 14:12:18 As discussed offline, we should keep track of the
Anders Johnsen 2014/02/20 14:46:53 Done.
516 } 512 }
517 } 513 }
518 break; 514 break;
519 515
520 case _State.RESPONSE_LINE_REASON_PHRASE: 516 case _State.RESPONSE_LINE_REASON_PHRASE:
521 if (byte == _CharCode.CR) { 517 if (byte == _CharCode.CR) {
522 _state = _State.RESPONSE_LINE_ENDING; 518 _state = _State.RESPONSE_LINE_ENDING;
523 } else { 519 } else {
524 if (byte == _CharCode.CR || byte == _CharCode.LF) { 520 if (byte == _CharCode.CR || byte == _CharCode.LF) {
525 throw new HttpException("Invalid response reason phrase"); 521 throw new HttpException("Invalid response reason phrase");
526 } 522 }
527 _uri_or_reason_phrase.add(byte); 523 _uri_or_reason_phrase.add(byte);
528 } 524 }
529 break; 525 break;
530 526
531 case _State.RESPONSE_LINE_ENDING: 527 case _State.RESPONSE_LINE_ENDING:
532 _expect(byte, _CharCode.LF); 528 _expect(byte, _CharCode.LF);
533 _messageType == _MessageType.RESPONSE; 529 _messageType == _MessageType.RESPONSE;
534 _statusCode = int.parse(
535 new String.fromCharCodes(_method_or_status_code));
536 if (_statusCode < 100 || _statusCode > 599) { 530 if (_statusCode < 100 || _statusCode > 599) {
537 throw new HttpException("Invalid response status code"); 531 throw new HttpException("Invalid response status code");
538 } else { 532 } else {
539 // Check whether this response will never have a body. 533 // Check whether this response will never have a body.
540 _noMessageBody = _statusCode <= 199 || _statusCode == 204 || 534 if (_statusCode <= 199 || _statusCode == 204 ||
541 _statusCode == 304; 535 _statusCode == 304) {
536 _noMessageBody = true;
537 }
542 } 538 }
543 _state = _State.HEADER_START; 539 _state = _State.HEADER_START;
544 break; 540 break;
545 541
546 case _State.HEADER_START: 542 case _State.HEADER_START:
547 _headers = new _HttpHeaders(version); 543 _headers = new _HttpHeaders(version);
548 if (byte == _CharCode.CR) { 544 if (byte == _CharCode.CR) {
549 _state = _State.HEADER_ENDING; 545 _state = _State.HEADER_ENDING;
550 } else { 546 } else {
551 // Start of new header field. 547 // Start of new header field.
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
588 _state = _State.HEADER_VALUE_FOLD_OR_END; 584 _state = _State.HEADER_VALUE_FOLD_OR_END;
589 break; 585 break;
590 586
591 case _State.HEADER_VALUE_FOLD_OR_END: 587 case _State.HEADER_VALUE_FOLD_OR_END:
592 if (byte == _CharCode.SP || byte == _CharCode.HT) { 588 if (byte == _CharCode.SP || byte == _CharCode.HT) {
593 _state = _State.HEADER_VALUE_START; 589 _state = _State.HEADER_VALUE_START;
594 } else { 590 } else {
595 String headerField = new String.fromCharCodes(_headerField); 591 String headerField = new String.fromCharCodes(_headerField);
596 String headerValue = new String.fromCharCodes(_headerValue); 592 String headerValue = new String.fromCharCodes(_headerValue);
597 if (headerField == "transfer-encoding" && 593 if (headerField == "transfer-encoding" &&
598 headerValue.toLowerCase() == "chunked") { 594 _caseInsensitiveCompare("chunked".codeUnits, _headerValue)) {
599 _chunked = true; 595 _chunked = true;
600 } 596 }
601 if (headerField == "connection") { 597 if (headerField == "connection") {
602 List<String> tokens = _tokenizeFieldValue(headerValue); 598 List<String> tokens = _tokenizeFieldValue(headerValue);
603 for (int i = 0; i < tokens.length; i++) { 599 for (int i = 0; i < tokens.length; i++) {
604 if (tokens[i].toLowerCase() == "upgrade") { 600 if (_caseInsensitiveCompare("upgrade".codeUnits,
601 tokens[i].codeUnits)) {
605 _connectionUpgrade = true; 602 _connectionUpgrade = true;
606 } 603 }
607 _headers._add(headerField, tokens[i]); 604 _headers._add(headerField, tokens[i]);
608 } 605 }
609 } else { 606 } else {
610 _headers._add(headerField, headerValue); 607 _headers._add(headerField, headerValue);
611 } 608 }
612 _headerField.clear(); 609 _headerField.clear();
613 _headerValue.clear(); 610 _headerValue.clear();
614 611
(...skipping 24 matching lines...) Expand all
639 _chunked == false) { 636 _chunked == false) {
640 _transferLength = 0; 637 _transferLength = 0;
641 } 638 }
642 if (_connectionUpgrade) { 639 if (_connectionUpgrade) {
643 _state = _State.UPGRADED; 640 _state = _State.UPGRADED;
644 _transferLength = 0; 641 _transferLength = 0;
645 } 642 }
646 _createIncoming(_transferLength); 643 _createIncoming(_transferLength);
647 if (_requestParser) { 644 if (_requestParser) {
648 _incoming.method = 645 _incoming.method =
649 new String.fromCharCodes(_method_or_status_code); 646 new String.fromCharCodes(_method);
650 _incoming.uri = 647 _incoming.uri =
651 Uri.parse( 648 Uri.parse(
652 new String.fromCharCodes(_uri_or_reason_phrase)); 649 new String.fromCharCodes(_uri_or_reason_phrase));
653 } else { 650 } else {
654 _incoming.statusCode = _statusCode; 651 _incoming.statusCode = _statusCode;
655 _incoming.reasonPhrase = 652 _incoming.reasonPhrase =
656 new String.fromCharCodes(_uri_or_reason_phrase); 653 new String.fromCharCodes(_uri_or_reason_phrase);
657 } 654 }
658 _method_or_status_code.clear(); 655 _method.clear();
659 _uri_or_reason_phrase.clear(); 656 _uri_or_reason_phrase.clear();
660 if (_connectionUpgrade) { 657 if (_connectionUpgrade) {
661 _incoming.upgraded = true; 658 _incoming.upgraded = true;
662 _parserCalled = false; 659 _parserCalled = false;
663 var tmp = _incoming; 660 var tmp = _incoming;
664 _closeIncoming(); 661 _closeIncoming();
665 _controller.add(tmp); 662 _controller.add(tmp);
666 return; 663 return;
667 } 664 }
668 if (_transferLength == 0 || 665 if (_transferLength == 0 ||
669 (_messageType == _MessageType.RESPONSE && 666 (_messageType == _MessageType.RESPONSE && _noMessageBody)) {
670 (_noMessageBody || _responseToMethod == "HEAD"))) {
671 _reset(); 667 _reset();
672 var tmp = _incoming; 668 var tmp = _incoming;
673 _closeIncoming(); 669 _closeIncoming();
674 _controller.add(tmp); 670 _controller.add(tmp);
675 break; 671 break;
676 } else if (_chunked) { 672 } else if (_chunked) {
677 _state = _State.CHUNK_SIZE; 673 _state = _State.CHUNK_SIZE;
678 _remainingContent = 0; 674 _remainingContent = 0;
679 } else if (_transferLength > 0) { 675 } else if (_transferLength > 0) {
680 _remainingContent = _transferLength; 676 _remainingContent = _transferLength;
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
859 return "1.1"; 855 return "1.1";
860 } 856 }
861 return null; 857 return null;
862 } 858 }
863 859
864 int get messageType => _messageType; 860 int get messageType => _messageType;
865 int get transferLength => _transferLength; 861 int get transferLength => _transferLength;
866 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED; 862 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED;
867 bool get persistentConnection => _persistentConnection; 863 bool get persistentConnection => _persistentConnection;
868 864
869 void set responseToMethod(String method) { _responseToMethod = method; } 865 void set isHead(bool value) {
866 if (value) _noMessageBody = true;
867 }
870 868
871 _HttpDetachedIncoming detachIncoming() { 869 _HttpDetachedIncoming detachIncoming() {
872 // Simulate detached by marking as upgraded. 870 // Simulate detached by marking as upgraded.
873 _state = _State.UPGRADED; 871 _state = _State.UPGRADED;
874 return new _HttpDetachedIncoming(_socketSubscription, 872 return new _HttpDetachedIncoming(_socketSubscription,
875 readUnparsedData()); 873 readUnparsedData());
876 } 874 }
877 875
878 List<int> readUnparsedData() { 876 List<int> readUnparsedData() {
879 if (_buffer == null) return null; 877 if (_buffer == null) return null;
880 if (_index == _buffer.length) return null; 878 if (_index == _buffer.length) return null;
881 var result = _buffer.sublist(_index); 879 var result = _buffer.sublist(_index);
882 _releaseBuffer(); 880 _releaseBuffer();
883 return result; 881 return result;
884 } 882 }
885 883
886 void _reset() { 884 void _reset() {
887 if (_state == _State.UPGRADED) return; 885 if (_state == _State.UPGRADED) return;
888 _state = _State.START; 886 _state = _State.START;
889 _messageType = _MessageType.UNDETERMINED; 887 _messageType = _MessageType.UNDETERMINED;
890 _headerField = new List(); 888 _headerField.clear();
891 _headerValue = new List(); 889 _headerValue.clear();
892 _method_or_status_code = new List(); 890 _method.clear();
893 _uri_or_reason_phrase = new List(); 891 _uri_or_reason_phrase.clear();
894 892
895 _statusCode = 0; 893 _statusCode = 0;
896 894
897 _httpVersion = _HttpVersion.UNDETERMINED; 895 _httpVersion = _HttpVersion.UNDETERMINED;
898 _transferLength = -1; 896 _transferLength = -1;
899 _persistentConnection = false; 897 _persistentConnection = false;
900 _connectionUpgrade = false; 898 _connectionUpgrade = false;
901 _chunked = false; 899 _chunked = false;
902 900
903 _noMessageBody = false; 901 _noMessageBody = false;
904 _responseToMethod = null;
905 _remainingContent = -1; 902 _remainingContent = -1;
906 903
907 _headers = null; 904 _headers = null;
908 } 905 }
909 906
910 void _releaseBuffer() { 907 void _releaseBuffer() {
911 _buffer = null; 908 _buffer = null;
912 _index = null; 909 _index = null;
913 } 910 }
914 911
(...skipping 18 matching lines...) Expand all
933 return tokens; 930 return tokens;
934 } 931 }
935 932
936 int _toLowerCase(int byte) { 933 int _toLowerCase(int byte) {
937 final int aCode = "A".codeUnitAt(0); 934 final int aCode = "A".codeUnitAt(0);
938 final int zCode = "Z".codeUnitAt(0); 935 final int zCode = "Z".codeUnitAt(0);
939 final int delta = "a".codeUnitAt(0) - aCode; 936 final int delta = "a".codeUnitAt(0) - aCode;
940 return (aCode <= byte && byte <= zCode) ? byte + delta : byte; 937 return (aCode <= byte && byte <= zCode) ? byte + delta : byte;
941 } 938 }
942 939
940 // expected should already be lowercase.
941 bool _caseInsensitiveCompare(List<int> expected, List<int> value) {
942 if (expected.length != value.length) return false;
943 for (int i = 0; i < expected.length; i++) {
944 if (expected[i] != _toLowerCase(value[i])) return false;
945 }
946 return true;
947 }
948
943 int _expect(int val1, int val2) { 949 int _expect(int val1, int val2) {
944 if (val1 != val2) { 950 if (val1 != val2) {
945 throw new HttpException("Failed to parse HTTP"); 951 throw new HttpException("Failed to parse HTTP");
946 } 952 }
947 } 953 }
948 954
949 int _expectHexDigit(int byte) { 955 int _expectHexDigit(int byte) {
950 if (0x30 <= byte && byte <= 0x39) { 956 if (0x30 <= byte && byte <= 0x39) {
951 return byte - 0x30; // 0 - 9 957 return byte - 0x30; // 0 - 9
952 } else if (0x41 <= byte && byte <= 0x46) { 958 } else if (0x41 <= byte && byte <= 0x46) {
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
1023 } 1029 }
1024 } 1030 }
1025 1031
1026 void _reportError(error, [stackTrace]) { 1032 void _reportError(error, [stackTrace]) {
1027 if (_socketSubscription != null) _socketSubscription.cancel(); 1033 if (_socketSubscription != null) _socketSubscription.cancel();
1028 _state = _State.FAILURE; 1034 _state = _State.FAILURE;
1029 _controller.addError(error, stackTrace); 1035 _controller.addError(error, stackTrace);
1030 _controller.close(); 1036 _controller.close();
1031 } 1037 }
1032 } 1038 }
OLDNEW
« no previous file with comments | « sdk/lib/io/http_impl.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698