// constructor for outbound connections public MsQuicConnection(QuicClientConnectionOptions options) { _remoteEndPoint = options.RemoteEndPoint !; _configuration = SafeMsQuicConfigurationHandle.Create(options); _stateHandle = GCHandle.Alloc(_state); try { // this handle is ref counted by MsQuic, so safe to dispose here. using SafeMsQuicConfigurationHandle config = SafeMsQuicConfigurationHandle.Create(options); uint status = MsQuicApi.Api.ConnectionOpenDelegate( MsQuicApi.Api.Registration, s_connectionDelegate, GCHandle.ToIntPtr(_stateHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "Could not open the connection."); } catch { _stateHandle.Free(); throw; } }
internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) { ThrowIfDisposed(); (string address, int port) = _remoteEndPoint switch { DnsEndPoint dnsEp => (dnsEp.Host, dnsEp.Port), IPEndPoint ipEp => (ipEp.Address.ToString(), ipEp.Port), _ => throw new Exception($"Unsupported remote endpoint type '{_remoteEndPoint.GetType()}'.") }; // values taken from https://github.com/microsoft/msquic/blob/main/docs/api/ConnectionStart.md int af = _remoteEndPoint.AddressFamily switch { AddressFamily.Unspecified => 0, AddressFamily.InterNetwork => 2, AddressFamily.InterNetworkV6 => 23, _ => throw new Exception(SR.Format(SR.net_quic_unsupported_address_family, _remoteEndPoint.AddressFamily)) }; QuicExceptionHelpers.ThrowIfFailed( MsQuicApi.Api.ConnectionStartDelegate( _ptr, (ushort)af, address, (ushort)port), "Failed to connect to peer."); return(new ValueTask(_connectTcs.Task)); }
private MsQuicStream StreamOpen( QUIC_STREAM_OPEN_FLAG flags) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this); } IntPtr streamPtr = IntPtr.Zero; QuicExceptionHelpers.ThrowIfFailed( MsQuicApi.Api.StreamOpenDelegate( _ptr, (uint)flags, MsQuicStream.NativeCallbackHandler, IntPtr.Zero, out streamPtr), "Failed to open stream to peer."); MsQuicStream stream = new MsQuicStream(this, flags, streamPtr, inbound: false); if (NetEventSource.IsEnabled) { NetEventSource.Exit(this); } return(stream); }
private unsafe IPEndPoint Start(QuicListenerOptions options) { List <SslApplicationProtocol> applicationProtocols = options.ServerAuthenticationOptions !.ApplicationProtocols !; IPEndPoint listenEndPoint = options.ListenEndPoint !; SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(listenEndPoint); uint status; Debug.Assert(_stateHandle.IsAllocated); MemoryHandle[]? handles = null; QuicBuffer[]? buffers = null; try { MsQuicAlpnHelper.Prepare(applicationProtocols, out handles, out buffers); status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, (QuicBuffer *)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)applicationProtocols.Count, ref address); } catch { _stateHandle.Free(); throw; } finally { MsQuicAlpnHelper.Return(ref handles, ref buffers); } QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed."); SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); return(MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress)); }
// constructor for outbound connections public MsQuicConnection(QuicClientConnectionOptions options) { _remoteEndPoint = options.RemoteEndPoint !; _configuration = SafeMsQuicConfigurationHandle.Create(options); _isServer = false; _remoteCertificateRequired = true; if (options.ClientAuthenticationOptions != null) { _revocationMode = options.ClientAuthenticationOptions.CertificateRevocationCheckMode; _remoteCertificateValidationCallback = options.ClientAuthenticationOptions.RemoteCertificateValidationCallback; } _state.StateGCHandle = GCHandle.Alloc(_state); try { uint status = MsQuicApi.Api.ConnectionOpenDelegate( MsQuicApi.Api.Registration, s_connectionDelegate, GCHandle.ToIntPtr(_state.StateGCHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "Could not open the connection."); } catch { _state.StateGCHandle.Free(); throw; } _state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(_state, $"{TraceId()} Outbound connection created"); } }
// outbound. internal MsQuicStream(SafeMsQuicConnectionHandle connection, QUIC_STREAM_OPEN_FLAGS flags) { Debug.Assert(connection != null); _canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); _canWrite = true; _stateHandle = GCHandle.Alloc(_state); try { uint status = MsQuicApi.Api.StreamOpenDelegate( connection, flags, s_streamDelegate, GCHandle.ToIntPtr(_stateHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to open stream to peer."); status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, QUIC_STREAM_START_FLAGS.ASYNC); QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream."); } catch { _state.Handle?.Dispose(); _stateHandle.Free(); throw; } }
internal override unsafe void Start() { ThrowIfDisposed(); SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(_listenEndPoint); uint status; MemoryHandle[]? handles = null; QuicBuffer[]? buffers = null; try { MsQuicAlpnHelper.Prepare(_applicationProtocols, out handles, out buffers); status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, (QuicBuffer *)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)_applicationProtocols.Count, ref address); } finally { MsQuicAlpnHelper.Return(ref handles, ref buffers); } QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed."); SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); _listenEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); }
// constructor for outbound connections public MsQuicConnection(QuicClientConnectionOptions options) { _remoteEndPoint = options.RemoteEndPoint !; _configuration = SafeMsQuicConfigurationHandle.Create(options); _isServer = false; _remoteCertificateRequired = true; if (options.ClientAuthenticationOptions != null) { _revocationMode = options.ClientAuthenticationOptions.CertificateRevocationCheckMode; _remoteCertificateValidationCallback = options.ClientAuthenticationOptions.RemoteCertificateValidationCallback; } _stateHandle = GCHandle.Alloc(_state); try { // this handle is ref counted by MsQuic, so safe to dispose here. using SafeMsQuicConfigurationHandle config = SafeMsQuicConfigurationHandle.Create(options); uint status = MsQuicApi.Api.ConnectionOpenDelegate( MsQuicApi.Api.Registration, s_connectionDelegate, GCHandle.ToIntPtr(_stateHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "Could not open the connection."); } catch { _stateHandle.Free(); throw; } }
internal MsQuicListener(QuicListenerOptions options) { ArgumentNullException.ThrowIfNull(options.ListenEndPoint, nameof(options.ListenEndPoint)); _state = new State(options); _stateHandle = GCHandle.Alloc(_state); try { uint status = MsQuicApi.Api.ListenerOpenDelegate( MsQuicApi.Api.Registration, s_listenerDelegate, GCHandle.ToIntPtr(_stateHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "ListenerOpen failed."); } catch { _stateHandle.Free(); throw; } _state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(_state, $"{_state.TraceId} Listener created"); } _listenEndPoint = Start(options); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(_state, $"{_state.TraceId} Listener started"); } }
private void SetParam( QUIC_PARAM_SESSION param, MsQuicNativeMethods.QuicBuffer buf) { QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.UnsafeSetParam( _nativeObjPtr, (uint)QUIC_PARAM_LEVEL.SESSION, (uint)param, buf), "Could not set parameter on session."); }
private ValueTask ShutdownAsync( QUIC_CONNECTION_SHUTDOWN_FLAG Flags, long ErrorCode) { uint status = MsQuicApi.Api.ConnectionShutdownDelegate( _ptr, (uint)Flags, ErrorCode); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to shutdown connection."); return(new ValueTask(_shutdownTcs.Task)); }
// outbound. internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_FLAGS flags) { Debug.Assert(connectionState.Handle != null); _canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); _canWrite = true; _state.StateGCHandle = GCHandle.Alloc(_state); if (!_canRead) { _state.ReadState = ReadState.Closed; } try { uint status = MsQuicApi.Api.StreamOpenDelegate( connectionState.Handle, flags, s_streamDelegate, GCHandle.ToIntPtr(_state.StateGCHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to open stream to peer."); status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, QUIC_STREAM_START_FLAGS.FAIL_BLOCKED); QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream."); } catch { _state.Handle?.Dispose(); _state.StateGCHandle.Free(); throw; } if (!connectionState.TryAddStream(this)) { _state.Handle?.Dispose(); _state.StateGCHandle.Free(); throw new ObjectDisposedException(nameof(QuicConnection)); } _state.ConnectionState = connectionState; if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info( _state, $"[Stream#{_state.GetHashCode()}] outbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " + $"in Connection#{_state.ConnectionState.GetHashCode()}."); } }
internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) { ThrowIfDisposed(); if (_configuration is null) { throw new Exception($"{nameof(ConnectAsync)} must not be called on a connection obtained from a listener."); } (string address, int port) = _remoteEndPoint switch { DnsEndPoint dnsEp => (dnsEp.Host, dnsEp.Port), IPEndPoint ipEp => (ipEp.Address.ToString(), ipEp.Port), _ => throw new Exception($"Unsupported remote endpoint type '{_remoteEndPoint.GetType()}'.") }; QUIC_ADDRESS_FAMILY af = _remoteEndPoint.AddressFamily switch { AddressFamily.Unspecified => QUIC_ADDRESS_FAMILY.UNSPEC, AddressFamily.InterNetwork => QUIC_ADDRESS_FAMILY.INET, AddressFamily.InterNetworkV6 => QUIC_ADDRESS_FAMILY.INET6, _ => throw new Exception(SR.Format(SR.net_quic_unsupported_address_family, _remoteEndPoint.AddressFamily)) }; Debug.Assert(_state.StateGCHandle.IsAllocated); _state.Connection = this; try { uint status = MsQuicApi.Api.ConnectionStartDelegate( _state.Handle, _configuration, af, address, (ushort)port); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); // this handle is ref counted by MsQuic, so safe to dispose here. _configuration.Dispose(); _configuration = null; } catch { _state.StateGCHandle.Free(); _state.Connection = null; throw; } return(new ValueTask(_state.ConnectTcs.Task)); }
internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) { ThrowIfDisposed(); QuicExceptionHelpers.ThrowIfFailed( MsQuicApi.Api.ConnectionStartDelegate( _ptr, (ushort)_remoteEndPoint.AddressFamily, _remoteEndPoint.Address.ToString(), (ushort)_remoteEndPoint.Port), "Failed to connect to peer."); return(_connectTcs.GetTypelessValueTask()); }
internal override void Start() { ThrowIfDisposed(); SetCallbackHandler(); SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(_listenEndPoint); QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ListenerStartDelegate( _ptr, ref address), "Failed to start listener."); SetListenPort(); }
private MsQuicStream StreamOpen( QUIC_STREAM_OPEN_FLAG flags) { IntPtr streamPtr = IntPtr.Zero; QuicExceptionHelpers.ThrowIfFailed( MsQuicApi.Api.StreamOpenDelegate( _ptr, (uint)flags, MsQuicStream.s_streamDelegate, IntPtr.Zero, out streamPtr), "Failed to open stream to peer."); return(new MsQuicStream(this, flags, streamPtr, inbound: false)); }
private static unsafe uint NativeCallbackHandler( IntPtr listener, IntPtr context, ref ListenerEvent evt) { if (evt.Type != QUIC_LISTENER_EVENT.NEW_CONNECTION) { return(MsQuicStatusCodes.InternalError); } GCHandle gcHandle = GCHandle.FromIntPtr(context); Debug.Assert(gcHandle.IsAllocated); Debug.Assert(gcHandle.Target is not null); var state = (State)gcHandle.Target; SafeMsQuicConnectionHandle?connectionHandle = null; try { ref NewConnectionInfo connectionInfo = ref *evt.Data.NewConnection.Info; IPEndPoint localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET *)connectionInfo.LocalAddress); IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET *)connectionInfo.RemoteAddress); connectionHandle = new SafeMsQuicConnectionHandle(evt.Data.NewConnection.Connection); uint status = MsQuicApi.Api.ConnectionSetConfigurationDelegate(connectionHandle, state.ConnectionConfiguration); QuicExceptionHelpers.ThrowIfFailed(status, "ConnectionSetConfiguration failed."); var msQuicConnection = new MsQuicConnection(localEndPoint, remoteEndPoint, connectionHandle, state.RemoteCertificateRequired, state.RevocationMode, state.RemoteCertificateValidationCallback); msQuicConnection.SetNegotiatedAlpn(connectionInfo.NegotiatedAlpn, connectionInfo.NegotiatedAlpnLength); if (!state.AcceptConnectionQueue.Writer.TryWrite(msQuicConnection)) { // This handle will be cleaned up by MsQuic. connectionHandle.SetHandleAsInvalid(); msQuicConnection.Dispose(); return(MsQuicStatusCodes.InternalError); } return(MsQuicStatusCodes.Success); }
private ValueTask ShutdownAsync( QUIC_CONNECTION_SHUTDOWN_FLAG Flags, long ErrorCode) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this); } uint status = MsQuicApi.Api.ConnectionShutdownDelegate( _ptr, (uint)Flags, ErrorCode); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to shutdown connection."); if (NetEventSource.IsEnabled) { NetEventSource.Exit(this); } return(_shutdownTcs.GetTypelessValueTask()); }
internal MsQuicListener(QuicListenerOptions options) { _state = new State(options); _stateHandle = GCHandle.Alloc(_state); try { uint status = MsQuicApi.Api.ListenerOpenDelegate( MsQuicApi.Api.Registration, s_listenerDelegate, GCHandle.ToIntPtr(_stateHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "ListenerOpen failed."); } catch { _stateHandle.Free(); throw; } _listenEndPoint = Start(options); }
internal override void Start() { ThrowIfDisposed(); // protect against double starts. if (_started) { throw new QuicException("Cannot start Listener multiple times"); } _started = true; SetCallbackHandler(); SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(_listenEndPoint); QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ListenerStartDelegate( _ptr, ref address), "Failed to start listener."); SetListenPort(); }
private unsafe IPEndPoint Start(QuicListenerOptions options) { List <SslApplicationProtocol> applicationProtocols = options.ServerAuthenticationOptions !.ApplicationProtocols !; IPEndPoint listenEndPoint = options.ListenEndPoint !; Internals.SocketAddress address = IPEndPointExtensions.Serialize(listenEndPoint); uint status; Debug.Assert(_stateHandle.IsAllocated); MemoryHandle[]? handles = null; QuicBuffer[]? buffers = null; try { Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); MsQuicAlpnHelper.Prepare(applicationProtocols, out handles, out buffers); fixed(byte *paddress = address.Buffer) { status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, (QuicBuffer *)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)applicationProtocols.Count, paddress); } } catch { _stateHandle.Free(); throw; } finally { MsQuicAlpnHelper.Return(ref handles, ref buffers); } QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed."); Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); return(MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS)); }
internal MsQuicListener(QuicListenerOptions options) { _applicationProtocols = options.ServerAuthenticationOptions !.ApplicationProtocols !; _listenEndPoint = options.ListenEndPoint !; _state = new State(options); _stateHandle = GCHandle.Alloc(_state); try { uint status = MsQuicApi.Api.ListenerOpenDelegate( MsQuicApi.Api.Registration, s_listenerDelegate, GCHandle.ToIntPtr(_stateHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "ListenerOpen failed."); } catch { _state.Handle?.Dispose(); _stateHandle.Free(); throw; } }
internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) { ThrowIfDisposed(); if (_configuration is null) { throw new Exception($"{nameof(ConnectAsync)} must not be called on a connection obtained from a listener."); } QUIC_ADDRESS_FAMILY af = _remoteEndPoint.AddressFamily switch { AddressFamily.Unspecified => QUIC_ADDRESS_FAMILY.UNSPEC, AddressFamily.InterNetwork => QUIC_ADDRESS_FAMILY.INET, AddressFamily.InterNetworkV6 => QUIC_ADDRESS_FAMILY.INET6, _ => throw new Exception(SR.Format(SR.net_quic_unsupported_address_family, _remoteEndPoint.AddressFamily)) }; Debug.Assert(_state.StateGCHandle.IsAllocated); _state.Connection = this; uint status; string targetHost; int port; if (_remoteEndPoint is IPEndPoint) { SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet((IPEndPoint)_remoteEndPoint); unsafe { Debug.Assert(!Monitor.IsEntered(_state)); status = MsQuicApi.Api.SetParamDelegate(_state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, (uint)sizeof(SOCKADDR_INET), (byte *)&address); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); } targetHost = _state.TargetHost ?? ((IPEndPoint)_remoteEndPoint).Address.ToString(); port = ((IPEndPoint)_remoteEndPoint).Port; } else if (_remoteEndPoint is DnsEndPoint) { // We don't have way how to set separate SNI and name for connection at this moment. targetHost = ((DnsEndPoint)_remoteEndPoint).Host; port = ((DnsEndPoint)_remoteEndPoint).Port; } else { throw new Exception($"Unsupported remote endpoint type '{_remoteEndPoint.GetType()}'."); } // We store TCS to local variable to avoid NRE if callbacks finish fast and set _state.ConnectTcs to null. var tcs = _state.ConnectTcs = new TaskCompletionSource <uint>(TaskCreationOptions.RunContinuationsAsynchronously); try { Debug.Assert(!Monitor.IsEntered(_state)); status = MsQuicApi.Api.ConnectionStartDelegate( _state.Handle, _configuration, af, targetHost, (ushort)port); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); // this handle is ref counted by MsQuic, so safe to dispose here. _configuration.Dispose(); _configuration = null; } catch { _state.StateGCHandle.Free(); _state.Connection = null; throw; } return(new ValueTask(tcs.Task)); }
internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) { ThrowIfDisposed(); if (_configuration is null) { throw new InvalidOperationException($"{nameof(ConnectAsync)} must not be called on a connection obtained from a listener."); } QUIC_ADDRESS_FAMILY af = _remoteEndPoint.AddressFamily switch { AddressFamily.Unspecified => QUIC_ADDRESS_FAMILY.UNSPEC, AddressFamily.InterNetwork => QUIC_ADDRESS_FAMILY.INET, AddressFamily.InterNetworkV6 => QUIC_ADDRESS_FAMILY.INET6, _ => throw new ArgumentException(SR.Format(SR.net_quic_unsupported_address_family, _remoteEndPoint.AddressFamily)) }; Debug.Assert(_state.StateGCHandle.IsAllocated); _state.Connection = this; uint status; string targetHost; int port; if (_remoteEndPoint is IPEndPoint ipEndPoint) { Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, ipEndPoint); targetHost = _state.TargetHost ?? ((IPEndPoint)_remoteEndPoint).Address.ToString(); port = ((IPEndPoint)_remoteEndPoint).Port; } else if (_remoteEndPoint is DnsEndPoint dnsEndPoint) { port = dnsEndPoint.Port; string dnsHost = dnsEndPoint.Host !; // We don't have way how to set separate SNI and name for connection at this moment. // If the name is actually IP address we can use it to make at least some cases work for people // who want to bypass DNS but connect to specific virtual host. if (!string.IsNullOrEmpty(_state.TargetHost) && !dnsHost.Equals(_state.TargetHost, StringComparison.InvariantCultureIgnoreCase) && IPAddress.TryParse(dnsHost, out IPAddress? address)) { // This is form of IPAddress and _state.TargetHost is set to different string Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, new IPEndPoint(address, port)); targetHost = _state.TargetHost !; } else { targetHost = dnsHost; } } else { throw new ArgumentException($"Unsupported remote endpoint type '{_remoteEndPoint.GetType()}'."); } // We store TCS to local variable to avoid NRE if callbacks finish fast and set _state.ConnectTcs to null. var tcs = _state.ConnectTcs = new TaskCompletionSource <uint>(TaskCreationOptions.RunContinuationsAsynchronously); try { Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); status = MsQuicApi.Api.ConnectionStartDelegate( _state.Handle, _configuration, af, targetHost, (ushort)port); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); // this handle is ref counted by MsQuic, so safe to dispose here. _configuration.Dispose(); _configuration = null; } catch { _state.Connection = null; throw; } return(new ValueTask(tcs.Task)); }