connect static method

Future<BrowserWebSocket> connect(
  1. Uri url, {
  2. Iterable<String>? protocols,
})
override

Create a new WebSocket connection using the JavaScript WebSocket 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.

Implementation

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

  final webSocket = web.WebSocket(url.toString(),
      protocols?.map((e) => e.toJS).toList().toJS ?? JSArray())
    ..binaryType = 'arraybuffer';
  final browserSocket = BrowserWebSocket._(webSocket);
  final webSocketConnected = Completer<BrowserWebSocket>();

  if (webSocket.readyState == web.WebSocket.OPEN) {
    webSocketConnected.complete(browserSocket);
  } else {
    if (webSocket.readyState == web.WebSocket.CLOSING ||
        webSocket.readyState == web.WebSocket.CLOSED) {
      webSocketConnected.completeError(WebSocketException(
          'Unexpected WebSocket state: ${webSocket.readyState}, '
          'expected CONNECTING (0) or OPEN (1)'));
    } else {
      // The socket API guarantees that only a single open event will be
      // emitted.
      unawaited(webSocket.onOpen.first.then((_) {
        webSocketConnected.complete(browserSocket);
      }));
    }
  }

  unawaited(webSocket.onError.first.then((e) {
    // Unfortunately, the underlying WebSocket API doesn't expose any
    // specific information about the error itself.
    if (!webSocketConnected.isCompleted) {
      final error = WebSocketException('Failed to connect WebSocket');
      webSocketConnected.completeError(error);
    } else {
      browserSocket._closed(1006, 'error');
    }
  }));

  webSocket.onMessage.listen((e) {
    if (browserSocket._events.isClosed) return;

    final eventData = e.data!;
    late WebSocketEvent data;
    if (eventData.typeofEquals('string')) {
      data = TextDataReceived((eventData as JSString).toDart);
    } else if (eventData.typeofEquals('object') &&
        (eventData as JSObject).instanceOfString('ArrayBuffer')) {
      data = BinaryDataReceived(
          (eventData as JSArrayBuffer).toDart.asUint8List());
    } else {
      throw StateError('unexpected message type: ${eventData.runtimeType}');
    }
    browserSocket._events.add(data);
  });

  unawaited(webSocket.onClose.first.then((event) {
    if (!webSocketConnected.isCompleted) {
      webSocketConnected.complete(browserSocket);
    }
    browserSocket._closed(event.code, event.reason);
  }));

  return webSocketConnected.future;
}