@@ -2019,4 +2019,81 @@ public class ChannelTests: XCTestCase {
20192019 try allDone. futureResult. wait ( )
20202020 XCTAssertNoThrow ( try sc. syncCloseAcceptingAlreadyClosed ( ) )
20212021 }
2022+
2023+ func testSocketFailingAsyncCorrectlyTearsTheChannelDownAndDoesntCrash( ) throws {
2024+ // regression test for #302
2025+ enum DummyError : Error { case dummy }
2026+ class SocketFailingAsyncConnect : Socket {
2027+ init ( ) throws {
2028+ try super. init ( protocolFamily: PF_INET, type: Posix . SOCK_STREAM, setNonBlocking: true )
2029+ }
2030+
2031+ override func connect( to address: SocketAddress ) throws -> Bool {
2032+ _ = try super. connect ( to: address)
2033+ return false
2034+ }
2035+
2036+ override func finishConnect( ) throws {
2037+ throw DummyError . dummy
2038+ }
2039+ }
2040+
2041+ class VerifyThingsAreRightHandler : ChannelInboundHandler {
2042+ typealias InboundIn = Never
2043+ private let allDone : EventLoopPromise < Void >
2044+ enum State {
2045+ case fresh
2046+ case registered
2047+ case unregistered
2048+ }
2049+ private var state : State = . fresh
2050+
2051+ init ( allDone: EventLoopPromise < Void > ) {
2052+ self . allDone = allDone
2053+ }
2054+ deinit { XCTAssertEqual ( . unregistered, self . state) }
2055+
2056+ func channelActive( ctx: ChannelHandlerContext ) { XCTFail ( " should never become active " ) }
2057+
2058+ func channelRead( ctx: ChannelHandlerContext , data: NIOAny ) { XCTFail ( " should never read " ) }
2059+
2060+ func channelReadComplete( ctx: ChannelHandlerContext ) { XCTFail ( " should never readComplete " ) }
2061+
2062+ func errorCaught( ctx: ChannelHandlerContext , error: Error ) { XCTFail ( " pipeline shouldn't be told about connect error " ) }
2063+
2064+ func channelRegistered( ctx: ChannelHandlerContext ) {
2065+ XCTAssertEqual ( . fresh, self . state)
2066+ self . state = . registered
2067+ }
2068+
2069+ func channelUnregistered( ctx: ChannelHandlerContext ) {
2070+ XCTAssertEqual ( . registered, self . state)
2071+ self . state = . unregistered
2072+ self . allDone. succeed ( result: ( ) )
2073+ }
2074+ }
2075+ let group = MultiThreadedEventLoopGroup ( numThreads: 2 )
2076+ defer {
2077+ XCTAssertNoThrow ( try group. syncShutdownGracefully ( ) )
2078+ }
2079+ let sc = try SocketChannel ( socket: SocketFailingAsyncConnect ( ) , eventLoop: group. next ( ) as! SelectableEventLoop )
2080+
2081+ let serverChannel = try ServerBootstrap ( group: group. next ( ) )
2082+ . bind ( host: " 127.0.0.1 " , port: 0 )
2083+ . wait ( )
2084+ defer {
2085+ XCTAssertNoThrow ( try serverChannel. syncCloseAcceptingAlreadyClosed ( ) )
2086+ }
2087+
2088+ let allDone : EventLoopPromise < Void > = group. next ( ) . newPromise ( )
2089+ XCTAssertNoThrow ( try sc. eventLoop. submit {
2090+ sc. pipeline. add ( handler: VerifyThingsAreRightHandler ( allDone: allDone) ) . then {
2091+ sc. register ( ) . then {
2092+ sc. connect ( to: serverChannel. localAddress!)
2093+ }
2094+ }
2095+ } . wait ( ) )
2096+ XCTAssertNoThrow ( try allDone. futureResult. wait ( ) )
2097+ XCTAssertNoThrow ( try sc. syncCloseAcceptingAlreadyClosed ( ) )
2098+ }
20222099}
0 commit comments