From bb96d8960d8f9c9a4a00454fca8154fa2b7fda7b Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Tue, 20 Feb 2024 13:53:46 -0800 Subject: [PATCH 01/11] Conformance tests --- pkgs/web_socket_conformance_tests/.gitignore | 7 + .../web_socket_conformance_tests/CHANGELOG.md | 3 + pkgs/web_socket_conformance_tests/README.md | 39 +++ .../analysis_options.yaml | 30 ++ .../bin/generate_server_wrappers.dart | 51 +++ .../lib/src/close_local_server.dart | 41 +++ .../lib/src/close_local_server_vm.dart | 12 + .../lib/src/close_local_server_web.dart | 9 + .../lib/src/close_local_tests.dart | 125 +++++++ .../lib/src/close_remote_server.dart | 38 +++ .../lib/src/close_remote_server_vm.dart | 12 + .../lib/src/close_remote_server_web.dart | 9 + .../lib/src/close_remote_tests.dart | 312 ++++++++++++++++++ .../src/disconnect_after_upgrade_server.dart | 48 +++ .../disconnect_after_upgrade_server_vm.dart | 12 + .../disconnect_after_upgrade_server_web.dart | 10 + .../src/disconnect_after_upgrade_tests.dart | 41 +++ .../lib/src/echo_server.dart | 30 ++ .../lib/src/echo_server_vm.dart | 12 + .../lib/src/echo_server_web.dart | 9 + .../lib/src/no_upgrade_server.dart | 33 ++ .../lib/src/no_upgrade_server_vm.dart | 12 + .../lib/src/no_upgrade_server_web.dart | 9 + .../lib/src/no_upgrade_tests.dart | 37 +++ .../lib/src/payload_transfer_tests.dart | 207 ++++++++++++ .../lib/src/peer_protocol_errors_server.dart | 49 +++ .../src/peer_protocol_errors_server_vm.dart | 12 + .../src/peer_protocol_errors_server_web.dart | 9 + .../lib/src/peer_protocol_errors_tests.dart | 55 +++ .../lib/web_socket_conformance_tests.dart | 29 ++ .../web_socket_conformance_tests/pubspec.yaml | 19 ++ 31 files changed, 1321 insertions(+) create mode 100644 pkgs/web_socket_conformance_tests/.gitignore create mode 100644 pkgs/web_socket_conformance_tests/CHANGELOG.md create mode 100644 pkgs/web_socket_conformance_tests/README.md create mode 100644 pkgs/web_socket_conformance_tests/analysis_options.yaml create mode 100644 pkgs/web_socket_conformance_tests/bin/generate_server_wrappers.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/close_local_server_vm.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/close_local_server_web.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/close_remote_server_vm.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/close_remote_server_web.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server_vm.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server_web.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/echo_server.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/echo_server_vm.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/echo_server_web.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server_vm.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server_web.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server_vm.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server_web.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart create mode 100644 pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart create mode 100644 pkgs/web_socket_conformance_tests/pubspec.yaml diff --git a/pkgs/web_socket_conformance_tests/.gitignore b/pkgs/web_socket_conformance_tests/.gitignore new file mode 100644 index 0000000000..3cceda5578 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/.gitignore @@ -0,0 +1,7 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ + +# Avoid committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock diff --git a/pkgs/web_socket_conformance_tests/CHANGELOG.md b/pkgs/web_socket_conformance_tests/CHANGELOG.md new file mode 100644 index 0000000000..effe43c82c --- /dev/null +++ b/pkgs/web_socket_conformance_tests/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/pkgs/web_socket_conformance_tests/README.md b/pkgs/web_socket_conformance_tests/README.md new file mode 100644 index 0000000000..8b55e735b5 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/pkgs/web_socket_conformance_tests/analysis_options.yaml b/pkgs/web_socket_conformance_tests/analysis_options.yaml new file mode 100644 index 0000000000..dee8927aaf --- /dev/null +++ b/pkgs/web_socket_conformance_tests/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/pkgs/web_socket_conformance_tests/bin/generate_server_wrappers.dart b/pkgs/web_socket_conformance_tests/bin/generate_server_wrappers.dart new file mode 100644 index 0000000000..7168868db2 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/bin/generate_server_wrappers.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2023, 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. + +/// Generates the '*_server_vm.dart' and '*_server_web.dart' support files. +library; + +import 'dart:core'; +import 'dart:io'; + +import 'package:dart_style/dart_style.dart'; + +const vm = '''// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; + +import ''; + +/// Starts the redirect test HTTP server in the same process. +Future> startServer() async { + final controller = StreamChannelController(sync: true); + hybridMain(controller.foreign); + return controller.local; +} +'''; + +const web = '''// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; + +/// Starts the redirect test HTTP server out-of-process. +Future> startServer() async => spawnHybridUri(Uri( + scheme: 'package', + path: 'web_socket_conformance_tests/src/')); +'''; + +void main() async { + final files = await Directory('lib/src').list().toList(); + final formatter = DartFormatter(); + + files.where((file) => file.path.endsWith('_server.dart')).forEach((file) { + final vmPath = file.path.replaceAll('_server.dart', '_server_vm.dart'); + File(vmPath).writeAsStringSync(formatter.format(vm.replaceAll( + '', file.uri.pathSegments.last))); + + final webPath = file.path.replaceAll('_server.dart', '_server_web.dart'); + File(webPath).writeAsStringSync(formatter.format(web.replaceAll( + '', file.uri.pathSegments.last))); + }); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart b/pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart new file mode 100644 index 0000000000..8ca6a79fcc --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart @@ -0,0 +1,41 @@ +// Copyright (c) 2023, 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:async'; +import 'dart:io'; + +import 'package:stream_channel/stream_channel.dart'; + +/// Starts an WebSocket server that echos the payload of the request. +/// +/// Channel protocol: +/// On Startup: +/// - send port +/// On Request Received: +/// - echoes the request payload +/// When Receive Anything: +/// - exit +void hybridMain(StreamChannel channel) async { + late HttpServer server; + + server = (await HttpServer.bind('localhost', 0)) + ..listen((request) async { + final webSocket = await WebSocketTransformer.upgrade( + request, + ); + + webSocket.listen((event) { + channel.sink.add(event); + }, onDone: () { + webSocket.close(4123, 'server closed the connection'); + channel.sink.add(webSocket.closeCode); + channel.sink.add(webSocket.closeReason); + }); + }); + + channel.sink.add(server.port); + await channel + .stream.first; // Any writes indicates that the server should exit. + unawaited(server.close()); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_local_server_vm.dart b/pkgs/web_socket_conformance_tests/lib/src/close_local_server_vm.dart new file mode 100644 index 0000000000..c0d0652326 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/close_local_server_vm.dart @@ -0,0 +1,12 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; + +import 'close_local_server.dart'; + +/// Starts the redirect test HTTP server in the same process. +Future> startServer() async { + final controller = StreamChannelController(sync: true); + hybridMain(controller.foreign); + return controller.local; +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_local_server_web.dart b/pkgs/web_socket_conformance_tests/lib/src/close_local_server_web.dart new file mode 100644 index 0000000000..f7bb3810a6 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/close_local_server_web.dart @@ -0,0 +1,9 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; + +/// Starts the redirect test HTTP server out-of-process. +Future> startServer() async => spawnHybridUri(Uri( + scheme: 'package', + path: 'web_socket_conformance_tests/src/close_local_server.dart')); diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart new file mode 100644 index 0000000000..e9769ca47d --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart @@ -0,0 +1,125 @@ +// Copyright (c) 2023, 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:typed_data'; + +import 'package:async/async.dart'; +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; +import 'package:websocket/websocket.dart'; + +import 'close_local_server_vm.dart' + if (dart.library.html) 'close_server_web.dart'; + +/// Tests that the [WebSocketChannel] can correctly transmit and receive text +/// and binary payloads. +void testLocalClose( + Future Function(Uri uri, {Iterable? protocols}) + channelFactory) { + group('local close', () { + late Uri uri; + late StreamChannel httpServerChannel; + late StreamQueue httpServerQueue; + + setUp(() async { + httpServerChannel = await startServer(); + httpServerQueue = StreamQueue(httpServerChannel.stream); + uri = Uri.parse('ws://localhost:${await httpServerQueue.next}'); + }); + tearDown(() async { + httpServerChannel.sink.add(null); +// await httpServerQueue.next; + }); +/* + test('connected', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + expect(channel.closeCode, null); + expect(channel.closeReason, null); + }); +*/ + // https://websockets.spec.whatwg.org/#eventdef-websocket-close + // Dart will wait up to 5 seconds to get the close code from the server otherwise + // it will use the local close code. + + test('reserved close code', () async { + // If code is present, but is neither an integer equal to 1000 nor an integer in the range 3000 to 4999, inclusive, throw an "InvalidAccessError" DOMException. + // If reasonBytes is longer than 123 bytes, then throw a "SyntaxError" DOMException. + + final channel = await channelFactory(uri); + await expectLater(() => channel.close(1004), throwsA(isA())); + }); + + test('too long close reason', () async { + final channel = await channelFactory(uri); + await expectLater(() => channel.close(3000, 'a'.padLeft(124)), + throwsA(isA())); + }); + + test('close', () async { + final channel = await channelFactory(uri); + + await channel.close(); + final closeCode = await httpServerQueue.next as int?; + final closeReason = await httpServerQueue.next as String?; + + expect(closeCode, 1005); + expect(closeReason, ''); + expect(await channel.events.isEmpty, true); + }); + + test('with code 3000', () async { + final channel = await channelFactory(uri); + + await channel.close(3000); + final closeCode = await httpServerQueue.next as int?; + final closeReason = await httpServerQueue.next as String?; + + expect(closeCode, 3000); + expect(closeReason, ''); + expect(await channel.events.isEmpty, true); + }); + + test('with code 4999', () async { + final channel = await channelFactory(uri); + + await channel.close(4999); + final closeCode = await httpServerQueue.next as int?; + final closeReason = await httpServerQueue.next as String?; + + expect(closeCode, 4999); + expect(closeReason, ''); + expect(await channel.events.isEmpty, true); + }); + + test('with code and reason', () async { + final channel = await channelFactory(uri); + + await channel.close(3000, 'Client initiated closure'); + final closeCode = await httpServerQueue.next as int?; + final closeReason = await httpServerQueue.next as String?; + + expect(closeCode, 3000); + expect(closeReason, 'Client initiated closure'); + expect(await channel.events.isEmpty, true); + }); + + test('close after close', () async { + final channel = await channelFactory(uri); + + await channel.close(3000, 'Client initiated closure'); + + expectLater( + () async => await channel.close(3001, 'Client initiated closure'), + throwsA(isA())); + final closeCode = await httpServerQueue.next as int?; + final closeReason = await httpServerQueue.next as String?; + + expect(closeCode, 3000); + expect(closeReason, 'Client initiated closure'); + expect(await channel.events.isEmpty, true); + }); + }); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart b/pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart new file mode 100644 index 0000000000..ae66836d99 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart @@ -0,0 +1,38 @@ +// Copyright (c) 2023, 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:async'; +import 'dart:io'; + +import 'package:stream_channel/stream_channel.dart'; + +/// Starts an WebSocket server that echos the payload of the request. +/// +/// Channel protocol: +/// On Startup: +/// - send port +/// On Request Received: +/// - echoes the request payload +/// When Receive Anything: +/// - exit +void hybridMain(StreamChannel channel) async { + late HttpServer server; + + server = (await HttpServer.bind('localhost', 0)) + ..listen((request) async { + final webSocket = await WebSocketTransformer.upgrade( + request, + ); + + webSocket.listen((event) { + channel.sink.add(event); + webSocket.close(4123, 'server closed the connection'); + }); + }); + + channel.sink.add(server.port); + await channel + .stream.first; // Any writes indicates that the server should exit. + unawaited(server.close()); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_remote_server_vm.dart b/pkgs/web_socket_conformance_tests/lib/src/close_remote_server_vm.dart new file mode 100644 index 0000000000..4cc6dba56e --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/close_remote_server_vm.dart @@ -0,0 +1,12 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; + +import 'close_remote_server.dart'; + +/// Starts the redirect test HTTP server in the same process. +Future> startServer() async { + final controller = StreamChannelController(sync: true); + hybridMain(controller.foreign); + return controller.local; +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_remote_server_web.dart b/pkgs/web_socket_conformance_tests/lib/src/close_remote_server_web.dart new file mode 100644 index 0000000000..6e832bacac --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/close_remote_server_web.dart @@ -0,0 +1,9 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; + +/// Starts the redirect test HTTP server out-of-process. +Future> startServer() async => spawnHybridUri(Uri( + scheme: 'package', + path: 'web_socket_conformance_tests/src/close_remote_server.dart')); diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart new file mode 100644 index 0000000000..daade10793 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart @@ -0,0 +1,312 @@ +// Copyright (c) 2023, 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:typed_data'; + +import 'package:async/async.dart'; +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; +import 'package:websocket/websocket.dart'; + +import 'close_remote_server_vm.dart' + if (dart.library.html) 'close_remote_server_web.dart'; + +/// Tests that the [WebSocketChannel] can correctly transmit and receive text +/// and binary payloads. +void testRemoteClose( + Future Function(Uri uri, {Iterable? protocols}) + channelFactory) { + group('remote close', () { + late Uri uri; + late StreamChannel httpServerChannel; + late StreamQueue httpServerQueue; + + setUp(() async { + httpServerChannel = await startServer(); + httpServerQueue = StreamQueue(httpServerChannel.stream); + uri = Uri.parse('ws://localhost:${await httpServerQueue.next}'); + }); + tearDown(() async { + httpServerChannel.sink.add(null); +// await httpServerQueue.next; + }); +/* + test('connected', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + expect(channel.closeCode, null); + expect(channel.closeReason, null); + }); +*/ + // https://websockets.spec.whatwg.org/#eventdef-websocket-close + // Dart will wait up to 5 seconds to get the close code from the server otherwise + // it will use the local close code. + +/* + test('reserved close code', () async { + // If code is present, but is neither an integer equal to 1000 nor an integer in the range 3000 to 4999, inclusive, throw an "InvalidAccessError" DOMException. + // If reasonBytes is longer than 123 bytes, then throw a "SyntaxError" DOMException. + + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + expect(channel.closeCode, null); + expect(channel.closeReason, null); + // web uncaught // InvalidAccessError + // sync WebSocketException + await channel.sink.close(1004, 'boom'); + }); + + test('too long close reason', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + expect(channel.closeCode, null); + expect(channel.closeReason, null); + // web uncaught // SyntaxError + // vm: passes! + await channel.sink.close(1000, 'Boom'.padLeft(1000)); + }); +*/ + test('with code and reason', () async { + final channel = await channelFactory(uri); + + channel.sendText('Please close'); + expect(await channel.events.toList(), + [CloseReceived(4123, 'server closed the connection')]); + }); + + test('send after close', () async { + final channel = await channelFactory(uri); + + channel.sendText('Please close'); + expect(await channel.events.toList(), + [CloseReceived(4123, 'server closed the connection')]); + expect(() => channel.sendText('test'), throwsStateError); +/* + final closeCode = await httpServerQueue.next as int?; + final closeReason = await httpServerQueue.next as String?; + expect(closeCode, 3000); + expect(closeReason, 'Client initiated closure');*/ + }); +/* + test('cancel', () async { + final channel = + channelFactory(uri.replace(queryParameters: {'sleep': '5'})); + + var sinkDoneComplete = false; + var sinkDoneOnError = false; + var streamOnData = false; + var streamOnDone = false; + var streamOnError = false; + + channel.sink.done.then((_) { + sinkDoneComplete = true; + }, onError: (_) { + sinkDoneOnError = true; + }); + + final streamSubscription = channel.stream.listen((_) { + streamOnData = true; + }, onError: (_) { + streamOnError = true; + }, onDone: () { + streamOnDone = true; + }); + + await expectLater(channel.ready, completes); + // VM: Cancels subscription to the socket, which means that this deadlocks. + await streamSubscription.cancel(); + expect(() => channel.stream.listen((_) {}), throwsStateError); + channel.sink.add('add after stream closed'); + + expect(channel.closeCode, null); + expect(channel.closeReason, null); + + expect(sinkDoneComplete, false); + expect(sinkDoneOnError, false); + expect(streamOnData, false); + expect(streamOnDone, false); + expect(streamOnError, false); + + await channel.sink.done; + expect(await httpServerQueue.next, 'add after stream closed'); + expect(await httpServerQueue.next, null); + expect(await httpServerQueue.next, null); + expect(channel.closeCode, 4123); + expect(channel.closeReason, 'server closed the connection'); + // cancelling should close according to lassa! + }, skip: _isVM); + + test('cancel - client close', () async { + final channel = + channelFactory(uri.replace(queryParameters: {'sleep': '5'})); + + var sinkDoneComplete = false; + var sinkDoneOnError = false; + var streamOnData = false; + var streamOnDone = false; + var streamOnError = false; + + channel.sink.done.then((_) { + sinkDoneComplete = true; + }, onError: (_) { + sinkDoneOnError = true; + }); + + final streamSubscription = channel.stream.listen((_) { + streamOnData = true; + }, onError: (_) { + streamOnError = true; + }, onDone: () { + streamOnDone = true; + }); + + await expectLater(channel.ready, completes); + await streamSubscription.cancel(); + expect(() => channel.stream.listen((_) {}), throwsStateError); + channel.sink.add('add after stream closed'); + + expect(channel.closeCode, null); + expect(channel.closeReason, null); + + expect(sinkDoneComplete, false); + expect(sinkDoneOnError, false); + expect(streamOnData, false); + expect(streamOnDone, false); + expect(streamOnError, false); + + await channel.sink.close(4444, 'client closed the connection'); + expect(await httpServerQueue.next, 'add after stream closed'); + expect(await httpServerQueue.next, 4444); + expect(await httpServerQueue.next, 'client closed the connection'); + expect(channel.closeCode, 4444); + expect(channel.closeReason, 'client closed the connection'); + }); + + test('client initiated', () async { + final channel = channelFactory(uri); + + var sinkDoneComplete = false; + var sinkDoneOnError = false; + var streamOnData = false; + var streamOnDone = false; + var streamOnError = false; + + channel.sink.done.then((_) { + sinkDoneComplete = true; + }, onError: (_) { + sinkDoneOnError = true; + }); + + channel.stream.listen((_) { + streamOnData = true; + }, onError: (_) { + streamOnError = true; + }, onDone: () { + streamOnDone = true; + }); + + await expectLater(channel.ready, completes); + await channel.sink.close(4444, 'client closed the connection'); + expect(channel.closeCode, null); // VM 4123 + expect(channel.closeReason, null); // VM 'server closed the connection' + + expect(await httpServerQueue.next, 4444); // VM 4123 + expect(await httpServerQueue.next, + 'client closed the connection'); // VM 'server closed the connection' + expect(channel.closeCode, 4123); + expect(channel.closeReason, 'server closed the connection'); + expect(() => channel.sink.add('add after connection closed'), + throwsStateError); + + expect(sinkDoneComplete, true); + expect(sinkDoneOnError, false); + expect(streamOnData, false); + expect(streamOnDone, true); + expect(streamOnError, false); + }, skip: _isVM); + + test('client initiated - slow server', () async { + final channel = + channelFactory(uri.replace(queryParameters: {'sleep': '5'})); + + var sinkDoneComplete = false; + var sinkDoneOnError = false; + var streamOnData = false; + var streamOnDone = false; + var streamOnError = false; + + channel.sink.done.then((_) { + sinkDoneComplete = true; + }, onError: (_) { + sinkDoneOnError = true; + }); + + channel.stream.listen((_) { + streamOnData = true; + }, onError: (_) { + streamOnError = true; + }, onDone: () { + streamOnDone = true; + }); + + await expectLater(channel.ready, completes); + await channel.sink.close(4444, 'client closed the connection'); + expect(channel.closeCode, null); + expect(channel.closeReason, null); + + expect(await httpServerQueue.next, 4444); + expect(await httpServerQueue.next, 'client closed the connection'); + expect(channel.closeCode, 4444); // VM: null - sometimes null + expect(channel.closeReason, 'client closed the connection'); // VM: null + expect(() => channel.sink.add('add after connection closed'), + throwsStateError); + await channel.sink.close(); + + expect(sinkDoneComplete, true); + expect(sinkDoneOnError, false); + expect(streamOnData, false); + expect(streamOnDone, true); + expect(streamOnError, false); + }); + + test('server initiated', () async { + final channel = channelFactory(uri); + + var sinkDoneComplete = false; + var sinkDoneOnError = false; + var streamOnData = false; + var streamOnDone = false; + var streamOnError = false; + + channel.sink.done.then((_) { + sinkDoneComplete = true; + }, onError: (_) { + sinkDoneOnError = true; + }); + + final streamListen = channel.stream.listen((_) { + streamOnData = true; + }, onError: (_) { + streamOnError = true; + }).asFuture(); + + await expectLater(channel.ready, completes); + await streamListen; + + expect(channel.closeCode, 4123); + expect(channel.closeReason, 'server closed the connection'); + channel.sink.add('add after connection closed'); + await channel.sink.close(); + + expect(sinkDoneComplete, true); + expect(sinkDoneOnError, false); + expect(streamOnData, false); + expect(streamOnError, false); + }); + */ + }); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart new file mode 100644 index 0000000000..1ab0e19265 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart @@ -0,0 +1,48 @@ +// Copyright (c) 2023, 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:async'; +import 'dart:convert'; +import 'dart:io'; +import "package:crypto/crypto.dart"; +import 'package:stream_channel/stream_channel.dart'; + +const WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +/// Starts an WebSocket server that echos the payload of the request. +/// +/// Channel protocol: +/// On Startup: +/// - send port +/// On Request Received: +/// - echoes the request payload +/// When Receive Anything: +/// - exit +void hybridMain(StreamChannel channel) async { + late final HttpServer server; + server = (await HttpServer.bind('localhost', 0)) + ..listen((request) async { + var key = request.headers.value('Sec-WebSocket-Key'); + var digest = sha1.convert("$key$WEB_SOCKET_GUID".codeUnits); + var accept = base64.encode(digest.bytes); + request.response + ..statusCode = HttpStatus.switchingProtocols + ..headers.add(HttpHeaders.connectionHeader, "Upgrade") + ..headers.add(HttpHeaders.upgradeHeader, "websocket") + ..headers.add("Sec-WebSocket-Accept", accept); + request.response.contentLength = 0; +// await request.response.close(); + final socket = await request.response.detachSocket(); +// final websocket = WebSocket.fromUpgradedSocket(socket, serverSide: true); +// websocket.listen((x) => print('server received: $x')); + socket.destroy(); + print('socket is closed'); + }); + + channel.sink.add(server.port); + + await channel + .stream.first; // Any writes indicates that the server should exit. + unawaited(server.close()); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server_vm.dart b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server_vm.dart new file mode 100644 index 0000000000..0bc7426239 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server_vm.dart @@ -0,0 +1,12 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; + +import 'disconnect_after_upgrade_server.dart'; + +/// Starts the redirect test HTTP server in the same process. +Future> startServer() async { + final controller = StreamChannelController(sync: true); + hybridMain(controller.foreign); + return controller.local; +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server_web.dart b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server_web.dart new file mode 100644 index 0000000000..9e1a13771f --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server_web.dart @@ -0,0 +1,10 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; + +/// Starts the redirect test HTTP server out-of-process. +Future> startServer() async => spawnHybridUri(Uri( + scheme: 'package', + path: + 'web_socket_conformance_tests/src/disconnect_after_upgrade_server.dart')); diff --git a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart new file mode 100644 index 0000000000..f114205bf6 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart @@ -0,0 +1,41 @@ +// Copyright (c) 2023, 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 'package:async/async.dart'; +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; +import 'package:websocket/websocket.dart'; + +import 'disconnect_after_upgrade_server_vm.dart' + if (dart.library.html) 'disconnect_after_upgrade_server_web.dart'; + +/// Tests that the [WebSocketChannel] can correctly transmit and receive text +/// and binary payloads. +void testDisconnectAfterUpgrade( + Future Function(Uri uri, {Iterable? protocols}) + channelFactory) { + group('disconnect', () { + late final Uri uri; + late final StreamChannel httpServerChannel; + late final StreamQueue httpServerQueue; + + setUpAll(() async { + httpServerChannel = await startServer(); + httpServerQueue = StreamQueue(httpServerChannel.stream); + uri = Uri.parse('ws://localhost:${await httpServerQueue.next}'); + }); + tearDownAll(() => httpServerChannel.sink.add(null)); + + test('disconnect after upgrade', () async { + final channel = await channelFactory(uri); + channel.sendText('test'); + expect( + (await channel.events.single as CloseReceived).code, + anyOf([ + 1005, // closed no status + 1006, // closed abnormal + ])); + }); + }); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/echo_server.dart b/pkgs/web_socket_conformance_tests/lib/src/echo_server.dart new file mode 100644 index 0000000000..1d7c14db1d --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/echo_server.dart @@ -0,0 +1,30 @@ +// Copyright (c) 2023, 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:async'; +import 'dart:io'; + +import 'package:stream_channel/stream_channel.dart'; + +/// Starts an WebSocket server that echos the payload of the request. +/// +/// Channel protocol: +/// On Startup: +/// - send port +/// On Request Received: +/// - echoes the request payload +/// When Receive Anything: +/// - exit +void hybridMain(StreamChannel channel) async { + late HttpServer server; + + server = (await HttpServer.bind('localhost', 0)) + ..transform(WebSocketTransformer()) + .listen((WebSocket webSocket) => webSocket.listen(webSocket.add)); + + channel.sink.add(server.port); + await channel + .stream.first; // Any writes indicates that the server should exit. + unawaited(server.close()); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/echo_server_vm.dart b/pkgs/web_socket_conformance_tests/lib/src/echo_server_vm.dart new file mode 100644 index 0000000000..a589cc0d1c --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/echo_server_vm.dart @@ -0,0 +1,12 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; + +import 'echo_server.dart'; + +/// Starts the redirect test HTTP server in the same process. +Future> startServer() async { + final controller = StreamChannelController(sync: true); + hybridMain(controller.foreign); + return controller.local; +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/echo_server_web.dart b/pkgs/web_socket_conformance_tests/lib/src/echo_server_web.dart new file mode 100644 index 0000000000..b553554f69 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/echo_server_web.dart @@ -0,0 +1,9 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; + +/// Starts the redirect test HTTP server out-of-process. +Future> startServer() async => spawnHybridUri(Uri( + scheme: 'package', + path: 'web_socket_conformance_tests/src/echo_server.dart')); diff --git a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart new file mode 100644 index 0000000000..f32c025d24 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart @@ -0,0 +1,33 @@ +// Copyright (c) 2023, 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:async'; +import 'dart:convert'; +import 'dart:io'; +import "package:crypto/crypto.dart"; +import 'package:stream_channel/stream_channel.dart'; + +const WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +/// Starts an WebSocket server that echos the payload of the request. +/// +/// Channel protocol: +/// On Startup: +/// - send port +/// On Request Received: +/// - echoes the request payload +/// When Receive Anything: +/// - exit +void hybridMain(StreamChannel channel) async { + final server = (await HttpServer.bind('localhost', 0)) + ..listen((request) async { + request.response.statusCode = 200; + request.response.close(); + }); + channel.sink.add(server.port); + + await channel + .stream.first; // Any writes indicates that the server should exit. + unawaited(server.close()); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server_vm.dart b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server_vm.dart new file mode 100644 index 0000000000..7f8cd5cf5a --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server_vm.dart @@ -0,0 +1,12 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; + +import 'no_upgrade_server.dart'; + +/// Starts the redirect test HTTP server in the same process. +Future> startServer() async { + final controller = StreamChannelController(sync: true); + hybridMain(controller.foreign); + return controller.local; +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server_web.dart b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server_web.dart new file mode 100644 index 0000000000..97409bc34a --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server_web.dart @@ -0,0 +1,9 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; + +/// Starts the redirect test HTTP server out-of-process. +Future> startServer() async => spawnHybridUri(Uri( + scheme: 'package', + path: 'web_socket_conformance_tests/src/no_upgrade_server.dart')); diff --git a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart new file mode 100644 index 0000000000..e3ff30da04 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart @@ -0,0 +1,37 @@ +// Copyright (c) 2023, 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:typed_data'; + +import 'package:async/async.dart'; +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; +import 'package:websocket/websocket.dart'; + +import 'no_upgrade_server_vm.dart' + if (dart.library.html) 'no_upgrade_server_web.dart'; + +/// Tests that the [WebSocketChannel] can correctly transmit and receive text +/// and binary payloads. +void testNoUpgrade( + Future Function(Uri uri, {Iterable? protocols}) + channelFactory) { + group('no upgrade', () { + late final Uri uri; + late final StreamChannel httpServerChannel; + late final StreamQueue httpServerQueue; + + setUpAll(() async { + httpServerChannel = await startServer(); + httpServerQueue = StreamQueue(httpServerChannel.stream); + uri = Uri.parse('ws://localhost:${await httpServerQueue.next}'); + }); + tearDownAll(() => httpServerChannel.sink.add(null)); + + test('close before upgrade', () async { + await expectLater( + () => channelFactory(uri), throwsA(isA())); + }); + }); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart new file mode 100644 index 0000000000..70245459ce --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart @@ -0,0 +1,207 @@ +// Copyright (c) 2023, 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:typed_data'; + +import 'package:async/async.dart'; +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; +import 'package:websocket/websocket.dart'; + +import 'echo_server_vm.dart' if (dart.library.html) 'echo_server_web.dart'; + +/// Tests that the [WebSocketChannel] can correctly transmit and receive text +/// and binary payloads. +void testPayloadTransfer( + Future Function(Uri uri, {Iterable? protocols}) + channelFactory) { + group('payload transfer', () { + late final Uri uri; + late final StreamChannel httpServerChannel; + late final StreamQueue httpServerQueue; + + setUpAll(() async { + httpServerChannel = await startServer(); + httpServerQueue = StreamQueue(httpServerChannel.stream); + uri = Uri.parse('ws://localhost:${await httpServerQueue.next}'); + }); + tearDownAll(() => httpServerChannel.sink.add(null)); + + test('close immediately', () async { + final channel = await channelFactory(uri); + + await channel.close(); + print('closed!'); + expect(await channel.events.isEmpty, + true); // Stream can't be listened to at this point. + }); + + test('empty string request and response', () async { + final channel = await channelFactory(uri); + + channel.sendText(''); + expect(await channel.events.first, TextDataReceived('')); + }); + + test('empty binary request and response', () async { + final channel = await channelFactory(uri); + + channel.sendBytes(Uint8List(0)); + expect(await channel.events.first, BinaryDataReceived(Uint8List(0))); + }); + + test('string request and response', () async { + final channel = await channelFactory(uri); + + channel.sendText("Hello World!"); + expect(await channel.events.first, TextDataReceived("Hello World!")); + }); + + test('binary request and response', () async { + final channel = await channelFactory(uri); + + channel.sendBytes(Uint8List.fromList([1, 2, 3, 4, 5])); + expect(await channel.events.first, + BinaryDataReceived(Uint8List.fromList([1, 2, 3, 4, 5]))); + }); + + test('large string request and response', () async { + final channel = await channelFactory(uri); + + channel.sendText("Hello World!" * 1000); + expect( + await channel.events.first, TextDataReceived("Hello World!" * 1000)); + }); + + test('large binary request and response - XXX', () async { + final channel = await channelFactory(uri); + + channel.sendBytes(Uint8List.fromList([1, 2, 3, 4, 5])); + expect(await channel.events.first, + BinaryDataReceived(Uint8List.fromList([1, 2, 3, 4, 5]))); + }); +/* + */ +/* + test('List request and response', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + + channel.sink.add([1, 2, 3, 4, 5]); + expect(await channel.stream.first, [1, 2, 3, 4, 5]); + }, skip: _isWeb); + + test('List with >255 value', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + + expect(() => channel.sink.add([1, 2, 256, 4, 5]), throwsArgumentError); + }, skip: _isWeb || _isVM); + + test('List with <0 value', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + + expect(() => channel.sink.add([1, 2, 256, 4, 5]), throwsArgumentError); + }, skip: _isWeb || _isVM); + + test('Uint8List request and response', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + + channel.sink.add(Uint8List.fromList([1, 2, 3, 4, 5])); + expect(await channel.stream.first, [1, 2, 3, 4, 5]); + }); + + test('duration request and response', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + expect(() => channel.sink.add(const Duration(seconds: 5)), + throwsArgumentError); + }, skip: _isWeb || _isVM); + + test('error added to sink', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + + expect(() => channel.sink.addError(Exception('what should this do?')), + throwsUnsupportedError); + await channel.sink.close(); + expect(channel.stream.isEmpty, true); + }, skip: _isWeb || _isVM); + + test('add after error', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + + expect(() => channel.sink.addError(Exception('what should this do?')), + throwsUnsupportedError); + + channel.sink.add('Hello World!'); + expect(await channel.stream.first, 'Hello World!'); + }, skip: _isWeb || _isVM); + + test('alternative string and binary request and response', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + + channel.sink.add('Hello '); + channel.sink.add([1, 2, 3]); + channel.sink.add('World!'); + channel.sink.add([4, 5]); + + expect(await channel.stream.take(4).toList(), [ + 'Hello ', + [1, 2, 3], + 'World!', + [4, 5] + ]); + }, skip: _isWeb); + + test('increasing payload string size', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + + final s = StringBuffer('Hello World\n'); + channel.sink.add(s.toString()); + await for (final response in channel.stream) { + expect(response, s.toString()); + if (s.length >= 10000) { + await channel.sink.close(); + break; + } + s.writeln('HelloWorld'); + channel.sink.add(s.toString()); + } + }); + + test('increasing payload binary size', () async { + final channel = channelFactory(uri); + + await expectLater(channel.ready, completes); + + final data = [1, 2, 3, 4, 5]; + channel.sink.add(data); + await for (final response in channel.stream) { + expect(response, data); + if (data.length >= 10000) { + await channel.sink.close(); + break; + } + data.addAll([1, 2, 3, 4, 5]); + channel.sink.add(data); + } + }); + */ + }); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart new file mode 100644 index 0000000000..0a41b357f6 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart @@ -0,0 +1,49 @@ +// Copyright (c) 2023, 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:async'; +import 'dart:convert'; +import 'dart:io'; +import "package:crypto/crypto.dart"; +import 'package:stream_channel/stream_channel.dart'; + +const WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +/// Starts an WebSocket server that echos the payload of the request. +/// +/// Channel protocol: +/// On Startup: +/// - send port +/// On Request Received: +/// - echoes the request payload +/// When Receive Anything: +/// - exit +void hybridMain(StreamChannel channel) async { + late final HttpServer server; + server = (await HttpServer.bind('localhost', 0)) + ..listen((request) async { + var key = request.headers.value('Sec-WebSocket-Key'); + var digest = sha1.convert("$key$WEB_SOCKET_GUID".codeUnits); + var accept = base64.encode(digest.bytes); + request.response + ..statusCode = HttpStatus.switchingProtocols + ..headers.add(HttpHeaders.connectionHeader, "Upgrade") + ..headers.add(HttpHeaders.upgradeHeader, "websocket") + ..headers.add("Sec-WebSocket-Accept", accept); + request.response.contentLength = 0; +// await request.response.close(); + final socket = await request.response.detachSocket(); +// socket.write('\r\n'); +// socket.write('\r\n'); +// final websocket = WebSocket.fromUpgradedSocket(socket, serverSide: true); +// websocket.listen((x) => print('server received: $x')); + socket.write('marry had a little lamb whose fleece was white as snow'); + }); + + channel.sink.add(server.port); + + await channel + .stream.first; // Any writes indicates that the server should exit. + unawaited(server.close()); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server_vm.dart b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server_vm.dart new file mode 100644 index 0000000000..4996e3b6c2 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server_vm.dart @@ -0,0 +1,12 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; + +import 'peer_protocol_errors_server.dart'; + +/// Starts the redirect test HTTP server in the same process. +Future> startServer() async { + final controller = StreamChannelController(sync: true); + hybridMain(controller.foreign); + return controller.local; +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server_web.dart b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server_web.dart new file mode 100644 index 0000000000..361b02c30f --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server_web.dart @@ -0,0 +1,9 @@ +// Generated by generate_server_wrappers.dart. Do not edit. + +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; + +/// Starts the redirect test HTTP server out-of-process. +Future> startServer() async => spawnHybridUri(Uri( + scheme: 'package', + path: 'web_socket_conformance_tests/src/peer_protocol_errors_server.dart')); diff --git a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart new file mode 100644 index 0000000000..fd6abd7e2a --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart @@ -0,0 +1,55 @@ +// Copyright (c) 2023, 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:typed_data'; + +import 'package:async/async.dart'; +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/test.dart'; +import 'package:websocket/websocket.dart'; + +import 'peer_protocol_errors_server_vm.dart' + if (dart.library.html) 'peer_protocol_errors_server_web.dart'; + +/// Tests that the [WebSocketChannel] can correctly transmit and receive text +/// and binary payloads. +void testPeerProtocolErrors( + Future Function(Uri uri, {Iterable? protocols}) + channelFactory) { + group('protocol errors', () { + late final Uri uri; + late final StreamChannel httpServerChannel; + late final StreamQueue httpServerQueue; + + setUpAll(() async { + httpServerChannel = await startServer(); + httpServerQueue = StreamQueue(httpServerChannel.stream); + uri = Uri.parse('ws://localhost:${await httpServerQueue.next}'); + }); + tearDownAll(() => httpServerChannel.sink.add(null)); + + test('bad data after upgrade', () async { + final channel = await channelFactory(uri); + expect( + (await channel.events.single as CloseReceived).code, + anyOf([ + 1002, // protocol error + 1005, // closed no status + 1006, // closed abnormal + ])); + }); + + test('bad data after upgrade with write', () async { + final channel = await channelFactory(uri); + channel.sendText('test'); + expect( + (await channel.events.single as CloseReceived).code, + anyOf([ + 1002, // protocol error + 1005, // closed no status + 1006, // closed abnormal + ])); + }); + }); +} diff --git a/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart b/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart new file mode 100644 index 0000000000..b51507ca73 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart @@ -0,0 +1,29 @@ +// Copyright (c) 2022, 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 'package:web_socket_conformance_tests/src/disconnect_after_upgrade_tests.dart'; +import 'package:websocket/websocket.dart'; + +// import 'src/failure_tests.dart'; +// import 'src/close_tests.dart'; +import 'src/close_local_tests.dart'; +import 'src/close_remote_tests.dart'; +import 'src/no_upgrade_tests.dart'; +import 'src/payload_transfer_tests.dart'; +import 'src/peer_protocol_errors_tests.dart'; + +// import 'src/protocol_tests.dart'; + +/// Runs the entire test suite against the given [WebSocketChannel]. +void testAll( + Future Function(Uri uri, {Iterable? protocols}) + webSocketFactory) { + testPayloadTransfer(webSocketFactory); + testLocalClose(webSocketFactory); + testRemoteClose(webSocketFactory); +// testProtocols(channelFactory); + testNoUpgrade(webSocketFactory); + testDisconnectAfterUpgrade(webSocketFactory); + testPeerProtocolErrors(webSocketFactory); +} diff --git a/pkgs/web_socket_conformance_tests/pubspec.yaml b/pkgs/web_socket_conformance_tests/pubspec.yaml new file mode 100644 index 0000000000..7c4cbca4b8 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/pubspec.yaml @@ -0,0 +1,19 @@ +name: web_socket_conformance_tests +description: A starting point for Dart libraries or applications. +version: 1.0.0 +# repository: https://github.com/my_org/my_repo +publish_to: none + +environment: + sdk: ^3.2.4 + +# Add regular dependencies here. +dependencies: + async: ^2.11.0 + dart_style: ^2.3.4 + stream_channel: ^2.1.2 + test: ^1.24.0 + websocket: + path: ../websocket +dev_dependencies: + lints: ^2.1.0 From b1a077a5685002b696fe189681ce5b557d5a8646 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Tue, 20 Feb 2024 14:09:00 -0800 Subject: [PATCH 02/11] Conformance test fixes --- .../.gitattributes | 2 + pkgs/web_socket_conformance_tests/.gitignore | 7 --- .../web_socket_conformance_tests/CHANGELOG.md | 3 -- pkgs/web_socket_conformance_tests/LICENSE | 27 ++++++++++ pkgs/web_socket_conformance_tests/README.md | 52 +++++++++---------- .../analysis_options.yaml | 30 ----------- .../bin/generate_server_wrappers.dart | 2 +- .../example/client_test.dart | 17 ++++++ .../lib/src/close_local_server.dart | 2 +- .../lib/src/close_local_tests.dart | 8 +-- .../lib/src/close_remote_server.dart | 2 +- .../lib/src/close_remote_tests.dart | 6 +-- .../src/disconnect_after_upgrade_server.dart | 2 +- .../src/disconnect_after_upgrade_tests.dart | 6 +-- .../lib/src/echo_server.dart | 2 +- .../lib/src/no_upgrade_server.dart | 2 +- .../lib/src/no_upgrade_tests.dart | 8 +-- .../lib/src/payload_transfer_tests.dart | 6 +-- .../lib/src/peer_protocol_errors_server.dart | 2 +- .../lib/src/peer_protocol_errors_tests.dart | 6 +-- .../lib/web_socket_conformance_tests.dart | 11 ++-- .../mono_pkg.yaml | 10 ++++ .../web_socket_conformance_tests/pubspec.yaml | 16 +++--- 23 files changed, 120 insertions(+), 109 deletions(-) create mode 100644 pkgs/web_socket_conformance_tests/.gitattributes delete mode 100644 pkgs/web_socket_conformance_tests/.gitignore delete mode 100644 pkgs/web_socket_conformance_tests/CHANGELOG.md create mode 100644 pkgs/web_socket_conformance_tests/LICENSE delete mode 100644 pkgs/web_socket_conformance_tests/analysis_options.yaml create mode 100644 pkgs/web_socket_conformance_tests/example/client_test.dart create mode 100644 pkgs/web_socket_conformance_tests/mono_pkg.yaml diff --git a/pkgs/web_socket_conformance_tests/.gitattributes b/pkgs/web_socket_conformance_tests/.gitattributes new file mode 100644 index 0000000000..104d0ecaf9 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/.gitattributes @@ -0,0 +1,2 @@ +lib/src/*_server_vm.dart linguist-generated=true +lib/src/*_server_web.dart linguist-generated=true diff --git a/pkgs/web_socket_conformance_tests/.gitignore b/pkgs/web_socket_conformance_tests/.gitignore deleted file mode 100644 index 3cceda5578..0000000000 --- a/pkgs/web_socket_conformance_tests/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# https://dart.dev/guides/libraries/private-files -# Created by `dart pub` -.dart_tool/ - -# Avoid committing pubspec.lock for library packages; see -# https://dart.dev/guides/libraries/private-files#pubspeclock. -pubspec.lock diff --git a/pkgs/web_socket_conformance_tests/CHANGELOG.md b/pkgs/web_socket_conformance_tests/CHANGELOG.md deleted file mode 100644 index effe43c82c..0000000000 --- a/pkgs/web_socket_conformance_tests/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 1.0.0 - -- Initial version. diff --git a/pkgs/web_socket_conformance_tests/LICENSE b/pkgs/web_socket_conformance_tests/LICENSE new file mode 100644 index 0000000000..e5b2b46dcf --- /dev/null +++ b/pkgs/web_socket_conformance_tests/LICENSE @@ -0,0 +1,27 @@ +Copyright 2024, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pkgs/web_socket_conformance_tests/README.md b/pkgs/web_socket_conformance_tests/README.md index 8b55e735b5..50b514cd32 100644 --- a/pkgs/web_socket_conformance_tests/README.md +++ b/pkgs/web_socket_conformance_tests/README.md @@ -1,39 +1,35 @@ - +This package is intended to be used in the tests of packages that implement +`package:web_socket` `Socket`. -TODO: Put a short description of the package here that helps potential users -know whether this package might be useful for them. +The tests work by starting a series of test servers and running the provided +`package:web_socket` `WebSocket` against them. -## Features - -TODO: List what your package can do. Maybe include images, gifs, or videos. +## Usage -## Getting started +`package:web_socket_conformance_tests` is meant to be used in the tests suite +of a `package:web_socket` `WebSocket` like: -TODO: List prerequisites and provide or point to information on how to -start using the package. +```dart +import 'package:web_socket/web_socket.dart'; +import 'package:test/test.dart'; -## Usage +import 'package:web_socket_conformance_tests/web_socket_conformance_tests.dart'; -TODO: Include short and useful examples for package users. Add longer examples -to `/example` folder. +class MyWebSocket implements WebSocket { + // Your implementation here. +} -```dart -const like = 'sample'; +void main() { + group('WebSocket conformance tests', () { + testAll(MyWebSocket()); + }); +} ``` -## Additional information - -TODO: Tell users more about the package: where to find more information, how to -contribute to the package, how to file issues, what response they can expect -from the package authors, and more. +**Note**: This package does not have it's own tests, instead it is +exercised by the tests in `package:web_socket`. diff --git a/pkgs/web_socket_conformance_tests/analysis_options.yaml b/pkgs/web_socket_conformance_tests/analysis_options.yaml deleted file mode 100644 index dee8927aaf..0000000000 --- a/pkgs/web_socket_conformance_tests/analysis_options.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# This file configures the static analysis results for your project (errors, -# warnings, and lints). -# -# This enables the 'recommended' set of lints from `package:lints`. -# This set helps identify many issues that may lead to problems when running -# or consuming Dart code, and enforces writing Dart using a single, idiomatic -# style and format. -# -# If you want a smaller set of lints you can change this to specify -# 'package:lints/core.yaml'. These are just the most critical lints -# (the recommended set includes the core lints). -# The core lints are also what is used by pub.dev for scoring packages. - -include: package:lints/recommended.yaml - -# Uncomment the following section to specify additional rules. - -# linter: -# rules: -# - camel_case_types - -# analyzer: -# exclude: -# - path/to/excluded/files/** - -# For more information about the core and recommended set of lints, see -# https://dart.dev/go/core-lints - -# For additional information about configuring this file, see -# https://dart.dev/guides/language/analysis-options diff --git a/pkgs/web_socket_conformance_tests/bin/generate_server_wrappers.dart b/pkgs/web_socket_conformance_tests/bin/generate_server_wrappers.dart index 7168868db2..c9787b6495 100644 --- a/pkgs/web_socket_conformance_tests/bin/generate_server_wrappers.dart +++ b/pkgs/web_socket_conformance_tests/bin/generate_server_wrappers.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. diff --git a/pkgs/web_socket_conformance_tests/example/client_test.dart b/pkgs/web_socket_conformance_tests/example/client_test.dart new file mode 100644 index 0000000000..0b4bbb3089 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/example/client_test.dart @@ -0,0 +1,17 @@ +import 'package:http/http.dart'; +import 'package:http_client_conformance_tests/http_client_conformance_tests.dart'; +import 'package:test/test.dart'; + +class MyHttpClient extends BaseClient { + @override + Future send(BaseRequest request) async { + // Your implementation here. + throw UnsupportedError('implement this method'); + } +} + +void main() { + group('client conformance tests', () { + testAll(MyHttpClient.new); + }); +} diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart b/pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart index 8ca6a79fcc..5c6096f191 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart index e9769ca47d..6cf48b04c0 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. @@ -7,7 +7,7 @@ import 'dart:typed_data'; import 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; -import 'package:websocket/websocket.dart'; +import 'package:web_socket/web_socket.dart'; import 'close_local_server_vm.dart' if (dart.library.html) 'close_server_web.dart'; @@ -15,7 +15,7 @@ import 'close_local_server_vm.dart' /// Tests that the [WebSocketChannel] can correctly transmit and receive text /// and binary payloads. void testLocalClose( - Future Function(Uri uri, {Iterable? protocols}) + Future Function(Uri uri, {Iterable? protocols}) channelFactory) { group('local close', () { late Uri uri; @@ -113,7 +113,7 @@ void testLocalClose( expectLater( () async => await channel.close(3001, 'Client initiated closure'), - throwsA(isA())); + throwsA(isA())); final closeCode = await httpServerQueue.next as int?; final closeReason = await httpServerQueue.next as String?; diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart b/pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart index ae66836d99..715606f358 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart index daade10793..1d93b4eefb 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. @@ -7,7 +7,7 @@ import 'dart:typed_data'; import 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; -import 'package:websocket/websocket.dart'; +import 'package:web_socket/web_socket.dart'; import 'close_remote_server_vm.dart' if (dart.library.html) 'close_remote_server_web.dart'; @@ -15,7 +15,7 @@ import 'close_remote_server_vm.dart' /// Tests that the [WebSocketChannel] can correctly transmit and receive text /// and binary payloads. void testRemoteClose( - Future Function(Uri uri, {Iterable? protocols}) + Future Function(Uri uri, {Iterable? protocols}) channelFactory) { group('remote close', () { late Uri uri; diff --git a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart index 1ab0e19265..ca0010dcb7 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. diff --git a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart index f114205bf6..d656917416 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart @@ -1,11 +1,11 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; -import 'package:websocket/websocket.dart'; +import 'package:web_socket/web_socket.dart'; import 'disconnect_after_upgrade_server_vm.dart' if (dart.library.html) 'disconnect_after_upgrade_server_web.dart'; @@ -13,7 +13,7 @@ import 'disconnect_after_upgrade_server_vm.dart' /// Tests that the [WebSocketChannel] can correctly transmit and receive text /// and binary payloads. void testDisconnectAfterUpgrade( - Future Function(Uri uri, {Iterable? protocols}) + Future Function(Uri uri, {Iterable? protocols}) channelFactory) { group('disconnect', () { late final Uri uri; diff --git a/pkgs/web_socket_conformance_tests/lib/src/echo_server.dart b/pkgs/web_socket_conformance_tests/lib/src/echo_server.dart index 1d7c14db1d..21bbd5b944 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/echo_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/echo_server.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. diff --git a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart index f32c025d24..3bb64a89ab 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. diff --git a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart index e3ff30da04..e17e0bc7ff 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. @@ -7,7 +7,7 @@ import 'dart:typed_data'; import 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; -import 'package:websocket/websocket.dart'; +import 'package:web_socket/web_socket.dart'; import 'no_upgrade_server_vm.dart' if (dart.library.html) 'no_upgrade_server_web.dart'; @@ -15,7 +15,7 @@ import 'no_upgrade_server_vm.dart' /// Tests that the [WebSocketChannel] can correctly transmit and receive text /// and binary payloads. void testNoUpgrade( - Future Function(Uri uri, {Iterable? protocols}) + Future Function(Uri uri, {Iterable? protocols}) channelFactory) { group('no upgrade', () { late final Uri uri; @@ -31,7 +31,7 @@ void testNoUpgrade( test('close before upgrade', () async { await expectLater( - () => channelFactory(uri), throwsA(isA())); + () => channelFactory(uri), throwsA(isA())); }); }); } diff --git a/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart index 70245459ce..1be0d15384 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. @@ -7,14 +7,14 @@ import 'dart:typed_data'; import 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; -import 'package:websocket/websocket.dart'; +import 'package:web_socket/web_socket.dart'; import 'echo_server_vm.dart' if (dart.library.html) 'echo_server_web.dart'; /// Tests that the [WebSocketChannel] can correctly transmit and receive text /// and binary payloads. void testPayloadTransfer( - Future Function(Uri uri, {Iterable? protocols}) + Future Function(Uri uri, {Iterable? protocols}) channelFactory) { group('payload transfer', () { late final Uri uri; diff --git a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart index 0a41b357f6..f0e375aa98 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. diff --git a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart index fd6abd7e2a..e43a98f79c 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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. @@ -7,7 +7,7 @@ import 'dart:typed_data'; import 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; -import 'package:websocket/websocket.dart'; +import 'package:web_socket/web_socket.dart'; import 'peer_protocol_errors_server_vm.dart' if (dart.library.html) 'peer_protocol_errors_server_web.dart'; @@ -15,7 +15,7 @@ import 'peer_protocol_errors_server_vm.dart' /// Tests that the [WebSocketChannel] can correctly transmit and receive text /// and binary payloads. void testPeerProtocolErrors( - Future Function(Uri uri, {Iterable? protocols}) + Future Function(Uri uri, {Iterable? protocols}) channelFactory) { group('protocol errors', () { late final Uri uri; diff --git a/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart b/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart index b51507ca73..11171bb0c6 100644 --- a/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart @@ -1,14 +1,11 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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 'package:web_socket_conformance_tests/src/disconnect_after_upgrade_tests.dart'; -import 'package:websocket/websocket.dart'; - -// import 'src/failure_tests.dart'; -// import 'src/close_tests.dart'; +import 'package:web_socket/web_socket.dart'; import 'src/close_local_tests.dart'; import 'src/close_remote_tests.dart'; +import 'src/disconnect_after_upgrade_tests.dart'; import 'src/no_upgrade_tests.dart'; import 'src/payload_transfer_tests.dart'; import 'src/peer_protocol_errors_tests.dart'; @@ -17,7 +14,7 @@ import 'src/peer_protocol_errors_tests.dart'; /// Runs the entire test suite against the given [WebSocketChannel]. void testAll( - Future Function(Uri uri, {Iterable? protocols}) + Future Function(Uri uri, {Iterable? protocols}) webSocketFactory) { testPayloadTransfer(webSocketFactory); testLocalClose(webSocketFactory); diff --git a/pkgs/web_socket_conformance_tests/mono_pkg.yaml b/pkgs/web_socket_conformance_tests/mono_pkg.yaml new file mode 100644 index 0000000000..16e4e7a5f3 --- /dev/null +++ b/pkgs/web_socket_conformance_tests/mono_pkg.yaml @@ -0,0 +1,10 @@ +sdk: +- pubspec +- dev + +stages: +- analyze_and_format: + - analyze: --fatal-infos + - format: + sdk: + - dev diff --git a/pkgs/web_socket_conformance_tests/pubspec.yaml b/pkgs/web_socket_conformance_tests/pubspec.yaml index 7c4cbca4b8..b92a9408ea 100644 --- a/pkgs/web_socket_conformance_tests/pubspec.yaml +++ b/pkgs/web_socket_conformance_tests/pubspec.yaml @@ -1,19 +1,21 @@ name: web_socket_conformance_tests -description: A starting point for Dart libraries or applications. -version: 1.0.0 -# repository: https://github.com/my_org/my_repo +description: >- + A library that tests whether implementations of `package:web_socket`'s + `WebSocket` class behave as expected. +repository: https://github.com/dart-lang/http/tree/master/pkgs/web_socket_conformance_tests + publish_to: none environment: sdk: ^3.2.4 -# Add regular dependencies here. dependencies: async: ^2.11.0 dart_style: ^2.3.4 stream_channel: ^2.1.2 test: ^1.24.0 - websocket: - path: ../websocket + web_socket: + path: ../web_socket + dev_dependencies: - lints: ^2.1.0 + dart_flutter_team_lints: ^2.0.0 From c30968769a2481a976af0a7669cfbdbf6e4f9872 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Wed, 21 Feb 2024 14:36:56 -0800 Subject: [PATCH 03/11] Lots of work --- .../example/web_socket_example.dart | 42 ++- pkgs/web_socket/lib/io_web_socket.dart | 1 + pkgs/web_socket/lib/src/io_web_socket.dart | 103 +++++++ pkgs/web_socket/pubspec.yaml | 5 +- .../test/io_web_socket_conformance_test.dart | 14 + .../example/client_test.dart | 16 +- .../lib/src/close_local_server.dart | 10 +- .../lib/src/close_local_tests.dart | 29 +- .../lib/src/close_remote_server.dart | 11 +- .../lib/src/close_remote_tests.dart | 273 +----------------- .../src/disconnect_after_upgrade_server.dart | 26 +- .../src/disconnect_after_upgrade_tests.dart | 4 +- .../lib/src/echo_server.dart | 8 - .../lib/src/no_upgrade_server.dart | 13 +- .../lib/src/no_upgrade_tests.dart | 6 +- .../lib/src/payload_transfer_tests.dart | 209 ++++---------- .../lib/src/peer_protocol_errors_server.dart | 28 +- .../lib/src/peer_protocol_errors_tests.dart | 5 +- .../lib/web_socket_conformance_tests.dart | 10 +- .../web_socket_conformance_tests/pubspec.yaml | 1 + 20 files changed, 269 insertions(+), 545 deletions(-) create mode 100644 pkgs/web_socket/lib/io_web_socket.dart create mode 100644 pkgs/web_socket/lib/src/io_web_socket.dart create mode 100644 pkgs/web_socket/test/io_web_socket_conformance_test.dart diff --git a/pkgs/web_socket/example/web_socket_example.dart b/pkgs/web_socket/example/web_socket_example.dart index 6cb625c543..5e23fce64a 100644 --- a/pkgs/web_socket/example/web_socket_example.dart +++ b/pkgs/web_socket/example/web_socket_example.dart @@ -1,3 +1,41 @@ -void main() { - // TODO: add an example. +import 'dart:convert'; +import 'dart:io'; + +import 'package:web_socket/io_web_socket.dart'; +import 'package:web_socket/src/web_socket.dart'; + +const requestId = 305; + +/// Prints the US dollar value of Bitcoins continuously. +void main() async { + // Whitebit public WebSocket API documentation: + // https://docs.whitebit.com/public/websocket/ + final socket = + await IOWebSocket.connect(Uri.parse('wss://api.whitebit.com/ws')); + + socket.events.listen((e) { + switch (e) { + case TextDataReceived(text: final text): + final json = jsonDecode(text) as Map; + if (json['id'] == requestId) { + if (json['error'] != null) { + stderr.writeln('Failure: ${json['error']}'); + socket.close(); + } + } else { + final params = (json['params'] as List).cast>(); + print('₿1 = USD\$${params[0][2]}'); + } + case BinaryDataReceived(): + stderr.writeln('Unexpected binary response from server'); + socket.close(); + case CloseReceived(): + stderr.writeln('Connection to server closed'); + } + }); + socket.sendText(jsonEncode({ + 'id': requestId, + 'method': 'candles_subscribe', + 'params': ['BTC_USD', 5] + })); } diff --git a/pkgs/web_socket/lib/io_web_socket.dart b/pkgs/web_socket/lib/io_web_socket.dart new file mode 100644 index 0000000000..eaea4f06dc --- /dev/null +++ b/pkgs/web_socket/lib/io_web_socket.dart @@ -0,0 +1 @@ +export 'src/io_web_socket.dart' show IOWebSocket; diff --git a/pkgs/web_socket/lib/src/io_web_socket.dart b/pkgs/web_socket/lib/src/io_web_socket.dart new file mode 100644 index 0000000000..10d67c7d86 --- /dev/null +++ b/pkgs/web_socket/lib/src/io_web_socket.dart @@ -0,0 +1,103 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io' as io; +import 'dart:typed_data'; + +import '../web_socket.dart'; + +class IOWebSocket implements WebSocket { + final io.WebSocket _webSocket; + final _events = StreamController(); + + static Future connect(Uri uri) async { + try { + final webSocket = await io.WebSocket.connect(uri.toString()); + return IOWebSocket._(webSocket); + } on io.WebSocketException catch (e) { + throw WebSocketException(e.message); + } + } + + IOWebSocket._(this._webSocket) { + _webSocket.listen( + (event) { + switch (event) { + case String e: + _events.add(TextDataReceived(e)); + case List e: + _events.add(BinaryDataReceived(Uint8List.fromList(e))); + } + }, + onError: (Object e, StackTrace st) { + final wse = switch (e) { + io.WebSocketException(message: final message) => + WebSocketException(message), + _ => WebSocketException(e.toString()), + }; + _events.addError(wse, st); + }, + onDone: () { + if (!_events.isClosed) { + _events + ..add(CloseReceived( + _webSocket.closeCode, _webSocket.closeReason ?? '')) + ..close(); + } + }, + ); + } + + @override + void sendBytes(Uint8List b) { + if (_events.isClosed) { + throw StateError('WebSocket is closed'); + } + _webSocket.add(b); + } + + @override + void sendText(String s) { + if (_events.isClosed) { + throw StateError('WebSocket is closed'); + } + _webSocket.add(s); + } + + /// Closes the stream. + /// https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.1 + /// Cannot send more data after this. + // If an endpoint receives a Close frame and did not previously send a + // Close frame, the endpoint MUST send a Close frame in response. (When + // sending a Close frame in response, the endpoint typically echos the + // status code it received.) It SHOULD do so as soon as practical. An + // endpoint MAY delay sending a Close frame until its current message is + // sent (for instance, if the majority of a fragmented message is + // already sent, an endpoint MAY send the remaining fragments before + // sending a Close frame). However, there is no guarantee that the + // endpoint that has already sent a Close frame will continue to process + // data. + @override + Future close([int? code, String? reason]) async { + if (_events.isClosed) { + throw StateError('WebSocket is closed'); + } + + if (code != null) { + RangeError.checkValueInInterval(code, 3000, 4999, 'code'); + } + if (reason != null && utf8.encode(reason).length > 123) { + throw ArgumentError.value(reason, 'reason', + 'reason must be <= 123 bytes long when encoded as UTF-8'); + } + + unawaited(_events.close()); + try { + await _webSocket.close(code, reason); + } on io.WebSocketException catch (e) { + throw WebSocketException(e.message); + } + } + + @override + Stream get events => _events.stream; +} diff --git a/pkgs/web_socket/pubspec.yaml b/pkgs/web_socket/pubspec.yaml index 237da791ea..d668f05dfc 100644 --- a/pkgs/web_socket/pubspec.yaml +++ b/pkgs/web_socket/pubspec.yaml @@ -1,11 +1,14 @@ name: web_socket description: "TODO: enter a descirption here" -publish_to: none repository: https://github.com/dart-lang/http/tree/master/pkgs/web_socket +publish_to: none + environment: sdk: ^3.2.6 dev_dependencies: dart_flutter_team_lints: ^2.0.0 test: ^1.24.0 + web_socket_conformance_tests: + path: ../web_socket_conformance_tests/ diff --git a/pkgs/web_socket/test/io_web_socket_conformance_test.dart b/pkgs/web_socket/test/io_web_socket_conformance_test.dart new file mode 100644 index 0000000000..9b3728ddd2 --- /dev/null +++ b/pkgs/web_socket/test/io_web_socket_conformance_test.dart @@ -0,0 +1,14 @@ +// Copyright (c) 2024, 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. + +@TestOn('vm') +library; + +import 'package:test/test.dart'; +import 'package:web_socket/io_web_socket.dart'; +import 'package:web_socket_conformance_tests/web_socket_conformance_tests.dart'; + +void main() { + testAll((uri, {protocols}) => IOWebSocket.connect(uri)); +} diff --git a/pkgs/web_socket_conformance_tests/example/client_test.dart b/pkgs/web_socket_conformance_tests/example/client_test.dart index 0b4bbb3089..16fe1b13d9 100644 --- a/pkgs/web_socket_conformance_tests/example/client_test.dart +++ b/pkgs/web_socket_conformance_tests/example/client_test.dart @@ -1,17 +1,17 @@ -import 'package:http/http.dart'; -import 'package:http_client_conformance_tests/http_client_conformance_tests.dart'; import 'package:test/test.dart'; +import 'package:web_socket/web_socket.dart'; +import 'package:web_socket_conformance_tests/web_socket_conformance_tests.dart'; -class MyHttpClient extends BaseClient { - @override - Future send(BaseRequest request) async { - // Your implementation here. - throw UnsupportedError('implement this method'); +class MyWebSocketImplementation implements WebSocket { + // Implement the `WebSocket` interface. + + static Future connect(Uri uri) { + return MyWebSocketImplementation(); } } void main() { group('client conformance tests', () { - testAll(MyHttpClient.new); + testAll(MyWebSocketImplementation.connect); }); } diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart b/pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart index 5c6096f191..8991de3cc8 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/close_local_server.dart @@ -7,15 +7,7 @@ import 'dart:io'; import 'package:stream_channel/stream_channel.dart'; -/// Starts an WebSocket server that echos the payload of the request. -/// -/// Channel protocol: -/// On Startup: -/// - send port -/// On Request Received: -/// - echoes the request payload -/// When Receive Anything: -/// - exit +/// Starts an WebSocket server that waits for the peer to send a Close frame. void hybridMain(StreamChannel channel) async { late HttpServer server; diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart index 6cf48b04c0..2fe27bb040 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart @@ -2,19 +2,16 @@ // 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:typed_data'; - import 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; import 'package:web_socket/web_socket.dart'; import 'close_local_server_vm.dart' - if (dart.library.html) 'close_server_web.dart'; + if (dart.library.html) 'close_local_server_web.dart'; -/// Tests that the [WebSocketChannel] can correctly transmit and receive text -/// and binary payloads. -void testLocalClose( +/// Tests that the [WebSocket] can correctly close the connection to the peer. +void testCloseLocal( Future Function(Uri uri, {Iterable? protocols}) channelFactory) { group('local close', () { @@ -29,25 +26,9 @@ void testLocalClose( }); tearDown(() async { httpServerChannel.sink.add(null); -// await httpServerQueue.next; - }); -/* - test('connected', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - expect(channel.closeCode, null); - expect(channel.closeReason, null); }); -*/ - // https://websockets.spec.whatwg.org/#eventdef-websocket-close - // Dart will wait up to 5 seconds to get the close code from the server otherwise - // it will use the local close code. test('reserved close code', () async { - // If code is present, but is neither an integer equal to 1000 nor an integer in the range 3000 to 4999, inclusive, throw an "InvalidAccessError" DOMException. - // If reasonBytes is longer than 123 bytes, then throw a "SyntaxError" DOMException. - final channel = await channelFactory(uri); await expectLater(() => channel.close(1004), throwsA(isA())); }); @@ -111,9 +92,9 @@ void testLocalClose( await channel.close(3000, 'Client initiated closure'); - expectLater( + await expectLater( () async => await channel.close(3001, 'Client initiated closure'), - throwsA(isA())); + throwsStateError); final closeCode = await httpServerQueue.next as int?; final closeReason = await httpServerQueue.next as String?; diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart b/pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart index 715606f358..9bbf84ed48 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart @@ -7,15 +7,8 @@ import 'dart:io'; import 'package:stream_channel/stream_channel.dart'; -/// Starts an WebSocket server that echos the payload of the request. -/// -/// Channel protocol: -/// On Startup: -/// - send port -/// On Request Received: -/// - echoes the request payload -/// When Receive Anything: -/// - exit +/// Starts an WebSocket server that sends a Close frame after receiving any +/// data. void hybridMain(StreamChannel channel) async { late HttpServer server; diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart index 1d93b4eefb..647f74e27c 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart @@ -2,8 +2,6 @@ // 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:typed_data'; - import 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; @@ -12,9 +10,8 @@ import 'package:web_socket/web_socket.dart'; import 'close_remote_server_vm.dart' if (dart.library.html) 'close_remote_server_web.dart'; -/// Tests that the [WebSocketChannel] can correctly transmit and receive text -/// and binary payloads. -void testRemoteClose( +/// Tests that the [WebSocket] can correctly receive Close frames from the peer. +void testCloseRemote( Future Function(Uri uri, {Iterable? protocols}) channelFactory) { group('remote close', () { @@ -29,47 +26,8 @@ void testRemoteClose( }); tearDown(() async { httpServerChannel.sink.add(null); -// await httpServerQueue.next; - }); -/* - test('connected', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - expect(channel.closeCode, null); - expect(channel.closeReason, null); - }); -*/ - // https://websockets.spec.whatwg.org/#eventdef-websocket-close - // Dart will wait up to 5 seconds to get the close code from the server otherwise - // it will use the local close code. - -/* - test('reserved close code', () async { - // If code is present, but is neither an integer equal to 1000 nor an integer in the range 3000 to 4999, inclusive, throw an "InvalidAccessError" DOMException. - // If reasonBytes is longer than 123 bytes, then throw a "SyntaxError" DOMException. - - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - expect(channel.closeCode, null); - expect(channel.closeReason, null); - // web uncaught // InvalidAccessError - // sync WebSocketException - await channel.sink.close(1004, 'boom'); }); - test('too long close reason', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - expect(channel.closeCode, null); - expect(channel.closeReason, null); - // web uncaught // SyntaxError - // vm: passes! - await channel.sink.close(1000, 'Boom'.padLeft(1000)); - }); -*/ test('with code and reason', () async { final channel = await channelFactory(uri); @@ -78,235 +36,22 @@ void testRemoteClose( [CloseReceived(4123, 'server closed the connection')]); }); - test('send after close', () async { + test('send after close received', () async { final channel = await channelFactory(uri); channel.sendText('Please close'); expect(await channel.events.toList(), [CloseReceived(4123, 'server closed the connection')]); expect(() => channel.sendText('test'), throwsStateError); -/* - final closeCode = await httpServerQueue.next as int?; - final closeReason = await httpServerQueue.next as String?; - expect(closeCode, 3000); - expect(closeReason, 'Client initiated closure');*/ - }); -/* - test('cancel', () async { - final channel = - channelFactory(uri.replace(queryParameters: {'sleep': '5'})); - - var sinkDoneComplete = false; - var sinkDoneOnError = false; - var streamOnData = false; - var streamOnDone = false; - var streamOnError = false; - - channel.sink.done.then((_) { - sinkDoneComplete = true; - }, onError: (_) { - sinkDoneOnError = true; - }); - - final streamSubscription = channel.stream.listen((_) { - streamOnData = true; - }, onError: (_) { - streamOnError = true; - }, onDone: () { - streamOnDone = true; - }); - - await expectLater(channel.ready, completes); - // VM: Cancels subscription to the socket, which means that this deadlocks. - await streamSubscription.cancel(); - expect(() => channel.stream.listen((_) {}), throwsStateError); - channel.sink.add('add after stream closed'); - - expect(channel.closeCode, null); - expect(channel.closeReason, null); - - expect(sinkDoneComplete, false); - expect(sinkDoneOnError, false); - expect(streamOnData, false); - expect(streamOnDone, false); - expect(streamOnError, false); - - await channel.sink.done; - expect(await httpServerQueue.next, 'add after stream closed'); - expect(await httpServerQueue.next, null); - expect(await httpServerQueue.next, null); - expect(channel.closeCode, 4123); - expect(channel.closeReason, 'server closed the connection'); - // cancelling should close according to lassa! - }, skip: _isVM); - - test('cancel - client close', () async { - final channel = - channelFactory(uri.replace(queryParameters: {'sleep': '5'})); - - var sinkDoneComplete = false; - var sinkDoneOnError = false; - var streamOnData = false; - var streamOnDone = false; - var streamOnError = false; - - channel.sink.done.then((_) { - sinkDoneComplete = true; - }, onError: (_) { - sinkDoneOnError = true; - }); - - final streamSubscription = channel.stream.listen((_) { - streamOnData = true; - }, onError: (_) { - streamOnError = true; - }, onDone: () { - streamOnDone = true; - }); - - await expectLater(channel.ready, completes); - await streamSubscription.cancel(); - expect(() => channel.stream.listen((_) {}), throwsStateError); - channel.sink.add('add after stream closed'); - - expect(channel.closeCode, null); - expect(channel.closeReason, null); - - expect(sinkDoneComplete, false); - expect(sinkDoneOnError, false); - expect(streamOnData, false); - expect(streamOnDone, false); - expect(streamOnError, false); - - await channel.sink.close(4444, 'client closed the connection'); - expect(await httpServerQueue.next, 'add after stream closed'); - expect(await httpServerQueue.next, 4444); - expect(await httpServerQueue.next, 'client closed the connection'); - expect(channel.closeCode, 4444); - expect(channel.closeReason, 'client closed the connection'); }); - test('client initiated', () async { - final channel = channelFactory(uri); - - var sinkDoneComplete = false; - var sinkDoneOnError = false; - var streamOnData = false; - var streamOnDone = false; - var streamOnError = false; - - channel.sink.done.then((_) { - sinkDoneComplete = true; - }, onError: (_) { - sinkDoneOnError = true; - }); - - channel.stream.listen((_) { - streamOnData = true; - }, onError: (_) { - streamOnError = true; - }, onDone: () { - streamOnDone = true; - }); - - await expectLater(channel.ready, completes); - await channel.sink.close(4444, 'client closed the connection'); - expect(channel.closeCode, null); // VM 4123 - expect(channel.closeReason, null); // VM 'server closed the connection' - - expect(await httpServerQueue.next, 4444); // VM 4123 - expect(await httpServerQueue.next, - 'client closed the connection'); // VM 'server closed the connection' - expect(channel.closeCode, 4123); - expect(channel.closeReason, 'server closed the connection'); - expect(() => channel.sink.add('add after connection closed'), - throwsStateError); - - expect(sinkDoneComplete, true); - expect(sinkDoneOnError, false); - expect(streamOnData, false); - expect(streamOnDone, true); - expect(streamOnError, false); - }, skip: _isVM); - - test('client initiated - slow server', () async { - final channel = - channelFactory(uri.replace(queryParameters: {'sleep': '5'})); - - var sinkDoneComplete = false; - var sinkDoneOnError = false; - var streamOnData = false; - var streamOnDone = false; - var streamOnError = false; - - channel.sink.done.then((_) { - sinkDoneComplete = true; - }, onError: (_) { - sinkDoneOnError = true; - }); - - channel.stream.listen((_) { - streamOnData = true; - }, onError: (_) { - streamOnError = true; - }, onDone: () { - streamOnDone = true; - }); - - await expectLater(channel.ready, completes); - await channel.sink.close(4444, 'client closed the connection'); - expect(channel.closeCode, null); - expect(channel.closeReason, null); - - expect(await httpServerQueue.next, 4444); - expect(await httpServerQueue.next, 'client closed the connection'); - expect(channel.closeCode, 4444); // VM: null - sometimes null - expect(channel.closeReason, 'client closed the connection'); // VM: null - expect(() => channel.sink.add('add after connection closed'), - throwsStateError); - await channel.sink.close(); - - expect(sinkDoneComplete, true); - expect(sinkDoneOnError, false); - expect(streamOnData, false); - expect(streamOnDone, true); - expect(streamOnError, false); - }); - - test('server initiated', () async { - final channel = channelFactory(uri); - - var sinkDoneComplete = false; - var sinkDoneOnError = false; - var streamOnData = false; - var streamOnDone = false; - var streamOnError = false; - - channel.sink.done.then((_) { - sinkDoneComplete = true; - }, onError: (_) { - sinkDoneOnError = true; - }); - - final streamListen = channel.stream.listen((_) { - streamOnData = true; - }, onError: (_) { - streamOnError = true; - }).asFuture(); - - await expectLater(channel.ready, completes); - await streamListen; - - expect(channel.closeCode, 4123); - expect(channel.closeReason, 'server closed the connection'); - channel.sink.add('add after connection closed'); - await channel.sink.close(); + test('close after close received', () async { + final channel = await channelFactory(uri); - expect(sinkDoneComplete, true); - expect(sinkDoneOnError, false); - expect(streamOnData, false); - expect(streamOnError, false); + channel.sendText('Please close'); + expect(await channel.events.toList(), + [CloseReceived(4123, 'server closed the connection')]); + await expectLater(channel.close, throwsStateError); }); - */ }); } diff --git a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart index ca0010dcb7..965521c439 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_server.dart @@ -5,39 +5,27 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import "package:crypto/crypto.dart"; +import 'package:crypto/crypto.dart'; import 'package:stream_channel/stream_channel.dart'; -const WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; +const _webSocketGuid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; -/// Starts an WebSocket server that echos the payload of the request. -/// -/// Channel protocol: -/// On Startup: -/// - send port -/// On Request Received: -/// - echoes the request payload -/// When Receive Anything: -/// - exit +/// Starts an WebSocket server that disconnects after WebSocket upgrade. void hybridMain(StreamChannel channel) async { late final HttpServer server; server = (await HttpServer.bind('localhost', 0)) ..listen((request) async { var key = request.headers.value('Sec-WebSocket-Key'); - var digest = sha1.convert("$key$WEB_SOCKET_GUID".codeUnits); + var digest = sha1.convert('$key$_webSocketGuid'.codeUnits); var accept = base64.encode(digest.bytes); request.response ..statusCode = HttpStatus.switchingProtocols - ..headers.add(HttpHeaders.connectionHeader, "Upgrade") - ..headers.add(HttpHeaders.upgradeHeader, "websocket") - ..headers.add("Sec-WebSocket-Accept", accept); + ..headers.add(HttpHeaders.connectionHeader, 'Upgrade') + ..headers.add(HttpHeaders.upgradeHeader, 'websocket') + ..headers.add('Sec-WebSocket-Accept', accept); request.response.contentLength = 0; -// await request.response.close(); final socket = await request.response.detachSocket(); -// final websocket = WebSocket.fromUpgradedSocket(socket, serverSide: true); -// websocket.listen((x) => print('server received: $x')); socket.destroy(); - print('socket is closed'); }); channel.sink.add(server.port); diff --git a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart index d656917416..16ccd68fa8 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart @@ -10,8 +10,8 @@ import 'package:web_socket/web_socket.dart'; import 'disconnect_after_upgrade_server_vm.dart' if (dart.library.html) 'disconnect_after_upgrade_server_web.dart'; -/// Tests that the [WebSocketChannel] can correctly transmit and receive text -/// and binary payloads. +/// Tests that the [WebSocket] generates a correct [CloseReceived] event if +/// the peer disconnects after WebSocket upgrade. void testDisconnectAfterUpgrade( Future Function(Uri uri, {Iterable? protocols}) channelFactory) { diff --git a/pkgs/web_socket_conformance_tests/lib/src/echo_server.dart b/pkgs/web_socket_conformance_tests/lib/src/echo_server.dart index 21bbd5b944..6728507a35 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/echo_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/echo_server.dart @@ -8,14 +8,6 @@ import 'dart:io'; import 'package:stream_channel/stream_channel.dart'; /// Starts an WebSocket server that echos the payload of the request. -/// -/// Channel protocol: -/// On Startup: -/// - send port -/// On Request Received: -/// - echoes the request payload -/// When Receive Anything: -/// - exit void hybridMain(StreamChannel channel) async { late HttpServer server; diff --git a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart index 3bb64a89ab..7e5d969d78 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart @@ -10,20 +10,13 @@ import 'package:stream_channel/stream_channel.dart'; const WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; -/// Starts an WebSocket server that echos the payload of the request. -/// -/// Channel protocol: -/// On Startup: -/// - send port -/// On Request Received: -/// - echoes the request payload -/// When Receive Anything: -/// - exit +/// Starts an WebSocket server that closes the HTTP connection before WebSocket +/// upgrade. void hybridMain(StreamChannel channel) async { final server = (await HttpServer.bind('localhost', 0)) ..listen((request) async { request.response.statusCode = 200; - request.response.close(); + await request.response.close(); }); channel.sink.add(server.port); diff --git a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart index e17e0bc7ff..c06955c70d 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_tests.dart @@ -2,8 +2,6 @@ // 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:typed_data'; - import 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; @@ -12,8 +10,8 @@ import 'package:web_socket/web_socket.dart'; import 'no_upgrade_server_vm.dart' if (dart.library.html) 'no_upgrade_server_web.dart'; -/// Tests that the [WebSocketChannel] can correctly transmit and receive text -/// and binary payloads. +/// Tests that the [WebSocket] generates the correct exception if the peer +/// closes the HTTP connection before WebSocket upgrade. void testNoUpgrade( Future Function(Uri uri, {Iterable? protocols}) channelFactory) { diff --git a/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart index 1be0d15384..6b04b91bb5 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart @@ -11,197 +11,92 @@ import 'package:web_socket/web_socket.dart'; import 'echo_server_vm.dart' if (dart.library.html) 'echo_server_web.dart'; -/// Tests that the [WebSocketChannel] can correctly transmit and receive text +/// Tests that the [WebSocket] can correctly transmit and receive text /// and binary payloads. void testPayloadTransfer( Future Function(Uri uri, {Iterable? protocols}) - channelFactory) { + webSocketFactory) { group('payload transfer', () { - late final Uri uri; - late final StreamChannel httpServerChannel; - late final StreamQueue httpServerQueue; + late Uri uri; + late StreamChannel httpServerChannel; + late StreamQueue httpServerQueue; + late WebSocket webSocket; - setUpAll(() async { + setUp(() async { httpServerChannel = await startServer(); httpServerQueue = StreamQueue(httpServerChannel.stream); uri = Uri.parse('ws://localhost:${await httpServerQueue.next}'); + webSocket = await webSocketFactory(uri); }); - tearDownAll(() => httpServerChannel.sink.add(null)); - - test('close immediately', () async { - final channel = await channelFactory(uri); - - await channel.close(); - print('closed!'); - expect(await channel.events.isEmpty, - true); // Stream can't be listened to at this point. + tearDown(() async { + httpServerChannel.sink.add(null); + await webSocket.close(); }); test('empty string request and response', () async { - final channel = await channelFactory(uri); - - channel.sendText(''); - expect(await channel.events.first, TextDataReceived('')); + webSocket.sendText(''); + expect(await webSocket.events.first, TextDataReceived('')); }); test('empty binary request and response', () async { - final channel = await channelFactory(uri); - - channel.sendBytes(Uint8List(0)); - expect(await channel.events.first, BinaryDataReceived(Uint8List(0))); + webSocket.sendBytes(Uint8List(0)); + expect(await webSocket.events.first, BinaryDataReceived(Uint8List(0))); }); test('string request and response', () async { - final channel = await channelFactory(uri); - - channel.sendText("Hello World!"); - expect(await channel.events.first, TextDataReceived("Hello World!")); + webSocket.sendText('Hello World!'); + expect(await webSocket.events.first, TextDataReceived('Hello World!')); }); test('binary request and response', () async { - final channel = await channelFactory(uri); - - channel.sendBytes(Uint8List.fromList([1, 2, 3, 4, 5])); - expect(await channel.events.first, + webSocket.sendBytes(Uint8List.fromList([1, 2, 3, 4, 5])); + expect(await webSocket.events.first, BinaryDataReceived(Uint8List.fromList([1, 2, 3, 4, 5]))); }); test('large string request and response', () async { - final channel = await channelFactory(uri); + final data = 'Hello World!' * 10000; - channel.sendText("Hello World!" * 1000); - expect( - await channel.events.first, TextDataReceived("Hello World!" * 1000)); + webSocket.sendText(data); + expect(await webSocket.events.first, TextDataReceived(data)); }); - test('large binary request and response - XXX', () async { - final channel = await channelFactory(uri); - - channel.sendBytes(Uint8List.fromList([1, 2, 3, 4, 5])); - expect(await channel.events.first, - BinaryDataReceived(Uint8List.fromList([1, 2, 3, 4, 5]))); + test('large binary request and response', () async { + final data = Uint8List(1000000); + data + ..fillRange(0, data.length ~/ 10, 1) + ..fillRange(0, data.length ~/ 10, 2) + ..fillRange(0, data.length ~/ 10, 3) + ..fillRange(0, data.length ~/ 10, 4) + ..fillRange(0, data.length ~/ 10, 5) + ..fillRange(0, data.length ~/ 10, 6) + ..fillRange(0, data.length ~/ 10, 7) + ..fillRange(0, data.length ~/ 10, 8) + ..fillRange(0, data.length ~/ 10, 9) + ..fillRange(0, data.length ~/ 10, 10); + + webSocket.sendBytes(data); + expect(await webSocket.events.first, BinaryDataReceived(data)); }); -/* - */ -/* - test('List request and response', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - - channel.sink.add([1, 2, 3, 4, 5]); - expect(await channel.stream.first, [1, 2, 3, 4, 5]); - }, skip: _isWeb); - - test('List with >255 value', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - - expect(() => channel.sink.add([1, 2, 256, 4, 5]), throwsArgumentError); - }, skip: _isWeb || _isVM); - - test('List with <0 value', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - - expect(() => channel.sink.add([1, 2, 256, 4, 5]), throwsArgumentError); - }, skip: _isWeb || _isVM); - test('Uint8List request and response', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - - channel.sink.add(Uint8List.fromList([1, 2, 3, 4, 5])); - expect(await channel.stream.first, [1, 2, 3, 4, 5]); + test('non-ascii string request and response', () async { + webSocket.sendText('🎨⛳🌈'); + expect(await webSocket.events.first, TextDataReceived('🎨⛳🌈')); }); - test('duration request and response', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - expect(() => channel.sink.add(const Duration(seconds: 5)), - throwsArgumentError); - }, skip: _isWeb || _isVM); - - test('error added to sink', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - - expect(() => channel.sink.addError(Exception('what should this do?')), - throwsUnsupportedError); - await channel.sink.close(); - expect(channel.stream.isEmpty, true); - }, skip: _isWeb || _isVM); - - test('add after error', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - - expect(() => channel.sink.addError(Exception('what should this do?')), - throwsUnsupportedError); - - channel.sink.add('Hello World!'); - expect(await channel.stream.first, 'Hello World!'); - }, skip: _isWeb || _isVM); - test('alternative string and binary request and response', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - - channel.sink.add('Hello '); - channel.sink.add([1, 2, 3]); - channel.sink.add('World!'); - channel.sink.add([4, 5]); - - expect(await channel.stream.take(4).toList(), [ - 'Hello ', - [1, 2, 3], - 'World!', - [4, 5] + webSocket + ..sendBytes(Uint8List.fromList([1])) + ..sendText('Hello!') + ..sendBytes(Uint8List.fromList([1, 2])) + ..sendText('Hello World!'); + + expect(await webSocket.events.take(4).toList(), [ + BinaryDataReceived(Uint8List.fromList([1])), + TextDataReceived('Hello!'), + BinaryDataReceived(Uint8List.fromList([1, 2])), + TextDataReceived('Hello World!') ]); - }, skip: _isWeb); - - test('increasing payload string size', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - - final s = StringBuffer('Hello World\n'); - channel.sink.add(s.toString()); - await for (final response in channel.stream) { - expect(response, s.toString()); - if (s.length >= 10000) { - await channel.sink.close(); - break; - } - s.writeln('HelloWorld'); - channel.sink.add(s.toString()); - } - }); - - test('increasing payload binary size', () async { - final channel = channelFactory(uri); - - await expectLater(channel.ready, completes); - - final data = [1, 2, 3, 4, 5]; - channel.sink.add(data); - await for (final response in channel.stream) { - expect(response, data); - if (data.length >= 10000) { - await channel.sink.close(); - break; - } - data.addAll([1, 2, 3, 4, 5]); - channel.sink.add(data); - } }); - */ }); } diff --git a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart index f0e375aa98..8760bb9a38 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_server.dart @@ -5,39 +5,27 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import "package:crypto/crypto.dart"; +import 'package:crypto/crypto.dart'; import 'package:stream_channel/stream_channel.dart'; -const WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; +const _webSocketGuid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; -/// Starts an WebSocket server that echos the payload of the request. -/// -/// Channel protocol: -/// On Startup: -/// - send port -/// On Request Received: -/// - echoes the request payload -/// When Receive Anything: -/// - exit +/// Starts an WebSocket server that sends invalid frames after completing the +/// WebSocket upgrade. void hybridMain(StreamChannel channel) async { late final HttpServer server; server = (await HttpServer.bind('localhost', 0)) ..listen((request) async { var key = request.headers.value('Sec-WebSocket-Key'); - var digest = sha1.convert("$key$WEB_SOCKET_GUID".codeUnits); + var digest = sha1.convert('$key$_webSocketGuid'.codeUnits); var accept = base64.encode(digest.bytes); request.response ..statusCode = HttpStatus.switchingProtocols - ..headers.add(HttpHeaders.connectionHeader, "Upgrade") - ..headers.add(HttpHeaders.upgradeHeader, "websocket") - ..headers.add("Sec-WebSocket-Accept", accept); + ..headers.add(HttpHeaders.connectionHeader, 'Upgrade') + ..headers.add(HttpHeaders.upgradeHeader, 'websocket') + ..headers.add('Sec-WebSocket-Accept', accept); request.response.contentLength = 0; -// await request.response.close(); final socket = await request.response.detachSocket(); -// socket.write('\r\n'); -// socket.write('\r\n'); -// final websocket = WebSocket.fromUpgradedSocket(socket, serverSide: true); -// websocket.listen((x) => print('server received: $x')); socket.write('marry had a little lamb whose fleece was white as snow'); }); diff --git a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart index e43a98f79c..1f955cd172 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart @@ -12,12 +12,11 @@ import 'package:web_socket/web_socket.dart'; import 'peer_protocol_errors_server_vm.dart' if (dart.library.html) 'peer_protocol_errors_server_web.dart'; -/// Tests that the [WebSocketChannel] can correctly transmit and receive text -/// and binary payloads. +/// Tests that the [WebSocket] can correctly handle incorrect WebSocket frames. void testPeerProtocolErrors( Future Function(Uri uri, {Iterable? protocols}) channelFactory) { - group('protocol errors', () { + group('peer protocol errors', () { late final Uri uri; late final StreamChannel httpServerChannel; late final StreamQueue httpServerQueue; diff --git a/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart b/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart index 11171bb0c6..721498ef3e 100644 --- a/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart @@ -16,11 +16,11 @@ import 'src/peer_protocol_errors_tests.dart'; void testAll( Future Function(Uri uri, {Iterable? protocols}) webSocketFactory) { - testPayloadTransfer(webSocketFactory); - testLocalClose(webSocketFactory); - testRemoteClose(webSocketFactory); -// testProtocols(channelFactory); - testNoUpgrade(webSocketFactory); + testCloseLocal(webSocketFactory); + testCloseRemote(webSocketFactory); testDisconnectAfterUpgrade(webSocketFactory); + testNoUpgrade(webSocketFactory); + testPayloadTransfer(webSocketFactory); testPeerProtocolErrors(webSocketFactory); +// testProtocols(channelFactory); } diff --git a/pkgs/web_socket_conformance_tests/pubspec.yaml b/pkgs/web_socket_conformance_tests/pubspec.yaml index b92a9408ea..0b4b6a19a7 100644 --- a/pkgs/web_socket_conformance_tests/pubspec.yaml +++ b/pkgs/web_socket_conformance_tests/pubspec.yaml @@ -11,6 +11,7 @@ environment: dependencies: async: ^2.11.0 + crypto: ^3.0.3 dart_style: ^2.3.4 stream_channel: ^2.1.2 test: ^1.24.0 From 97f1b3b5d69d4a28a4fad1a2cf4d0a35c39ab7b5 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Wed, 21 Feb 2024 15:52:15 -0800 Subject: [PATCH 04/11] Fix lints --- .../lib/src/no_upgrade_server.dart | 4 ---- .../lib/src/peer_protocol_errors_tests.dart | 2 -- 2 files changed, 6 deletions(-) diff --git a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart index 7e5d969d78..dec194186f 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/no_upgrade_server.dart @@ -3,13 +3,9 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; -import 'dart:convert'; import 'dart:io'; -import "package:crypto/crypto.dart"; import 'package:stream_channel/stream_channel.dart'; -const WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - /// Starts an WebSocket server that closes the HTTP connection before WebSocket /// upgrade. void hybridMain(StreamChannel channel) async { diff --git a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart index 1f955cd172..ba44f5122c 100644 --- a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart @@ -2,8 +2,6 @@ // 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:typed_data'; - import 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; From b601ea1d60fbc1a0c20b0931485ba6308c837bdb Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Wed, 21 Feb 2024 16:52:06 -0800 Subject: [PATCH 05/11] Update --- pkgs/web_socket/README.md | 48 ++++++++++++++++++- pkgs/web_socket/lib/src/io_web_socket.dart | 14 +----- pkgs/web_socket/lib/src/web_socket.dart | 23 ++++++++- pkgs/web_socket/lib/web_socket.dart | 3 -- .../example/client_test.dart | 20 ++++++-- .../lib/web_socket_conformance_tests.dart | 5 +- 6 files changed, 86 insertions(+), 27 deletions(-) diff --git a/pkgs/web_socket/README.md b/pkgs/web_socket/README.md index e43843ebc9..cb558de66d 100644 --- a/pkgs/web_socket/README.md +++ b/pkgs/web_socket/README.md @@ -1,2 +1,46 @@ -TODO: Put a short description of the package here that helps potential users -know whether this package might be useful for them. +[![pub package](https://img.shields.io/pub/v/web_socket.svg)](https://pub.dev/packages/web_socket) +[![package publisher](https://img.shields.io/pub/publisher/web_socket.svg)](https://pub.dev/packages/web_socket/publisher) + +Any easy-to-use library for communicating with WebSockets that has multiple +implementations. + +## Using + +```dart +import 'package:web_socket/io_web_socket.dart'; +import 'package:web_socket/src/web_socket.dart'; + +void main() async { + final socket = + await IOWebSocket.connect(Uri.parse('wss://ws.postman-echo.com/raw')); + + socket.events.listen((e) async { + switch (e) { + case TextDataReceived(text: final text): + print('Received Text: $text'); + await socket.close(); + case BinaryDataReceived(data: final data): + print('Received Binary: $data'); + case CloseReceived(code: final code, reason: final reason): + print('Connection to server closed: $code [$reason]'); + } + }); + + socket.sendText('Hello Dart WebSockets! 🎉'); +} +``` + +## Status: experimental + +**NOTE**: This package is currently experimental and published under the +[labs.dart.dev](https://dart.dev/dart-team-packages) pub publisher in order to +solicit feedback. + +For packages in the labs.dart.dev publisher we generally plan to either graduate +the package into a supported publisher (dart.dev, tools.dart.dev) after a period +of feedback and iteration, or discontinue the package. These packages have a +much higher expected rate of API and breaking changes. + +Your feedback is valuable and will help us evolve this package. For general +feedback, suggestions, and comments, please file an issue in the +[bug tracker](https://github.com/dart-lang/http/issues). diff --git a/pkgs/web_socket/lib/src/io_web_socket.dart b/pkgs/web_socket/lib/src/io_web_socket.dart index 10d67c7d86..4141aaff4a 100644 --- a/pkgs/web_socket/lib/src/io_web_socket.dart +++ b/pkgs/web_socket/lib/src/io_web_socket.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import '../web_socket.dart'; +/// A `dart-io`-based [WebSocket] implementation. class IOWebSocket implements WebSocket { final io.WebSocket _webSocket; final _events = StreamController(); @@ -63,19 +64,6 @@ class IOWebSocket implements WebSocket { _webSocket.add(s); } - /// Closes the stream. - /// https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.1 - /// Cannot send more data after this. - // If an endpoint receives a Close frame and did not previously send a - // Close frame, the endpoint MUST send a Close frame in response. (When - // sending a Close frame in response, the endpoint typically echos the - // status code it received.) It SHOULD do so as soon as practical. An - // endpoint MAY delay sending a Close frame until its current message is - // sent (for instance, if the majority of a fragmented message is - // already sent, an endpoint MAY send the remaining fragments before - // sending a Close frame). However, there is no guarantee that the - // endpoint that has already sent a Close frame will continue to process - // data. @override Future close([int? code, String? reason]) async { if (_events.isClosed) { diff --git a/pkgs/web_socket/lib/src/web_socket.dart b/pkgs/web_socket/lib/src/web_socket.dart index ffc0a3844c..4109c37960 100644 --- a/pkgs/web_socket/lib/src/web_socket.dart +++ b/pkgs/web_socket/lib/src/web_socket.dart @@ -85,7 +85,28 @@ class WebSocketConnectionClosed extends WebSocketException { /// The interface for WebSocket connections. /// -/// TODO: insert a usage example. +/// ```dart +/// import 'package:web_socket/io_web_socket.dart'; +/// import 'package:web_socket/src/web_socket.dart'; +/// +/// void main() async { +/// final socket = +/// await IOWebSocket.connect(Uri.parse('wss://ws.postman-echo.com/raw')); +/// +/// socket.events.listen((e) async { +/// switch (e) { +/// case TextDataReceived(text: final text): +/// print('Received Text: $text'); +/// await socket.close(); +/// case BinaryDataReceived(data: final data): +/// print('Received Binary: $data'); +/// case CloseReceived(code: final code, reason: final reason): +/// print('Connection to server closed: $code [$reason]'); +/// } +/// }); +/// +/// socket.sendText('Hello Dart WebSockets! 🎉'); +/// } abstract interface class WebSocket { /// Sends text data to the connected peer. /// diff --git a/pkgs/web_socket/lib/web_socket.dart b/pkgs/web_socket/lib/web_socket.dart index b901ebc76a..b08a48fd61 100644 --- a/pkgs/web_socket/lib/web_socket.dart +++ b/pkgs/web_socket/lib/web_socket.dart @@ -1,4 +1 @@ -/// TODO: write this doc string. -library; - export 'src/web_socket.dart'; diff --git a/pkgs/web_socket_conformance_tests/example/client_test.dart b/pkgs/web_socket_conformance_tests/example/client_test.dart index 16fe1b13d9..ec3d01c17a 100644 --- a/pkgs/web_socket_conformance_tests/example/client_test.dart +++ b/pkgs/web_socket_conformance_tests/example/client_test.dart @@ -1,13 +1,25 @@ +import 'dart:typed_data'; + import 'package:test/test.dart'; import 'package:web_socket/web_socket.dart'; import 'package:web_socket_conformance_tests/web_socket_conformance_tests.dart'; class MyWebSocketImplementation implements WebSocket { - // Implement the `WebSocket` interface. + static Future connect(Uri uri, + {Iterable? protocols}) async => + MyWebSocketImplementation(); + + @override + Future close([int? code, String? reason]) => throw UnimplementedError(); + + @override + Stream get events => throw UnimplementedError(); + + @override + void sendBytes(Uint8List b) => throw UnimplementedError(); - static Future connect(Uri uri) { - return MyWebSocketImplementation(); - } + @override + void sendText(String s) => throw UnimplementedError(); } void main() { diff --git a/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart b/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart index 721498ef3e..248fc3870a 100644 --- a/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart +++ b/pkgs/web_socket_conformance_tests/lib/web_socket_conformance_tests.dart @@ -10,9 +10,7 @@ import 'src/no_upgrade_tests.dart'; import 'src/payload_transfer_tests.dart'; import 'src/peer_protocol_errors_tests.dart'; -// import 'src/protocol_tests.dart'; - -/// Runs the entire test suite against the given [WebSocketChannel]. +/// Runs the entire test suite against the given [WebSocket]. void testAll( Future Function(Uri uri, {Iterable? protocols}) webSocketFactory) { @@ -22,5 +20,4 @@ void testAll( testNoUpgrade(webSocketFactory); testPayloadTransfer(webSocketFactory); testPeerProtocolErrors(webSocketFactory); -// testProtocols(channelFactory); } From 79f51bf0c664b7c775e48b74681b43e4de0635d2 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Wed, 21 Feb 2024 16:56:28 -0800 Subject: [PATCH 06/11] Update dart.yml --- .github/workflows/dart.yml | 106 +++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 23 deletions(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 22c1e5205c..fe44425cb7 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -100,6 +100,36 @@ jobs: if: "always() && steps.pkgs_http_client_conformance_tests_pub_upgrade.conclusion == 'success'" working-directory: pkgs/http_client_conformance_tests job_004: + name: "analyze_and_format; linux; Dart 3.2.4; PKG: pkgs/web_socket_conformance_tests; `dart analyze --fatal-infos`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.2.4;packages:pkgs/web_socket_conformance_tests;commands:analyze_1" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.4;packages:pkgs/web_socket_conformance_tests + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.4 + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 + with: + sdk: "3.2.4" + - id: checkout + name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - id: pkgs_web_socket_conformance_tests_pub_upgrade + name: pkgs/web_socket_conformance_tests; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + - name: "pkgs/web_socket_conformance_tests; dart analyze --fatal-infos" + run: dart analyze --fatal-infos + if: "always() && steps.pkgs_web_socket_conformance_tests_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + job_005: name: "analyze_and_format; linux; Dart 3.2.6; PKG: pkgs/web_socket; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: @@ -129,7 +159,7 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_web_socket_pub_upgrade.conclusion == 'success'" working-directory: pkgs/web_socket - job_005: + job_006: name: "analyze_and_format; linux; Dart 3.3.0; PKG: pkgs/http; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: @@ -159,17 +189,17 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_http_pub_upgrade.conclusion == 'success'" working-directory: pkgs/http - job_006: - name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/http, pkgs/http_client_conformance_tests, pkgs/http_profile, pkgs/web_socket; `dart analyze --fatal-infos`" + job_007: + name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/http, pkgs/http_client_conformance_tests, pkgs/http_profile, pkgs/web_socket, pkgs/web_socket_conformance_tests; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket;commands:analyze_1" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket-pkgs/web_socket_conformance_tests;commands:analyze_1" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket + os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket-pkgs/web_socket_conformance_tests os:ubuntu-latest;pub-cache-hosted;sdk:dev os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -216,17 +246,26 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_web_socket_pub_upgrade.conclusion == 'success'" working-directory: pkgs/web_socket - job_007: - name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/http, pkgs/http_client_conformance_tests, pkgs/http_profile, pkgs/web_socket; `dart format --output=none --set-exit-if-changed .`" + - id: pkgs_web_socket_conformance_tests_pub_upgrade + name: pkgs/web_socket_conformance_tests; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + - name: "pkgs/web_socket_conformance_tests; dart analyze --fatal-infos" + run: dart analyze --fatal-infos + if: "always() && steps.pkgs_web_socket_conformance_tests_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + job_008: + name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/http, pkgs/http_client_conformance_tests, pkgs/http_profile, pkgs/web_socket, pkgs/web_socket_conformance_tests; `dart format --output=none --set-exit-if-changed .`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket;commands:format" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket-pkgs/web_socket_conformance_tests;commands:format" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket + os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket-pkgs/web_socket_conformance_tests os:ubuntu-latest;pub-cache-hosted;sdk:dev os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -273,7 +312,16 @@ jobs: run: "dart format --output=none --set-exit-if-changed ." if: "always() && steps.pkgs_web_socket_pub_upgrade.conclusion == 'success'" working-directory: pkgs/web_socket - job_008: + - id: pkgs_web_socket_conformance_tests_pub_upgrade + name: pkgs/web_socket_conformance_tests; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + - name: "pkgs/web_socket_conformance_tests; dart format --output=none --set-exit-if-changed ." + run: "dart format --output=none --set-exit-if-changed ." + if: "always() && steps.pkgs_web_socket_conformance_tests_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + job_009: name: "analyze_and_format; linux; Flutter stable; PKG: pkgs/flutter_http_example; `dart format --output=none --set-exit-if-changed .`" runs-on: ubuntu-latest steps: @@ -303,7 +351,7 @@ jobs: run: "dart format --output=none --set-exit-if-changed ." if: "always() && steps.pkgs_flutter_http_example_pub_upgrade.conclusion == 'success'" working-directory: pkgs/flutter_http_example - job_009: + job_010: name: "analyze_and_format; linux; Flutter stable; PKG: pkgs/flutter_http_example; `flutter analyze --fatal-infos`" runs-on: ubuntu-latest steps: @@ -333,7 +381,7 @@ jobs: run: flutter analyze --fatal-infos if: "always() && steps.pkgs_flutter_http_example_pub_upgrade.conclusion == 'success'" working-directory: pkgs/flutter_http_example - job_010: + job_011: name: "unit_test; linux; Dart 3.0.0; PKG: pkgs/http_profile; `dart test --platform vm`" runs-on: ubuntu-latest steps: @@ -373,7 +421,8 @@ jobs: - job_007 - job_008 - job_009 - job_011: + - job_010 + job_012: name: "unit_test; linux; Dart 3.3.0; PKG: pkgs/http; `dart run --define=no_default_http_client=true test/no_default_http_client_test.dart`" runs-on: ubuntu-latest steps: @@ -413,7 +462,8 @@ jobs: - job_007 - job_008 - job_009 - job_012: + - job_010 + job_013: name: "unit_test; linux; Dart 3.3.0; PKG: pkgs/http; `dart test --platform chrome`" runs-on: ubuntu-latest steps: @@ -453,7 +503,8 @@ jobs: - job_007 - job_008 - job_009 - job_013: + - job_010 + job_014: name: "unit_test; linux; Dart 3.3.0; PKG: pkgs/http; `dart test --platform vm`" runs-on: ubuntu-latest steps: @@ -493,7 +544,8 @@ jobs: - job_007 - job_008 - job_009 - job_014: + - job_010 + job_015: name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart run --define=no_default_http_client=true test/no_default_http_client_test.dart`" runs-on: ubuntu-latest steps: @@ -533,7 +585,8 @@ jobs: - job_007 - job_008 - job_009 - job_015: + - job_010 + job_016: name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart test --platform chrome`" runs-on: ubuntu-latest steps: @@ -573,7 +626,8 @@ jobs: - job_007 - job_008 - job_009 - job_016: + - job_010 + job_017: name: "unit_test; linux; Dart dev; PKGS: pkgs/http, pkgs/http_profile; `dart test --platform vm`" runs-on: ubuntu-latest steps: @@ -622,7 +676,8 @@ jobs: - job_007 - job_008 - job_009 - job_017: + - job_010 + job_018: name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart test --test-randomize-ordering-seed=random -p chrome -c dart2wasm`" runs-on: ubuntu-latest steps: @@ -662,7 +717,8 @@ jobs: - job_007 - job_008 - job_009 - job_018: + - job_010 + job_019: name: "unit_test; linux; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test --platform chrome`" runs-on: ubuntu-latest steps: @@ -702,7 +758,8 @@ jobs: - job_007 - job_008 - job_009 - job_019: + - job_010 + job_020: name: "unit_test; linux; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: ubuntu-latest steps: @@ -742,7 +799,8 @@ jobs: - job_007 - job_008 - job_009 - job_020: + - job_010 + job_021: name: "unit_test; macos; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: macos-latest steps: @@ -782,7 +840,8 @@ jobs: - job_007 - job_008 - job_009 - job_021: + - job_010 + job_022: name: "unit_test; windows; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: windows-latest steps: @@ -812,3 +871,4 @@ jobs: - job_007 - job_008 - job_009 + - job_010 From 4d8660eb6b7bb530575f3d5bc386286584d7ad63 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Wed, 21 Feb 2024 17:00:04 -0800 Subject: [PATCH 07/11] Update dart.yml --- .github/workflows/dart.yml | 110 ++++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 25 deletions(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 2194f389d2..9629f3baa9 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -70,6 +70,36 @@ jobs: if: "always() && steps.pkgs_http_client_conformance_tests_pub_upgrade.conclusion == 'success'" working-directory: pkgs/http_client_conformance_tests job_003: + name: "analyze_and_format; linux; Dart 3.2.4; PKG: pkgs/web_socket_conformance_tests; `dart analyze --fatal-infos`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.2.4;packages:pkgs/web_socket_conformance_tests;commands:analyze_1" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.4;packages:pkgs/web_socket_conformance_tests + os:ubuntu-latest;pub-cache-hosted;sdk:3.2.4 + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 + with: + sdk: "3.2.4" + - id: checkout + name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - id: pkgs_web_socket_conformance_tests_pub_upgrade + name: pkgs/web_socket_conformance_tests; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + - name: "pkgs/web_socket_conformance_tests; dart analyze --fatal-infos" + run: dart analyze --fatal-infos + if: "always() && steps.pkgs_web_socket_conformance_tests_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + job_004: name: "analyze_and_format; linux; Dart 3.2.6; PKG: pkgs/web_socket; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: @@ -99,7 +129,7 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_web_socket_pub_upgrade.conclusion == 'success'" working-directory: pkgs/web_socket - job_004: + job_005: name: "analyze_and_format; linux; Dart 3.3.0; PKG: pkgs/http; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: @@ -129,7 +159,7 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_http_pub_upgrade.conclusion == 'success'" working-directory: pkgs/http - job_005: + job_006: name: "analyze_and_format; linux; Dart 3.4.0-154.0.dev; PKG: pkgs/http_profile; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: @@ -159,17 +189,17 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_http_profile_pub_upgrade.conclusion == 'success'" working-directory: pkgs/http_profile - job_006: - name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/http, pkgs/http_client_conformance_tests, pkgs/http_profile, pkgs/web_socket; `dart analyze --fatal-infos`" + job_007: + name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/http, pkgs/http_client_conformance_tests, pkgs/http_profile, pkgs/web_socket, pkgs/web_socket_conformance_tests; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket;commands:analyze_1" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket-pkgs/web_socket_conformance_tests;commands:analyze_1" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket + os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket-pkgs/web_socket_conformance_tests os:ubuntu-latest;pub-cache-hosted;sdk:dev os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -216,17 +246,26 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_web_socket_pub_upgrade.conclusion == 'success'" working-directory: pkgs/web_socket - job_007: - name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/http, pkgs/http_client_conformance_tests, pkgs/http_profile, pkgs/web_socket; `dart format --output=none --set-exit-if-changed .`" + - id: pkgs_web_socket_conformance_tests_pub_upgrade + name: pkgs/web_socket_conformance_tests; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + - name: "pkgs/web_socket_conformance_tests; dart analyze --fatal-infos" + run: dart analyze --fatal-infos + if: "always() && steps.pkgs_web_socket_conformance_tests_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + job_008: + name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/http, pkgs/http_client_conformance_tests, pkgs/http_profile, pkgs/web_socket, pkgs/web_socket_conformance_tests; `dart format --output=none --set-exit-if-changed .`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket;commands:format" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket-pkgs/web_socket_conformance_tests;commands:format" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket + os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/http-pkgs/http_client_conformance_tests-pkgs/http_profile-pkgs/web_socket-pkgs/web_socket_conformance_tests os:ubuntu-latest;pub-cache-hosted;sdk:dev os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -273,7 +312,16 @@ jobs: run: "dart format --output=none --set-exit-if-changed ." if: "always() && steps.pkgs_web_socket_pub_upgrade.conclusion == 'success'" working-directory: pkgs/web_socket - job_008: + - id: pkgs_web_socket_conformance_tests_pub_upgrade + name: pkgs/web_socket_conformance_tests; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + - name: "pkgs/web_socket_conformance_tests; dart format --output=none --set-exit-if-changed ." + run: "dart format --output=none --set-exit-if-changed ." + if: "always() && steps.pkgs_web_socket_conformance_tests_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + job_009: name: "analyze_and_format; linux; Flutter stable; PKG: pkgs/flutter_http_example; `dart format --output=none --set-exit-if-changed .`" runs-on: ubuntu-latest steps: @@ -303,7 +351,7 @@ jobs: run: "dart format --output=none --set-exit-if-changed ." if: "always() && steps.pkgs_flutter_http_example_pub_upgrade.conclusion == 'success'" working-directory: pkgs/flutter_http_example - job_009: + job_010: name: "analyze_and_format; linux; Flutter stable; PKG: pkgs/flutter_http_example; `flutter analyze --fatal-infos`" runs-on: ubuntu-latest steps: @@ -333,7 +381,7 @@ jobs: run: flutter analyze --fatal-infos if: "always() && steps.pkgs_flutter_http_example_pub_upgrade.conclusion == 'success'" working-directory: pkgs/flutter_http_example - job_010: + job_011: name: "unit_test; linux; Dart 3.3.0; PKG: pkgs/http; `dart run --define=no_default_http_client=true test/no_default_http_client_test.dart`" runs-on: ubuntu-latest steps: @@ -373,7 +421,8 @@ jobs: - job_007 - job_008 - job_009 - job_011: + - job_010 + job_012: name: "unit_test; linux; Dart 3.3.0; PKG: pkgs/http; `dart test --platform chrome`" runs-on: ubuntu-latest steps: @@ -413,7 +462,8 @@ jobs: - job_007 - job_008 - job_009 - job_012: + - job_010 + job_013: name: "unit_test; linux; Dart 3.3.0; PKG: pkgs/http; `dart test --platform vm`" runs-on: ubuntu-latest steps: @@ -453,7 +503,8 @@ jobs: - job_007 - job_008 - job_009 - job_013: + - job_010 + job_014: name: "unit_test; linux; Dart 3.4.0-154.0.dev; PKG: pkgs/http_profile; `dart test --platform vm`" runs-on: ubuntu-latest steps: @@ -493,7 +544,8 @@ jobs: - job_007 - job_008 - job_009 - job_014: + - job_010 + job_015: name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart run --define=no_default_http_client=true test/no_default_http_client_test.dart`" runs-on: ubuntu-latest steps: @@ -533,7 +585,8 @@ jobs: - job_007 - job_008 - job_009 - job_015: + - job_010 + job_016: name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart test --platform chrome`" runs-on: ubuntu-latest steps: @@ -573,7 +626,8 @@ jobs: - job_007 - job_008 - job_009 - job_016: + - job_010 + job_017: name: "unit_test; linux; Dart dev; PKGS: pkgs/http, pkgs/http_profile; `dart test --platform vm`" runs-on: ubuntu-latest steps: @@ -622,7 +676,8 @@ jobs: - job_007 - job_008 - job_009 - job_017: + - job_010 + job_018: name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart test --test-randomize-ordering-seed=random -p chrome -c dart2wasm`" runs-on: ubuntu-latest steps: @@ -662,7 +717,8 @@ jobs: - job_007 - job_008 - job_009 - job_018: + - job_010 + job_019: name: "unit_test; linux; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test --platform chrome`" runs-on: ubuntu-latest steps: @@ -702,7 +758,8 @@ jobs: - job_007 - job_008 - job_009 - job_019: + - job_010 + job_020: name: "unit_test; linux; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: ubuntu-latest steps: @@ -742,7 +799,8 @@ jobs: - job_007 - job_008 - job_009 - job_020: + - job_010 + job_021: name: "unit_test; macos; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: macos-latest steps: @@ -782,7 +840,8 @@ jobs: - job_007 - job_008 - job_009 - job_021: + - job_010 + job_022: name: "unit_test; windows; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: windows-latest steps: @@ -811,4 +870,5 @@ jobs: - job_006 - job_007 - job_008 - - job_009 \ No newline at end of file + - job_009 + - job_010 From fd7997086233a4f81cd64391dd4878a077df3228 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Wed, 21 Feb 2024 17:02:43 -0800 Subject: [PATCH 08/11] SDK version --- pkgs/web_socket/pubspec.yaml | 2 +- pkgs/web_socket_conformance_tests/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/web_socket/pubspec.yaml b/pkgs/web_socket/pubspec.yaml index d668f05dfc..6ebe7bbed5 100644 --- a/pkgs/web_socket/pubspec.yaml +++ b/pkgs/web_socket/pubspec.yaml @@ -5,7 +5,7 @@ repository: https://github.com/dart-lang/http/tree/master/pkgs/web_socket publish_to: none environment: - sdk: ^3.2.6 + sdk: ^3.3.0 dev_dependencies: dart_flutter_team_lints: ^2.0.0 diff --git a/pkgs/web_socket_conformance_tests/pubspec.yaml b/pkgs/web_socket_conformance_tests/pubspec.yaml index 0b4b6a19a7..afb952f804 100644 --- a/pkgs/web_socket_conformance_tests/pubspec.yaml +++ b/pkgs/web_socket_conformance_tests/pubspec.yaml @@ -7,7 +7,7 @@ repository: https://github.com/dart-lang/http/tree/master/pkgs/web_socket_confor publish_to: none environment: - sdk: ^3.2.4 + sdk: ^3.3.0 dependencies: async: ^2.11.0 From b40557d9dd28a80b3063a75a3fba69951d650b90 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Wed, 21 Feb 2024 17:03:52 -0800 Subject: [PATCH 09/11] Update --- .github/workflows/dart.yml | 134 ++++++++++--------------------------- 1 file changed, 34 insertions(+), 100 deletions(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 9629f3baa9..0f53da75a6 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -70,56 +70,35 @@ jobs: if: "always() && steps.pkgs_http_client_conformance_tests_pub_upgrade.conclusion == 'success'" working-directory: pkgs/http_client_conformance_tests job_003: - name: "analyze_and_format; linux; Dart 3.2.4; PKG: pkgs/web_socket_conformance_tests; `dart analyze --fatal-infos`" + name: "analyze_and_format; linux; Dart 3.3.0; PKGS: pkgs/http, pkgs/web_socket, pkgs/web_socket_conformance_tests; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.2.4;packages:pkgs/web_socket_conformance_tests;commands:analyze_1" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0;packages:pkgs/http-pkgs/web_socket-pkgs/web_socket_conformance_tests;commands:analyze_1" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:3.2.4;packages:pkgs/web_socket_conformance_tests - os:ubuntu-latest;pub-cache-hosted;sdk:3.2.4 + os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0;packages:pkgs/http-pkgs/web_socket-pkgs/web_socket_conformance_tests + os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0 os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest - name: Setup Dart SDK uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: - sdk: "3.2.4" + sdk: "3.3.0" - id: checkout name: Checkout repository uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - id: pkgs_web_socket_conformance_tests_pub_upgrade - name: pkgs/web_socket_conformance_tests; dart pub upgrade + - id: pkgs_http_pub_upgrade + name: pkgs/http; dart pub upgrade run: dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" - working-directory: pkgs/web_socket_conformance_tests - - name: "pkgs/web_socket_conformance_tests; dart analyze --fatal-infos" + working-directory: pkgs/http + - name: "pkgs/http; dart analyze --fatal-infos" run: dart analyze --fatal-infos - if: "always() && steps.pkgs_web_socket_conformance_tests_pub_upgrade.conclusion == 'success'" - working-directory: pkgs/web_socket_conformance_tests - job_004: - name: "analyze_and_format; linux; Dart 3.2.6; PKG: pkgs/web_socket; `dart analyze --fatal-infos`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.2.6;packages:pkgs/web_socket;commands:analyze_1" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:3.2.6;packages:pkgs/web_socket - os:ubuntu-latest;pub-cache-hosted;sdk:3.2.6 - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - name: Setup Dart SDK - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 - with: - sdk: "3.2.6" - - id: checkout - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + if: "always() && steps.pkgs_http_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/http - id: pkgs_web_socket_pub_upgrade name: pkgs/web_socket; dart pub upgrade run: dart pub upgrade @@ -129,37 +108,16 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_web_socket_pub_upgrade.conclusion == 'success'" working-directory: pkgs/web_socket - job_005: - name: "analyze_and_format; linux; Dart 3.3.0; PKG: pkgs/http; `dart analyze --fatal-infos`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0;packages:pkgs/http;commands:analyze_1" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0;packages:pkgs/http - os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0 - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - name: Setup Dart SDK - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 - with: - sdk: "3.3.0" - - id: checkout - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - id: pkgs_http_pub_upgrade - name: pkgs/http; dart pub upgrade + - id: pkgs_web_socket_conformance_tests_pub_upgrade + name: pkgs/web_socket_conformance_tests; dart pub upgrade run: dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" - working-directory: pkgs/http - - name: "pkgs/http; dart analyze --fatal-infos" + working-directory: pkgs/web_socket_conformance_tests + - name: "pkgs/web_socket_conformance_tests; dart analyze --fatal-infos" run: dart analyze --fatal-infos - if: "always() && steps.pkgs_http_pub_upgrade.conclusion == 'success'" - working-directory: pkgs/http - job_006: + if: "always() && steps.pkgs_web_socket_conformance_tests_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/web_socket_conformance_tests + job_004: name: "analyze_and_format; linux; Dart 3.4.0-154.0.dev; PKG: pkgs/http_profile; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: @@ -189,7 +147,7 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_http_profile_pub_upgrade.conclusion == 'success'" working-directory: pkgs/http_profile - job_007: + job_005: name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/http, pkgs/http_client_conformance_tests, pkgs/http_profile, pkgs/web_socket, pkgs/web_socket_conformance_tests; `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: @@ -255,7 +213,7 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_web_socket_conformance_tests_pub_upgrade.conclusion == 'success'" working-directory: pkgs/web_socket_conformance_tests - job_008: + job_006: name: "analyze_and_format; linux; Dart dev; PKGS: pkgs/http, pkgs/http_client_conformance_tests, pkgs/http_profile, pkgs/web_socket, pkgs/web_socket_conformance_tests; `dart format --output=none --set-exit-if-changed .`" runs-on: ubuntu-latest steps: @@ -321,7 +279,7 @@ jobs: run: "dart format --output=none --set-exit-if-changed ." if: "always() && steps.pkgs_web_socket_conformance_tests_pub_upgrade.conclusion == 'success'" working-directory: pkgs/web_socket_conformance_tests - job_009: + job_007: name: "analyze_and_format; linux; Flutter stable; PKG: pkgs/flutter_http_example; `dart format --output=none --set-exit-if-changed .`" runs-on: ubuntu-latest steps: @@ -351,7 +309,7 @@ jobs: run: "dart format --output=none --set-exit-if-changed ." if: "always() && steps.pkgs_flutter_http_example_pub_upgrade.conclusion == 'success'" working-directory: pkgs/flutter_http_example - job_010: + job_008: name: "analyze_and_format; linux; Flutter stable; PKG: pkgs/flutter_http_example; `flutter analyze --fatal-infos`" runs-on: ubuntu-latest steps: @@ -381,7 +339,7 @@ jobs: run: flutter analyze --fatal-infos if: "always() && steps.pkgs_flutter_http_example_pub_upgrade.conclusion == 'success'" working-directory: pkgs/flutter_http_example - job_011: + job_009: name: "unit_test; linux; Dart 3.3.0; PKG: pkgs/http; `dart run --define=no_default_http_client=true test/no_default_http_client_test.dart`" runs-on: ubuntu-latest steps: @@ -420,9 +378,7 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 - job_012: + job_010: name: "unit_test; linux; Dart 3.3.0; PKG: pkgs/http; `dart test --platform chrome`" runs-on: ubuntu-latest steps: @@ -461,9 +417,7 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 - job_013: + job_011: name: "unit_test; linux; Dart 3.3.0; PKG: pkgs/http; `dart test --platform vm`" runs-on: ubuntu-latest steps: @@ -502,9 +456,7 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 - job_014: + job_012: name: "unit_test; linux; Dart 3.4.0-154.0.dev; PKG: pkgs/http_profile; `dart test --platform vm`" runs-on: ubuntu-latest steps: @@ -543,9 +495,7 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 - job_015: + job_013: name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart run --define=no_default_http_client=true test/no_default_http_client_test.dart`" runs-on: ubuntu-latest steps: @@ -584,9 +534,7 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 - job_016: + job_014: name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart test --platform chrome`" runs-on: ubuntu-latest steps: @@ -625,9 +573,7 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 - job_017: + job_015: name: "unit_test; linux; Dart dev; PKGS: pkgs/http, pkgs/http_profile; `dart test --platform vm`" runs-on: ubuntu-latest steps: @@ -675,9 +621,7 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 - job_018: + job_016: name: "unit_test; linux; Dart dev; PKG: pkgs/http; `dart test --test-randomize-ordering-seed=random -p chrome -c dart2wasm`" runs-on: ubuntu-latest steps: @@ -716,9 +660,7 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 - job_019: + job_017: name: "unit_test; linux; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test --platform chrome`" runs-on: ubuntu-latest steps: @@ -757,9 +699,7 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 - job_020: + job_018: name: "unit_test; linux; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: ubuntu-latest steps: @@ -798,9 +738,7 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 - job_021: + job_019: name: "unit_test; macos; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: macos-latest steps: @@ -839,9 +777,7 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 - job_022: + job_020: name: "unit_test; windows; Flutter stable; PKG: pkgs/flutter_http_example; `flutter test`" runs-on: windows-latest steps: @@ -870,5 +806,3 @@ jobs: - job_006 - job_007 - job_008 - - job_009 - - job_010 From df6707f83c6e6547385193c0eb15a7a4a93f0956 Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Wed, 28 Feb 2024 16:28:04 -0800 Subject: [PATCH 10/11] Update pkgs/web_socket/README.md Co-authored-by: Nate Bosch --- pkgs/web_socket/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/web_socket/README.md b/pkgs/web_socket/README.md index cb558de66d..3e9c2d0146 100644 --- a/pkgs/web_socket/README.md +++ b/pkgs/web_socket/README.md @@ -8,7 +8,7 @@ implementations. ```dart import 'package:web_socket/io_web_socket.dart'; -import 'package:web_socket/src/web_socket.dart'; +import 'package:web_socket/web_socket.dart'; void main() async { final socket = From b0e264f226291c94d6d432efb6eacf7b5aec636a Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Wed, 28 Feb 2024 16:28:09 -0800 Subject: [PATCH 11/11] Update pkgs/web_socket/example/web_socket_example.dart Co-authored-by: Nate Bosch --- pkgs/web_socket/example/web_socket_example.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/web_socket/example/web_socket_example.dart b/pkgs/web_socket/example/web_socket_example.dart index 5e23fce64a..27ab4569c1 100644 --- a/pkgs/web_socket/example/web_socket_example.dart +++ b/pkgs/web_socket/example/web_socket_example.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:web_socket/io_web_socket.dart'; -import 'package:web_socket/src/web_socket.dart'; +import 'package:web_socket/web_socket.dart'; const requestId = 305;