1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
|
//===----------------------------------------------------------------------===//
//
// 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 = #file, 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 = #file, 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 = #file, 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 = #file, 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 = #file, 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 = #file, 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 = #file, line: UInt = #line) -> EventLoopFuture<Void> {
let promise = makePromise(file: file, line: line)
triggerUserOutboundEvent(event, promise: promise)
return promise.futureResult
}
private func makePromise(file: StaticString = #file, 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 {
/// 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
}
|