/src/swift-nio/Sources/NIOCore/ChannelInvoker.swift
Line | Count | Source (jump to first uncovered line) |
1 | | //===----------------------------------------------------------------------===// |
2 | | // |
3 | | // This source file is part of the SwiftNIO open source project |
4 | | // |
5 | | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors |
6 | | // Licensed under Apache License v2.0 |
7 | | // |
8 | | // See LICENSE.txt for license information |
9 | | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors |
10 | | // |
11 | | // SPDX-License-Identifier: Apache-2.0 |
12 | | // |
13 | | //===----------------------------------------------------------------------===// |
14 | | /// Allows users to invoke an "outbound" operation related to a `Channel` that will flow through the `ChannelPipeline` until |
15 | | /// it will finally be executed by the the `ChannelCore` implementation. |
16 | | public protocol ChannelOutboundInvoker { |
17 | | |
18 | | /// Register on an `EventLoop` and so have all its IO handled. |
19 | | /// |
20 | | /// - Parameters: |
21 | | /// - promise: the `EventLoopPromise` that will be notified once the operation completes, |
22 | | /// or `nil` if not interested in the outcome of the operation. |
23 | | func register(promise: EventLoopPromise<Void>?) |
24 | | |
25 | | /// Bind to a `SocketAddress`. |
26 | | /// - Parameters: |
27 | | /// - to: the `SocketAddress` to which we should bind the `Channel`. |
28 | | /// - promise: the `EventLoopPromise` that will be notified once the operation completes, |
29 | | /// or `nil` if not interested in the outcome of the operation. |
30 | | func bind(to: SocketAddress, promise: EventLoopPromise<Void>?) |
31 | | |
32 | | /// Connect to a `SocketAddress`. |
33 | | /// - Parameters: |
34 | | /// - to: the `SocketAddress` to which we should connect the `Channel`. |
35 | | /// - promise: the `EventLoopPromise` that will be notified once the operation completes, |
36 | | /// or `nil` if not interested in the outcome of the operation. |
37 | | func connect(to: SocketAddress, promise: EventLoopPromise<Void>?) |
38 | | |
39 | | /// Write data to the remote peer. |
40 | | /// |
41 | | /// Be aware that to be sure that data is really written to the remote peer you need to call `flush` or use `writeAndFlush`. |
42 | | /// Calling `write` multiple times and then `flush` may allow the `Channel` to `write` multiple data objects to the remote peer with one syscall. |
43 | | /// |
44 | | /// - Parameters: |
45 | | /// - data: the data to write |
46 | | /// - promise: the `EventLoopPromise` that will be notified once the operation completes, |
47 | | /// or `nil` if not interested in the outcome of the operation. |
48 | | @available( |
49 | | *, |
50 | | deprecated, |
51 | | message: "NIOAny is not Sendable. Avoid wrapping the value in NIOAny to silence this warning." |
52 | | ) |
53 | | func write(_ data: NIOAny, promise: EventLoopPromise<Void>?) |
54 | | |
55 | | /// Flush data that was previously written via `write` to the remote peer. |
56 | | func flush() |
57 | | |
58 | | /// Shortcut for calling `write` and `flush`. |
59 | | /// |
60 | | /// - Parameters: |
61 | | /// - data: the data to write |
62 | | /// - promise: the `EventLoopPromise` that will be notified once the `write` operation completes, |
63 | | /// or `nil` if not interested in the outcome of the operation. |
64 | | @available( |
65 | | *, |
66 | | deprecated, |
67 | | message: "NIOAny is not Sendable. Avoid wrapping the value in NIOAny to silence this warning." |
68 | | ) |
69 | | func writeAndFlush(_ data: NIOAny, promise: EventLoopPromise<Void>?) |
70 | | |
71 | | /// Signal that we want to read from the `Channel` once there is data ready. |
72 | | /// |
73 | | /// If `ChannelOptions.autoRead` is set for a `Channel` (which is the default) this method is automatically invoked by the transport implementation, |
74 | | /// otherwise it's the user's responsibility to call this method manually once new data should be read and processed. |
75 | | /// |
76 | | func read() |
77 | | |
78 | | /// Close the `Channel` and so the connection if one exists. |
79 | | /// |
80 | | /// - Parameters: |
81 | | /// - mode: the `CloseMode` that is used |
82 | | /// - promise: the `EventLoopPromise` that will be notified once the operation completes, |
83 | | /// or `nil` if not interested in the outcome of the operation. |
84 | | func close(mode: CloseMode, promise: EventLoopPromise<Void>?) |
85 | | |
86 | | /// Trigger a custom user outbound event which will flow through the `ChannelPipeline`. |
87 | | /// |
88 | | /// - Parameters: |
89 | | /// - event: The event itself. |
90 | | /// - promise: the `EventLoopPromise` that will be notified once the operation completes, |
91 | | /// or `nil` if not interested in the outcome of the operation. |
92 | | @preconcurrency |
93 | | func triggerUserOutboundEvent(_ event: Any & Sendable, promise: EventLoopPromise<Void>?) |
94 | | |
95 | | /// The `EventLoop` which is used by this `ChannelOutboundInvoker` for execution. |
96 | | var eventLoop: EventLoop { get } |
97 | | } |
98 | | |
99 | | /// Extra `ChannelOutboundInvoker` methods. Each method that returns a `EventLoopFuture` will just do the following: |
100 | | /// - create a new `EventLoopPromise<Void>` |
101 | | /// - call the corresponding method that takes a `EventLoopPromise<Void>` |
102 | | /// - return `EventLoopPromise.futureResult` |
103 | | extension ChannelOutboundInvoker { |
104 | | |
105 | | /// Register on an `EventLoop` and so have all its IO handled. |
106 | | /// - Parameters: |
107 | | /// - file: The file this function was called in, for debugging purposes. |
108 | | /// - line: The line this function was called on, for debugging purposes. |
109 | | /// - Returns: the future which will be notified once the operation completes. |
110 | 13.8k | public func register(file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> { |
111 | 13.8k | let promise = makePromise(file: file, line: line) |
112 | 13.8k | register(promise: promise) |
113 | 13.8k | return promise.futureResult |
114 | 13.8k | } |
115 | | |
116 | | /// Bind to a `SocketAddress`. |
117 | | /// - Parameters: |
118 | | /// - address: the `SocketAddress` to which we should bind the `Channel`. |
119 | | /// - file: The file this function was called in, for debugging purposes. |
120 | | /// - line: The line this function was called on, for debugging purposes. |
121 | | /// - Returns: the future which will be notified once the operation completes. |
122 | | public func bind( |
123 | | to address: SocketAddress, |
124 | | file: StaticString = #fileID, |
125 | | line: UInt = #line |
126 | 0 | ) -> EventLoopFuture<Void> { |
127 | 0 | let promise = makePromise(file: file, line: line) |
128 | 0 | bind(to: address, promise: promise) |
129 | 0 | return promise.futureResult |
130 | 0 | } |
131 | | |
132 | | /// Connect to a `SocketAddress`. |
133 | | /// - Parameters: |
134 | | /// - address: the `SocketAddress` to which we should connect the `Channel`. |
135 | | /// - file: The file this function was called in, for debugging purposes. |
136 | | /// - line: The line this function was called on, for debugging purposes. |
137 | | /// - Returns: the future which will be notified once the operation completes. |
138 | | public func connect( |
139 | | to address: SocketAddress, |
140 | | file: StaticString = #fileID, |
141 | | line: UInt = #line |
142 | 0 | ) -> EventLoopFuture<Void> { |
143 | 0 | let promise = makePromise(file: file, line: line) |
144 | 0 | connect(to: address, promise: promise) |
145 | 0 | return promise.futureResult |
146 | 0 | } |
147 | | |
148 | | /// Write data to the remote peer. |
149 | | /// |
150 | | /// Be aware that to be sure that data is really written to the remote peer you need to call `flush` or use `writeAndFlush`. |
151 | | /// Calling `write` multiple times and then `flush` may allow the `Channel` to `write` multiple data objects to the remote peer with one syscall. |
152 | | /// |
153 | | /// - Parameters: |
154 | | /// - data: the data to write |
155 | | /// - file: The file this function was called in, for debugging purposes. |
156 | | /// - line: The line this function was called on, for debugging purposes. |
157 | | /// - Returns: the future which will be notified once the operation completes. |
158 | | @available( |
159 | | *, |
160 | | deprecated, |
161 | | message: "NIOAny is not Sendable. Avoid wrapping the value in NIOAny to silence this warning." |
162 | | ) |
163 | 0 | public func write(_ data: NIOAny, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> { |
164 | 0 | let promise = makePromise(file: file, line: line) |
165 | 0 | write(data, promise: promise) |
166 | 0 | return promise.futureResult |
167 | 0 | } |
168 | | |
169 | | /// Shortcut for calling `write` and `flush`. |
170 | | /// |
171 | | /// - Parameters: |
172 | | /// - data: the data to write |
173 | | /// - file: The file this function was called in, for debugging purposes. |
174 | | /// - line: The line this function was called on, for debugging purposes. |
175 | | /// - Returns: the future which will be notified once the `write` operation completes. |
176 | | @available( |
177 | | *, |
178 | | deprecated, |
179 | | message: "NIOAny is not Sendable. Avoid wrapping the value in NIOAny to silence this warning." |
180 | | ) |
181 | | public func writeAndFlush(_ data: NIOAny, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> |
182 | 0 | { |
183 | 0 | let promise = makePromise(file: file, line: line) |
184 | 0 | writeAndFlush(data, promise: promise) |
185 | 0 | return promise.futureResult |
186 | 0 | } |
187 | | |
188 | | /// Close the `Channel` and so the connection if one exists. |
189 | | /// |
190 | | /// - Parameters: |
191 | | /// - mode: the `CloseMode` that is used |
192 | | /// - file: The file this function was called in, for debugging purposes. |
193 | | /// - line: The line this function was called on, for debugging purposes. |
194 | | /// - Returns: the future which will be notified once the operation completes. |
195 | | public func close(mode: CloseMode = .all, file: StaticString = #fileID, line: UInt = #line) -> EventLoopFuture<Void> |
196 | 13.8k | { |
197 | 13.8k | let promise = makePromise(file: file, line: line) |
198 | 13.8k | self.close(mode: mode, promise: promise) |
199 | 13.8k | return promise.futureResult |
200 | 13.8k | } |
201 | | |
202 | | /// Trigger a custom user outbound event which will flow through the `ChannelPipeline`. |
203 | | /// |
204 | | /// - Parameters: |
205 | | /// - event: the event itself. |
206 | | /// - file: The file this function was called in, for debugging purposes. |
207 | | /// - line: The line this function was called on, for debugging purposes. |
208 | | /// - Returns: the future which will be notified once the operation completes. |
209 | | @preconcurrency |
210 | | public func triggerUserOutboundEvent( |
211 | | _ event: Any & Sendable, |
212 | | file: StaticString = #fileID, |
213 | | line: UInt = #line |
214 | 0 | ) -> EventLoopFuture<Void> { |
215 | 0 | let promise = makePromise(file: file, line: line) |
216 | 0 | triggerUserOutboundEvent(event, promise: promise) |
217 | 0 | return promise.futureResult |
218 | 0 | } |
219 | | |
220 | 27.7k | private func makePromise(file: StaticString = #fileID, line: UInt = #line) -> EventLoopPromise<Void> { |
221 | 27.7k | eventLoop.makePromise(file: file, line: line) |
222 | 27.7k | } |
223 | | } |
224 | | |
225 | | /// Fire inbound events related to a `Channel` through the `ChannelPipeline` until its end is reached or it's consumed by a `ChannelHandler`. |
226 | | public protocol ChannelInboundInvoker { |
227 | | |
228 | | /// Called once a `Channel` was registered to its `EventLoop` and so IO will be processed. |
229 | | func fireChannelRegistered() |
230 | | |
231 | | /// Called once a `Channel` was unregistered from its `EventLoop` which means no IO will be handled for a `Channel` anymore. |
232 | | func fireChannelUnregistered() |
233 | | |
234 | | /// Called once a `Channel` becomes active. |
235 | | /// |
236 | | /// What active means depends on the `Channel` implementation and semantics. |
237 | | /// For example for TCP it means the `Channel` is connected to the remote peer. |
238 | | func fireChannelActive() |
239 | | |
240 | | /// Called once a `Channel` becomes inactive. |
241 | | /// |
242 | | /// What inactive means depends on the `Channel` implementation and semantics. |
243 | | /// For example for TCP it means the `Channel` was disconnected from the remote peer and closed. |
244 | | func fireChannelInactive() |
245 | | |
246 | | /// Called once there is some data read for a `Channel` that needs processing. |
247 | | /// |
248 | | /// - Parameters: |
249 | | /// - data: the data that was read and is ready to be processed. |
250 | | @available( |
251 | | *, |
252 | | deprecated, |
253 | | message: "NIOAny is not Sendable. Avoid wrapping the value in NIOAny to silence this warning." |
254 | | ) |
255 | | func fireChannelRead(_ data: NIOAny) |
256 | | |
257 | | /// Called once there is no more data to read immediately on a `Channel`. Any new data received will be handled later. |
258 | | func fireChannelReadComplete() |
259 | | |
260 | | /// Called when a `Channel`'s writable state changes. |
261 | | /// |
262 | | /// The writability state of a Channel depends on watermarks that can be set via `Channel.setOption` and how much data |
263 | | /// is still waiting to be transferred to the remote peer. |
264 | | /// You should take care to enforce some kind of backpressure if the channel becomes unwritable which means `Channel.isWritable` |
265 | | /// 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 |
266 | | /// protocol and other semantics. But for example you may want to stop writing to the `Channel` until `Channel.writable` becomes |
267 | | /// `true` again or stop reading at all. |
268 | | func fireChannelWritabilityChanged() |
269 | | |
270 | | /// Called when an inbound operation `Error` was caught. |
271 | | /// |
272 | | /// Be aware that for inbound operations this method is called while for outbound operations defined in `ChannelOutboundInvoker` |
273 | | /// the `EventLoopFuture` or `EventLoopPromise` will be notified. |
274 | | /// |
275 | | /// - Parameters: |
276 | | /// - error: the error we encountered. |
277 | | func fireErrorCaught(_ error: Error) |
278 | | |
279 | | /// Trigger a custom user inbound event which will flow through the `ChannelPipeline`. |
280 | | /// |
281 | | /// - Parameters: |
282 | | /// - event: the event itself. |
283 | | @preconcurrency |
284 | | func fireUserInboundEventTriggered(_ event: Any & Sendable) |
285 | | } |
286 | | |
287 | | /// A protocol that signals that outbound and inbound events are triggered by this invoker. |
288 | | public protocol ChannelInvoker: ChannelOutboundInvoker, ChannelInboundInvoker {} |
289 | | |
290 | | /// Specify what kind of close operation is requested. |
291 | | public enum CloseMode: Sendable { |
292 | | /// Close the output (writing) side of the `Channel` without closing the actual file descriptor. |
293 | | /// This is an optional mode which means it may not be supported by all `Channel` implementations. |
294 | | case output |
295 | | |
296 | | /// Close the input (reading) side of the `Channel` without closing the actual file descriptor. |
297 | | /// This is an optional mode which means it may not be supported by all `Channel` implementations. |
298 | | case input |
299 | | |
300 | | /// Close the whole `Channel (file descriptor). |
301 | | case all |
302 | | } |