Skip to content
2 changes: 2 additions & 0 deletions pkgs/cupertino_http/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## 3.0.0-wip

* Remove `shouldUseExtendedBackgroundIdleMode` from `URLSessionConfiguration`.
* Fix a bug where close reasons not containing valid UTF-8 would cause an
uncatchable exception to be thrown.
* Exclude unnecessary generated code. Slightly reduces disk space requirements.

## 2.4.0
Expand Down
9 changes: 7 additions & 2 deletions pkgs/cupertino_http/lib/src/cupertino_web_socket.dart
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,13 @@ class CupertinoWebSocket implements WebSocket {

void _connectionClosed(int? closeCode, objc.NSData? reason) {
if (!_events.isClosed) {
final closeReason = reason == null ? '' : utf8.decode(reason.toList());

String closeReason;
try {
closeReason = reason == null ? '' : utf8.decode(reason.toList());
} on FormatException {
closeCode = 1006;
closeReason = '';
}
_events
..add(CloseReceived(closeCode, closeReason))
..close();
Expand Down
13 changes: 10 additions & 3 deletions pkgs/web_socket/test/fake_web_socket_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,16 @@ void proxy(WebSocket from, WebSocket to) {
case BinaryDataReceived(:final data):
to.sendBytes(data);
case CloseReceived(:var code, :final reason):
if (code != null && code != 1000 && (code < 3000 || code > 4999)) {
code = null;
}
// Remove close codes that are not allowed to be sent by WebSocket
// clients.
// TODO(https://github.com/dart-lang/test/issues/2576): Throw
// `SkipTest` instead of setting `code` to `null`.
code = switch (code) {
null => null,
1000 => code,
< 3000 || > 4999 => null,
_ => code,
};
to.close(code, reason);
}
} on WebSocketConnectionClosed {
Expand Down
30 changes: 30 additions & 0 deletions pkgs/web_socket_conformance_tests/lib/src/close_remote_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,47 @@
// 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 _webSocketGuid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';

Future<void> _invalidCloseReason(HttpRequest request) async {
final key = request.headers.value('Sec-WebSocket-Key');
final accept =
base64.encode(sha1.convert(utf8.encode(key! + _webSocketGuid)).bytes);
request.response
..statusCode = HttpStatus.switchingProtocols
..headers.add('Upgrade', 'websocket')
..headers.add('Connection', 'Upgrade')
..headers.add('Sec-WebSocket-Accept', accept);
final socket = await request.response.detachSocket();

// From https://datatracker.ietf.org/doc/html/rfc6455#section-5.2:
//
// 0x88 = final frame (0x80) | close opcode (0x08).
// 0x03 = payload length.
// 0x10, 0x1b = close code 4123.
// 0xff = invalid UTF-8 byte.
socket.add([0x88, 0x03, 0x10, 0x1b, 0xff]);
await socket.close();
}

/// Starts an WebSocket server that sends a Close frame after receiving any
/// data.
void hybridMain(StreamChannel<Object?> channel) async {
late HttpServer server;

server = (await HttpServer.bind('localhost', 0))
..listen((request) async {
if (request.uri.queryParameters.containsKey('badutf8')) {
await _invalidCloseReason(request);
return;
}

final webSocket = await WebSocketTransformer.upgrade(
request,
);
Expand Down
18 changes: 18 additions & 0 deletions pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,23 @@ void testCloseRemote(
await expectLater(
channel.close, throwsA(isA<WebSocketConnectionClosed>()));
});

test('with invalid reason', () async {
final channel =
await channelFactory(uri.replace(queryParameters: {'badutf8': ''}));

channel.sendText('Please close');
final events = await channel.events.toList();
expect(events, [
isA<CloseReceived>().having(
(e) => e.code,
'code',
anyOf(
1005, // No Status Received.
1006, // Abnormal Closure.
1007, // Invalid Frame Payload Data.
))
]);
});
});
}
Loading