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

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

Powered by Google App Engine
This is Rietveld 408576698