dart-sdk/tests/standalone/io/web_socket_protocol_processor_test.dart
Leaf Petersen 131da0516b Refactor standalone tests to avoid including platform library code via parts
Add testing only stubs to dart:_http, and make it available for use in
the standalone tests so that the tests can stop including platform
library code directly using "part" directives (which can cause
surprising breakage, since the same code gets included twice via
different uris).

TEST=Existing tests

Change-Id: I1b5a5061008ef36980bd21b46a9d0fd701286f66
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/262780
Reviewed-by: Kallen Tu <kallentu@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
Reviewed-by: Bob Nystrom <rnystrom@google.com>
2022-10-29 00:29:09 +00:00

235 lines
7.1 KiB
Dart

// 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._http;
import "dart:async";
import "dart:convert";
// ignore: IMPORT_INTERNAL_LIBRARY
import "dart:_http"
show
TestingClass$_WebSocketProtocolTransformer,
Testing$_WebSocketProtocolTransformer;
import "dart:math";
import "dart:typed_data";
import "package:async_helper/async_helper.dart";
import "package:expect/expect.dart";
typedef _WebSocketProtocolTransformer
= TestingClass$_WebSocketProtocolTransformer;
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;
void 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<List<int>>(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.test$_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.test$_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.test$_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 = [for (int i = 0; i < messageLength; 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<List<int>>(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 = [for (int i = 0; i < messageLength; 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<List<int>>(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();
}