--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net.Sockets;
+
+namespace System.Net
+{
+ internal static class NetworkErrorHelper
+ {
+ internal static NetworkException MapSocketException(SocketException socketException)
+ {
+ NetworkError error = socketException.SocketErrorCode switch
+ {
+ SocketError.AddressAlreadyInUse => NetworkError.EndPointInUse,
+ SocketError.HostNotFound => NetworkError.HostNotFound,
+ SocketError.ConnectionRefused => NetworkError.ConnectionRefused,
+ SocketError.OperationAborted => NetworkError.OperationAborted,
+ SocketError.ConnectionAborted => NetworkError.ConnectionAborted,
+ SocketError.ConnectionReset => NetworkError.ConnectionReset,
+ _ => NetworkError.Unknown
+ };
+
+ return new NetworkException(error, socketException);
+ }
+ }
+}
Link="Common\System\IO\StreamHelpers.CopyValidation.cs" />
<Compile Include="$(CommonPath)System\Net\Security\SslClientAuthenticationOptionsExtensions.cs"
Link="Common\System\Net\Security\SslClientAuthenticationOptionsExtensions.cs" />
+ <Compile Include="$(CommonPath)System\Net\NetworkErrorHelper.cs"
+ Link="Common\System\Net\NetworkErrorHelper.cs" />
<Compile Include="$(CommonPath)System\IO\DelegatingStream.cs"
Link="Common\System\IO\DelegatingStream.cs" />
<Compile Include="$(CommonPath)System\IO\ReadOnlyMemoryStream.cs"
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Net.Http;
using System.Net.Sockets;
+using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
{
internal sealed class SocketConnection : Connection, IConnectionProperties
{
- private readonly SocketConnectionNetworkStream _stream;
+ private readonly NetworkStream _stream;
public override EndPoint? RemoteEndPoint => _stream.Socket.RemoteEndPoint;
public override EndPoint? LocalEndPoint => _stream.Socket.LocalEndPoint;
public SocketConnection(Socket socket)
{
- _stream = new SocketConnectionNetworkStream(socket, this);
+ _stream = new NetworkStream(socket, ownsSocket: true);
}
protected override ValueTask CloseAsyncCore(ConnectionCloseMethod method, CancellationToken cancellationToken)
_stream.Socket.Dispose();
}
- _stream.DisposeWithoutClosingConnection();
+ _stream.Dispose();
+ }
+ catch (SocketException socketException)
+ {
+ return ValueTask.FromException(ExceptionDispatchInfo.SetCurrentStackTrace(NetworkErrorHelper.MapSocketException(socketException)));
}
catch (Exception ex)
{
property = null;
return false;
}
-
- // This is done to couple disposal of the SocketConnection and the NetworkStream.
- private sealed class SocketConnectionNetworkStream : NetworkStream
- {
- private readonly SocketConnection _connection;
-
- public SocketConnectionNetworkStream(Socket socket, SocketConnection connection) : base(socket, ownsSocket: true)
- {
- _connection = connection;
- }
-
- public void DisposeWithoutClosingConnection()
- {
- base.Dispose(true);
- }
-
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- // This will call base.Dispose().
- _connection.Dispose();
- }
- else
- {
- base.Dispose(disposing);
- }
- }
-
- public override ValueTask DisposeAsync()
- {
- // This will call base.Dispose().
- Dispose(true);
- return default;
- }
- }
}
}
socket.NoDelay = true;
return new SocketConnection(socket);
}
+ catch (SocketException socketException)
+ {
+ socket.Dispose();
+ throw NetworkErrorHelper.MapSocketException(socketException);
+ }
catch
{
socket.Dispose();
using HttpClient client = CreateHttpClient(handler);
- await Assert.ThrowsAnyAsync<HttpRequestException>(() => client.GetStringAsync($"http://{Guid.NewGuid():N}.com/foo"));
+ HttpRequestException e = await Assert.ThrowsAnyAsync<HttpRequestException>(() => client.GetStringAsync($"http://{Guid.NewGuid():N}.com/foo"));
+ NetworkException networkException = Assert.IsType<NetworkException>(e.InnerException);
+ Assert.Equal(NetworkError.HostNotFound, networkException.NetworkError);
}
}
protected TransportContext() { }
public abstract System.Security.Authentication.ExtendedProtection.ChannelBinding? GetChannelBinding(System.Security.Authentication.ExtendedProtection.ChannelBindingKind kind);
}
+ public enum NetworkError : int
+ {
+ Unknown = 0,
+ EndPointInUse,
+ HostNotFound,
+ ConnectionRefused,
+ OperationAborted,
+ ConnectionAborted,
+ ConnectionReset,
+ }
+ public class NetworkException : System.IO.IOException
+ {
+ public NetworkException(NetworkError error, Exception? innerException = null) { }
+ public NetworkException(string message, NetworkError error, Exception? innerException = null) { }
+ protected NetworkException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) { }
+ public NetworkError NetworkError { get { throw null; } }
+ }
}
namespace System.Net.Cache
{
<data name="bad_endpoint_string" xml:space="preserve">
<value>An invalid IPEndPoint was specified.</value>
</data>
+ <data name="networkerror_unknown" xml:space="preserve">
+ <value>An unknown network error occurred.</value>
+ </data>
+ <data name="networkerror_addressinuse" xml:space="preserve">
+ <value>The requested EndPoint is already in use.</value>
+ </data>
+ <data name="networkerror_connectionrefused" xml:space="preserve">
+ <value>No connection could be made because the remote host actively refused it.</value>
+ </data>
+ <data name="networkerror_hostnotfound" xml:space="preserve">
+ <value>No such host is known.</value>
+ </data>
+ <data name="networkerror_operationaborted" xml:space="preserve">
+ <value>The operation was aborted by the user.</value>
+ </data>
+ <data name="networkerror_connectionaborted" xml:space="preserve">
+ <value>The connection was aborted by the local host.</value>
+ </data>
+ <data name="networkerror_connectionreset" xml:space="preserve">
+ <value>The connection was forcibly closed by the remote host.</value>
+ </data>
</root>
<Compile Include="System\Net\IWebProxy.cs" />
<Compile Include="System\Net\NetEventSource.Primitives.cs" />
<Compile Include="System\Net\NetworkCredential.cs" />
+ <Compile Include="System\Net\NetworkException.cs" />
+ <Compile Include="System\Net\NetworkError.cs" />
<Compile Include="System\Net\TransportContext.cs" />
<Compile Include="System\Net\SocketException.cs" />
<Compile Include="System\Net\SecureProtocols\NegotiateEnumTypes.cs" />
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Net
+{
+ /// <summary>Defines a set of error codes for use with <see cref='System.Net.NetworkException'/>.</summary>
+ public enum NetworkError : int
+ {
+ /// <summary>An unknown network error occurred.</summary>
+ Unknown = 0,
+
+ /// <summary>The requested EndPoint is already in use.</summary>
+ EndPointInUse,
+
+ /// <summary>No such host is known.</summary>
+ HostNotFound,
+
+ /// <summary>No connection could be made because the remote host actively refused it.</summary>
+ ConnectionRefused,
+
+ /// <summary>The operation was aborted by the user.</summary>
+ OperationAborted,
+
+ /// <summary>The connection was aborted by the local host.</summary>
+ ConnectionAborted,
+
+ /// <summary>The connection was forcibly closed by the remote host.</summary>
+ ConnectionReset,
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using System.Runtime.Serialization;
+
+namespace System.Net
+{
+ /// <summary>Provides socket exceptions to the application.</summary>
+ [Serializable]
+ public class NetworkException : IOException
+ {
+ /// <summary>Creates a new instance of the <see cref='System.Net.NetworkException'/> class with the specified error code.</summary>
+ public NetworkException(NetworkError error, Exception? innerException = null)
+ : this(GetExceptionMessage(error), error, innerException) {}
+
+ /// <summary>Creates a new instance of the <see cref='System.Net.NetworkException'/> class with the specified error code and message.</summary>
+ public NetworkException(string message, NetworkError error, Exception? innerException = null)
+ : base(message, innerException)
+ {
+ NetworkError = error;
+ }
+
+ /// <summary>Creates a new instance of the <see cref='System.Net.NetworkException'/> from serialized data.</summary>
+ protected NetworkException(SerializationInfo serializationInfo, StreamingContext streamingContext)
+ : base(serializationInfo, streamingContext)
+ {
+ NetworkError = (NetworkError)serializationInfo.GetInt32("NetworkError");
+ }
+
+ /// <summary>Populates the serialization data for this object.</summary>
+ public override void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
+ {
+ base.GetObjectData(serializationInfo, streamingContext);
+ serializationInfo.AddValue("NetworkError", (int)NetworkError);
+ }
+
+ /// <summary>Returns the specific kind of error.</summary>
+ public NetworkError NetworkError { get; }
+
+ private static string GetExceptionMessage(NetworkError error) => error switch
+ {
+ NetworkError.EndPointInUse => SR.networkerror_addressinuse,
+ NetworkError.HostNotFound => SR.networkerror_hostnotfound,
+ NetworkError.ConnectionRefused => SR.networkerror_connectionrefused,
+ NetworkError.ConnectionAborted => SR.networkerror_connectionaborted,
+ NetworkError.ConnectionReset => SR.networkerror_connectionreset,
+ NetworkError.OperationAborted => SR.networkerror_operationaborted,
+ _ => SR.networkerror_unknown
+ };
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.Net.Primitives.Functional.Tests
+{
+ public static class NetworkExceptionTest
+ {
+ [Fact]
+ public static void Create_AllErrorCodes_Success()
+ {
+ foreach (NetworkError error in Enum.GetValues(typeof(NetworkError)))
+ {
+ NetworkException e = new NetworkException(error);
+ Assert.Equal(error, e.NetworkError);
+ Assert.Null(e.InnerException);
+ Assert.NotNull(e.Message);
+ }
+ }
+
+ [Fact]
+ public static void Create_InnerExceptionAndMessage_Success()
+ {
+ const string Message = "Hello";
+ Exception inner = new Exception();
+
+ NetworkException e = new NetworkException(Message, NetworkError.Unknown, inner);
+
+ Assert.Equal(inner, e.InnerException);
+ Assert.Equal(Message, e.Message);
+ }
+ }
+}
<Compile Include="IPEndPointParsing.cs" />
<Compile Include="IPEndPointTest.cs" />
<Compile Include="NetworkCredentialTest.cs" />
+ <Compile Include="NetworkExceptionTest.cs" />
<Compile Include="SocketAddressTest.cs" />
<Compile Include="LoggingTest.cs" />
<Compile Include="RequestCachePolicyTest.cs" />
Link="Common\System\Net\Sockets\ProtocolType.cs" />
<Compile Include="$(CommonPath)System\Net\Sockets\SocketType.cs"
Link="Common\System\Net\Sockets\SocketType.cs" />
+ <Compile Include="$(CommonPath)System\Net\NetworkErrorHelper.cs"
+ Link="Common\System\Net\NetworkErrorHelper.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
<!-- Windows: CoreCLR -->
// allowing non-blocking sockets could result in non-deterministic failures from those
// operations. A developer that requires using NetworkStream with a non-blocking socket can
// temporarily flip Socket.Blocking as a workaround.
- throw new IOException(SR.net_sockets_blocking);
+ throw GetCustomNetworkException(SR.net_sockets_blocking);
}
if (!socket.Connected)
{
- throw new IOException(SR.net_notconnected);
+ throw GetCustomNetworkException(SR.net_notconnected);
}
if (socket.SocketType != SocketType.Stream)
{
- throw new IOException(SR.net_notstream);
+ throw GetCustomNetworkException(SR.net_notstream);
}
_streamSocket = socket;
{
return _streamSocket.Receive(buffer, offset, size, 0);
}
+ catch (SocketException socketException)
+ {
+ throw NetworkErrorHelper.MapSocketException(socketException);
+ }
catch (Exception exception) when (!(exception is OutOfMemoryException))
{
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
+ throw GetCustomNetworkException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
}
}
if (errorCode != SocketError.Success)
{
var exception = new SocketException((int)errorCode);
- throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
+ throw NetworkErrorHelper.MapSocketException(exception);
}
return bytesRead;
}
// after ALL the requested number of bytes was transferred.
_streamSocket.Send(buffer, offset, size, SocketFlags.None);
}
+ catch (SocketException socketException)
+ {
+ throw NetworkErrorHelper.MapSocketException(socketException);
+ }
catch (Exception exception) when (!(exception is OutOfMemoryException))
{
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
+ throw GetCustomNetworkException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
}
}
if (errorCode != SocketError.Success)
{
var exception = new SocketException((int)errorCode);
- throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
+ throw NetworkErrorHelper.MapSocketException(exception);
}
}
callback,
state);
}
+ catch (SocketException socketException)
+ {
+ throw NetworkErrorHelper.MapSocketException(socketException);
+ }
catch (Exception exception) when (!(exception is OutOfMemoryException))
{
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
+ throw GetCustomNetworkException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
}
}
{
return _streamSocket.EndReceive(asyncResult);
}
+ catch (SocketException socketException)
+ {
+ throw NetworkErrorHelper.MapSocketException(socketException);
+ }
catch (Exception exception) when (!(exception is OutOfMemoryException))
{
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
+ throw GetCustomNetworkException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
}
}
callback,
state);
}
+ catch (SocketException socketException)
+ {
+ throw NetworkErrorHelper.MapSocketException(socketException);
+ }
catch (Exception exception) when (!(exception is OutOfMemoryException))
{
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
+ throw GetCustomNetworkException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
}
}
{
_streamSocket.EndSend(asyncResult);
}
+ catch (SocketException socketException)
+ {
+ throw NetworkErrorHelper.MapSocketException(socketException);
+ }
catch (Exception exception) when (!(exception is OutOfMemoryException))
{
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
+ throw GetCustomNetworkException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
}
}
fromNetworkStream: true,
cancellationToken).AsTask();
}
+ catch (SocketException socketException)
+ {
+ throw NetworkErrorHelper.MapSocketException(socketException);
+ }
catch (Exception exception) when (!(exception is OutOfMemoryException))
{
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
+ throw GetCustomNetworkException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
}
}
fromNetworkStream: true,
cancellationToken: cancellationToken);
}
+ catch (SocketException socketException)
+ {
+ throw NetworkErrorHelper.MapSocketException(socketException);
+ }
catch (Exception exception) when (!(exception is OutOfMemoryException))
{
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
+ throw GetCustomNetworkException(SR.Format(SR.net_io_readfailure, exception.Message), exception);
}
}
SocketFlags.None,
cancellationToken).AsTask();
}
+ catch (SocketException socketException)
+ {
+ throw NetworkErrorHelper.MapSocketException(socketException);
+ }
catch (Exception exception) when (!(exception is OutOfMemoryException))
{
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
+ throw GetCustomNetworkException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
}
}
SocketFlags.None,
cancellationToken);
}
+ catch (SocketException socketException)
+ {
+ throw NetworkErrorHelper.MapSocketException(socketException);
+ }
catch (Exception exception) when (!(exception is OutOfMemoryException))
{
- // Some sort of error occurred on the socket call,
- // set the SocketException as InnerException and throw.
- throw new IOException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
+ throw GetCustomNetworkException(SR.Format(SR.net_io_writefailure, exception.Message), exception);
}
}
void ThrowObjectDisposedException() => throw new ObjectDisposedException(GetType().FullName);
}
+
+ private static NetworkException GetCustomNetworkException(string message, Exception? innerException = null)
+ {
+ return new NetworkException(message, NetworkError.Unknown, innerException);
+ }
}
}
Debug.Assert(saea.BufferList == null);
saea.SetBuffer(buffer);
saea.SocketFlags = socketFlags;
- saea.WrapExceptionsInIOExceptions = fromNetworkStream;
+ saea.WrapExceptionsInNetworkExceptions = fromNetworkStream;
return saea.ReceiveAsync(this, cancellationToken);
}
Debug.Assert(saea.BufferList == null);
saea.SetBuffer(MemoryMarshal.AsMemory(buffer));
saea.SocketFlags = socketFlags;
- saea.WrapExceptionsInIOExceptions = false;
+ saea.WrapExceptionsInNetworkExceptions = false;
return saea.SendAsync(this, cancellationToken);
}
Debug.Assert(saea.BufferList == null);
saea.SetBuffer(MemoryMarshal.AsMemory(buffer));
saea.SocketFlags = socketFlags;
- saea.WrapExceptionsInIOExceptions = true;
+ saea.WrapExceptionsInNetworkExceptions = true;
return saea.SendAsyncForNetworkStream(this, cancellationToken);
}
_isReadForCaching = isReceiveForCaching;
}
- public bool WrapExceptionsInIOExceptions { get; set; }
+ public bool WrapExceptionsInNetworkExceptions { get; set; }
private void Release()
{
e = ExceptionDispatchInfo.SetCurrentStackTrace(e);
}
- return WrapExceptionsInIOExceptions ?
- new IOException(SR.Format(SR.net_io_readfailure, e.Message), e) :
+ return WrapExceptionsInNetworkExceptions ?
+ NetworkErrorHelper.MapSocketException((SocketException)e) :
e;
}
}
}
[Fact]
- public void Ctor_NotConnected_ThrowsIOException()
+ public void Ctor_NotConnected_ThrowsNetworkException()
{
- Assert.Throws<IOException>(() => new NetworkStream(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)));
+ Assert.Throws<NetworkException>(() => new NetworkStream(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)));
}
[Fact]
- public async Task Ctor_NotStream_ThrowsIOException()
+ public async Task Ctor_NotStream_ThrowsNetworkException()
{
using (Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
listener.Bind(new IPEndPoint(IPAddress.Loopback, 0));
await client.ConnectAsync(new IPEndPoint(IPAddress.Loopback, ((IPEndPoint)listener.LocalEndPoint).Port));
- Assert.Throws<IOException>(() => new NetworkStream(client));
+ Assert.Throws<NetworkException>(() => new NetworkStream(client));
}
}
[Fact]
- public async Task Ctor_NonBlockingSocket_ThrowsIOException()
+ public async Task Ctor_NonBlockingSocket_ThrowsNetworkException()
{
using (Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
using (Socket server = await acceptTask)
{
server.Blocking = false;
- Assert.Throws<IOException>(() => new NetworkStream(server));
+ Assert.Throws<NetworkException>(() => new NetworkStream(server));
}
}
}
}
else if (ownsSocket)
{
- Assert.IsType<IOException>(e);
+ Assert.IsType<NetworkException>(e);
}
else
{
}
[Fact]
- public async Task DisposeSocketDirectly_ReadWriteThrowIOException()
+ public async Task DisposeSocketDirectly_ReadWriteThrowNetworkException()
{
using (Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
using (Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
serverSocket.Dispose();
- Assert.Throws<IOException>(() => server.Read(new byte[1], 0, 1));
- Assert.Throws<IOException>(() => server.Write(new byte[1], 0, 1));
+ Assert.Throws<NetworkException>(() => server.Read(new byte[1], 0, 1));
+ Assert.Throws<NetworkException>(() => server.Write(new byte[1], 0, 1));
- Assert.Throws<IOException>(() => server.BeginRead(new byte[1], 0, 1, null, null));
- Assert.Throws<IOException>(() => server.BeginWrite(new byte[1], 0, 1, null, null));
+ Assert.Throws<NetworkException>(() => server.BeginRead(new byte[1], 0, 1, null, null));
+ Assert.Throws<NetworkException>(() => server.BeginWrite(new byte[1], 0, 1, null, null));
- Assert.Throws<IOException>(() => { server.ReadAsync(new byte[1], 0, 1); });
- Assert.Throws<IOException>(() => { server.WriteAsync(new byte[1], 0, 1); });
+ Assert.Throws<NetworkException>(() => { server.ReadAsync(new byte[1], 0, 1); });
+ Assert.Throws<NetworkException>(() => { server.WriteAsync(new byte[1], 0, 1); });
}
}
}
{
await RunWithConnectedNetworkStreamsAsync((server, _) =>
{
- Assert.Throws<IOException>(() => server.EndRead(Task.CompletedTask));
- Assert.Throws<IOException>(() => server.EndWrite(Task.CompletedTask));
+ Assert.Throws<NetworkException>(() => server.EndRead(Task.CompletedTask));
+ Assert.Throws<NetworkException>(() => server.EndWrite(Task.CompletedTask));
return Task.CompletedTask;
});
}
Assert.Equal(-1, server.ReadTimeout);
server.ReadTimeout = 1;
- Assert.ThrowsAny<IOException>(() => server.Read(new byte[1], 0, 1));
+ Assert.ThrowsAny<NetworkException>(() => server.Read(new byte[1], 0, 1));
return Task.CompletedTask;
});
// before that takes effect, it may also complete as aborted.
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
Assert.True(
- (isWindows && e is IOException) ||
- (!isWindows && (e == null || e is IOException)),
+ (isWindows && e is NetworkException) ||
+ (!isWindows && (e == null || e is NetworkException)),
$"Got unexpected exception: {e?.ToString() ?? "(null)"}");
// Copying after disposing the stream