Future<WebSocket> connectToWebSocket(String url, { Iterable<String> protocols, Map<String, dynamic> headers, HttpClient httpClient, bool useStandardWebSocket })

Custom WebSocket Connection logic.

Source

static Future<WebSocket> connectToWebSocket(
    String url, {
    Iterable<String> protocols,
    Map<String, dynamic> headers,
    HttpClient httpClient,
    bool useStandardWebSocket
  }) async {
  Uri uri = Uri.parse(url);

  if (useStandardWebSocket == null) {
    useStandardWebSocket = enableStandardWebSocket;
  }

  if (useStandardWebSocket == true && uri.scheme != "wss") {
    return await awaitWithTimeout(WebSocket.connect(
      url,
      protocols: protocols,
      headers: headers
    ), 60000, onSuccessAfterTimeout: (WebSocket socket){
      socket.close();
    });
  }

  if (uri.scheme != "ws" && uri.scheme != "wss") {
    throw new WebSocketException("Unsupported URL scheme '${uri.scheme}'");
  }

  Random random = new Random();
  // Generate 16 random bytes.
  Uint8List nonceData = new Uint8List(16);
  for (int i = 0; i < 16; i++) {
    nonceData[i] = random.nextInt(256);
  }
  String nonce = BASE64.encode(nonceData);

  int port = uri.port;
  if (port == 0) {
    port = uri.scheme == "wss" ? 443 : 80;
  }

  uri = new Uri(
    scheme: uri.scheme == "wss" ? "https" : "http",
    userInfo: uri.userInfo,
    host: uri.host,
    port: port,
    path: uri.path,
    query: uri.query
  );

  HttpClient _client = httpClient == null ? (
    new HttpClient()
      ..badCertificateCallback = (a, b, c) => true
  ) : httpClient;

  return _client.openUrl("GET", uri).then((HttpClientRequest request) async {
    if (uri.userInfo != null && !uri.userInfo.isEmpty) {
      // If the URL contains user information use that for basic
      // authorization.
      String auth = BASE64.encode(UTF8.encode(uri.userInfo));
      request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
    }
    if (headers != null) {
      headers.forEach((field, value) => request.headers.add(field, value));
    }
    // Setup the initial handshake.
    request.headers
      ..set(HttpHeaders.CONNECTION, "Upgrade")
      ..set(HttpHeaders.UPGRADE, "websocket")
      ..set("Sec-WebSocket-Key", nonce)
      ..set("Cache-Control", "no-cache")
      ..set("Sec-WebSocket-Version", "13");
    if (protocols != null) {
      request.headers.add("Sec-WebSocket-Protocol", protocols.toList());
    }
    return request.close();
  }).then((response) {
    return response;
  }).then((HttpClientResponse response) {
    void error(String message) {
      // Flush data.
      response.detachSocket().then((Socket socket) {
        socket.destroy();
      });
      throw new WebSocketException(message);
    }
    if (response.statusCode != HttpStatus.SWITCHING_PROTOCOLS ||
      response.headers[HttpHeaders.CONNECTION] == null ||
      !response.headers[HttpHeaders.CONNECTION].any(
            (value) => value.toLowerCase() == "upgrade") ||
      response.headers.value(HttpHeaders.UPGRADE).toLowerCase() != "websocket") {
      error("Connection to '$uri' was not upgraded to websocket");
    }
    String accept = response.headers.value("Sec-WebSocket-Accept");
    if (accept == null) {
      error("Response did not contain a 'Sec-WebSocket-Accept' header");
    }
    List<int> expectedAccept = sha1.convert("$nonce$_webSocketGUID".codeUnits).bytes;
    List<int> receivedAccept = BASE64.decode(accept);
    if (expectedAccept.length != receivedAccept.length) {
      error("Response header 'Sec-WebSocket-Accept' is the wrong length");
    }
    for (int i = 0; i < expectedAccept.length; i++) {
      if (expectedAccept[i] != receivedAccept[i]) {
        error("Bad response 'Sec-WebSocket-Accept' header");
      }
    }
    var protocol = response.headers.value('Sec-WebSocket-Protocol');
    return response.detachSocket().then((socket) {
      socket.setOption(SocketOption.TCP_NODELAY, _tcpNoDelay);
      return new WebSocket.fromUpgradedSocket(
        socket,
        protocol: protocol,
        serverSide: false
      );
    });
  }).timeout(new Duration(minutes: 1), onTimeout:(){
    _client.close(force: true);
    throw new WebSocketException('timeout');
  });
}