diff --git a/Package.swift b/Package.swift index 4f7ca4096..7a5aef1b2 100644 --- a/Package.swift +++ b/Package.swift @@ -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", diff --git a/Sources/GRPC/ClientConnection.swift b/Sources/GRPC/ClientConnection.swift index 6e9754f7c..0749922d1 100644 --- a/Sources/GRPC/ClientConnection.swift +++ b/Sources/GRPC/ClientConnection.swift @@ -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`. /// @@ -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. @@ -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, diff --git a/Sources/GRPC/ConnectionManagerChannelProvider.swift b/Sources/GRPC/ConnectionManagerChannelProvider.swift index 8b8e66ac3..3a23e85c2 100644 --- a/Sources/GRPC/ConnectionManagerChannelProvider.swift +++ b/Sources/GRPC/ConnectionManagerChannelProvider.swift @@ -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 @@ -102,6 +104,7 @@ internal struct DefaultChannelProvider: ConnectionManagerChannelProvider { tlsConfiguration: GRPCTLSConfiguration?, httpTargetWindowSize: Int, httpMaxFrameSize: Int, + httpMaxResetStreams: Int, errorDelegate: ClientErrorDelegate?, debugChannelInitializer: ((Channel) -> EventLoopFuture)?, nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? @@ -114,6 +117,7 @@ internal struct DefaultChannelProvider: ConnectionManagerChannelProvider { tlsConfiguration: tlsConfiguration, httpTargetWindowSize: httpTargetWindowSize, httpMaxFrameSize: httpMaxFrameSize, + httpMaxResetStreams: httpMaxResetStreams, errorDelegate: errorDelegate, debugChannelInitializer: debugChannelInitializer ) @@ -131,6 +135,7 @@ internal struct DefaultChannelProvider: ConnectionManagerChannelProvider { tlsConfiguration: GRPCTLSConfiguration?, httpTargetWindowSize: Int, httpMaxFrameSize: Int, + httpMaxResetStreams: Int, errorDelegate: ClientErrorDelegate?, debugChannelInitializer: ((Channel) -> EventLoopFuture)? ) { @@ -143,6 +148,7 @@ internal struct DefaultChannelProvider: ConnectionManagerChannelProvider { self.httpTargetWindowSize = httpTargetWindowSize self.httpMaxFrameSize = httpMaxFrameSize + self.httpMaxResetStreams = httpMaxResetStreams self.errorDelegate = errorDelegate self.debugChannelInitializer = debugChannelInitializer @@ -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 ) @@ -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 ) diff --git a/Sources/GRPC/ConnectionPool/GRPCChannelPool.swift b/Sources/GRPC/ConnectionPool/GRPCChannelPool.swift index 2847e67ba..e84b903db 100644 --- a/Sources/GRPC/ConnectionPool/GRPCChannelPool.swift +++ b/Sources/GRPC/ConnectionPool/GRPCChannelPool.swift @@ -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") + } + } } } diff --git a/Sources/GRPC/ConnectionPool/PooledChannel.swift b/Sources/GRPC/ConnectionPool/PooledChannel.swift index 1d7c7fee5..0c7b95fcd 100644 --- a/Sources/GRPC/ConnectionPool/PooledChannel.swift +++ b/Sources/GRPC/ConnectionPool/PooledChannel.swift @@ -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 @@ -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 ) @@ -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 ) diff --git a/Sources/GRPC/GRPCServerPipelineConfigurator.swift b/Sources/GRPC/GRPCServerPipelineConfigurator.swift index c1b208e3a..6b05faaee 100644 --- a/Sources/GRPC/GRPCServerPipelineConfigurator.swift +++ b/Sources/GRPC/GRPCServerPipelineConfigurator.swift @@ -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. diff --git a/Sources/GRPC/Server.swift b/Sources/GRPC/Server.swift index dae123a3a..491f05447 100644 --- a/Sources/GRPC/Server.swift +++ b/Sources/GRPC/Server.swift @@ -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.