1999 lines
80 KiB
Swift
1999 lines
80 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the SwiftNIO open source project
|
|
//
|
|
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// A list of `ChannelHandler`s that handle or intercept inbound events and outbound operations of a
|
|
/// `Channel`. `ChannelPipeline` implements an advanced form of the Intercepting Filter pattern
|
|
/// to give a user full control over how an event is handled and how the `ChannelHandler`s in a pipeline
|
|
/// interact with each other.
|
|
///
|
|
/// # Creation of a pipeline
|
|
///
|
|
/// Each `Channel` has its own `ChannelPipeline` and it is created automatically when a new `Channel` is created.
|
|
///
|
|
/// # How an event flows in a pipeline
|
|
///
|
|
/// The following diagram describes how I/O events are typically processed by `ChannelHandler`s in a `ChannelPipeline`.
|
|
/// An I/O event is handled by either a `ChannelInboundHandler` or a `ChannelOutboundHandler`
|
|
/// and is forwarded to the next handler in the `ChannelPipeline` by calling the event propagation methods defined in
|
|
/// `ChannelHandlerContext`, such as `ChannelHandlerContext.fireChannelRead` and
|
|
/// `ChannelHandlerContext.write`.
|
|
///
|
|
/// ```
|
|
/// I/O Request
|
|
/// via `Channel` or
|
|
/// `ChannelHandlerContext`
|
|
/// |
|
|
/// +---------------------------------------------------+---------------+
|
|
/// | ChannelPipeline | |
|
|
/// | TAIL \|/ |
|
|
/// | +---------------------+ +-----------+----------+ |
|
|
/// | | Inbound Handler N | | Outbound Handler 1 | |
|
|
/// | +----------+----------+ +-----------+----------+ |
|
|
/// | /|\ | |
|
|
/// | | \|/ |
|
|
/// | +----------+----------+ +-----------+----------+ |
|
|
/// | | Inbound Handler N-1 | | Outbound Handler 2 | |
|
|
/// | +----------+----------+ +-----------+----------+ |
|
|
/// | /|\ . |
|
|
/// | . . |
|
|
/// | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
|
|
/// | [ method call] [method call] |
|
|
/// | . . |
|
|
/// | . \|/ |
|
|
/// | +----------+----------+ +-----------+----------+ |
|
|
/// | | Inbound Handler 2 | | Outbound Handler M-1 | |
|
|
/// | +----------+----------+ +-----------+----------+ |
|
|
/// | /|\ | |
|
|
/// | | \|/ |
|
|
/// | +----------+----------+ +-----------+----------+ |
|
|
/// | | Inbound Handler 1 | | Outbound Handler M | |
|
|
/// | +----------+----------+ +-----------+----------+ |
|
|
/// | /|\ HEAD | |
|
|
/// +---------------+-----------------------------------+---------------+
|
|
/// | \|/
|
|
/// +---------------+-----------------------------------+---------------+
|
|
/// | | | |
|
|
/// | [ Socket.read ] [ Socket.write ] |
|
|
/// | |
|
|
/// | SwiftNIO Internal I/O Threads (Transport Implementation) |
|
|
/// +-------------------------------------------------------------------+
|
|
/// ```
|
|
///
|
|
/// An inbound event is handled by the inbound handlers in the head-to-tail direction as shown on the left side of the
|
|
/// diagram. An inbound handler usually handles the inbound data generated by the I/O thread on the bottom of the
|
|
/// diagram. The inbound data is often read from a remote peer via the actual input operation such as
|
|
/// `Socket.read`. If an inbound event goes beyond the tail inbound handler, it is discarded
|
|
/// silently, or logged if it needs your attention.
|
|
///
|
|
/// An outbound event is handled by the outbound handlers in the tail-to-head direction as shown on the right side of the
|
|
/// diagram. An outbound handler usually generates or transforms the outbound traffic such as write requests.
|
|
/// If an outbound event goes beyond the head outbound handler, it is handled by an I/O thread associated with the
|
|
/// `Channel`. The I/O thread often performs the actual output operation such as `Socket.write`.
|
|
///
|
|
///
|
|
/// For example, let us assume that we created the following pipeline:
|
|
///
|
|
/// ```
|
|
/// ChannelPipeline p = ...
|
|
/// let future = p.add(name: "1", handler: InboundHandlerA()).flatMap {
|
|
/// p.add(name: "2", handler: InboundHandlerB())
|
|
/// }.flatMap {
|
|
/// p.add(name: "3", handler: OutboundHandlerA())
|
|
/// }.flatMap {
|
|
/// p.add(name: "4", handler: OutboundHandlerB())
|
|
/// }.flatMap {
|
|
/// p.add(name: "5", handler: InboundOutboundHandlerX())
|
|
/// }
|
|
/// // Handle the future as well ....
|
|
/// ```
|
|
///
|
|
/// In the example above, a class whose name starts with `Inbound` is an inbound handler.
|
|
/// A class whose name starts with `Outbound` is an outbound handler.
|
|
///
|
|
/// In the given example configuration, the handler evaluation order is 1, 2, 3, 4, 5 when an event goes inbound.
|
|
/// When an event goes outbound, the order is 5, 4, 3, 2, 1. On top of this principle, `ChannelPipeline` skips
|
|
/// the evaluation of certain handlers to shorten the stack depth:
|
|
///
|
|
/// - 3 and 4 don't implement `ChannelInboundHandler`, and therefore the actual evaluation order of an inbound event will be: 1, 2, and 5.
|
|
/// - 1 and 2 don't implement `ChannelOutboundHandler`, and therefore the actual evaluation order of a outbound event will be: 5, 4, and 3.
|
|
/// - If 5 implements both `ChannelInboundHandler` and `ChannelOutboundHandler`, the evaluation order of an inbound and a outbound event could be 125 and 543 respectively.
|
|
///
|
|
/// Note: Handlers may choose not to propagate messages down the pipeline immediately. For example a handler may need to wait
|
|
/// for additional data before sending a protocol event to the next handler in the pipeline. Due to this you can't assume that later handlers
|
|
/// in the pipeline will receive the same number of events as were sent, or that events of different types will arrive in the same order.
|
|
/// For example - a user event could overtake a data event if a handler is aggregating data events before propagating but immediately
|
|
/// propagating user events.
|
|
///
|
|
/// # Forwarding an event to the next handler
|
|
///
|
|
/// As you might noticed in the diagram above, a handler has to invoke the event propagation methods in
|
|
/// `ChannelHandlerContext` to forward an event to its next handler.
|
|
/// Those methods include:
|
|
///
|
|
/// - Inbound event propagation methods defined in `ChannelInboundInvoker`
|
|
/// - Outbound event propagation methods defined in `ChannelOutboundInvoker`.
|
|
///
|
|
/// # Building a pipeline
|
|
///
|
|
/// A user is supposed to have one or more `ChannelHandler`s in a `ChannelPipeline` to receive I/O events (e.g. read) and
|
|
/// to request I/O operations (e.g. write and close). For example, a typical server will have the following handlers
|
|
/// in each channel's pipeline, but your mileage may vary depending on the complexity and characteristics of the
|
|
/// protocol and business logic:
|
|
///
|
|
/// - Protocol Decoder - translates binary data (e.g. `ByteBuffer`) into a struct / class
|
|
/// - Protocol Encoder - translates a struct / class into binary data (e.g. `ByteBuffer`)
|
|
/// - Business Logic Handler - performs the actual business logic (e.g. database access)
|
|
///
|
|
/// # Thread safety
|
|
///
|
|
/// A `ChannelHandler` can be added or removed at any time because a `ChannelPipeline` is thread safe.
|
|
public final class ChannelPipeline: ChannelInvoker {
|
|
private var head: Optional<ChannelHandlerContext>
|
|
private var tail: Optional<ChannelHandlerContext>
|
|
|
|
private var idx: Int = 0
|
|
internal private(set) var destroyed: Bool = false
|
|
|
|
/// The `EventLoop` that is used by the underlying `Channel`.
|
|
public let eventLoop: EventLoop
|
|
|
|
/// The `Channel` that this `ChannelPipeline` belongs to.
|
|
///
|
|
/// - note: This will be nil after the channel has closed
|
|
private var _channel: Optional<Channel>
|
|
|
|
/// The `Channel` that this `ChannelPipeline` belongs to.
|
|
internal var channel: Channel {
|
|
self.eventLoop.assertInEventLoop()
|
|
assert(self._channel != nil || self.destroyed)
|
|
return self._channel ?? DeadChannel(pipeline: self)
|
|
}
|
|
|
|
/// Add a `ChannelHandler` to the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - name: the name to use for the `ChannelHandler` when it's added. If none is specified it will generate a name.
|
|
/// - handler: the `ChannelHandler` to add
|
|
/// - position: The position in the `ChannelPipeline` to add `handler`. Defaults to `.last`.
|
|
/// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was added.
|
|
public func addHandler(_ handler: ChannelHandler,
|
|
name: String? = nil,
|
|
position: ChannelPipeline.Position = .last) -> EventLoopFuture<Void> {
|
|
let future: EventLoopFuture<Void>
|
|
|
|
if self.eventLoop.inEventLoop {
|
|
future = self.eventLoop.makeCompletedFuture(self.addHandlerSync(handler, name: name, position: position))
|
|
} else {
|
|
future = self.eventLoop.submit {
|
|
try self.addHandlerSync(handler, name: name, position: position).get()
|
|
}
|
|
}
|
|
|
|
return future
|
|
}
|
|
|
|
/// Synchronously add a `ChannelHandler` to the `ChannelPipeline`.
|
|
///
|
|
/// May only be called from on the event loop.
|
|
///
|
|
/// - parameters:
|
|
/// - handler: the `ChannelHandler` to add
|
|
/// - name: the name to use for the `ChannelHandler` when it's added. If none is specified a name will be generated.
|
|
/// - position: The position in the `ChannelPipeline` to add `handler`. Defaults to `.last`.
|
|
/// - returns: the result of adding this handler - either success or failure with an error code if this could not be completed.
|
|
fileprivate func addHandlerSync(_ handler: ChannelHandler,
|
|
name: String? = nil,
|
|
position: ChannelPipeline.Position = .last) -> Result<Void, Error> {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if self.destroyed {
|
|
return .failure(ChannelError.ioOnClosedChannel)
|
|
}
|
|
|
|
switch position {
|
|
case .first:
|
|
return self.add0(name: name,
|
|
handler: handler,
|
|
relativeContext: head!,
|
|
operation: self.add0(context:after:))
|
|
case .last:
|
|
return self.add0(name: name,
|
|
handler: handler,
|
|
relativeContext: tail!,
|
|
operation: self.add0(context:before:))
|
|
case .before(let beforeHandler):
|
|
return self.add0(name: name,
|
|
handler: handler,
|
|
relativeHandler: beforeHandler,
|
|
operation: self.add0(context:before:))
|
|
case .after(let afterHandler):
|
|
return self.add0(name: name,
|
|
handler: handler,
|
|
relativeHandler: afterHandler,
|
|
operation: self.add0(context:after:))
|
|
}
|
|
}
|
|
|
|
/// Synchronously add a `ChannelHandler` to the pipeline, relative to another `ChannelHandler`,
|
|
/// where the insertion is done by a specific operation.
|
|
///
|
|
/// May only be called from on the event loop.
|
|
///
|
|
/// This will search the pipeline for `relativeHandler` and, if it cannot find it, will fail
|
|
/// `promise` with `ChannelPipelineError.notFound`.
|
|
///
|
|
/// - parameters:
|
|
/// - name: The name to use for the `ChannelHandler` when its added. If none is specified, a name will be
|
|
/// automatically generated.
|
|
/// - handler: The `ChannelHandler` to add.
|
|
/// - relativeHandler: The `ChannelHandler` already in the `ChannelPipeline` that `handler` will be
|
|
/// inserted relative to.
|
|
/// - operation: A callback that will insert `handler` relative to `relativeHandler`.
|
|
/// - returns: the result of adding this handler - either success or failure with an error code if this could not be completed.
|
|
private func add0(name: String?,
|
|
handler: ChannelHandler,
|
|
relativeHandler: ChannelHandler,
|
|
operation: (ChannelHandlerContext, ChannelHandlerContext) -> Void) -> Result<Void, Error> {
|
|
self.eventLoop.assertInEventLoop()
|
|
if self.destroyed {
|
|
return .failure(ChannelError.ioOnClosedChannel)
|
|
}
|
|
|
|
guard let context = self.contextForPredicate0({ $0.handler === relativeHandler }) else {
|
|
return .failure(ChannelPipelineError.notFound)
|
|
}
|
|
|
|
return self.add0(name: name, handler: handler, relativeContext: context, operation: operation)
|
|
}
|
|
|
|
/// Synchronously add a `ChannelHandler` to the pipeline, relative to a `ChannelHandlerContext`,
|
|
/// where the insertion is done by a specific operation.
|
|
///
|
|
/// May only be called from on the event loop.
|
|
///
|
|
/// This method is more efficient than the one that takes a `relativeHandler` as it does not need to
|
|
/// search the pipeline for the insertion point. It should be used whenever possible.
|
|
///
|
|
/// - parameters:
|
|
/// - name: The name to use for the `ChannelHandler` when its added. If none is specified, a name will be
|
|
/// automatically generated.
|
|
/// - handler: The `ChannelHandler` to add.
|
|
/// - relativeContext: The `ChannelHandlerContext` already in the `ChannelPipeline` that `handler` will be
|
|
/// inserted relative to.
|
|
/// - operation: A callback that will insert `handler` relative to `relativeHandler`.
|
|
/// - returns: the result of adding this handler - either success or failure with an error code if this could not be completed.
|
|
private func add0(name: String?,
|
|
handler: ChannelHandler,
|
|
relativeContext: ChannelHandlerContext,
|
|
operation: (ChannelHandlerContext, ChannelHandlerContext) -> Void) -> Result<Void, Error> {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if self.destroyed {
|
|
return .failure(ChannelError.ioOnClosedChannel)
|
|
}
|
|
|
|
let context = ChannelHandlerContext(name: name ?? nextName(), handler: handler, pipeline: self)
|
|
operation(context, relativeContext)
|
|
|
|
context.invokeHandlerAdded()
|
|
return .success(())
|
|
}
|
|
|
|
/// Synchronously add a single new `ChannelHandlerContext` after one that currently exists in the
|
|
/// pipeline.
|
|
///
|
|
/// Must be called from within the event loop thread, as it synchronously manipulates the
|
|
/// `ChannelHandlerContext`s on the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - new: The `ChannelHandlerContext` to add to the pipeline.
|
|
/// - existing: The `ChannelHandlerContext` that `new` will be added after.
|
|
private func add0(context new: ChannelHandlerContext, after existing: ChannelHandlerContext) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
let next = existing.next
|
|
new.prev = existing
|
|
new.next = next
|
|
existing.next = new
|
|
next?.prev = new
|
|
}
|
|
|
|
/// Synchronously add a single new `ChannelHandlerContext` before one that currently exists in the
|
|
/// pipeline.
|
|
///
|
|
/// Must be called from within the event loop thread, as it synchronously manipulates the
|
|
/// `ChannelHandlerContext`s on the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - new: The `ChannelHandlerContext` to add to the pipeline.
|
|
/// - existing: The `ChannelHandlerContext` that `new` will be added before.
|
|
private func add0(context new: ChannelHandlerContext, before existing: ChannelHandlerContext) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
let prev = existing.prev
|
|
new.prev = prev
|
|
new.next = existing
|
|
existing.prev = new
|
|
prev?.next = new
|
|
}
|
|
|
|
/// Remove a `ChannelHandler` from the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - handler: the `ChannelHandler` to remove.
|
|
/// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was removed.
|
|
public func removeHandler(_ handler: RemovableChannelHandler) -> EventLoopFuture<Void> {
|
|
let promise = self.eventLoop.makePromise(of: Void.self)
|
|
self.removeHandler(handler, promise: promise)
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Remove a `ChannelHandler` from the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - name: the name that was used to add the `ChannelHandler` to the `ChannelPipeline` before.
|
|
/// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was removed.
|
|
public func removeHandler(name: String) -> EventLoopFuture<Void> {
|
|
let promise = self.eventLoop.makePromise(of: Void.self)
|
|
self.removeHandler(name: name, promise: promise)
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Remove a `ChannelHandler` from the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - context: the `ChannelHandlerContext` that belongs to `ChannelHandler` that should be removed.
|
|
/// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was removed.
|
|
public func removeHandler(context: ChannelHandlerContext) -> EventLoopFuture<Void> {
|
|
let promise = self.eventLoop.makePromise(of: Void.self)
|
|
self.removeHandler(context: context, promise: promise)
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Remove a `ChannelHandler` from the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - handler: the `ChannelHandler` to remove.
|
|
/// - promise: An `EventLoopPromise` that will complete when the `ChannelHandler` is removed.
|
|
public func removeHandler(_ handler: RemovableChannelHandler, promise: EventLoopPromise<Void>?) {
|
|
func removeHandler0() {
|
|
switch self.contextSync(handler: handler) {
|
|
case .success(let context):
|
|
self.removeHandler(context: context, promise: promise)
|
|
case .failure(let error):
|
|
promise?.fail(error)
|
|
}
|
|
}
|
|
|
|
if self.eventLoop.inEventLoop {
|
|
removeHandler0()
|
|
} else {
|
|
self.eventLoop.execute {
|
|
removeHandler0()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Remove a `ChannelHandler` from the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - name: the name that was used to add the `ChannelHandler` to the `ChannelPipeline` before.
|
|
/// - promise: An `EventLoopPromise` that will complete when the `ChannelHandler` is removed.
|
|
public func removeHandler(name: String, promise: EventLoopPromise<Void>?) {
|
|
func removeHandler0() {
|
|
switch self.contextSync(name: name) {
|
|
case .success(let context):
|
|
self.removeHandler(context: context, promise: promise)
|
|
case .failure(let error):
|
|
promise?.fail(error)
|
|
}
|
|
}
|
|
|
|
if self.eventLoop.inEventLoop {
|
|
removeHandler0()
|
|
} else {
|
|
self.eventLoop.execute {
|
|
removeHandler0()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Remove a `ChannelHandler` from the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - context: the `ChannelHandlerContext` that belongs to `ChannelHandler` that should be removed.
|
|
/// - promise: An `EventLoopPromise` that will complete when the `ChannelHandler` is removed.
|
|
public func removeHandler(context: ChannelHandlerContext, promise: EventLoopPromise<Void>?) {
|
|
guard context.handler is RemovableChannelHandler else {
|
|
promise?.fail(ChannelError.unremovableHandler)
|
|
return
|
|
}
|
|
func removeHandler0() {
|
|
context.startUserTriggeredRemoval(promise: promise)
|
|
}
|
|
|
|
if self.eventLoop.inEventLoop {
|
|
removeHandler0()
|
|
} else {
|
|
self.eventLoop.execute {
|
|
removeHandler0()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns the `ChannelHandlerContext` that belongs to a `ChannelHandler`.
|
|
///
|
|
/// - parameters:
|
|
/// - handler: the `ChannelHandler` for which the `ChannelHandlerContext` should be returned
|
|
/// - returns: the `EventLoopFuture` which will be notified once the the operation completes.
|
|
public func context(handler: ChannelHandler) -> EventLoopFuture<ChannelHandlerContext> {
|
|
let promise = self.eventLoop.makePromise(of: ChannelHandlerContext.self)
|
|
|
|
if self.eventLoop.inEventLoop {
|
|
promise.completeWith(self.contextSync(handler: handler))
|
|
} else {
|
|
self.eventLoop.execute {
|
|
promise.completeWith(self.contextSync(handler: handler))
|
|
}
|
|
}
|
|
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Synchronously returns the `ChannelHandlerContext` that belongs to a `ChannelHandler`.
|
|
///
|
|
/// - Important: This must be called on the `EventLoop`.
|
|
/// - parameters:
|
|
/// - handler: the `ChannelHandler` for which the `ChannelHandlerContext` should be returned
|
|
/// - returns: the `ChannelHandlerContext` that belongs to the `ChannelHandler`, if one exists.
|
|
fileprivate func contextSync(handler: ChannelHandler) -> Result<ChannelHandlerContext, Error> {
|
|
return self._contextSync({ $0.handler === handler })
|
|
}
|
|
|
|
/// Returns the `ChannelHandlerContext` that belongs to a `ChannelHandler`.
|
|
///
|
|
/// - parameters:
|
|
/// - name: the name that was used to add the `ChannelHandler` to the `ChannelPipeline` before.
|
|
/// - returns: the `EventLoopFuture` which will be notified once the the operation completes.
|
|
public func context(name: String) -> EventLoopFuture<ChannelHandlerContext> {
|
|
let promise = self.eventLoop.makePromise(of: ChannelHandlerContext.self)
|
|
|
|
if self.eventLoop.inEventLoop {
|
|
promise.completeWith(self.contextSync(name: name))
|
|
} else {
|
|
self.eventLoop.execute {
|
|
promise.completeWith(self.contextSync(name: name))
|
|
}
|
|
}
|
|
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Synchronously finds and returns the `ChannelHandlerContext` that belongs to the
|
|
/// `ChannelHandler` with the given name.
|
|
///
|
|
/// - Important: This must be called on the `EventLoop`.
|
|
/// - Parameter name: The name of the `ChannelHandler` to find.
|
|
/// - Returns: the `ChannelHandlerContext` that belongs to the `ChannelHandler`, if one exists.
|
|
fileprivate func contextSync(name: String) -> Result<ChannelHandlerContext, Error> {
|
|
return self._contextSync({ $0.name == name })
|
|
}
|
|
|
|
/// Returns the `ChannelHandlerContext` that belongs to a `ChannelHandler` of the given type.
|
|
///
|
|
/// If multiple channel handlers of the same type are present in the pipeline, returns the context
|
|
/// belonging to the first such handler.
|
|
///
|
|
/// - parameters:
|
|
/// - handlerType: The type of the handler to search for.
|
|
/// - returns: the `EventLoopFuture` which will be notified once the the operation completes.
|
|
@inlinable
|
|
public func context<Handler: ChannelHandler>(handlerType: Handler.Type) -> EventLoopFuture<ChannelHandlerContext> {
|
|
let promise = self.eventLoop.makePromise(of: ChannelHandlerContext.self)
|
|
|
|
if self.eventLoop.inEventLoop {
|
|
promise.completeWith(self._contextSync(handlerType: handlerType))
|
|
} else {
|
|
self.eventLoop.execute {
|
|
promise.completeWith(self._contextSync(handlerType: handlerType))
|
|
}
|
|
}
|
|
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Synchronously finds and returns the `ChannelHandlerContext` that belongs to the first
|
|
/// `ChannelHandler` of the given type.
|
|
///
|
|
/// - Important: This must be called on the `EventLoop`.
|
|
/// - Parameter handlerType: The type of handler to search for.
|
|
/// - Returns: the `ChannelHandlerContext` that belongs to the `ChannelHandler`, if one exists.
|
|
@inlinable // should be fileprivate
|
|
internal func _contextSync<Handler: ChannelHandler>(handlerType: Handler.Type) -> Result<ChannelHandlerContext, Error> {
|
|
return self._contextSync({ $0.handler is Handler })
|
|
}
|
|
|
|
/// Synchronously finds a `ChannelHandlerContext` in the `ChannelPipeline`.
|
|
/// - Important: This must be called on the `EventLoop`.
|
|
@usableFromInline // should be fileprivate
|
|
internal func _contextSync(_ body: (ChannelHandlerContext) -> Bool) -> Result<ChannelHandlerContext, Error> {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let context = self.contextForPredicate0(body) {
|
|
return .success(context)
|
|
} else {
|
|
return .failure(ChannelPipelineError.notFound)
|
|
}
|
|
}
|
|
|
|
/// Returns a `ChannelHandlerContext` which matches.
|
|
///
|
|
/// This skips head and tail (as these are internal and should not be accessible by the user).
|
|
///
|
|
/// - parameters:
|
|
/// - body: The predicate to execute per `ChannelHandlerContext` in the `ChannelPipeline`.
|
|
/// - returns: The first `ChannelHandlerContext` that matches or `nil` if none did.
|
|
private func contextForPredicate0(_ body: (ChannelHandlerContext) -> Bool) -> ChannelHandlerContext? {
|
|
var curCtx: ChannelHandlerContext? = self.head?.next
|
|
while let context = curCtx, context !== self.tail {
|
|
if body(context) {
|
|
return context
|
|
}
|
|
curCtx = context.next
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
/// Remove a `ChannelHandlerContext` from the `ChannelPipeline` directly without going through the
|
|
/// `RemovableChannelHandler` API. This must only be used to clear the pipeline on `Channel` tear down and
|
|
/// as a result of the `leavePipeline` call in the `RemovableChannelHandler` API.
|
|
internal func removeHandlerFromPipeline(context: ChannelHandlerContext, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
let nextCtx = context.next
|
|
let prevCtx = context.prev
|
|
|
|
if let prevCtx = prevCtx {
|
|
prevCtx.next = nextCtx
|
|
}
|
|
if let nextCtx = nextCtx {
|
|
nextCtx.prev = prevCtx
|
|
}
|
|
|
|
context.invokeHandlerRemoved()
|
|
promise?.succeed(())
|
|
|
|
// We need to keep the current node alive until after the callout in case the user uses the context.
|
|
context.next = nil
|
|
context.prev = nil
|
|
}
|
|
|
|
/// Returns the next name to use for a `ChannelHandler`.
|
|
private func nextName() -> String {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
let name = "handler\(idx)"
|
|
idx += 1
|
|
return name
|
|
}
|
|
|
|
/// Remove all the `ChannelHandler`s from the `ChannelPipeline` and destroy these.
|
|
///
|
|
/// This method must only be called from within the `EventLoop`. It should only be called from a `ChannelCore`
|
|
/// implementation. Once called, the `ChannelPipeline` is no longer active and cannot be used again.
|
|
func removeHandlers() {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let head = self.head {
|
|
while let context = head.next {
|
|
removeHandlerFromPipeline(context: context, promise: nil)
|
|
}
|
|
removeHandlerFromPipeline(context: self.head!, promise: nil)
|
|
}
|
|
self.head = nil
|
|
self.tail = nil
|
|
|
|
self.destroyed = true
|
|
self._channel = nil
|
|
}
|
|
|
|
// Just delegate to the head and tail context
|
|
public func fireChannelRegistered() {
|
|
if eventLoop.inEventLoop {
|
|
fireChannelRegistered0()
|
|
} else {
|
|
eventLoop.execute {
|
|
self.fireChannelRegistered0()
|
|
}
|
|
}
|
|
}
|
|
|
|
public func fireChannelUnregistered() {
|
|
if eventLoop.inEventLoop {
|
|
fireChannelUnregistered0()
|
|
} else {
|
|
eventLoop.execute {
|
|
self.fireChannelUnregistered0()
|
|
}
|
|
}
|
|
}
|
|
|
|
public func fireChannelInactive() {
|
|
if eventLoop.inEventLoop {
|
|
fireChannelInactive0()
|
|
} else {
|
|
eventLoop.execute {
|
|
self.fireChannelInactive0()
|
|
}
|
|
}
|
|
}
|
|
|
|
public func fireChannelActive() {
|
|
if eventLoop.inEventLoop {
|
|
fireChannelActive0()
|
|
} else {
|
|
eventLoop.execute {
|
|
self.fireChannelActive0()
|
|
}
|
|
}
|
|
}
|
|
|
|
public func fireChannelRead(_ data: NIOAny) {
|
|
if eventLoop.inEventLoop {
|
|
fireChannelRead0(data)
|
|
} else {
|
|
eventLoop.execute {
|
|
self.fireChannelRead0(data)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func fireChannelReadComplete() {
|
|
if eventLoop.inEventLoop {
|
|
fireChannelReadComplete0()
|
|
} else {
|
|
eventLoop.execute {
|
|
self.fireChannelReadComplete0()
|
|
}
|
|
}
|
|
}
|
|
|
|
public func fireChannelWritabilityChanged() {
|
|
if eventLoop.inEventLoop {
|
|
fireChannelWritabilityChanged0()
|
|
} else {
|
|
eventLoop.execute {
|
|
self.fireChannelWritabilityChanged0()
|
|
}
|
|
}
|
|
}
|
|
|
|
public func fireUserInboundEventTriggered(_ event: Any) {
|
|
if eventLoop.inEventLoop {
|
|
fireUserInboundEventTriggered0(event)
|
|
} else {
|
|
eventLoop.execute {
|
|
self.fireUserInboundEventTriggered0(event)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func fireErrorCaught(_ error: Error) {
|
|
if eventLoop.inEventLoop {
|
|
fireErrorCaught0(error: error)
|
|
} else {
|
|
eventLoop.execute {
|
|
self.fireErrorCaught0(error: error)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func close(mode: CloseMode = .all, promise: EventLoopPromise<Void>?) {
|
|
if eventLoop.inEventLoop {
|
|
close0(mode: mode, promise: promise)
|
|
} else {
|
|
eventLoop.execute {
|
|
self.close0(mode: mode, promise: promise)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func flush() {
|
|
if eventLoop.inEventLoop {
|
|
flush0()
|
|
} else {
|
|
eventLoop.execute {
|
|
self.flush0()
|
|
}
|
|
}
|
|
}
|
|
|
|
public func read() {
|
|
if eventLoop.inEventLoop {
|
|
read0()
|
|
} else {
|
|
eventLoop.execute {
|
|
self.read0()
|
|
}
|
|
}
|
|
}
|
|
|
|
public func write(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
if eventLoop.inEventLoop {
|
|
write0(data, promise: promise)
|
|
} else {
|
|
eventLoop.execute {
|
|
self.write0(data, promise: promise)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func writeAndFlush(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
if eventLoop.inEventLoop {
|
|
writeAndFlush0(data, promise: promise)
|
|
} else {
|
|
eventLoop.execute {
|
|
self.writeAndFlush0(data, promise: promise)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func bind(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
if eventLoop.inEventLoop {
|
|
bind0(to: address, promise: promise)
|
|
} else {
|
|
eventLoop.execute {
|
|
self.bind0(to: address, promise: promise)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func connect(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
if eventLoop.inEventLoop {
|
|
connect0(to: address, promise: promise)
|
|
} else {
|
|
eventLoop.execute {
|
|
self.connect0(to: address, promise: promise)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func register(promise: EventLoopPromise<Void>?) {
|
|
if eventLoop.inEventLoop {
|
|
register0(promise: promise)
|
|
} else {
|
|
eventLoop.execute {
|
|
self.register0(promise: promise)
|
|
}
|
|
}
|
|
}
|
|
|
|
public func triggerUserOutboundEvent(_ event: Any, promise: EventLoopPromise<Void>?) {
|
|
if eventLoop.inEventLoop {
|
|
triggerUserOutboundEvent0(event, promise: promise)
|
|
} else {
|
|
eventLoop.execute {
|
|
self.triggerUserOutboundEvent0(event, promise: promise)
|
|
}
|
|
}
|
|
}
|
|
|
|
// These methods are expected to only be called from within the EventLoop
|
|
|
|
private var firstOutboundCtx: ChannelHandlerContext? {
|
|
return self.tail?.prev
|
|
}
|
|
|
|
private var firstInboundCtx: ChannelHandlerContext? {
|
|
return self.head?.next
|
|
}
|
|
|
|
private func close0(mode: CloseMode, promise: EventLoopPromise<Void>?) {
|
|
if let firstOutboundCtx = firstOutboundCtx {
|
|
firstOutboundCtx.invokeClose(mode: mode, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.alreadyClosed)
|
|
}
|
|
}
|
|
|
|
private func flush0() {
|
|
if let firstOutboundCtx = firstOutboundCtx {
|
|
firstOutboundCtx.invokeFlush()
|
|
}
|
|
}
|
|
|
|
private func read0() {
|
|
if let firstOutboundCtx = firstOutboundCtx {
|
|
firstOutboundCtx.invokeRead()
|
|
}
|
|
}
|
|
|
|
private func write0(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
if let firstOutboundCtx = firstOutboundCtx {
|
|
firstOutboundCtx.invokeWrite(data, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
private func writeAndFlush0(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
if let firstOutboundCtx = firstOutboundCtx {
|
|
firstOutboundCtx.invokeWriteAndFlush(data, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
private func bind0(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
if let firstOutboundCtx = firstOutboundCtx {
|
|
firstOutboundCtx.invokeBind(to: address, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
private func connect0(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
if let firstOutboundCtx = firstOutboundCtx {
|
|
firstOutboundCtx.invokeConnect(to: address, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
private func register0(promise: EventLoopPromise<Void>?) {
|
|
if let firstOutboundCtx = firstOutboundCtx {
|
|
firstOutboundCtx.invokeRegister(promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
private func triggerUserOutboundEvent0(_ event: Any, promise: EventLoopPromise<Void>?) {
|
|
if let firstOutboundCtx = firstOutboundCtx {
|
|
firstOutboundCtx.invokeTriggerUserOutboundEvent(event, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
private func fireChannelRegistered0() {
|
|
if let firstInboundCtx = firstInboundCtx {
|
|
firstInboundCtx.invokeChannelRegistered()
|
|
}
|
|
}
|
|
|
|
private func fireChannelUnregistered0() {
|
|
if let firstInboundCtx = firstInboundCtx {
|
|
firstInboundCtx.invokeChannelUnregistered()
|
|
}
|
|
}
|
|
|
|
private func fireChannelInactive0() {
|
|
if let firstInboundCtx = firstInboundCtx {
|
|
firstInboundCtx.invokeChannelInactive()
|
|
}
|
|
}
|
|
|
|
private func fireChannelActive0() {
|
|
if let firstInboundCtx = firstInboundCtx {
|
|
firstInboundCtx.invokeChannelActive()
|
|
}
|
|
}
|
|
|
|
private func fireChannelRead0(_ data: NIOAny) {
|
|
if let firstInboundCtx = firstInboundCtx {
|
|
firstInboundCtx.invokeChannelRead(data)
|
|
}
|
|
}
|
|
|
|
private func fireChannelReadComplete0() {
|
|
if let firstInboundCtx = firstInboundCtx {
|
|
firstInboundCtx.invokeChannelReadComplete()
|
|
}
|
|
}
|
|
|
|
private func fireChannelWritabilityChanged0() {
|
|
if let firstInboundCtx = firstInboundCtx {
|
|
firstInboundCtx.invokeChannelWritabilityChanged()
|
|
}
|
|
}
|
|
|
|
private func fireUserInboundEventTriggered0(_ event: Any) {
|
|
if let firstInboundCtx = firstInboundCtx {
|
|
firstInboundCtx.invokeUserInboundEventTriggered(event)
|
|
}
|
|
}
|
|
|
|
private func fireErrorCaught0(error: Error) {
|
|
assert((error as? ChannelError).map { $0 != .eof } ?? true)
|
|
if let firstInboundCtx = firstInboundCtx {
|
|
firstInboundCtx.invokeErrorCaught(error)
|
|
}
|
|
}
|
|
|
|
private var inEventLoop: Bool {
|
|
return eventLoop.inEventLoop
|
|
}
|
|
|
|
/// Create `ChannelPipeline` for a given `Channel`. This method should never be called by the end-user
|
|
/// directly: it is only intended for use with custom `Channel` implementations. Users should always use
|
|
/// `channel.pipeline` to access the `ChannelPipeline` for a `Channel`.
|
|
///
|
|
/// - parameters:
|
|
/// - channel: The `Channel` this `ChannelPipeline` is created for.
|
|
public init(channel: Channel) {
|
|
self._channel = channel
|
|
self.eventLoop = channel.eventLoop
|
|
self.head = nil // we need to initialise these to `nil` so we can use `self` in the lines below
|
|
self.tail = nil // we need to initialise these to `nil` so we can use `self` in the lines below
|
|
|
|
self.head = ChannelHandlerContext(name: HeadChannelHandler.name, handler: HeadChannelHandler.sharedInstance, pipeline: self)
|
|
self.tail = ChannelHandlerContext(name: TailChannelHandler.name, handler: TailChannelHandler.sharedInstance, pipeline: self)
|
|
self.head?.next = self.tail
|
|
self.tail?.prev = self.head
|
|
}
|
|
}
|
|
|
|
#if swift(>=5.7)
|
|
extension ChannelPipeline: @unchecked Sendable {}
|
|
#endif
|
|
|
|
extension ChannelPipeline {
|
|
/// Adds the provided channel handlers to the pipeline in the order given, taking account
|
|
/// of the behaviour of `ChannelHandler.add(first:)`.
|
|
///
|
|
/// - parameters:
|
|
/// - handlers: The array of `ChannelHandler`s to be added.
|
|
/// - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
|
|
///
|
|
/// - returns: A future that will be completed when all of the supplied `ChannelHandler`s were added.
|
|
public func addHandlers(_ handlers: [ChannelHandler],
|
|
position: ChannelPipeline.Position = .last) -> EventLoopFuture<Void> {
|
|
let future: EventLoopFuture<Void>
|
|
|
|
if self.eventLoop.inEventLoop {
|
|
future = self.eventLoop.makeCompletedFuture(self.addHandlersSync(handlers, position: position))
|
|
} else {
|
|
future = self.eventLoop.submit {
|
|
try self.addHandlersSync(handlers, position: position).get()
|
|
}
|
|
}
|
|
|
|
return future
|
|
}
|
|
|
|
/// Adds the provided channel handlers to the pipeline in the order given, taking account
|
|
/// of the behaviour of `ChannelHandler.add(first:)`.
|
|
///
|
|
/// - parameters:
|
|
/// - handlers: One or more `ChannelHandler`s to be added.
|
|
/// - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
|
|
///
|
|
/// - returns: A future that will be completed when all of the supplied `ChannelHandler`s were added.
|
|
public func addHandlers(_ handlers: ChannelHandler...,
|
|
position: ChannelPipeline.Position = .last) -> EventLoopFuture<Void> {
|
|
return self.addHandlers(handlers, position: position)
|
|
}
|
|
|
|
/// Synchronously adds the provided `ChannelHandler`s to the pipeline in the order given, taking
|
|
/// account of the behaviour of `ChannelHandler.add(first:)`.
|
|
///
|
|
/// - Important: Must be called on the `EventLoop`.
|
|
/// - Parameters:
|
|
/// - handlers: The array of `ChannelHandler`s to add.
|
|
/// - position: The position in the `ChannelPipeline` to add the handlers.
|
|
/// - Returns: A result representing whether the handlers were added or not.
|
|
fileprivate func addHandlersSync(_ handlers: [ChannelHandler],
|
|
position: ChannelPipeline.Position) -> Result<Void, Error> {
|
|
switch position {
|
|
case .first, .after:
|
|
return self._addHandlersSync(handlers.reversed(), position: position)
|
|
case .last, .before:
|
|
return self._addHandlersSync(handlers, position: position)
|
|
}
|
|
}
|
|
|
|
/// Synchronously adds a sequence of `ChannelHandlers` to the pipeline at the given position.
|
|
///
|
|
/// - Important: Must be called on the `EventLoop`.
|
|
/// - Parameters:
|
|
/// - handlers: A sequence of handlers to add.
|
|
/// - position: The position in the `ChannelPipeline` to add the handlers.
|
|
/// - Returns: A result representing whether the handlers were added or not.
|
|
private func _addHandlersSync<Handlers: Sequence>(_ handlers: Handlers,
|
|
position: ChannelPipeline.Position) -> Result<Void, Error> where Handlers.Element == ChannelHandler {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
for handler in handlers {
|
|
let result = self.addHandlerSync(handler, position: position)
|
|
switch result {
|
|
case .success:
|
|
()
|
|
case .failure:
|
|
return result
|
|
}
|
|
}
|
|
|
|
return .success(())
|
|
}
|
|
}
|
|
|
|
// MARK: - Synchronous View
|
|
|
|
extension ChannelPipeline {
|
|
/// A view of a `ChannelPipeline` which may be used to invoke synchronous operations.
|
|
///
|
|
/// All functions **must** be called from the pipeline's event loop.
|
|
public struct SynchronousOperations {
|
|
@usableFromInline
|
|
internal let _pipeline: ChannelPipeline
|
|
|
|
fileprivate init(pipeline: ChannelPipeline) {
|
|
self._pipeline = pipeline
|
|
}
|
|
|
|
/// The `EventLoop` of the `Channel` this synchronous operations view corresponds to.
|
|
public var eventLoop: EventLoop {
|
|
return self._pipeline.eventLoop
|
|
}
|
|
|
|
/// Add a handler to the pipeline.
|
|
///
|
|
/// - Important: This *must* be called on the event loop.
|
|
/// - Parameters:
|
|
/// - handler: The handler to add.
|
|
/// - name: The name to use for the `ChannelHandler` when it's added. If no name is specified the one will be generated.
|
|
/// - position: The position in the `ChannelPipeline` to add `handler`. Defaults to `.last`.
|
|
public func addHandler(_ handler: ChannelHandler,
|
|
name: String? = nil,
|
|
position: ChannelPipeline.Position = .last) throws {
|
|
try self._pipeline.addHandlerSync(handler, name: name, position: position).get()
|
|
}
|
|
|
|
/// Add an array of handlers to the pipeline.
|
|
///
|
|
/// - Important: This *must* be called on the event loop.
|
|
/// - Parameters:
|
|
/// - handlers: The handlers to add.
|
|
/// - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
|
|
public func addHandlers(_ handlers: [ChannelHandler],
|
|
position: ChannelPipeline.Position = .last) throws {
|
|
try self._pipeline.addHandlersSync(handlers, position: position).get()
|
|
}
|
|
|
|
/// Add one or more handlers to the pipeline.
|
|
///
|
|
/// - Important: This *must* be called on the event loop.
|
|
/// - Parameters:
|
|
/// - handlers: The handlers to add.
|
|
/// - position: The position in the `ChannelPipeline` to add `handlers`. Defaults to `.last`.
|
|
public func addHandlers(_ handlers: ChannelHandler...,
|
|
position: ChannelPipeline.Position = .last) throws {
|
|
try self._pipeline.addHandlersSync(handlers, position: position).get()
|
|
}
|
|
|
|
/// Returns the `ChannelHandlerContext` for the given handler instance if it is in
|
|
/// the `ChannelPipeline`, if it exists.
|
|
///
|
|
/// - Important: This *must* be called on the event loop.
|
|
/// - Parameter handler: The handler belonging to the context to fetch.
|
|
/// - Returns: The `ChannelHandlerContext` associated with the handler.
|
|
public func context(handler: ChannelHandler) throws -> ChannelHandlerContext {
|
|
return try self._pipeline._contextSync({ $0.handler === handler }).get()
|
|
}
|
|
|
|
/// Returns the `ChannelHandlerContext` for the handler with the given name, if one exists.
|
|
///
|
|
/// - Important: This *must* be called on the event loop.
|
|
/// - Parameter name: The name of the handler whose context is being fetched.
|
|
/// - Returns: The `ChannelHandlerContext` associated with the handler.
|
|
public func context(name: String) throws -> ChannelHandlerContext {
|
|
return try self._pipeline.contextSync(name: name).get()
|
|
}
|
|
|
|
/// Returns the `ChannelHandlerContext` for the handler of given type, if one exists.
|
|
///
|
|
/// - Important: This *must* be called on the event loop.
|
|
/// - Parameter name: The name of the handler whose context is being fetched.
|
|
/// - Returns: The `ChannelHandlerContext` associated with the handler.
|
|
@inlinable
|
|
public func context<Handler: ChannelHandler>(handlerType: Handler.Type) throws -> ChannelHandlerContext {
|
|
return try self._pipeline._contextSync(handlerType: handlerType).get()
|
|
}
|
|
|
|
/// Returns the `ChannelHandler` of the given type from the `ChannelPipeline`, if it exists.
|
|
///
|
|
/// - Important: This *must* be called on the event loop.
|
|
/// - Returns: A `ChannelHandler` of the given type if one exists in the `ChannelPipeline`.
|
|
@inlinable
|
|
public func handler<Handler: ChannelHandler>(type _: Handler.Type) throws -> Handler {
|
|
return try self._pipeline._handlerSync(type: Handler.self).get()
|
|
}
|
|
|
|
/// Fires `channelRegistered` from the head to the tail.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func fireChannelRegistered() {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.fireChannelRegistered0()
|
|
}
|
|
|
|
/// Fires `channelUnregistered` from the head to the tail.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func fireChannelUnregistered() {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.fireChannelUnregistered0()
|
|
}
|
|
|
|
/// Fires `channelInactive` from the head to the tail.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func fireChannelInactive() {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.fireChannelInactive0()
|
|
}
|
|
|
|
/// Fires `channelActive` from the head to the tail.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func fireChannelActive() {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.fireChannelActive0()
|
|
}
|
|
|
|
/// Fires `channelRead` from the head to the tail.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func fireChannelRead(_ data: NIOAny) {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.fireChannelRead0(data)
|
|
}
|
|
|
|
/// Fires `channelReadComplete` from the head to the tail.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func fireChannelReadComplete() {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.fireChannelReadComplete0()
|
|
}
|
|
|
|
/// Fires `channelWritabilityChanged` from the head to the tail.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func fireChannelWritabilityChanged() {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.fireChannelWritabilityChanged0()
|
|
}
|
|
|
|
/// Fires `userInboundEventTriggered` from the head to the tail.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func fireUserInboundEventTriggered(_ event: Any) {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.fireUserInboundEventTriggered0(event)
|
|
}
|
|
|
|
/// Fires `errorCaught` from the head to the tail.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func fireErrorCaught(_ error: Error) {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.fireErrorCaught0(error: error)
|
|
}
|
|
|
|
/// Fires `close` from the tail to the head.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func close(mode: CloseMode = .all, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.close0(mode: mode, promise: promise)
|
|
}
|
|
|
|
/// Fires `flush` from the tail to the head.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func flush() {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.flush0()
|
|
}
|
|
|
|
/// Fires `read` from the tail to the head.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func read() {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.read0()
|
|
}
|
|
|
|
/// Fires `write` from the tail to the head.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func write(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.write0(data, promise: promise)
|
|
}
|
|
|
|
/// Fires `writeAndFlush` from the tail to the head.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func writeAndFlush(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.writeAndFlush0(data, promise: promise)
|
|
}
|
|
|
|
/// Fires `bind` from the tail to the head.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func bind(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.bind0(to: address, promise: promise)
|
|
}
|
|
|
|
/// Fires `connect` from the tail to the head.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func connect(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.connect0(to: address, promise: promise)
|
|
}
|
|
|
|
/// Fires `register` from the tail to the head.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func register(promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.register0(promise: promise)
|
|
}
|
|
|
|
/// Fires `triggerUserOutboundEvent` from the tail to the head.
|
|
///
|
|
/// This method should typically only be called by `Channel` implementations directly.
|
|
public func triggerUserOutboundEvent(_ event: Any, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
self._pipeline.triggerUserOutboundEvent0(event, promise: promise)
|
|
}
|
|
}
|
|
|
|
/// Returns a view of operations which can be performed synchronously on this pipeline. All
|
|
/// operations **must** be called on the event loop.
|
|
public var syncOperations: SynchronousOperations {
|
|
return SynchronousOperations(pipeline: self)
|
|
}
|
|
}
|
|
|
|
#if swift(>=5.7)
|
|
@available(*, unavailable)
|
|
extension ChannelPipeline.SynchronousOperations: Sendable {}
|
|
#endif
|
|
|
|
extension ChannelPipeline {
|
|
/// A `Position` within the `ChannelPipeline` used to insert handlers into the `ChannelPipeline`.
|
|
public enum Position {
|
|
/// The first `ChannelHandler` -- the front of the `ChannelPipeline`.
|
|
case first
|
|
|
|
/// The last `ChannelHandler` -- the back of the `ChannelPipeline`.
|
|
case last
|
|
|
|
/// Before the given `ChannelHandler`.
|
|
case before(ChannelHandler)
|
|
|
|
/// After the given `ChannelHandler`.
|
|
case after(ChannelHandler)
|
|
}
|
|
}
|
|
|
|
#if swift(>=5.7)
|
|
@available(*, unavailable)
|
|
extension ChannelPipeline.Position: Sendable {}
|
|
#endif
|
|
|
|
/// Special `ChannelHandler` that forwards all events to the `Channel.Unsafe` implementation.
|
|
/* private but tests */ final class HeadChannelHandler: _ChannelOutboundHandler {
|
|
|
|
static let name = "head"
|
|
static let sharedInstance = HeadChannelHandler()
|
|
|
|
private init() { }
|
|
|
|
func register(context: ChannelHandlerContext, promise: EventLoopPromise<Void>?) {
|
|
context.channel._channelCore.register0(promise: promise)
|
|
}
|
|
|
|
func bind(context: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
context.channel._channelCore.bind0(to: address, promise: promise)
|
|
}
|
|
|
|
func connect(context: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
context.channel._channelCore.connect0(to: address, promise: promise)
|
|
}
|
|
|
|
func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
context.channel._channelCore.write0(data, promise: promise)
|
|
}
|
|
|
|
func flush(context: ChannelHandlerContext) {
|
|
context.channel._channelCore.flush0()
|
|
}
|
|
|
|
func close(context: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise<Void>?) {
|
|
context.channel._channelCore.close0(error: mode.error, mode: mode, promise: promise)
|
|
}
|
|
|
|
func read(context: ChannelHandlerContext) {
|
|
context.channel._channelCore.read0()
|
|
}
|
|
|
|
func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise<Void>?) {
|
|
context.channel._channelCore.triggerUserOutboundEvent0(event, promise: promise)
|
|
}
|
|
|
|
}
|
|
|
|
private extension CloseMode {
|
|
/// Returns the error to fail outstanding operations writes with.
|
|
var error: ChannelError {
|
|
switch self {
|
|
case .all:
|
|
return .ioOnClosedChannel
|
|
case .output:
|
|
return .outputClosed
|
|
case .input:
|
|
return .inputClosed
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Special `ChannelInboundHandler` which will consume all inbound events.
|
|
/* private but tests */ final class TailChannelHandler: _ChannelInboundHandler {
|
|
|
|
static let name = "tail"
|
|
static let sharedInstance = TailChannelHandler()
|
|
|
|
private init() { }
|
|
|
|
func channelRegistered(context: ChannelHandlerContext) {
|
|
// Discard
|
|
}
|
|
|
|
func channelUnregistered(context: ChannelHandlerContext) {
|
|
// Discard
|
|
}
|
|
|
|
func channelActive(context: ChannelHandlerContext) {
|
|
// Discard
|
|
}
|
|
|
|
func channelInactive(context: ChannelHandlerContext) {
|
|
// Discard
|
|
}
|
|
|
|
func channelReadComplete(context: ChannelHandlerContext) {
|
|
// Discard
|
|
}
|
|
|
|
func channelWritabilityChanged(context: ChannelHandlerContext) {
|
|
// Discard
|
|
}
|
|
|
|
func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) {
|
|
// Discard
|
|
}
|
|
|
|
func errorCaught(context: ChannelHandlerContext, error: Error) {
|
|
context.channel._channelCore.errorCaught0(error: error)
|
|
}
|
|
|
|
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
|
context.channel._channelCore.channelRead0(data)
|
|
}
|
|
}
|
|
|
|
/// `Error` that is used by the `ChannelPipeline` to inform the user of an error.
|
|
public enum ChannelPipelineError: Error {
|
|
/// `ChannelHandler` was already removed.
|
|
case alreadyRemoved
|
|
/// `ChannelHandler` was not found.
|
|
case notFound
|
|
}
|
|
|
|
/// Every `ChannelHandler` has -- when added to a `ChannelPipeline` -- a corresponding `ChannelHandlerContext` which is
|
|
/// the way `ChannelHandler`s can interact with other `ChannelHandler`s in the pipeline.
|
|
///
|
|
/// Most `ChannelHandler`s need to send events through the `ChannelPipeline` which they do by calling the respective
|
|
/// method on their `ChannelHandlerContext`. In fact all the `ChannelHandler` default implementations just forward
|
|
/// the event using the `ChannelHandlerContext`.
|
|
///
|
|
/// Many events are instrumental for a `ChannelHandler`'s life-cycle and it is therefore very important to send them
|
|
/// at the right point in time. Often, the right behaviour is to react to an event and then forward it to the next
|
|
/// `ChannelHandler`.
|
|
public final class ChannelHandlerContext: ChannelInvoker {
|
|
// visible for ChannelPipeline to modify
|
|
fileprivate var next: Optional<ChannelHandlerContext>
|
|
fileprivate var prev: Optional<ChannelHandlerContext>
|
|
|
|
public let pipeline: ChannelPipeline
|
|
|
|
public var channel: Channel {
|
|
return self.pipeline.channel
|
|
}
|
|
|
|
public var handler: ChannelHandler {
|
|
return self.inboundHandler ?? self.outboundHandler!
|
|
}
|
|
|
|
public var remoteAddress: SocketAddress? {
|
|
do {
|
|
// Fast-path access to the remoteAddress.
|
|
return try self.channel._channelCore.remoteAddress0()
|
|
} catch ChannelError.ioOnClosedChannel {
|
|
// Channel was closed already but we may still have the address cached so try to access it via the Channel
|
|
// so we are able to use it in channelInactive(...) / handlerRemoved(...) methods.
|
|
return self.channel.remoteAddress
|
|
} catch {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public var localAddress: SocketAddress? {
|
|
do {
|
|
// Fast-path access to the localAddress.
|
|
return try self.channel._channelCore.localAddress0()
|
|
} catch ChannelError.ioOnClosedChannel {
|
|
// Channel was closed already but we may still have the address cached so try to access it via the Channel
|
|
// so we are able to use it in channelInactive(...) / handlerRemoved(...) methods.
|
|
return self.channel.localAddress
|
|
} catch {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public var eventLoop: EventLoop {
|
|
return self.pipeline.eventLoop
|
|
}
|
|
|
|
public let name: String
|
|
private let inboundHandler: _ChannelInboundHandler?
|
|
private let outboundHandler: _ChannelOutboundHandler?
|
|
private var removeHandlerInvoked = false
|
|
private var userTriggeredRemovalStarted = false
|
|
|
|
// Only created from within ChannelPipeline
|
|
fileprivate init(name: String, handler: ChannelHandler, pipeline: ChannelPipeline) {
|
|
self.name = name
|
|
self.pipeline = pipeline
|
|
self.inboundHandler = handler as? _ChannelInboundHandler
|
|
self.outboundHandler = handler as? _ChannelOutboundHandler
|
|
self.next = nil
|
|
self.prev = nil
|
|
precondition(self.inboundHandler != nil || self.outboundHandler != nil, "ChannelHandlers need to either be inbound or outbound")
|
|
}
|
|
|
|
/// Send a `channelRegistered` event to the next (inbound) `ChannelHandler` in the `ChannelPipeline`.
|
|
///
|
|
/// - note: For correct operation it is very important to forward any `channelRegistered` event using this method at the right point in time, that is usually when received.
|
|
public func fireChannelRegistered() {
|
|
self.next?.invokeChannelRegistered()
|
|
}
|
|
|
|
/// Send a `channelUnregistered` event to the next (inbound) `ChannelHandler` in the `ChannelPipeline`.
|
|
///
|
|
/// - note: For correct operation it is very important to forward any `channelUnregistered` event using this method at the right point in time, that is usually when received.
|
|
public func fireChannelUnregistered() {
|
|
self.next?.invokeChannelUnregistered()
|
|
}
|
|
|
|
/// Send a `channelActive` event to the next (inbound) `ChannelHandler` in the `ChannelPipeline`.
|
|
///
|
|
/// - note: For correct operation it is very important to forward any `channelActive` event using this method at the right point in time, that is often when received.
|
|
public func fireChannelActive() {
|
|
self.next?.invokeChannelActive()
|
|
}
|
|
|
|
/// Send a `channelInactive` event to the next (inbound) `ChannelHandler` in the `ChannelPipeline`.
|
|
///
|
|
/// - note: For correct operation it is very important to forward any `channelInactive` event using this method at the right point in time, that is often when received.
|
|
public func fireChannelInactive() {
|
|
self.next?.invokeChannelInactive()
|
|
}
|
|
|
|
/// Send data to the next inbound `ChannelHandler`. The data should be of type `ChannelInboundHandler.InboundOut`.
|
|
public func fireChannelRead(_ data: NIOAny) {
|
|
self.next?.invokeChannelRead(data)
|
|
}
|
|
|
|
/// Signal to the next `ChannelHandler` that a read burst has finished.
|
|
public func fireChannelReadComplete() {
|
|
self.next?.invokeChannelReadComplete()
|
|
}
|
|
|
|
/// Send a `writabilityChanged` event to the next (inbound) `ChannelHandler` in the `ChannelPipeline`.
|
|
///
|
|
/// - note: For correct operation it is very important to forward any `writabilityChanged` event using this method at the right point in time, that is usually when received.
|
|
public func fireChannelWritabilityChanged() {
|
|
self.next?.invokeChannelWritabilityChanged()
|
|
}
|
|
|
|
/// Send an error to the next inbound `ChannelHandler`.
|
|
public func fireErrorCaught(_ error: Error) {
|
|
self.next?.invokeErrorCaught(error)
|
|
}
|
|
|
|
/// Send a user event to the next inbound `ChannelHandler`.
|
|
public func fireUserInboundEventTriggered(_ event: Any) {
|
|
self.next?.invokeUserInboundEventTriggered(event)
|
|
}
|
|
|
|
/// Send a `register` event to the next (outbound) `ChannelHandler` in the `ChannelPipeline`.
|
|
///
|
|
/// - note: For correct operation it is very important to forward any `register` event using this method at the right point in time, that is usually when received.
|
|
public func register(promise: EventLoopPromise<Void>?) {
|
|
if let outboundNext = self.prev {
|
|
outboundNext.invokeRegister(promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
/// Send a `bind` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
|
|
/// When the `bind` event reaches the `HeadChannelHandler` a `ServerSocketChannel` will be bound.
|
|
///
|
|
/// - parameters:
|
|
/// - address: The address to bind to.
|
|
/// - promise: The promise fulfilled when the socket is bound or failed if it cannot be bound.
|
|
public func bind(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
if let outboundNext = self.prev {
|
|
outboundNext.invokeBind(to: address, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
/// Send a `connect` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
|
|
/// When the `connect` event reaches the `HeadChannelHandler` a `SocketChannel` will be connected.
|
|
///
|
|
/// - parameters:
|
|
/// - address: The address to connect to.
|
|
/// - promise: The promise fulfilled when the socket is connected or failed if it cannot be connected.
|
|
public func connect(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
if let outboundNext = self.prev {
|
|
outboundNext.invokeConnect(to: address, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
/// Send a `write` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
|
|
/// When the `write` event reaches the `HeadChannelHandler` the data will be enqueued to be written on the next
|
|
/// `flush` event.
|
|
///
|
|
/// - parameters:
|
|
/// - data: The data to write, should be of type `ChannelOutboundHandler.OutboundOut`.
|
|
/// - promise: The promise fulfilled when the data has been written or failed if it cannot be written.
|
|
public func write(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
if let outboundNext = self.prev {
|
|
outboundNext.invokeWrite(data, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
/// Send a `flush` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
|
|
/// When the `flush` event reaches the `HeadChannelHandler` the data previously enqueued will be attempted to be
|
|
/// written to the socket.
|
|
///
|
|
/// - parameters:
|
|
/// - promise: The promise fulfilled when the previously written data been flushed or failed if it cannot be flushed.
|
|
public func flush() {
|
|
if let outboundNext = self.prev {
|
|
outboundNext.invokeFlush()
|
|
}
|
|
}
|
|
|
|
/// Send a `write` event followed by a `flush` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
|
|
/// When the `write` event reaches the `HeadChannelHandler` the data will be enqueued to be written when the `flush`
|
|
/// also reaches the `HeadChannelHandler`.
|
|
///
|
|
/// - parameters:
|
|
/// - data: The data to write, should be of type `ChannelOutboundHandler.OutboundOut`.
|
|
/// - promise: The promise fulfilled when the previously written data been written and flushed or if that failed.
|
|
public func writeAndFlush(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
if let outboundNext = self.prev {
|
|
outboundNext.invokeWriteAndFlush(data, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
/// Send a `read` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
|
|
/// When the `read` event reaches the `HeadChannelHandler` the interest to read data will be signalled to the
|
|
/// `Selector`. This will subsequently -- when data becomes readable -- cause `channelRead` events containing the
|
|
/// data being sent through the `ChannelPipeline`.
|
|
public func read() {
|
|
if let outboundNext = self.prev {
|
|
outboundNext.invokeRead()
|
|
}
|
|
}
|
|
|
|
/// Send a `close` event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
|
|
/// When the `close` event reaches the `HeadChannelHandler` the socket will be closed.
|
|
///
|
|
/// - parameters:
|
|
/// - mode: The `CloseMode` to use.
|
|
/// - promise: The promise fulfilled when the `Channel` has been closed or failed if it the closing failed.
|
|
public func close(mode: CloseMode = .all, promise: EventLoopPromise<Void>?) {
|
|
if let outboundNext = self.prev {
|
|
outboundNext.invokeClose(mode: mode, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.alreadyClosed)
|
|
}
|
|
}
|
|
|
|
/// Send a user event to the next outbound `ChannelHandler` in the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - event: The user event to send.
|
|
/// - promise: The promise fulfilled when the user event has been sent or failed if it couldn't be sent.
|
|
public func triggerUserOutboundEvent(_ event: Any, promise: EventLoopPromise<Void>?) {
|
|
if let outboundNext = self.prev {
|
|
outboundNext.invokeTriggerUserOutboundEvent(event, promise: promise)
|
|
} else {
|
|
promise?.fail(ChannelError.ioOnClosedChannel)
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeChannelRegistered() {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let inboundHandler = self.inboundHandler {
|
|
inboundHandler.channelRegistered(context: self)
|
|
} else {
|
|
self.next?.invokeChannelRegistered()
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeChannelUnregistered() {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let inboundHandler = self.inboundHandler {
|
|
inboundHandler.channelUnregistered(context: self)
|
|
} else {
|
|
self.next?.invokeChannelUnregistered()
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeChannelActive() {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let inboundHandler = self.inboundHandler {
|
|
inboundHandler.channelActive(context: self)
|
|
} else {
|
|
self.next?.invokeChannelActive()
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeChannelInactive() {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let inboundHandler = self.inboundHandler {
|
|
inboundHandler.channelInactive(context: self)
|
|
} else {
|
|
self.next?.invokeChannelInactive()
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeChannelRead(_ data: NIOAny) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let inboundHandler = self.inboundHandler {
|
|
inboundHandler.channelRead(context: self, data: data)
|
|
} else {
|
|
self.next?.invokeChannelRead(data)
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeChannelReadComplete() {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let inboundHandler = self.inboundHandler {
|
|
inboundHandler.channelReadComplete(context: self)
|
|
} else {
|
|
self.next?.invokeChannelReadComplete()
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeChannelWritabilityChanged() {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let inboundHandler = self.inboundHandler {
|
|
inboundHandler.channelWritabilityChanged(context: self)
|
|
} else {
|
|
self.next?.invokeChannelWritabilityChanged()
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeErrorCaught(_ error: Error) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let inboundHandler = self.inboundHandler {
|
|
inboundHandler.errorCaught(context: self, error: error)
|
|
} else {
|
|
self.next?.invokeErrorCaught(error)
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeUserInboundEventTriggered(_ event: Any) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let inboundHandler = self.inboundHandler {
|
|
inboundHandler.userInboundEventTriggered(context: self, event: event)
|
|
} else {
|
|
self.next?.invokeUserInboundEventTriggered(event)
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeRegister(promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let outboundHandler = self.outboundHandler {
|
|
outboundHandler.register(context: self, promise: promise)
|
|
} else {
|
|
self.prev?.invokeRegister(promise: promise)
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeBind(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let outboundHandler = self.outboundHandler {
|
|
outboundHandler.bind(context: self, to: address, promise: promise)
|
|
} else {
|
|
self.prev?.invokeBind(to: address, promise: promise)
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeConnect(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let outboundHandler = self.outboundHandler {
|
|
outboundHandler.connect(context: self, to: address, promise: promise)
|
|
} else {
|
|
self.prev?.invokeConnect(to: address, promise: promise)
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeWrite(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let outboundHandler = self.outboundHandler {
|
|
outboundHandler.write(context: self, data: data, promise: promise)
|
|
} else {
|
|
self.prev?.invokeWrite(data, promise: promise)
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeFlush() {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let outboundHandler = self.outboundHandler {
|
|
outboundHandler.flush(context: self)
|
|
} else {
|
|
self.prev?.invokeFlush()
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeWriteAndFlush(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let outboundHandler = self.outboundHandler {
|
|
outboundHandler.write(context: self, data: data, promise: promise)
|
|
outboundHandler.flush(context: self)
|
|
} else {
|
|
self.prev?.invokeWriteAndFlush(data, promise: promise)
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeRead() {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let outboundHandler = self.outboundHandler {
|
|
outboundHandler.read(context: self)
|
|
} else {
|
|
self.prev?.invokeRead()
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeClose(mode: CloseMode, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let outboundHandler = self.outboundHandler {
|
|
outboundHandler.close(context: self, mode: mode, promise: promise)
|
|
} else {
|
|
self.prev?.invokeClose(mode: mode, promise: promise)
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeTriggerUserOutboundEvent(_ event: Any, promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
if let outboundHandler = self.outboundHandler {
|
|
outboundHandler.triggerUserOutboundEvent(context: self, event: event, promise: promise)
|
|
} else {
|
|
self.prev?.invokeTriggerUserOutboundEvent(event, promise: promise)
|
|
}
|
|
}
|
|
|
|
fileprivate func invokeHandlerAdded() {
|
|
self.eventLoop.assertInEventLoop()
|
|
|
|
handler.handlerAdded(context: self)
|
|
}
|
|
|
|
fileprivate func invokeHandlerRemoved() {
|
|
self.eventLoop.assertInEventLoop()
|
|
guard !self.removeHandlerInvoked else {
|
|
return
|
|
}
|
|
self.removeHandlerInvoked = true
|
|
|
|
handler.handlerRemoved(context: self)
|
|
}
|
|
}
|
|
|
|
#if swift(>=5.7)
|
|
@available(*, unavailable)
|
|
extension ChannelHandlerContext: Sendable {}
|
|
#endif
|
|
|
|
extension ChannelHandlerContext {
|
|
/// A `RemovalToken` is handed to a `RemovableChannelHandler` when its `removeHandler` function is invoked. A
|
|
/// `RemovableChannelHandler` is then required to remove itself from the `ChannelPipeline`. The removal process
|
|
/// is finalized by handing the `RemovalToken` to the `ChannelHandlerContext.leavePipeline` function.
|
|
public struct RemovalToken: Sendable {
|
|
internal let promise: EventLoopPromise<Void>?
|
|
}
|
|
|
|
/// Synchronously remove the `ChannelHandler` with the given `ChannelHandlerContext`.
|
|
///
|
|
/// - note: This function must only be used from a `RemovableChannelHandler` to remove itself. Calling this method
|
|
/// on any other `ChannelHandlerContext` leads to undefined behaviour.
|
|
///
|
|
/// - parameters:
|
|
/// - removalToken: The removal token received from `RemovableChannelHandler.removeHandler`
|
|
public func leavePipeline(removalToken: RemovalToken) {
|
|
self.eventLoop.preconditionInEventLoop()
|
|
self.pipeline.removeHandlerFromPipeline(context: self, promise: removalToken.promise)
|
|
}
|
|
|
|
internal func startUserTriggeredRemoval(promise: EventLoopPromise<Void>?) {
|
|
self.eventLoop.assertInEventLoop()
|
|
guard !self.userTriggeredRemovalStarted else {
|
|
promise?.fail(NIOAttemptedToRemoveHandlerMultipleTimesError())
|
|
return
|
|
}
|
|
self.userTriggeredRemovalStarted = true
|
|
(self.handler as! RemovableChannelHandler).removeHandler(context: self,
|
|
removalToken: .init(promise: promise))
|
|
}
|
|
}
|
|
|
|
extension ChannelPipeline: CustomDebugStringConvertible {
|
|
public var debugDescription: String {
|
|
// This method forms output in the following format:
|
|
//
|
|
// ChannelPipeline[0x0000000000000000]:
|
|
// [I] ↓↑ [O]
|
|
// <incoming handler type> ↓↑ [<name>]
|
|
// ↓↑ <outgoing handler type> [<name>]
|
|
// <duplex handler type> ↓↑ <duplex handler type> [<name>]
|
|
//
|
|
var desc = ["ChannelPipeline[\(ObjectIdentifier(self))]:"]
|
|
let debugInfos = self.collectHandlerDebugInfos()
|
|
let maxIncomingTypeNameCount = debugInfos.filter { $0.isIncoming }
|
|
.map { $0.typeName.count }
|
|
.max() ?? 0
|
|
let maxOutgoingTypeNameCount = debugInfos.filter { $0.isOutgoing }
|
|
.map { $0.typeName.count }
|
|
.max() ?? 0
|
|
|
|
func whitespace(count: Int) -> String {
|
|
return String(repeating: " ", count: count)
|
|
}
|
|
|
|
if debugInfos.isEmpty {
|
|
desc.append(" <no handlers>")
|
|
} else {
|
|
desc.append(whitespace(count: maxIncomingTypeNameCount - 2) + "[I] ↓↑ [O]")
|
|
for debugInfo in debugInfos {
|
|
var line = [String]()
|
|
line.append(" ")
|
|
if debugInfo.isIncoming {
|
|
line.append(whitespace(count: maxIncomingTypeNameCount - debugInfo.typeName.count))
|
|
line.append(debugInfo.typeName)
|
|
} else {
|
|
line.append(whitespace(count: maxIncomingTypeNameCount))
|
|
}
|
|
line.append(" ↓↑ ")
|
|
if debugInfo.isOutgoing {
|
|
line.append(debugInfo.typeName)
|
|
line.append(whitespace(count: maxOutgoingTypeNameCount - debugInfo.typeName.count))
|
|
} else {
|
|
line.append(whitespace(count: maxOutgoingTypeNameCount))
|
|
}
|
|
line.append(" ")
|
|
line.append("[\(debugInfo.name)]")
|
|
desc.append(line.joined())
|
|
}
|
|
}
|
|
|
|
return desc.joined(separator: "\n")
|
|
}
|
|
|
|
/// Returns the first `ChannelHandler` of the given type.
|
|
///
|
|
/// - parameters:
|
|
/// - type: the type of `ChannelHandler` to return.
|
|
@inlinable
|
|
public func handler<Handler: ChannelHandler>(type _: Handler.Type) -> EventLoopFuture<Handler> {
|
|
return self.context(handlerType: Handler.self).map { context in
|
|
guard let typedContext = context.handler as? Handler else {
|
|
preconditionFailure("Expected channel handler of type \(Handler.self), got \(type(of: context.handler)) instead.")
|
|
}
|
|
|
|
return typedContext
|
|
}
|
|
}
|
|
|
|
/// Synchronously finds and returns the first `ChannelHandler` of the given type.
|
|
///
|
|
/// - Important: This must be called on the `EventLoop`.
|
|
/// - Parameters:
|
|
/// - type: the type of `ChannelHandler` to return.
|
|
@inlinable // should be fileprivate
|
|
internal func _handlerSync<Handler: ChannelHandler>(type _: Handler.Type) -> Result<Handler, Error> {
|
|
return self._contextSync(handlerType: Handler.self).map { context in
|
|
guard let typedContext = context.handler as? Handler else {
|
|
preconditionFailure("Expected channel handler of type \(Handler.self), got \(type(of: context.handler)) instead.")
|
|
}
|
|
return typedContext
|
|
}
|
|
}
|
|
|
|
private struct ChannelHandlerDebugInfo {
|
|
let handler: ChannelHandler
|
|
let name: String
|
|
var isIncoming: Bool {
|
|
return self.handler is _ChannelInboundHandler
|
|
}
|
|
var isOutgoing: Bool {
|
|
return self.handler is _ChannelOutboundHandler
|
|
}
|
|
var typeName: String {
|
|
return "\(type(of: self.handler))"
|
|
}
|
|
}
|
|
|
|
private func collectHandlerDebugInfos() -> [ChannelHandlerDebugInfo] {
|
|
var handlers = [ChannelHandlerDebugInfo]()
|
|
var node = self.head?.next
|
|
while let context = node, context !== self.tail {
|
|
handlers.append(.init(handler: context.handler, name: context.name))
|
|
node = context.next
|
|
}
|
|
return handlers
|
|
}
|
|
}
|