| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import "dart:math"; |
| |
| part "../../../sdk/lib/io/websocket.dart"; |
| part "../../../sdk/lib/io/websocket_impl.dart"; |
| |
| class WebSocketFrame { |
| WebSocketFrame(int opcode, List<int> data); |
| } |
| |
| // Class that when hooked up to the web socket protocol processor will |
| // collect the message and expect it to be equal to the |
| // expectedMessage field when fully received. |
| class WebSocketMessageCollector { |
| WebSocketMessageCollector(_WebSocketProtocolProcessor this.processor, |
| [List<int> this.expectedMessage = null]) { |
| processor.onMessageStart = onMessageStart; |
| processor.onMessageData = onMessageData; |
| processor.onMessageEnd = onMessageEnd; |
| processor.onClosed = onClosed; |
| } |
| |
| void onMessageStart(int type) { |
| data = new List<int>(); |
| } |
| |
| void onMessageData(List<int> buffer, int index, int count) { |
| data.addAll(buffer.getRange(index, count)); |
| } |
| |
| void onMessageEnd() { |
| messageCount++; |
| Expect.listEquals(expectedMessage, data); |
| data = null; |
| } |
| |
| void onClosed(int status, String reason) { |
| closeCount++; |
| } |
| |
| void onError(e) { |
| Expect.fail("Unexpected error $e"); |
| } |
| |
| _WebSocketProtocolProcessor processor; |
| List<int> expectedMessage; |
| |
| List<int> data; |
| int messageCount = 0; |
| int closeCount = 0; |
| } |
| |
| |
| // Web socket constants. |
| const int FRAME_OPCODE_TEXT = 1; |
| const int FRAME_OPCODE_BINARY = 2; |
| |
| |
| // Function for building a web socket frame. |
| List<int> createFrame(bool fin, |
| int opcode, |
| int maskingKey, |
| List<int> data, |
| int offset, |
| int count) { |
| int frameSize = 2; |
| if (count > 125) frameSize += 2; |
| if (count > 65535) frameSize += 6; |
| frameSize += count; |
| // No masking. |
| assert(maskingKey == null); |
| List<int> frame = new List<int>(frameSize); |
| int frameIndex = 0; |
| frame[frameIndex++] = (fin ? 0x80 : 0x00) | opcode; |
| if (count < 126) { |
| frame[frameIndex++] = count; |
| } else if (count < 65536) { |
| frame[frameIndex++] = 126; |
| frame[frameIndex++] = count >> 8; |
| frame[frameIndex++] = count & 0xFF; |
| } else { |
| frame[frameIndex++] = 127; |
| for (int i = 0; i < 8; i++) { |
| frame[frameIndex++] = count >> ((7 - i) * 8) & 0xFF; |
| } |
| } |
| frame.setRange(frameIndex, count, data, offset); |
| return frame; |
| } |
| |
| |
| // Test processing messages which are sent in a single frame. |
| void testFullMessages() { |
| // Use the same web socket protocol processor for all frames. |
| _WebSocketProtocolProcessor processor = new _WebSocketProtocolProcessor(); |
| WebSocketMessageCollector mc = new WebSocketMessageCollector(processor); |
| |
| int messageCount = 0; |
| |
| void testMessage(int opcode, List<int> message) { |
| mc.expectedMessage = message; |
| List<int> frame = createFrame( |
| true, opcode, null, message, 0, message.length); |
| |
| // Update the processor with one big chunk. |
| messageCount++; |
| processor.update(frame, 0, frame.length); |
| Expect.isNull(mc.data); |
| Expect.equals(0, processor._state); |
| |
| // Only run this part on small messages. |
| if (message.length < 1000) { |
| // Update the processor one byte at the time. |
| messageCount++; |
| for (int i = 0; i < frame.length; i++) { |
| processor.update(frame, i, 1); |
| } |
| Expect.equals(0, processor._state); |
| Expect.isNull(mc.data); |
| |
| // Update the processor two bytes at the time. |
| messageCount++; |
| for (int i = 0; i < frame.length; i += 2) { |
| processor.update(frame, i, i + 1 < frame.length ? 2 : 1); |
| } |
| Expect.equals(0, processor._state); |
| Expect.isNull(mc.data); |
| } |
| } |
| |
| void runTest(int from, int to, int step) { |
| for (int messageLength = from; messageLength < to; messageLength += step) { |
| List<int> message = new List<int>(messageLength); |
| for (int i = 0; i < messageLength; i++) message[i] = i & 0xFF; |
| testMessage(FRAME_OPCODE_TEXT, message); |
| testMessage(FRAME_OPCODE_BINARY, message); |
| } |
| } |
| |
| // Test different message sizes. |
| runTest(0, 10, 1); |
| runTest(120, 130, 1); |
| runTest(0, 1000, 100); |
| runTest(65534, 65537, 1); |
| print("Messages test, messages $messageCount"); |
| Expect.equals(messageCount, mc.messageCount); |
| Expect.equals(0, mc.closeCount); |
| } |
| |
| |
| // Test processing of frames which are split into fragments. |
| void testFragmentedMessages() { |
| // Use the same web socket protocol processor for all frames. |
| _WebSocketProtocolProcessor processor = new _WebSocketProtocolProcessor(); |
| WebSocketMessageCollector mc = new WebSocketMessageCollector(processor); |
| |
| int messageCount = 0; |
| int frameCount = 0; |
| |
| void testFragmentMessage(int opcode, List<int> message, int fragmentSize) { |
| messageCount++; |
| int messageIndex = 0; |
| int remaining = message.length; |
| bool firstFrame = true; |
| bool lastFrame = false; |
| while (!lastFrame) { |
| int payloadSize = min(fragmentSize, remaining); |
| lastFrame = payloadSize == remaining; |
| List<int> frame = createFrame(lastFrame, |
| firstFrame ? opcode : 0x00, |
| null, |
| message, |
| messageIndex, |
| payloadSize); |
| frameCount++; |
| messageIndex += payloadSize; |
| processor.update(frame, 0, frame.length); |
| remaining -= payloadSize; |
| firstFrame = false; |
| } |
| } |
| |
| void testMessageFragmentation(int opcode, List<int> message) { |
| mc.expectedMessage = message; |
| |
| // Test with fragmenting the message in different fragment sizes. |
| if (message.length <= 10) { |
| for (int i = 1; i < 10; i++) { |
| testFragmentMessage(opcode, message, i); |
| } |
| } else { |
| testFragmentMessage(opcode, message, 10); |
| testFragmentMessage(opcode, message, 100); |
| } |
| } |
| |
| void runTest(int from, int to, int step) { |
| for (int messageLength = from; messageLength < to; messageLength += step) { |
| List<int> message = new List<int>(messageLength); |
| for (int i = 0; i < messageLength; i++) message[i] = i & 0xFF; |
| testMessageFragmentation(FRAME_OPCODE_TEXT, message); |
| testMessageFragmentation(FRAME_OPCODE_BINARY, message); |
| } |
| } |
| |
| // Test different message sizes. |
| runTest(0, 10, 1); |
| runTest(120, 130, 1); |
| runTest(0, 1000, 100); |
| runTest(65534, 65537, 1); |
| print("Fragment messages test, messages $messageCount, frames $frameCount"); |
| Expect.equals(messageCount, mc.messageCount); |
| Expect.equals(0, mc.closeCount); |
| } |
| |
| void main() { |
| testFullMessages(); |
| testFragmentedMessages(); |
| } |