Skip to content

Commit 2d8e6ca

Browse files
authored
Tolerate sending data after close(mode: .output) (#2421)
Motivation We shouldn't crash on somewhat likely user error. Modifications Pass on writes after close(mode: .output) instead of crashing. Result User code is more robust to weird edge cases.
1 parent 7f4d10b commit 2d8e6ca

File tree

2 files changed

+21
-1
lines changed

2 files changed

+21
-1
lines changed

Sources/NIOHTTP1/HTTPServerPipelineHandler.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ public final class HTTPServerPipelineHandler: ChannelDuplexHandler, RemovableCha
111111
// We got a response while still receiving a request, which we have to
112112
// wait for.
113113
self = .requestEndPending
114-
case .requestEndPending, .idle, .sentCloseOutput, .sentCloseOutputRequestEndPending:
114+
case .sentCloseOutput, .sentCloseOutputRequestEndPending:
115+
// This is a user error: they have sent close(mode: .output), but are continuing to write.
116+
// The write will fail, so we can allow it to pass.
117+
()
118+
case .requestEndPending, .idle:
115119
preconditionFailure("Unexpectedly received a response in state \(self)")
116120
}
117121
}

Tests/NIOHTTP1Tests/HTTPServerPipelineHandlerTest.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,4 +1165,20 @@ class HTTPServerPipelineHandlerTest: XCTestCase {
11651165
[.channelRead(HTTPServerRequestPart.head(self.requestHead)),
11661166
.channelRead(HTTPServerRequestPart.end(nil))])
11671167
}
1168+
1169+
func testWritesAfterCloseOutputAreDropped() throws {
1170+
// Send in a request
1171+
XCTAssertNoThrow(try self.channel.writeInbound(HTTPServerRequestPart.head(self.requestHead)))
1172+
XCTAssertNoThrow(try self.channel.writeInbound(HTTPServerRequestPart.end(nil)))
1173+
1174+
// Server sends a head.
1175+
XCTAssertNoThrow(try self.channel.writeOutbound(HTTPServerResponsePart.head(self.responseHead)))
1176+
1177+
// Now the server sends close output
1178+
XCTAssertNoThrow(try channel.close(mode: .output).wait())
1179+
1180+
// Now, in error, the server sends .body and .end. Both pass unannounced.
1181+
XCTAssertNoThrow(try self.channel.writeOutbound(HTTPServerResponsePart.body(.byteBuffer(ByteBuffer()))))
1182+
XCTAssertNoThrow(try self.channel.writeOutbound(HTTPServerResponsePart.end(nil)))
1183+
}
11681184
}

0 commit comments

Comments
 (0)