Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usage of cupertino_http with web_socket_channel #1282

Closed
orestesgaolin opened this issue Aug 1, 2024 · 0 comments · Fixed by #1297
Closed

Usage of cupertino_http with web_socket_channel #1282

orestesgaolin opened this issue Aug 1, 2024 · 0 comments · Fixed by #1297
Assignees
Labels
package:cupertino_http Issues related to package:cupertino_http type-enhancement A request for a change that isn't a bug

Comments

@orestesgaolin
Copy link

orestesgaolin commented Aug 1, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
package:cupertino_http Issues related to package:cupertino_http type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants