blob: 271d14158fa29723aca193956fadcebf69255711 [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.
library dart.io;
import "package:async_helper/async_helper.dart";
import "package:expect/expect.dart";
import "dart:async";
import "dart:collection";
import "dart:convert";
import "dart:developer";
import "dart:io";
import "dart:math";
import "dart:typed_data";
import "dart:isolate";
part "../../../sdk/lib/_http/crypto.dart";
part "../../../sdk/lib/_http/http_impl.dart";
part "../../../sdk/lib/_http/http_date.dart";
part "../../../sdk/lib/_http/http_parser.dart";
part "../../../sdk/lib/_http/http_headers.dart";
part "../../../sdk/lib/_http/http_session.dart";
part "../../../sdk/lib/_http/websocket.dart";
part "../../../sdk/lib/_http/websocket_impl.dart";
class WebSocketFrame {
WebSocketFrame(int opcode, List<int> data);
}
// Class that when hooked up to the web socket protocol transformer will
// collect the message and expect it to be equal to the
// expectedMessage field when fully received.
class WebSocketMessageCollector {
List<int> expectedMessage;
int messageCount = 0;
var data;
Function onClosed;
WebSocketMessageCollector(Stream stream,
[List<int> this.expectedMessage = null]) {
stream.listen(onMessageData, onDone: onClosed, onError: onError);
}
void onMessageData(buffer) {
if (buffer is String) {
buffer = utf8.encode(buffer);
}
Expect.listEquals(expectedMessage, buffer);
messageCount++;
data = buffer;
}
void onError(e, trace) {
String msg = "Unexpected error $e";
if (trace != null) msg += "\nStackTrace: $trace";
Expect.fail(msg);
}
}
// 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 Uint8List(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, frameIndex + count, data, offset);
return frame;
}
// Test processing messages which are sent in a single frame.
void testFullMessages() {
void testMessage(int opcode, List<int> message) {
int messageCount = 0;
// Use the same web socket protocol transformer for all frames.
var transformer = new _WebSocketProtocolTransformer();
var controller = new StreamController(sync: true);
WebSocketMessageCollector mc = new WebSocketMessageCollector(
controller.stream.transform(transformer), message);
List<int> frame =
createFrame(true, opcode, null, message, 0, message.length);
// Update the transformer with one big chunk.
messageCount++;
controller.add(frame);
mc.onClosed = () {
Expect.isNotNull(mc.data);
Expect.equals(0, transformer._state);
mc.data = null;
// Only run this part on small messages.
if (message.length < 1000) {
// Update the transformer one byte at the time.
messageCount++;
for (int i = 0; i < frame.length; i++) {
controller.add(<int>[frame[i]]);
}
Expect.equals(0, transformer._state);
Expect.isNotNull(mc.data);
mc.data = null;
// Update the transformer two bytes at the time.
messageCount++;
for (int i = 0; i < frame.length; i += 2) {
controller.add(frame.sublist(i, min(i + 2, frame.length)));
}
Expect.equals(0, transformer._state);
Expect.isNotNull(mc.data);
}
Expect.equals(messageCount, mc.messageCount);
print("Messages test, messages $messageCount");
};
controller.close();
}
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 & 0x7F;
testMessage(FRAME_OPCODE_TEXT, message);
for (int i = 0; i < messageLength; i++) message[i] = i & 0xFF;
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);
}
// Test processing of frames which are split into fragments.
void testFragmentedMessages() {
// Use the same web socket protocol transformer for all frames.
var transformer = new _WebSocketProtocolTransformer();
var controller = new StreamController(sync: true);
WebSocketMessageCollector mc =
new WebSocketMessageCollector(controller.stream.transform(transformer));
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;
controller.add(frame);
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 & 0x7F;
testMessageFragmentation(FRAME_OPCODE_TEXT, message);
for (int i = 0; i < messageLength; i++) message[i] = i & 0xFF;
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);
}
void testUnmaskedMessage() {
var transformer = new _WebSocketProtocolTransformer(true);
var controller = new StreamController(sync: true);
asyncStart();
controller.stream.transform(transformer).listen((_) {}, onError: (e) {
asyncEnd();
});
var message = new Uint8List(10);
List<int> frame =
createFrame(true, FRAME_OPCODE_BINARY, null, message, 0, message.length);
controller.add(frame);
}
void main() {
testFullMessages();
testFragmentedMessages();
testUnmaskedMessage();
}