Project

General

Profile

Feature #21642

Updated by ioquatix (Samuel Williams) 1 day ago

Currently, different IO implementations in Ruby raise inconsistent exception types when a connection is reset or broken. 

 For example: 

 ```ruby 
 # Plain TCP socket: 
 socket.read_nonblock(1024) 
 # => Errno::ECONNRESET 

 # SSL socket: 
 ssl_socket.read_nonblock(1024) 
 # => OpenSSL::SSL::SSLError: SSL_read: unexpected eof while reading 
 ``` 

 Both represent a *connection reset by peer*, but the errors differ significantly in type and message. 
 This inconsistency makes it difficult to handle connection-level errors generically across IO types. 

 Similarly, `EPIPE` is used in some contexts to signal a *broken connection*, but again, the representation and message differ between IO classes. 

 ### Proposal 

 Introduce explicit subclasses of the corresponding system errors as part of Ruby’s standard IO interface: 

 ```ruby 
 class IO 
   class ConnectionResetError ConnectionReset < Errno::ECONNRESET; end 
   class BrokenPipeError BrokenPipe < Errno::EPIPE; end 
 end 
 ``` 

 Then, standardize the Ruby I/O ecosystem (including OpenSSL) to raise these subclasses instead of raw system errors or library-specific error wrappers. 

 This would establish a consistent, well-defined public interface for handling connection-level failures. 

 ### Motivation 

 * **Consistency:** Users can handle `IO::ConnectionResetError` `IO::ConnectionReset` across `IO`, `TCPSocket`, `OpenSSL::SSL::SSLSocket`, and other IO-like objects. 
 * **Clarity:** The name clearly expresses a high-level semantic (“connection reset”) rather than a low-level system error. 
 * **Extensibility:** Other Ruby IO implementations (custom sockets, pipes, etc.) can follow the same convention. 
 * **Backwards Compatibility:** Because `IO::ConnectionResetError `IO::ConnectionReset < Errno::ECONNRESET`, existing rescue clauses continue to work: 

   ```ruby 
   rescue Errno::ECONNRESET 
     # still catches it 
   end 
   ``` 

 ### Examples 

 ```ruby 
 begin 
   io.read_nonblock(1024) 
 rescue IO::ConnectionResetError IO::ConnectionReset 
   puts "Connection was reset by peer." 
 rescue IO::BrokenPipe 
   puts "Connection was broken (EPIPE)." 
 end 
 ``` 

 ### Impact on existing code 

 * Minimal to none. 
 * Existing code that rescues `Errno::ECONNRESET` or `Errno::EPIPE` will continue to function. 
 * Future code gains a more semantic and portable way to handle these common failure modes. 

Back