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

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: Limit status code length. 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 int _statusCodeLength = 0;
251 List _uri_or_reason_phrase; 251 final List<int> _method = [];
252 List _headerField; 252 final List<int> _uri_or_reason_phrase = [];
253 List _headerValue; 253 final List<int> _headerField = [];
254 final List<int> _headerValue = [];
254 255
255 int _httpVersion; 256 int _httpVersion;
256 int _transferLength = -1; 257 int _transferLength = -1;
257 bool _persistentConnection; 258 bool _persistentConnection;
258 bool _connectionUpgrade; 259 bool _connectionUpgrade;
259 bool _chunked; 260 bool _chunked;
260 261
261 bool _noMessageBody; 262 bool _noMessageBody = false;
262 String _responseToMethod; // Indicates the method used for the request.
263 int _remainingContent = -1; 263 int _remainingContent = -1;
264 264
265 _HttpHeaders _headers; 265 _HttpHeaders _headers;
266 266
267 // The current incoming connection. 267 // The current incoming connection.
268 _HttpIncoming _incoming; 268 _HttpIncoming _incoming;
269 StreamSubscription _socketSubscription; 269 StreamSubscription _socketSubscription;
270 bool _paused = true; 270 bool _paused = true;
271 bool _bodyPaused = false; 271 bool _bodyPaused = false;
272 StreamController<_HttpIncoming> _controller; 272 StreamController<_HttpIncoming> _controller;
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
367 case _State.START: 367 case _State.START:
368 if (byte == _Const.HTTP[0]) { 368 if (byte == _Const.HTTP[0]) {
369 // Start parsing method or HTTP version. 369 // Start parsing method or HTTP version.
370 _httpVersionIndex = 1; 370 _httpVersionIndex = 1;
371 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION; 371 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION;
372 } else { 372 } else {
373 // Start parsing method. 373 // Start parsing method.
374 if (!_isTokenChar(byte)) { 374 if (!_isTokenChar(byte)) {
375 throw new HttpException("Invalid request method"); 375 throw new HttpException("Invalid request method");
376 } 376 }
377 _method_or_status_code.add(byte); 377 _method.add(byte);
378 if (!_requestParser) { 378 if (!_requestParser) {
379 throw new HttpException("Invalid response line"); 379 throw new HttpException("Invalid response line");
380 } 380 }
381 _state = _State.REQUEST_LINE_METHOD; 381 _state = _State.REQUEST_LINE_METHOD;
382 } 382 }
383 break; 383 break;
384 384
385 case _State.METHOD_OR_RESPONSE_HTTP_VERSION: 385 case _State.METHOD_OR_RESPONSE_HTTP_VERSION:
386 if (_httpVersionIndex < _Const.HTTP.length && 386 if (_httpVersionIndex < _Const.HTTP.length &&
387 byte == _Const.HTTP[_httpVersionIndex]) { 387 byte == _Const.HTTP[_httpVersionIndex]) {
388 // Continue parsing HTTP version. 388 // Continue parsing HTTP version.
389 _httpVersionIndex++; 389 _httpVersionIndex++;
390 } else if (_httpVersionIndex == _Const.HTTP.length && 390 } else if (_httpVersionIndex == _Const.HTTP.length &&
391 byte == _CharCode.SLASH) { 391 byte == _CharCode.SLASH) {
392 // HTTP/ parsed. As method is a token this cannot be a 392 // HTTP/ parsed. As method is a token this cannot be a
393 // method anymore. 393 // method anymore.
394 _httpVersionIndex++; 394 _httpVersionIndex++;
395 if (_requestParser) { 395 if (_requestParser) {
396 throw new HttpException("Invalid request line"); 396 throw new HttpException("Invalid request line");
397 } 397 }
398 _state = _State.RESPONSE_HTTP_VERSION; 398 _state = _State.RESPONSE_HTTP_VERSION;
399 } else { 399 } else {
400 // Did not parse HTTP version. Expect method instead. 400 // Did not parse HTTP version. Expect method instead.
401 for (int i = 0; i < _httpVersionIndex; i++) { 401 for (int i = 0; i < _httpVersionIndex; i++) {
402 _method_or_status_code.add(_Const.HTTP[i]); 402 _method.add(_Const.HTTP[i]);
403 } 403 }
404 if (byte == _CharCode.SP) { 404 if (byte == _CharCode.SP) {
405 _state = _State.REQUEST_LINE_URI; 405 _state = _State.REQUEST_LINE_URI;
406 } else { 406 } else {
407 _method_or_status_code.add(byte); 407 _method.add(byte);
408 _httpVersion = _HttpVersion.UNDETERMINED; 408 _httpVersion = _HttpVersion.UNDETERMINED;
409 if (!_requestParser) { 409 if (!_requestParser) {
410 throw new HttpException("Invalid response line"); 410 throw new HttpException("Invalid response line");
411 } 411 }
412 _state = _State.REQUEST_LINE_METHOD; 412 _state = _State.REQUEST_LINE_METHOD;
413 } 413 }
414 } 414 }
415 break; 415 break;
416 416
417 case _State.RESPONSE_HTTP_VERSION: 417 case _State.RESPONSE_HTTP_VERSION:
(...skipping 24 matching lines...) Expand all
442 442
443 case _State.REQUEST_LINE_METHOD: 443 case _State.REQUEST_LINE_METHOD:
444 if (byte == _CharCode.SP) { 444 if (byte == _CharCode.SP) {
445 _state = _State.REQUEST_LINE_URI; 445 _state = _State.REQUEST_LINE_URI;
446 } else { 446 } else {
447 if (_Const.SEPARATOR_MAP[byte] || 447 if (_Const.SEPARATOR_MAP[byte] ||
448 byte == _CharCode.CR || 448 byte == _CharCode.CR ||
449 byte == _CharCode.LF) { 449 byte == _CharCode.LF) {
450 throw new HttpException("Invalid request method"); 450 throw new HttpException("Invalid request method");
451 } 451 }
452 _method_or_status_code.add(byte); 452 _method.add(byte);
453 } 453 }
454 break; 454 break;
455 455
456 case _State.REQUEST_LINE_URI: 456 case _State.REQUEST_LINE_URI:
457 if (byte == _CharCode.SP) { 457 if (byte == _CharCode.SP) {
458 if (_uri_or_reason_phrase.length == 0) { 458 if (_uri_or_reason_phrase.length == 0) {
459 throw new HttpException("Invalid request URI"); 459 throw new HttpException("Invalid request URI");
460 } 460 }
461 _state = _State.REQUEST_LINE_HTTP_VERSION; 461 _state = _State.REQUEST_LINE_HTTP_VERSION;
462 _httpVersionIndex = 0; 462 _httpVersionIndex = 0;
(...skipping 30 matching lines...) Expand all
493 break; 493 break;
494 494
495 case _State.REQUEST_LINE_ENDING: 495 case _State.REQUEST_LINE_ENDING:
496 _expect(byte, _CharCode.LF); 496 _expect(byte, _CharCode.LF);
497 _messageType = _MessageType.REQUEST; 497 _messageType = _MessageType.REQUEST;
498 _state = _State.HEADER_START; 498 _state = _State.HEADER_START;
499 break; 499 break;
500 500
501 case _State.RESPONSE_LINE_STATUS_CODE: 501 case _State.RESPONSE_LINE_STATUS_CODE:
502 if (byte == _CharCode.SP) { 502 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; 503 _state = _State.RESPONSE_LINE_REASON_PHRASE;
507 } else if (byte == _CharCode.CR) { 504 } else if (byte == _CharCode.CR) {
508 // Some HTTP servers does not follow the spec. and send 505 // Some HTTP servers does not follow the spec. and send
509 // \r\n right after the status code. 506 // \r\n right after the status code.
510 _state = _State.RESPONSE_LINE_ENDING; 507 _state = _State.RESPONSE_LINE_ENDING;
511 } else { 508 } else {
512 if (byte < 0x30 && 0x39 < byte) { 509 _statusCodeLength++;
510 if ((byte < 0x30 && 0x39 < byte) || _statusCodeLength > 3) {
513 throw new HttpException("Invalid response status code"); 511 throw new HttpException("Invalid response status code");
514 } else { 512 } else {
515 _method_or_status_code.add(byte); 513 _statusCode = _statusCode * 10 + byte - 0x30;
516 } 514 }
517 } 515 }
518 break; 516 break;
519 517
520 case _State.RESPONSE_LINE_REASON_PHRASE: 518 case _State.RESPONSE_LINE_REASON_PHRASE:
521 if (byte == _CharCode.CR) { 519 if (byte == _CharCode.CR) {
522 _state = _State.RESPONSE_LINE_ENDING; 520 _state = _State.RESPONSE_LINE_ENDING;
523 } else { 521 } else {
524 if (byte == _CharCode.CR || byte == _CharCode.LF) { 522 if (byte == _CharCode.CR || byte == _CharCode.LF) {
525 throw new HttpException("Invalid response reason phrase"); 523 throw new HttpException("Invalid response reason phrase");
526 } 524 }
527 _uri_or_reason_phrase.add(byte); 525 _uri_or_reason_phrase.add(byte);
528 } 526 }
529 break; 527 break;
530 528
531 case _State.RESPONSE_LINE_ENDING: 529 case _State.RESPONSE_LINE_ENDING:
532 _expect(byte, _CharCode.LF); 530 _expect(byte, _CharCode.LF);
533 _messageType == _MessageType.RESPONSE; 531 _messageType == _MessageType.RESPONSE;
534 _statusCode = int.parse(
535 new String.fromCharCodes(_method_or_status_code));
536 if (_statusCode < 100 || _statusCode > 599) { 532 if (_statusCode < 100 || _statusCode > 599) {
537 throw new HttpException("Invalid response status code"); 533 throw new HttpException("Invalid response status code");
538 } else { 534 } else {
539 // Check whether this response will never have a body. 535 // Check whether this response will never have a body.
540 _noMessageBody = _statusCode <= 199 || _statusCode == 204 || 536 if (_statusCode <= 199 || _statusCode == 204 ||
541 _statusCode == 304; 537 _statusCode == 304) {
538 _noMessageBody = true;
539 }
542 } 540 }
543 _state = _State.HEADER_START; 541 _state = _State.HEADER_START;
544 break; 542 break;
545 543
546 case _State.HEADER_START: 544 case _State.HEADER_START:
547 _headers = new _HttpHeaders(version); 545 _headers = new _HttpHeaders(version);
548 if (byte == _CharCode.CR) { 546 if (byte == _CharCode.CR) {
549 _state = _State.HEADER_ENDING; 547 _state = _State.HEADER_ENDING;
550 } else { 548 } else {
551 // Start of new header field. 549 // 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; 586 _state = _State.HEADER_VALUE_FOLD_OR_END;
589 break; 587 break;
590 588
591 case _State.HEADER_VALUE_FOLD_OR_END: 589 case _State.HEADER_VALUE_FOLD_OR_END:
592 if (byte == _CharCode.SP || byte == _CharCode.HT) { 590 if (byte == _CharCode.SP || byte == _CharCode.HT) {
593 _state = _State.HEADER_VALUE_START; 591 _state = _State.HEADER_VALUE_START;
594 } else { 592 } else {
595 String headerField = new String.fromCharCodes(_headerField); 593 String headerField = new String.fromCharCodes(_headerField);
596 String headerValue = new String.fromCharCodes(_headerValue); 594 String headerValue = new String.fromCharCodes(_headerValue);
597 if (headerField == "transfer-encoding" && 595 if (headerField == "transfer-encoding" &&
598 headerValue.toLowerCase() == "chunked") { 596 _caseInsensitiveCompare("chunked".codeUnits, _headerValue)) {
599 _chunked = true; 597 _chunked = true;
600 } 598 }
601 if (headerField == "connection") { 599 if (headerField == "connection") {
602 List<String> tokens = _tokenizeFieldValue(headerValue); 600 List<String> tokens = _tokenizeFieldValue(headerValue);
603 for (int i = 0; i < tokens.length; i++) { 601 for (int i = 0; i < tokens.length; i++) {
604 if (tokens[i].toLowerCase() == "upgrade") { 602 if (_caseInsensitiveCompare("upgrade".codeUnits,
603 tokens[i].codeUnits)) {
605 _connectionUpgrade = true; 604 _connectionUpgrade = true;
606 } 605 }
607 _headers._add(headerField, tokens[i]); 606 _headers._add(headerField, tokens[i]);
608 } 607 }
609 } else { 608 } else {
610 _headers._add(headerField, headerValue); 609 _headers._add(headerField, headerValue);
611 } 610 }
612 _headerField.clear(); 611 _headerField.clear();
613 _headerValue.clear(); 612 _headerValue.clear();
614 613
(...skipping 24 matching lines...) Expand all
639 _chunked == false) { 638 _chunked == false) {
640 _transferLength = 0; 639 _transferLength = 0;
641 } 640 }
642 if (_connectionUpgrade) { 641 if (_connectionUpgrade) {
643 _state = _State.UPGRADED; 642 _state = _State.UPGRADED;
644 _transferLength = 0; 643 _transferLength = 0;
645 } 644 }
646 _createIncoming(_transferLength); 645 _createIncoming(_transferLength);
647 if (_requestParser) { 646 if (_requestParser) {
648 _incoming.method = 647 _incoming.method =
649 new String.fromCharCodes(_method_or_status_code); 648 new String.fromCharCodes(_method);
650 _incoming.uri = 649 _incoming.uri =
651 Uri.parse( 650 Uri.parse(
652 new String.fromCharCodes(_uri_or_reason_phrase)); 651 new String.fromCharCodes(_uri_or_reason_phrase));
653 } else { 652 } else {
654 _incoming.statusCode = _statusCode; 653 _incoming.statusCode = _statusCode;
655 _incoming.reasonPhrase = 654 _incoming.reasonPhrase =
656 new String.fromCharCodes(_uri_or_reason_phrase); 655 new String.fromCharCodes(_uri_or_reason_phrase);
657 } 656 }
658 _method_or_status_code.clear(); 657 _method.clear();
659 _uri_or_reason_phrase.clear(); 658 _uri_or_reason_phrase.clear();
660 if (_connectionUpgrade) { 659 if (_connectionUpgrade) {
661 _incoming.upgraded = true; 660 _incoming.upgraded = true;
662 _parserCalled = false; 661 _parserCalled = false;
663 var tmp = _incoming; 662 var tmp = _incoming;
664 _closeIncoming(); 663 _closeIncoming();
665 _controller.add(tmp); 664 _controller.add(tmp);
666 return; 665 return;
667 } 666 }
668 if (_transferLength == 0 || 667 if (_transferLength == 0 ||
669 (_messageType == _MessageType.RESPONSE && 668 (_messageType == _MessageType.RESPONSE && _noMessageBody)) {
670 (_noMessageBody || _responseToMethod == "HEAD"))) {
671 _reset(); 669 _reset();
672 var tmp = _incoming; 670 var tmp = _incoming;
673 _closeIncoming(); 671 _closeIncoming();
674 _controller.add(tmp); 672 _controller.add(tmp);
675 break; 673 break;
676 } else if (_chunked) { 674 } else if (_chunked) {
677 _state = _State.CHUNK_SIZE; 675 _state = _State.CHUNK_SIZE;
678 _remainingContent = 0; 676 _remainingContent = 0;
679 } else if (_transferLength > 0) { 677 } else if (_transferLength > 0) {
680 _remainingContent = _transferLength; 678 _remainingContent = _transferLength;
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
859 return "1.1"; 857 return "1.1";
860 } 858 }
861 return null; 859 return null;
862 } 860 }
863 861
864 int get messageType => _messageType; 862 int get messageType => _messageType;
865 int get transferLength => _transferLength; 863 int get transferLength => _transferLength;
866 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED; 864 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED;
867 bool get persistentConnection => _persistentConnection; 865 bool get persistentConnection => _persistentConnection;
868 866
869 void set responseToMethod(String method) { _responseToMethod = method; } 867 void set isHead(bool value) {
868 if (value) _noMessageBody = true;
869 }
870 870
871 _HttpDetachedIncoming detachIncoming() { 871 _HttpDetachedIncoming detachIncoming() {
872 // Simulate detached by marking as upgraded. 872 // Simulate detached by marking as upgraded.
873 _state = _State.UPGRADED; 873 _state = _State.UPGRADED;
874 return new _HttpDetachedIncoming(_socketSubscription, 874 return new _HttpDetachedIncoming(_socketSubscription,
875 readUnparsedData()); 875 readUnparsedData());
876 } 876 }
877 877
878 List<int> readUnparsedData() { 878 List<int> readUnparsedData() {
879 if (_buffer == null) return null; 879 if (_buffer == null) return null;
880 if (_index == _buffer.length) return null; 880 if (_index == _buffer.length) return null;
881 var result = _buffer.sublist(_index); 881 var result = _buffer.sublist(_index);
882 _releaseBuffer(); 882 _releaseBuffer();
883 return result; 883 return result;
884 } 884 }
885 885
886 void _reset() { 886 void _reset() {
887 if (_state == _State.UPGRADED) return; 887 if (_state == _State.UPGRADED) return;
888 _state = _State.START; 888 _state = _State.START;
889 _messageType = _MessageType.UNDETERMINED; 889 _messageType = _MessageType.UNDETERMINED;
890 _headerField = new List(); 890 _headerField.clear();
891 _headerValue = new List(); 891 _headerValue.clear();
892 _method_or_status_code = new List(); 892 _method.clear();
893 _uri_or_reason_phrase = new List(); 893 _uri_or_reason_phrase.clear();
894 894
895 _statusCode = 0; 895 _statusCode = 0;
896 _statusCodeLength = 0;
896 897
897 _httpVersion = _HttpVersion.UNDETERMINED; 898 _httpVersion = _HttpVersion.UNDETERMINED;
898 _transferLength = -1; 899 _transferLength = -1;
899 _persistentConnection = false; 900 _persistentConnection = false;
900 _connectionUpgrade = false; 901 _connectionUpgrade = false;
901 _chunked = false; 902 _chunked = false;
902 903
903 _noMessageBody = false; 904 _noMessageBody = false;
904 _responseToMethod = null;
905 _remainingContent = -1; 905 _remainingContent = -1;
906 906
907 _headers = null; 907 _headers = null;
908 } 908 }
909 909
910 void _releaseBuffer() { 910 void _releaseBuffer() {
911 _buffer = null; 911 _buffer = null;
912 _index = null; 912 _index = null;
913 } 913 }
914 914
(...skipping 18 matching lines...) Expand all
933 return tokens; 933 return tokens;
934 } 934 }
935 935
936 int _toLowerCase(int byte) { 936 int _toLowerCase(int byte) {
937 final int aCode = "A".codeUnitAt(0); 937 final int aCode = "A".codeUnitAt(0);
938 final int zCode = "Z".codeUnitAt(0); 938 final int zCode = "Z".codeUnitAt(0);
939 final int delta = "a".codeUnitAt(0) - aCode; 939 final int delta = "a".codeUnitAt(0) - aCode;
940 return (aCode <= byte && byte <= zCode) ? byte + delta : byte; 940 return (aCode <= byte && byte <= zCode) ? byte + delta : byte;
941 } 941 }
942 942
943 // expected should already be lowercase.
944 bool _caseInsensitiveCompare(List<int> expected, List<int> value) {
945 if (expected.length != value.length) return false;
946 for (int i = 0; i < expected.length; i++) {
947 if (expected[i] != _toLowerCase(value[i])) return false;
948 }
949 return true;
950 }
951
943 int _expect(int val1, int val2) { 952 int _expect(int val1, int val2) {
944 if (val1 != val2) { 953 if (val1 != val2) {
945 throw new HttpException("Failed to parse HTTP"); 954 throw new HttpException("Failed to parse HTTP");
946 } 955 }
947 } 956 }
948 957
949 int _expectHexDigit(int byte) { 958 int _expectHexDigit(int byte) {
950 if (0x30 <= byte && byte <= 0x39) { 959 if (0x30 <= byte && byte <= 0x39) {
951 return byte - 0x30; // 0 - 9 960 return byte - 0x30; // 0 - 9
952 } else if (0x41 <= byte && byte <= 0x46) { 961 } else if (0x41 <= byte && byte <= 0x46) {
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
1023 } 1032 }
1024 } 1033 }
1025 1034
1026 void _reportError(error, [stackTrace]) { 1035 void _reportError(error, [stackTrace]) {
1027 if (_socketSubscription != null) _socketSubscription.cancel(); 1036 if (_socketSubscription != null) _socketSubscription.cancel();
1028 _state = _State.FAILURE; 1037 _state = _State.FAILURE;
1029 _controller.addError(error, stackTrace); 1038 _controller.addError(error, stackTrace);
1030 _controller.close(); 1039 _controller.close();
1031 } 1040 }
1032 } 1041 }
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