Coverage Report

Created: 2026-02-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-nio/Sources/NIOCore/ChannelHandler.swift
Line
Count
Source
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
15
/// Base protocol for handlers that handle I/O events or intercept an I/O operation.
16
///
17
/// All methods are called from within the `EventLoop` that is assigned to the `Channel` itself.
18
///
19
/// You should _never_ implement this protocol directly. Please implement one of its sub-protocols.
20
public protocol ChannelHandler: AnyObject {
21
    /// Called when this `ChannelHandler` is added to the `ChannelPipeline`.
22
    ///
23
    /// - Parameters:
24
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
25
    func handlerAdded(context: ChannelHandlerContext)
26
27
    /// Called when this `ChannelHandler` is removed from the `ChannelPipeline`.
28
    ///
29
    /// - Parameters:
30
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
31
    func handlerRemoved(context: ChannelHandlerContext)
32
}
33
34
/// Untyped `ChannelHandler` which handles outbound I/O events or intercept an outbound I/O operation.
35
///
36
/// Despite the fact that `write` is one of the methods on this protocol, you should avoid assuming that "outbound" events are to do with
37
/// writing to channel sources. Instead, "outbound" events are events that are passed *to* the channel source (e.g. a socket): that is, things you tell
38
/// the channel source to do. That includes `write` ("write this data to the channel source"), but it also includes `read` ("please begin attempting to read from
39
/// the channel source") and `bind` ("please bind the following address"), which have nothing to do with sending data.
40
///
41
/// We _strongly_ advise against implementing this protocol directly. Please implement `ChannelOutboundHandler`.
42
public protocol _ChannelOutboundHandler: ChannelHandler {
43
44
    /// Called to request that the `Channel` register itself for I/O events with its `EventLoop`.
45
    /// This should call `context.register` to forward the operation to the next `_ChannelOutboundHandler` in the `ChannelPipeline` or
46
    /// complete the `EventLoopPromise` to let the caller know that the operation completed.
47
    ///
48
    /// - Parameters:
49
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
50
    ///   - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place.
51
    func register(context: ChannelHandlerContext, promise: EventLoopPromise<Void>?)
52
53
    /// Called to request that the `Channel` bind to a specific `SocketAddress`.
54
    ///
55
    /// This should call `context.bind` to forward the operation to the next `_ChannelOutboundHandler` in the `ChannelPipeline` or
56
    /// complete the `EventLoopPromise` to let the caller know that the operation completed.
57
    ///
58
    /// - Parameters:
59
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
60
    ///   - to: The `SocketAddress` to which this `Channel` should bind.
61
    ///   - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place.
62
    func bind(context: ChannelHandlerContext, to: SocketAddress, promise: EventLoopPromise<Void>?)
63
64
    /// Called to request that the `Channel` connect to a given `SocketAddress`.
65
    ///
66
    /// This should call `context.connect` to forward the operation to the next `_ChannelOutboundHandler` in the `ChannelPipeline` or
67
    /// complete the `EventLoopPromise` to let the caller know that the operation completed.
68
    ///
69
    /// - Parameters:
70
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
71
    ///   - to: The `SocketAddress` to which the the `Channel` should connect.
72
    ///   - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place.
73
    func connect(context: ChannelHandlerContext, to: SocketAddress, promise: EventLoopPromise<Void>?)
74
75
    /// Called to request a write operation. The write operation will write the messages through the
76
    /// `ChannelPipeline`. Those are then ready to be flushed to the actual `Channel` when
77
    /// `Channel.flush` or `ChannelHandlerContext.flush` is called.
78
    ///
79
    /// This should call `context.write` to forward the operation to the next `_ChannelOutboundHandler` in the `ChannelPipeline` or
80
    /// complete the `EventLoopPromise` to let the caller know that the operation completed.
81
    ///
82
    /// - Parameters:
83
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
84
    ///   - data: The data to write through the `Channel`, wrapped in a `NIOAny`.
85
    ///   - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place.
86
    func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?)
87
88
    /// Called to request that the `Channel` flush all pending writes. The flush operation will try to flush out all previous written messages
89
    /// that are pending.
90
    ///
91
    /// This should call `context.flush` to forward the operation to the next `_ChannelOutboundHandler` in the `ChannelPipeline` or just
92
    /// discard it if the flush should be suppressed.
93
    ///
