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; |
} |