/// <inheritdoc/> public override async ValueTask <Connection> ConnectAsync(EndPoint endPoint, IConnectionProperties?options = null, CancellationToken cancellationToken = default) { string authority = endPoint switch { DnsEndPoint dns => Tools.EscapeIdnHost(dns.Host) + ":" + dns.Port.ToString(CultureInfo.InvariantCulture), IPEndPoint ip4 when ip4.AddressFamily == AddressFamily.InterNetwork => ip4.Address.ToString() + ":" + ip4.Port.ToString(CultureInfo.InvariantCulture), IPEndPoint ip6 when ip6.AddressFamily == AddressFamily.InterNetworkV6 => "[" + ip6.Address.ToString() + "]:" + ip6.Port.ToString(CultureInfo.InvariantCulture), null => throw new ArgumentNullException(nameof(endPoint)), _ => throw new ArgumentException($"{nameof(EndPoint)} is of an unsupported type. Must be one of {nameof(DnsEndPoint)} or {nameof(IPEndPoint)}", nameof(endPoint)) }; byte[] authorityBytes = Encoding.ASCII.GetBytes(authority); ValueHttpRequest request = (await _httpConnection.CreateNewRequestAsync(_httpVersion, _httpVersionPolicy, cancellationToken).ConfigureAwait(false)) ?? throw new Exception($"{nameof(HttpConnection)} in use by {nameof(HttpTunnelConnectionFactory)} has been closed by peer."); try { request.ConfigureRequest(contentLength: null, hasTrailingHeaders: false); request.WriteConnectRequest(authorityBytes); await request.FlushHeadersAsync(cancellationToken).ConfigureAwait(false); bool hasResponse = await request.ReadToFinalResponseAsync(cancellationToken).ConfigureAwait(false); Debug.Assert(hasResponse); if ((int)request.StatusCode > 299) { throw new Exception($"Connect to HTTP tunnel failed; received status code {request.StatusCode}."); } var localEndPoint = new TunnelEndPoint(request.LocalEndPoint, request.RemoteEndPoint); var stream = new HttpContentStream(request, ownsRequest: true); return(new HttpTunnelConnection(localEndPoint, endPoint, stream)); } catch { await request.DisposeAsync(cancellationToken).ConfigureAwait(false); throw; } }
protected internal override void WriteConnectRequest(int version, ReadOnlySpan <byte> authority) { ThrowIfDisposed(version); _request.WriteConnectRequest(authority); }