94
    /// - Parameters:
95
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
96
    func flush(context: ChannelHandlerContext)
97
98
    /// Called to request that the `Channel` perform a read when data is ready. The read operation will signal that we are ready to read more data.
99
    ///
100
    /// This should call `context.read` to forward the operation to the next `_ChannelOutboundHandler` in the `ChannelPipeline` or just
101
    /// discard it if the read should be suppressed.
102
    ///
103
    /// - Parameters:
104
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
105
    func read(context: ChannelHandlerContext)
106
107
    /// Called to request that the `Channel` close itself down.
108
    ///
109
    /// This should call `context.close` to forward the operation to the next `_ChannelOutboundHandler` in the `ChannelPipeline` or
110
    /// complete the `EventLoopPromise` to let the caller know that the operation completed.
111
    ///
112
    /// - Parameters:
113
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
114
    ///   - mode: The `CloseMode` to apply
115
    ///   - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place.
116
    func close(context: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise<Void>?)
117
118
    /// Called when an user outbound event is triggered.
119
    ///
120
    /// This should call `context.triggerUserOutboundEvent` to forward the operation to the next `_ChannelOutboundHandler` in the `ChannelPipeline` or
121
    /// complete the `EventLoopPromise` to let the caller know that the operation completed.
122
    ///
123
    /// - Parameters:
124
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
125
    ///   - event: The triggered event.
126
    ///   - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place.
127
    func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise<Void>?)
