startConnect static method

Future<ConnectionTask<RawSecureSocket>> startConnect(
  1. dynamic host,
  2. int port, {
  3. SecurityContext? context,
  4. bool onBadCertificate(
    1. X509Certificate certificate
    )?,
  5. void keyLog(
    1. String
    )?,
  6. List<String>? supportedProtocols,
})
override

Like connect, but returns a Future that completes with a ConnectionTask that can be cancelled if the RawSecureSocket is no longer needed.

Implementation

static Future<ConnectionTask<RawSecureSocket>> startConnect(
  host,
  int port, {
  SecurityContext? context,
  bool Function(X509Certificate certificate)? onBadCertificate,
  void Function(String line)? keyLog,
  List<String>? supportedProtocols,
}) async {
  final tcpTask = await RawSocket.startConnect(host, port);
  final result = Completer<RawSecureSocket>();
  var cancelled = false;
  RawSocket? connected;

  SocketException cancelledException() => SocketException(
    "Connection attempt cancelled, host: $host, port: $port",
  );

  Future<void> connectAndSecure() async {
    try {
      final socket = await tcpTask.socket;
      if (cancelled) {
        socket.close();
        throw cancelledException();
      }
      connected = socket;
      final secureSocket = await secure(
        socket,
        context: context,
        onBadCertificate: onBadCertificate,
        keyLog: keyLog,
        supportedProtocols: supportedProtocols,
      );
      if (result.isCompleted) {
        secureSocket.close();
      } else {
        result.complete(secureSocket);
      }
    } catch (error, stackTrace) {
      if (!result.isCompleted) result.completeError(error, stackTrace);
    }
  }

  void cancel() {
    cancelled = true;
    tcpTask.cancel();
    // Once TCP has connected, cancelling the connect does nothing, so close
    // the socket too; otherwise a stalled handshake leaks it. Closing the
    // socket does not complete the handshake, so complete the result here.
    // After the handshake has completed, cancel does nothing.
    final socket = connected;
    if (socket != null && !result.isCompleted) {
      socket.close();
      result.completeError(cancelledException(), StackTrace.current);
    }
  }

  unawaited(connectAndSecure());
  return ConnectionTask<RawSecureSocket>._(result.future, cancel);
}