-
Notifications
You must be signed in to change notification settings - Fork 388
Description
Recently added cupertino web socket API uses web_socket package. However, there are some higher-level libraries like graphql, ferry or gql that still rely on dart:io WebSocket implementations via web_socket_channel package.
This means that the benefits coming from cupertino_http (like proxy support, observability via Xcode Instruments) cannot be leveraged there without additional work.
I'd like to request an adapter example that could let us use CupertinoWebSocket with web_socket_channel. It could be just a part of the documentation on how to use cupertino_http with dart:io-related packages.
In my first attempt I tried to wrap CupertinoWebSocket with WebSocketChannel via StreamChannelMixin. It doesn't work but feels like relatively acceptable amount of code to be put into the documentation (assuming it works).
Is such addition to the docs possible?
Edit: I found AdapterWebSocketChannel class that can be used as follows:
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:async/async.dart' as async;
import 'package:cupertino_http/cupertino_http.dart';
import 'package:http/http.dart';
import 'package:stream_channel/stream_channel.dart' as sc;
import 'package:web_socket/web_socket.dart' as ws;
import 'package:web_socket_channel/web_socket_channel.dart' as wsc;
// usage
return AdapterWebSocketChannel(
CupertinoWebSocket.connect(
Uri.parse(baseWs),
protocols: ['graphql-transport-ws'],
config: URLSessionConfiguration.defaultSessionConfiguration(),
),
);
// adapter below
class AdapterWebSocketChannel extends sc.StreamChannelMixin implements wsc.WebSocketChannel {
// ...
AdapterWebSocketChannel(FutureOr<ws.WebSocket> webSocket) {
// ...
}Previous non working impl
import 'dart:io';
import 'dart:typed_data';
import 'package:cupertino_http/cupertino_http.dart';
import 'package:stream_channel/stream_channel.dart' as sc;
import 'package:web_socket/web_socket.dart' as ws;
import 'package:web_socket_channel/web_socket_channel.dart' as wsc;
class CupertinoWebSocketChannelAdapter {
static Future<wsc.WebSocketChannel> connect(String baseWs) async {
final socket = await CupertinoWebSocket.connect(
Uri.parse(baseWs),
protocols: ['graphql-transport-ws'],
config: URLSessionConfiguration.defaultSessionConfiguration(),
);
socket.events.asBroadcastStream().listen((e) async {
switch (e) {
case ws.TextDataReceived(text: final text):
print('Received Text: $text');
case ws.BinaryDataReceived(data: final data):
print('Received Binary: $data');
case ws.CloseReceived(code: final code, reason: final reason):
print('Connection to server closed: $code [$reason]');
}
});
return wsc.WebSocketChannel(StreamChannelAdapter(socket));
}
}
class StreamChannelAdapter with sc.StreamChannelMixin<List<int>> {
StreamChannelAdapter(this._socket) {
// subscription not handled
print('AdapterStreamChannel created');
_sinkController.stream.listen(
(event) {
print('AdapterStreamChannel event: $event');
final bytes = Uint8List.fromList(event);
_socket.sendBytes(bytes);
},
onDone: () => {
print('AdapterStreamChannel done'),
},
);
}
final StreamController<List<int>> _sinkController = StreamController<List<int>>.broadcast();
final CupertinoWebSocket _socket;
@override
StreamSink<List<int>> get sink => _sinkController.sink;
@override
Stream<List<int>> get stream => _socket.events.asBroadcastStream().map((e) {
print('AdapterStreamChannel event: $e');
switch (e) {
case ws.TextDataReceived(text: final text):
return text.codeUnits;
case ws.BinaryDataReceived(data: final data):
return data;
case ws.CloseReceived(code: final code, reason: final reason):
throw Exception('Connection to server closed: $code [$reason]');
}
});
}
Related