Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |