// Copyright (c) 2013, 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 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 this.expectedMessage = null]) { processor.onMessageStart = onMessageStart; processor.onMessageData = onMessageData; processor.onMessageEnd = onMessageEnd; processor.onClosed = onClosed; } void onMessageStart(int type) { data = new List(); } void onMessageData(List 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 expectedMessage; List 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 createFrame(bool fin, int opcode, int maskingKey, List 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 frame = new List.fixedLength(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 message) { mc.expectedMessage = message; List frame = createFrame( true, opcode, null, message, 0, message.length); // Update the processor with one big chunk. messageCount++; processor.update(frame); 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.getRange(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.getRange(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 message = new List.fixedLength(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 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 frame = createFrame(lastFrame, firstFrame ? opcode : 0x00, null, message, messageIndex, payloadSize); frameCount++; messageIndex += payloadSize; processor.update(frame); remaining -= payloadSize; firstFrame = false; } } void testMessageFragmentation(int opcode, List 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 message = new List.fixedLength(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(); }