diff --git a/pkgs/shelf_web_socket/CHANGELOG.md b/pkgs/shelf_web_socket/CHANGELOG.md index d3596cef..5984e065 100644 --- a/pkgs/shelf_web_socket/CHANGELOG.md +++ b/pkgs/shelf_web_socket/CHANGELOG.md @@ -1,6 +1,8 @@ -## 1.0.5-wip +## 2.0.0-wip * Require Dart `^3.0.0`. +* **BREAKING:**: Remove support for hijacking WebSocket requests that are not + being transported using `dart:io` `Socket`s. ## 1.0.4 diff --git a/pkgs/shelf_web_socket/lib/src/web_socket_handler.dart b/pkgs/shelf_web_socket/lib/src/web_socket_handler.dart index 17287655..a41ed697 100644 --- a/pkgs/shelf_web_socket/lib/src/web_socket_handler.dart +++ b/pkgs/shelf_web_socket/lib/src/web_socket_handler.dart @@ -3,8 +3,10 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:convert'; +import 'dart:io'; import 'package:shelf/shelf.dart'; +import 'package:web_socket_channel/io.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; /// A class that exposes a handler for upgrading WebSocket requests. @@ -78,9 +80,16 @@ class WebSocketHandler { if (protocol != null) sink.add('Sec-WebSocket-Protocol: $protocol\r\n'); sink.add('\r\n'); + if (channel.sink is! Socket) { + throw ArgumentError('channel.sink must be a dart:io `Socket`.'); + } + + final webSocket = WebSocket.fromUpgradedSocket(channel.sink as Socket, + protocol: protocol, serverSide: true) + ..pingInterval = _pingInterval; + // ignore: avoid_dynamic_calls - _onConnection( - WebSocketChannel(channel, pingInterval: _pingInterval), protocol); + _onConnection(IOWebSocketChannel(webSocket), protocol); }); } diff --git a/pkgs/shelf_web_socket/pubspec.yaml b/pkgs/shelf_web_socket/pubspec.yaml index 9c3fd945..2a8f846b 100644 --- a/pkgs/shelf_web_socket/pubspec.yaml +++ b/pkgs/shelf_web_socket/pubspec.yaml @@ -1,5 +1,5 @@ name: shelf_web_socket -version: 1.0.5-wip +version: 2.0.0-wip description: > A shelf handler that wires up a listener for every connection. repository: https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_web_socket @@ -20,3 +20,8 @@ dev_dependencies: dart_flutter_team_lints: ^2.0.0 http: '>=0.13.0 <2.0.0' test: ^1.16.0 + +# Remove this when a version of `package:test` is released that is compatible +# with self_web_socket 2.x +dependency_overrides: + test: 1.25.2 diff --git a/pkgs/shelf_web_socket/test/web_socket_test.dart b/pkgs/shelf_web_socket/test/web_socket_test.dart index b5195923..eb8e00bb 100644 --- a/pkgs/shelf_web_socket/test/web_socket_test.dart +++ b/pkgs/shelf_web_socket/test/web_socket_test.dart @@ -5,8 +5,10 @@ import 'dart:io'; import 'package:http/http.dart' as http; +import 'package:shelf/shelf.dart'; import 'package:shelf/shelf_io.dart' as shelf_io; import 'package:shelf_web_socket/shelf_web_socket.dart'; +import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; @@ -103,6 +105,32 @@ void main() { } }); + test('cannot hijack non-Socket StreamChannel', () async { + final handler = + webSocketHandler((WebSocketChannel webSocket, String? protocol) { + expect(protocol, isNull); + webSocket.sink.close(); + }); + + expect( + () => handler(Request('GET', Uri.parse('ws://example.com/'), + protocolVersion: '1.1', + headers: { + 'Host': 'example.com', + 'Upgrade': 'websocket', + 'Connection': 'Upgrade', + 'Sec-WebSocket-Key': 'x3JJHMbDL1EzLkh9GBhXDw==', + 'Sec-WebSocket-Version': '13', + 'Origin': 'http://example.com', + }, onHijack: (fn) { + // `.foreign` is not a Socket so hijacking the request should + // fail. + expect(() => fn(StreamChannelController>().foreign), + throwsArgumentError); + })), + throwsA(isA())); + }); + group('with a set of allowed origins', () { late HttpServer server; late Uri url;