@@ -350,4 +350,100 @@ public class SocketChannelTest : XCTestCase {
350350
351351 try serverChannel. close ( ) . wait ( )
352352 }
353+
354+ public func testPendingConnectNotificationOrder( ) throws {
355+
356+ class NotificationOrderHandler : ChannelDuplexHandler {
357+ typealias InboundIn = Never
358+ typealias OutboundIn = Never
359+
360+ private var connectPromise : EventLoopPromise < Void > ?
361+
362+ public func channelInactive( ctx: ChannelHandlerContext ) {
363+ if let connectPromise = self . connectPromise {
364+ XCTAssertTrue ( connectPromise. futureResult. isFulfilled)
365+ } else {
366+ XCTFail ( " connect(...) not called before " )
367+ }
368+ }
369+
370+ public func connect( ctx: ChannelHandlerContext , to address: SocketAddress , promise: EventLoopPromise < Void > ? ) {
371+ XCTAssertNil ( self . connectPromise)
372+ self . connectPromise = promise
373+ ctx. connect ( to: address, promise: promise)
374+ }
375+
376+ func handlerAdded( ctx: ChannelHandlerContext ) {
377+ XCTAssertNil ( self . connectPromise)
378+ }
379+
380+ func handlerRemoved( ctx: ChannelHandlerContext ) {
381+ if let connectPromise = self . connectPromise {
382+ XCTAssertTrue ( connectPromise. futureResult. isFulfilled)
383+ } else {
384+ XCTFail ( " connect(...) not called before " )
385+ }
386+ }
387+ }
388+
389+ let group = MultiThreadedEventLoopGroup ( numThreads: 1 )
390+ defer { XCTAssertNoThrow ( try group. syncShutdownGracefully ( ) ) }
391+
392+ let serverChannel = try ServerBootstrap ( group: group) . bind ( host: " 127.0.0.1 " , port: 0 ) . wait ( )
393+ defer { XCTAssertNoThrow ( try serverChannel. close ( ) . wait ( ) ) }
394+
395+ let eventLoop = group. next ( )
396+ let promise : EventLoopPromise < Void > = eventLoop. newPromise ( )
397+
398+ class ConnectPendingSocket : Socket {
399+ let promise : EventLoopPromise < Void >
400+ init ( promise: EventLoopPromise < Void > ) throws {
401+ self . promise = promise
402+ try super. init ( protocolFamily: PF_INET, type: Posix . SOCK_STREAM)
403+ }
404+
405+ override func connect( to address: SocketAddress ) throws -> Bool {
406+ // We want to return false here to have a pending connect.
407+ _ = try super. connect ( to: address)
408+ self . promise. succeed ( result: ( ) )
409+ return false
410+ }
411+ }
412+
413+ let channel = try SocketChannel ( socket: ConnectPendingSocket ( promise: promise) , parent: nil , eventLoop: eventLoop as! SelectableEventLoop )
414+ let connectPromise : EventLoopPromise < Void > = channel. eventLoop. newPromise ( )
415+ let closePromise : EventLoopPromise < Void > = channel. eventLoop. newPromise ( )
416+
417+ closePromise. futureResult. whenComplete {
418+ XCTAssertTrue ( connectPromise. futureResult. isFulfilled)
419+ }
420+ connectPromise. futureResult. whenComplete {
421+ XCTAssertFalse ( closePromise. futureResult. isFulfilled)
422+ }
423+
424+ XCTAssertNoThrow ( try channel. pipeline. add ( handler: NotificationOrderHandler ( ) ) . wait ( ) )
425+
426+ // We need to call submit {...} here to ensure then {...} is called while on the EventLoop already to not have
427+ // a ECONNRESET sneak in.
428+ XCTAssertNoThrow ( try channel. eventLoop. submit {
429+ channel. register ( ) . map { ( ) -> Void in
430+ channel. connect ( to: serverChannel. localAddress!, promise: connectPromise)
431+ } . map { ( ) -> Void in
432+ XCTAssertFalse ( connectPromise. futureResult. isFulfilled)
433+ // The close needs to happen in the then { ... } block to ensure we close the channel
434+ // before we have the chance to register it for .write.
435+ channel. close ( promise: closePromise)
436+ }
437+ } . wait ( ) . wait ( ) as Void )
438+
439+ do {
440+ try connectPromise. futureResult. wait ( )
441+ XCTFail ( " Did not throw " )
442+ } catch let err as ChannelError where err == . alreadyClosed {
443+ // expected
444+ }
445+ XCTAssertNoThrow ( try closePromise. futureResult. wait ( ) )
446+ XCTAssertNoThrow ( try channel. closeFuture. wait ( ) )
447+ XCTAssertNoThrow ( try promise. futureResult. wait ( ) )
448+ }
353449}
0 commit comments