* Simplify ConnectHelper.ConnectAsync to just return Stream
SocketsHttpHandler's ConnectHelper.ConnectAsync currently returns a tuple of (Socket, Stream). We can instead just have it return the Stream and allow the call site to fish out the Socket from the Stream if it can. This is slightly more efficient memory-wise, in that the state machine involved will be smaller, but it also allows for a potential extensibility point that's simpler (abstracting out the ConnectAsync as a callback) and potentially allows us to in the future get a Socket in more cases, e.g. any connection helper handing back a NetworkStream.
Unfortunately, NetworkStream.Socket is protected rather than public. Until such time that there's a public property for this, there are two options here:
1. Use reflection to create a delegate we can then use to get at the property; "Socket" is part of the exposed surface area, so the only dependency here is on documented surface area.
2. Create a custom NetworkStream-derived type that exposes the protected as a public.
I started with (2), which works fine for our own internal needs currently, but shifted to (1), as it's more flexible.
* Don't use reflection
Commit migrated from https://github.com/dotnet/corefx/commit/
d9cdbc3cca0f75e5d7692f9ae03978dd0ed9b2e9
<Compile Include="System\Net\Http\SocketsHttpHandler\CreditManager.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\DecompressionHandler.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\EmptyReadStream.cs" />
+ <Compile Include="System\Net\Http\SocketsHttpHandler\ExposedSocketNetworkStream.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\Http2Connection.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\Http2Stream.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpAuthenticatedConnectionHandler.cs" />
}
}
- public static async ValueTask<(Socket, Stream)> ConnectAsync(string host, int port, CancellationToken cancellationToken)
+ public static async ValueTask<Stream> ConnectAsync(string host, int port, CancellationToken cancellationToken)
{
// Rather than creating a new Socket and calling ConnectAsync on it, we use the static
// Socket.ConnectAsync with a SocketAsyncEventArgs, as we can then use Socket.CancelConnectAsync
// Configure the socket and return a stream for it.
Socket socket = saea.ConnectSocket;
socket.NoDelay = true;
- return (socket, new NetworkStream(socket, ownsSocket: true));
+ return new ExposedSocketNetworkStream(socket, ownsSocket: true);
}
catch (Exception error)
{
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Net.Sockets
+{
+ // TODO: Delete once https://github.com/dotnet/corefx/issues/35410 is available.
+ internal sealed class ExposedSocketNetworkStream : NetworkStream
+ {
+ public ExposedSocketNetworkStream(Socket socket, bool ownsSocket) : base(socket, ownsSocket) { }
+
+ public new Socket Socket => base.Socket;
+ }
+}
try
{
- Socket socket = null;
Stream stream = null;
switch (_kind)
{
case HttpConnectionKind.Http:
case HttpConnectionKind.Https:
case HttpConnectionKind.ProxyConnect:
- (socket, stream) = await ConnectHelper.ConnectAsync(_host, _port, cancellationToken).ConfigureAwait(false);
+ stream = await ConnectHelper.ConnectAsync(_host, _port, cancellationToken).ConfigureAwait(false);
break;
case HttpConnectionKind.Proxy:
- (socket, stream) = await ConnectHelper.ConnectAsync(_proxyUri.IdnHost, _proxyUri.Port, cancellationToken).ConfigureAwait(false);
+ stream = await ConnectHelper.ConnectAsync(_proxyUri.IdnHost, _proxyUri.Port, cancellationToken).ConfigureAwait(false);
break;
case HttpConnectionKind.ProxyTunnel:
break;
}
+ Socket socket = (stream as ExposedSocketNetworkStream)?.Socket; // TODO: Use NetworkStream when https://github.com/dotnet/corefx/issues/35410 is available.
+
TransportContext transportContext = null;
if (_kind == HttpConnectionKind.Https || _kind == HttpConnectionKind.SslProxyTunnel)
{