swift-nio/Sources/NIO/ChannelPipeline.swift

1283 lines
47 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()).then {
/// p.add(name: "2", handler: InboundHandlerB())
/// }.then {
/// p.add(name: "3", handler: OutboundHandlerA())
/// }.then {
/// p.add(name: "4", handler: OutboundHandlerB())
/// }.then {
/// 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.
///
///
/// # 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: ChannelHandlerContext?
private var tail: 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.
///
/// - warning: This is unsafe as it's only valid if the `Channel` is still open
private unowned let _channel: Channel
/// The `Channel` that this `ChannelPipeline` belongs to.
internal var channel: Channel {
assert(self.eventLoop.inEventLoop)
return !self.destroyed ? self._channel : DeadChannel(pipeline: self)
}
/// Add a `ChannelHandler` to the `ChannelPipeline`.
///
/// - parameters:
/// - name: the name to use for the `ChannelHandler` when its added. If none is specified it will generate a name.
/// - handler: the `ChannelHandler` to add
/// - first: `true` to add this handler to the front of the `ChannelPipeline`, `false to add it last
/// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was added.
public func add(name: String? = nil, handler: ChannelHandler, first: Bool = false) -> EventLoopFuture<Void> {
let promise: EventLoopPromise<Void> = eventLoop.newPromise()
if eventLoop.inEventLoop {
add0(name: name, handler: handler, first: first, promise: promise)
} else {
eventLoop.execute {
self.add0(name: name, handler: handler, first: first, promise: promise)
}
}
return promise.futureResult
}
/// Add the handler to the `ChannelPipeline`. This operation must only be called when within the `EventLoop` thread.
private func add0(name: String?, handler: ChannelHandler, first: Bool, promise: EventLoopPromise<Void>) {
assert(eventLoop.inEventLoop)
if destroyed {
promise.fail(error: ChannelError.ioOnClosedChannel)
return
}
let ctx = ChannelHandlerContext(name: name ?? nextName(), handler: handler, pipeline: self)
if first {
let next = self.head?.next
ctx.prev = self.head
ctx.next = next
head?.next = ctx
next?.prev = ctx
} else {
let prev = self.tail?.prev
ctx.prev = prev
ctx.next = self.tail
tail?.prev = ctx
prev?.next = ctx
}
do {
try ctx.invokeHandlerAdded()
promise.succeed(result: ())
} catch let err {
remove0(ctx: ctx, promise: nil)
promise.fail(error: err)
}
}
/// 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 remove(handler: ChannelHandler) -> EventLoopFuture<Bool> {
let promise: EventLoopPromise<Bool> = self.eventLoop.newPromise()
context0({
return $0.handler === handler
}).map { ctx in
self.remove0(ctx: ctx, promise: promise)
}.cascadeFailure(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 remove(name: String) -> EventLoopFuture<Bool> {
let promise: EventLoopPromise<Bool> = self.eventLoop.newPromise()
context0({ $0.name == name }).map { ctx in
self.remove0(ctx: ctx, promise: promise)
}.cascadeFailure(promise: promise)
return promise.futureResult
}
/// Remove a `ChannelHandler` from the `ChannelPipeline`.
///
/// - parameters:
/// - ctx: the `ChannelHandlerContext` that belongs to `ChannelHandler` that should be removed.
/// - returns: the `EventLoopFuture` which will be notified once the `ChannelHandler` was removed.
public func remove(ctx: ChannelHandlerContext) -> EventLoopFuture<Bool> {
let promise: EventLoopPromise<Bool> = self.eventLoop.newPromise()
if self.eventLoop.inEventLoop {
remove0(ctx: ctx, promise: promise)
} else {
self.eventLoop.execute {
self.remove0(ctx: ctx, promise: promise)
}
}
return promise.futureResult
}
/// Returns the `ChannelHandlerContext` that belongs to a `ChannelHandler`.
///
/// - parameters:
/// - handler: the `ChannelHandler` for which the `ChannelHandlerContext` shoud be returned
/// - returns: the `EventLoopFuture` which will be notified once the the operation completes.
public func context(handler: ChannelHandler) -> EventLoopFuture<ChannelHandlerContext> {
return context0({ $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> {
return context0({ $0.name == name })
}
/// Find a `ChannelHandlerContext` in the `ChannelPipeline`.
private func context0(_ fn: @escaping ((ChannelHandlerContext) -> Bool)) -> EventLoopFuture<ChannelHandlerContext> {
let promise: EventLoopPromise<ChannelHandlerContext> = eventLoop.newPromise()
func _context0() {
var curCtx: ChannelHandlerContext? = self.head
while let ctx = curCtx {
if fn(ctx) {
promise.succeed(result: ctx)
return
}
curCtx = ctx.next
}
promise.fail(error: ChannelPipelineError.notFound)
}
if eventLoop.inEventLoop {
_context0()
} else {
eventLoop.execute {
_context0()
}
}
return promise.futureResult
}
/// Remove a `ChannelHandlerContext` from the `ChannelPipeline`. Must only be called from within the `EventLoop`.
private func remove0(ctx: ChannelHandlerContext, promise: EventLoopPromise<Bool>?) {
assert(self.eventLoop.inEventLoop)
let nextCtx = ctx.next
let prevCtx = ctx.prev
if let prevCtx = prevCtx {
prevCtx.next = nextCtx
}
if let nextCtx = nextCtx {
nextCtx.prev = prevCtx
}
do {
try ctx.invokeHandlerRemoved()
promise?.succeed(result: true)
} catch let err {
promise?.fail(error: err)
}
// We need to keep the current node alive until after the callout in case the user uses the context.
ctx.next = nil
ctx.prev = nil
}
/// Returns the next name to use for a `ChannelHandler`.
private func nextName() -> String {
assert(eventLoop.inEventLoop)
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`.
func removeHandlers() {
assert(eventLoop.inEventLoop)
if let head = self.head {
while let ctx = head.next {
remove0(ctx: ctx, promise: nil)
}
remove0(ctx: self.head!, promise: nil)
}
self.head = nil
self.tail = nil
destroyed = true
}
// 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(promise: EventLoopPromise<Void>?) {
if eventLoop.inEventLoop {
flush0(promise: promise)
} else {
eventLoop.execute {
self.flush0(promise: promise)
}
}
}
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
}
func close0(mode: CloseMode, promise: EventLoopPromise<Void>?) {
if let firstOutboundCtx = firstOutboundCtx {
firstOutboundCtx.invokeClose(mode: mode, promise: promise)
} else {
promise?.fail(error: ChannelError.alreadyClosed)
}
}
func flush0(promise: EventLoopPromise<Void>?) {
if let firstOutboundCtx = firstOutboundCtx {
firstOutboundCtx.invokeFlush(promise: promise)
} else {
promise?.fail(error: ChannelError.ioOnClosedChannel)
}
}
func read0() {
if let firstOutboundCtx = firstOutboundCtx {
firstOutboundCtx.invokeRead()
}
}
func write0(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
if let firstOutboundCtx = firstOutboundCtx {
firstOutboundCtx.invokeWrite(data, promise: promise)
} else {
promise?.fail(error: ChannelError.ioOnClosedChannel)
}
}
func writeAndFlush0(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
if let firstOutboundCtx = firstOutboundCtx {
firstOutboundCtx.invokeWriteAndFlush(data, promise: promise)
} else {
promise?.fail(error: ChannelError.ioOnClosedChannel)
}
}
func bind0(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
if let firstOutboundCtx = firstOutboundCtx {
firstOutboundCtx.invokeBind(to: address, promise: promise)
} else {
promise?.fail(error: ChannelError.ioOnClosedChannel)
}
}
func connect0(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
if let firstOutboundCtx = firstOutboundCtx {
firstOutboundCtx.invokeConnect(to: address, promise: promise)
} else {
promise?.fail(error: ChannelError.ioOnClosedChannel)
}
}
func register0(promise: EventLoopPromise<Void>?) {
if let firstOutboundCtx = firstOutboundCtx {
firstOutboundCtx.invokeRegister(promise: promise)
} else {
promise?.fail(error: ChannelError.ioOnClosedChannel)
}
}
func triggerUserOutboundEvent0(_ event: Any, promise: EventLoopPromise<Void>?) {
if let firstOutboundCtx = firstOutboundCtx {
firstOutboundCtx.invokeTriggerUserOutboundEvent(event, promise: promise)
} else {
promise?.fail(error: ChannelError.ioOnClosedChannel)
}
}
func fireChannelRegistered0() {
if let firstInboundCtx = firstInboundCtx {
firstInboundCtx.invokeChannelRegistered()
}
}
func fireChannelUnregistered0() {
if let firstInboundCtx = firstInboundCtx {
firstInboundCtx.invokeChannelUnregistered()
}
}
func fireChannelInactive0() {
if let firstInboundCtx = firstInboundCtx {
firstInboundCtx.invokeChannelInactive()
}
}
func fireChannelActive0() {
if let firstInboundCtx = firstInboundCtx {
firstInboundCtx.invokeChannelActive()
}
}
func fireChannelRead0(_ data: NIOAny) {
if let firstInboundCtx = firstInboundCtx {
firstInboundCtx.invokeChannelRead(data)
}
}
func fireChannelReadComplete0() {
if let firstInboundCtx = firstInboundCtx {
firstInboundCtx.invokeChannelReadComplete()
}
}
func fireChannelWritabilityChanged0() {
if let firstInboundCtx = firstInboundCtx {
firstInboundCtx.invokeChannelWritabilityChanged()
}
}
func fireUserInboundEventTriggered0(_ event: Any) {
if let firstInboundCtx = firstInboundCtx {
firstInboundCtx.invokeUserInboundEventTriggered(event)
}
}
func fireErrorCaught0(error: Error) {
if let firstInboundCtx = firstInboundCtx {
firstInboundCtx.invokeErrorCaught(error)
}
}
private var inEventLoop : Bool {
return eventLoop.inEventLoop
}
// Only executed from Channel
init (channel: Channel) {
self._channel = channel
self.eventLoop = channel.eventLoop
self.head = ChannelHandlerContext(name: "head", handler: HeadChannelHandler.sharedInstance, pipeline: self)
self.tail = ChannelHandlerContext(name: "tail", handler: TailChannelHandler.sharedInstance, pipeline: self)
self.head?.next = self.tail
self.tail?.prev = self.head
self.tail?.prev = self.head
self.head?.next = self.tail
}
}
/// Special `ChannelHandler` that forwards all events to the `Channel.Unsafe` implementation.
private final class HeadChannelHandler : _ChannelOutboundHandler {
static let sharedInstance = HeadChannelHandler()
private init() { }
func register(ctx: ChannelHandlerContext, promise: EventLoopPromise<Void>?) {
ctx.channel._unsafe.register0(promise: promise)
}
func bind(ctx: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise<Void>?) {
ctx.channel._unsafe.bind0(to: address, promise: promise)
}
func connect(ctx: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise<Void>?) {
ctx.channel._unsafe.connect0(to: address, promise: promise)
}
func write(ctx: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
ctx.channel._unsafe.write0(data, promise: promise)
}
func flush(ctx: ChannelHandlerContext, promise: EventLoopPromise<Void>?) {
ctx.channel._unsafe.flush0(promise: promise)
}
func close(ctx: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise<Void>?) {
ctx.channel._unsafe.close0(error: mode.error, mode: mode, promise: promise)
}
func read(ctx: ChannelHandlerContext) {
ctx.channel._unsafe.read0()
}
func triggerUserOutboundEvent(ctx: ChannelHandlerContext, event: Any, promise: EventLoopPromise<Void>?) {
ctx.channel._unsafe.triggerUserOutboundEvent0(event, promise: promise)
}
}
private extension CloseMode {
var error: ChannelError {
switch self {
case .all:
return ChannelError.alreadyClosed
case .output:
return ChannelError.outputClosed
case .input:
return ChannelError.inputClosed
}
}
}
/// Special `ChannelInboundHandler` which will consume all inbound events.
private final class TailChannelHandler : _ChannelInboundHandler, _ChannelOutboundHandler {
static let sharedInstance = TailChannelHandler()
private init() { }
func channelRegistered(ctx: ChannelHandlerContext) {
// Discard
}
func channelUnregistered(ctx: ChannelHandlerContext) throws {
// Discard
}
func channelActive(ctx: ChannelHandlerContext) {
// Discard
}
func channelInactive(ctx: ChannelHandlerContext) {
// Discard
}
func channelReadComplete(ctx: ChannelHandlerContext) {
// Discard
}
func channelWritabilityChanged(ctx: ChannelHandlerContext) {
// Discard
}
func userInboundEventTriggered(ctx: ChannelHandlerContext, event: Any) {
// Discard
}
func errorCaught(ctx: ChannelHandlerContext, error: Error) {
ctx.channel._unsafe.errorCaught0(error: error)
}
func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
ctx.channel._unsafe.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: ChannelHandlerContext?
fileprivate var prev: 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? {
return try? self.channel._unsafe.remoteAddress0()
}
public var localAddress: SocketAddress? {
return try? self.channel._unsafe.localAddress0()
}
public let name: String
public let eventLoop: EventLoop
private let inboundHandler: _ChannelInboundHandler?
private let outboundHandler: _ChannelOutboundHandler?
// Only created from within ChannelPipeline
fileprivate init(name: String, handler: ChannelHandler, pipeline: ChannelPipeline) {
self.name = name
self.pipeline = pipeline
self.eventLoop = pipeline.eventLoop
if let handler = handler as? _ChannelInboundHandler {
self.inboundHandler = handler
} else {
self.inboundHandler = nil
}
if let handler = handler as? _ChannelOutboundHandler {
self.outboundHandler = handler
} else {
self.outboundHandler = 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(error: 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(error: 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(error: 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(error: 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(promise: EventLoopPromise<Void>?) {
if let outboundNext = self.prev {
outboundNext.invokeFlush(promise: promise)
} else {
promise?.fail(error: ChannelError.ioOnClosedChannel)
}
}
/// 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(error: 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(error: 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(error: ChannelError.ioOnClosedChannel)
}
}
fileprivate func invokeChannelRegistered() {
assert(inEventLoop)
do {
if let inboundHandler = self.inboundHandler {
try inboundHandler.channelRegistered(ctx: self)
} else {
self.next?.invokeChannelRegistered()
}
} catch let err {
invokeErrorCaught(err)
}
}
fileprivate func invokeChannelUnregistered() {
assert(inEventLoop)
do {
if let inboundHandler = self.inboundHandler {
try inboundHandler.channelUnregistered(ctx: self)
} else {
self.next?.invokeChannelUnregistered()
}
} catch let err {
invokeErrorCaught(err)
}
}
fileprivate func invokeChannelActive() {
assert(inEventLoop)
do {
if let inboundHandler = self.inboundHandler {
try inboundHandler.channelActive(ctx: self)
} else {
self.next?.invokeChannelActive()
}
} catch let err {
invokeErrorCaught(err)
}
}
fileprivate func invokeChannelInactive() {
assert(inEventLoop)
do {
if let inboundHandler = self.inboundHandler {
try inboundHandler.channelInactive(ctx: self)
} else {
self.next?.invokeChannelInactive()
}
} catch let err {
invokeErrorCaught(err)
}
}
fileprivate func invokeChannelRead(_ data: NIOAny) {
assert(inEventLoop)
do {
if let inboundHandler = self.inboundHandler {
try inboundHandler.channelRead(ctx: self, data: data)
} else {
self.next?.invokeChannelRead(data)
}
} catch let err {
invokeErrorCaught(err)
}
}
fileprivate func invokeChannelReadComplete() {
assert(inEventLoop)
do {
if let inboundHandler = self.inboundHandler {
try inboundHandler.channelReadComplete(ctx: self)
} else {
self.next?.invokeChannelReadComplete()
}
} catch let err {
invokeErrorCaught(err)
}
}
fileprivate func invokeChannelWritabilityChanged() {
assert(inEventLoop)
do {
if let inboundHandler = self.inboundHandler {
try inboundHandler.channelWritabilityChanged(ctx: self)
} else {
self.next?.invokeChannelWritabilityChanged()
}
} catch let err {
invokeErrorCaught(err)
}
}
fileprivate func invokeErrorCaught(_ error: Error) {
assert(inEventLoop)
do {
if let inboundHandler = self.inboundHandler {
try inboundHandler.errorCaught(ctx: self, error: error)
} else {
self.next?.invokeErrorCaught(error)
}
} catch let err {
// Forward the error thrown by errorCaught through the pipeline
fireErrorCaught(err)
}
}
fileprivate func invokeUserInboundEventTriggered(_ event: Any) {
assert(inEventLoop)
do {
if let inboundHandler = self.inboundHandler {
try inboundHandler.userInboundEventTriggered(ctx: self, event: event)
} else {
self.next?.invokeUserInboundEventTriggered(event)
}
} catch let err {
invokeErrorCaught(err)
}
}
fileprivate func invokeRegister(promise: EventLoopPromise<Void>?) {
assert(inEventLoop)
assert(promise.map { !$0.futureResult.fulfilled } ?? true, "Promise \(promise!) already fulfilled")
if let outboundHandler = self.outboundHandler {
outboundHandler.register(ctx: self, promise: promise)
} else {
self.prev?.invokeRegister(promise: promise)
}
}
fileprivate func invokeBind(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
assert(inEventLoop)
assert(promise.map { !$0.futureResult.fulfilled } ?? true, "Promise \(promise!) already fulfilled")
if let outboundHandler = self.outboundHandler {
outboundHandler.bind(ctx: self, to: address, promise: promise)
} else {
self.prev?.invokeBind(to: address, promise: promise)
}
}
fileprivate func invokeConnect(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
assert(inEventLoop)
assert(promise.map { !$0.futureResult.fulfilled } ?? true, "Promise \(promise!) already fulfilled")
if let outboundHandler = self.outboundHandler {
outboundHandler.connect(ctx: self, to: address, promise: promise)
} else {
self.prev?.invokeConnect(to: address, promise: promise)
}
}
fileprivate func invokeWrite(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
assert(inEventLoop)
assert(promise.map { !$0.futureResult.fulfilled } ?? true, "Promise \(promise!) already fulfilled")
if let outboundHandler = self.outboundHandler {
outboundHandler.write(ctx: self, data: data, promise: promise)
} else {
self.prev?.invokeWrite(data, promise: promise)
}
}
fileprivate func invokeFlush(promise: EventLoopPromise<Void>?) {
assert(inEventLoop)
if let outboundHandler = self.outboundHandler {
outboundHandler.flush(ctx: self, promise: promise)
} else {
self.prev?.invokeFlush(promise: promise)
}
}
fileprivate func invokeWriteAndFlush(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
assert(inEventLoop)
assert(promise.map { !$0.futureResult.fulfilled } ?? true, "Promise \(promise!) already fulfilled")
if let outboundHandler = self.outboundHandler {
if let promise = promise {
let writePromise: EventLoopPromise<Void> = eventLoop.newPromise()
let flushPromise: EventLoopPromise<Void> = eventLoop.newPromise()
outboundHandler.write(ctx: self, data: data, promise: writePromise)
outboundHandler.flush(ctx: self, promise: flushPromise)
writePromise.futureResult.and(flushPromise.futureResult).map { (_: ((), ())) in }.cascade(promise: promise)
} else {
outboundHandler.write(ctx: self, data: data, promise: nil)
outboundHandler.flush(ctx: self, promise: nil)
}
} else {
self.prev?.invokeWriteAndFlush(data, promise: promise)
}
}
fileprivate func invokeRead() {
assert(inEventLoop)
if let outboundHandler = self.outboundHandler {
outboundHandler.read(ctx: self)
} else {
self.prev?.invokeRead()
}
}
fileprivate func invokeClose(mode: CloseMode, promise: EventLoopPromise<Void>?) {
assert(inEventLoop)
assert(promise.map { !$0.futureResult.fulfilled } ?? true, "Promise \(promise!) already fulfilled")
if let outboundHandler = self.outboundHandler {
outboundHandler.close(ctx: self, mode: mode, promise: promise)
} else {
self.prev?.invokeClose(mode: mode, promise: promise)
}
}
fileprivate func invokeTriggerUserOutboundEvent(_ event: Any, promise: EventLoopPromise<Void>?) {
assert(inEventLoop)
assert(promise.map { !$0.futureResult.fulfilled } ?? true, "Promise \(promise!) already fulfilled")
if let outboundHandler = self.outboundHandler {
outboundHandler.triggerUserOutboundEvent(ctx: self, event: event, promise: promise)
} else {
self.prev?.invokeTriggerUserOutboundEvent(event, promise: promise)
}
}
fileprivate func invokeHandlerAdded() throws {
assert(inEventLoop)
try handler.handlerAdded(ctx: self)
}
fileprivate func invokeHandlerRemoved() throws {
assert(inEventLoop)
try handler.handlerRemoved(ctx: self)
}
private var inEventLoop : Bool {
return eventLoop.inEventLoop
}
}
extension ChannelPipeline: CustomDebugStringConvertible {
public var debugDescription: String {
var desc = "ChannelPipeline (\(ObjectIdentifier(self))):\n"
var node = self.head
while let ctx = node {
let inboundStr = ctx.handler is _ChannelInboundHandler ? "I" : ""
let outboundStr = ctx.handler is _ChannelOutboundHandler ? "O" : ""
desc += " \(ctx.name) (\(type(of: ctx.handler))) [\(inboundStr)\(outboundStr)]\n"
node = ctx.next
}
return desc
}
}