connect static method

Future<CupertinoWebSocket> connect(
  1. Uri url, {
  2. Iterable<String>? protocols,
  3. URLSessionConfiguration? config,
})

Create a new WebSocket connection using the NSURLSessionWebSocketTask API.

The URL supplied in url must use the scheme ws or wss.

If provided, the protocols argument indicates that subprotocols that the peer is able to select. See RFC-6455 1.9.

NOTE: the WebSocket interface is currently experimental and may change in the future.

Implementation

static Future<CupertinoWebSocket> connect(Uri url,
    {Iterable<String>? protocols, URLSessionConfiguration? config}) async {
  if (!url.isScheme('ws') && !url.isScheme('wss')) {
    throw ArgumentError.value(
        url, 'url', 'only ws: and wss: schemes are supported');
  }

  final readyCompleter = Completer<CupertinoWebSocket>();
  late CupertinoWebSocket webSocket;

  final session = URLSession.sessionWithConfiguration(
      config ?? URLSessionConfiguration.defaultSessionConfiguration(),
      // In a successful flow, the callbacks will be made in this order:
      // onWebSocketTaskOpened(...)        // Good connect.
      // <receive/send messages to the peer>
      // onWebSocketTaskClosed(...)        // Optional: peer sent Close frame.
      // onComplete(..., error=null)       // Disconnected.
      //
      // In a failure to connect to the peer, the flow will be:
      // onComplete(session, task, error=error):
      //
      // `onComplete` can also be called at any point if the peer is
      // disconnected without Close frames being exchanged.
      onWebSocketTaskOpened: (session, task, protocol) {
    webSocket = CupertinoWebSocket._(task, protocol ?? '');
    readyCompleter.complete(webSocket);
  }, onWebSocketTaskClosed: (session, task, closeCode, reason) {
    assert(readyCompleter.isCompleted);
    webSocket._connectionClosed(closeCode, reason);
  }, onComplete: (session, task, error) {
    if (!readyCompleter.isCompleted) {
      // `onWebSocketTaskOpened should have been called and completed
      // `readyCompleter`. So either there was a error creating the connection
      // or a logic error.
      if (error == null) {
        throw AssertionError(
            'expected an error or "onWebSocketTaskOpened" to be called '
            'first');
      }
      readyCompleter.completeError(
          ConnectionException('connection ended unexpectedly', error));
    } else {
      // There are three possibilities here:
      // 1. the peer sent a close Frame, `onWebSocketTaskClosed` was already
      //    called and `_connectionClosed` is a no-op.
      // 2. we sent a close Frame (through `close()`) and `_connectionClosed`
      //    is a no-op.
      // 3. an error occured (e.g. network failure) and `_connectionClosed`
      //    will signal that and close `event`.
      webSocket._connectionClosed(
          1006, Data.fromList('abnormal close'.codeUnits));
    }
  });

  session.webSocketTaskWithURL(url, protocols: protocols).resume();
  return readyCompleter.future;
}