Skip to content

Commit 39a0aa4

Browse files
authored
Only flush when necessary in the HTTP2ChannelHandler (#320)
Motivation: The `wroteFrame` flag in `HTTP2ChannelHandler` indicates whether a frame has been written. This flag is checked before flushing in order to avoid unnecessary flushes. Unfortunately this is only ever set to `true`. Modifications: - Add a `flushIfNecessary` to check and toggle `wroteFrame` and flush if necessary - Add a test Result: - Few unnecessary flushes - Resolves #245
1 parent ff53cbc commit 39a0aa4

File tree

3 files changed

+31
-3
lines changed

3 files changed

+31
-3
lines changed

Sources/NIOHTTP2/HTTP2ChannelHandler.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ extension NIOHTTP2Handler {
330330
// The user may choose to fire a more specific error if they wish.
331331
let goAwayFrame = HTTP2Frame(streamID: .rootStream, payload: .goAway(lastStreamID: .maxID, errorCode: reason, opaqueData: nil))
332332
self.writeUnbufferedFrame(context: context, frame: goAwayFrame)
333-
context.flush()
333+
self.flushIfNecessary(context: context)
334334
context.fireErrorCaught(underlyingError)
335335
}
336336

@@ -341,7 +341,7 @@ extension NIOHTTP2Handler {
341341
// the user's issue.
342342
let rstStreamFrame = HTTP2Frame(streamID: streamID, payload: .rstStream(reason))
343343
self.writeBufferedFrame(context: context, frame: rstStreamFrame, promise: nil)
344-
context.flush()
344+
self.flushIfNecessary(context: context)
345345
context.fireErrorCaught(underlyingError)
346346
}
347347

@@ -379,7 +379,7 @@ extension NIOHTTP2Handler {
379379

380380
let initialSettingsFrame = HTTP2Frame(streamID: .rootStream, payload: .settings(.settings(self.initialSettings)))
381381
self.writeUnbufferedFrame(context: context, frame: initialSettingsFrame)
382-
context.flush()
382+
self.flushIfNecessary(context: context)
383383
}
384384

385385
/// Write a frame that is allowed to be buffered (that is, that participates in the outbound frame buffer).
@@ -591,8 +591,13 @@ extension NIOHTTP2Handler {
591591
}
592592

593593
self.isUnbufferingAndFlushingAutomaticFrames = false
594+
self.flushIfNecessary(context: context)
595+
}
594596

597+
/// Emits a flush if a frame has been written.
598+
private func flushIfNecessary(context: ChannelHandlerContext) {
595599
if self.wroteFrame {
600+
self.wroteFrame = false
596601
context.flush()
597602
}
598603
}

Tests/NIOHTTP2Tests/SimpleClientServerFramePayloadStreamTests+XCTest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ extension SimpleClientServerFramePayloadStreamTests {
7575
("testGreasedSettingsAreTolerated", testGreasedSettingsAreTolerated),
7676
("testStreamCreationOrder", testStreamCreationOrder),
7777
("testStreamClosedInvalidRequestHeaders", testStreamClosedInvalidRequestHeaders),
78+
("testHTTP2HandlerDoesNotFlushExcessively", testHTTP2HandlerDoesNotFlushExcessively),
7879
]
7980
}
8081
}

Tests/NIOHTTP2Tests/SimpleClientServerFramePayloadStreamTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1960,4 +1960,26 @@ class SimpleClientServerFramePayloadStreamTests: XCTestCase {
19601960
XCTAssertNoThrow(try self.clientChannel.finish())
19611961
XCTAssertNoThrow(try self.serverChannel.finish())
19621962
}
1963+
1964+
func testHTTP2HandlerDoesNotFlushExcessively() throws {
1965+
try self.basicHTTP2Connection()
1966+
1967+
class FlushCounter: ChannelOutboundHandler {
1968+
typealias OutboundIn = Never
1969+
var flushCount = 0
1970+
1971+
func flush(context: ChannelHandlerContext) {
1972+
self.flushCount += 1
1973+
context.flush()
1974+
}
1975+
}
1976+
1977+
let counter = FlushCounter()
1978+
XCTAssertNoThrow(try self.clientChannel.pipeline.syncOperations.addHandler(counter, position: .first))
1979+
1980+
// 'channelReadComplete' should cause the HTTP2Handler to emit a flush if any frames have
1981+
// been written. No frames have been written, so no flush should be emitted.
1982+
self.clientChannel.pipeline.fireChannelReadComplete()
1983+
XCTAssertEqual(counter.flushCount, 0)
1984+
}
19631985
}

0 commit comments

Comments
 (0)