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

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

Issue 15256002: Rewrite parts of http-parser, to better handle connection errors and pausing. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Cleanup. Created 7 years, 7 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') | tests/standalone/io/http_parser_test.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) 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 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 factory _HttpParser.requestParser() { 190 factory _HttpParser.requestParser() {
191 return new _HttpParser._(true); 191 return new _HttpParser._(true);
192 } 192 }
193 193
194 factory _HttpParser.responseParser() { 194 factory _HttpParser.responseParser() {
195 return new _HttpParser._(false); 195 return new _HttpParser._(false);
196 } 196 }
197 197
198 _HttpParser._(this._requestParser) { 198 _HttpParser._(this._requestParser) {
199 _controller = new StreamController<_HttpIncoming>( 199 _controller = new StreamController<_HttpIncoming>(
200 onListen: _updateParsePauseState, 200 onListen: () {
201 onPause: _updateParsePauseState, 201 _socketSubscription.resume();
202 onResume: _updateParsePauseState, 202 _paused = false;
203 onCancel: _updateParsePauseState); 203 },
204 onPause: () {
205 _paused = true;
206 _pauseStateChanged();
207 },
208 onResume: () {
209 _paused = false;
210 _pauseStateChanged();
211 },
212 onCancel: () {
213 try {
214 _socketSubscription.cancel();
215 } catch (e) {
216 }
217 });
204 _reset(); 218 _reset();
205 } 219 }
206 220
207 221
208 StreamSubscription<_HttpIncoming> listen(void onData(_HttpIncoming event), 222 StreamSubscription<_HttpIncoming> listen(void onData(_HttpIncoming event),
209 {void onError(error), 223 {void onError(error),
210 void onDone(), 224 void onDone(),
211 bool cancelOnError}) { 225 bool cancelOnError}) {
212 return _controller.stream.listen(onData, 226 return _controller.stream.listen(onData,
213 onError: onError, 227 onError: onError,
214 onDone: onDone, 228 onDone: onDone,
215 cancelOnError: cancelOnError); 229 cancelOnError: cancelOnError);
216 } 230 }
217 231
218 Future<_HttpParser> addStream(Stream<List<int>> stream) { 232 Future<_HttpParser> addStream(Stream<List<int>> stream) {
219 // Listen to the stream and handle data accordingly. When a 233 // Listen to the stream and handle data accordingly. When a
220 // _HttpIncoming is created, _dataPause, _dataResume, _dataDone is 234 // _HttpIncoming is created, _dataPause, _dataResume, _dataDone is
221 // given to provide a way of controlling the parser. 235 // given to provide a way of controlling the parser.
222 // TODO(ajohnsen): Remove _dataPause, _dataResume and _dataDone and clean up 236 // TODO(ajohnsen): Remove _dataPause, _dataResume and _dataDone and clean up
223 // how the _HttpIncoming signals the parser. 237 // how the _HttpIncoming signals the parser.
224 var completer = new Completer(); 238 var completer = new Completer();
225 _socketSubscription = stream.listen( 239 _socketSubscription = stream.listen(
226 _onData, 240 _onData,
227 onError: _onError, 241 onError: _onError,
228 onDone: () { 242 onDone: () {
229 completer.complete(this); 243 completer.complete(this);
230 }); 244 });
245 _socketSubscription.pause();
231 return completer.future; 246 return completer.future;
232 } 247 }
233 248
234 Future<_HttpParser> close() { 249 Future<_HttpParser> close() {
235 _onDone(); 250 _onDone();
236 return new Future.value(this); 251 return new Future.value(this);
237 } 252 }
238 253
239 void _parse() { 254 void _parse() {
240 try { 255 try {
(...skipping 19 matching lines...) Expand all
260 if (_state == _State.CLOSED) { 275 if (_state == _State.CLOSED) {
261 throw new HttpParserException("Data on closed connection"); 276 throw new HttpParserException("Data on closed connection");
262 } 277 }
263 if (_state == _State.FAILURE) { 278 if (_state == _State.FAILURE) {
264 throw new HttpParserException("Data on failed connection"); 279 throw new HttpParserException("Data on failed connection");
265 } 280 }
266 while (_buffer != null && 281 while (_buffer != null &&
267 _index < _buffer.length && 282 _index < _buffer.length &&
268 _state != _State.FAILURE && 283 _state != _State.FAILURE &&
269 _state != _State.UPGRADED) { 284 _state != _State.UPGRADED) {
270 if (_paused) { 285 // Depending on _incoming, we either break on _bodyPaused or _paused.
286 if ((_incoming != null && _bodyPaused) ||
287 (_incoming == null && _paused)) {
271 _parserCalled = false; 288 _parserCalled = false;
272 return; 289 return;
273 } 290 }
274 int byte = _buffer[_index++]; 291 int byte = _buffer[_index++];
275 switch (_state) { 292 switch (_state) {
276 case _State.START: 293 case _State.START:
277 if (byte == _Const.HTTP[0]) { 294 if (byte == _Const.HTTP[0]) {
278 // Start parsing method or HTTP version. 295 // Start parsing method or HTTP version.
279 _httpVersionIndex = 1; 296 _httpVersionIndex = 1;
280 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION; 297 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION;
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after
555 new String.fromCharCodes(_uri_or_reason_phrase)); 572 new String.fromCharCodes(_uri_or_reason_phrase));
556 } else { 573 } else {
557 _incoming.statusCode = _statusCode; 574 _incoming.statusCode = _statusCode;
558 _incoming.reasonPhrase = 575 _incoming.reasonPhrase =
559 new String.fromCharCodes(_uri_or_reason_phrase); 576 new String.fromCharCodes(_uri_or_reason_phrase);
560 } 577 }
561 _method_or_status_code.clear(); 578 _method_or_status_code.clear();
562 _uri_or_reason_phrase.clear(); 579 _uri_or_reason_phrase.clear();
563 if (_connectionUpgrade) { 580 if (_connectionUpgrade) {
564 _incoming.upgraded = true; 581 _incoming.upgraded = true;
582 _parserCalled = false;
565 _controller.add(_incoming); 583 _controller.add(_incoming);
566 break; 584 return;
567 } 585 }
568 if (_transferLength == 0 || 586 if (_transferLength == 0 ||
569 (_messageType == _MessageType.RESPONSE && 587 (_messageType == _MessageType.RESPONSE &&
570 (_noMessageBody || _responseToMethod == "HEAD"))) { 588 (_noMessageBody || _responseToMethod == "HEAD"))) {
571 _reset(); 589 _reset();
572 var tmp = _incoming; 590 var tmp = _incoming;
573 _closeIncoming(); 591 _closeIncoming();
574 _controller.add(tmp); 592 _controller.add(tmp);
575 break; 593 break;
576 } else if (_chunked) { 594 } else if (_chunked) {
577 _state = _State.CHUNK_SIZE; 595 _state = _State.CHUNK_SIZE;
578 _remainingContent = 0; 596 _remainingContent = 0;
579 } else if (_transferLength > 0) { 597 } else if (_transferLength > 0) {
580 _remainingContent = _transferLength; 598 _remainingContent = _transferLength;
581 _state = _State.BODY; 599 _state = _State.BODY;
582 } else { 600 } else {
583 // Neither chunked nor content length. End of body 601 // Neither chunked nor content length. End of body
584 // indicated by close. 602 // indicated by close.
585 _state = _State.BODY; 603 _state = _State.BODY;
586 } 604 }
605 _parserCalled = false;
587 _controller.add(_incoming); 606 _controller.add(_incoming);
588 break; 607 return;
589 608
590 case _State.CHUNK_SIZE_STARTING_CR: 609 case _State.CHUNK_SIZE_STARTING_CR:
591 _expect(byte, _CharCode.CR); 610 _expect(byte, _CharCode.CR);
592 _state = _State.CHUNK_SIZE_STARTING_LF; 611 _state = _State.CHUNK_SIZE_STARTING_LF;
593 break; 612 break;
594 613
595 case _State.CHUNK_SIZE_STARTING_LF: 614 case _State.CHUNK_SIZE_STARTING_LF:
596 _expect(byte, _CharCode.LF); 615 _expect(byte, _CharCode.LF);
597 _state = _State.CHUNK_SIZE; 616 _state = _State.CHUNK_SIZE;
598 break; 617 break;
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after
857 } else if (0x61 <= byte && byte <= 0x66) { 876 } else if (0x61 <= byte && byte <= 0x66) {
858 return byte - 0x61 + 10; // a - f 877 return byte - 0x61 + 10; // a - f
859 } else { 878 } else {
860 throw new HttpParserException("Failed to parse HTTP"); 879 throw new HttpParserException("Failed to parse HTTP");
861 } 880 }
862 } 881 }
863 882
864 void _createIncoming(int transferLength) { 883 void _createIncoming(int transferLength) {
865 assert(_incoming == null); 884 assert(_incoming == null);
866 assert(_bodyController == null); 885 assert(_bodyController == null);
886 assert(!_bodyPaused);
887 var incoming;
867 _bodyController = new StreamController<List<int>>( 888 _bodyController = new StreamController<List<int>>(
868 onListen: _bodySubscriptionStateChange, 889 onListen: () {
869 onPause: _updateParsePauseState, 890 if (incoming != _incoming) return;
870 onResume: _updateParsePauseState, 891 assert(_bodyPaused);
871 onCancel: _bodySubscriptionStateChange); 892 _bodyPaused = false;
872 _incoming = new _HttpIncoming( 893 _pauseStateChanged();
894 },
895 onPause: () {
896 if (incoming != _incoming) return;
897 assert(!_bodyPaused);
898 _bodyPaused = true;
899 _pauseStateChanged();
900 },
901 onResume: () {
902 if (incoming != _incoming) return;
903 assert(_bodyPaused);
904 _bodyPaused = false;
905 _pauseStateChanged();
906 },
907 onCancel: () {
908 if (incoming != _incoming) return;
909 if (_socketSubscription != null) {
910 _socketSubscription.cancel();
911 }
912 _closeIncoming();
913 _controller.close();
914 });
915 incoming = _incoming = new _HttpIncoming(
873 _headers, transferLength, _bodyController.stream); 916 _headers, transferLength, _bodyController.stream);
874 _pauseParsing(); // Needed to handle detaching - don't start on the body! 917 _bodyPaused = true;
918 _pauseStateChanged();
875 } 919 }
876 920
877 void _closeIncoming() { 921 void _closeIncoming() {
878 // Ignore multiple close (can happend in re-entrance). 922 // Ignore multiple close (can happend in re-entrance).
879 if (_incoming == null) return; 923 if (_incoming == null) return;
880 var tmp = _incoming; 924 var tmp = _incoming;
925 tmp.close();
881 _incoming = null; 926 _incoming = null;
882 tmp.close();
883 if (_bodyController != null) { 927 if (_bodyController != null) {
884 _bodyController.close(); 928 _bodyController.close();
885 _bodyController = null; 929 _bodyController = null;
886 } 930 }
887 _updateParsePauseState(); 931 _bodyPaused = false;
932 _pauseStateChanged();
888 } 933 }
889 934
890 void _continueParsing() { 935 void _pauseStateChanged() {
891 _paused = false; 936 void update(bool pause) {
892 if (!_parserCalled && _buffer != null) _parse(); 937 if (pause && !_socketSubscription.isPaused) {
893 } 938 _socketSubscription.pause();
939 } else if (!pause && _socketSubscription.isPaused) {
940 _socketSubscription.resume();
941 }
942 }
894 943
895 void _pauseParsing() { 944 if (_incoming != null) {
896 _paused = true; 945 if (!_bodyPaused && !_parserCalled) {
897 } 946 _parse();
898
899 void _bodySubscriptionStateChange() {
900 if (_incoming != null && !_bodyController.hasListener) {
901 _closeIncoming();
902 } else {
903 _updateParsePauseState();
904 }
905 }
906
907 void _updateParsePauseState() {
908 if (_bodyController != null) {
909 if (_bodyController.hasListener && !_bodyController.isPaused) {
910 _continueParsing();
911 } else {
912 _pauseParsing();
913 } 947 }
914 } else { 948 } else {
915 if (_controller.hasListener && !_controller.isPaused) { 949 if (!_paused && !_parserCalled) {
916 _continueParsing(); 950 _parse();
917 } else {
918 _pauseParsing();
919 } 951 }
920 } 952 }
921 } 953 }
922 954
923 void error(error, [stackTrace]) { 955 void error(error, [stackTrace]) {
924 if (_socketSubscription != null) _socketSubscription.cancel(); 956 if (_socketSubscription != null) _socketSubscription.cancel();
925 _state = _State.FAILURE; 957 _state = _State.FAILURE;
926 _controller.addError(error, stackTrace); 958 _controller.addError(error, stackTrace);
927 _controller.close(); 959 _controller.close();
928 } 960 }
(...skipping 23 matching lines...) Expand all
952 984
953 bool _noMessageBody; 985 bool _noMessageBody;
954 String _responseToMethod; // Indicates the method used for the request. 986 String _responseToMethod; // Indicates the method used for the request.
955 int _remainingContent; 987 int _remainingContent;
956 988
957 _HttpHeaders _headers; 989 _HttpHeaders _headers;
958 990
959 // The current incoming connection. 991 // The current incoming connection.
960 _HttpIncoming _incoming; 992 _HttpIncoming _incoming;
961 StreamSubscription _socketSubscription; 993 StreamSubscription _socketSubscription;
962 bool _paused = false; 994 bool _paused = true;
995 bool _bodyPaused = false;
963 Completer _pauseCompleter; 996 Completer _pauseCompleter;
964 StreamController<_HttpIncoming> _controller; 997 StreamController<_HttpIncoming> _controller;
965 StreamController<List<int>> _bodyController; 998 StreamController<List<int>> _bodyController;
966 } 999 }
967 1000
968 1001
969 class HttpParserException implements Exception { 1002 class HttpParserException implements Exception {
970 const HttpParserException([String this.message = ""]); 1003 const HttpParserException([String this.message = ""]);
971 String toString() => "HttpParserException: $message"; 1004 String toString() => "HttpParserException: $message";
972 final String message; 1005 final String message;
973 } 1006 }
OLDNEW
« no previous file with comments | « sdk/lib/io/http_impl.dart ('k') | tests/standalone/io/http_parser_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698