Skip to content

Expose max reset streams HTTP2 configuration #2226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 29, 2025
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
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ let packageDependencies: [Package.Dependency] = [
),
.package(
url: "https://github.com/apple/swift-nio-http2.git",
from: "1.32.0"
from: "1.36.0"
),
.package(
url: "https://github.com/apple/swift-nio-transport-services.git",
Expand Down
14 changes: 12 additions & 2 deletions Sources/GRPC/ClientConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,13 @@ extension ClientConnection {
}
}

/// The HTTP/2 max number of reset streams. Defaults to 32. Must be non-negative.
public var httpMaxResetStreams: Int = 32 {
willSet {
precondition(newValue >= 0, "httpMaxResetStreams must be non-negative")
}
}

/// A logger for background information (such as connectivity state). A separate logger for
/// requests may be provided in the `CallOptions`.
///
Expand Down Expand Up @@ -630,10 +637,12 @@ extension ChannelPipeline.SynchronousOperations {
connectionIdleTimeout: TimeAmount,
httpTargetWindowSize: Int,
httpMaxFrameSize: Int,
httpMaxResetStreams: Int,
errorDelegate: ClientErrorDelegate?,
logger: Logger
) throws {
let initialSettings = [
var configuration = NIOHTTP2Handler.ConnectionConfiguration()
configuration.initialSettings = [
// As per the default settings for swift-nio-http2:
HTTP2Setting(parameter: .maxHeaderListSize, value: HPACKDecoder.defaultMaxHeaderListSize),
// We never expect (or allow) server initiated streams.
Expand All @@ -642,10 +651,11 @@ extension ChannelPipeline.SynchronousOperations {
HTTP2Setting(parameter: .maxFrameSize, value: httpMaxFrameSize),
HTTP2Setting(parameter: .initialWindowSize, value: httpTargetWindowSize),
]
configuration.maximumRecentlyResetStreams = httpMaxResetStreams

// We could use 'configureHTTP2Pipeline' here, but we need to add a few handlers between the
// two HTTP/2 handlers so we'll do it manually instead.
try self.addHandler(NIOHTTP2Handler(mode: .client, initialSettings: initialSettings))
try self.addHandler(NIOHTTP2Handler(mode: .client, connectionConfiguration: configuration))

let h2Multiplexer = HTTP2StreamMultiplexer(
mode: .client,
Expand Down
8 changes: 8 additions & 0 deletions Sources/GRPC/ConnectionManagerChannelProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ internal struct DefaultChannelProvider: ConnectionManagerChannelProvider {
internal var httpTargetWindowSize: Int
@usableFromInline
internal var httpMaxFrameSize: Int
@usableFromInline
internal var httpMaxResetStreams: Int

@usableFromInline
internal var errorDelegate: Optional<ClientErrorDelegate>
Expand Down Expand Up @@ -102,6 +104,7 @@ internal struct DefaultChannelProvider: ConnectionManagerChannelProvider {
tlsConfiguration: GRPCTLSConfiguration?,
httpTargetWindowSize: Int,
httpMaxFrameSize: Int,
httpMaxResetStreams: Int,
errorDelegate: ClientErrorDelegate?,
debugChannelInitializer: ((Channel) -> EventLoopFuture<Void>)?,
nwParametersConfigurator: (@Sendable (NWParameters) -> Void)?
Expand All @@ -114,6 +117,7 @@ internal struct DefaultChannelProvider: ConnectionManagerChannelProvider {
tlsConfiguration: tlsConfiguration,
httpTargetWindowSize: httpTargetWindowSize,
httpMaxFrameSize: httpMaxFrameSize,
httpMaxResetStreams: httpMaxResetStreams,
errorDelegate: errorDelegate,
debugChannelInitializer: debugChannelInitializer
)
Expand All @@ -131,6 +135,7 @@ internal struct DefaultChannelProvider: ConnectionManagerChannelProvider {
tlsConfiguration: GRPCTLSConfiguration?,
httpTargetWindowSize: Int,
httpMaxFrameSize: Int,
httpMaxResetStreams: Int,
errorDelegate: ClientErrorDelegate?,
debugChannelInitializer: ((Channel) -> EventLoopFuture<Void>)?
) {
Expand All @@ -143,6 +148,7 @@ internal struct DefaultChannelProvider: ConnectionManagerChannelProvider {

self.httpTargetWindowSize = httpTargetWindowSize
self.httpMaxFrameSize = httpMaxFrameSize
self.httpMaxResetStreams = httpMaxResetStreams

self.errorDelegate = errorDelegate
self.debugChannelInitializer = debugChannelInitializer
Expand Down Expand Up @@ -180,6 +186,7 @@ internal struct DefaultChannelProvider: ConnectionManagerChannelProvider {
tlsConfiguration: configuration.tlsConfiguration,
httpTargetWindowSize: configuration.httpTargetWindowSize,
httpMaxFrameSize: configuration.httpMaxFrameSize,
httpMaxResetStreams: configuration.httpMaxResetStreams,
errorDelegate: configuration.errorDelegate,
debugChannelInitializer: configuration.debugChannelInitializer
)
Expand Down Expand Up @@ -259,6 +266,7 @@ internal struct DefaultChannelProvider: ConnectionManagerChannelProvider {
connectionIdleTimeout: self.connectionIdleTimeout,
httpTargetWindowSize: self.httpTargetWindowSize,
httpMaxFrameSize: self.httpMaxFrameSize,
httpMaxResetStreams: self.httpMaxResetStreams,
errorDelegate: self.errorDelegate,
logger: logger
)
Expand Down
7 changes: 7 additions & 0 deletions Sources/GRPC/ConnectionPool/GRPCChannelPool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,13 @@ extension GRPCChannelPool.Configuration {
self.maxFrameSize = self.maxFrameSize.clamped(to: Self.allowedMaxFrameSizes)
}
}

/// The HTTP/2 max number of reset streams. Defaults to 32. Must be non-negative.
public var maxResetStreams: Int = 32 {
willSet {
precondition(newValue >= 0, "maxResetStreams must be non-negative")
}
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions Sources/GRPC/ConnectionPool/PooledChannel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ internal final class PooledChannel: GRPCChannel {
tlsConfiguration: configuration.transportSecurity.tlsConfiguration,
httpTargetWindowSize: configuration.http2.targetWindowSize,
httpMaxFrameSize: configuration.http2.maxFrameSize,
httpMaxResetStreams: configuration.http2.maxResetStreams,
errorDelegate: configuration.errorDelegate,
debugChannelInitializer: configuration.debugChannelInitializer,
nwParametersConfigurator: configuration.transportServices.nwParametersConfigurator
Expand All @@ -103,6 +104,7 @@ internal final class PooledChannel: GRPCChannel {
tlsConfiguration: configuration.transportSecurity.tlsConfiguration,
httpTargetWindowSize: configuration.http2.targetWindowSize,
httpMaxFrameSize: configuration.http2.maxFrameSize,
httpMaxResetStreams: configuration.http2.maxResetStreams,
errorDelegate: configuration.errorDelegate,
debugChannelInitializer: configuration.debugChannelInitializer
)
Expand All @@ -116,6 +118,7 @@ internal final class PooledChannel: GRPCChannel {
tlsConfiguration: configuration.transportSecurity.tlsConfiguration,
httpTargetWindowSize: configuration.http2.targetWindowSize,
httpMaxFrameSize: configuration.http2.maxFrameSize,
httpMaxResetStreams: configuration.http2.maxResetStreams,
errorDelegate: configuration.errorDelegate,
debugChannelInitializer: configuration.debugChannelInitializer
)
Expand Down
42 changes: 21 additions & 21 deletions Sources/GRPC/GRPCServerPipelineConfigurator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,27 +79,27 @@ final class GRPCServerPipelineConfigurator: ChannelInboundHandler, RemovableChan

/// Makes an HTTP/2 handler.
private func makeHTTP2Handler() -> NIOHTTP2Handler {
return .init(
mode: .server,
initialSettings: [
HTTP2Setting(
parameter: .maxConcurrentStreams,
value: self.configuration.httpMaxConcurrentStreams
),
HTTP2Setting(
parameter: .maxHeaderListSize,
value: HPACKDecoder.defaultMaxHeaderListSize
),
HTTP2Setting(
parameter: .maxFrameSize,
value: self.configuration.httpMaxFrameSize
),
HTTP2Setting(
parameter: .initialWindowSize,
value: self.configuration.httpTargetWindowSize
),
]
)
var configuration = NIOHTTP2Handler.ConnectionConfiguration()
configuration.initialSettings = [
HTTP2Setting(
parameter: .maxConcurrentStreams,
value: self.configuration.httpMaxConcurrentStreams
),
HTTP2Setting(
parameter: .maxHeaderListSize,
value: HPACKDecoder.defaultMaxHeaderListSize
),
HTTP2Setting(
parameter: .maxFrameSize,
value: self.configuration.httpMaxFrameSize
),
HTTP2Setting(
parameter: .initialWindowSize,
value: self.configuration.httpTargetWindowSize
),
]
configuration.maximumRecentlyResetStreams = self.configuration.httpMaxResetStreams
return NIOHTTP2Handler(mode: .server, connectionConfiguration: configuration)
}

/// Makes an HTTP/2 multiplexer suitable handling gRPC requests.
Expand Down
7 changes: 7 additions & 0 deletions Sources/GRPC/Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,13 @@ extension Server {
}
}

/// The HTTP/2 max number of reset streams. Defaults to 32. Must be non-negative.
public var httpMaxResetStreams: Int = 32 {
willSet {
precondition(newValue >= 0, "httpMaxResetStreams must be non-negative")
}
}

/// The root server logger. Accepted connections will branch from this logger and RPCs on
/// each connection will use a logger branched from the connections logger. This logger is made
/// available to service providers via `context`. Defaults to a no-op logger.
Expand Down