| 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]; |
| 11 // Bytes for "HTTP/1.". | 11 // Bytes for "HTTP/1.". |
| 12 static const HTTP1DOT = const [72, 84, 84, 80, 47, 49, 46]; | 12 static const HTTP1DOT = const [72, 84, 84, 80, 47, 49, 46]; |
| 13 // Bytes for "HTTP/1.0". | 13 // Bytes for "HTTP/1.0". |
| 14 static const HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48]; | 14 static const HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48]; |
| 15 // Bytes for "HTTP/1.1". | 15 // Bytes for "HTTP/1.1". |
| 16 static const HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49]; | 16 static const HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49]; |
| 17 | 17 |
| 18 static const bool T = true; | 18 static const bool T = true; |
| 19 static const bool F = false; | 19 static const bool F = false; |
| 20 // Loopup-map for the following characters: '()<>@,;:\\"/[]?={} \t'. | 20 // Loopup-map for the following characters: '()<>@,;:\\"/[]?={} \t'. |
| 21 static const SEPARATOR_MAP = const [ | 21 static const SEPARATOR_MAP = const [ |
| 22 F,F,F,F,F,F,F,F,F,T,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,T,F,T,F,F, | 22 F, F, F, F, F, F, F, F, F, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, // |
| 23 F,F,F,T,T,F,F,T,F,F,T,F,F,F,F,F,F,F,F,F,F,T,T,T,T,T,T,T,F,F,F,F,F,F,F,F,F, | 23 F, F, F, F, F, F, F, F, T, F, T, F, F, F, F, F, T, T, F, F, T, F, F, T, // |
| 24 F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,T,T,T,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F, | 24 F, F, F, F, F, F, F, F, F, F, T, T, T, T, T, T, T, F, F, F, F, F, F, F, // |
| 25 F,F,F,F,F,F,F,F,F,F,F,F,T,F,T,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F, | 25 F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, T, T, T, F, F, // |
| 26 F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F, | 26 F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, // |
| 27 F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F, | 27 F, F, F, T, F, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, // |
| 28 F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F]; | 28 F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, // |
| 29 F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, // |
| 30 F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, // |
| 31 F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, // |
| 32 F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F |
| 33 ]; |
| 29 } | 34 } |
| 30 | 35 |
| 31 | |
| 32 // Frequently used character codes. | 36 // Frequently used character codes. |
| 33 class _CharCode { | 37 class _CharCode { |
| 34 static const int HT = 9; | 38 static const int HT = 9; |
| 35 static const int LF = 10; | 39 static const int LF = 10; |
| 36 static const int CR = 13; | 40 static const int CR = 13; |
| 37 static const int SP = 32; | 41 static const int SP = 32; |
| 38 static const int AMPERSAND = 38; | 42 static const int AMPERSAND = 38; |
| 39 static const int COMMA = 44; | 43 static const int COMMA = 44; |
| 40 static const int DASH = 45; | 44 static const int DASH = 45; |
| 41 static const int SLASH = 47; | 45 static const int SLASH = 47; |
| 42 static const int ZERO = 48; | 46 static const int ZERO = 48; |
| 43 static const int ONE = 49; | 47 static const int ONE = 49; |
| 44 static const int COLON = 58; | 48 static const int COLON = 58; |
| 45 static const int SEMI_COLON = 59; | 49 static const int SEMI_COLON = 59; |
| 46 static const int EQUAL = 61; | 50 static const int EQUAL = 61; |
| 47 } | 51 } |
| 48 | 52 |
| 49 | |
| 50 // States of the HTTP parser state machine. | 53 // States of the HTTP parser state machine. |
| 51 class _State { | 54 class _State { |
| 52 static const int START = 0; | 55 static const int START = 0; |
| 53 static const int METHOD_OR_RESPONSE_HTTP_VERSION = 1; | 56 static const int METHOD_OR_RESPONSE_HTTP_VERSION = 1; |
| 54 static const int RESPONSE_HTTP_VERSION = 2; | 57 static const int RESPONSE_HTTP_VERSION = 2; |
| 55 static const int REQUEST_LINE_METHOD = 3; | 58 static const int REQUEST_LINE_METHOD = 3; |
| 56 static const int REQUEST_LINE_URI = 4; | 59 static const int REQUEST_LINE_URI = 4; |
| 57 static const int REQUEST_LINE_HTTP_VERSION = 5; | 60 static const int REQUEST_LINE_HTTP_VERSION = 5; |
| 58 static const int REQUEST_LINE_ENDING = 6; | 61 static const int REQUEST_LINE_ENDING = 6; |
| 59 static const int RESPONSE_LINE_STATUS_CODE = 7; | 62 static const int RESPONSE_LINE_STATUS_CODE = 7; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 89 static const int HTTP11 = 2; | 92 static const int HTTP11 = 2; |
| 90 } | 93 } |
| 91 | 94 |
| 92 // States of the HTTP parser state machine. | 95 // States of the HTTP parser state machine. |
| 93 class _MessageType { | 96 class _MessageType { |
| 94 static const int UNDETERMINED = 0; | 97 static const int UNDETERMINED = 0; |
| 95 static const int REQUEST = 1; | 98 static const int REQUEST = 1; |
| 96 static const int RESPONSE = 0; | 99 static const int RESPONSE = 0; |
| 97 } | 100 } |
| 98 | 101 |
| 99 | |
| 100 /** | 102 /** |
| 101 * The _HttpDetachedStreamSubscription takes a subscription and some extra data, | 103 * The _HttpDetachedStreamSubscription takes a subscription and some extra data, |
| 102 * and makes it possible to "inject" the data in from of other data events | 104 * and makes it possible to "inject" the data in from of other data events |
| 103 * from the subscription. | 105 * from the subscription. |
| 104 * | 106 * |
| 105 * It does so by overriding pause/resume, so that once the | 107 * It does so by overriding pause/resume, so that once the |
| 106 * _HttpDetachedStreamSubscription is resumed, it'll deliver the data before | 108 * _HttpDetachedStreamSubscription is resumed, it'll deliver the data before |
| 107 * resuming the underlaying subscription. | 109 * resuming the underlaying subscription. |
| 108 */ | 110 */ |
| 109 class _HttpDetachedStreamSubscription implements StreamSubscription<List<int>> { | 111 class _HttpDetachedStreamSubscription implements StreamSubscription<List<int>> { |
| 110 StreamSubscription<List<int>> _subscription; | 112 StreamSubscription<List<int>> _subscription; |
| 111 List<int> _injectData; | 113 List<int> _injectData; |
| 112 bool _isCanceled = false; | 114 bool _isCanceled = false; |
| 113 int _pauseCount = 1; | 115 int _pauseCount = 1; |
| 114 Function _userOnData; | 116 Function _userOnData; |
| 115 bool _scheduled = false; | 117 bool _scheduled = false; |
| 116 | 118 |
| 117 _HttpDetachedStreamSubscription(this._subscription, | 119 _HttpDetachedStreamSubscription( |
| 118 this._injectData, | 120 this._subscription, this._injectData, this._userOnData); |
| 119 this._userOnData); | |
| 120 | 121 |
| 121 bool get isPaused => _subscription.isPaused; | 122 bool get isPaused => _subscription.isPaused; |
| 122 | 123 |
| 123 Future<T> asFuture<T>([T futureValue]) => | 124 Future<T> |
| 124 _subscription.asFuture<T>(futureValue); | 125 asFuture<T>([T futureValue]) => _subscription.asFuture<T>(futureValue); |
| 125 | 126 |
| 126 Future cancel() { | 127 Future cancel() { |
| 127 _isCanceled = true; | 128 _isCanceled = true; |
| 128 _injectData = null; | 129 _injectData = null; |
| 129 return _subscription.cancel(); | 130 return _subscription.cancel(); |
| 130 } | 131 } |
| 131 | 132 |
| 132 void onData(void handleData(List<int> data)) { | 133 void onData(void handleData(List<int> data)) { |
| 133 _userOnData = handleData; | 134 _userOnData = handleData; |
| 134 _subscription.onData(handleData); | 135 _subscription.onData(handleData); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 // To ensure that 'subscription.isPaused' is false, we resume the | 175 // To ensure that 'subscription.isPaused' is false, we resume the |
| 175 // subscription here. This is fine as potential events are delayed. | 176 // subscription here. This is fine as potential events are delayed. |
| 176 _subscription.resume(); | 177 _subscription.resume(); |
| 177 if (_userOnData != null) { | 178 if (_userOnData != null) { |
| 178 _userOnData(data); | 179 _userOnData(data); |
| 179 } | 180 } |
| 180 }); | 181 }); |
| 181 } | 182 } |
| 182 } | 183 } |
| 183 | 184 |
| 184 | |
| 185 class _HttpDetachedIncoming extends Stream<List<int>> { | 185 class _HttpDetachedIncoming extends Stream<List<int>> { |
| 186 final StreamSubscription<List<int>> subscription; | 186 final StreamSubscription<List<int>> subscription; |
| 187 final List<int> bufferedData; | 187 final List<int> bufferedData; |
| 188 | 188 |
| 189 _HttpDetachedIncoming(this.subscription, this.bufferedData); | 189 _HttpDetachedIncoming(this.subscription, this.bufferedData); |
| 190 | 190 |
| 191 StreamSubscription<List<int>> listen(void onData(List<int> event), | 191 StreamSubscription<List<int>> listen(void onData(List<int> event), |
| 192 {Function onError, | 192 {Function onError, void onDone(), bool cancelOnError}) { |
| 193 void onDone(), | |
| 194 bool cancelOnError}) { | |
| 195 if (subscription != null) { | 193 if (subscription != null) { |
| 196 subscription | 194 subscription |
| 197 ..onData(onData) | 195 ..onData(onData) |
| 198 ..onError(onError) | 196 ..onError(onError) |
| 199 ..onDone(onDone); | 197 ..onDone(onDone); |
| 200 if (bufferedData == null) { | 198 if (bufferedData == null) { |
| 201 return subscription..resume(); | 199 return subscription..resume(); |
| 202 } | 200 } |
| 203 return new _HttpDetachedStreamSubscription(subscription, | 201 return new _HttpDetachedStreamSubscription( |
| 204 bufferedData, | 202 subscription, bufferedData, onData)..resume(); |
| 205 onData) | |
| 206 ..resume(); | |
| 207 } else { | 203 } else { |
| 208 // TODO(26379): add test for this branch. | 204 // TODO(26379): add test for this branch. |
| 209 return new Stream<List<int>>.fromIterable([bufferedData]) | 205 return new Stream<List<int>>.fromIterable([bufferedData]).listen(onData, |
| 210 .listen(onData, | 206 onError: onError, onDone: onDone, cancelOnError: cancelOnError); |
| 211 onError: onError, | |
| 212 onDone: onDone, | |
| 213 cancelOnError: cancelOnError); | |
| 214 } | 207 } |
| 215 } | 208 } |
| 216 } | 209 } |
| 217 | 210 |
| 218 | |
| 219 /** | 211 /** |
| 220 * HTTP parser which parses the data stream given to [consume]. | 212 * HTTP parser which parses the data stream given to [consume]. |
| 221 * | 213 * |
| 222 * If an HTTP parser error occours, the parser will signal an error to either | 214 * If an HTTP parser error occours, the parser will signal an error to either |
| 223 * the current _HttpIncoming or the _parser itself. | 215 * the current _HttpIncoming or the _parser itself. |
| 224 * | 216 * |
| 225 * The connection upgrades (e.g. switching from HTTP/1.1 to the | 217 * The connection upgrades (e.g. switching from HTTP/1.1 to the |
| 226 * WebSocket protocol) is handled in a special way. If connection | 218 * WebSocket protocol) is handled in a special way. If connection |
| 227 * upgrade is specified in the headers, then on the callback to | 219 * upgrade is specified in the headers, then on the callback to |
| 228 * [:responseStart:] the [:upgrade:] property on the [:HttpParser:] | 220 * [:responseStart:] the [:upgrade:] property on the [:HttpParser:] |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 297 _pauseStateChanged(); | 289 _pauseStateChanged(); |
| 298 }, | 290 }, |
| 299 onCancel: () { | 291 onCancel: () { |
| 300 if (_socketSubscription != null) { | 292 if (_socketSubscription != null) { |
| 301 _socketSubscription.cancel(); | 293 _socketSubscription.cancel(); |
| 302 } | 294 } |
| 303 }); | 295 }); |
| 304 _reset(); | 296 _reset(); |
| 305 } | 297 } |
| 306 | 298 |
| 307 | |
| 308 StreamSubscription<_HttpIncoming> listen(void onData(_HttpIncoming event), | 299 StreamSubscription<_HttpIncoming> listen(void onData(_HttpIncoming event), |
| 309 {Function onError, | 300 {Function onError, void onDone(), bool cancelOnError}) { |
| 310 void onDone(), | |
| 311 bool cancelOnError}) { | |
| 312 return _controller.stream.listen(onData, | 301 return _controller.stream.listen(onData, |
| 313 onError: onError, | 302 onError: onError, onDone: onDone, cancelOnError: cancelOnError); |
| 314 onDone: onDone, | |
| 315 cancelOnError: cancelOnError); | |
| 316 } | 303 } |
| 317 | 304 |
| 318 void listenToStream(Stream<List<int>> stream) { | 305 void listenToStream(Stream<List<int>> stream) { |
| 319 // Listen to the stream and handle data accordingly. When a | 306 // Listen to the stream and handle data accordingly. When a |
| 320 // _HttpIncoming is created, _dataPause, _dataResume, _dataDone is | 307 // _HttpIncoming is created, _dataPause, _dataResume, _dataDone is |
| 321 // given to provide a way of controlling the parser. | 308 // given to provide a way of controlling the parser. |
| 322 // TODO(ajohnsen): Remove _dataPause, _dataResume and _dataDone and clean up | 309 // TODO(ajohnsen): Remove _dataPause, _dataResume and _dataDone and clean up |
| 323 // how the _HttpIncoming signals the parser. | 310 // how the _HttpIncoming signals the parser. |
| 324 _socketSubscription = stream.listen( | 311 _socketSubscription = |
| 325 _onData, | 312 stream.listen(_onData, onError: _controller.addError, onDone: _onDone); |
| 326 onError: _controller.addError, | |
| 327 onDone: _onDone); | |
| 328 } | 313 } |
| 329 | 314 |
| 330 void _parse() { | 315 void _parse() { |
| 331 try { | 316 try { |
| 332 _doParse(); | 317 _doParse(); |
| 333 } catch (e, s) { | 318 } catch (e, s) { |
| 334 _state = _State.FAILURE; | 319 _state = _State.FAILURE; |
| 335 _reportError(e, s); | 320 _reportError(e, s); |
| 336 } | 321 } |
| 337 } | 322 } |
| 338 | 323 |
| 339 // Process end of headers. Returns true if the parser should stop | 324 // Process end of headers. Returns true if the parser should stop |
| 340 // parsing and return. This will be in case of either an upgrade | 325 // parsing and return. This will be in case of either an upgrade |
| 341 // request or a request or response with an empty body. | 326 // request or a request or response with an empty body. |
| 342 bool _headersEnd() { | 327 bool _headersEnd() { |
| 343 _headers._mutable = false; | 328 _headers._mutable = false; |
| 344 | 329 |
| 345 _transferLength = _headers.contentLength; | 330 _transferLength = _headers.contentLength; |
| 346 // Ignore the Content-Length header if Transfer-Encoding | 331 // Ignore the Content-Length header if Transfer-Encoding |
| 347 // is chunked (RFC 2616 section 4.4) | 332 // is chunked (RFC 2616 section 4.4) |
| 348 if (_chunked) _transferLength = -1; | 333 if (_chunked) _transferLength = -1; |
| 349 | 334 |
| 350 // If a request message has neither Content-Length nor | 335 // If a request message has neither Content-Length nor |
| 351 // Transfer-Encoding the message must not have a body (RFC | 336 // Transfer-Encoding the message must not have a body (RFC |
| 352 // 2616 section 4.3). | 337 // 2616 section 4.3). |
| 353 if (_messageType == _MessageType.REQUEST && | 338 if (_messageType == _MessageType.REQUEST && |
| 354 _transferLength < 0 && | 339 _transferLength < 0 && |
| 355 _chunked == false) { | 340 _chunked == false) { |
| 356 _transferLength = 0; | 341 _transferLength = 0; |
| 357 } | 342 } |
| 358 if (_connectionUpgrade) { | 343 if (_connectionUpgrade) { |
| 359 _state = _State.UPGRADED; | 344 _state = _State.UPGRADED; |
| 360 _transferLength = 0; | 345 _transferLength = 0; |
| 361 } | 346 } |
| 362 _createIncoming(_transferLength); | 347 _createIncoming(_transferLength); |
| 363 if (_requestParser) { | 348 if (_requestParser) { |
| 364 _incoming.method = | 349 _incoming.method = new String.fromCharCodes(_method); |
| 365 new String.fromCharCodes(_method); | |
| 366 _incoming.uri = | 350 _incoming.uri = |
| 367 Uri.parse( | 351 Uri.parse(new String.fromCharCodes(_uri_or_reason_phrase)); |
| 368 new String.fromCharCodes(_uri_or_reason_phrase)); | |
| 369 } else { | 352 } else { |
| 370 _incoming.statusCode = _statusCode; | 353 _incoming.statusCode = _statusCode; |
| 371 _incoming.reasonPhrase = | 354 _incoming.reasonPhrase = new String.fromCharCodes(_uri_or_reason_phrase); |
| 372 new String.fromCharCodes(_uri_or_reason_phrase); | |
| 373 } | 355 } |
| 374 _method.clear(); | 356 _method.clear(); |
| 375 _uri_or_reason_phrase.clear(); | 357 _uri_or_reason_phrase.clear(); |
| 376 if (_connectionUpgrade) { | 358 if (_connectionUpgrade) { |
| 377 _incoming.upgraded = true; | 359 _incoming.upgraded = true; |
| 378 _parserCalled = false; | 360 _parserCalled = false; |
| 379 var tmp = _incoming; | 361 var tmp = _incoming; |
| 380 _closeIncoming(); | 362 _closeIncoming(); |
| 381 _controller.add(tmp); | 363 _controller.add(tmp); |
| 382 return true; | 364 return true; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 416 void _doParse() { | 398 void _doParse() { |
| 417 assert(!_parserCalled); | 399 assert(!_parserCalled); |
| 418 _parserCalled = true; | 400 _parserCalled = true; |
| 419 if (_state == _State.CLOSED) { | 401 if (_state == _State.CLOSED) { |
| 420 throw new HttpException("Data on closed connection"); | 402 throw new HttpException("Data on closed connection"); |
| 421 } | 403 } |
| 422 if (_state == _State.FAILURE) { | 404 if (_state == _State.FAILURE) { |
| 423 throw new HttpException("Data on failed connection"); | 405 throw new HttpException("Data on failed connection"); |
| 424 } | 406 } |
| 425 while (_buffer != null && | 407 while (_buffer != null && |
| 426 _index < _buffer.length && | 408 _index < _buffer.length && |
| 427 _state != _State.FAILURE && | 409 _state != _State.FAILURE && |
| 428 _state != _State.UPGRADED) { | 410 _state != _State.UPGRADED) { |
| 429 // Depending on _incoming, we either break on _bodyPaused or _paused. | 411 // Depending on _incoming, we either break on _bodyPaused or _paused. |
| 430 if ((_incoming != null && _bodyPaused) || | 412 if ((_incoming != null && _bodyPaused) || |
| 431 (_incoming == null && _paused)) { | 413 (_incoming == null && _paused)) { |
| 432 _parserCalled = false; | 414 _parserCalled = false; |
| 433 return; | 415 return; |
| 434 } | 416 } |
| 435 int byte = _buffer[_index++]; | 417 int byte = _buffer[_index++]; |
| 436 switch (_state) { | 418 switch (_state) { |
| 437 case _State.START: | 419 case _State.START: |
| 438 if (byte == _Const.HTTP[0]) { | 420 if (byte == _Const.HTTP[0]) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 451 _state = _State.REQUEST_LINE_METHOD; | 433 _state = _State.REQUEST_LINE_METHOD; |
| 452 } | 434 } |
| 453 break; | 435 break; |
| 454 | 436 |
| 455 case _State.METHOD_OR_RESPONSE_HTTP_VERSION: | 437 case _State.METHOD_OR_RESPONSE_HTTP_VERSION: |
| 456 if (_httpVersionIndex < _Const.HTTP.length && | 438 if (_httpVersionIndex < _Const.HTTP.length && |
| 457 byte == _Const.HTTP[_httpVersionIndex]) { | 439 byte == _Const.HTTP[_httpVersionIndex]) { |
| 458 // Continue parsing HTTP version. | 440 // Continue parsing HTTP version. |
| 459 _httpVersionIndex++; | 441 _httpVersionIndex++; |
| 460 } else if (_httpVersionIndex == _Const.HTTP.length && | 442 } else if (_httpVersionIndex == _Const.HTTP.length && |
| 461 byte == _CharCode.SLASH) { | 443 byte == _CharCode.SLASH) { |
| 462 // HTTP/ parsed. As method is a token this cannot be a | 444 // HTTP/ parsed. As method is a token this cannot be a |
| 463 // method anymore. | 445 // method anymore. |
| 464 _httpVersionIndex++; | 446 _httpVersionIndex++; |
| 465 if (_requestParser) { | 447 if (_requestParser) { |
| 466 throw new HttpException("Invalid request line"); | 448 throw new HttpException("Invalid request line"); |
| 467 } | 449 } |
| 468 _state = _State.RESPONSE_HTTP_VERSION; | 450 _state = _State.RESPONSE_HTTP_VERSION; |
| 469 } else { | 451 } else { |
| 470 // Did not parse HTTP version. Expect method instead. | 452 // Did not parse HTTP version. Expect method instead. |
| 471 for (int i = 0; i < _httpVersionIndex; i++) { | 453 for (int i = 0; i < _httpVersionIndex; i++) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 483 } | 465 } |
| 484 } | 466 } |
| 485 break; | 467 break; |
| 486 | 468 |
| 487 case _State.RESPONSE_HTTP_VERSION: | 469 case _State.RESPONSE_HTTP_VERSION: |
| 488 if (_httpVersionIndex < _Const.HTTP1DOT.length) { | 470 if (_httpVersionIndex < _Const.HTTP1DOT.length) { |
| 489 // Continue parsing HTTP version. | 471 // Continue parsing HTTP version. |
| 490 _expect(byte, _Const.HTTP1DOT[_httpVersionIndex]); | 472 _expect(byte, _Const.HTTP1DOT[_httpVersionIndex]); |
| 491 _httpVersionIndex++; | 473 _httpVersionIndex++; |
| 492 } else if (_httpVersionIndex == _Const.HTTP1DOT.length && | 474 } else if (_httpVersionIndex == _Const.HTTP1DOT.length && |
| 493 byte == _CharCode.ONE) { | 475 byte == _CharCode.ONE) { |
| 494 // HTTP/1.1 parsed. | 476 // HTTP/1.1 parsed. |
| 495 _httpVersion = _HttpVersion.HTTP11; | 477 _httpVersion = _HttpVersion.HTTP11; |
| 496 _persistentConnection = true; | 478 _persistentConnection = true; |
| 497 _httpVersionIndex++; | 479 _httpVersionIndex++; |
| 498 } else if (_httpVersionIndex == _Const.HTTP1DOT.length && | 480 } else if (_httpVersionIndex == _Const.HTTP1DOT.length && |
| 499 byte == _CharCode.ZERO) { | 481 byte == _CharCode.ZERO) { |
| 500 // HTTP/1.0 parsed. | 482 // HTTP/1.0 parsed. |
| 501 _httpVersion = _HttpVersion.HTTP10; | 483 _httpVersion = _HttpVersion.HTTP10; |
| 502 _persistentConnection = false; | 484 _persistentConnection = false; |
| 503 _httpVersionIndex++; | 485 _httpVersionIndex++; |
| 504 } else if (_httpVersionIndex == _Const.HTTP1DOT.length + 1) { | 486 } else if (_httpVersionIndex == _Const.HTTP1DOT.length + 1) { |
| 505 _expect(byte, _CharCode.SP); | 487 _expect(byte, _CharCode.SP); |
| 506 // HTTP version parsed. | 488 // HTTP version parsed. |
| 507 _state = _State.RESPONSE_LINE_STATUS_CODE; | 489 _state = _State.RESPONSE_LINE_STATUS_CODE; |
| 508 } else { | 490 } else { |
| 509 throw new HttpException("Invalid response line"); | 491 throw new HttpException("Invalid response line"); |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 601 } | 583 } |
| 602 break; | 584 break; |
| 603 | 585 |
| 604 case _State.RESPONSE_LINE_ENDING: | 586 case _State.RESPONSE_LINE_ENDING: |
| 605 _expect(byte, _CharCode.LF); | 587 _expect(byte, _CharCode.LF); |
| 606 _messageType == _MessageType.RESPONSE; | 588 _messageType == _MessageType.RESPONSE; |
| 607 if (_statusCode < 100 || _statusCode > 599) { | 589 if (_statusCode < 100 || _statusCode > 599) { |
| 608 throw new HttpException("Invalid response status code"); | 590 throw new HttpException("Invalid response status code"); |
| 609 } else { | 591 } else { |
| 610 // Check whether this response will never have a body. | 592 // Check whether this response will never have a body. |
| 611 if (_statusCode <= 199 || _statusCode == 204 || | 593 if (_statusCode <= 199 || |
| 594 _statusCode == 204 || |
| 612 _statusCode == 304) { | 595 _statusCode == 304) { |
| 613 _noMessageBody = true; | 596 _noMessageBody = true; |
| 614 } | 597 } |
| 615 } | 598 } |
| 616 _state = _State.HEADER_START; | 599 _state = _State.HEADER_START; |
| 617 break; | 600 break; |
| 618 | 601 |
| 619 case _State.HEADER_START: | 602 case _State.HEADER_START: |
| 620 _headers = new _HttpHeaders(version); | 603 _headers = new _HttpHeaders(version); |
| 621 if (byte == _CharCode.CR) { | 604 if (byte == _CharCode.CR) { |
| 622 _state = _State.HEADER_ENDING; | 605 _state = _State.HEADER_ENDING; |
| 623 } else if (byte == _CharCode.LF) { | 606 } else if (byte == _CharCode.LF) { |
| 624 _state = _State.HEADER_ENDING; | 607 _state = _State.HEADER_ENDING; |
| 625 _index--; // Make the new state see the LF again. | 608 _index--; // Make the new state see the LF again. |
| 626 } else { | 609 } else { |
| 627 // Start of new header field. | 610 // Start of new header field. |
| 628 _headerField.add(_toLowerCaseByte(byte)); | 611 _headerField.add(_toLowerCaseByte(byte)); |
| 629 _state = _State.HEADER_FIELD; | 612 _state = _State.HEADER_FIELD; |
| 630 } | 613 } |
| 631 break; | 614 break; |
| 632 | 615 |
| 633 case _State.HEADER_FIELD: | 616 case _State.HEADER_FIELD: |
| 634 if (byte == _CharCode.COLON) { | 617 if (byte == _CharCode.COLON) { |
| 635 _state = _State.HEADER_VALUE_START; | 618 _state = _State.HEADER_VALUE_START; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 674 } else { | 657 } else { |
| 675 String headerField = new String.fromCharCodes(_headerField); | 658 String headerField = new String.fromCharCodes(_headerField); |
| 676 String headerValue = new String.fromCharCodes(_headerValue); | 659 String headerValue = new String.fromCharCodes(_headerValue); |
| 677 if (headerField == "transfer-encoding" && | 660 if (headerField == "transfer-encoding" && |
| 678 _caseInsensitiveCompare("chunked".codeUnits, _headerValue)) { | 661 _caseInsensitiveCompare("chunked".codeUnits, _headerValue)) { |
| 679 _chunked = true; | 662 _chunked = true; |
| 680 } | 663 } |
| 681 if (headerField == "connection") { | 664 if (headerField == "connection") { |
| 682 List<String> tokens = _tokenizeFieldValue(headerValue); | 665 List<String> tokens = _tokenizeFieldValue(headerValue); |
| 683 for (int i = 0; i < tokens.length; i++) { | 666 for (int i = 0; i < tokens.length; i++) { |
| 684 if (_caseInsensitiveCompare("upgrade".codeUnits, | 667 if (_caseInsensitiveCompare( |
| 685 tokens[i].codeUnits)) { | 668 "upgrade".codeUnits, tokens[i].codeUnits)) { |
| 686 _connectionUpgrade = true; | 669 _connectionUpgrade = true; |
| 687 } | 670 } |
| 688 _headers._add(headerField, tokens[i]); | 671 _headers._add(headerField, tokens[i]); |
| 689 } | 672 } |
| 690 } else { | 673 } else { |
| 691 _headers._add(headerField, headerValue); | 674 _headers._add(headerField, headerValue); |
| 692 } | 675 } |
| 693 _headerField.clear(); | 676 _headerField.clear(); |
| 694 _headerValue.clear(); | 677 _headerValue.clear(); |
| 695 | 678 |
| 696 if (byte == _CharCode.CR) { | 679 if (byte == _CharCode.CR) { |
| 697 _state = _State.HEADER_ENDING; | 680 _state = _State.HEADER_ENDING; |
| 698 } else if (byte == _CharCode.LF) { | 681 } else if (byte == _CharCode.LF) { |
| 699 _state = _State.HEADER_ENDING; | 682 _state = _State.HEADER_ENDING; |
| 700 _index--; // Make the new state see the LF again. | 683 _index--; // Make the new state see the LF again. |
| 701 } else { | 684 } else { |
| 702 // Start of new header field. | 685 // Start of new header field. |
| 703 _headerField.add(_toLowerCaseByte(byte)); | 686 _headerField.add(_toLowerCaseByte(byte)); |
| 704 _state = _State.HEADER_FIELD; | 687 _state = _State.HEADER_FIELD; |
| 705 } | 688 } |
| 706 } | 689 } |
| 707 break; | 690 break; |
| 708 | 691 |
| 709 case _State.HEADER_ENDING: | 692 case _State.HEADER_ENDING: |
| 710 _expect(byte, _CharCode.LF); | 693 _expect(byte, _CharCode.LF); |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 765 case _State.BODY: | 748 case _State.BODY: |
| 766 // The body is not handled one byte at a time but in blocks. | 749 // The body is not handled one byte at a time but in blocks. |
| 767 _index--; | 750 _index--; |
| 768 int dataAvailable = _buffer.length - _index; | 751 int dataAvailable = _buffer.length - _index; |
| 769 if (_remainingContent >= 0 && dataAvailable > _remainingContent) { | 752 if (_remainingContent >= 0 && dataAvailable > _remainingContent) { |
| 770 dataAvailable = _remainingContent; | 753 dataAvailable = _remainingContent; |
| 771 } | 754 } |
| 772 // Always present the data as a view. This way we can handle all | 755 // Always present the data as a view. This way we can handle all |
| 773 // cases like this, and the user will not experince different data | 756 // cases like this, and the user will not experince different data |
| 774 // typed (which could lead to polymorphic user code). | 757 // typed (which could lead to polymorphic user code). |
| 775 List<int> data = new Uint8List.view(_buffer.buffer, | 758 List<int> data = new Uint8List.view( |
| 776 _buffer.offsetInBytes + _index, | 759 _buffer.buffer, _buffer.offsetInBytes + _index, dataAvailable); |
| 777 dataAvailable); | |
| 778 _bodyController.add(data); | 760 _bodyController.add(data); |
| 779 if (_remainingContent != -1) { | 761 if (_remainingContent != -1) { |
| 780 _remainingContent -= data.length; | 762 _remainingContent -= data.length; |
| 781 } | 763 } |
| 782 _index += data.length; | 764 _index += data.length; |
| 783 if (_remainingContent == 0) { | 765 if (_remainingContent == 0) { |
| 784 if (!_chunked) { | 766 if (!_chunked) { |
| 785 _reset(); | 767 _reset(); |
| 786 _closeIncoming(); | 768 _closeIncoming(); |
| 787 } else { | 769 } else { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 824 void _onDone() { | 806 void _onDone() { |
| 825 // onDone cancles the subscription. | 807 // onDone cancles the subscription. |
| 826 _socketSubscription = null; | 808 _socketSubscription = null; |
| 827 if (_state == _State.CLOSED || _state == _State.FAILURE) return; | 809 if (_state == _State.CLOSED || _state == _State.FAILURE) return; |
| 828 | 810 |
| 829 if (_incoming != null) { | 811 if (_incoming != null) { |
| 830 if (_state != _State.UPGRADED && | 812 if (_state != _State.UPGRADED && |
| 831 !(_state == _State.START && !_requestParser) && | 813 !(_state == _State.START && !_requestParser) && |
| 832 !(_state == _State.BODY && !_chunked && _transferLength == -1)) { | 814 !(_state == _State.BODY && !_chunked && _transferLength == -1)) { |
| 833 _bodyController.addError( | 815 _bodyController.addError( |
| 834 new HttpException("Connection closed while receiving data")); | 816 new HttpException("Connection closed while receiving data")); |
| 835 } | 817 } |
| 836 _closeIncoming(true); | 818 _closeIncoming(true); |
| 837 _controller.close(); | 819 _controller.close(); |
| 838 return; | 820 return; |
| 839 } | 821 } |
| 840 // If the connection is idle the HTTP stream is closed. | 822 // If the connection is idle the HTTP stream is closed. |
| 841 if (_state == _State.START) { | 823 if (_state == _State.START) { |
| 842 if (!_requestParser) { | 824 if (!_requestParser) { |
| 843 _reportError(new HttpException( | 825 _reportError(new HttpException( |
| 844 "Connection closed before full header was received")); | 826 "Connection closed before full header was received")); |
| 845 } | 827 } |
| 846 _controller.close(); | 828 _controller.close(); |
| 847 return; | 829 return; |
| 848 } | 830 } |
| 849 | 831 |
| 850 if (_state == _State.UPGRADED) { | 832 if (_state == _State.UPGRADED) { |
| 851 _controller.close(); | 833 _controller.close(); |
| 852 return; | 834 return; |
| 853 } | 835 } |
| 854 | 836 |
| 855 if (_state < _State.FIRST_BODY_STATE) { | 837 if (_state < _State.FIRST_BODY_STATE) { |
| 856 _state = _State.FAILURE; | 838 _state = _State.FAILURE; |
| 857 // Report the error through the error callback if any. Otherwise | 839 // Report the error through the error callback if any. Otherwise |
| 858 // throw the error. | 840 // throw the error. |
| 859 _reportError(new HttpException( | 841 _reportError(new HttpException( |
| 860 "Connection closed before full header was received")); | 842 "Connection closed before full header was received")); |
| 861 _controller.close(); | 843 _controller.close(); |
| 862 return; | 844 return; |
| 863 } | 845 } |
| 864 | 846 |
| 865 if (!_chunked && _transferLength == -1) { | 847 if (!_chunked && _transferLength == -1) { |
| 866 _state = _State.CLOSED; | 848 _state = _State.CLOSED; |
| 867 } else { | 849 } else { |
| 868 _state = _State.FAILURE; | 850 _state = _State.FAILURE; |
| 869 // Report the error through the error callback if any. Otherwise | 851 // Report the error through the error callback if any. Otherwise |
| 870 // throw the error. | 852 // throw the error. |
| 871 _reportError(new HttpException( | 853 _reportError( |
| 872 "Connection closed before full body was received")); | 854 new HttpException("Connection closed before full body was received")); |
| 873 } | 855 } |
| 874 _controller.close(); | 856 _controller.close(); |
| 875 } | 857 } |
| 876 | 858 |
| 877 String get version { | 859 String get version { |
| 878 switch (_httpVersion) { | 860 switch (_httpVersion) { |
| 879 case _HttpVersion.HTTP10: | 861 case _HttpVersion.HTTP10: |
| 880 return "1.0"; | 862 return "1.0"; |
| 881 case _HttpVersion.HTTP11: | 863 case _HttpVersion.HTTP11: |
| 882 return "1.1"; | 864 return "1.1"; |
| 883 } | 865 } |
| 884 return null; | 866 return null; |
| 885 } | 867 } |
| 886 | 868 |
| 887 int get messageType => _messageType; | 869 int get messageType => _messageType; |
| 888 int get transferLength => _transferLength; | 870 int get transferLength => _transferLength; |
| 889 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED; | 871 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED; |
| 890 bool get persistentConnection => _persistentConnection; | 872 bool get persistentConnection => _persistentConnection; |
| 891 | 873 |
| 892 void set isHead(bool value) { | 874 void set isHead(bool value) { |
| 893 if (value) _noMessageBody = true; | 875 if (value) _noMessageBody = true; |
| 894 } | 876 } |
| 895 | 877 |
| 896 _HttpDetachedIncoming detachIncoming() { | 878 _HttpDetachedIncoming detachIncoming() { |
| 897 // Simulate detached by marking as upgraded. | 879 // Simulate detached by marking as upgraded. |
| 898 _state = _State.UPGRADED; | 880 _state = _State.UPGRADED; |
| 899 return new _HttpDetachedIncoming(_socketSubscription, | 881 return new _HttpDetachedIncoming(_socketSubscription, readUnparsedData()); |
| 900 readUnparsedData()); | |
| 901 } | 882 } |
| 902 | 883 |
| 903 List<int> readUnparsedData() { | 884 List<int> readUnparsedData() { |
| 904 if (_buffer == null) return null; | 885 if (_buffer == null) return null; |
| 905 if (_index == _buffer.length) return null; | 886 if (_index == _buffer.length) return null; |
| 906 var result = _buffer.sublist(_index); | 887 var result = _buffer.sublist(_index); |
| 907 _releaseBuffer(); | 888 _releaseBuffer(); |
| 908 return result; | 889 return result; |
| 909 } | 890 } |
| 910 | 891 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 935 void _releaseBuffer() { | 916 void _releaseBuffer() { |
| 936 _buffer = null; | 917 _buffer = null; |
| 937 _index = null; | 918 _index = null; |
| 938 } | 919 } |
| 939 | 920 |
| 940 static bool _isTokenChar(int byte) { | 921 static bool _isTokenChar(int byte) { |
| 941 return byte > 31 && byte < 128 && !_Const.SEPARATOR_MAP[byte]; | 922 return byte > 31 && byte < 128 && !_Const.SEPARATOR_MAP[byte]; |
| 942 } | 923 } |
| 943 | 924 |
| 944 static bool _isValueChar(int byte) { | 925 static bool _isValueChar(int byte) { |
| 945 return (byte > 31 && byte < 128) || (byte == _CharCode.SP) || | 926 return (byte > 31 && byte < 128) || |
| 927 (byte == _CharCode.SP) || |
| 946 (byte == _CharCode.HT); | 928 (byte == _CharCode.HT); |
| 947 } | 929 } |
| 948 | 930 |
| 949 static List<String> _tokenizeFieldValue(String headerValue) { | 931 static List<String> _tokenizeFieldValue(String headerValue) { |
| 950 List<String> tokens = new List<String>(); | 932 List<String> tokens = new List<String>(); |
| 951 int start = 0; | 933 int start = 0; |
| 952 int index = 0; | 934 int index = 0; |
| 953 while (index < headerValue.length) { | 935 while (index < headerValue.length) { |
| 954 if (headerValue[index] == ",") { | 936 if (headerValue[index] == ",") { |
| 955 tokens.add(headerValue.substring(start, index)); | 937 tokens.add(headerValue.substring(start, index)); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 982 } | 964 } |
| 983 | 965 |
| 984 int _expect(int val1, int val2) { | 966 int _expect(int val1, int val2) { |
| 985 if (val1 != val2) { | 967 if (val1 != val2) { |
| 986 throw new HttpException("Failed to parse HTTP"); | 968 throw new HttpException("Failed to parse HTTP"); |
| 987 } | 969 } |
| 988 } | 970 } |
| 989 | 971 |
| 990 int _expectHexDigit(int byte) { | 972 int _expectHexDigit(int byte) { |
| 991 if (0x30 <= byte && byte <= 0x39) { | 973 if (0x30 <= byte && byte <= 0x39) { |
| 992 return byte - 0x30; // 0 - 9 | 974 return byte - 0x30; // 0 - 9 |
| 993 } else if (0x41 <= byte && byte <= 0x46) { | 975 } else if (0x41 <= byte && byte <= 0x46) { |
| 994 return byte - 0x41 + 10; // A - F | 976 return byte - 0x41 + 10; // A - F |
| 995 } else if (0x61 <= byte && byte <= 0x66) { | 977 } else if (0x61 <= byte && byte <= 0x66) { |
| 996 return byte - 0x61 + 10; // a - f | 978 return byte - 0x61 + 10; // a - f |
| 997 } else { | 979 } else { |
| 998 throw new HttpException("Failed to parse HTTP"); | 980 throw new HttpException("Failed to parse HTTP"); |
| 999 } | 981 } |
| 1000 } | 982 } |
| 1001 | 983 |
| 1002 void _createIncoming(int transferLength) { | 984 void _createIncoming(int transferLength) { |
| 1003 assert(_incoming == null); | 985 assert(_incoming == null); |
| 1004 assert(_bodyController == null); | 986 assert(_bodyController == null); |
| 1005 assert(!_bodyPaused); | 987 assert(!_bodyPaused); |
| 1006 var incoming; | 988 var incoming; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1025 _pauseStateChanged(); | 1007 _pauseStateChanged(); |
| 1026 }, | 1008 }, |
| 1027 onCancel: () { | 1009 onCancel: () { |
| 1028 if (incoming != _incoming) return; | 1010 if (incoming != _incoming) return; |
| 1029 if (_socketSubscription != null) { | 1011 if (_socketSubscription != null) { |
| 1030 _socketSubscription.cancel(); | 1012 _socketSubscription.cancel(); |
| 1031 } | 1013 } |
| 1032 _closeIncoming(true); | 1014 _closeIncoming(true); |
| 1033 _controller.close(); | 1015 _controller.close(); |
| 1034 }); | 1016 }); |
| 1035 incoming = _incoming = new _HttpIncoming( | 1017 incoming = _incoming = |
| 1036 _headers, transferLength, _bodyController.stream); | 1018 new _HttpIncoming(_headers, transferLength, _bodyController.stream); |
| 1037 _bodyPaused = true; | 1019 _bodyPaused = true; |
| 1038 _pauseStateChanged(); | 1020 _pauseStateChanged(); |
| 1039 } | 1021 } |
| 1040 | 1022 |
| 1041 void _closeIncoming([bool closing = false]) { | 1023 void _closeIncoming([bool closing = false]) { |
| 1042 // Ignore multiple close (can happen in re-entrance). | 1024 // Ignore multiple close (can happen in re-entrance). |
| 1043 if (_incoming == null) return; | 1025 if (_incoming == null) return; |
| 1044 var tmp = _incoming; | 1026 var tmp = _incoming; |
| 1045 tmp.close(closing); | 1027 tmp.close(closing); |
| 1046 _incoming = null; | 1028 _incoming = null; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1064 } | 1046 } |
| 1065 } | 1047 } |
| 1066 | 1048 |
| 1067 void _reportError(error, [stackTrace]) { | 1049 void _reportError(error, [stackTrace]) { |
| 1068 if (_socketSubscription != null) _socketSubscription.cancel(); | 1050 if (_socketSubscription != null) _socketSubscription.cancel(); |
| 1069 _state = _State.FAILURE; | 1051 _state = _State.FAILURE; |
| 1070 _controller.addError(error, stackTrace); | 1052 _controller.addError(error, stackTrace); |
| 1071 _controller.close(); | 1053 _controller.close(); |
| 1072 } | 1054 } |
| 1073 } | 1055 } |
| OLD | NEW |