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(); 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)); }