Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add NetworkException #40344

Merged
merged 17 commits into from
Aug 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/libraries/Common/src/System/Net/NetworkErrorHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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);
}
}
}
2 changes: 2 additions & 0 deletions src/libraries/System.Net.Http/src/System.Net.Http.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@
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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,25 @@

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;

namespace System.Net.Connections
{
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 override IConnectionProperties ConnectionProperties => this;

public SocketConnection(Socket socket)
{
_stream = new SocketConnectionNetworkStream(socket, this);
_stream = new NetworkStream(socket, ownsSocket: true);
}

protected override ValueTask CloseAsyncCore(ConnectionCloseMethod method, CancellationToken cancellationToken)
Expand All @@ -38,7 +40,11 @@ protected override ValueTask CloseAsyncCore(ConnectionCloseMethod method, Cancel
_stream.Socket.Dispose();
}

_stream.DisposeWithoutClosingConnection();
_stream.Dispose();
}
catch (SocketException socketException)
{
return ValueTask.FromException(ExceptionDispatchInfo.SetCurrentStackTrace(NetworkErrorHelper.MapSocketException(socketException)));
}
catch (Exception ex)
{
Expand All @@ -61,41 +67,5 @@ bool IConnectionProperties.TryGet(Type propertyKey, [NotNullWhen(true)] out obje
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;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ public virtual async ValueTask<Connection> EstablishConnectionAsync(HttpRequestM
socket.NoDelay = true;
return new SocketConnection(socket);
}
catch (SocketException socketException)
{
socket.Dispose();
throw NetworkErrorHelper.MapSocketException(socketException);
}
catch
{
socket.Dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ public async Task CustomConnectionFactory_SyncRequest_Fails()

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

Expand Down
17 changes: 17 additions & 0 deletions src/libraries/System.Net.Primitives/ref/System.Net.Primitives.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,23 @@ public abstract partial class TransportContext
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
{
Expand Down
21 changes: 21 additions & 0 deletions src/libraries/System.Net.Primitives/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,25 @@
<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>
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
<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" />
Expand Down
30 changes: 30 additions & 0 deletions src/libraries/System.Net.Primitives/src/System/Net/NetworkError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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
geoffkizer marked this conversation as resolved.
Show resolved Hide resolved
{
/// <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,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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)
geoffkizer marked this conversation as resolved.
Show resolved Hide resolved
: 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
geoffkizer marked this conversation as resolved.
Show resolved Hide resolved
{
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
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<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" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@
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 -->
Expand Down
Loading