Skip to content
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
10 changes: 10 additions & 0 deletions Sources/NIOSSH/Child Channels/SSHChannelMultiplexer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ final class SSHChannelMultiplexer {

private var childChannelInitializer: SSHChildChannel.Initializer?

/// Whether new channels are allowed. Set to `false` once the parent channel is shut down at the TCP level.
private var canCreateNewChannels: Bool

init(delegate: SSHMultiplexerDelegate, allocator: ByteBufferAllocator, childChannelInitializer: SSHChildChannel.Initializer?) {
self.channels = [:]
self.channels.reserveCapacity(8)
Expand All @@ -39,13 +42,15 @@ final class SSHChannelMultiplexer {
self.nextChannelID = 0
self.allocator = allocator
self.childChannelInitializer = childChannelInitializer
self.canCreateNewChannels = true
}

// Time to clean up. We drop references to things that may be keeping us alive.
// Note that we don't drop the child channels because we expect that they'll be cleaning themselves up.
func parentHandlerRemoved() {
self.delegate = nil
self.childChannelInitializer = nil
self.canCreateNewChannels = false
}
}

Expand Down Expand Up @@ -160,6 +165,7 @@ extension SSHChannelMultiplexer {
}

func parentChannelInactive() {
self.canCreateNewChannels = false
for channel in self.channels.values {
channel.parentChannelInactive()
}
Expand All @@ -171,6 +177,10 @@ extension SSHChannelMultiplexer {
throw NIOSSHError.protocolViolation(protocolName: "channel", violation: "Opening new channel after channel shutdown")
}

guard self.canCreateNewChannels else {
throw NIOSSHError.tcpShutdown
}

// TODO: We need a better channel ID system. Maybe use indices into Arrays instead?
let channelID = self.nextChannelID
self.nextChannelID &+= 1
Expand Down
22 changes: 22 additions & 0 deletions Tests/NIOSSHTests/ChildChannelMultiplexerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1552,4 +1552,26 @@ final class ChildChannelMultiplexerTests: XCTestCase {
harness.multiplexer.parentChannelReadComplete()
XCTAssertEqual(readCounter.readCount, 2)
}

func testTCPCloseBeforeInitializer() throws {
let harness = self.harnessForbiddingInboundChannels()
defer {
harness.finish()
}

let childPromise: EventLoopPromise<Channel> = harness.eventLoop.makePromise()

var childPromiseError: Error?
childPromise.futureResult.whenFailure { error in childPromiseError = error }

// TCP Close
harness.multiplexer.parentChannelInactive()
harness.multiplexer.createChildChannel(childPromise, channelType: .session) { channel, _ in
channel.eventLoop.makeSucceededFuture(())
}
harness.eventLoop.run()

XCTAssertEqual(harness.flushedMessages.count, 0)
XCTAssertEqual((childPromiseError as? NIOSSHError?)??.type, .tcpShutdown)
}
}