runtime: Exception types for System.Net.Connections
Background and Motivation
We need an exception model for System.Net.Connections implementations, so that users can catch and react to common network events such as a connection reset.
Normally a SocketException
would be used, but an abstracted connection may not be using a Socket
and so that may not be appropriate. Furthermore it might confuse bugs in the connection’s code versus events parsed from the protocol that the connection would like to communicate.
This proposes an exception that implementations should throw, when appropriate.
Proposed API
The API is a reduction of the existing SocketException
and SocketError
model.
Option A
We define two new exception types:
- ConnectionException, with a ConnectionError enum, for exceptions from System.Net.Connections APIs
- NetworkException, with a NetworkError enum, for exceptions from Stream.Read/Write[Async].
(We don’t love the name NetworkException, but we don’t have a better alternative. This is intended to be thrown from connection-oriented Streams like NetworkStream that would be returned from System.Net.Connections APIs. Note, however, that these exceptions are thrown regardless whether the Stream was created via System.Net.Connections or not – we don’t want different semantics based on how the Stream was created.)
namespace System.Net.Connections
{
public class ConnectionException : IOException
{
public ConnectionException(string message, ConnectionError connectionError = ConnectionError.Unknown, Exception innerException = null);
// serialization constructor as usual
public ConnectionError ConnectionError { get; }
}
public enum ConnectionError
{
Unknown,
AddressInUse, // SocketError.AddressAlreadyInUse
InvalidAddress, // SocketError.AddressFamilyNotSupported, SocketError.AddressNotAvailable, etc
ConnectionRefused, // SocketError.ConnectionRefused
HostNotFound, // SocketError.HostNotFound, SocketError.HostUnreachable, etc
}
}
namespace System.IO
{
public class NetworkException : IOException
{
public NetworkException(string message, NetworkError error, Exception innerException = null);
// serialization constructor as usual
public NetworkError NetworkError { get; }
}
public enum NetworkError
{
Aborted,
Reset,
}
}
Option B
We define one new exception type: NetworkException, with NetworkError enum. System.Net.Connections APIs are expected to throw this exception, as are Stream.Read/Write[Async] in the case of a connection abort or reset. The various differentiated conditions from Option A are merged into a single NetworkError enum.
(Again, we don’t love the name; see comments under Option A.)
namespace System.IO
{
public class NetworkException : IOException
{
public NetworkException(string message, NetworkError error, Exception innerException = null);
// serialization constructor as usual
public NetworkError NetworkError { get; }
}
public enum NetworkError
{
Unknown,
AddressInUse, // SocketError.AddressAlreadyInUse
InvalidAddress, // SocketError.AddressFamilyNotSupported, SocketError.AddressNotAvailable, etc
ConnectionRefused, // SocketError.ConnectionRefused
HostNotFound, // SocketError.HostNotFound, SocketError.HostUnreachable, etc
Aborted,
Reset,
}
}
Option C
We define no new exception type, but instead add an IOError enum property to IOException. System.Net.Connections APIs are expected to throw IOException with an appropriate IOError value, when applicable. Stream.Read/Write[Async] will continue to throw plain IOException, but set the IOError to the appropriate value in the case of a connection abort or reset.
The set of defined IOError values is currently focused on connection-oriented streams, but could be extended to other types of Streams (e.g. FileStream, MemoryStream) in the future.
namespace System.IO
{
public class IOException : Exception
{
public IOException(string message, IOError error, Exception innerException = null);
public IOError IOError { get; }
}
public enum IOError
{
Unknown,
AddressInUse, // SocketError.AddressAlreadyInUse
InvalidAddress, // SocketError.AddressFamilyNotSupported, SocketError.AddressNotAvailable, etc
ConnectionRefused, // SocketError.ConnectionRefused
HostNotFound, // SocketError.HostNotFound, SocketError.HostUnreachable, etc
Aborted,
Reset,
}
}
Notes
NetworkStream
currently throws IOException
and would be updated to throw this.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 34 (34 by maintainers)
Why? The moment you want to do the same thing for multiple values, it’s simpler and cleaner to use a filter.
Let me try to capture the above conversation as well as some offline discussion.
Goals
Currently, ASP.NET detects these conditions by looking for specific SocketError codes on SocketException. Since System.Net.Connections is a general-purpose abstraction, we need to define a way to surface these conditions independent from SocketException.
Currently, we are often throwing IOException from these APIs (which may be fine), but in some cases we may be throwing domain-specific exceptions (which is not fine). We need to codify what exceptions are expected here.
Option A
We define two new exception types:
(We don’t love the name NetworkException, but we don’t have a better alternative. This is intended to be thrown from connection-oriented Streams like NetworkStream that would be returned from System.Net.Connections APIs. Note, however, that these exceptions are thrown regardless whether the Stream was created via System.Net.Connections or not – we don’t want different semantics based on how the Stream was created.)
Option B
We define one new exception type: NetworkException, with NetworkError enum. System.Net.Connections APIs are expected to throw this exception, as are Stream.Read/Write[Async] in the case of a connection abort or reset. The various differentiated conditions from Option A are merged into a single NetworkError enum.
(Again, we don’t love the name; see comments under Option A.)
Option C
We define no new exception type, but instead add an IOError enum property to IOException. System.Net.Connections APIs are expected to throw IOException with an appropriate IOError value, when applicable. Stream.Read/Write[Async] will continue to throw plain IOException, but set the IOError to the appropriate value in the case of a connection abort or reset.
The set of defined IOError values is currently focused on connection-oriented streams, but could be extended to other types of Streams (e.g. FileStream, MemoryStream) in the future.
I think this captures all the discussion so far; comments welcome. If there’s general agreement that these are the options worth considering, I’ll mark as ready for review and we can have an API review discussion of this.