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
4 changes: 3 additions & 1 deletion Sources/NIOTestUtils/NIOHTTP1TestServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,9 @@ public final class NIOHTTP1TestServer {
try channel.pipeline.syncOperations.addHandler(TransformerHandler())
_ = try channel.syncOptions!.setOption(.autoRead, value: true)
} catch {
print("Channel initialization failed with: \(error)")
// This happens when the channel has been closed while it was waiting in
// the pipeline. It's benign: the closure passed to the close future above will
// have executed already, and started working on getting the next channel.
channel.close(promise: nil)
}
}
Expand Down
49 changes: 49 additions & 0 deletions Tests/NIOTestUtilsTests/NIOHTTP1TestServerTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,55 @@ class NIOHTTP1TestServerTest: XCTestCase {
XCTAssertNotNil(channel)
XCTAssertNoThrow(try channel.closeFuture.wait())
}

func testCloseChannelWhileItIsWaiting() throws {
let testServer = NIOHTTP1TestServer(group: self.group, aggregateBody: false)
let firstResponsePromise = self.group.next().makePromise(of: String.self)
let firstChannel = try self.connect(serverPort: testServer.serverPort, responsePromise: firstResponsePromise)
.wait()

// Send a request head and wait for it to be sent, and received at the test server so we know the connection is well underway.
let requestHead = HTTPRequestHead(version: .http1_1, method: .POST, uri: "/uri")
firstChannel.writeAndFlush(SendableRequestPart.head(requestHead), promise: nil)
XCTAssertNoThrow(
try testServer.receiveHeadAndVerify { head in
XCTAssertEqual(head.uri, "/uri")
}
)

// Create a second channel now.
let secondResponsePromise = self.group.next().makePromise(of: String.self)
let secondChannel = try self.connect(serverPort: testServer.serverPort, responsePromise: secondResponsePromise)
.wait()

// To burn a little time and convince ourselves that things are going fairly well, we can send a body payload on the first channel
// and confirm it comes through.
firstChannel.writeAndFlush(
SendableRequestPart.body(ByteBuffer(string: "ping")),
promise: nil
)
XCTAssertNoThrow(
try testServer.receiveBodyAndVerify { buffer in
XCTAssertEqual(String(buffer: buffer), "ping")
}
)

// Now, close the second channel.
try secondChannel.close().wait()

// Now we can complete the transaction.
firstChannel.writeAndFlush(SendableRequestPart.end(nil), promise: nil)
XCTAssertNoThrow(
try testServer.receiveEndAndVerify { trailers in
XCTAssertNil(trailers)
}
)
XCTAssertNoThrow(try testServer.writeOutbound(.head(.init(version: .http1_1, status: .ok))))
XCTAssertNoThrow(try testServer.writeOutbound(.end(nil)))

// The promise for the second should error.
XCTAssertThrowsError(try secondResponsePromise.futureResult.wait())
}
}

private final class TestHTTPHandler: ChannelInboundHandler {
Expand Down
Loading