Skip to content

Usage of cupertino_http with web_socket_channel #1282

@orestesgaolin

Description

@orestesgaolin

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

Metadata

Metadata

Assignees

Labels

package:cupertino_httpIssues related to package:cupertino_httptype-enhancementA request for a change that isn't a bug

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions