diff --git a/pkgs/cupertino_http/CHANGELOG.md b/pkgs/cupertino_http/CHANGELOG.md index 16aa082e57..1a254d5317 100644 --- a/pkgs/cupertino_http/CHANGELOG.md +++ b/pkgs/cupertino_http/CHANGELOG.md @@ -1,6 +1,9 @@ ## 1.4.1-wip * Upgrade to `package:ffigen` 11.0.0. +* Bring `WebSocket` behavior in line with the documentation by throwing + `WebSocketConnectionClosed` rather `StateError` when attempting to send + data to or close an already closed `CupertinoWebSocket`. * Update minimum supported iOS/macOS versions to be in sync with the minimum (best effort) supported for Flutter: iOS 12, macOS 10.14 diff --git a/pkgs/cupertino_http/lib/src/cupertino_web_socket.dart b/pkgs/cupertino_http/lib/src/cupertino_web_socket.dart index c3d50ef508..564c0ba1b1 100644 --- a/pkgs/cupertino_http/lib/src/cupertino_web_socket.dart +++ b/pkgs/cupertino_http/lib/src/cupertino_web_socket.dart @@ -106,6 +106,8 @@ class CupertinoWebSocket implements WebSocket { /// Handle an incoming message from the peer and schedule receiving the next /// message. void _handleMessage(URLSessionWebSocketMessage value) { + if (_events.isClosed) return; + late WebSocketEvent event; switch (value.type) { case URLSessionWebSocketMessageType.urlSessionWebSocketMessageTypeString: @@ -160,7 +162,7 @@ class CupertinoWebSocket implements WebSocket { @override void sendBytes(Uint8List b) { if (_events.isClosed) { - throw StateError('WebSocket is closed'); + throw WebSocketConnectionClosed(); } _task .sendMessage(URLSessionWebSocketMessage.fromData(Data.fromList(b))) @@ -170,7 +172,7 @@ class CupertinoWebSocket implements WebSocket { @override void sendText(String s) { if (_events.isClosed) { - throw StateError('WebSocket is closed'); + throw WebSocketConnectionClosed(); } _task .sendMessage(URLSessionWebSocketMessage.fromString(s)) @@ -180,7 +182,7 @@ class CupertinoWebSocket implements WebSocket { @override Future close([int? code, String? reason]) async { if (_events.isClosed) { - throw StateError('WebSocket is closed'); + throw WebSocketConnectionClosed(); } if (code != null) { diff --git a/pkgs/web_socket/CHANGELOG.md b/pkgs/web_socket/CHANGELOG.md index 55b26d3b7d..2f2a07b25d 100644 --- a/pkgs/web_socket/CHANGELOG.md +++ b/pkgs/web_socket/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.1.3 + +- Bring the behavior in line with the documentation by throwing + `WebSocketConnectionClosed` rather `StateError` when attempting to send + data to or close an already closed `WebSocket`. + ## 0.1.2 - Fix a `StateError` in `IOWebSocket` when data is received from the peer diff --git a/pkgs/web_socket/lib/src/browser_web_socket.dart b/pkgs/web_socket/lib/src/browser_web_socket.dart index 80135fdc3e..fc66628b7a 100644 --- a/pkgs/web_socket/lib/src/browser_web_socket.dart +++ b/pkgs/web_socket/lib/src/browser_web_socket.dart @@ -104,7 +104,7 @@ class BrowserWebSocket implements WebSocket { @override void sendBytes(Uint8List b) { if (_events.isClosed) { - throw StateError('WebSocket is closed'); + throw WebSocketConnectionClosed(); } // Silently discards the data if the connection is closed. _webSocket.send(b.jsify()!); @@ -113,7 +113,7 @@ class BrowserWebSocket implements WebSocket { @override void sendText(String s) { if (_events.isClosed) { - throw StateError('WebSocket is closed'); + throw WebSocketConnectionClosed(); } // Silently discards the data if the connection is closed. _webSocket.send(s.jsify()!); @@ -122,7 +122,7 @@ class BrowserWebSocket implements WebSocket { @override Future close([int? code, String? reason]) async { if (_events.isClosed) { - throw StateError('WebSocket is closed'); + throw WebSocketConnectionClosed(); } checkCloseCode(code); diff --git a/pkgs/web_socket/lib/src/io_web_socket.dart b/pkgs/web_socket/lib/src/io_web_socket.dart index 8b82218bcf..7ea084bec3 100644 --- a/pkgs/web_socket/lib/src/io_web_socket.dart +++ b/pkgs/web_socket/lib/src/io_web_socket.dart @@ -86,7 +86,7 @@ class IOWebSocket implements WebSocket { @override void sendBytes(Uint8List b) { if (_events.isClosed) { - throw StateError('WebSocket is closed'); + throw WebSocketConnectionClosed(); } _webSocket.add(b); } @@ -94,7 +94,7 @@ class IOWebSocket implements WebSocket { @override void sendText(String s) { if (_events.isClosed) { - throw StateError('WebSocket is closed'); + throw WebSocketConnectionClosed(); } _webSocket.add(s); } @@ -102,7 +102,7 @@ class IOWebSocket implements WebSocket { @override Future close([int? code, String? reason]) async { if (_events.isClosed) { - throw StateError('WebSocket is closed'); + throw WebSocketConnectionClosed(); } checkCloseCode(code); diff --git a/pkgs/web_socket/pubspec.yaml b/pkgs/web_socket/pubspec.yaml index f468dcb379..f9d95a75ff 100644 --- a/pkgs/web_socket/pubspec.yaml +++ b/pkgs/web_socket/pubspec.yaml @@ -3,7 +3,7 @@ description: >- Any easy-to-use library for communicating with WebSockets that has multiple implementations. repository: https://github.com/dart-lang/http/tree/master/pkgs/web_socket -version: 0.1.2 +version: 0.1.3 environment: sdk: ^3.3.0 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 cb496ed776..5cf8e739f8 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,6 +2,8 @@ // 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'; @@ -119,13 +121,25 @@ void testCloseLocal( await expectLater( () async => await channel.close(3001, 'Client initiated closure'), - throwsStateError); - final closeCode = await httpServerQueue.next as int?; - final closeReason = await httpServerQueue.next as String?; + throwsA(isA())); + }); - expect(closeCode, 3000); - expect(closeReason, 'Client initiated closure'); - expect(await channel.events.isEmpty, true); + test('sendBytes after close', () async { + final channel = await channelFactory(uri); + + await channel.close(3000, 'Client initiated closure'); + + expect(() => channel.sendBytes(Uint8List(10)), + throwsA(isA())); + }); + + test('sendText after close', () async { + final channel = await channelFactory(uri); + + await channel.close(3000, 'Client initiated closure'); + + expect(() => channel.sendText('Hello World'), + throwsA(isA())); }); }); } 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 647f74e27c..b7b3e5956f 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,6 +2,8 @@ // 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'; @@ -36,13 +38,24 @@ void testCloseRemote( [CloseReceived(4123, 'server closed the connection')]); }); - test('send after close received', () async { + test('sendBytes after close received', () async { + final channel = await channelFactory(uri); + + channel.sendBytes(Uint8List(10)); + expect(await channel.events.toList(), + [CloseReceived(4123, 'server closed the connection')]); + expect(() => channel.sendText('test'), + throwsA(isA())); + }); + + test('sendText 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); + expect(() => channel.sendText('test'), + throwsA(isA())); }); test('close after close received', () async { @@ -51,7 +64,8 @@ void testCloseRemote( channel.sendText('Please close'); expect(await channel.events.toList(), [CloseReceived(4123, 'server closed the connection')]); - await expectLater(channel.close, throwsStateError); + await expectLater( + channel.close, throwsA(isA())); }); }); }