blob: 9c891d66526436db7570d45af91f2274ee822d6b [file] [log] [blame]
// 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";
import "dart:async";
part "../../../sdk/lib/io/http.dart";
part "../../../sdk/lib/io/io_sink.dart";
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();
}