Chromium Code Reviews| Index: sdk/lib/io/websocket_impl.dart |
| diff --git a/sdk/lib/io/websocket_impl.dart b/sdk/lib/io/websocket_impl.dart |
| index 48eeca131d3f6ce888b612521836f4200e59a72b..f499055ac10a472d5358c4ac0b017e29a22cad3c 100644 |
| --- a/sdk/lib/io/websocket_impl.dart |
| +++ b/sdk/lib/io/websocket_impl.dart |
| @@ -51,31 +51,26 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
| static const int CLOSED = 5; |
| static const int FAILURE = 6; |
| - int _state; |
| - bool _fin; |
| - int _opcode; |
| - int _len; |
| - bool _masked; |
| - int _maskingKey; |
| - int _remainingLenBytes; |
| - int _remainingMaskingKeyBytes; |
| - int _remainingPayloadBytes; |
| - int _unmaskingIndex; |
| - |
| - int _currentMessageType; |
| - List<int> _controlPayload; |
| - StreamController _controller; |
| - |
| + int _state = START; |
| + bool _fin = false; |
| + int _opcode = -1; |
| + int _len = -1; |
| + bool _masked = false; |
| + int _remainingLenBytes = -1; |
| + int _remainingMaskingKeyBytes = 4; |
| + int _remainingPayloadBytes = -1; |
| + int _unmaskingIndex = 0; |
| + int _currentMessageType = _WebSocketMessageType.NONE; |
| int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
| String closeReason = ""; |
| - bool _serverSide; |
| EventSink _eventSink; |
| - _WebSocketProtocolTransformer([this._serverSide = false]) { |
| - _prepareForNextFrame(); |
| - _currentMessageType = _WebSocketMessageType.NONE; |
| - } |
| + final bool _serverSide; |
| + final List _maskingBytes = new List(4); |
| + final BytesBuilder _payload = new BytesBuilder(); |
| + |
| + _WebSocketProtocolTransformer([this._serverSide = false]); |
| Stream bind(Stream stream) { |
| return new Stream.eventTransformed( |
| @@ -101,163 +96,125 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
| int count = buffer.length; |
| int index = 0; |
| int lastIndex = count; |
| - try { |
| - if (_state == CLOSED) { |
| - throw new WebSocketException("Data on closed connection"); |
| - } |
| - if (_state == FAILURE) { |
| - throw new WebSocketException("Data on failed connection"); |
| - } |
| - while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { |
| - int byte = buffer[index]; |
| - switch (_state) { |
| - case START: |
| - _fin = (byte & 0x80) != 0; |
| - if ((byte & 0x70) != 0) { |
| - // The RSV1, RSV2 bits RSV3 most be all zero. |
| + if (_state == CLOSED) { |
| + throw new WebSocketException("Data on closed connection"); |
| + } |
| + if (_state == FAILURE) { |
| + throw new WebSocketException("Data on failed connection"); |
| + } |
| + while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { |
| + int byte = buffer[index]; |
| + switch (_state) { |
|
kasperl
2014/01/27 14:40:07
Is there one of the cases that's way more likely t
Anders Johnsen
2014/01/28 08:56:10
Moved the switch in to a binary-like if-hierarchy.
|
| + case START: |
| + _fin = (byte & 0x80) != 0; |
| + if ((byte & 0x70) != 0) { |
| + // The RSV1, RSV2 bits RSV3 most be all zero. |
|
kasperl
2014/01/27 14:40:07
most -> must
Anders Johnsen
2014/01/28 08:56:10
Done.
|
| + throw new WebSocketException("Protocol error"); |
| + } |
| + _opcode = (byte & 0xF); |
| + switch (_opcode) { |
| + case _WebSocketOpcode.CONTINUATION: |
|
kasperl
2014/01/27 14:40:07
Indent the cases.
Anders Johnsen
2014/01/28 08:56:10
Done.
|
| + if (_currentMessageType == _WebSocketMessageType.NONE) { |
| throw new WebSocketException("Protocol error"); |
| } |
| - _opcode = (byte & 0xF); |
| - switch (_opcode) { |
| - case _WebSocketOpcode.CONTINUATION: |
| - if (_currentMessageType == _WebSocketMessageType.NONE) { |
| - throw new WebSocketException("Protocol error"); |
| - } |
| - break; |
| + break; |
| - case _WebSocketOpcode.TEXT: |
| - if (_currentMessageType != _WebSocketMessageType.NONE) { |
| - throw new WebSocketException("Protocol error"); |
| - } |
| - _currentMessageType = _WebSocketMessageType.TEXT; |
| - _controller = new StreamController(sync: true); |
| - _controller.stream |
| - .transform(UTF8.decoder) |
| - .fold(new StringBuffer(), (buffer, str) => buffer..write(str)) |
| - .then((buffer) { |
| - _eventSink.add(buffer.toString()); |
| - }, onError: _eventSink.addError); |
| - break; |
| - |
| - case _WebSocketOpcode.BINARY: |
| - if (_currentMessageType != _WebSocketMessageType.NONE) { |
| - throw new WebSocketException("Protocol error"); |
| - } |
| - _currentMessageType = _WebSocketMessageType.BINARY; |
| - _controller = new StreamController(sync: true); |
| - _controller.stream |
| - .fold(new BytesBuilder(), (buffer, data) => buffer..add(data)) |
| - .then((buffer) { |
| - _eventSink.add(buffer.takeBytes()); |
| - }, onError: _eventSink.addError); |
| - break; |
| - |
| - case _WebSocketOpcode.CLOSE: |
| - case _WebSocketOpcode.PING: |
| - case _WebSocketOpcode.PONG: |
| - // Control frames cannot be fragmented. |
| - if (!_fin) throw new WebSocketException("Protocol error"); |
| - break; |
| - |
| - default: |
| + case _WebSocketOpcode.TEXT: |
| + if (_currentMessageType != _WebSocketMessageType.NONE) { |
| throw new WebSocketException("Protocol error"); |
| } |
| - _state = LEN_FIRST; |
| + _currentMessageType = _WebSocketMessageType.TEXT; |
| break; |
| - case LEN_FIRST: |
| - _masked = (byte & 0x80) != 0; |
| - _len = byte & 0x7F; |
| - if (_isControlFrame() && _len > 125) { |
| + case _WebSocketOpcode.BINARY: |
| + if (_currentMessageType != _WebSocketMessageType.NONE) { |
| throw new WebSocketException("Protocol error"); |
| } |
| - if (_len < 126) { |
| - _lengthDone(); |
| - } else if (_len == 126) { |
| - _len = 0; |
| - _remainingLenBytes = 2; |
| - _state = LEN_REST; |
| - } else if (_len == 127) { |
| - _len = 0; |
| - _remainingLenBytes = 8; |
| - _state = LEN_REST; |
| - } |
| + _currentMessageType = _WebSocketMessageType.BINARY; |
| break; |
| - case LEN_REST: |
| - _len = _len << 8 | byte; |
| - _remainingLenBytes--; |
| - if (_remainingLenBytes == 0) { |
| - _lengthDone(); |
| - } |
| + case _WebSocketOpcode.CLOSE: |
| + case _WebSocketOpcode.PING: |
| + case _WebSocketOpcode.PONG: |
| + // Control frames cannot be fragmented. |
| + if (!_fin) throw new WebSocketException("Protocol error"); |
| break; |
| - case MASK: |
| - _maskingKey = _maskingKey << 8 | byte; |
| - _remainingMaskingKeyBytes--; |
| - if (_remainingMaskingKeyBytes == 0) { |
| - _maskDone(); |
| - } |
| - break; |
| + default: |
| + throw new WebSocketException("Protocol error"); |
| + } |
| + _state = LEN_FIRST; |
| + break; |
| - case PAYLOAD: |
| - // The payload is not handled one byte at a time but in blocks. |
| - int payload; |
| - if (lastIndex - index <= _remainingPayloadBytes) { |
| - payload = lastIndex - index; |
| - } else { |
| - payload = _remainingPayloadBytes; |
| - } |
| - _remainingPayloadBytes -= payload; |
| - |
| - // Unmask payload if masked. |
| - if (_masked) { |
| - for (int i = 0; i < payload; i++) { |
| - int maskingByte = |
| - ((_maskingKey >> ((3 - _unmaskingIndex) * 8)) & 0xFF); |
| - buffer[index + i] = buffer[index + i] ^ maskingByte; |
| - _unmaskingIndex = (_unmaskingIndex + 1) % 4; |
| - } |
| - } |
| + case LEN_FIRST: |
| + _masked = (byte & 0x80) != 0; |
| + _len = byte & 0x7F; |
| + if (_isControlFrame() && _len > 125) { |
| + throw new WebSocketException("Protocol error"); |
| + } |
| + if (_len < 126) { |
| + _lengthDone(); |
| + } else if (_len == 126) { |
| + _len = 0; |
| + _remainingLenBytes = 2; |
| + _state = LEN_REST; |
| + } else if (_len == 127) { |
| + _len = 0; |
| + _remainingLenBytes = 8; |
| + _state = LEN_REST; |
| + } |
| + break; |
| - if (_isControlFrame()) { |
| - if (payload > 0) { |
| - // Allocate a buffer for collecting the control frame |
| - // payload if any. |
| - if (_controlPayload == null) { |
| - _controlPayload = new List<int>(); |
| - } |
| - _controlPayload.addAll(buffer.sublist(index, index + payload)); |
| - index += payload; |
| - } |
| - |
| - if (_remainingPayloadBytes == 0) { |
| - _controlFrameEnd(); |
| - } |
| - } else { |
| - if (_currentMessageType != _WebSocketMessageType.TEXT && |
| - _currentMessageType != _WebSocketMessageType.BINARY) { |
| - throw new WebSocketException("Protocol error"); |
| - } |
| - _controller.add( |
| - new Uint8List.view(buffer.buffer, index, payload)); |
| - index += payload; |
| - if (_remainingPayloadBytes == 0) { |
| - _messageFrameEnd(); |
| - } |
| - } |
| + case LEN_REST: |
| + _len = _len << 8 | byte; |
| + _remainingLenBytes--; |
| + if (_remainingLenBytes == 0) { |
| + _lengthDone(); |
| + } |
| + break; |
| - // Hack - as we always do index++ below. |
| - index--; |
| - break; |
| - } |
| + case MASK: |
| + _maskingBytes[4 - _remainingMaskingKeyBytes--] = byte; |
| + if (_remainingMaskingKeyBytes == 0) { |
| + _maskDone(); |
| + } |
| + break; |
| + |
| + case PAYLOAD: |
| + // The payload is not handled one byte at a time but in blocks. |
| + int payload; |
| + if (lastIndex - index <= _remainingPayloadBytes) { |
| + payload = lastIndex - index; |
| + } else { |
| + payload = _remainingPayloadBytes; |
| + } |
| + _remainingPayloadBytes -= payload; |
| + // Unmask payload if masked. |
| + if (_masked) { |
| + for (int i = index; i < index + payload; i++) { |
| + buffer[i] ^= _maskingBytes[_unmaskingIndex++ & 3]; |
| + } |
| + } |
| + // Control frame and data frame share _payload builder. |
| + _payload.add(new Uint8List.view(buffer.buffer, index, payload)); |
| + index += payload; |
| + if (_isControlFrame()) { |
| + if (_remainingPayloadBytes == 0) _controlFrameEnd(); |
| + } else { |
| + if (_currentMessageType != _WebSocketMessageType.TEXT && |
| + _currentMessageType != _WebSocketMessageType.BINARY) { |
| + throw new WebSocketException("Protocol error"); |
| + } |
| + if (_remainingPayloadBytes == 0) _messageFrameEnd(); |
| + } |
| - // Move to the next byte. |
| - index++; |
| + // Hack - as we always do index++ below. |
| + index--; |
| + break; |
| } |
| - } catch (e, stackTrace) { |
| - _state = FAILURE; |
| - _eventSink.addError(e, stackTrace); |
| + |
| + // Move to the next byte. |
| + index++; |
| } |
| } |
| @@ -267,7 +224,6 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
| throw new WebSocketException("Received masked frame from server"); |
| } |
| _state = MASK; |
| - _remainingMaskingKeyBytes = 4; |
| } else { |
| if (_serverSide) { |
| throw new WebSocketException("Received unmasked frame from client"); |
| @@ -312,13 +268,12 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
| if (_fin) { |
| switch (_currentMessageType) { |
| case _WebSocketMessageType.TEXT: |
| - _controller.close(); |
| + _eventSink.add(UTF8.decode(_payload.takeBytes())); |
| break; |
| case _WebSocketMessageType.BINARY: |
| - _controller.close(); |
| + _eventSink.add(_payload.takeBytes()); |
| break; |
| } |
| - _controller = null; |
| _currentMessageType = _WebSocketMessageType.NONE; |
| } |
| _prepareForNextFrame(); |
| @@ -328,16 +283,17 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
| switch (_opcode) { |
| case _WebSocketOpcode.CLOSE: |
| closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
| - if (_controlPayload.length > 0) { |
| - if (_controlPayload.length == 1) { |
| + if (_payload.length > 0) { |
| + var bytes = _payload.takeBytes(); |
| + if (bytes.length == 1) { |
| throw new WebSocketException("Protocol error"); |
| } |
| - closeCode = _controlPayload[0] << 8 | _controlPayload[1]; |
| + closeCode = bytes[0] << 8 | bytes[1]; |
| if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { |
| throw new WebSocketException("Protocol error"); |
| } |
| - if (_controlPayload.length > 2) { |
| - closeReason = UTF8.decode(_controlPayload.sublist(2)); |
| + if (bytes.length > 2) { |
| + closeReason = UTF8.decode(bytes.sublist(2)); |
| } |
| } |
| _state = CLOSED; |
| @@ -345,11 +301,11 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
| break; |
| case _WebSocketOpcode.PING: |
| - _eventSink.add(new _WebSocketPing(_controlPayload)); |
| + _eventSink.add(new _WebSocketPing(_payload.takeBytes())); |
| break; |
| case _WebSocketOpcode.PONG: |
| - _eventSink.add(new _WebSocketPong(_controlPayload)); |
| + _eventSink.add(new _WebSocketPong(_payload.takeBytes())); |
| break; |
| } |
| _prepareForNextFrame(); |
| @@ -363,16 +319,13 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
| void _prepareForNextFrame() { |
| if (_state != CLOSED && _state != FAILURE) _state = START; |
| - _fin = null; |
| - _opcode = null; |
| - _len = null; |
| - _masked = null; |
| - _maskingKey = 0; |
| - _remainingLenBytes = null; |
| - _remainingMaskingKeyBytes = null; |
| - _remainingPayloadBytes = null; |
| + _fin = false; |
| + _opcode = -1; |
| + _len = -1; |
| + _remainingLenBytes = -1; |
| + _remainingMaskingKeyBytes = 4; |
| + _remainingPayloadBytes = -1; |
| _unmaskingIndex = 0; |
| - _controlPayload = null; |
| } |
| } |
| @@ -571,7 +524,7 @@ class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { |
| } else if (dataLength > 125) { |
| headerSize += 2; |
| } |
| - List<int> header = new List<int>(headerSize); |
| + Uint8List header = new Uint8List(headerSize); |
| int index = 0; |
| // Set FIN and opcode. |
| header[index++] = 0x80 | opcode; |
| @@ -605,7 +558,7 @@ class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { |
| } |
| if (data is Uint8List) { |
| for (int i = 0; i < data.length; i++) { |
| - list[i] = data[i] ^ maskBytes[i % 4]; |
| + list[i] = data[i] ^ maskBytes[i & 3]; |
| } |
| } else { |
| for (int i = 0; i < data.length; i++) { |
| @@ -786,7 +739,7 @@ class _WebSocketImpl extends Stream implements WebSocket { |
| Random random = new Random(); |
| // Generate 16 random bytes. |
| - List<int> nonceData = new List<int>(16); |
| + Uint8List nonceData = new Uint8List(16); |
| for (int i = 0; i < 16; i++) { |
| nonceData[i] = random.nextInt(256); |
| } |