Skip to content

Commit 65ebab9

Browse files
authored
stream channel initialiser: allow closed & failed (#183)
Motivation: Previously, if we failed in the stream channel initialiser and the stream channel was closed before finishing the stream channel initialiser, we would crash on an assertion because `closedWhileOpen` correctly asserted that it has nothing to do and shouldn't be called. Modification: Apart from `.idle` also stop calling `closedWhileOpen` in `.closed` and `.localActive` because the network actually doesn't think this channel is open. Result: - Fewer crashes - fixes rdar://59342916
1 parent 73be3a1 commit 65ebab9

File tree

3 files changed

+32
-3
lines changed

3 files changed

+32
-3
lines changed

Sources/NIOHTTP2/HTTP2StreamChannel.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,13 @@ final class HTTP2StreamChannel: Channel, ChannelCore {
169169
}
170170

171171
f.whenFailure { (error: Error) in
172-
if self.state != .idle {
173-
self.closedWhileOpen()
174-
} else {
172+
switch self.state {
173+
case .idle, .localActive, .closed:
174+
// The stream isn't open on the network, nothing to close.
175175
self.errorEncountered(error: error)
176+
case .remoteActive, .active, .closing, .closingNeverActivated:
177+
// In all of these states the stream is still on the network and we may need to take action.
178+
self.closedWhileOpen()
176179
}
177180

178181
promise?.fail(error)

Tests/NIOHTTP2Tests/HTTP2StreamMultiplexerTests+XCTest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ extension HTTP2StreamMultiplexerTests {
7070
("testMultiplexerModifiesStreamChannelWritabilityBasedOnFixedSizeTokens", testMultiplexerModifiesStreamChannelWritabilityBasedOnFixedSizeTokens),
7171
("testMultiplexerModifiesStreamChannelWritabilityBasedOnParentChannelWritability", testMultiplexerModifiesStreamChannelWritabilityBasedOnParentChannelWritability),
7272
("testMultiplexerModifiesStreamChannelWritabilityBasedOnFixedSizeTokensAndChannelWritability", testMultiplexerModifiesStreamChannelWritabilityBasedOnFixedSizeTokensAndChannelWritability),
73+
("testStreamChannelToleratesFailingInitialier", testStreamChannelToleratesFailingInitialier),
7374
]
7475
}
7576
}

Tests/NIOHTTP2Tests/HTTP2StreamMultiplexerTests.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,4 +1773,29 @@ final class HTTP2StreamMultiplexerTests: XCTestCase {
17731773
self.channel.pipeline.fireChannelWritabilityChanged()
17741774
XCTAssertTrue(childChannel.isWritable)
17751775
}
1776+
1777+
func testStreamChannelToleratesFailingInitialier() {
1778+
struct DummyError: Error {}
1779+
let multiplexer = HTTP2StreamMultiplexer(mode: .client,
1780+
channel: self.channel,
1781+
outboundBufferSizeHighWatermark: 100,
1782+
outboundBufferSizeLowWatermark: 50) { (_, _) in
1783+
XCTFail("Must not be called")
1784+
return self.channel.eventLoop.makeFailedFuture(MyError())
1785+
}
1786+
XCTAssertNoThrow(try self.channel.pipeline.addHandler(multiplexer).wait())
1787+
1788+
// We need to activate the underlying channel here.
1789+
XCTAssertNoThrow(try self.channel.connect(to: SocketAddress(ipAddress: "1.2.3.4", port: 5)).wait())
1790+
1791+
// Now we want to create a new child stream.
1792+
let childChannelPromise = self.channel.eventLoop.makePromise(of: Channel.self)
1793+
multiplexer.createStreamChannel(promise: childChannelPromise) { childChannel, _ in
1794+
childChannel.close().flatMap {
1795+
childChannel.eventLoop.makeFailedFuture(DummyError())
1796+
}
1797+
}
1798+
self.channel.embeddedEventLoop.run()
1799+
}
1800+
17761801
}

0 commit comments

Comments
 (0)