| Index: sdk/lib/io/http_parser.dart
|
| diff --git a/sdk/lib/io/http_parser.dart b/sdk/lib/io/http_parser.dart
|
| index 61e0a64a588e34fc9111455ccda531969c1bc8bb..45a2d4f0a43296c949e0922c9af2775b1f9a33b3 100644
|
| --- a/sdk/lib/io/http_parser.dart
|
| +++ b/sdk/lib/io/http_parser.dart
|
| @@ -103,6 +103,7 @@ class _MessageType {
|
| * [:headersComplete:]
|
| * [:dataReceived:]
|
| * [:dataEnd:]
|
| + * [:closed:]
|
| * [:error:]
|
| *
|
| * If an HTTP parser error occours it is possible to get an exception
|
| @@ -124,7 +125,12 @@ class _MessageType {
|
| * upgraded to.
|
| */
|
| class _HttpParser {
|
| - _HttpParser() {
|
| + _HttpParser.requestParser() {
|
| + _requestParser = true;
|
| + _reset();
|
| + }
|
| + _HttpParser.responseParser() {
|
| + _requestParser = false;
|
| _reset();
|
| }
|
|
|
| @@ -165,6 +171,9 @@ class _HttpParser {
|
| throw new HttpParserException("Invalid request method");
|
| }
|
| _method_or_status_code.addCharCode(byte);
|
| + if (!_requestParser) {
|
| + throw new HttpParserException("Invalid response line");
|
| + }
|
| _state = _State.REQUEST_LINE_METHOD;
|
| }
|
| break;
|
| @@ -179,6 +188,9 @@ class _HttpParser {
|
| // HTTP/ parsed. As method is a token this cannot be a
|
| // method anymore.
|
| _httpVersionIndex++;
|
| + if (_requestParser) {
|
| + throw new HttpParserException("Invalid request line");
|
| + }
|
| _state = _State.RESPONSE_HTTP_VERSION;
|
| } else {
|
| // Did not parse HTTP version. Expect method instead.
|
| @@ -190,6 +202,9 @@ class _HttpParser {
|
| } else {
|
| _method_or_status_code.addCharCode(byte);
|
| _httpVersion = _HttpVersion.UNDETERMINED;
|
| + if (!_requestParser) {
|
| + throw new HttpParserException("Invalid response line");
|
| + }
|
| _state = _State.REQUEST_LINE_METHOD;
|
| }
|
| }
|
| @@ -274,11 +289,9 @@ class _HttpParser {
|
| case _State.REQUEST_LINE_ENDING:
|
| _expect(byte, _CharCode.LF);
|
| _messageType = _MessageType.REQUEST;
|
| - if (requestStart != null) {
|
| - requestStart(_method_or_status_code.toString(),
|
| - _uri_or_reason_phrase.toString(),
|
| - version);
|
| - }
|
| + requestStart(_method_or_status_code.toString(),
|
| + _uri_or_reason_phrase.toString(),
|
| + version);
|
| _method_or_status_code.clear();
|
| _uri_or_reason_phrase.clear();
|
| _state = _State.HEADER_START;
|
| @@ -324,11 +337,9 @@ class _HttpParser {
|
| _noMessageBody =
|
| statusCode <= 199 || statusCode == 204 || statusCode == 304;
|
| }
|
| - if (responseStart != null) {
|
| - responseStart(statusCode,
|
| - _uri_or_reason_phrase.toString(),
|
| - version);
|
| - }
|
| + responseStart(statusCode,
|
| + _uri_or_reason_phrase.toString(),
|
| + version);
|
| _method_or_status_code.clear();
|
| _uri_or_reason_phrase.clear();
|
| _state = _State.HEADER_START;
|
| @@ -400,9 +411,7 @@ class _HttpParser {
|
| } else if (token == "upgrade") {
|
| _connectionUpgrade = true;
|
| }
|
| - if (headerReceived != null) {
|
| - headerReceived(headerField, token);
|
| - }
|
| + headerReceived(headerField, token);
|
| }
|
| reportHeader = false;
|
| } else if (headerField == "transfer-encoding" &&
|
| @@ -412,7 +421,7 @@ class _HttpParser {
|
| _chunked = true;
|
| _contentLength = -1;
|
| }
|
| - if (reportHeader && headerReceived != null) {
|
| + if (reportHeader) {
|
| headerReceived(headerField, headerValue);
|
| }
|
| _headerField.clear();
|
| @@ -440,9 +449,9 @@ class _HttpParser {
|
| }
|
| if (_connectionUpgrade) {
|
| _state = _State.UPGRADED;
|
| - if (headersComplete != null) headersComplete();
|
| + headersComplete();
|
| } else {
|
| - if (headersComplete != null) headersComplete();
|
| + headersComplete();
|
| if (_chunked) {
|
| _state = _State.CHUNK_SIZE;
|
| _remainingContent = 0;
|
| @@ -525,7 +534,7 @@ class _HttpParser {
|
| data.setRange(0, _remainingContent, _buffer, _index);
|
| }
|
|
|
| - if (dataReceived != null) dataReceived(data);
|
| + dataReceived(data);
|
| if (_remainingContent != null) {
|
| _remainingContent -= data.length;
|
| }
|
| @@ -552,14 +561,8 @@ class _HttpParser {
|
| }
|
| }
|
| } catch (e) {
|
| - // Report the error through the error callback if any. Otherwise
|
| - // throw the error.
|
| - if (error != null) {
|
| - error(e);
|
| - _state = _State.FAILURE;
|
| - } else {
|
| - throw e;
|
| - }
|
| + _state = _State.FAILURE;
|
| + error(e);
|
| }
|
|
|
| // If all data is parsed or not needed due to failure there is no
|
| @@ -567,45 +570,60 @@ class _HttpParser {
|
| if (_state != _State.UPGRADED) _releaseBuffer();
|
| }
|
|
|
| - void writeList(List<int> buffer, int offset, int count) {
|
| + void streamData(List<int> buffer) {
|
| assert(_buffer == null);
|
| _buffer = buffer;
|
| - _index = offset;
|
| - _lastIndex = offset + count;
|
| + _index = 0;
|
| + _lastIndex = buffer.length;
|
| _parse();
|
| }
|
|
|
| - void connectionClosed() {
|
| + void streamDone() {
|
| + // If the connection is idle the HTTP stream is closed.
|
| + if (_state == _State.START) {
|
| + if (_requestParser) {
|
| + closed();
|
| + } else {
|
| + error(
|
| + new HttpParserException(
|
| + "Connection closed before full header was received"));
|
| + }
|
| + return;
|
| + }
|
| +
|
| if (_state < _State.FIRST_BODY_STATE) {
|
| _state = _State.FAILURE;
|
| // Report the error through the error callback if any. Otherwise
|
| // throw the error.
|
| - var e = new HttpParserException(
|
| - "Connection closed before full header was received");
|
| - if (error != null) {
|
| - error(e);
|
| - return;
|
| - }
|
| - throw e;
|
| + error(
|
| + new HttpParserException(
|
| + "Connection closed before full header was received"));
|
| + return;
|
| }
|
|
|
| if (!_chunked && _contentLength == -1) {
|
| - if (_state != _State.START) {
|
| - if (dataEnd != null) dataEnd(true);
|
| - }
|
| + dataEnd(true);
|
| _state = _State.CLOSED;
|
| + closed();
|
| } else {
|
| _state = _State.FAILURE;
|
| // Report the error through the error callback if any. Otherwise
|
| // throw the error.
|
| - var e = new HttpParserException(
|
| - "Connection closed before full body was received");
|
| - if (error != null) {
|
| - error(e);
|
| - return;
|
| - }
|
| - throw e;
|
| + error(
|
| + new HttpParserException(
|
| + "Connection closed before full body was received"));
|
| + }
|
| + }
|
| +
|
| + void streamError(e) {
|
| + // Don't report errors when HTTP parser is in idle state. Clients
|
| + // can close the connection and cause a connection reset by peer
|
| + // error which is OK.
|
| + if (_state == _State.START) {
|
| + closed();
|
| + return;
|
| }
|
| + error(e);
|
| }
|
|
|
| String get version {
|
| @@ -625,8 +643,6 @@ class _HttpParser {
|
|
|
| void set responseToMethod(String method) { _responseToMethod = method; }
|
|
|
| - bool get isIdle => _state == _State.START;
|
| -
|
| List<int> readUnparsedData() {
|
| if (_buffer == null) return [];
|
| if (_index == _lastIndex) return [];
|
| @@ -636,9 +652,7 @@ class _HttpParser {
|
| }
|
|
|
| void _bodyEnd() {
|
| - if (dataEnd != null) {
|
| - dataEnd(_messageType == _MessageType.RESPONSE && !_persistentConnection);
|
| - }
|
| + dataEnd(_messageType == _MessageType.RESPONSE && !_persistentConnection);
|
| }
|
|
|
| _reset() {
|
| @@ -717,6 +731,7 @@ class _HttpParser {
|
| int _index;
|
| int _lastIndex;
|
|
|
| + bool _requestParser;
|
| int _state;
|
| int _httpVersionIndex;
|
| int _messageType;
|
| @@ -743,6 +758,7 @@ class _HttpParser {
|
| Function dataReceived;
|
| Function dataEnd;
|
| Function error;
|
| + Function closed;
|
| }
|
|
|
|
|
|
|