/src/swift-nio/Sources/NIOCore/ChannelOption.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 | | /// A configuration option that can be set on a `Channel` to configure different behaviour. |
16 | | public protocol ChannelOption: Equatable, _NIOPreconcurrencySendable { |
17 | | /// The type of the `ChannelOption`'s value. |
18 | | associatedtype Value: Sendable |
19 | | } |
20 | | |
21 | | public typealias SocketOptionName = Int32 |
22 | | #if (os(Linux) || os(Android)) && !canImport(Musl) |
23 | | public typealias SocketOptionLevel = Int |
24 | | public typealias SocketOptionValue = Int |
25 | | #else |
26 | | public typealias SocketOptionLevel = CInt |
27 | | public typealias SocketOptionValue = CInt |
28 | | #endif |
29 | | |
30 | | @available(*, deprecated, renamed: "ChannelOptions.Types.SocketOption") |
31 | | public typealias SocketOption = ChannelOptions.Types.SocketOption |
32 | | |
33 | | @available(*, deprecated, renamed: "ChannelOptions.Types.AllocatorOption") |
34 | | public typealias AllocatorOption = ChannelOptions.Types.AllocatorOption |
35 | | |
36 | | @available(*, deprecated, renamed: "ChannelOptions.Types.RecvAllocatorOption") |
37 | | public typealias RecvAllocatorOption = ChannelOptions.Types.RecvAllocatorOption |
38 | | |
39 | | @available(*, deprecated, renamed: "ChannelOptions.Types.AutoReadOption") |
40 | | public typealias AutoReadOption = ChannelOptions.Types.AutoReadOption |
41 | | |
42 | | @available(*, deprecated, renamed: "ChannelOptions.Types.WriteSpinOption") |
43 | | public typealias WriteSpinOption = ChannelOptions.Types.WriteSpinOption |
44 | | |
45 | | @available(*, deprecated, renamed: "ChannelOptions.Types.MaxMessagesPerReadOption") |
46 | | public typealias MaxMessagesPerReadOption = ChannelOptions.Types.MaxMessagesPerReadOption |
47 | | |
48 | | @available(*, deprecated, renamed: "ChannelOptions.Types.BacklogOption") |
49 | | public typealias BacklogOption = ChannelOptions.Types.BacklogOption |
50 | | |
51 | | @available(*, deprecated, renamed: "ChannelOptions.Types.DatagramVectorReadMessageCountOption") |
52 | | public typealias DatagramVectorReadMessageCountOption = ChannelOptions.Types.DatagramVectorReadMessageCountOption |
53 | | |
54 | | @available(*, deprecated, renamed: "ChannelOptions.Types.WriteBufferWaterMark") |
55 | | public typealias WriteBufferWaterMark = ChannelOptions.Types.WriteBufferWaterMark |
56 | | |
57 | | @available(*, deprecated, renamed: "ChannelOptions.Types.WriteBufferWaterMarkOption") |
58 | | public typealias WriteBufferWaterMarkOption = ChannelOptions.Types.WriteBufferWaterMarkOption |
59 | | |
60 | | @available(*, deprecated, renamed: "ChannelOptions.Types.ConnectTimeoutOption") |
61 | | public typealias ConnectTimeoutOption = ChannelOptions.Types.ConnectTimeoutOption |
62 | | |
63 | | @available(*, deprecated, renamed: "ChannelOptions.Types.AllowRemoteHalfClosureOption") |
64 | | public typealias AllowRemoteHalfClosureOption = ChannelOptions.Types.AllowRemoteHalfClosureOption |
65 | | |
66 | | extension ChannelOptions { |
67 | | public enum Types: Sendable { |
68 | | |
69 | | /// `SocketOption` allows users to specify configuration settings that are directly applied to the underlying socket file descriptor. |
70 | | /// |
71 | | /// Valid options are typically found in the various man pages like `man 4 tcp`. |
72 | | public struct SocketOption: ChannelOption, Equatable, Sendable { |
73 | | public typealias Value = (SocketOptionValue) |
74 | | |
75 | | public var optionLevel: NIOBSDSocket.OptionLevel |
76 | | public var optionName: NIOBSDSocket.Option |
77 | | |
78 | | public var level: SocketOptionLevel { |
79 | 0 | get { |
80 | 0 | SocketOptionLevel(optionLevel.rawValue) |
81 | 0 | } |
82 | 0 | set { |
83 | 0 | self.optionLevel = NIOBSDSocket.OptionLevel(rawValue: CInt(newValue)) |
84 | 0 | } |
85 | | } |
86 | | public var name: SocketOptionName { |
87 | 0 | get { |
88 | 0 | SocketOptionName(optionName.rawValue) |
89 | 0 | } |
90 | 0 | set { |
91 | 0 | self.optionName = NIOBSDSocket.Option(rawValue: CInt(newValue)) |
92 | 0 | } |
93 | | } |
94 | | |
95 | | #if !os(Windows) |
96 | | // Unavailable on Windows: there, WinSDK imports the socket option |
97 | | // level/name constants (`IPPROTO_IP`, `IP_TTL`, `SO_REUSEADDR`, …) |
98 | | // as enumeration cases rather than plain integers, so they don't fit |
99 | | // the integer `SocketOptionLevel`/`SocketOptionName` typealiases. |
100 | | // Use `init(level:name:)` taking `NIOBSDSocket.OptionLevel`/`Option` |
101 | | // instead (their `.ip`/`.tcp`/etc. accessors read the enum's raw |
102 | | // value). |
103 | | |
104 | | /// Create a new `SocketOption`. |
105 | | /// |
106 | | /// - Parameters: |
107 | | /// - level: The level for the option as defined in `man setsockopt`, e.g. SO_SOCKET. |
108 | | /// - name: The name of the option as defined in `man setsockopt`, e.g. `SO_REUSEADDR`. |
109 | 0 | public init(level: SocketOptionLevel, name: SocketOptionName) { |
110 | 0 | self.optionLevel = NIOBSDSocket.OptionLevel(rawValue: CInt(level)) |
111 | 0 | self.optionName = NIOBSDSocket.Option(rawValue: CInt(name)) |
112 | 0 | } |
113 | | #endif |
114 | | |
115 | | /// Create a new `SocketOption`. |
116 | | /// |
117 | | /// - Parameters: |
118 | | /// - level: The level for the option as defined in `man setsockopt`, e.g. SO_SOCKET. |
119 | | /// - name: The name of the option as defined in `man setsockopt`, e.g. `SO_REUSEADDR`. |
120 | 0 | public init(level: NIOBSDSocket.OptionLevel, name: NIOBSDSocket.Option) { |
121 | 0 | self.optionLevel = level |
122 | 0 | self.optionName = name |
123 | 0 | } |
124 | | } |
125 | | |
126 | | /// `AllocatorOption` allows to specify the `ByteBufferAllocator` to use. |
127 | | public struct AllocatorOption: ChannelOption, Sendable { |
128 | | public typealias Value = ByteBufferAllocator |
129 | | |
130 | 0 | public init() {} |
131 | | } |
132 | | |
133 | | /// `RecvAllocatorOption` allows users to specify the `RecvByteBufferAllocator` to use. |
134 | | public struct RecvAllocatorOption: ChannelOption, Sendable { |
135 | | public typealias Value = RecvByteBufferAllocator |
136 | | |
137 | 0 | public init() {} |
138 | | } |
139 | | |
140 | | /// `AutoReadOption` allows users to configure if a `Channel` should automatically call `Channel.read` again once all data was read from the transport or |
141 | | /// if the user is responsible to call `Channel.read` manually. |
142 | | public struct AutoReadOption: ChannelOption, Sendable { |
143 | | public typealias Value = Bool |
144 | | |
145 | 0 | public init() {} |
146 | | } |
147 | | |
148 | | /// `WriteSpinOption` allows users to configure the number of repetitions of a only partially successful write call before considering the `Channel` not writable. |
149 | | /// Setting this option to `0` means that we only issue one write call and if that call does not write all the bytes, |
150 | | /// we consider the `Channel` not writable. |
151 | | public struct WriteSpinOption: ChannelOption, Sendable { |
152 | | public typealias Value = UInt |
153 | | |
154 | 0 | public init() {} |
155 | | } |
156 | | |
157 | | /// `MaxMessagesPerReadOption` allows users to configure the maximum number of read calls to the underlying transport are performed before wait again until |
158 | | /// there is more to read and be notified. |
159 | | public struct MaxMessagesPerReadOption: ChannelOption, Sendable { |
160 | | public typealias Value = UInt |
161 | | |
162 | 0 | public init() {} |
163 | | } |
164 | | |
165 | | /// `BacklogOption` allows users to configure the `backlog` value as specified in `man 2 listen`. This is only useful for `ServerSocketChannel`s. |
166 | | public struct BacklogOption: ChannelOption, Sendable { |
167 | | public typealias Value = Int32 |
168 | | |
169 | 0 | public init() {} |
170 | | } |
171 | | |
172 | | /// `DatagramVectorReadMessageCountOption` allows users to configure the number of messages to attempt to read in a single syscall on a |
173 | | /// datagram `Channel`. |
174 | | /// |
175 | | /// Some datagram `Channel`s have extremely high datagram throughput. This can occur when the single datagram socket is encapsulating |
176 | | /// many logical "connections" (e.g. with QUIC) or when the datagram socket is simply serving an enormous number of consumers (e.g. |
177 | | /// with a public-facing DNS server). In this case the overhead of one syscall per datagram is profoundly limiting. Using this |
178 | | /// `ChannelOption` allows the `Channel` to read multiple datagrams at once. |
179 | | /// |
180 | | /// Note that simply increasing this number will not necessarily bring performance gains and may in fact cause data loss. Any increase |
181 | | /// to this should be matched by increasing the size of the buffers allocated by the `Channel` `RecvByteBufferAllocator` (as set by |
182 | | /// `ChannelOption.recvAllocator`) proportionally. For example, to receive 10 messages at a time, set the size of the buffers allocated |
183 | | /// by the `RecvByteBufferAllocator` to at least 10x the size of the maximum datagram size you wish to receive. |
184 | | /// |
185 | | /// Naturally, this option is only valid on datagram channels. |
186 | | /// |
187 | | /// This option only works on the following platforms: |
188 | | /// |
189 | | /// - Linux |
190 | | /// - FreeBSD |
191 | | /// - Android |
192 | | /// |
193 | | /// On all other platforms, setting it has no effect. |
194 | | /// |
195 | | /// Set this option to 0 to disable vector reads and to use serial reads instead. |
196 | | public struct DatagramVectorReadMessageCountOption: ChannelOption, Sendable { |
197 | | public typealias Value = Int |
198 | | |
199 | 0 | public init() {} |
200 | | } |
201 | | |
202 | | /// ``DatagramSegmentSize`` controls the `UDP_SEGMENT` socket option (sometimes reffered to as 'GSO') which allows for |
203 | | /// large writes to be sent via `sendmsg` and `sendmmsg` and segmented into separate datagrams by the kernel (or in some cases, the NIC). |
204 | | /// The size of segments the large write is split into is controlled by the value of this option (note that writes do not need to be a |
205 | | /// multiple of this option). |
206 | | /// |
207 | | /// This option is currently only supported on Linux (4.18 and newer). Support can be checked using ``System/supportsUDPSegmentationOffload``. |
208 | | /// |
209 | | /// Setting this option to zero disables segmentation offload. |
210 | | public struct DatagramSegmentSize: ChannelOption, Sendable { |
211 | | public typealias Value = CInt |
212 | 0 | public init() {} |
213 | | } |
214 | | |
215 | | /// ``DatagramReceiveOffload`` sets the `UDP_GRO` socket option which allows for datagrams to be accumulated |
216 | | /// by the kernel (or in some cases, the NIC) and reduces traversals in the kernel's networking layer. |
217 | | /// |
218 | | /// This option is currently only supported on Linux (5.10 and newer). Support can be checked |
219 | | /// using ``System/supportsUDPReceiveOffload``. |
220 | | /// |
221 | | /// - Note: users should ensure they use an appropriate receive buffer allocator when enabling this option. |
222 | | /// The default allocator for datagram channels uses fixed sized buffers of 2048 bytes. |
223 | | public struct DatagramReceiveOffload: ChannelOption, Sendable { |
224 | | public typealias Value = Bool |
225 | 0 | public init() {} |
226 | | } |
227 | | |
228 | | /// ``DatagramReceiveSegmentSize`` enables per-message GRO (Generic Receive Offload) segment size reporting. |
229 | | /// When enabled, the kernel will provide the original segment size via control messages for aggregated datagrams, |
230 | | /// which will be reported in `AddressedEnvelope.Metadata.segmentSize`. |
231 | | /// |
232 | | /// This option requires ``DatagramReceiveOffload`` to be enabled first and is only supported on Linux. |
233 | | /// Support can be checked using ``System/supportsUDPReceiveOffload``. |
234 | | /// |
235 | | /// - Note: This provides the receive-side complement to per-message GSO (``AddressedEnvelope/Metadata/segmentSize``). |
236 | | public struct DatagramReceiveSegmentSize: ChannelOption, Sendable { |
237 | | public typealias Value = Bool |
238 | 0 | public init() {} |
239 | | } |
240 | | |
241 | | /// When set to true IP level ECN information will be reported through `AddressedEnvelope.Metadata` |
242 | | public struct ExplicitCongestionNotificationsOption: ChannelOption, Sendable { |
243 | | public typealias Value = Bool |
244 | 0 | public init() {} |
245 | | } |
246 | | |
247 | | /// The watermark used to detect when `Channel.isWritable` returns `true` or `false`. |
248 | | public struct WriteBufferWaterMark: Sendable { |
249 | | /// The low mark setting for a `Channel`. |
250 | | /// |
251 | | /// When the amount of buffered bytes in the `Channel`s outbound buffer drops below this value the `Channel` will be |
252 | | /// marked as writable again (after it was non-writable). |
253 | | public let low: Int |
254 | | |
255 | | /// The high mark setting for a `Channel`. |
256 | | /// |
257 | | /// When the amount of buffered bytes in the `Channel`s outbound exceeds this value the `Channel` will be |
258 | | /// marked as non-writable. It will be marked as writable again once the amount of buffered bytes drops below `low`. |
259 | | public let high: Int |
260 | | |
261 | | /// Create a new instance. |
262 | | /// |
263 | | /// Valid initialization is restricted to `1 <= low <= high`. |
264 | | /// |
265 | | /// - Parameters: |
266 | | /// - low: The low watermark. |
267 | | /// - high: The high watermark. |
268 | 0 | public init(low: Int, high: Int) { |
269 | 0 | precondition(low >= 1, "low must be >= 1 but was \(low)") |
270 | 0 | precondition(high >= low, "low must be <= high, but was low: \(low) high: \(high)") |
271 | 0 | self.low = low |
272 | 0 | self.high = high |
273 | 0 | } |
274 | | } |
275 | | |
276 | | /// `WriteBufferWaterMarkOption` allows users to configure when a `Channel` should be marked as writable or not. Once the amount of bytes queued in a |
277 | | /// `Channel`s outbound buffer is larger than `WriteBufferWaterMark.high` the channel will be marked as non-writable and so |
278 | | /// `Channel.isWritable` will return `false`. Once we were able to write some data out of the outbound buffer and the amount of bytes queued |
279 | | /// falls below `WriteBufferWaterMark.low` the `Channel` will become writable again. Once this happens `Channel.writable` will return |
280 | | /// `true` again. These writability changes are also propagated through the `ChannelPipeline` and so can be intercepted via `ChannelInboundHandler.channelWritabilityChanged`. |
281 | | public struct WriteBufferWaterMarkOption: ChannelOption, Sendable { |
282 | | public typealias Value = WriteBufferWaterMark |
283 | | |
284 | 0 | public init() {} |
285 | | } |
286 | | |
287 | | /// `ConnectTimeoutOption` allows users to configure the `TimeAmount` after which a connect will fail if it was not established in the meantime. May be |
288 | | /// `nil`, in which case the connection attempt will never time out. |
289 | | public struct ConnectTimeoutOption: ChannelOption, Sendable { |
290 | | public typealias Value = TimeAmount? |
291 | | |
292 | 0 | public init() {} |
293 | | } |
294 | | |
295 | | /// `AllowRemoteHalfClosureOption` allows users to configure whether the `Channel` will close itself when its remote |
296 | | /// peer shuts down its send stream, or whether it will remain open. If set to `false` (the default), the `Channel` |
297 | | /// will be closed automatically if the remote peer shuts down its send stream. If set to true, the `Channel` will |
298 | | /// not be closed: instead, a `ChannelEvent.inboundClosed` user event will be sent on the `ChannelPipeline`, |
299 | | /// and no more data will be received. |
300 | | public struct AllowRemoteHalfClosureOption: ChannelOption, Sendable { |
301 | | public typealias Value = Bool |
302 | | |
303 | 0 | public init() {} |
304 | | } |
305 | | |
306 | | /// When set to true IP level Packet Info information will be reported through `AddressedEnvelope.Metadata` for UDP packets. |
307 | | public struct ReceivePacketInfo: ChannelOption, Sendable { |
308 | | public typealias Value = Bool |
309 | 0 | public init() {} |
310 | | } |
311 | | |
312 | | /// `BufferedWritableBytesOption` allows users to know the number of writable bytes currently buffered in the `Channel`. |
313 | | public struct BufferedWritableBytesOption: ChannelOption, Sendable { |
314 | | public typealias Value = Int |
315 | | |
316 | 0 | public init() {} |
317 | | } |
318 | | } |
319 | | } |
320 | | |
321 | | /// Provides `ChannelOption`s to be used with a `Channel`, `Bootstrap` or `ServerBootstrap`. |
322 | | public struct ChannelOptions: Sendable { |
323 | | // Unavailable on Windows; see `SocketOption.init(level:name:)`. Use the |
324 | | // typed `socketOption(_:)`/`ipOption(_:)`/`tcpOption(_:)` accessors instead. |
325 | | #if !os(Windows) |
326 | 0 | public static let socket: @Sendable (SocketOptionLevel, SocketOptionName) -> ChannelOptions.Types.SocketOption = { |
327 | 0 | (level: SocketOptionLevel, name: SocketOptionName) -> Types.SocketOption in |
328 | 0 | .init(level: NIOBSDSocket.OptionLevel(rawValue: CInt(level)), name: NIOBSDSocket.Option(rawValue: CInt(name))) |
329 | 0 | } |
330 | | #endif |
331 | | |
332 | | /// - seealso: `SocketOption`. |
333 | 0 | public static let socketOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = { |
334 | 0 | (name: NIOBSDSocket.Option) -> Types.SocketOption in |
335 | 0 | .init(level: .socket, name: name) |
336 | 0 | } |
337 | | |
338 | | /// - seealso: `SocketOption`. |
339 | 0 | public static let ipOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = { |
340 | 0 | (name: NIOBSDSocket.Option) -> Types.SocketOption in |
341 | 0 | .init(level: .ip, name: name) |
342 | 0 | } |
343 | | |
344 | | /// - seealso: `SocketOption`. |
345 | 0 | public static let tcpOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = { |
346 | 0 | (name: NIOBSDSocket.Option) -> Types.SocketOption in |
347 | 0 | .init(level: .tcp, name: name) |
348 | 0 | } |
349 | | |
350 | | /// - seealso: `AllocatorOption`. |
351 | | public static let allocator = Types.AllocatorOption() |
352 | | |
353 | | /// - seealso: `RecvAllocatorOption`. |
354 | | public static let recvAllocator = Types.RecvAllocatorOption() |
355 | | |
356 | | /// - seealso: `AutoReadOption`. |
357 | | public static let autoRead = Types.AutoReadOption() |
358 | | |
359 | | /// - seealso: `MaxMessagesPerReadOption`. |
360 | | public static let maxMessagesPerRead = Types.MaxMessagesPerReadOption() |
361 | | |
362 | | /// - seealso: `BacklogOption`. |
363 | | public static let backlog = Types.BacklogOption() |
364 | | |
365 | | /// - seealso: `WriteSpinOption`. |
366 | | public static let writeSpin = Types.WriteSpinOption() |
367 | | |
368 | | /// - seealso: `WriteBufferWaterMarkOption`. |
369 | | public static let writeBufferWaterMark = Types.WriteBufferWaterMarkOption() |
370 | | |
371 | | /// - seealso: `ConnectTimeoutOption`. |
372 | | public static let connectTimeout = Types.ConnectTimeoutOption() |
373 | | |
374 | | /// - seealso: `AllowRemoteHalfClosureOption`. |
375 | | public static let allowRemoteHalfClosure = Types.AllowRemoteHalfClosureOption() |
376 | | |
377 | | /// - seealso: `DatagramVectorReadMessageCountOption` |
378 | | public static let datagramVectorReadMessageCount = Types.DatagramVectorReadMessageCountOption() |
379 | | |
380 | | /// - seealso: `DatagramSegmentSize` |
381 | | public static let datagramSegmentSize = Types.DatagramSegmentSize() |
382 | | |
383 | | /// - seealso: `DatagramReceiveOffload` |
384 | | public static let datagramReceiveOffload = Types.DatagramReceiveOffload() |
385 | | |
386 | | /// - seealso: `DatagramReceiveSegmentSize` |
387 | | public static let datagramReceiveSegmentSize = Types.DatagramReceiveSegmentSize() |
388 | | |
389 | | /// - seealso: `ExplicitCongestionNotificationsOption` |
390 | | public static let explicitCongestionNotification = Types.ExplicitCongestionNotificationsOption() |
391 | | |
392 | | /// - seealso: `ReceivePacketInfo` |
393 | | public static let receivePacketInfo = Types.ReceivePacketInfo() |
394 | | |
395 | | /// - seealso: `BufferedWritableBytesOption` |
396 | | public static let bufferedWritableBytes = Types.BufferedWritableBytesOption() |
397 | | } |
398 | | |
399 | | /// - seealso: `SocketOption`. |
400 | | extension ChannelOption where Self == ChannelOptions.Types.SocketOption { |
401 | | // Unavailable on Windows; see `SocketOption.init(level:name:)`. Use the |
402 | | // typed `socketOption(_:)`/`ipOption(_:)`/`tcpOption(_:)` accessors instead. |
403 | | #if !(os(Windows)) |
404 | 0 | public static func socket(_ level: SocketOptionLevel, _ name: SocketOptionName) -> Self { |
405 | 0 | .init(level: NIOBSDSocket.OptionLevel(rawValue: CInt(level)), name: NIOBSDSocket.Option(rawValue: CInt(name))) |
406 | 0 | } |
407 | | #endif |
408 | | |
409 | 0 | public static func socketOption(_ name: NIOBSDSocket.Option) -> Self { |
410 | 0 | .init(level: .socket, name: name) |
411 | 0 | } |
412 | | |
413 | 0 | public static func ipOption(_ name: NIOBSDSocket.Option) -> Self { |
414 | 0 | .init(level: .ip, name: name) |
415 | 0 | } |
416 | | |
417 | 0 | public static func tcpOption(_ name: NIOBSDSocket.Option) -> Self { |
418 | 0 | .init(level: .tcp, name: name) |
419 | 0 | } |
420 | | } |
421 | | |
422 | | /// - seealso: `AllocatorOption`. |
423 | | extension ChannelOption where Self == ChannelOptions.Types.AllocatorOption { |
424 | 0 | public static var allocator: Self { .init() } |
425 | | } |
426 | | |
427 | | /// - seealso: `RecvAllocatorOption`. |
428 | | extension ChannelOption where Self == ChannelOptions.Types.RecvAllocatorOption { |
429 | 0 | public static var recvAllocator: Self { .init() } |
430 | | } |
431 | | |
432 | | /// - seealso: `AutoReadOption`. |
433 | | extension ChannelOption where Self == ChannelOptions.Types.AutoReadOption { |
434 | 0 | public static var autoRead: Self { .init() } |
435 | | } |
436 | | |
437 | | /// - seealso: `MaxMessagesPerReadOption`. |
438 | | extension ChannelOption where Self == ChannelOptions.Types.MaxMessagesPerReadOption { |
439 | 0 | public static var maxMessagesPerRead: Self { .init() } |
440 | | } |
441 | | |
442 | | /// - seealso: `BacklogOption`. |
443 | | extension ChannelOption where Self == ChannelOptions.Types.BacklogOption { |
444 | 0 | public static var backlog: Self { .init() } |
445 | | } |
446 | | |
447 | | /// - seealso: `WriteSpinOption`. |
448 | | extension ChannelOption where Self == ChannelOptions.Types.WriteSpinOption { |
449 | 0 | public static var writeSpin: Self { .init() } |
450 | | } |
451 | | |
452 | | /// - seealso: `WriteBufferWaterMarkOption`. |
453 | | extension ChannelOption where Self == ChannelOptions.Types.WriteBufferWaterMarkOption { |
454 | 0 | public static var writeBufferWaterMark: Self { .init() } |
455 | | } |
456 | | |
457 | | /// - seealso: `ConnectTimeoutOption`. |
458 | | extension ChannelOption where Self == ChannelOptions.Types.ConnectTimeoutOption { |
459 | 0 | public static var connectTimeout: Self { .init() } |
460 | | } |
461 | | |
462 | | /// - seealso: `AllowRemoteHalfClosureOption`. |
463 | | extension ChannelOption where Self == ChannelOptions.Types.AllowRemoteHalfClosureOption { |
464 | 0 | public static var allowRemoteHalfClosure: Self { .init() } |
465 | | } |
466 | | |
467 | | /// - seealso: `DatagramVectorReadMessageCountOption`. |
468 | | extension ChannelOption where Self == ChannelOptions.Types.DatagramVectorReadMessageCountOption { |
469 | 0 | public static var datagramVectorReadMessageCount: Self { .init() } |
470 | | } |
471 | | |
472 | | /// - seealso: `DatagramSegmentSize`. |
473 | | extension ChannelOption where Self == ChannelOptions.Types.DatagramSegmentSize { |
474 | 0 | public static var datagramSegmentSize: Self { .init() } |
475 | | } |
476 | | |
477 | | /// - seealso: `DatagramReceiveOffload`. |
478 | | extension ChannelOption where Self == ChannelOptions.Types.DatagramReceiveOffload { |
479 | 0 | public static var datagramReceiveOffload: Self { .init() } |
480 | | } |
481 | | |
482 | | /// - seealso: `DatagramReceiveSegmentSize`. |
483 | | extension ChannelOption where Self == ChannelOptions.Types.DatagramReceiveSegmentSize { |
484 | 0 | public static var datagramReceiveSegmentSize: Self { .init() } |
485 | | } |
486 | | |
487 | | /// - seealso: `ExplicitCongestionNotificationsOption`. |
488 | | extension ChannelOption where Self == ChannelOptions.Types.ExplicitCongestionNotificationsOption { |
489 | 0 | public static var explicitCongestionNotification: Self { .init() } |
490 | | } |
491 | | |
492 | | /// - seealso: `ReceivePacketInfo`. |
493 | | extension ChannelOption where Self == ChannelOptions.Types.ReceivePacketInfo { |
494 | 0 | public static var receivePacketInfo: Self { .init() } |
495 | | } |
496 | | |
497 | | /// - seealso: `BufferedWritableBytesOption` |
498 | | extension ChannelOption where Self == ChannelOptions.Types.BufferedWritableBytesOption { |
499 | 0 | public static var bufferedWritableBytes: Self { .init() } |
500 | | } |
501 | | |
502 | | extension ChannelOptions { |
503 | | /// A type-safe storage facility for `ChannelOption`s. You will only ever need this if you implement your own |
504 | | /// `Channel` that needs to store `ChannelOption`s. |
505 | | public struct Storage: Sendable { |
506 | | @usableFromInline |
507 | | internal var _storage: |
508 | | [( |
509 | | any ChannelOption, |
510 | | (any Sendable, @Sendable (Channel) -> (any ChannelOption, any Sendable) -> EventLoopFuture<Void>) |
511 | | )] |
512 | | |
513 | 0 | public init() { |
514 | 0 | self._storage = [] |
515 | 0 | self._storage.reserveCapacity(2) |
516 | 0 | } |
517 | | |
518 | | /// Add `Options`, a `ChannelOption` to the `ChannelOptions.Storage`. |
519 | | /// |
520 | | /// - Parameters: |
521 | | /// - newKey: the key for the option |
522 | | /// - newValue: the value for the option |
523 | | @inlinable |
524 | 0 | public mutating func append<Option: ChannelOption>(key newKey: Option, value newValue: Option.Value) { |
525 | 0 | @Sendable |
526 | 0 | func applier(_ t: Channel) -> (any ChannelOption, any Sendable) -> EventLoopFuture<Void> { |
527 | 0 | { (option, value) in |
528 | 0 | t.setOption(option as! Option, value: value as! Option.Value) |
529 | 0 | } |
530 | 0 | } |
531 | 0 | var hasSet = false |
532 | 0 | self._storage = self._storage.map { currentKeyAndValue in |
533 | 0 | let (currentKey, _) = currentKeyAndValue |
534 | 0 | if let currentKey = currentKey as? Option, currentKey == newKey { |
535 | 0 | hasSet = true |
536 | 0 | return (currentKey, (newValue, applier)) |
537 | 0 | } else { |
538 | 0 | return currentKeyAndValue |
539 | 0 | } |
540 | 0 | } |
541 | 0 | if !hasSet { |
542 | 0 | self._storage.append((newKey, (newValue, applier))) |
543 | 0 | } |
544 | 0 | } |
545 | | |
546 | | /// Apply all stored `ChannelOption`s to `Channel`. |
547 | | /// |
548 | | /// - Parameters: |
549 | | /// - channel: The `Channel` to apply the `ChannelOption`s to |
550 | | /// - Returns: |
551 | | /// - An `EventLoopFuture` that is fulfilled when all `ChannelOption`s have been applied to the `Channel`. |
552 | 0 | public func applyAllChannelOptions(to channel: Channel) -> EventLoopFuture<Void> { |
553 | 0 | let applyPromise = channel.eventLoop.makePromise(of: Void.self) |
554 | 0 | let it = self._storage.makeIterator() |
555 | 0 |
|
556 | 0 | @Sendable |
557 | 0 | func applyNext( |
558 | 0 | iterator: IndexingIterator< |
559 | 0 | [( |
560 | 0 | any ChannelOption, |
561 | 0 | ( |
562 | 0 | any Sendable, |
563 | 0 | @Sendable (any Channel) -> (any ChannelOption, any Sendable) -> EventLoopFuture<Void> |
564 | 0 | ) |
565 | 0 | )] |
566 | 0 | > |
567 | 0 | ) { |
568 | 0 | var iterator = iterator |
569 | 0 | guard let (key, (value, applier)) = iterator.next() else { |
570 | 0 | // If we reached the end, everything is applied. |
571 | 0 | applyPromise.succeed(()) |
572 | 0 | return |
573 | 0 | } |
574 | 0 | let it = iterator |
575 | 0 |
|
576 | 0 | applier(channel)(key, value).map { |
577 | 0 | applyNext( |
578 | 0 | iterator: it |
579 | 0 | ) |
580 | 0 | }.cascadeFailure(to: applyPromise) |
581 | 0 | } |
582 | 0 | applyNext(iterator: it) |
583 | 0 |
|
584 | 0 | return applyPromise.futureResult |
585 | 0 | } |
586 | | |
587 | | /// Remove all options with the given `key`. |
588 | | /// |
589 | | /// Calling this function has the effect of removing all instances of a ``ChannelOption`` |
590 | | /// from the ``ChannelOptions/Storage``, as if none had been added. This is useful in rare |
591 | | /// cases where a bootstrap knows that some configuration must purge options of a certain kind. |
592 | | /// |
593 | | /// - Parameters: |
594 | | /// - key: The ``ChannelOption`` to remove. |
595 | 0 | public mutating func remove<Option: ChannelOption>(key: Option) { |
596 | 0 | self._storage.removeAll(where: { existingKey, _ in |
597 | 0 | (existingKey as? Option) == key |
598 | 0 | }) |
599 | 0 | } |
600 | | } |
601 | | } |