Chromium Code Reviews| Index: runtime/bin/http_parser.dart |
| diff --git a/runtime/bin/http_parser.dart b/runtime/bin/http_parser.dart |
| index 644c0e0cbaa73c9be3c72e25a6b07b3ac0893b62..a349eb5ca7d6b8ca188cebc3cd99be5d72fd395b 100644 |
| --- a/runtime/bin/http_parser.dart |
| +++ b/runtime/bin/http_parser.dart |
| @@ -10,6 +10,15 @@ class _Const { |
| static final HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49]; |
| static final END_CHUNKED = const [0x30, 13, 10, 13, 10]; |
| + |
| + // Bytes for '()<>@,;:\\"/[]?={} \t'. |
| + static final SEPARATORS = const [40, 41, 60, 62, 64, 44, 59, 58, 92, 34, 47, |
| + 91, 93, 63, 61, 123, 125, 32, 9]; |
| + |
| + // Bytes for '()<>@,;:\\"/[]?={} \t\r\n'. |
| + static final SEPARATORS_AND_CR_LF = const [40, 41, 60, 62, 64, 44, 59, 58, 92, |
| + 34, 47, 91, 93, 63, 61, 123, 125, |
| + 32, 9, 13, 10]; |
| } |
| @@ -50,6 +59,7 @@ class _State { |
| static final int CHUNKED_BODY_DONE_CR = 21; |
| static final int CHUNKED_BODY_DONE_LF = 22; |
| static final int BODY = 23; |
| + static final int FAILURE = 24; |
| } |
| @@ -63,16 +73,23 @@ class _MessageType { |
| /** |
| * HTTP parser which parses the HTTP stream as data is supplied |
| - * through the writeList method. As the data is parsed the events |
| - * RequestStart |
| - * ResponseStart |
| - * UriReceived |
| - * HeaderReceived |
| - * HeadersComplete |
| - * DataReceived |
| - * DataEnd |
| - * are generated. |
| - * Currently only HTTP requests with Content-Length header are supported. |
| + * through the [:writeList:] and [:connectionClosed:] methods. As the |
| + * data is parsed the following callbacks are called: |
| + * |
| + * [:requestStart:] |
| + * [:responseStart:] |
| + * [:headerReceived:] |
| + * [:headersComplete:] |
| + * [:dataReceived:] |
| + * [:dataEnd:] |
| + * [:error:] |
| + * |
| + * If an HTTP parser error occours it is possible to get an exception |
| + * thrown from the [:writeList:] and [:connectionClosed:] methods if |
| + * the error callback is not set. |
| + * |
| + * For keep-alive connections where the underlying connection stays |
| + * open the [:connectionClosed:] method would not be called. |
| */ |
| class _HttpParser { |
| _HttpParser() { |
| @@ -91,182 +108,137 @@ class _HttpParser { |
| int writeList(List<int> buffer, int offset, int count) { |
| int index = offset; |
| int lastIndex = offset + count; |
| - while ((index < lastIndex) && !_failure) { |
| - int byte = buffer[index]; |
| - switch (_state) { |
| - case _State.START: |
| - if (byte == _Const.HTTP11[0]) { |
| - // Start parsing HTTP method. |
| - _httpVersionIndex = 1; |
| - _state = _State.METHOD_OR_HTTP_VERSION; |
| - } else { |
| - // Start parsing method. |
| - _method_or_status_code.addCharCode(byte); |
| - _state = _State.REQUEST_LINE_METHOD; |
| - } |
| - break; |
| - |
| - case _State.METHOD_OR_HTTP_VERSION: |
| - if (_httpVersionIndex < _Const.HTTP11.length && |
| - byte == _Const.HTTP11[_httpVersionIndex]) { |
| - // Continue parsing HTTP version. |
| - _httpVersionIndex++; |
| - } else if (_httpVersionIndex == _Const.HTTP11.length && |
| - byte == _CharCode.SP) { |
| - // HTTP version parsed. |
| - _state = _State.RESPONSE_LINE_STATUS_CODE; |
| - } else { |
| - // Did not parse HTTP version. Expect method instead. |
| - for (int i = 0; i < _httpVersionIndex; i++) { |
| - _method_or_status_code.addCharCode(_Const.HTTP11[i]); |
| + try { |
| + while ((index < lastIndex) && _state != _State.FAILURE) { |
| + int byte = buffer[index]; |
| + switch (_state) { |
| + case _State.START: |
| + if (byte == _Const.HTTP11[0]) { |
| + // Start parsing HTTP method. |
| + _httpVersionIndex = 1; |
| + _state = _State.METHOD_OR_HTTP_VERSION; |
| + } else { |
| + // Start parsing method. |
| + if (_Const.SEPARATORS_AND_CR_LF.indexOf(byte) != -1) { |
| + throw new HttpParserException("Invalid request method"); |
| + } |
| + _method_or_status_code.addCharCode(byte); |
| + _state = _State.REQUEST_LINE_METHOD; |
| } |
| - _state = _State.REQUEST_LINE_URI; |
| - } |
| - break; |
| - |
| - case _State.REQUEST_LINE_METHOD: |
| - if (byte == _CharCode.SP) { |
| - _state = _State.REQUEST_LINE_URI; |
| - } else { |
| - _method_or_status_code.addCharCode(byte); |
| - } |
| - break; |
| - |
| - case _State.REQUEST_LINE_URI: |
| - if (byte == _CharCode.SP) { |
| - _state = _State.REQUEST_LINE_HTTP_VERSION; |
| - _httpVersionIndex = 0; |
| - } else { |
| - _uri_or_reason_phrase.addCharCode(byte); |
| - } |
| - break; |
| - |
| - case _State.REQUEST_LINE_HTTP_VERSION: |
| - if (_httpVersionIndex < _Const.HTTP11.length) { |
| - _expect(byte, _Const.HTTP11[_httpVersionIndex]); |
| - _httpVersionIndex++; |
| - } else { |
| - _expect(byte, _CharCode.CR); |
| - _state = _State.REQUEST_LINE_ENDING; |
| - } |
| - break; |
| - |
| - 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()); |
| - } |
| - _method_or_status_code.clear(); |
| - _uri_or_reason_phrase.clear(); |
| - _state = _State.HEADER_START; |
| - break; |
| - |
| - case _State.RESPONSE_LINE_STATUS_CODE: |
| - if (byte == _CharCode.SP) { |
| - _state = _State.RESPONSE_LINE_REASON_PHRASE; |
| - } else { |
| - if (byte < 0x30 && 0x39 < byte) { |
| - if (error != null) error("Failed to parse HTTP"); |
| - _failure = true; |
| + break; |
| + |
| + case _State.METHOD_OR_HTTP_VERSION: |
| + if (_httpVersionIndex < _Const.HTTP11.length && |
| + byte == _Const.HTTP11[_httpVersionIndex]) { |
| + // Continue parsing HTTP version. |
| + _httpVersionIndex++; |
| + } else if (_httpVersionIndex == _Const.HTTP11.length && |
| + byte == _CharCode.SP) { |
| + // HTTP version parsed. |
| + _state = _State.RESPONSE_LINE_STATUS_CODE; |
| } else { |
| + // Did not parse HTTP version. Expect method instead. |
| + for (int i = 0; i < _httpVersionIndex; i++) { |
| + _method_or_status_code.addCharCode(_Const.HTTP11[i]); |
| + } |
| + _state = _State.REQUEST_LINE_URI; |
| + } |
| + break; |
| + |
| + case _State.REQUEST_LINE_METHOD: |
| + if (byte == _CharCode.SP) { |
| + _state = _State.REQUEST_LINE_URI; |
| + } else { |
| + if (_Const.SEPARATORS_AND_CR_LF.indexOf(byte) != -1) { |
| + throw new HttpParserException("Invalid request method"); |
| + } |
| _method_or_status_code.addCharCode(byte); |
| } |
| - } |
| - break; |
| - |
| - case _State.RESPONSE_LINE_REASON_PHRASE: |
| - if (byte == _CharCode.CR) { |
| - _state = _State.RESPONSE_LINE_ENDING; |
| - } else { |
| - _uri_or_reason_phrase.addCharCode(byte); |
| - } |
| - break; |
| - |
| - case _State.RESPONSE_LINE_ENDING: |
| - _expect(byte, _CharCode.LF); |
| - _messageType == _MessageType.RESPONSE; |
| - if (responseStart != null) { |
| + break; |
| + |
| + case _State.REQUEST_LINE_URI: |
| + if (byte == _CharCode.SP) { |
| + _state = _State.REQUEST_LINE_HTTP_VERSION; |
| + _httpVersionIndex = 0; |
| + } else { |
| + if (byte == _CharCode.CR || byte == _CharCode.LF) { |
| + throw new HttpParserException("Invalid request URI"); |
| + } |
| + _uri_or_reason_phrase.addCharCode(byte); |
| + } |
| + break; |
| + |
| + case _State.REQUEST_LINE_HTTP_VERSION: |
| + if (_httpVersionIndex < _Const.HTTP11.length) { |
| + _expect(byte, _Const.HTTP11[_httpVersionIndex]); |
| + _httpVersionIndex++; |
| + } else { |
| + _expect(byte, _CharCode.CR); |
| + _state = _State.REQUEST_LINE_ENDING; |
| + } |
| + break; |
| + |
| + 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()); |
| + } |
| + _method_or_status_code.clear(); |
| + _uri_or_reason_phrase.clear(); |
| + _state = _State.HEADER_START; |
| + break; |
| + |
| + case _State.RESPONSE_LINE_STATUS_CODE: |
| + if (byte == _CharCode.SP) { |
| + if (_method_or_status_code.length != 3) { |
| + throw new HttpParserException("Invalid response status code"); |
| + } |
| + _state = _State.RESPONSE_LINE_REASON_PHRASE; |
| + } else { |
| + if (byte < 0x30 && 0x39 < byte) { |
| + throw new HttpParserException("Invalid response status code"); |
| + } else { |
| + _method_or_status_code.addCharCode(byte); |
| + } |
| + } |
| + break; |
| + |
| + case _State.RESPONSE_LINE_REASON_PHRASE: |
| + if (byte == _CharCode.CR) { |
| + if (_uri_or_reason_phrase.length == 0) { |
| + throw new HttpParserException("Invalid response reason phrase"); |
| + } |
| + _state = _State.RESPONSE_LINE_ENDING; |
| + } else { |
| + if (byte == _CharCode.CR || byte == _CharCode.LF) { |
| + throw new HttpParserException("Invalid response reason phrase"); |
| + } |
| + _uri_or_reason_phrase.addCharCode(byte); |
| + } |
| + break; |
| + |
| + case _State.RESPONSE_LINE_ENDING: |
| + _expect(byte, _CharCode.LF); |
| + _messageType == _MessageType.RESPONSE; |
| int statusCode = Math.parseInt(_method_or_status_code.toString()); |
| if (statusCode < 100 || statusCode > 599) { |
| - if (error != null) error("Invalid response status code"); |
| - _failure = true; |
| + throw new HttpParserException("Invalid response status code"); |
| } else { |
| // Check whether this response will never have a body. |
| _noMessageBody = |
| statusCode <= 199 || statusCode == 204 || statusCode == 304; |
| - responseStart(statusCode, _uri_or_reason_phrase.toString()); |
| - } |
| - } |
| - _method_or_status_code.clear(); |
| - _uri_or_reason_phrase.clear(); |
| - _state = _State.HEADER_START; |
| - break; |
| - |
| - case _State.HEADER_START: |
| - if (byte == _CharCode.CR) { |
| - _state = _State.HEADER_ENDING; |
| - } else { |
| - // Start of new header field. |
| - _headerField.addCharCode(_toLowerCase(byte)); |
| - _state = _State.HEADER_FIELD; |
| - } |
| - break; |
| - |
| - case _State.HEADER_FIELD: |
| - if (byte == _CharCode.COLON) { |
| - _state = _State.HEADER_VALUE_START; |
| - } else { |
| - _headerField.addCharCode(_toLowerCase(byte)); |
| - } |
| - break; |
| - |
| - case _State.HEADER_VALUE_START: |
| - if (byte != _CharCode.SP && byte != _CharCode.HT) { |
| - // Start of new header value. |
| - _headerValue.addCharCode(byte); |
| - _state = _State.HEADER_VALUE; |
| - } |
| - break; |
| - |
| - case _State.HEADER_VALUE: |
| - if (byte == _CharCode.CR) { |
| - _state = _State.HEADER_VALUE_FOLDING_OR_ENDING; |
| - } else { |
| - _headerValue.addCharCode(byte); |
| - } |
| - break; |
| - |
| - case _State.HEADER_VALUE_FOLDING_OR_ENDING: |
| - _expect(byte, _CharCode.LF); |
| - _state = _State.HEADER_VALUE_FOLD_OR_END; |
| - break; |
| - |
| - case _State.HEADER_VALUE_FOLD_OR_END: |
| - if (byte == _CharCode.SP || byte == _CharCode.HT) { |
| - _state = _State.HEADER_VALUE_START; |
| - } else { |
| - String headerField = _headerField.toString(); |
| - String headerValue =_headerValue.toString(); |
| - // Ignore the Content-Length header if Transfer-Encoding |
| - // is chunked (RFC 2616 section 4.4) |
| - if (headerField == "content-length" && !_chunked) { |
| - _contentLength = Math.parseInt(headerValue); |
| - } else if (headerField == "connection" && |
| - headerValue == "keep-alive") { |
| - _keepAlive = true; |
| - } else if (headerField == "transfer-encoding" && |
| - headerValue == "chunked") { |
| - _chunked = true; |
| - _contentLength = -1; |
| } |
| - if (headerReceived != null) { |
| - headerReceived(headerField, headerValue); |
| + if (responseStart != null) { |
| + responseStart(statusCode, _uri_or_reason_phrase.toString()); |
| } |
| - _headerField.clear(); |
| - _headerValue.clear(); |
| + _method_or_status_code.clear(); |
| + _uri_or_reason_phrase.clear(); |
| + _state = _State.HEADER_START; |
| + break; |
| + case _State.HEADER_START: |
| if (byte == _CharCode.CR) { |
| _state = _State.HEADER_ENDING; |
| } else { |
| @@ -274,119 +246,197 @@ class _HttpParser { |
| _headerField.addCharCode(_toLowerCase(byte)); |
| _state = _State.HEADER_FIELD; |
| } |
| - } |
| - break; |
| + break; |
| + |
| + case _State.HEADER_FIELD: |
| + if (byte == _CharCode.COLON) { |
| + _state = _State.HEADER_VALUE_START; |
| + } else { |
| + _headerField.addCharCode(_toLowerCase(byte)); |
| + } |
| + break; |
| + |
| + case _State.HEADER_VALUE_START: |
| + if (byte != _CharCode.SP && byte != _CharCode.HT) { |
| + // Start of new header value. |
| + _headerValue.addCharCode(byte); |
| + _state = _State.HEADER_VALUE; |
| + } |
| + break; |
| + |
| + case _State.HEADER_VALUE: |
| + if (byte == _CharCode.CR) { |
| + _state = _State.HEADER_VALUE_FOLDING_OR_ENDING; |
| + } else { |
| + _headerValue.addCharCode(byte); |
| + } |
| + break; |
| + |
| + case _State.HEADER_VALUE_FOLDING_OR_ENDING: |
| + _expect(byte, _CharCode.LF); |
| + _state = _State.HEADER_VALUE_FOLD_OR_END; |
| + break; |
| - case _State.HEADER_ENDING: |
| - _expect(byte, _CharCode.LF); |
| - if (headersComplete != null) headersComplete(); |
| + case _State.HEADER_VALUE_FOLD_OR_END: |
| + if (byte == _CharCode.SP || byte == _CharCode.HT) { |
| + _state = _State.HEADER_VALUE_START; |
| + } else { |
| + String headerField = _headerField.toString(); |
| + String headerValue =_headerValue.toString(); |
| + // Ignore the Content-Length header if Transfer-Encoding |
| + // is chunked (RFC 2616 section 4.4) |
| + if (headerField == "content-length" && !_chunked) { |
| + _contentLength = Math.parseInt(headerValue); |
| + } else if (headerField == "connection" && |
| + headerValue == "keep-alive") { |
| + _keepAlive = true; |
| + } else if (headerField == "transfer-encoding" && |
| + headerValue == "chunked") { |
| + _chunked = true; |
| + _contentLength = -1; |
| + } |
| + if (headerReceived != null) { |
| + headerReceived(headerField, headerValue); |
| + } |
| + _headerField.clear(); |
| + _headerValue.clear(); |
| + |
| + if (byte == _CharCode.CR) { |
| + _state = _State.HEADER_ENDING; |
| + } else { |
| + // Start of new header field. |
| + _headerField.addCharCode(_toLowerCase(byte)); |
| + _state = _State.HEADER_FIELD; |
| + } |
| + } |
| + break; |
| + |
| + case _State.HEADER_ENDING: |
| + _expect(byte, _CharCode.LF); |
| + if (headersComplete != null) headersComplete(); |
| + |
| + if (_chunked) { |
| + _state = _State.CHUNK_SIZE; |
| + _remainingContent = 0; |
| + } else if (_contentLength == 0 || |
| + (_messageType == _MessageType.REQUEST && |
| + _contentLength == -1) || |
| + (_messageType == _MessageType.RESPONSE && |
| + (_noMessageBody || _responseToMethod == "HEAD"))) { |
| + // If there is no message body get ready to process the |
| + // next request. |
| + if (dataEnd != null) dataEnd(); |
| + _state = _State.START; |
| + } else if (_contentLength > 0) { |
| + _remainingContent = _contentLength; |
| + _state = _State.BODY; |
| + } else { |
| + // Neither chunked nor content length. End of body |
| + // indicated by close. |
| + _state = _State.BODY; |
| + } |
| + break; |
| + |
| + case _State.CHUNK_SIZE_STARTING_CR: |
| + _expect(byte, _CharCode.CR); |
| + _state = _State.CHUNK_SIZE_STARTING_LF; |
| + break; |
| - if (_chunked) { |
| + case _State.CHUNK_SIZE_STARTING_LF: |
| + _expect(byte, _CharCode.LF); |
| _state = _State.CHUNK_SIZE; |
| - _remainingContent = 0; |
| - } else if (_contentLength == 0 || |
| - (_messageType == _MessageType.REQUEST && |
| - _contentLength == -1) || |
| - (_messageType == _MessageType.RESPONSE && |
| - (_noMessageBody || _responseToMethod == "HEAD"))) { |
| - // If there is no message body get ready to process the |
| - // next request. |
| + break; |
| + |
| + case _State.CHUNK_SIZE: |
| + if (byte == _CharCode.CR) { |
| + _state = _State.CHUNK_SIZE_ENDING; |
| + } else if (byte == _CharCode.SEMI_COLON) { |
| + _state = _State.CHUNK_SIZE_EXTENSION; |
| + } else { |
| + int value = _expectHexDigit(byte); |
| + _remainingContent = _remainingContent * 16 + value; |
| + } |
| + break; |
| + |
| + case _State.CHUNK_SIZE_EXTENSION: |
| + if (byte == _CharCode.CR) { |
| + _state = _State.CHUNK_SIZE_ENDING; |
| + } |
| + break; |
| + |
| + case _State.CHUNK_SIZE_ENDING: |
| + _expect(byte, _CharCode.LF); |
| + if (_remainingContent > 0) { |
| + _state = _State.BODY; |
| + } else { |
| + _state = _State.CHUNKED_BODY_DONE_CR; |
| + } |
| + break; |
| + |
| + case _State.CHUNKED_BODY_DONE_CR: |
| + _expect(byte, _CharCode.CR); |
| + _state = _State.CHUNKED_BODY_DONE_LF; |
| + break; |
| + |
| + case _State.CHUNKED_BODY_DONE_LF: |
| + _expect(byte, _CharCode.LF); |
| if (dataEnd != null) dataEnd(); |
| - _state = _State.START; |
| - } else if (_contentLength > 0) { |
| - _remainingContent = _contentLength; |
| - _state = _State.BODY; |
| - } else { |
| - // Neither chunked nor content length. End of body |
| - // indicated by close. |
| - _state = _State.BODY; |
| - } |
| - break; |
| - |
| - case _State.CHUNK_SIZE_STARTING_CR: |
| - _expect(byte, _CharCode.CR); |
| - _state = _State.CHUNK_SIZE_STARTING_LF; |
| - break; |
| - |
| - case _State.CHUNK_SIZE_STARTING_LF: |
| - _expect(byte, _CharCode.LF); |
| - _state = _State.CHUNK_SIZE; |
| - break; |
| - |
| - case _State.CHUNK_SIZE: |
| - if (byte == _CharCode.CR) { |
| - _state = _State.CHUNK_SIZE_ENDING; |
| - } else if (byte == _CharCode.SEMI_COLON) { |
| - _state = _State.CHUNK_SIZE_EXTENSION; |
| - } else { |
| - int value = _expectHexDigit(byte); |
| - _remainingContent = _remainingContent * 16 + value; |
| - } |
| - break; |
| - |
| - case _State.CHUNK_SIZE_EXTENSION: |
| - if (byte == _CharCode.CR) { |
| - _state = _State.CHUNK_SIZE_ENDING; |
| - } |
| - break; |
| - |
| - case _State.CHUNK_SIZE_ENDING: |
| - _expect(byte, _CharCode.LF); |
| - if (_remainingContent > 0) { |
| - _state = _State.BODY; |
| - } else { |
| - _state = _State.CHUNKED_BODY_DONE_CR; |
| - } |
| - break; |
| - |
| - case _State.CHUNKED_BODY_DONE_CR: |
| - _expect(byte, _CharCode.CR); |
| - _state = _State.CHUNKED_BODY_DONE_LF; |
| - break; |
| - |
| - case _State.CHUNKED_BODY_DONE_LF: |
| - _expect(byte, _CharCode.LF); |
| - if (dataEnd != null) dataEnd(); |
| - _reset(); |
| - break; |
| - |
| - case _State.BODY: |
| - // The body is not handled one byte at the time but in blocks. |
| - int dataAvailable = lastIndex - index; |
| - ByteArray data; |
| - if (_remainingContent == null || dataAvailable <= _remainingContent) { |
| - data = new ByteArray(dataAvailable); |
| - data.setRange(0, dataAvailable, buffer, index); |
| - } else { |
| - data = new ByteArray(_remainingContent); |
| - data.setRange(0, _remainingContent, buffer, index); |
| - } |
| - |
| - if (dataReceived != null) dataReceived(data); |
| - if (_remainingContent != null) { |
| - _remainingContent -= data.length; |
| - } |
| - index += data.length; |
| - if (_remainingContent == 0) { |
| - if (!_chunked) { |
| - if (dataEnd != null) dataEnd(); |
| - _reset(); |
| + _reset(); |
| + break; |
| + |
| + case _State.BODY: |
| + // The body is not handled one byte at the time but in blocks. |
| + int dataAvailable = lastIndex - index; |
| + ByteArray data; |
| + if (_remainingContent == null || dataAvailable <= _remainingContent) { |
|
Mads Ager (google)
2012/03/22 14:30:08
Long line?
Søren Gjesse
2012/03/23 07:34:20
Done.
|
| + data = new ByteArray(dataAvailable); |
| + data.setRange(0, dataAvailable, buffer, index); |
| } else { |
| - _state = _State.CHUNK_SIZE_STARTING_CR; |
| + data = new ByteArray(_remainingContent); |
| + data.setRange(0, _remainingContent, buffer, index); |
| } |
| - } |
| - // Hack - as we always do index++ below. |
| - index--; |
| - break; |
| + if (dataReceived != null) dataReceived(data); |
| + if (_remainingContent != null) { |
| + _remainingContent -= data.length; |
| + } |
| + index += data.length; |
| + if (_remainingContent == 0) { |
| + if (!_chunked) { |
| + if (dataEnd != null) dataEnd(); |
| + _reset(); |
| + } else { |
| + _state = _State.CHUNK_SIZE_STARTING_CR; |
| + } |
| + } |
| - default: |
| - // Should be unreachable. |
| - assert(false); |
| - } |
| + // Hack - as we always do index++ below. |
| + index--; |
| + break; |
| - // Move to the next byte. |
| - index++; |
| + case _state = _State.FAILURE: |
| + // Should be unreachable. |
| + assert(false); |
| + break; |
| + |
| + default: |
| + // Should be unreachable. |
| + assert(false); |
| + break; |
| + } |
| + |
| + // Move to the next byte. |
| + index++; |
| + } |
| + } catch (var 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; |
| + } |
| } |
| // Return the number of bytes parsed. |
| @@ -397,8 +447,14 @@ class _HttpParser { |
| if (!_chunked && _contentLength == -1) { |
| if (dataEnd != null) dataEnd(); |
| } else { |
| + // Report the error through the error callback if any. Otherwise |
|
Mads Ager (google)
2012/03/22 14:30:08
Maybe this should really be our behavior for all o
Søren Gjesse
2012/03/23 07:34:20
That might be a good idea. This can lead to VM ter
|
| + // throw the error. |
| + var e = new HttpParserException( |
| + "Connection closed before full body was received"); |
| if (error != null) { |
| - error("Connection closed before full body was received"); |
| + error(e); |
| + } else { |
| + throw e; |
| } |
| } |
| } |
| @@ -411,7 +467,6 @@ class _HttpParser { |
| _reset() { |
| _state = _State.START; |
| - _failure = false; |
| _messageType = _MessageType.UNDETERMINED; |
| _headerField = new StringBuffer(); |
| _headerValue = new StringBuffer(); |
| @@ -436,8 +491,7 @@ class _HttpParser { |
| int _expect(int val1, int val2) { |
| if (val1 != val2) { |
| - if (error != null) error("Failed to parse HTTP"); |
| - _failure = true; |
| + throw new HttpParserException("Failed to parse HTTP"); |
| } |
| } |
| @@ -449,13 +503,12 @@ class _HttpParser { |
| } else if (0x61 <= byte && byte <= 0x66) { |
| return byte - 0x61 + 10; // a - f |
| } else { |
| - _failure = true; |
| + throw new HttpParserException("Failed to parse HTTP"); |
| return 0; |
| } |
| } |
| int _state; |
| - bool _failure; |
| int _httpVersionIndex; |
| int _messageType; |
| StringBuffer _method_or_status_code; |
| @@ -480,3 +533,10 @@ class _HttpParser { |
| Function dataEnd; |
| Function error; |
| } |
| + |
| + |
| +class HttpParserException implements Exception { |
| + const HttpParserException([String this.message = ""]); |
| + String toString() => "HttpParserException: $message"; |
| + final String message; |
| +} |