246 lines
12 KiB
Swift
246 lines
12 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
/// Allows users to invoke an "outbound" operation related to a `Channel` that will flow through the `ChannelPipeline` until
|
|
/// it will finally be executed by the the `ChannelCore` implementation.
|
|
public protocol ChannelOutboundInvoker {
|
|
|
|
/// Register on an `EventLoop` and so have all its IO handled.
|
|
///
|
|
/// - parameters:
|
|
/// - promise: the `EventLoopPromise` that will be notified once the operation completes,
|
|
/// or `nil` if not interested in the outcome of the operation.
|
|
func register(promise: EventLoopPromise<Void>?)
|
|
|
|
/// Bind to a `SocketAddress`.
|
|
/// - parameters:
|
|
/// - to: the `SocketAddress` to which we should bind the `Channel`.
|
|
/// - promise: the `EventLoopPromise` that will be notified once the operation completes,
|
|
/// or `nil` if not interested in the outcome of the operation.
|
|
func bind(to: SocketAddress, promise: EventLoopPromise<Void>?)
|
|
|
|
/// Connect to a `SocketAddress`.
|
|
/// - parameters:
|
|
/// - to: the `SocketAddress` to which we should connect the `Channel`.
|
|
/// - promise: the `EventLoopPromise` that will be notified once the operation completes,
|
|
/// or `nil` if not interested in the outcome of the operation.
|
|
func connect(to: SocketAddress, promise: EventLoopPromise<Void>?)
|
|
|
|
/// Write data to the remote peer.
|
|
///
|
|
/// Be aware that to be sure that data is really written to the remote peer you need to call `flush` or use `writeAndFlush`.
|
|
/// Calling `write` multiple times and then `flush` may allow the `Channel` to `write` multiple data objects to the remote peer with one syscall.
|
|
///
|
|
/// - parameters:
|
|
/// - data: the data to write
|
|
/// - promise: the `EventLoopPromise` that will be notified once the operation completes,
|
|
/// or `nil` if not interested in the outcome of the operation.
|
|
func write(_ data: NIOAny, promise: EventLoopPromise<Void>?)
|
|
|
|
/// Flush data that was previously written via `write` to the remote peer.
|
|
func flush()
|
|
|
|
/// Shortcut for calling `write` and `flush`.
|
|
///
|
|
/// - parameters:
|
|
/// - data: the data to write
|
|
/// - promise: the `EventLoopPromise` that will be notified once the `write` operation completes,
|
|
/// or `nil` if not interested in the outcome of the operation.
|
|
func writeAndFlush(_ data: NIOAny, promise: EventLoopPromise<Void>?)
|
|
|
|
/// Signal that we want to read from the `Channel` once there is data ready.
|
|
///
|
|
/// If `ChannelOptions.autoRead` is set for a `Channel` (which is the default) this method is automatically invoked by the transport implementation,
|
|
/// otherwise it's the user's responsibility to call this method manually once new data should be read and processed.
|
|
///
|
|
func read()
|
|
|
|
/// Close the `Channel` and so the connection if one exists.
|
|
///
|
|
/// - parameters:
|
|
/// - mode: the `CloseMode` that is used
|
|
/// - promise: the `EventLoopPromise` that will be notified once the operation completes,
|
|
/// or `nil` if not interested in the outcome of the operation.
|
|
func close(mode: CloseMode, promise: EventLoopPromise<Void>?)
|
|
|
|
/// Trigger a custom user outbound event which will flow through the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - promise: the `EventLoopPromise` that will be notified once the operation completes,
|
|
/// or `nil` if not interested in the outcome of the operation.
|
|
func triggerUserOutboundEvent(_ event: Any, promise: EventLoopPromise<Void>?)
|
|
|
|
/// The `EventLoop` which is used by this `ChannelOutboundInvoker` for execution.
|
|
var eventLoop: EventLoop { get }
|
|
}
|
|
|
|
/// Extra `ChannelOutboundInvoker` methods. Each method that returns a `EventLoopFuture` will just do the following:
|
|
/// - create a new `EventLoopPromise<Void>`
|
|
/// - call the corresponding method that takes a `EventLoopPromise<Void>`
|
|
/// - return `EventLoopPromise.futureResult`
|
|
extension ChannelOutboundInvoker {
|
|
|
|
/// Register on an `EventLoop` and so have all its IO handled.
|
|
///
|
|
/// - returns: the future which will be notified once the operation completes.
|
|
public func register(file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
|
let promise = makePromise(file: file, line: line)
|
|
register(promise: promise)
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Bind to a `SocketAddress`.
|
|
/// - parameters:
|
|
/// - to: the `SocketAddress` to which we should bind the `Channel`.
|
|
/// - returns: the future which will be notified once the operation completes.
|
|
public func bind(to address: SocketAddress, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
|
let promise = makePromise(file: file, line: line)
|
|
bind(to: address, promise: promise)
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Connect to a `SocketAddress`.
|
|
/// - parameters:
|
|
/// - to: the `SocketAddress` to which we should connect the `Channel`.
|
|
/// - returns: the future which will be notified once the operation completes.
|
|
public func connect(to address: SocketAddress, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
|
let promise = makePromise(file: file, line: line)
|
|
connect(to: address, promise: promise)
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Write data to the remote peer.
|
|
///
|
|
/// Be aware that to be sure that data is really written to the remote peer you need to call `flush` or use `writeAndFlush`.
|
|
/// Calling `write` multiple times and then `flush` may allow the `Channel` to `write` multiple data objects to the remote peer with one syscall.
|
|
///
|
|
/// - parameters:
|
|
/// - data: the data to write
|
|
/// - returns: the future which will be notified once the operation completes.
|
|
public func write(_ data: NIOAny, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
|
let promise = makePromise(file: file, line: line)
|
|
write(data, promise: promise)
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Shortcut for calling `write` and `flush`.
|
|
///
|
|
/// - parameters:
|
|
/// - data: the data to write
|
|
/// - returns: the future which will be notified once the `write` operation completes.
|
|
public func writeAndFlush(_ data: NIOAny, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
|
let promise = makePromise(file: file, line: line)
|
|
writeAndFlush(data, promise: promise)
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Close the `Channel` and so the connection if one exists.
|
|
///
|
|
/// - parameters:
|
|
/// - mode: the `CloseMode` that is used
|
|
/// - returns: the future which will be notified once the operation completes.
|
|
public func close(mode: CloseMode = .all, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
|
let promise = makePromise(file: file, line: line)
|
|
close(mode: mode, promise: promise)
|
|
return promise.futureResult
|
|
}
|
|
|
|
/// Trigger a custom user outbound event which will flow through the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - event: the event itself.
|
|
/// - returns: the future which will be notified once the operation completes.
|
|
public func triggerUserOutboundEvent(_ event: Any, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> {
|
|
let promise = makePromise(file: file, line: line)
|
|
triggerUserOutboundEvent(event, promise: promise)
|
|
return promise.futureResult
|
|
}
|
|
|
|
private func makePromise(file: StaticString = #fileID, line: UInt = #line) -> EventLoopPromise<Void> {
|
|
return eventLoop.makePromise(file: file, line: line)
|
|
}
|
|
}
|
|
|
|
/// Fire inbound events related to a `Channel` through the `ChannelPipeline` until its end is reached or it's consumed by a `ChannelHandler`.
|
|
public protocol ChannelInboundInvoker {
|
|
|
|
/// Called once a `Channel` was registered to its `EventLoop` and so IO will be processed.
|
|
func fireChannelRegistered()
|
|
|
|
/// Called once a `Channel` was unregistered from its `EventLoop` which means no IO will be handled for a `Channel` anymore.
|
|
func fireChannelUnregistered()
|
|
|
|
/// Called once a `Channel` becomes active.
|
|
///
|
|
/// What active means depends on the `Channel` implementation and semantics.
|
|
/// For example for TCP it means the `Channel` is connected to the remote peer.
|
|
func fireChannelActive()
|
|
|
|
/// Called once a `Channel` becomes inactive.
|
|
///
|
|
/// What inactive means depends on the `Channel` implementation and semantics.
|
|
/// For example for TCP it means the `Channel` was disconnected from the remote peer and closed.
|
|
func fireChannelInactive()
|
|
|
|
/// Called once there is some data read for a `Channel` that needs processing.
|
|
///
|
|
/// - parameters:
|
|
/// - data: the data that was read and is ready to be processed.
|
|
func fireChannelRead(_ data: NIOAny)
|
|
|
|
/// Called once there is no more data to read immediately on a `Channel`. Any new data received will be handled later.
|
|
func fireChannelReadComplete()
|
|
|
|
/// Called when a `Channel`'s writable state changes.
|
|
///
|
|
/// The writability state of a Channel depends on watermarks that can be set via `Channel.setOption` and how much data
|
|
/// is still waiting to be transferred to the remote peer.
|
|
/// You should take care to enforce some kind of backpressure if the channel becomes unwritable which means `Channel.isWritable`
|
|
/// will return `false` to ensure you do not consume too much memory due to queued writes. What exactly you should do here depends on the
|
|
/// protocol and other semantics. But for example you may want to stop writing to the `Channel` until `Channel.writable` becomes
|
|
/// `true` again or stop reading at all.
|
|
func fireChannelWritabilityChanged()
|
|
|
|
/// Called when an inbound operation `Error` was caught.
|
|
///
|
|
/// Be aware that for inbound operations this method is called while for outbound operations defined in `ChannelOutboundInvoker`
|
|
/// the `EventLoopFuture` or `EventLoopPromise` will be notified.
|
|
///
|
|
/// - parameters:
|
|
/// - error: the error we encountered.
|
|
func fireErrorCaught(_ error: Error)
|
|
|
|
/// Trigger a custom user inbound event which will flow through the `ChannelPipeline`.
|
|
///
|
|
/// - parameters:
|
|
/// - event: the event itself.
|
|
func fireUserInboundEventTriggered(_ event: Any)
|
|
}
|
|
|
|
/// A protocol that signals that outbound and inbound events are triggered by this invoker.
|
|
public protocol ChannelInvoker: ChannelOutboundInvoker, ChannelInboundInvoker { }
|
|
|
|
/// Specify what kind of close operation is requested.
|
|
public enum CloseMode: Sendable {
|
|
/// Close the output (writing) side of the `Channel` without closing the actual file descriptor.
|
|
/// This is an optional mode which means it may not be supported by all `Channel` implementations.
|
|
case output
|
|
|
|
/// Close the input (reading) side of the `Channel` without closing the actual file descriptor.
|
|
/// This is an optional mode which means it may not be supported by all `Channel` implementations.
|
|
case input
|
|
|
|
/// Close the whole `Channel (file descriptor).
|
|
case all
|
|
}
|