128
}
129
130
/// Untyped `ChannelHandler` which handles inbound I/O events.
131
///
132
/// Despite the fact that `channelRead` is one of the methods on this protocol, you should avoid assuming that "inbound" events are to do with
133
/// reading from channel sources. Instead, "inbound" events are events that originate *from* the channel source (e.g. the socket): that is, events that the
134
/// channel source tells you about. This includes things like `channelRead` ("there is some data to read"), but it also includes things like
135
/// `channelWritabilityChanged` ("this source is no longer marked writable").
136
///
137
/// We _strongly_ advise against implementing this protocol directly. Please implement `ChannelInboundHandler`.
138
public protocol _ChannelInboundHandler: ChannelHandler {
139
140
    /// Called when the `Channel` has successfully registered with its `EventLoop` to handle I/O.
141
    ///
142
    /// This should call `context.fireChannelRegistered` to forward the operation to the next `_ChannelInboundHandler` in the `ChannelPipeline` if you want to allow the next handler to also handle the event.
143
    ///
144
    /// - Parameters:
145
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
146
    func channelRegistered(context: ChannelHandlerContext)
147
148
    /// Called when the `Channel` has unregistered from its `EventLoop`, and so will no longer be receiving I/O events.
149
    ///
150
    /// This should call `context.fireChannelUnregistered` to forward the operation to the next `_ChannelInboundHandler` in the `ChannelPipeline` if you want to allow the next handler to also handle the event.
151
    ///
152
    /// - Parameters:
153
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
154
    func channelUnregistered(context: ChannelHandlerContext)
155
156
    /// Called when the `Channel` has become active, and is able to send and receive data.
157
    ///
158
    /// This should call `context.fireChannelActive` to forward the operation to the next `_ChannelInboundHandler` in the `ChannelPipeline` if you want to allow the next handler to also handle the event.
159
    ///
160
    /// - Parameters:
161
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
162
    func channelActive(context: ChannelHandlerContext)
163
164
    /// Called when the `Channel` has become inactive and is no longer able to send and receive data.
165
    ///
166
    /// This should call `context.fireChannelInactive` to forward the operation to the next `_ChannelInboundHandler` in the `ChannelPipeline` if you want to allow the next handler to also handle the event.
167
    ///
168
    /// - Parameters:
169
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
170
    func channelInactive(context: ChannelHandlerContext)
171
172
    /// Called when some data has been read from the remote peer.
173
    ///
174
    /// This should call `context.fireChannelRead` to forward the operation to the next `_ChannelInboundHandler` in the `ChannelPipeline` if you want to allow the next handler to also handle the event.
175
    ///
176
    /// - Parameters:
177
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
178
    ///   - data: The data read from the remote peer, wrapped in a `NIOAny`.
179
    func channelRead(context: ChannelHandlerContext, data: NIOAny)
180
181
    /// Called when the `Channel` has completed its current read loop, either because no more data is available to read from the transport at this time, or because the `Channel` needs to yield to the event loop to process other I/O events for other `Channel`s.
182
    /// If `ChannelOptions.autoRead` is `false` no further read attempt will be made until `ChannelHandlerContext.read` or `Channel.read` is explicitly called.
183
    ///
184
    /// This should call `context.fireChannelReadComplete` to forward the operation to the next `_ChannelInboundHandler` in the `ChannelPipeline` if you want to allow the next handler to also handle the event.
185
    ///
186
    /// - Parameters:
187
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
188
    func channelReadComplete(context: ChannelHandlerContext)
189
190
    /// The writability state of the `Channel` has changed, either because it has buffered more data than the writability high water mark, or because the amount of buffered data has dropped below the writability low water mark.
191
    /// You can check the state with `Channel.isWritable`.
192
    ///
193
    /// This should call `context.fireChannelWritabilityChanged` to forward the operation to the next `_ChannelInboundHandler` in the `ChannelPipeline` if you want to allow the next handler to also handle the event.
194
    ///
195
    /// - Parameters:
196
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
197
    func channelWritabilityChanged(context: ChannelHandlerContext)
198
199
    /// Called when a user inbound event has been triggered.
200
    ///
201
    /// This should call `context.fireUserInboundEventTriggered` to forward the operation to the next `_ChannelInboundHandler` in the `ChannelPipeline` if you want to allow the next handler to also handle the event.
202
    ///
203
    /// - Parameters:
204
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
205
    ///   - event: The event.
206
    func userInboundEventTriggered(context: ChannelHandlerContext, event: Any)
207
208
    /// An error was encountered earlier in the inbound `ChannelPipeline`.
209
    ///
210
    /// This should call `context.fireErrorCaught` to forward the operation to the next `_ChannelInboundHandler` in the `ChannelPipeline` if you want to allow the next handler to also handle the error.
211
    ///
212
    /// - Parameters:
213
    ///   - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to.
214
    ///   - error: The `Error` that was encountered.
215
    func errorCaught(context: ChannelHandlerContext, error: Error)
216
}
217
218
// Default implementations for the ChannelHandler protocol
219
extension ChannelHandler {
220
221
    /// Do nothing by default.
222
0
    public func handlerAdded(context: ChannelHandlerContext) {
223
0
    }
224
225
    /// Do nothing by default.
226
886k
    public func handlerRemoved(context: ChannelHandlerContext) {
227
886k
    }
228
}
229
230
/// Provides default implementations for all methods defined by `_ChannelOutboundHandler`.
231
///
232
/// These default implementations will just call `context.methodName` to forward to the next `_ChannelOutboundHandler` in
233
/// the `ChannelPipeline` until the operation is handled by the `Channel` itself.
234
extension _ChannelOutboundHandler {
235
236
0
    public func register(context: ChannelHandlerContext, promise: EventLoopPromise<Void>?) {
237
0
        context.register(promise: promise)
238
0
    }
239
240
0
    public func bind(context: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise<Void>?) {
241
0
        context.bind(to: address, promise: promise)
242
0
    }
243
244
0
    public func connect(context: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise<Void>?) {
245
0
        context.connect(to: address, promise: promise)
246
0
    }
247
248
0
    public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
249
0
        context.write(data, promise: promise)
250
0
    }
251
252
0
    public func flush(context: ChannelHandlerContext) {
253
0
        context.flush()
254
0
    }
255
256
0
    public func read(context: ChannelHandlerContext) {
257
0
        context.read()
258
0
    }
259
260
0
    public func close(context: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise<Void>?) {
261
0
        context.close(mode: mode, promise: promise)
262
0
    }
263
264
0
    public func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise<Void>?) {
265
0
        context.triggerUserOutboundEvent(event, promise: promise)
266
0
    }
267
}
268
269
/// Provides default implementations for all methods defined by `_ChannelInboundHandler`.
270
///
271
/// These default implementations will just `context.fire*` to forward to the next `_ChannelInboundHandler` in
272
/// the `ChannelPipeline` until the operation is handled by the `Channel` itself.
273
extension _ChannelInboundHandler {
274
275
0
    public func channelRegistered(context: ChannelHandlerContext) {
276
0
        context.fireChannelRegistered()
277
0
    }
278
279
13.1k
    public func channelUnregistered(context: ChannelHandlerContext) {
280
13.1k
        context.fireChannelUnregistered()
281
13.1k
    }
282
283
0
    public func channelActive(context: ChannelHandlerContext) {
284
0
        context.fireChannelActive()
285
0
    }
286
287
0
    public func channelInactive(context: ChannelHandlerContext) {
288
0
        context.fireChannelInactive()
289
0
    }
290
291
0
    public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
292
0
        context.fireChannelRead(data)
293
0
    }
