diff --git a/Sources/NIOHTTP2/HTTP2ChannelHandler+InboundStreamMultiplexer.swift b/Sources/NIOHTTP2/HTTP2ChannelHandler+InboundStreamMultiplexer.swift index 108273fd..ee339cdc 100644 --- a/Sources/NIOHTTP2/HTTP2ChannelHandler+InboundStreamMultiplexer.swift +++ b/Sources/NIOHTTP2/HTTP2ChannelHandler+InboundStreamMultiplexer.swift @@ -39,6 +39,7 @@ extension NIOHTTP2Handler { /// Abstracts over the integrated stream multiplexing (inline) and the chained channel handler (legacy) multiplexing approaches. /// /// We use an enum for this purpose since we can't use a generic (for API compatibility reasons) and it allows us to avoid the cost of using an existential. + @usableFromInline internal enum InboundStreamMultiplexer: HTTP2InboundStreamMultiplexer { case legacy(LegacyInboundStreamMultiplexer) case inline(InlineStreamMultiplexer) @@ -149,6 +150,7 @@ extension NIOHTTP2Handler.InboundStreamMultiplexer { /// Provides an inbound stream multiplexer interface for legacy compatibility. /// /// This doesn't actually do any demultiplexing of inbound streams but communicates with the `HTTP2StreamChannel` which does - mostly via user inbound events. +@usableFromInline internal struct LegacyInboundStreamMultiplexer { let context: ChannelHandlerContext } diff --git a/Sources/NIOHTTP2/HTTP2ChannelHandler+InlineStreamMultiplexer.swift b/Sources/NIOHTTP2/HTTP2ChannelHandler+InlineStreamMultiplexer.swift index e5215949..ce726a89 100644 --- a/Sources/NIOHTTP2/HTTP2ChannelHandler+InlineStreamMultiplexer.swift +++ b/Sources/NIOHTTP2/HTTP2ChannelHandler+InlineStreamMultiplexer.swift @@ -14,10 +14,13 @@ import NIOCore +@usableFromInline internal struct InlineStreamMultiplexer { private let context: ChannelHandlerContext - private let commonStreamMultiplexer: HTTP2CommonInboundStreamMultiplexer + @usableFromInline + internal let _commonStreamMultiplexer: HTTP2CommonInboundStreamMultiplexer + private let outboundView: NIOHTTP2Handler.OutboundView /// The delegate to be notified upon stream creation and close. @@ -25,7 +28,7 @@ internal struct InlineStreamMultiplexer { init(context: ChannelHandlerContext, outboundView: NIOHTTP2Handler.OutboundView, mode: NIOHTTP2Handler.ParserMode, inboundStreamStateInitializer: MultiplexerAbstractChannel.InboundStreamStateInitializer, targetWindowSize: Int, streamChannelOutboundBytesHighWatermark: Int, streamChannelOutboundBytesLowWatermark: Int, streamDelegate: NIOHTTP2StreamDelegate?) { self.context = context - self.commonStreamMultiplexer = HTTP2CommonInboundStreamMultiplexer( + self._commonStreamMultiplexer = HTTP2CommonInboundStreamMultiplexer( mode: mode, channel: context.channel, inboundStreamStateInitializer: inboundStreamStateInitializer, @@ -40,22 +43,22 @@ internal struct InlineStreamMultiplexer { extension InlineStreamMultiplexer: HTTP2InboundStreamMultiplexer { func receivedFrame(_ frame: HTTP2Frame) { - self.commonStreamMultiplexer.receivedFrame(frame, context: self.context, multiplexer: .inline(self)) + self._commonStreamMultiplexer.receivedFrame(frame, context: self.context, multiplexer: .inline(self)) } func streamError(streamID: HTTP2StreamID, error: Error) { let streamError = NIOHTTP2Errors.streamError(streamID: streamID, baseError: error) - self.commonStreamMultiplexer.streamError(context: self.context, streamError) + self._commonStreamMultiplexer.streamError(context: self.context, streamError) } func streamCreated(event: NIOHTTP2StreamCreatedEvent) { - if let childChannel = self.commonStreamMultiplexer.streamCreated(event: event) { + if let childChannel = self._commonStreamMultiplexer.streamCreated(event: event) { self.streamDelegate?.streamCreated(event.streamID, channel: childChannel) } } func streamClosed(event: StreamClosedEvent) { - if let childChannel = self.commonStreamMultiplexer.streamClosed(event: event) { + if let childChannel = self._commonStreamMultiplexer.streamClosed(event: event) { self.streamDelegate?.streamClosed(event.streamID, channel: childChannel) } } @@ -65,16 +68,16 @@ extension InlineStreamMultiplexer: HTTP2InboundStreamMultiplexer { // This force-unwrap is safe: we always have a connection window. self.newConnectionWindowSize(newSize: event.inboundWindowSize!) } else { - self.commonStreamMultiplexer.childStreamWindowUpdated(event: event) + self._commonStreamMultiplexer.childStreamWindowUpdated(event: event) } } func initialStreamWindowChanged(event: NIOHTTP2BulkStreamWindowChangeEvent) { - self.commonStreamMultiplexer.initialStreamWindowChanged(event: event) + self._commonStreamMultiplexer.initialStreamWindowChanged(event: event) } private func newConnectionWindowSize(newSize: Int) { - guard let increment = self.commonStreamMultiplexer.newConnectionWindowSize(newSize) else { + guard let increment = self._commonStreamMultiplexer.newConnectionWindowSize(newSize) else { return } @@ -90,7 +93,7 @@ extension InlineStreamMultiplexer: HTTP2OutboundStreamMultiplexer { } func flushStream(_ id: HTTP2StreamID) { - switch self.commonStreamMultiplexer.flushDesired() { + switch self._commonStreamMultiplexer.flushDesired() { case .proceed: self.outboundView.flush(context: self.context) case .waitForReadsToComplete: @@ -99,33 +102,33 @@ extension InlineStreamMultiplexer: HTTP2OutboundStreamMultiplexer { } func requestStreamID(forChannel channel: NIOCore.Channel) -> HTTP2StreamID { - self.commonStreamMultiplexer.requestStreamID(forChannel: channel) + self._commonStreamMultiplexer.requestStreamID(forChannel: channel) } func streamClosed(channelID: ObjectIdentifier) { - self.commonStreamMultiplexer.childChannelClosed(channelID: channelID) + self._commonStreamMultiplexer.childChannelClosed(channelID: channelID) } func streamClosed(id: HTTP2StreamID) { - self.commonStreamMultiplexer.childChannelClosed(streamID: id) + self._commonStreamMultiplexer.childChannelClosed(streamID: id) } } extension InlineStreamMultiplexer { internal func propagateChannelActive() { - self.commonStreamMultiplexer.propagateChannelActive(context: self.context) + self._commonStreamMultiplexer.propagateChannelActive(context: self.context) } internal func propagateChannelInactive() { - self.commonStreamMultiplexer.propagateChannelInactive() + self._commonStreamMultiplexer.propagateChannelInactive() } internal func propagateChannelWritabilityChanged() { - self.commonStreamMultiplexer.propagateChannelWritabilityChanged(context: self.context) + self._commonStreamMultiplexer.propagateChannelWritabilityChanged(context: self.context) } internal func propagateReadComplete() { - switch self.commonStreamMultiplexer.propagateReadComplete() { + switch self._commonStreamMultiplexer.propagateReadComplete() { case .flushNow: // we had marked a flush as blocked by an active read which we may now perform self.outboundView.flush(context: self.context) @@ -135,21 +138,22 @@ extension InlineStreamMultiplexer { } internal func processedFrame(frame: HTTP2Frame) { - self.commonStreamMultiplexer.processedFrame(streamID: frame.streamID, size: frame.payload.flowControlledSize) + self._commonStreamMultiplexer.processedFrame(streamID: frame.streamID, size: frame.payload.flowControlledSize) } } extension InlineStreamMultiplexer { internal func createStreamChannel(promise: EventLoopPromise?, _ streamStateInitializer: @escaping NIOChannelInitializer) { - self.commonStreamMultiplexer.createStreamChannel(multiplexer: .inline(self), promise: promise, streamStateInitializer) + self._commonStreamMultiplexer.createStreamChannel(multiplexer: .inline(self), promise: promise, streamStateInitializer) } internal func createStreamChannel(_ streamStateInitializer: @escaping NIOChannelInitializer) -> EventLoopFuture { - self.commonStreamMultiplexer.createStreamChannel(multiplexer: .inline(self), streamStateInitializer) + self._commonStreamMultiplexer.createStreamChannel(multiplexer: .inline(self), streamStateInitializer) } + @inlinable internal func createStreamChannel(_ initializer: @escaping NIOChannelInitializerWithOutput) -> EventLoopFuture { - self.commonStreamMultiplexer.createStreamChannel(multiplexer: .inline(self), initializer) + self._commonStreamMultiplexer.createStreamChannel(multiplexer: .inline(self), initializer) } } @@ -208,7 +212,7 @@ extension NIOHTTP2Handler { extension InlineStreamMultiplexer { func setChannelContinuation(_ streamChannels: any AnyContinuation) { - self.commonStreamMultiplexer.setChannelContinuation(streamChannels) + self._commonStreamMultiplexer.setChannelContinuation(streamChannels) } } @@ -230,10 +234,11 @@ extension NIOHTTP2Handler { /// `Output`. This type may be `HTTP2Frame` or changed to any other type. @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public struct AsyncStreamMultiplexer { - private let inlineStreamMultiplexer: InlineStreamMultiplexer + @usableFromInline internal let inlineStreamMultiplexer: InlineStreamMultiplexer public let inbound: NIOHTTP2AsyncSequence // Cannot be created by users. + @usableFromInline internal init(_ inlineStreamMultiplexer: InlineStreamMultiplexer, continuation: any AnyContinuation, inboundStreamChannels: NIOHTTP2AsyncSequence) { self.inlineStreamMultiplexer = inlineStreamMultiplexer self.inlineStreamMultiplexer.setChannelContinuation(continuation) @@ -245,6 +250,7 @@ extension NIOHTTP2Handler { /// - Parameter initializer: A closure that will be called upon the created stream which is responsible for /// initializing the stream's `Channel`. /// - Returns: The result of the `initializer`. + @inlinable public func openStream(_ initializer: @escaping NIOChannelInitializerWithOutput) async throws -> Output { return try await self.inlineStreamMultiplexer.createStreamChannel(initializer).get() } diff --git a/Sources/NIOHTTP2/HTTP2ChannelHandler.swift b/Sources/NIOHTTP2/HTTP2ChannelHandler.swift index 6e9ac229..fd809311 100644 --- a/Sources/NIOHTTP2/HTTP2ChannelHandler.swift +++ b/Sources/NIOHTTP2/HTTP2ChannelHandler.swift @@ -40,7 +40,7 @@ public final class NIOHTTP2Handler: ChannelDuplexHandler { private static let clientMagic: StaticString = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" /// The event loop on which this handler will do work. - private let eventLoop: EventLoop? + @usableFromInline internal let _eventLoop: EventLoop? /// The connection state machine. We always have one of these. private var stateMachine: HTTP2ConnectionStateMachine @@ -103,6 +103,8 @@ public final class NIOHTTP2Handler: ChannelDuplexHandler { /// The delegate for (de)multiplexing inbound streams. private var inboundStreamMultiplexerState: InboundStreamMultiplexerState + + @usableFromInline internal var inboundStreamMultiplexer: InboundStreamMultiplexer? { return self.inboundStreamMultiplexerState.multiplexer } @@ -273,7 +275,7 @@ public final class NIOHTTP2Handler: ChannelDuplexHandler { maximumBufferedControlFrames: Int, maximumResetFrameCount: Int, resetFrameCounterWindow: TimeAmount) { - self.eventLoop = eventLoop + self._eventLoop = eventLoop self.stateMachine = HTTP2ConnectionStateMachine(role: .init(mode), headerBlockValidation: .init(headerBlockValidation), contentLengthValidation: .init(contentLengthValidation)) self.mode = mode self.initialSettings = initialSettings @@ -312,7 +314,7 @@ public final class NIOHTTP2Handler: ChannelDuplexHandler { resetFrameCounterWindow: TimeAmount = .seconds(30)) { self.stateMachine = HTTP2ConnectionStateMachine(role: .init(mode), headerBlockValidation: .init(headerBlockValidation), contentLengthValidation: .init(contentLengthValidation)) self.mode = mode - self.eventLoop = nil + self._eventLoop = nil self.initialSettings = initialSettings self.outboundBuffer = CompoundOutboundBuffer(mode: mode, initialMaxOutboundStreams: 100, maxBufferedControlFrames: maximumBufferedControlFrames) self.denialOfServiceValidator = DOSHeuristics(maximumSequentialEmptyDataFrames: maximumSequentialEmptyDataFrames, maximumResetFrameCount: maximumResetFrameCount, resetFrameCounterWindow: resetFrameCounterWindow) @@ -1036,6 +1038,7 @@ extension NIOHTTP2Handler { /// The type of all `inboundStreamInitializer` callbacks which do not need to return data. public typealias StreamInitializer = NIOChannelInitializer /// The type of NIO Channel initializer callbacks which need to return untyped data. + @usableFromInline internal typealias StreamInitializerWithAnyOutput = @Sendable (Channel) -> EventLoopFuture /// Creates a new ``NIOHTTP2Handler`` with a local multiplexer. (i.e. using @@ -1073,6 +1076,7 @@ extension NIOHTTP2Handler { self.inboundStreamMultiplexerState = .uninitializedInline(streamConfiguration, inboundStreamInitializer, streamDelegate) } + @usableFromInline internal convenience init( mode: ParserMode, eventLoop: EventLoop, @@ -1154,12 +1158,12 @@ extension NIOHTTP2Handler { /// i.e. it was initialized with an `inboundStreamInitializer`. public var multiplexer: EventLoopFuture { // We need to return a future here so that we can synchronize access on the underlying `self.inboundStreamMultiplexer` - if self.eventLoop!.inEventLoop { - return self.eventLoop!.makeCompletedFuture { + if self._eventLoop!.inEventLoop { + return self._eventLoop!.makeCompletedFuture { return try self.syncMultiplexer() } } else { - return self.eventLoop!.submit { + return self._eventLoop!.submit { return try self.syncMultiplexer() } } @@ -1171,7 +1175,7 @@ extension NIOHTTP2Handler { /// > - The ``NIOHTTP2Handler`` uses a local multiplexer, i.e. it was initialized with an `inboundStreamInitializer`. /// > - The caller is already on the correct event loop. public func syncMultiplexer() throws -> StreamMultiplexer { - self.eventLoop!.preconditionInEventLoop() + self._eventLoop!.preconditionInEventLoop() switch self.inboundStreamMultiplexer { case let .some(.inline(multiplexer)): @@ -1181,9 +1185,10 @@ extension NIOHTTP2Handler { } } + @inlinable @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) internal func syncAsyncStreamMultiplexer(continuation: any AnyContinuation, inboundStreamChannels: NIOHTTP2AsyncSequence) throws -> AsyncStreamMultiplexer { - self.eventLoop!.preconditionInEventLoop() + self._eventLoop!.preconditionInEventLoop() switch self.inboundStreamMultiplexer { case let .some(.inline(multiplexer)): diff --git a/Sources/NIOHTTP2/HTTP2CommonInboundStreamMultiplexer.swift b/Sources/NIOHTTP2/HTTP2CommonInboundStreamMultiplexer.swift index ede25351..f3dd0df3 100644 --- a/Sources/NIOHTTP2/HTTP2CommonInboundStreamMultiplexer.swift +++ b/Sources/NIOHTTP2/HTTP2CommonInboundStreamMultiplexer.swift @@ -15,15 +15,16 @@ import NIOCore /// Represents the common multiplexing machinery used by both legacy ``HTTP2StreamMultiplexer`` and new ``InlineStreamMultiplexer`` inbound stream multiplexing. +@usableFromInline internal class HTTP2CommonInboundStreamMultiplexer { - private let channel: Channel + @usableFromInline internal let _channel: Channel // NOTE: All state below should only be modified from the `EventLoop` of the supplied `Channel` // Streams which have a stream ID. private var streams: [HTTP2StreamID: MultiplexerAbstractChannel] = [:] // Streams which don't yet have a stream ID assigned to them. - private var pendingStreams: [ObjectIdentifier: MultiplexerAbstractChannel] = [:] + @usableFromInline internal var _pendingStreams: [ObjectIdentifier: MultiplexerAbstractChannel] = [:] private var didReadChannels: StreamChannelList = StreamChannelList() private var nextOutboundStreamID: HTTP2StreamID private let inboundStreamStateInitializer: MultiplexerAbstractChannel.InboundStreamStateInitializer @@ -31,9 +32,9 @@ internal class HTTP2CommonInboundStreamMultiplexer { private var connectionFlowControlManager: InboundWindowManager private let mode: NIOHTTP2Handler.ParserMode - private let targetWindowSize: Int - private let streamChannelOutboundBytesHighWatermark: Int - private let streamChannelOutboundBytesLowWatermark: Int + @usableFromInline internal let _targetWindowSize: Int + @usableFromInline internal let _streamChannelOutboundBytesHighWatermark: Int + @usableFromInline internal let _streamChannelOutboundBytesLowWatermark: Int private var isReading = false private var flushPending = false @@ -48,12 +49,12 @@ internal class HTTP2CommonInboundStreamMultiplexer { streamChannelOutboundBytesHighWatermark: Int, streamChannelOutboundBytesLowWatermark: Int ) { - self.channel = channel + self._channel = channel self.inboundStreamStateInitializer = inboundStreamStateInitializer - self.targetWindowSize = targetWindowSize + self._targetWindowSize = targetWindowSize self.connectionFlowControlManager = InboundWindowManager(targetSize: Int32(targetWindowSize)) - self.streamChannelOutboundBytesHighWatermark = streamChannelOutboundBytesHighWatermark - self.streamChannelOutboundBytesLowWatermark = streamChannelOutboundBytesLowWatermark + self._streamChannelOutboundBytesHighWatermark = streamChannelOutboundBytesHighWatermark + self._streamChannelOutboundBytesLowWatermark = streamChannelOutboundBytesLowWatermark self.mode = mode switch mode { case .client: @@ -68,7 +69,7 @@ internal class HTTP2CommonInboundStreamMultiplexer { // note this is intentionally not bound to `HTTP2InboundStreamMultiplexer` to allow for freedom in modifying the shared driver function signatures extension HTTP2CommonInboundStreamMultiplexer { func receivedFrame(_ frame: HTTP2Frame, context: ChannelHandlerContext, multiplexer: HTTP2StreamChannel.OutboundStreamMultiplexer) { - self.channel.eventLoop.preconditionInEventLoop() + self._channel.eventLoop.preconditionInEventLoop() self.isReading = true let streamID = frame.streamID @@ -91,13 +92,13 @@ extension HTTP2CommonInboundStreamMultiplexer { } } else if case .headers = frame.payload { let channel = MultiplexerAbstractChannel( - allocator: self.channel.allocator, - parent: self.channel, + allocator: self._channel.allocator, + parent: self._channel, multiplexer: multiplexer, streamID: streamID, - targetWindowSize: Int32(self.targetWindowSize), - outboundBytesHighWatermark: self.streamChannelOutboundBytesHighWatermark, - outboundBytesLowWatermark: self.streamChannelOutboundBytesLowWatermark, + targetWindowSize: Int32(self._targetWindowSize), + outboundBytesHighWatermark: self._streamChannelOutboundBytesHighWatermark, + outboundBytesLowWatermark: self._streamChannelOutboundBytesLowWatermark, inboundStreamStateInitializer: self.inboundStreamStateInitializer ) @@ -112,7 +113,7 @@ extension HTTP2CommonInboundStreamMultiplexer { // If we have an async sequence of inbound stream channels yield the channel to it // but only once we are sure initialization and activation succeed if let streamChannelContinuation = self.streamChannelContinuation { - let promise = self.channel.eventLoop.makePromise(of: Any.self) + let promise = self._channel.eventLoop.makePromise(of: Any.self) promise.futureResult.whenSuccess { value in streamChannelContinuation.yield(any: value) } @@ -134,13 +135,13 @@ extension HTTP2CommonInboundStreamMultiplexer { } func streamError(context: ChannelHandlerContext, _ streamError: NIOHTTP2Errors.StreamError) { - self.channel.eventLoop.preconditionInEventLoop() + self._channel.eventLoop.preconditionInEventLoop() self.streams[streamError.streamID]?.receiveStreamError(streamError) context.fireErrorCaught(streamError.baseError) } func streamCreated(event: NIOHTTP2StreamCreatedEvent) -> Channel? { - self.channel.eventLoop.preconditionInEventLoop() + self._channel.eventLoop.preconditionInEventLoop() if let channel = self.streams[event.streamID] { channel.networkActivationReceived() return channel.baseChannel @@ -149,7 +150,7 @@ extension HTTP2CommonInboundStreamMultiplexer { } func streamClosed(event: StreamClosedEvent) -> Channel? { - self.channel.eventLoop.preconditionInEventLoop() + self._channel.eventLoop.preconditionInEventLoop() if let channel = self.streams[event.streamID] { channel.receiveStreamClosed(event.reason) return channel.baseChannel @@ -158,12 +159,12 @@ extension HTTP2CommonInboundStreamMultiplexer { } func newConnectionWindowSize(_ newSize: Int) -> Int? { - self.channel.eventLoop.preconditionInEventLoop() + self._channel.eventLoop.preconditionInEventLoop() return self.connectionFlowControlManager.newWindowSize(newSize) } func childStreamWindowUpdated(event: NIOHTTP2WindowUpdatedEvent) { - self.channel.eventLoop.preconditionInEventLoop() + self._channel.eventLoop.preconditionInEventLoop() precondition(event.streamID != .rootStream, "not to be called on the root stream") if let windowSize = event.inboundWindowSize { @@ -190,7 +191,7 @@ extension HTTP2CommonInboundStreamMultiplexer { channel.performActivation() } } - for channel in self.pendingStreams.values { + for channel in self._pendingStreams.values { if context.channel.isActive { channel.performActivation() } @@ -201,7 +202,7 @@ extension HTTP2CommonInboundStreamMultiplexer { for channel in self.streams.values { channel.receiveStreamClosed(nil) } - for channel in self.pendingStreams.values { + for channel in self._pendingStreams.values { channel.receiveStreamClosed(nil) } // there cannot be any more inbound streams now that the connection channel is inactive @@ -212,7 +213,7 @@ extension HTTP2CommonInboundStreamMultiplexer { for channel in self.streams.values { channel.parentChannelWritabilityChanged(newValue: context.channel.isWritable) } - for channel in self.pendingStreams.values { + for channel in self._pendingStreams.values { channel.parentChannelWritabilityChanged(newValue: context.channel.isWritable) } } @@ -264,7 +265,7 @@ extension HTTP2CommonInboundStreamMultiplexer { } internal func childChannelClosed(channelID: ObjectIdentifier) { - self.pendingStreams.removeValue(forKey: channelID) + self._pendingStreams.removeValue(forKey: channelID) } /// Requests a ``HTTP2StreamID`` for the given `Channel`. @@ -275,7 +276,7 @@ extension HTTP2CommonInboundStreamMultiplexer { // This unwrap shouldn't fail: the multiplexer owns the stream and the stream only requests // a streamID once. - guard let abstractChannel = self.pendingStreams.removeValue(forKey: channelID) else { + guard let abstractChannel = self._pendingStreams.removeValue(forKey: channelID) else { preconditionFailure("No pending streams have channelID \(channelID)") } assert(abstractChannel.channelID == channelID) @@ -293,24 +294,25 @@ extension HTTP2CommonInboundStreamMultiplexer { } extension HTTP2CommonInboundStreamMultiplexer { + @inlinable internal func _createStreamChannel( _ multiplexer: HTTP2StreamChannel.OutboundStreamMultiplexer, _ promise: EventLoopPromise?, _ streamStateInitializer: @escaping NIOChannelInitializerWithOutput ) { - self.channel.eventLoop.assertInEventLoop() + self._channel.eventLoop.assertInEventLoop() let channel = MultiplexerAbstractChannel( - allocator: self.channel.allocator, - parent: self.channel, + allocator: self._channel.allocator, + parent: self._channel, multiplexer: multiplexer, streamID: nil, - targetWindowSize: Int32(self.targetWindowSize), - outboundBytesHighWatermark: self.streamChannelOutboundBytesHighWatermark, - outboundBytesLowWatermark: self.streamChannelOutboundBytesLowWatermark, + targetWindowSize: Int32(self._targetWindowSize), + outboundBytesHighWatermark: self._streamChannelOutboundBytesHighWatermark, + outboundBytesLowWatermark: self._streamChannelOutboundBytesLowWatermark, inboundStreamStateInitializer: .excludesStreamID(nil) ) - self.pendingStreams[channel.channelID] = channel + self._pendingStreams[channel.channelID] = channel let anyInitializer: NIOChannelInitializerWithOutput = { channel in streamStateInitializer(channel).map { return $0 } @@ -336,6 +338,7 @@ extension HTTP2CommonInboundStreamMultiplexer { channel.configure(initializer: anyInitializer, userPromise: anyPromise) } + @inlinable internal func createStreamChannel( multiplexer: HTTP2StreamChannel.OutboundStreamMultiplexer, promise: EventLoopPromise?, @@ -344,16 +347,17 @@ extension HTTP2CommonInboundStreamMultiplexer { // Always create streams channels on the next event loop tick. This avoids re-entrancy // issues where handlers interposed between the two HTTP/2 handlers could create streams // in channel active which become activated twice. - self.channel.eventLoop.execute { + self._channel.eventLoop.execute { self._createStreamChannel(multiplexer, promise, streamStateInitializer) } } + @inlinable internal func createStreamChannel( multiplexer: HTTP2StreamChannel.OutboundStreamMultiplexer, _ streamStateInitializer: @escaping NIOChannelInitializerWithOutput ) -> EventLoopFuture { - let promise = self.channel.eventLoop.makePromise(of: Output.self) + let promise = self._channel.eventLoop.makePromise(of: Output.self) self.createStreamChannel(multiplexer: multiplexer, promise: promise, streamStateInitializer) return promise.futureResult } @@ -364,16 +368,16 @@ extension HTTP2CommonInboundStreamMultiplexer { _ streamStateInitializer: @escaping NIOChannelInitializer ) { let channel = MultiplexerAbstractChannel( - allocator: self.channel.allocator, - parent: self.channel, + allocator: self._channel.allocator, + parent: self._channel, multiplexer: multiplexer, streamID: nil, - targetWindowSize: Int32(self.targetWindowSize), - outboundBytesHighWatermark: self.streamChannelOutboundBytesHighWatermark, - outboundBytesLowWatermark: self.streamChannelOutboundBytesLowWatermark, + targetWindowSize: Int32(self._targetWindowSize), + outboundBytesHighWatermark: self._streamChannelOutboundBytesHighWatermark, + outboundBytesLowWatermark: self._streamChannelOutboundBytesLowWatermark, inboundStreamStateInitializer: .excludesStreamID(nil) ) - self.pendingStreams[channel.channelID] = channel + self._pendingStreams[channel.channelID] = channel channel.configure(initializer: streamStateInitializer, userPromise: promise) } @@ -386,7 +390,7 @@ extension HTTP2CommonInboundStreamMultiplexer { // Always create streams channels on the next event loop tick. This avoids re-entrancy // issues where handlers interposed between the two HTTP/2 handlers could create streams // in channel active which become activated twice. - self.channel.eventLoop.execute { + self._channel.eventLoop.execute { self._createStreamChannel(multiplexer, promise, streamStateInitializer) } } @@ -394,7 +398,7 @@ extension HTTP2CommonInboundStreamMultiplexer { internal func createStreamChannel( multiplexer: HTTP2StreamChannel.OutboundStreamMultiplexer, _ streamStateInitializer: @escaping NIOChannelInitializer) -> EventLoopFuture { - let promise = self.channel.eventLoop.makePromise(of: Channel.self) + let promise = self._channel.eventLoop.makePromise(of: Channel.self) self.createStreamChannel(multiplexer: multiplexer, promise: promise, streamStateInitializer) return promise.futureResult } @@ -405,16 +409,16 @@ extension HTTP2CommonInboundStreamMultiplexer { promise: EventLoopPromise?, _ streamStateInitializer: @escaping (Channel, HTTP2StreamID) -> EventLoopFuture ) { - self.channel.eventLoop.execute { + self._channel.eventLoop.execute { let streamID = self.nextStreamID() let channel = MultiplexerAbstractChannel( - allocator: self.channel.allocator, - parent: self.channel, + allocator: self._channel.allocator, + parent: self._channel, multiplexer: multiplexer, streamID: streamID, - targetWindowSize: Int32(self.targetWindowSize), - outboundBytesHighWatermark: self.streamChannelOutboundBytesHighWatermark, - outboundBytesLowWatermark: self.streamChannelOutboundBytesLowWatermark, + targetWindowSize: Int32(self._targetWindowSize), + outboundBytesHighWatermark: self._streamChannelOutboundBytesHighWatermark, + outboundBytesLowWatermark: self._streamChannelOutboundBytesLowWatermark, inboundStreamStateInitializer: .includesStreamID(nil) ) self.streams[streamID] = channel @@ -439,7 +443,7 @@ extension HTTP2CommonInboundStreamMultiplexer { extension HTTP2CommonInboundStreamMultiplexer { func setChannelContinuation(_ streamChannels: any AnyContinuation) { - self.channel.eventLoop.assertInEventLoop() + self._channel.eventLoop.assertInEventLoop() self.streamChannelContinuation = streamChannels } } @@ -449,6 +453,7 @@ extension HTTP2CommonInboundStreamMultiplexer { /// /// This is useful in in the case of the `HTTP2ChannelHandler` which must deal with types which hold stream initializers /// which have a generic return type. +@usableFromInline internal protocol AnyContinuation { func yield(any: Any) func finish() @@ -491,6 +496,7 @@ public struct NIOHTTP2AsyncSequence: AsyncSequence { extension NIOHTTP2AsyncSequence { /// `Continuation` is a wrapper for a generic `AsyncThrowingStream` to which the products of the initializers of /// inbound (remotely-initiated) HTTP/2 stream channels are yielded. + @usableFromInline @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) struct Continuation: AnyContinuation { private var continuation: AsyncThrowingStream.Continuation @@ -502,6 +508,7 @@ extension NIOHTTP2AsyncSequence { /// `yield` takes a channel as outputted by the stream initializer and yields the wrapped `AsyncThrowingStream`. /// /// It takes channels as as `Any` type to allow wrapping by the stream initializer. + @usableFromInline func yield(any: Any) { let yieldResult = self.continuation.yield(any as! Output) switch yieldResult { @@ -517,11 +524,13 @@ extension NIOHTTP2AsyncSequence { } /// `finish` marks the continuation as finished. + @usableFromInline func finish() { self.continuation.finish() } /// `finish` marks the continuation as finished with the supplied error. + @usableFromInline func finish(throwing error: Error) { self.continuation.finish(throwing: error) } @@ -534,6 +543,7 @@ extension NIOHTTP2AsyncSequence { /// - Parameters: /// - inboundStreamInitializerOutput: The type which is returned by the initializer operating on the inbound /// (remotely-initiated) HTTP/2 streams. + @usableFromInline static func initialize(inboundStreamInitializerOutput: Output.Type = Output.self) -> (NIOHTTP2AsyncSequence, Continuation) { let (stream, continuation) = AsyncThrowingStream.makeStream(of: Output.self) return (.init(stream), Continuation(wrapping: continuation)) diff --git a/Sources/NIOHTTP2/HTTP2PipelineHelpers.swift b/Sources/NIOHTTP2/HTTP2PipelineHelpers.swift index cbfea273..0cbbce44 100644 --- a/Sources/NIOHTTP2/HTTP2PipelineHelpers.swift +++ b/Sources/NIOHTTP2/HTTP2PipelineHelpers.swift @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import NIOCore +import NIOHTTP1 import NIOTLS /// The supported ALPN protocol tokens for NIO's HTTP/2 abstraction layer. @@ -434,6 +435,7 @@ extension Channel { /// The output of this closure is the element type of the returned multiplexer /// - Returns: An `EventLoopFuture` containing the `AsyncStreamMultiplexer` inserted into this pipeline, which can /// be used to initiate new streams and iterate over inbound HTTP/2 stream channels. + @inlinable @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func configureAsyncHTTP2Pipeline( mode: NIOHTTP2Handler.ParserMode, @@ -485,6 +487,7 @@ extension Channel { /// channel has been fully mutated. /// - Returns: An `EventLoopFuture` of an `EventLoopFuture` containing the `NIOProtocolNegotiationResult` that completes when the channel /// is ready to negotiate. + @inlinable internal func configureHTTP2AsyncSecureUpgrade( http1ConnectionInitializer: @escaping NIOChannelInitializerWithOutput, http2ConnectionInitializer: @escaping NIOChannelInitializerWithOutput @@ -530,6 +533,7 @@ extension Channel { /// - Returns: An `EventLoopFuture` containing a ``NIOTypedApplicationProtocolNegotiationHandler`` that completes when the channel /// is ready to negotiate. This can then be used to access the ``NIOProtocolNegotiationResult`` which may itself /// be waited on to retrieve the result of the negotiation. + @inlinable @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func configureAsyncHTTPServerPipeline( http2Configuration: NIOHTTP2Handler.Configuration = .init(), @@ -580,6 +584,7 @@ extension ChannelPipeline.SynchronousOperations { /// The output of this closure is the element type of the returned multiplexer /// - Returns: An `EventLoopFuture` containing the `AsyncStreamMultiplexer` inserted into this pipeline, which can /// be used to initiate new streams and iterate over inbound HTTP/2 stream channels. + @inlinable @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public func configureAsyncHTTP2Pipeline( mode: NIOHTTP2Handler.ParserMode, diff --git a/Sources/NIOHTTP2/HTTP2StreamChannel+OutboundStreamMultiplexer.swift b/Sources/NIOHTTP2/HTTP2StreamChannel+OutboundStreamMultiplexer.swift index 8e9bb2f1..799946f2 100644 --- a/Sources/NIOHTTP2/HTTP2StreamChannel+OutboundStreamMultiplexer.swift +++ b/Sources/NIOHTTP2/HTTP2StreamChannel+OutboundStreamMultiplexer.swift @@ -41,6 +41,7 @@ extension HTTP2StreamChannel { /// Abstracts over the integrated stream multiplexing (new) and the chained channel handler (legacy) multiplexing approaches. /// /// We use an enum for this purpose since we can't use a generic (for API compatibility reasons) and it allows us to avoid the cost of using an existential. + @usableFromInline internal enum OutboundStreamMultiplexer: HTTP2OutboundStreamMultiplexer { case legacy(LegacyOutboundStreamMultiplexer) case inline(InlineStreamMultiplexer) @@ -95,6 +96,7 @@ extension HTTP2StreamChannel { /// Provides a 'multiplexer' interface for legacy compatibility. /// /// This doesn't actually do any multiplexing but delegates it to the `HTTP2StreamMultiplexer` which does. +@usableFromInline internal struct LegacyOutboundStreamMultiplexer { let multiplexer: HTTP2StreamMultiplexer } diff --git a/Sources/NIOHTTP2/HTTP2StreamChannel.swift b/Sources/NIOHTTP2/HTTP2StreamChannel.swift index 23cb71db..d00a135b 100644 --- a/Sources/NIOHTTP2/HTTP2StreamChannel.swift +++ b/Sources/NIOHTTP2/HTTP2StreamChannel.swift @@ -145,6 +145,7 @@ private enum HTTP2StreamData { } } +@usableFromInline final class HTTP2StreamChannel: Channel, ChannelCore { /// The stream data type of the channel. private let streamDataType: HTTP2StreamDataType @@ -367,14 +368,17 @@ final class HTTP2StreamChannel: Channel, ChannelCore { public let parent: Channel? + @usableFromInline func localAddress0() throws -> SocketAddress { fatalError() } + @usableFromInline func remoteAddress0() throws -> SocketAddress { fatalError() } + @usableFromInline func setOption(_ option: Option, value: Option.Value) -> EventLoopFuture { if self.eventLoop.inEventLoop { do { diff --git a/Sources/NIOHTTP2/MultiplexerAbstractChannel.swift b/Sources/NIOHTTP2/MultiplexerAbstractChannel.swift index 3a2de26c..d92a9d5c 100644 --- a/Sources/NIOHTTP2/MultiplexerAbstractChannel.swift +++ b/Sources/NIOHTTP2/MultiplexerAbstractChannel.swift @@ -23,9 +23,11 @@ import NIOCore /// /// Note that while this is a `struct`, this `struct` has _reference semantics_. /// The implementation of `Equatable` & `Hashable` on this type reinforces that requirement. +@usableFromInline struct MultiplexerAbstractChannel { - private(set) var baseChannel: HTTP2StreamChannel + @usableFromInline private(set) var baseChannel: HTTP2StreamChannel + @usableFromInline init(allocator: ByteBufferAllocator, parent: Channel, multiplexer: HTTP2StreamChannel.OutboundStreamMultiplexer, @@ -60,6 +62,7 @@ struct MultiplexerAbstractChannel { } extension MultiplexerAbstractChannel { + @usableFromInline enum InboundStreamStateInitializer { case includesStreamID(((Channel, HTTP2StreamID) -> EventLoopFuture)?) case excludesStreamID(((Channel) -> EventLoopFuture)?) @@ -73,6 +76,7 @@ extension MultiplexerAbstractChannel { return self.baseChannel.streamID } + @usableFromInline var channelID: ObjectIdentifier { return ObjectIdentifier(self.baseChannel) } @@ -121,6 +125,7 @@ extension MultiplexerAbstractChannel { } // used for async multiplexer + @usableFromInline func configure(initializer: @escaping NIOChannelInitializerWithOutput, userPromise promise: EventLoopPromise?) { self.baseChannel.configure(initializer: initializer, userPromise: promise) } @@ -167,12 +172,14 @@ extension MultiplexerAbstractChannel { } extension MultiplexerAbstractChannel: Equatable { + @inlinable static func ==(lhs: MultiplexerAbstractChannel, rhs: MultiplexerAbstractChannel) -> Bool { return lhs.baseChannel === rhs.baseChannel } } extension MultiplexerAbstractChannel: Hashable { + @inlinable func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(self.baseChannel)) }