Skip to content

Add a server connection manager#532

Open
aryan-25 wants to merge 7 commits intoapple:mainfrom
aryan-25:server-connection-manager
Open

Add a server connection manager#532
aryan-25 wants to merge 7 commits intoapple:mainfrom
aryan-25:server-connection-manager

Conversation

@aryan-25
Copy link
Contributor

@aryan-25 aryan-25 commented Feb 3, 2026

Motivation:

NIOHTTP2Handler currently does not support graceful shutdown because it does not action upon a ChannelShouldQuiesceEvent event. See issue #336.

Per the comment on that issue, gRPC-swift-nio-transport implements this functionality in a separate channel handler, named ServerConnectionManagementHandler. We should introduce this to this repo too.

Modifications:

  • Added ServerConnectionManagementHandler from gRPC-swift-nio-transport with some slight modifications such as removing gRPC-specific functionality like policing client pings.
  • Added a new configureAsyncHTTP2Pipeline helper, which takes the connection manager handler as an argument and sets it up alongside NIOHTTP2Handler.

Result:

Addresses issue #336.

@aryan-25 aryan-25 added the 🆕 semver/minor Adds new public API. label Feb 3, 2026
Copy link
Contributor

@glbrntt glbrntt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add the unmodified code from gRPC as the first commit in this PR to make it easier to see the changes you've made?

@aryan-25 aryan-25 force-pushed the server-connection-manager branch from edf43a7 to ce91534 Compare February 5, 2026 10:00
/// While NIO's `EmbeddedEventLoop` provides control over its view of time (and therefore any
/// events scheduled on it) it doesn't offer a way to get the current time. This is usually done
/// via `NIODeadline`.
public struct Clock {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would err on the side of caution and keep this internal/package because it was the minimal API required to enable various tests in gRPC. We can always think a bit harder about the API and make it public in the future if necessary.

/// - keepaliveConfiguration: Configuration for keep-alive ping behavior. Defaults to `nil`, disabling keep-alive
/// pings.
/// - clock: A clock providing the current time.
public init(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend adding a config struct for all of these values: it will make API evolution much easier (e.g. if more config is added later). Also means you can provide a set of sensible default values as a static computed property on that config too.

@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
public func configureAsyncHTTP2Pipeline<Output: Sendable>(
mode: NIOHTTP2Handler.ParserMode,
connectionManager: NIOHTTP2ServerConnectionManagementHandler,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing in the handler is a bit odd. It'd be more idiomatic to pass in the config for the handler and have this func create the handler for the caller.

}
}

extension Timer: NIOScheduledCallbackHandler, @unchecked Sendable where Handler: Sendable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unchecked sendable seems wrong: it relies on correct use. Can we make it actually Sendable?

}

/// A manual clock for testing that allows explicit control over time.
public final class Manual {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These publics don’t do anything, so we can safely remove them.

}

public func handlerAdded(context: ChannelHandlerContext) {
assert(context.eventLoop === self.eventLoop)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace this assert with an in-event-loop assertion.


// Timer handler views.
extension NIOHTTP2ServerConnectionManagementHandler {
struct MaxIdleTimerHandlerView: @unchecked Sendable, NIOScheduledCallbackHandler {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What’s the need for these to be Sendable? If we do actually require it, we should fully enforce it using proper preconditions or loop bounds.


extension NIOHTTP2ServerConnectionManagementHandler {
/// A delegate for receiving HTTP/2 stream lifecycle events.
public struct HTTP2StreamDelegate: @unchecked Sendable, NIOHTTP2StreamDelegate {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this public? Same note here re sendability as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🆕 semver/minor Adds new public API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants