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

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

Issue 11364134: Merge libv1. (Closed) Base URL: https://dart.googlecode.com/svn/experimental/lib_v2/dart
Patch Set: Reupload due to error Created 8 years, 1 month 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') | sdk/lib/io/path_impl.dart » ('j') | 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) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 // Global constants. 5 // Global constants.
6 class _Const { 6 class _Const {
7 // Bytes for "HTTP". 7 // Bytes for "HTTP".
8 static const HTTP = const [72, 84, 84, 80]; 8 static const HTTP = const [72, 84, 84, 80];
9 // Bytes for "HTTP/1.". 9 // Bytes for "HTTP/1.".
10 static const HTTP1DOT = const [72, 84, 84, 80, 47, 49, 46]; 10 static const HTTP1DOT = const [72, 84, 84, 80, 47, 49, 46];
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
109 * thrown from the [:writeList:] and [:connectionClosed:] methods if 109 * thrown from the [:writeList:] and [:connectionClosed:] methods if
110 * the error callback is not set. 110 * the error callback is not set.
111 * 111 *
112 * The connection upgrades (e.g. switching from HTTP/1.1 to the 112 * The connection upgrades (e.g. switching from HTTP/1.1 to the
113 * WebSocket protocol) is handled in a special way. If connection 113 * WebSocket protocol) is handled in a special way. If connection
114 * upgrade is specified in the headers, then on the callback to 114 * upgrade is specified in the headers, then on the callback to
115 * [:headersComplete:] the [:upgrade:] property on the [:HttpParser:] 115 * [:headersComplete:] the [:upgrade:] property on the [:HttpParser:]
116 * object will be [:true:] indicating that from now on the protocol is 116 * object will be [:true:] indicating that from now on the protocol is
117 * not HTTP anymore and no more callbacks will happen, that is 117 * not HTTP anymore and no more callbacks will happen, that is
118 * [:dataReceived:] and [:dataEnd:] are not called in this case as 118 * [:dataReceived:] and [:dataEnd:] are not called in this case as
119 * there is no more HTTP data. After the upgrade the call to 119 * there is no more HTTP data. After the upgrade the method
120 * [:writeList:] causing the upgrade will return with the number of 120 * [:readUnparsedData:] can be used to read any remaining bytes in the
121 * bytes parsed as HTTP. Any unparsed bytes is part of the protocol 121 * HTTP parser which are part of the protocol the connection is
122 * the connection is upgrading to and should be handled according to 122 * upgrading to. These bytes cannot be processed by the HTTP parser
123 * that protocol. 123 * and should be handled according to whatever protocol is being
124 * upgraded to.
124 */ 125 */
125 class _HttpParser { 126 class _HttpParser {
126 _HttpParser() { 127 _HttpParser() {
127 _reset(); 128 _reset();
128 } 129 }
129 130
130 // From RFC 2616. 131 // From RFC 2616.
131 // generic-message = start-line 132 // generic-message = start-line
132 // *(message-header CRLF) 133 // *(message-header CRLF)
133 // CRLF 134 // CRLF
134 // [ message-body ] 135 // [ message-body ]
135 // start-line = Request-Line | Status-Line 136 // start-line = Request-Line | Status-Line
136 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF 137 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
137 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF 138 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
138 // message-header = field-name ":" [ field-value ] 139 // message-header = field-name ":" [ field-value ]
139 int writeList(List<int> buffer, int offset, int count) { 140 void _parse() {
140 int index = offset;
141 int lastIndex = offset + count;
142 try { 141 try {
143 if (_state == _State.CLOSED) { 142 if (_state == _State.CLOSED) {
144 throw new HttpParserException("Data on closed connection"); 143 throw new HttpParserException("Data on closed connection");
145 } 144 }
146 if (_state == _State.UPGRADED) { 145 if (_state == _State.UPGRADED) {
147 throw new HttpParserException("Data on upgraded connection"); 146 throw new HttpParserException("Data on upgraded connection");
148 } 147 }
149 if (_state == _State.FAILURE) { 148 if (_state == _State.FAILURE) {
150 throw new HttpParserException("Data on failed connection"); 149 throw new HttpParserException("Data on failed connection");
151 } 150 }
152 while ((index < lastIndex) && 151 while (_buffer != null &&
152 _index < _lastIndex &&
153 _state != _State.FAILURE && 153 _state != _State.FAILURE &&
154 _state != _State.UPGRADED) { 154 _state != _State.UPGRADED) {
155 int byte = buffer[index]; 155 int byte = _buffer[_index++];
156 switch (_state) { 156 switch (_state) {
157 case _State.START: 157 case _State.START:
158 if (byte == _Const.HTTP[0]) { 158 if (byte == _Const.HTTP[0]) {
159 // Start parsing method or HTTP version. 159 // Start parsing method or HTTP version.
160 _httpVersionIndex = 1; 160 _httpVersionIndex = 1;
161 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION; 161 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION;
162 } else { 162 } else {
163 // Start parsing method. 163 // Start parsing method.
164 if (!_isTokenChar(byte)) { 164 if (!_isTokenChar(byte)) {
165 throw new HttpParserException("Invalid request method"); 165 throw new HttpParserException("Invalid request method");
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
318 _messageType == _MessageType.RESPONSE; 318 _messageType == _MessageType.RESPONSE;
319 int statusCode = parseInt(_method_or_status_code.toString()); 319 int statusCode = parseInt(_method_or_status_code.toString());
320 if (statusCode < 100 || statusCode > 599) { 320 if (statusCode < 100 || statusCode > 599) {
321 throw new HttpParserException("Invalid response status code"); 321 throw new HttpParserException("Invalid response status code");
322 } else { 322 } else {
323 // Check whether this response will never have a body. 323 // Check whether this response will never have a body.
324 _noMessageBody = 324 _noMessageBody =
325 statusCode <= 199 || statusCode == 204 || statusCode == 304; 325 statusCode <= 199 || statusCode == 204 || statusCode == 304;
326 } 326 }
327 if (responseStart != null) { 327 if (responseStart != null) {
328 responseStart(statusCode, _uri_or_reason_phrase.toString(), versio n); 328 responseStart(statusCode,
329 _uri_or_reason_phrase.toString(),
330 version);
329 } 331 }
330 _method_or_status_code.clear(); 332 _method_or_status_code.clear();
331 _uri_or_reason_phrase.clear(); 333 _uri_or_reason_phrase.clear();
332 _state = _State.HEADER_START; 334 _state = _State.HEADER_START;
333 break; 335 break;
334 336
335 case _State.HEADER_START: 337 case _State.HEADER_START:
336 if (byte == _CharCode.CR) { 338 if (byte == _CharCode.CR) {
337 _state = _State.HEADER_ENDING; 339 _state = _State.HEADER_ENDING;
338 } else { 340 } else {
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
431 // If a request message has neither Content-Length nor 433 // If a request message has neither Content-Length nor
432 // Transfer-Encoding the message must not have a body (RFC 434 // Transfer-Encoding the message must not have a body (RFC
433 // 2616 section 4.3). 435 // 2616 section 4.3).
434 if (_messageType == _MessageType.REQUEST && 436 if (_messageType == _MessageType.REQUEST &&
435 _contentLength < 0 && 437 _contentLength < 0 &&
436 _chunked == false) { 438 _chunked == false) {
437 _contentLength = 0; 439 _contentLength = 0;
438 } 440 }
439 if (_connectionUpgrade) { 441 if (_connectionUpgrade) {
440 _state = _State.UPGRADED; 442 _state = _State.UPGRADED;
441 _unparsedData =
442 buffer.getRange(index + 1, count - (index + 1 - offset));
443 if (headersComplete != null) headersComplete(); 443 if (headersComplete != null) headersComplete();
444 } else { 444 } else {
445 if (headersComplete != null) headersComplete(); 445 if (headersComplete != null) headersComplete();
446 if (_chunked) { 446 if (_chunked) {
447 _state = _State.CHUNK_SIZE; 447 _state = _State.CHUNK_SIZE;
448 _remainingContent = 0; 448 _remainingContent = 0;
449 } else if (_contentLength == 0 || 449 } else if (_contentLength == 0 ||
450 (_messageType == _MessageType.RESPONSE && 450 (_messageType == _MessageType.RESPONSE &&
451 (_noMessageBody || _responseToMethod == "HEAD"))) { 451 (_noMessageBody || _responseToMethod == "HEAD"))) {
452 // If there is no message body get ready to process the 452 // If there is no message body get ready to process the
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
506 break; 506 break;
507 507
508 case _State.CHUNKED_BODY_DONE_LF: 508 case _State.CHUNKED_BODY_DONE_LF:
509 _expect(byte, _CharCode.LF); 509 _expect(byte, _CharCode.LF);
510 _bodyEnd(); 510 _bodyEnd();
511 _reset(); 511 _reset();
512 break; 512 break;
513 513
514 case _State.BODY: 514 case _State.BODY:
515 // The body is not handled one byte at a time but in blocks. 515 // The body is not handled one byte at a time but in blocks.
516 int dataAvailable = lastIndex - index; 516 _index--;
517 int dataAvailable = _lastIndex - _index;
517 List<int> data; 518 List<int> data;
518 if (_remainingContent == null || 519 if (_remainingContent == null ||
519 dataAvailable <= _remainingContent) { 520 dataAvailable <= _remainingContent) {
520 data = new Uint8List(dataAvailable); 521 data = new Uint8List(dataAvailable);
521 data.setRange(0, dataAvailable, buffer, index); 522 data.setRange(0, dataAvailable, _buffer, _index);
522 } else { 523 } else {
523 data = new Uint8List(_remainingContent); 524 data = new Uint8List(_remainingContent);
524 data.setRange(0, _remainingContent, buffer, index); 525 data.setRange(0, _remainingContent, _buffer, _index);
525 } 526 }
526 527
527 if (dataReceived != null) dataReceived(data); 528 if (dataReceived != null) dataReceived(data);
528 if (_remainingContent != null) { 529 if (_remainingContent != null) {
529 _remainingContent -= data.length; 530 _remainingContent -= data.length;
530 } 531 }
531 index += data.length; 532 _index += data.length;
532 if (_remainingContent == 0) { 533 if (_remainingContent == 0) {
533 if (!_chunked) { 534 if (!_chunked) {
534 _bodyEnd(); 535 _bodyEnd();
535 _reset(); 536 _reset();
536 } else { 537 } else {
537 _state = _State.CHUNK_SIZE_STARTING_CR; 538 _state = _State.CHUNK_SIZE_STARTING_CR;
538 } 539 }
539 } 540 }
540
541 // Hack - as we always do index++ below.
542 index--;
543 break; 541 break;
544 542
545 case _State.FAILURE: 543 case _State.FAILURE:
546 // Should be unreachable. 544 // Should be unreachable.
547 assert(false); 545 assert(false);
548 break; 546 break;
549 547
550 default: 548 default:
551 // Should be unreachable. 549 // Should be unreachable.
552 assert(false); 550 assert(false);
553 break; 551 break;
554 } 552 }
555
556 // Move to the next byte.
557 index++;
558 } 553 }
559 } catch (e) { 554 } catch (e) {
560 // Report the error through the error callback if any. Otherwise 555 // Report the error through the error callback if any. Otherwise
561 // throw the error. 556 // throw the error.
562 if (error != null) { 557 if (error != null) {
563 error(e); 558 error(e);
564 _state = _State.FAILURE; 559 _state = _State.FAILURE;
565 } else { 560 } else {
566 throw e; 561 throw e;
567 } 562 }
568 } 563 }
569 564
570 // Return the number of bytes parsed. 565 // If all data is parsed or not needed due to failure there is no
571 return index - offset; 566 // need to hold on to the buffer.
567 if (_state != _State.UPGRADED) _releaseBuffer();
568 }
569
570 void writeList(List<int> buffer, int offset, int count) {
571 assert(_buffer == null);
572 _buffer = buffer;
573 _index = offset;
574 _lastIndex = offset + count;
575 _parse();
572 } 576 }
573 577
574 void connectionClosed() { 578 void connectionClosed() {
575 if (_state < _State.FIRST_BODY_STATE) { 579 if (_state < _State.FIRST_BODY_STATE) {
576 _state = _State.FAILURE; 580 _state = _State.FAILURE;
577 // Report the error through the error callback if any. Otherwise 581 // Report the error through the error callback if any. Otherwise
578 // throw the error. 582 // throw the error.
579 var e = new HttpParserException( 583 var e = new HttpParserException(
580 "Connection closed before full header was received"); 584 "Connection closed before full header was received");
581 if (error != null) { 585 if (error != null) {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
616 620
617 int get messageType => _messageType; 621 int get messageType => _messageType;
618 int get contentLength => _contentLength; 622 int get contentLength => _contentLength;
619 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED; 623 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED;
620 bool get persistentConnection => _persistentConnection; 624 bool get persistentConnection => _persistentConnection;
621 625
622 void set responseToMethod(String method) { _responseToMethod = method; } 626 void set responseToMethod(String method) { _responseToMethod = method; }
623 627
624 bool get isIdle => _state == _State.START; 628 bool get isIdle => _state == _State.START;
625 629
626 List<int> get unparsedData => _unparsedData; 630 List<int> readUnparsedData() {
631 if (_buffer == null) return [];
632 if (_index == _lastIndex) return [];
633 var result = _buffer.getRange(_index, _lastIndex - _index);
634 _releaseBuffer();
635 return result;
636 }
627 637
628 void _bodyEnd() { 638 void _bodyEnd() {
629 if (dataEnd != null) { 639 if (dataEnd != null) {
630 dataEnd(_messageType == _MessageType.RESPONSE && !_persistentConnection); 640 dataEnd(_messageType == _MessageType.RESPONSE && !_persistentConnection);
631 } 641 }
632 } 642 }
633 643
634 _reset() { 644 _reset() {
635 _state = _State.START; 645 _state = _State.START;
636 _messageType = _MessageType.UNDETERMINED; 646 _messageType = _MessageType.UNDETERMINED;
637 _headerField = new StringBuffer(); 647 _headerField = new StringBuffer();
638 _headerValue = new StringBuffer(); 648 _headerValue = new StringBuffer();
639 _method_or_status_code = new StringBuffer(); 649 _method_or_status_code = new StringBuffer();
640 _uri_or_reason_phrase = new StringBuffer(); 650 _uri_or_reason_phrase = new StringBuffer();
641 651
642 _httpVersion = _HttpVersion.UNDETERMINED; 652 _httpVersion = _HttpVersion.UNDETERMINED;
643 _contentLength = -1; 653 _contentLength = -1;
644 _persistentConnection = false; 654 _persistentConnection = false;
645 _connectionUpgrade = false; 655 _connectionUpgrade = false;
646 _chunked = false; 656 _chunked = false;
647 657
648 _noMessageBody = false; 658 _noMessageBody = false;
649 _responseToMethod = null; 659 _responseToMethod = null;
650 _remainingContent = null; 660 _remainingContent = null;
651 } 661 }
652 662
663 _releaseBuffer() {
664 _buffer = null;
665 _index = null;
666 _lastIndex = null;
667 }
668
653 bool _isTokenChar(int byte) { 669 bool _isTokenChar(int byte) {
654 return byte > 31 && byte < 128 && _Const.SEPARATORS.indexOf(byte) == -1; 670 return byte > 31 && byte < 128 && _Const.SEPARATORS.indexOf(byte) == -1;
655 } 671 }
656 672
657 List<String> _tokenizeFieldValue(String headerValue) { 673 List<String> _tokenizeFieldValue(String headerValue) {
658 List<String> tokens = new List<String>(); 674 List<String> tokens = new List<String>();
659 int start = 0; 675 int start = 0;
660 int index = 0; 676 int index = 0;
661 while (index < headerValue.length) { 677 while (index < headerValue.length) {
662 if (headerValue[index] == ",") { 678 if (headerValue[index] == ",") {
(...skipping 26 matching lines...) Expand all
689 return byte - 0x30; // 0 - 9 705 return byte - 0x30; // 0 - 9
690 } else if (0x41 <= byte && byte <= 0x46) { 706 } else if (0x41 <= byte && byte <= 0x46) {
691 return byte - 0x41 + 10; // A - F 707 return byte - 0x41 + 10; // A - F
692 } else if (0x61 <= byte && byte <= 0x66) { 708 } else if (0x61 <= byte && byte <= 0x66) {
693 return byte - 0x61 + 10; // a - f 709 return byte - 0x61 + 10; // a - f
694 } else { 710 } else {
695 throw new HttpParserException("Failed to parse HTTP"); 711 throw new HttpParserException("Failed to parse HTTP");
696 } 712 }
697 } 713 }
698 714
715 // The data that is currently being parsed.
716 List<int> _buffer;
717 int _index;
718 int _lastIndex;
719
699 int _state; 720 int _state;
700 int _httpVersionIndex; 721 int _httpVersionIndex;
701 int _messageType; 722 int _messageType;
702 StringBuffer _method_or_status_code; 723 StringBuffer _method_or_status_code;
703 StringBuffer _uri_or_reason_phrase; 724 StringBuffer _uri_or_reason_phrase;
704 StringBuffer _headerField; 725 StringBuffer _headerField;
705 StringBuffer _headerValue; 726 StringBuffer _headerValue;
706 727
707 int _httpVersion; 728 int _httpVersion;
708 int _contentLength; 729 int _contentLength;
709 bool _persistentConnection; 730 bool _persistentConnection;
710 bool _connectionUpgrade; 731 bool _connectionUpgrade;
711 bool _chunked; 732 bool _chunked;
712 733
713 bool _noMessageBody; 734 bool _noMessageBody;
714 String _responseToMethod; // Indicates the method used for the request. 735 String _responseToMethod; // Indicates the method used for the request.
715 int _remainingContent; 736 int _remainingContent;
716 737
717 List<int> _unparsedData; // Unparsed data after connection upgrade.
718 // Callbacks. 738 // Callbacks.
719 Function requestStart; 739 Function requestStart;
720 Function responseStart; 740 Function responseStart;
721 Function headerReceived; 741 Function headerReceived;
722 Function headersComplete; 742 Function headersComplete;
723 Function dataReceived; 743 Function dataReceived;
724 Function dataEnd; 744 Function dataEnd;
725 Function error; 745 Function error;
726 } 746 }
727 747
728 748
729 class HttpParserException implements Exception { 749 class HttpParserException implements Exception {
730 const HttpParserException([String this.message = ""]); 750 const HttpParserException([String this.message = ""]);
731 String toString() => "HttpParserException: $message"; 751 String toString() => "HttpParserException: $message";
732 final String message; 752 final String message;
733 } 753 }
OLDNEW
« no previous file with comments | « sdk/lib/io/http_impl.dart ('k') | sdk/lib/io/path_impl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698