294
295
13.1k
    public func channelReadComplete(context: ChannelHandlerContext) {
296
13.1k
        context.fireChannelReadComplete()
297
13.1k
    }
298
299
0
    public func channelWritabilityChanged(context: ChannelHandlerContext) {
300
0
        context.fireChannelWritabilityChanged()
301
0
    }
302
303
0
    public func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) {
304
0
        context.fireUserInboundEventTriggered(event)
305
0
    }
306
307
0
    public func errorCaught(context: ChannelHandlerContext, error: Error) {
308
0
        context.fireErrorCaught(error)
309
0
    }
310
}
311
312
/// A `RemovableChannelHandler` is a `ChannelHandler` that can be dynamically removed from a `ChannelPipeline` whilst
313
/// the `Channel` is operating normally.
314
/// A `RemovableChannelHandler` is required to remove itself from the `ChannelPipeline` (using
315
/// `ChannelHandlerContext.leavePipeline`) as soon as possible.
316
///
317
/// - Note: When a `Channel` gets torn down, every `ChannelHandler` in the `Channel`'s `ChannelPipeline` will be
318
///         removed from the `ChannelPipeline`. Those removals however happen synchronously and are not going through
319
///         the methods of this protocol.
320
public protocol RemovableChannelHandler: ChannelHandler {
321
    /// Ask the receiving `RemovableChannelHandler` to remove itself from the `ChannelPipeline` as soon as possible.
322
    /// The receiving `RemovableChannelHandler` may elect to remove itself sometime after this method call, rather than
323
    /// immediately, but if it does so it must take the necessary precautions to handle events arriving between the
324
    /// invocation of this method and the call to `ChannelHandlerContext.leavePipeline` that triggers the actual
325
    /// removal.
326
    ///
327
    /// - Note: Like the other `ChannelHandler` methods, this method should not be invoked by the user directly. To
328
    ///         remove a `RemovableChannelHandler` from the `ChannelPipeline`, use `ChannelPipeline.removeHandler`.
329
    ///
330
    /// - Parameters:
331
    ///    - context: The `ChannelHandlerContext` of the `RemovableChannelHandler` to be removed from the `ChannelPipeline`.
332
    ///    - removalToken: The removal token to hand to `ChannelHandlerContext.leavePipeline` to trigger the actual
333
    ///                    removal from the `ChannelPipeline`.
334
    func removeHandler(context: ChannelHandlerContext, removalToken: ChannelHandlerContext.RemovalToken)
335
}
336
337
extension RemovableChannelHandler {
338
    /// Implements the default behaviour which is to synchronously remove the handler from the pipeline. Thanks to this,
339
    /// stateless `ChannelHandler`s can just use `RemovableChannelHandler` as a marker-protocol and declare themselves
340
    /// as removable without writing any extra code.
341
0
    public func removeHandler(context: ChannelHandlerContext, removalToken: ChannelHandlerContext.RemovalToken) {
342
0
        precondition(context.handler === self)
343
0
        context.leavePipeline(removalToken: removalToken)
344
0
    }
345
}
346
347
/// A `NIOOutboundByteBufferingChannelHandler` is a `ChannelHandler` that
348
/// reports the number of bytes buffered for outbound direction.
349
public protocol NIOOutboundByteBufferingChannelHandler {
350
    /// The number of bytes buffered in the channel handler, which are queued to be sent to
351
    /// the next outbound channel handler.
352
    var outboundBufferedBytes: Int { get }
353
}
354
355
/// A `NIOInboundByteBufferingChannelHandler` is a `ChannelHandler` that
356
/// reports the number of bytes buffered for inbound direction.
357
public protocol NIOInboundByteBufferingChannelHandler {
358
    /// The number of bytes buffered in the channel handler, which are queued to be sent to
359
    /// the next inbound channel handler.
360
    var inboundBufferedBytes: Int { get }
361
}