Coverage Report

Created: 2026-06-01 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-nio/Sources/NIOPosix/Bootstrap.swift
Line
Count
Source
1
//===----------------------------------------------------------------------===//
2
//
3
// This source file is part of the SwiftNIO open source project
4
//
5
// Copyright (c) 2017-2024 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
#if !os(WASI)
16
17
import CNIOLinux
18
import CNIOOpenBSD
19
import NIOCore
20
21
#if os(Windows)
22
import ucrt
23
24
import func WinSDK.GetFileType
25
26
import let WinSDK.FILE_TYPE_PIPE
27
import let WinSDK.INVALID_HANDLE_VALUE
28
29
import struct WinSDK.DWORD
30
import struct WinSDK.HANDLE
31
#endif
32
33
/// The type of all `channelInitializer` callbacks.
34
internal typealias ChannelInitializerCallback = @Sendable (Channel) -> EventLoopFuture<Void>
35
36
/// Common functionality for all NIO on sockets bootstraps.
37
internal enum NIOOnSocketsBootstraps {
38
0
    internal static func isCompatible(group: EventLoopGroup) -> Bool {
39
0
        group is SelectableEventLoop || group is MultiThreadedEventLoopGroup
40
0
    }
41
}
42
43
/// A `ServerBootstrap` is an easy way to bootstrap a `ServerSocketChannel` when creating network servers.
44
///
45
/// Example:
46
///
47
/// ```swift
48
///     let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
49
///     defer {
50
///         try! group.syncShutdownGracefully()
51
///     }
52
///     let bootstrap = ServerBootstrap(group: group)
53
///         // Specify backlog and enable SO_REUSEADDR for the server itself
54
///         .serverChannelOption(.backlog, value: 256)
55
///         .serverChannelOption(.socketOption(.so_reuseaddr), value: 1)
56
///
57
///         // Set the handlers that are applied to the accepted child `Channel`s.
58
///         .childChannelInitializer { channel in
59
///            channel.eventLoop.makeCompletedFuture {
60
///                // Ensure we don't read faster than we can write by adding the BackPressureHandler into the pipeline.
61
///                try channel.pipeline.syncOperations.addHandler(BackPressureHandler())
62
///                try channel.pipeline.syncOperations.addHandler(MyChannelHandler())
63
///            }
64
///         }
65
///
66
///         // Enable SO_REUSEADDR for the accepted Channels
67
///         .childChannelOption(.socketOption(.so_reuseaddr), value: 1)
68
///         .childChannelOption(.maxMessagesPerRead, value: 16)
69
///         .childChannelOption(.recvAllocator, value: AdaptiveRecvByteBufferAllocator())
70
///     let channel = try! bootstrap.bind(host: host, port: port).wait()
71
///     /* the server will now be accepting connections */
72
///
73
///     try! channel.closeFuture.wait() // wait forever as we never close the Channel
74
/// ```
75
///
76
/// The `EventLoopFuture` returned by `bind` will fire with a `ServerSocketChannel`. This is the channel that owns the listening socket.
77
/// Each time it accepts a new connection it will fire a `SocketChannel` through the `ChannelPipeline` via `fireChannelRead`: as a result,
78
/// the `ServerSocketChannel` operates on `Channel`s as inbound messages. Outbound messages are not supported on a `ServerSocketChannel`
79
/// which means that each write attempt will fail.
80
///
81
/// Accepted `SocketChannel`s operate on `ByteBuffer` as inbound data, and `IOData` as outbound data.
82
public final class ServerBootstrap {
83
84
    private let group: EventLoopGroup
85
    private let childGroup: EventLoopGroup
86
    private var serverChannelInit: Optional<ChannelInitializerCallback>
87
    private var childChannelInit: Optional<ChannelInitializerCallback>
88
    @usableFromInline
89
    internal var _serverChannelOptions: ChannelOptions.Storage
90
    @usableFromInline
91
    internal var _childChannelOptions: ChannelOptions.Storage
92
    private var enableMPTCP: Bool
93
94
    /// Create a `ServerBootstrap` on the `EventLoopGroup` `group`.
95
    ///
96
    /// The `EventLoopGroup` `group` must be compatible, otherwise the program will crash. `ServerBootstrap` is
97
    /// compatible only with `MultiThreadedEventLoopGroup` as well as the `EventLoop`s returned by
98
    /// `MultiThreadedEventLoopGroup.next`. See `init(validatingGroup:childGroup:)` for a fallible initializer for
99
    /// situations where it's impossible to tell ahead of time if the `EventLoopGroup`s are compatible or not.
100
    ///
101
    /// - Parameters:
102
    ///   - group: The `EventLoopGroup` to use for the `bind` of the `ServerSocketChannel` and to accept new `SocketChannel`s with.
103
0
    public convenience init(group: EventLoopGroup) {
104
0
        guard NIOOnSocketsBootstraps.isCompatible(group: group) else {
105
0
            preconditionFailure(
106
0
                "ServerBootstrap is only compatible with MultiThreadedEventLoopGroup and "
107
0
                    + "SelectableEventLoop. You tried constructing one with \(group) which is incompatible."
108
0
            )
109
0
        }
110
0
        self.init(validatingGroup: group, childGroup: group)!
111
0
    }
112
113
    /// Create a `ServerBootstrap` on the `EventLoopGroup` `group` which accepts `Channel`s on `childGroup`.
114
    ///
115
    /// The `EventLoopGroup`s `group` and `childGroup` must be compatible, otherwise the program will crash.
116
    /// `ServerBootstrap` is compatible only with `MultiThreadedEventLoopGroup` as well as the `EventLoop`s returned by
117
    /// `MultiThreadedEventLoopGroup.next`. See `init(validatingGroup:childGroup:)` for a fallible initializer for
118
    /// situations where it's impossible to tell ahead of time if the `EventLoopGroup`s are compatible or not.
119
    ///
120
    /// - Parameters:
121
    ///   - group: The `EventLoopGroup` to use for the `bind` of the `ServerSocketChannel` and to accept new `SocketChannel`s with.
122
    ///   - childGroup: The `EventLoopGroup` to run the accepted `SocketChannel`s on.
123
0
    public convenience init(group: EventLoopGroup, childGroup: EventLoopGroup) {
124
0
        guard
125
0
            NIOOnSocketsBootstraps.isCompatible(group: group) && NIOOnSocketsBootstraps.isCompatible(group: childGroup)
126
0
        else {
127
0
            preconditionFailure(
128
0
                "ServerBootstrap is only compatible with MultiThreadedEventLoopGroup and "
129
0
                    + "SelectableEventLoop. You tried constructing one with group: \(group) and "
130
0
                    + "childGroup: \(childGroup) at least one of which is incompatible."
131
0
            )
132
0
        }
133
0
        self.init(validatingGroup: group, childGroup: childGroup)!
134
0
135
0
    }
136
137
    /// Create a `ServerBootstrap` on the `EventLoopGroup` `group` which accepts `Channel`s on `childGroup`, validating
138
    /// that the `EventLoopGroup`s are compatible with `ServerBootstrap`.
139
    ///
140
    /// - Parameters:
141
    ///   - group: The `EventLoopGroup` to use for the `bind` of the `ServerSocketChannel` and to accept new `SocketChannel`s with.
142
    ///   - childGroup: The `EventLoopGroup` to run the accepted `SocketChannel`s on. If `nil`, `group` is used.
143
0
    public init?(validatingGroup group: EventLoopGroup, childGroup: EventLoopGroup? = nil) {
144
0
        let childGroup = childGroup ?? group
145
0
        guard
146
0
            NIOOnSocketsBootstraps.isCompatible(group: group) && NIOOnSocketsBootstraps.isCompatible(group: childGroup)
147
0
        else {
148
0
            return nil
149
0
        }
150
0
151
0
        self.group = group
152
0
        self.childGroup = childGroup
153
0
        self._serverChannelOptions = ChannelOptions.Storage()
154
0
        self._childChannelOptions = ChannelOptions.Storage()
155
0
        self.serverChannelInit = nil
156
0
        self.childChannelInit = nil
157
0
        self._serverChannelOptions.append(key: .tcpOption(.tcp_nodelay), value: 1)
158
0
        self.enableMPTCP = false
159
0
    }
160
161
    /// Initialize the `ServerSocketChannel` with `initializer`. The most common task in initializer is to add
162
    /// `ChannelHandler`s to the `ChannelPipeline`.
163
    ///
164
    /// The `ServerSocketChannel` uses the accepted `Channel`s as inbound messages.
165
    ///
166
    /// - Note: To set the initializer for the accepted `SocketChannel`s, look at `ServerBootstrap.childChannelInitializer`.
167
    ///
168
    /// - Parameters:
169
    ///   - initializer: A closure that initializes the provided `Channel`.
170
    @preconcurrency
171
    public func serverChannelInitializer(_ initializer: @escaping @Sendable (Channel) -> EventLoopFuture<Void>) -> Self
172
0
    {
173
0
        self.serverChannelInit = initializer
174
0
        return self
175
0
    }
176
177
    /// Initialize the accepted `SocketChannel`s with `initializer`. The most common task in initializer is to add
178
    /// `ChannelHandler`s to the `ChannelPipeline`. Note that if the `initializer` fails then the error will be
179
    /// fired in the *parent* channel.
180
    ///
181
    /// - warning: The `initializer` will be invoked once for every accepted connection. Therefore it's usually the
182
    ///            right choice to instantiate stateful `ChannelHandler`s within the closure to make sure they are not
183
    ///            accidentally shared across `Channel`s. There are expert use-cases where stateful handler need to be
184
    ///            shared across `Channel`s in which case the user is responsible to synchronise the state access
185
    ///            appropriately.
186
    ///
187
    /// The accepted `Channel` will operate on `ByteBuffer` as inbound and `IOData` as outbound messages.
188
    ///
189
    /// - Parameters:
190
    ///   - initializer: A closure that initializes the provided `Channel`.
191
    @preconcurrency
192
0
    public func childChannelInitializer(_ initializer: @escaping @Sendable (Channel) -> EventLoopFuture<Void>) -> Self {
193
0
        self.childChannelInit = initializer
194
0
        return self
195
0
    }
196
197
    /// Specifies a `ChannelOption` to be applied to the `ServerSocketChannel`.
198
    ///
199
    /// - Note: To specify options for the accepted `SocketChannel`s, look at `ServerBootstrap.childChannelOption`.
200
    ///
201
    /// - Parameters:
202
    ///   - option: The option to be applied.
203
    ///   - value: The value for the option.
204
    @inlinable
205
0
    public func serverChannelOption<Option: ChannelOption>(_ option: Option, value: Option.Value) -> Self {
206
0
        self._serverChannelOptions.append(key: option, value: value)
207
0
        return self
208
0
    }
209
210
    /// Specifies a `ChannelOption` to be applied to the accepted `SocketChannel`s.
211
    ///
212
    /// - Parameters:
213
    ///   - option: The option to be applied.
214
    ///   - value: The value for the option.
215
    @inlinable
216
0
    public func childChannelOption<Option: ChannelOption>(_ option: Option, value: Option.Value) -> Self {
217
0
        self._childChannelOptions.append(key: option, value: value)
218
0
        return self
219
0
    }
220
221
    /// Specifies a timeout to apply to a bind attempt. Currently unsupported.
222
    ///
223
    /// - Parameters:
224
    ///   - timeout: The timeout that will apply to the bind attempt.
225
0
    public func bindTimeout(_ timeout: TimeAmount) -> Self {
226
0
        self
227
0
    }
228
229
    /// Enables multi-path TCP support.
230
    ///
231
    /// This option is only supported on some systems, and will lead to bind
232
    /// failing if the system does not support it. Users are recommended to
233
    /// only enable this in response to configuration or feature detection.
234
    ///
235
    /// > Note: Enabling this setting will re-enable Nagle's algorithm, even if it
236
    /// > had been disabled. This is a temporary workaround for a Linux kernel
237
    /// > limitation.
238
    ///
239
    /// - Parameters:
240
    ///   - value: Whether to enable MPTCP or not.
241
0
    public func enableMPTCP(_ value: Bool) -> Self {
242
0
        self.enableMPTCP = value
243
0
244
0
        // This is a temporary workaround until we get some stable Linux kernel
245
0
        // versions that support TCP_NODELAY and MPTCP.
246
0
        if value {
247
0
            self._serverChannelOptions.remove(key: .tcpOption(.tcp_nodelay))
248
0
        }
249
0
250
0
        return self
251
0
    }
252
253
    /// Bind the `ServerSocketChannel` to `host` and `port`.
254
    ///
255
    /// - Parameters:
256
    ///   - host: The host to bind on.
257
    ///   - port: The port to bind on.
258
0
    public func bind(host: String, port: Int) -> EventLoopFuture<Channel> {
259
0
        bind0 {
260
0
            try SocketAddress.makeAddressResolvingHost(host, port: port)
261
0
        }
262
0
    }
263
264
    /// Bind the `ServerSocketChannel` to `address`.
265
    ///
266
    /// - Parameters:
267
    ///   - address: The `SocketAddress` to bind on.
268
0
    public func bind(to address: SocketAddress) -> EventLoopFuture<Channel> {
269
0
        bind0 { address }
270
0
    }
271
272
    /// Bind the `ServerSocketChannel` to a UNIX Domain Socket.
273
    ///
274
    /// - Parameters:
275
    ///   - unixDomainSocketPath: The _Unix domain socket_ path to bind to. `unixDomainSocketPath` must not exist, it will be created by the system.
276
0
    public func bind(unixDomainSocketPath: String) -> EventLoopFuture<Channel> {
277
0
        bind0 {
278
0
            try SocketAddress(unixDomainSocketPath: unixDomainSocketPath)
279
0
        }
280
0
    }
281
282
    /// Bind the `ServerSocketChannel` to a UNIX Domain Socket.
283
    ///
284
    /// - Parameters:
285
    ///   - unixDomainSocketPath: The path of the UNIX Domain Socket to bind on. The`unixDomainSocketPath` must not exist,
286
    ///     unless `cleanupExistingSocketFile`is set to `true`.
287
    ///   - cleanupExistingSocketFile: Whether to cleanup an existing socket file at `unixDomainSocketPath`.
288
0
    public func bind(unixDomainSocketPath: String, cleanupExistingSocketFile: Bool) -> EventLoopFuture<Channel> {
289
0
        if cleanupExistingSocketFile {
290
0
            do {
291
0
                try BaseSocket.cleanupSocket(unixDomainSocketPath: unixDomainSocketPath)
292
0
            } catch {
293
0
                return group.next().makeFailedFuture(error)
294
0
            }
295
0
        }
296
0
297
0
        return self.bind(unixDomainSocketPath: unixDomainSocketPath)
298
0
    }
299
300
    /// Bind the `ServerSocketChannel` to a VSOCK socket.
301
    ///
302
    /// - Parameters:
303
    ///   - vsockAddress: The VSOCK socket address to bind on.
304
0
    public func bind(to vsockAddress: VsockAddress) -> EventLoopFuture<Channel> {
305
0
        func makeChannel(
306
0
            _ eventLoop: SelectableEventLoop,
307
0
            _ childEventLoopGroup: EventLoopGroup,
308
0
            _ enableMPTCP: Bool
309
0
        ) throws -> ServerSocketChannel {
310
0
            try ServerSocketChannel(
311
0
                eventLoop: eventLoop,
312
0
                group: childEventLoopGroup,
313
0
                protocolFamily: .vsock,
314
0
                enableMPTCP: enableMPTCP
315
0
            )
316
0
        }
317
0
        return bind0(makeServerChannel: makeChannel) { (eventLoop, serverChannel) in
318
0
            serverChannel.register().flatMap {
319
0
                let promise = eventLoop.makePromise(of: Void.self)
320
0
                serverChannel.triggerUserOutboundEvent0(
321
0
                    VsockChannelEvents.BindToAddress(vsockAddress),
322
0
                    promise: promise
323
0
                )
324
0
                return promise.futureResult
325
0
            }
326
0
        }
327
0
    }
328
329
    #if !os(Windows)
330
    /// Use the existing bound socket file descriptor.
331
    ///
332
    /// - Parameters:
333
    ///   - descriptor: The _Unix file descriptor_ representing the bound stream socket.
334
    @available(*, deprecated, renamed: "withBoundSocket(_:)")
335
0
    public func withBoundSocket(descriptor: CInt) -> EventLoopFuture<Channel> {
336
0
        withBoundSocket(descriptor)
337
0
    }
338
    #endif
339
340
    /// Use the existing bound socket file descriptor.
341
    ///
342
    /// - Parameters:
343
    ///   - socket: The _Unix file descriptor_ representing the bound stream socket.
344
0
    public func withBoundSocket(_ socket: NIOBSDSocket.Handle) -> EventLoopFuture<Channel> {
345
0
        func makeChannel(
346
0
            _ eventLoop: SelectableEventLoop,
347
0
            _ childEventLoopGroup: EventLoopGroup,
348
0
            _ enableMPTCP: Bool
349
0
        ) throws -> ServerSocketChannel {
350
0
            if enableMPTCP {
351
0
                throw ChannelError._operationUnsupported
352
0
            }
353
0
            return try ServerSocketChannel(socket: socket, eventLoop: eventLoop, group: childEventLoopGroup)
354
0
        }
355
0
        return bind0(makeServerChannel: makeChannel) { (eventLoop, serverChannel) in
356
0
            let promise = eventLoop.makePromise(of: Void.self)
357
0
            serverChannel.registerAlreadyConfigured0(promise: promise)
358
0
            return promise.futureResult
359
0
        }
360
0
    }
361
362
0
    private func bind0(_ makeSocketAddress: () throws -> SocketAddress) -> EventLoopFuture<Channel> {
363
0
        let address: SocketAddress
364
0
        do {
365
0
            address = try makeSocketAddress()
366
0
        } catch {
367
0
            return group.next().makeFailedFuture(error)
368
0
        }
369
0
        func makeChannel(
370
0
            _ eventLoop: SelectableEventLoop,
371
0
            _ childEventLoopGroup: EventLoopGroup,
372
0
            _ enableMPTCP: Bool
373
0
        ) throws -> ServerSocketChannel {
374
0
            try ServerSocketChannel(
375
0
                eventLoop: eventLoop,
376
0
                group: childEventLoopGroup,
377
0
                protocolFamily: address.protocol,
378
0
                enableMPTCP: enableMPTCP
379
0
            )
380
0
        }
381
0
382
0
        return bind0(makeServerChannel: makeChannel) { (eventLoop, serverChannel) in
383
0
            serverChannel.registerAndDoSynchronously { serverChannel in
384
0
                serverChannel.bind(to: address)
385
0
            }
386
0
        }
387
0
    }
388
389
    private func bind0(
390
        makeServerChannel: (_ eventLoop: SelectableEventLoop, _ childGroup: EventLoopGroup, _ enableMPTCP: Bool) throws
391
            -> ServerSocketChannel,
392
        _ register: @escaping @Sendable (EventLoop, ServerSocketChannel) -> EventLoopFuture<Void>
393
0
    ) -> EventLoopFuture<Channel> {
394
0
        let eventLoop = self.group.next()
395
0
        let childEventLoopGroup = self.childGroup
396
0
        let serverChannelOptions = self._serverChannelOptions
397
0
        let serverChannelInit = self.serverChannelInit ?? { _ in eventLoop.makeSucceededFuture(()) }
Unexecuted instantiation: $s8NIOPosix15ServerBootstrapC5bind033_F13D6D61D4A528D08BD67995505C2487LL04makeB7Channel_7NIOCore15EventLoopFutureCyAG0N0_pGAA0b6SocketN0CAA010SelectablepQ0C_AG0pQ5Group_pSbtKXE_AIyytGAG0pQ0_p_AMtYbctFAqgJ_pYbcyKXEfu_
Unexecuted instantiation: $s8NIOPosix15ServerBootstrapC5bind033_F13D6D61D4A528D08BD67995505C2487LL04makeB7Channel_7NIOCore15EventLoopFutureCyAG0N0_pGAA0b6SocketN0CAA010SelectablepQ0C_AG0pQ5Group_pSbtKXE_AIyytGAG0pQ0_p_AMtYbctFAqgJ_pYbcyKXEfu_AqgJ_pYbcfU_
398
0
        let childChannelInit = self.childChannelInit
399
0
        let childChannelOptions = self._childChannelOptions
400
0
401
0
        let serverChannel: ServerSocketChannel
402
0
        do {
403
0
            serverChannel = try makeServerChannel(
404
0
                eventLoop as! SelectableEventLoop,
405
0
                childEventLoopGroup,
406
0
                self.enableMPTCP
407
0
            )
408
0
        } catch {
409
0
            return eventLoop.makeFailedFuture(error)
410
0
        }
411
0
412
0
        return eventLoop.submit {
413
0
            serverChannelOptions.applyAllChannelOptions(to: serverChannel).flatMap {
414
0
                serverChannelInit(serverChannel)
415
0
            }.flatMap {
416
0
                do {
417
0
                    try serverChannel.pipeline.syncOperations.addHandler(
418
0
                        AcceptHandler(
419
0
                            childChannelInitializer: childChannelInit,
420
0
                            childChannelOptions: childChannelOptions
421
0
                        ),
422
0
                        name: "AcceptHandler"
423
0
                    )
424
0
                    return register(eventLoop, serverChannel)
425
0
                } catch {
426
0
                    return eventLoop.makeFailedFuture(error)
427
0
                }
428
0
            }.map {
429
0
                serverChannel as Channel
430
0
            }.flatMapError { error in
431
0
                serverChannel.close0(error: error, mode: .all, promise: nil)
432
0
                return eventLoop.makeFailedFuture(error)
433
0
            }
434
0
        }.flatMap {
435
0
            $0
436
0
        }
437
0
    }
438
439
    final class AcceptHandler: ChannelInboundHandler {
440
        public typealias InboundIn = SocketChannel
441
        public typealias InboundOut = SocketChannel
442
443
        private let childChannelInit: (@Sendable (Channel) -> EventLoopFuture<Void>)?
444
        private let childChannelOptions: ChannelOptions.Storage
445
446
        init(
447
            childChannelInitializer: (@Sendable (Channel) -> EventLoopFuture<Void>)?,
448
            childChannelOptions: ChannelOptions.Storage
449
0
        ) {
450
0
            self.childChannelInit = childChannelInitializer
451
0
            self.childChannelOptions = childChannelOptions
452
0
        }
453
454
0
        func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) {
455
0
            if event is ChannelShouldQuiesceEvent {
456
0
                let loopBoundContext = context.loopBound
457
0
                context.channel.close().whenFailure { error in
458
0
                    let context = loopBoundContext.value
459
0
                    context.fireErrorCaught(error)
460
0
                }
461
0
            }
462
0
            context.fireUserInboundEventTriggered(event)
463
0
        }
464
465
0
        func channelRead(context: ChannelHandlerContext, data: NIOAny) {
466
0
            let accepted = AcceptHandler.unwrapInboundIn(data)
467
0
            let ctxEventLoop = context.eventLoop
468
0
            let childEventLoop = accepted.eventLoop
469
0
            let childChannelInit = self.childChannelInit ?? { (_: Channel) in childEventLoop.makeSucceededFuture(()) }
Unexecuted instantiation: $s8NIOPosix15ServerBootstrapC13AcceptHandlerC11channelRead7context4datay7NIOCore07ChannelE7ContextC_AI6NIOAnyVtFAI15EventLoopFutureCyytGAI0K0_pYbcyKXEfu_
Unexecuted instantiation: $s8NIOPosix15ServerBootstrapC13AcceptHandlerC11channelRead7context4datay7NIOCore07ChannelE7ContextC_AI6NIOAnyVtFAI15EventLoopFutureCyytGAI0K0_pYbcyKXEfu_ApiQ_pYbcfU_
470
0
            let childChannelOptions = self.childChannelOptions
471
0
472
0
            @inline(__always)
473
0
            @Sendable
474
0
            func setupChildChannel() -> EventLoopFuture<Void> {
475
0
                childChannelOptions.applyAllChannelOptions(to: accepted).flatMap { () -> EventLoopFuture<Void> in
476
0
                    childEventLoop.assertInEventLoop()
477
0
                    return childChannelInit(accepted)
478
0
                }
479
0
            }
480
0
481
0
            @inline(__always)
482
0
            func fireThroughPipeline(_ future: EventLoopFuture<Void>, context: ChannelHandlerContext) {
483
0
                // Strictly these asserts are redundant with future.assumeIsolated(), but as this code
484
0
                // has guarantees that can be quite hard to follow we keep them here.
485
0
                ctxEventLoop.assertInEventLoop()
486
0
                assert(ctxEventLoop === context.eventLoop)
487
0
                future.assumeIsolated().flatMap { (_) -> EventLoopFuture<Void> in
488
0
                    guard context.channel.isActive else {
489
0
                        return ctxEventLoop.makeFailedFuture(ChannelError._ioOnClosedChannel)
490
0
                    }
491
0
                    context.fireChannelRead(AcceptHandler.wrapInboundOut(accepted))
492
0
                    return context.eventLoop.makeSucceededFuture(())
493
0
                }.whenFailure { error in
494
0
                    self.closeAndFire(context: context, accepted: accepted, err: error)
495
0
                }
496
0
            }
497
0
498
0
            if childEventLoop === ctxEventLoop {
499
0
                fireThroughPipeline(setupChildChannel(), context: context)
500
0
            } else {
501
0
                fireThroughPipeline(
502
0
                    childEventLoop.flatSubmit {
503
0
                        setupChildChannel()
504
0
                    }.hop(to: ctxEventLoop),
505
0
                    context: context
506
0
                )
507
0
            }
508
0
        }
509
510
0
        private func closeAndFire(context: ChannelHandlerContext, accepted: SocketChannel, err: Error) {
511
0
            accepted.close(promise: nil)
512
0
            if context.eventLoop.inEventLoop {
513
0
                context.fireErrorCaught(err)
514
0
            } else {
515
0
                let loopBoundContext = context.loopBound
516
0
                context.eventLoop.execute {
517
0
                    let context = loopBoundContext.value
518
0
                    context.fireErrorCaught(err)
519
0
                }
520
0
            }
521
0
        }
522
    }
523
}
524
525
// MARK: Async bind methods
526
527
extension ServerBootstrap {
528
    /// Bind the `ServerSocketChannel` to the `host` and `port` parameters.
529
    ///
530
    /// - Parameters:
531
    ///   - host: The host to bind on.
532
    ///   - port: The port to bind on.
533
    ///   - serverBackPressureStrategy: The back pressure strategy used by the server socket channel.
534
    ///   - childChannelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
535
    ///   method.
536
    /// - Returns: The result of the channel initializer.
537
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
538
    public func bind<Output: Sendable>(
539
        host: String,
540
        port: Int,
541
        serverBackPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark? = nil,
542
        childChannelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
543
0
    ) async throws -> NIOAsyncChannel<Output, Never> {
544
0
        let address = try SocketAddress.makeAddressResolvingHost(host, port: port)
545
0
546
0
        return try await bind(
547
0
            to: address,
548
0
            serverBackPressureStrategy: serverBackPressureStrategy,
549
0
            childChannelInitializer: childChannelInitializer
550
0
        )
551
0
    }
552
553
    /// Bind the `ServerSocketChannel` to the `address` parameter.
554
    ///
555
    /// - Parameters:
556
    ///   - address: The `SocketAddress` to bind on.
557
    ///   - serverBackPressureStrategy: The back pressure strategy used by the server socket channel.
558
    ///   - childChannelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
559
    ///   method.
560
    /// - Returns: The result of the channel initializer.
561
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
562
    public func bind<Output: Sendable>(
563
        to address: SocketAddress,
564
        serverBackPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark? = nil,
565
        childChannelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
566
0
    ) async throws -> NIOAsyncChannel<Output, Never> {
567
0
        try await bind0(
568
0
            makeServerChannel: { eventLoop, childEventLoopGroup, enableMPTCP in
569
0
                try ServerSocketChannel(
570
0
                    eventLoop: eventLoop,
571
0
                    group: childEventLoopGroup,
572
0
                    protocolFamily: address.protocol,
573
0
                    enableMPTCP: enableMPTCP
574
0
                )
575
0
            },
576
0
            serverBackPressureStrategy: serverBackPressureStrategy,
577
0
            childChannelInitializer: childChannelInitializer,
578
0
            registration: { serverChannel in
579
0
                serverChannel.registerAndDoSynchronously { serverChannel in
580
0
                    serverChannel.bind(to: address)
581
0
                }
582
0
            }
583
0
        ).get()
584
0
    }
585
586
    /// Bind the `ServerSocketChannel` to a UNIX Domain Socket.
587
    ///
588
    /// - Parameters:
589
    ///   - unixDomainSocketPath: The path of the UNIX Domain Socket to bind on. The`unixDomainSocketPath` must not exist,
590
    ///     unless `cleanupExistingSocketFile`is set to `true`.
591
    ///   - cleanupExistingSocketFile: Whether to cleanup an existing socket file at `unixDomainSocketPath`.
592
    ///   - serverBackPressureStrategy: The back pressure strategy used by the server socket channel.
593
    ///   - childChannelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
594
    ///   method.
595
    /// - Returns: The result of the channel initializer.
596
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
597
    public func bind<Output: Sendable>(
598
        unixDomainSocketPath: String,
599
        cleanupExistingSocketFile: Bool = false,
600
        serverBackPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark? = nil,
601
        childChannelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
602
0
    ) async throws -> NIOAsyncChannel<Output, Never> {
603
0
        if cleanupExistingSocketFile {
604
0
            try BaseSocket.cleanupSocket(unixDomainSocketPath: unixDomainSocketPath)
605
0
        }
606
0
607
0
        let address = try SocketAddress(unixDomainSocketPath: unixDomainSocketPath)
608
0
609
0
        return try await self.bind(
610
0
            to: address,
611
0
            serverBackPressureStrategy: serverBackPressureStrategy,
612
0
            childChannelInitializer: childChannelInitializer
613
0
        )
614
0
    }
615
616
    /// Bind the `ServerSocketChannel` to a VSOCK socket.
617
    ///
618
    /// - Parameters:
619
    ///   - vsockAddress: The VSOCK socket address to bind on.
620
    ///   - serverBackPressureStrategy: The back pressure strategy used by the server socket channel.
621
    ///   - childChannelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
622
    ///   method.
623
    /// - Returns: The result of the channel initializer.
624
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
625
    public func bind<Output: Sendable>(
626
        to vsockAddress: VsockAddress,
627
        serverBackPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark? = nil,
628
        childChannelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
629
0
    ) async throws -> NIOAsyncChannel<Output, Never> {
630
0
        func makeChannel(
631
0
            _ eventLoop: SelectableEventLoop,
632
0
            _ childEventLoopGroup: EventLoopGroup,
633
0
            _ enableMPTCP: Bool
634
0
        ) throws -> ServerSocketChannel {
635
0
            try ServerSocketChannel(
636
0
                eventLoop: eventLoop,
637
0
                group: childEventLoopGroup,
638
0
                protocolFamily: .vsock,
639
0
                enableMPTCP: enableMPTCP
640
0
            )
641
0
        }
642
0
643
0
        return try await self.bind0(
644
0
            makeServerChannel: makeChannel,
645
0
            serverBackPressureStrategy: serverBackPressureStrategy,
646
0
            childChannelInitializer: childChannelInitializer
647
0
        ) { channel in
648
0
            channel.register().flatMap {
649
0
                let promise = channel.eventLoop.makePromise(of: Void.self)
650
0
                channel.triggerUserOutboundEvent0(
651
0
                    VsockChannelEvents.BindToAddress(vsockAddress),
652
0
                    promise: promise
653
0
                )
654
0
                return promise.futureResult
655
0
            }
656
0
        }.get()
657
0
    }
658
659
    /// Use the existing bound socket file descriptor.
660
    ///
661
    /// - Parameters:
662
    ///   - socket: The _Unix file descriptor_ representing the bound stream socket.
663
    ///   - cleanupExistingSocketFile: Unused.
664
    ///   - serverBackPressureStrategy: The back pressure strategy used by the server socket channel.
665
    ///   - childChannelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
666
    ///   method.
667
    /// - Returns: The result of the channel initializer.
668
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
669
    public func bind<Output: Sendable>(
670
        _ socket: NIOBSDSocket.Handle,
671
        cleanupExistingSocketFile: Bool = false,
672
        serverBackPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark? = nil,
673
        childChannelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
674
0
    ) async throws -> NIOAsyncChannel<Output, Never> {
675
0
        try await bind0(
676
0
            makeServerChannel: { eventLoop, childEventLoopGroup, enableMPTCP in
677
0
                if enableMPTCP {
678
0
                    throw ChannelError._operationUnsupported
679
0
                }
680
0
                return try ServerSocketChannel(
681
0
                    socket: socket,
682
0
                    eventLoop: eventLoop,
683
0
                    group: childEventLoopGroup
684
0
                )
685
0
            },
686
0
            serverBackPressureStrategy: serverBackPressureStrategy,
687
0
            childChannelInitializer: childChannelInitializer,
688
0
            registration: { serverChannel in
689
0
                let promise = serverChannel.eventLoop.makePromise(of: Void.self)
690
0
                serverChannel.registerAlreadyConfigured0(promise: promise)
691
0
                return promise.futureResult
692
0
            }
693
0
        ).get()
694
0
    }
695
696
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
697
    private func bind0<ChannelInitializerResult>(
698
        makeServerChannel: @escaping (SelectableEventLoop, EventLoopGroup, Bool) throws -> ServerSocketChannel,
699
        serverBackPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark?,
700
        childChannelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>,
701
        registration: @escaping @Sendable (ServerSocketChannel) -> EventLoopFuture<Void>
702
0
    ) -> EventLoopFuture<NIOAsyncChannel<ChannelInitializerResult, Never>> {
703
0
        let eventLoop = self.group.next()
704
0
        let childEventLoopGroup = self.childGroup
705
0
        let serverChannelOptions = self._serverChannelOptions
706
0
        let serverChannelInit = self.serverChannelInit ?? { _ in eventLoop.makeSucceededFuture(()) }
Unexecuted instantiation: $s8NIOPosix15ServerBootstrapC5bind033_F13D6D61D4A528D08BD67995505C2487LL04makeB7Channel26serverBackPressureStrategy05childN11Initializer12registration7NIOCore15EventLoopFutureCyAJ08NIOAsyncN0Vyxs5NeverOGGAA0b6SocketN0CAA010SelectablewX0C_AJ0wX5Group_pSbtKc_AJ0z16SequenceProducerpQ10StrategiesO16HighLowWatermarkVSgALyxGAJ0N0_pYbcALyytGATYbcts8SendableRzlFA3_AJA2__pYbcyKXEfu_
Unexecuted instantiation: $s8NIOPosix15ServerBootstrapC5bind033_F13D6D61D4A528D08BD67995505C2487LL04makeB7Channel26serverBackPressureStrategy05childN11Initializer12registration7NIOCore15EventLoopFutureCyAJ08NIOAsyncN0Vyxs5NeverOGGAA0b6SocketN0CAA010SelectablewX0C_AJ0wX5Group_pSbtKc_AJ0z16SequenceProducerpQ10StrategiesO16HighLowWatermarkVSgALyxGAJ0N0_pYbcALyytGATYbcts8SendableRzlFA3_AJA2__pYbcyKXEfu_A3_AJA2__pYbcfU_
707
0
        let childChannelInit = self.childChannelInit
708
0
        let childChannelOptions = self._childChannelOptions
709
0
710
0
        let serverChannel: ServerSocketChannel
711
0
        do {
712
0
            serverChannel = try makeServerChannel(
713
0
                eventLoop as! SelectableEventLoop,
714
0
                childEventLoopGroup,
715
0
                self.enableMPTCP
716
0
            )
717
0
        } catch {
718
0
            return eventLoop.makeFailedFuture(error)
719
0
        }
720
0
721
0
        return eventLoop.submit {
722
0
            serverChannelOptions.applyAllChannelOptions(to: serverChannel).flatMap {
723
0
                serverChannelInit(serverChannel)
724
0
            }.flatMap { (_) -> EventLoopFuture<NIOAsyncChannel<ChannelInitializerResult, Never>> in
725
0
                do {
726
0
                    try serverChannel.pipeline.syncOperations.addHandler(
727
0
                        AcceptBackoffHandler(shouldForwardIOErrorCaught: false),
728
0
                        name: "AcceptBackOffHandler"
729
0
                    )
730
0
                    try serverChannel.pipeline.syncOperations.addHandler(
731
0
                        AcceptHandler(
732
0
                            childChannelInitializer: childChannelInit,
733
0
                            childChannelOptions: childChannelOptions
734
0
                        ),
735
0
                        name: "AcceptHandler"
736
0
                    )
737
0
                    let asyncChannel = try NIOAsyncChannel<ChannelInitializerResult, Never>
738
0
                        ._wrapAsyncChannelWithTransformations(
739
0
                            wrappingChannelSynchronously: serverChannel,
740
0
                            backPressureStrategy: serverBackPressureStrategy,
741
0
                            channelReadTransformation: { channel -> EventLoopFuture<ChannelInitializerResult> in
742
0
                                // The channelReadTransformation is run on the EL of the server channel
743
0
                                // We have to make sure that we execute child channel initializer on the
744
0
                                // EL of the child channel.
745
0
                                channel.eventLoop.flatSubmit {
746
0
                                    childChannelInitializer(channel)
747
0
                                }
748
0
                            }
749
0
                        )
750
0
                    return registration(serverChannel)
751
0
                        .map { (_) -> NIOAsyncChannel<ChannelInitializerResult, Never> in asyncChannel
752
0
                        }
753
0
                } catch {
754
0
                    return eventLoop.makeFailedFuture(error)
755
0
                }
756
0
            }.flatMapError { error -> EventLoopFuture<NIOAsyncChannel<ChannelInitializerResult, Never>> in
757
0
                serverChannel.close0(error: error, mode: .all, promise: nil)
758
0
                return eventLoop.makeFailedFuture(error)
759
0
            }
760
0
        }.flatMap {
761
0
            $0
762
0
        }
763
0
    }
764
}
765
766
@available(*, unavailable)
767
extension ServerBootstrap: Sendable {}
768
769
extension Channel {
770
    fileprivate func registerAndDoSynchronously(
771
        _ body: @escaping (Channel) -> EventLoopFuture<Void>
772
0
    ) -> EventLoopFuture<Void> {
773
0
        // this is pretty delicate at the moment:
774
0
        // In many cases `body` must be _synchronously_ follow `register`, otherwise in our current
775
0
        // implementation, `epoll` will send us `EPOLLHUP`. To have it run synchronously, we need to invoke the
776
0
        // `flatMap` on the eventloop that the `register` will succeed on.
777
0
        self.eventLoop.assertInEventLoop()
778
0
        return self.register().assumeIsolated().flatMap {
779
0
            body(self)
780
0
        }.nonisolated()
781
0
    }
782
}
783
784
/// A `ClientBootstrap` is an easy way to bootstrap a `SocketChannel` when creating network clients.
785
///
786
/// Usually you re-use a `ClientBootstrap` once you set it up and called `connect` multiple times on it.
787
/// This way you ensure that the same `EventLoop`s will be shared across all your connections.
788
///
789
/// Example:
790
///
791
/// ```swift
792
///     let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
793
///     defer {
794
///         try! group.syncShutdownGracefully()
795
///     }
796
///     let bootstrap = ClientBootstrap(group: group)
797
///         // Enable SO_REUSEADDR.
798
///         .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
799
///         .channelInitializer { channel in
800
///             // always instantiate the handler _within_ the closure as
801
///             // it may be called multiple times (for example if the hostname
802
///             // resolves to both IPv4 and IPv6 addresses, cf. Happy Eyeballs).
803
///             channel.pipeline.addHandler(MyChannelHandler())
804
///         }
805
///     try! bootstrap.connect(host: "example.org", port: 12345).wait()
806
///     /* the Channel is now connected */
807
/// ```
808
///
809
/// The connected `SocketChannel` will operate on `ByteBuffer` as inbound and on `IOData` as outbound messages.
810
public final class ClientBootstrap: NIOClientTCPBootstrapProtocol {
811
    private let group: EventLoopGroup
812
    private var protocolHandlers: Optional<@Sendable () -> [ChannelHandler]>
813
    private var _channelInitializer: ChannelInitializerCallback
814
0
    private var channelInitializer: ChannelInitializerCallback {
815
0
        if let protocolHandlers = self.protocolHandlers {
816
0
            let channelInitializer = _channelInitializer
817
0
            return { channel in
818
0
                channelInitializer(channel).hop(to: channel.eventLoop).flatMapThrowing {
819
0
                    try channel.pipeline.syncOperations.addHandlers(protocolHandlers(), position: .first)
820
0
                }
821
0
            }
822
0
        } else {
823
0
            return self._channelInitializer
824
0
        }
825
0
    }
826
    @usableFromInline
827
    internal var _channelOptions: ChannelOptions.Storage
828
0
    private var connectTimeout: TimeAmount = TimeAmount.seconds(10)
829
    private var resolver: Optional<Resolver & Sendable>
830
    private var bindTarget: Optional<SocketAddress>
831
    private var enableMPTCP: Bool
832
833
    /// Create a `ClientBootstrap` on the `EventLoopGroup` `group`.
834
    ///
835
    /// The `EventLoopGroup` `group` must be compatible, otherwise the program will crash. `ClientBootstrap` is
836
    /// compatible only with `MultiThreadedEventLoopGroup` as well as the `EventLoop`s returned by
837
    /// `MultiThreadedEventLoopGroup.next`. See `init(validatingGroup:)` for a fallible initializer for
838
    /// situations where it's impossible to tell ahead of time if the `EventLoopGroup` is compatible or not.
839
    ///
840
    /// - Parameters:
841
    ///   - group: The `EventLoopGroup` to use.
842
0
    public convenience init(group: EventLoopGroup) {
843
0
        guard NIOOnSocketsBootstraps.isCompatible(group: group) else {
844
0
            preconditionFailure(
845
0
                "ClientBootstrap is only compatible with MultiThreadedEventLoopGroup and "
846
0
                    + "SelectableEventLoop. You tried constructing one with \(group) which is incompatible."
847
0
            )
848
0
        }
849
0
        self.init(validatingGroup: group)!
850
0
    }
851
852
    /// Create a `ClientBootstrap` on the `EventLoopGroup` `group`, validating that `group` is compatible.
853
    ///
854
    /// - Parameters:
855
    ///   - group: The `EventLoopGroup` to use.
856
0
    public init?(validatingGroup group: EventLoopGroup) {
857
0
        guard NIOOnSocketsBootstraps.isCompatible(group: group) else {
858
0
            return nil
859
0
        }
860
0
        self.group = group
861
0
        self._channelOptions = ChannelOptions.Storage()
862
0
        self._channelOptions.append(key: .tcpOption(.tcp_nodelay), value: 1)
863
0
        self._channelInitializer = { channel in channel.eventLoop.makeSucceededFuture(()) }
864
0
        self.protocolHandlers = nil
865
0
        self.resolver = nil
866
0
        self.bindTarget = nil
867
0
        self.enableMPTCP = false
868
0
    }
869
870
    /// Initialize the connected `SocketChannel` with `initializer`. The most common task in initializer is to add
871
    /// `ChannelHandler`s to the `ChannelPipeline`.
872
    ///
873
    /// The connected `Channel` will operate on `ByteBuffer` as inbound and `IOData` as outbound messages.
874
    ///
875
    /// - warning: The `handler` closure may be invoked _multiple times_ so it's usually the right choice to instantiate
876
    ///            `ChannelHandler`s within `handler`. The reason `handler` may be invoked multiple times is that to
877
    ///            successfully set up a connection multiple connections might be setup in the process. Assuming a
878
    ///            hostname that resolves to both IPv4 and IPv6 addresses, NIO will follow
879
    ///            [_Happy Eyeballs_](https://en.wikipedia.org/wiki/Happy_Eyeballs) and race both an IPv4 and an IPv6
880
    ///            connection. It is possible that both connections get fully established before the IPv4 connection
881
    ///            will be closed again because the IPv6 connection 'won the race'. Therefore the `channelInitializer`
882
    ///            might be called multiple times and it's important not to share stateful `ChannelHandler`s in more
883
    ///            than one `Channel`.
884
    ///
885
    /// - Parameters:
886
    ///   - handler: A closure that initializes the provided `Channel`.
887
    @preconcurrency
888
0
    public func channelInitializer(_ handler: @escaping @Sendable (Channel) -> EventLoopFuture<Void>) -> Self {
889
0
        self._channelInitializer = handler
890
0
        return self
891
0
    }
892
893
    /// Sets the protocol handlers that will be added to the front of the `ChannelPipeline` right after the
894
    /// `channelInitializer` has been called.
895
    ///
896
    /// Per bootstrap, you can only set the `protocolHandlers` once. Typically, `protocolHandlers` are used for the TLS
897
    /// implementation. Most notably, `NIOClientTCPBootstrap`, NIO's "universal bootstrap" abstraction, uses
898
    /// `protocolHandlers` to add the required `ChannelHandler`s for many TLS implementations.
899
    @preconcurrency
900
0
    public func protocolHandlers(_ handlers: @escaping @Sendable () -> [ChannelHandler]) -> Self {
901
0
        precondition(self.protocolHandlers == nil, "protocol handlers can only be set once")
902
0
        self.protocolHandlers = handlers
903
0
        return self
904
0
    }
905
906
    /// Specifies a `ChannelOption` to be applied to the `SocketChannel`.
907
    ///
908
    /// - Parameters:
909
    ///   - option: The option to be applied.
910
    ///   - value: The value for the option.
911
    @inlinable
912
0
    public func channelOption<Option: ChannelOption>(_ option: Option, value: Option.Value) -> Self {
913
0
        self._channelOptions.append(key: option, value: value)
914
0
        return self
915
0
    }
916
917
    /// Specifies a timeout to apply to a connection attempt.
918
    ///
919
    /// - Parameters:
920
    ///   - timeout: The timeout that will apply to the connection attempt.
921
0
    public func connectTimeout(_ timeout: TimeAmount) -> Self {
922
0
        self.connectTimeout = timeout
923
0
        return self
924
0
    }
925
926
    /// Specifies the `Resolver` to use or `nil` if the default should be used.
927
    ///
928
    /// - Parameters:
929
    ///   - resolver: The resolver that will be used during the connection attempt.
930
    @preconcurrency
931
0
    public func resolver(_ resolver: (Resolver & Sendable)?) -> Self {
932
0
        self.resolver = resolver
933
0
        return self
934
0
    }
935
936
    /// Enables multi-path TCP support.
937
    ///
938
    /// This option is only supported on some systems, and will lead to bind
939
    /// failing if the system does not support it. Users are recommended to
940
    /// only enable this in response to configuration or feature detection.
941
    ///
942
    /// > Note: Enabling this setting will re-enable Nagle's algorithm, even if it
943
    /// > had been disabled. This is a temporary workaround for a Linux kernel
944
    /// > limitation.
945
    ///
946
    /// - Parameters:
947
    ///   - value: Whether to enable MPTCP or not.
948
0
    public func enableMPTCP(_ value: Bool) -> Self {
949
0
        self.enableMPTCP = value
950
0
951
0
        // This is a temporary workaround until we get some stable Linux kernel
952
0
        // versions that support TCP_NODELAY and MPTCP.
953
0
        if value {
954
0
            self._channelOptions.remove(key: .tcpOption(.tcp_nodelay))
955
0
        }
956
0
957
0
        return self
958
0
    }
959
960
    /// Bind the `SocketChannel` to `address`.
961
    ///
962
    /// Using `bind` is not necessary unless you need the local address to be bound to a specific address.
963
    ///
964
    /// - Parameters:
965
    ///   - address: The `SocketAddress` to bind on.
966
0
    public func bind(to address: SocketAddress) -> ClientBootstrap {
967
0
        self.bindTarget = address
968
0
        return self
969
0
    }
970
971
    func makeSocketChannel(
972
        eventLoop: EventLoop,
973
        protocolFamily: NIOBSDSocket.ProtocolFamily
974
0
    ) throws -> SocketChannel {
975
0
        try Self.makeSocketChannel(
976
0
            eventLoop: eventLoop,
977
0
            protocolFamily: protocolFamily,
978
0
            enableMPTCP: self.enableMPTCP
979
0
        )
980
0
    }
981
982
    static func makeSocketChannel(
983
        eventLoop: EventLoop,
984
        protocolFamily: NIOBSDSocket.ProtocolFamily,
985
        enableMPTCP: Bool
986
0
    ) throws -> SocketChannel {
987
0
        try SocketChannel(
988
0
            eventLoop: eventLoop as! SelectableEventLoop,
989
0
            protocolFamily: protocolFamily,
990
0
            enableMPTCP: enableMPTCP
991
0
        )
992
0
    }
993
994
    /// Specify the `host` and `port` to connect to for the TCP `Channel` that will be established.
995
    ///
996
    /// - Note: Makes use of Happy Eyeballs.
997
    ///
998
    /// - Parameters:
999
    ///   - host: The host to connect to.
1000
    ///   - port: The port to connect to.
1001
    /// - Returns: An `EventLoopFuture<Channel>` to deliver the `Channel` when connected.
1002
0
    public func connect(host: String, port: Int) -> EventLoopFuture<Channel> {
1003
0
        let loop = self.group.next()
1004
0
        let resolver =
1005
0
            self.resolver
1006
0
            ?? GetaddrinfoResolver(
1007
0
                loop: loop,
1008
0
                aiSocktype: .stream,
1009
0
                aiProtocol: .tcp
1010
0
            )
1011
0
        let enableMPTCP = self.enableMPTCP
1012
0
        let channelInitializer = self.channelInitializer
1013
0
        let channelOptions = self._channelOptions
1014
0
        let bindTarget = self.bindTarget
1015
0
1016
0
        let connector = HappyEyeballsConnector(
1017
0
            resolver: resolver,
1018
0
            loop: loop,
1019
0
            host: host,
1020
0
            port: port,
1021
0
            connectTimeout: self.connectTimeout
1022
0
        ) { eventLoop, protocolFamily in
1023
0
            Self.initializeAndRegisterNewChannel(
1024
0
                eventLoop: eventLoop,
1025
0
                protocolFamily: protocolFamily,
1026
0
                enableMPTCP: enableMPTCP,
1027
0
                channelInitializer: channelInitializer,
1028
0
                channelOptions: channelOptions,
1029
0
                bindTarget: bindTarget
1030
0
            ) {
1031
0
                $0.eventLoop.makeSucceededFuture(())
1032
0
            }
1033
0
        }
1034
0
        return connector.resolveAndConnect()
1035
0
    }
1036
1037
    private static func connect(
1038
        freshChannel channel: Channel,
1039
        address: SocketAddress,
1040
        connectTimeout: TimeAmount
1041
0
    ) -> EventLoopFuture<Void> {
1042
0
        let connectPromise = channel.eventLoop.makePromise(of: Void.self)
1043
0
        channel.connect(to: address, promise: connectPromise)
1044
0
        let cancelTask = channel.eventLoop.scheduleTask(in: connectTimeout) {
1045
0
            connectPromise.fail(ChannelError.connectTimeout(connectTimeout))
1046
0
            channel.close(promise: nil)
1047
0
        }
1048
0
1049
0
        connectPromise.futureResult.whenComplete { (_: Result<Void, Error>) in
1050
0
            cancelTask.cancel()
1051
0
        }
1052
0
        return connectPromise.futureResult
1053
0
    }
1054
1055
    internal func testOnly_connect(
1056
        injectedChannel: SocketChannel,
1057
        to address: SocketAddress
1058
0
    ) -> EventLoopFuture<Channel> {
1059
0
        let connectTimeout = self.connectTimeout
1060
0
        return self.initializeAndRegisterChannel(injectedChannel) { channel in
1061
0
            Self.connect(freshChannel: channel, address: address, connectTimeout: connectTimeout)
1062
0
        }
1063
0
    }
1064
1065
    /// Specify the `address` to connect to for the TCP `Channel` that will be established.
1066
    ///
1067
    /// - Parameters:
1068
    ///   - address: The address to connect to.
1069
    /// - Returns: An `EventLoopFuture<Channel>` to deliver the `Channel` when connected.
1070
0
    public func connect(to address: SocketAddress) -> EventLoopFuture<Channel> {
1071
0
        let connectTimeout = self.connectTimeout
1072
0
1073
0
        return self.initializeAndRegisterNewChannel(
1074
0
            eventLoop: self.group.next(),
1075
0
            protocolFamily: address.protocol
1076
0
        ) { channel in
1077
0
            Self.connect(freshChannel: channel, address: address, connectTimeout: connectTimeout)
1078
0
        }
1079
0
    }
1080
1081
    /// Specify the `unixDomainSocket` path to connect to for the UDS `Channel` that will be established.
1082
    ///
1083
    /// - Parameters:
1084
    ///   - unixDomainSocketPath: The _Unix domain socket_ path to connect to.
1085
    /// - Returns: An `EventLoopFuture<Channel>` to deliver the `Channel` when connected.
1086
0
    public func connect(unixDomainSocketPath: String) -> EventLoopFuture<Channel> {
1087
0
        do {
1088
0
            let address = try SocketAddress(unixDomainSocketPath: unixDomainSocketPath)
1089
0
            return self.connect(to: address)
1090
0
        } catch {
1091
0
            return self.group.next().makeFailedFuture(error)
1092
0
        }
1093
0
    }
1094
1095
    /// Specify the VSOCK address to connect to for the `Channel`.
1096
    ///
1097
    /// - Parameters:
1098
    ///   - address: The VSOCK address to connect to.
1099
    /// - Returns: An `EventLoopFuture<Channel>` for when the `Channel` is connected.
1100
0
    public func connect(to address: VsockAddress) -> EventLoopFuture<Channel> {
1101
0
        let connectTimeout = self.connectTimeout
1102
0
        return self.initializeAndRegisterNewChannel(
1103
0
            eventLoop: self.group.next(),
1104
0
            protocolFamily: .vsock
1105
0
        ) { channel in
1106
0
            let connectPromise = channel.eventLoop.makePromise(of: Void.self)
1107
0
            channel.triggerUserOutboundEvent(VsockChannelEvents.ConnectToAddress(address), promise: connectPromise)
1108
0
1109
0
            let cancelTask = channel.eventLoop.scheduleTask(in: connectTimeout) {
1110
0
                connectPromise.fail(ChannelError.connectTimeout(connectTimeout))
1111
0
                channel.close(promise: nil)
1112
0
            }
1113
0
            connectPromise.futureResult.whenComplete { (_: Result<Void, Error>) in
1114
0
                cancelTask.cancel()
1115
0
            }
1116
0
1117
0
            return connectPromise.futureResult
1118
0
        }
1119
0
    }
1120
1121
    #if !os(Windows)
1122
    /// Use the existing connected socket file descriptor.
1123
    ///
1124
    /// - Parameters:
1125
    ///   - descriptor: The _Unix file descriptor_ representing the connected stream socket.
1126
    /// - Returns: an `EventLoopFuture<Channel>` to deliver the `Channel`.
1127
    @available(*, deprecated, renamed: "withConnectedSocket(_:)")
1128
0
    public func withConnectedSocket(descriptor: CInt) -> EventLoopFuture<Channel> {
1129
0
        self.withConnectedSocket(descriptor)
1130
0
    }
1131
    #endif
1132
1133
    /// Use the existing connected socket file descriptor.
1134
    ///
1135
    /// - Parameters:
1136
    ///   - socket: The _Unix file descriptor_ representing the connected stream socket.
1137
    /// - Returns: an `EventLoopFuture<Channel>` to deliver the `Channel`.
1138
0
    public func withConnectedSocket(_ socket: NIOBSDSocket.Handle) -> EventLoopFuture<Channel> {
1139
0
        let eventLoop = group.next()
1140
0
        let channelInitializer = self.channelInitializer
1141
0
        let options = self._channelOptions
1142
0
1143
0
        let channel: SocketChannel
1144
0
        do {
1145
0
            channel = try SocketChannel(eventLoop: eventLoop as! SelectableEventLoop, socket: socket)
1146
0
        } catch {
1147
0
            return eventLoop.makeFailedFuture(error)
1148
0
        }
1149
0
1150
0
        @Sendable
1151
0
        func setupChannel() -> EventLoopFuture<Channel> {
1152
0
            eventLoop.assertInEventLoop()
1153
0
            return options.applyAllChannelOptions(to: channel).flatMap {
1154
0
                channelInitializer(channel)
1155
0
            }.flatMap {
1156
0
                eventLoop.assertInEventLoop()
1157
0
                let promise = eventLoop.makePromise(of: Void.self)
1158
0
                channel.registerAlreadyConfigured0(promise: promise)
1159
0
                return promise.futureResult
1160
0
            }.map {
1161
0
                channel
1162
0
            }.flatMapError { error in
1163
0
                channel.close0(error: error, mode: .all, promise: nil)
1164
0
                return channel.eventLoop.makeFailedFuture(error)
1165
0
            }
1166
0
        }
1167
0
1168
0
        if eventLoop.inEventLoop {
1169
0
            return setupChannel()
1170
0
        } else {
1171
0
            return eventLoop.flatSubmit { setupChannel() }
1172
0
        }
1173
0
    }
1174
1175
    private func initializeAndRegisterNewChannel(
1176
        eventLoop: EventLoop,
1177
        protocolFamily: NIOBSDSocket.ProtocolFamily,
1178
        _ body: @escaping @Sendable (Channel) -> EventLoopFuture<Void>
1179
0
    ) -> EventLoopFuture<Channel> {
1180
0
        Self.initializeAndRegisterNewChannel(
1181
0
            eventLoop: eventLoop,
1182
0
            protocolFamily: protocolFamily,
1183
0
            enableMPTCP: self.enableMPTCP,
1184
0
            channelInitializer: self.channelInitializer,
1185
0
            channelOptions: self._channelOptions,
1186
0
            bindTarget: self.bindTarget,
1187
0
            body
1188
0
        )
1189
0
    }
1190
1191
    private static func initializeAndRegisterNewChannel(
1192
        eventLoop: EventLoop,
1193
        protocolFamily: NIOBSDSocket.ProtocolFamily,
1194
        enableMPTCP: Bool,
1195
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Void>,
1196
        channelOptions: ChannelOptions.Storage,
1197
        bindTarget: SocketAddress?,
1198
        _ body: @escaping @Sendable (Channel) -> EventLoopFuture<Void>
1199
0
    ) -> EventLoopFuture<Channel> {
1200
0
        let channel: SocketChannel
1201
0
        do {
1202
0
            channel = try Self.makeSocketChannel(
1203
0
                eventLoop: eventLoop,
1204
0
                protocolFamily: protocolFamily,
1205
0
                enableMPTCP: enableMPTCP
1206
0
            )
1207
0
        } catch {
1208
0
            return eventLoop.makeFailedFuture(error)
1209
0
        }
1210
0
        return Self.initializeAndRegisterChannel(
1211
0
            channel,
1212
0
            channelInitializer: channelInitializer,
1213
0
            channelOptions: channelOptions,
1214
0
            bindTarget: bindTarget,
1215
0
            body
1216
0
        )
1217
0
    }
1218
1219
    private func initializeAndRegisterChannel(
1220
        _ channel: SocketChannel,
1221
        _ body: @escaping @Sendable (Channel) -> EventLoopFuture<Void>
1222
0
    ) -> EventLoopFuture<Channel> {
1223
0
        Self.initializeAndRegisterChannel(
1224
0
            channel,
1225
0
            channelInitializer: self.channelInitializer,
1226
0
            channelOptions: self._channelOptions,
1227
0
            bindTarget: self.bindTarget,
1228
0
            body
1229
0
        )
1230
0
    }
1231
1232
    private static func initializeAndRegisterChannel(
1233
        _ channel: SocketChannel,
1234
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Void>,
1235
        channelOptions: ChannelOptions.Storage,
1236
        bindTarget: SocketAddress?,
1237
        _ body: @escaping @Sendable (Channel) -> EventLoopFuture<Void>
1238
0
    ) -> EventLoopFuture<Channel> {
1239
0
        let eventLoop = channel.eventLoop
1240
0
1241
0
        @inline(__always)
1242
0
        @Sendable
1243
0
        func setupChannel() -> EventLoopFuture<Channel> {
1244
0
            eventLoop.assertInEventLoop()
1245
0
            return channelOptions.applyAllChannelOptions(to: channel).flatMap {
1246
0
                if let bindTarget = bindTarget {
1247
0
                    return channel.bind(to: bindTarget).flatMap {
1248
0
                        channelInitializer(channel)
1249
0
                    }
1250
0
                } else {
1251
0
                    return channelInitializer(channel)
1252
0
                }
1253
0
            }.flatMap {
1254
0
                eventLoop.assertInEventLoop()
1255
0
                return channel.registerAndDoSynchronously(body)
1256
0
            }.map {
1257
0
                channel
1258
0
            }.flatMapError { error in
1259
0
                channel.close0(error: error, mode: .all, promise: nil)
1260
0
                return channel.eventLoop.makeFailedFuture(error)
1261
0
            }
1262
0
        }
1263
0
1264
0
        if eventLoop.inEventLoop {
1265
0
            return setupChannel()
1266
0
        } else {
1267
0
            return eventLoop.flatSubmit {
1268
0
                setupChannel()
1269
0
            }
1270
0
        }
1271
0
    }
1272
}
1273
1274
// MARK: Async connect methods
1275
1276
extension ClientBootstrap {
1277
    /// Specify the `host` and `port` to connect to for the TCP `Channel` that will be established.
1278
    ///
1279
    /// - Parameters:
1280
    ///   - host: The host to connect to.
1281
    ///   - port: The port to connect to.
1282
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
1283
    ///   method.
1284
    /// - Returns: The result of the channel initializer.
1285
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1286
    public func connect<Output: Sendable>(
1287
        host: String,
1288
        port: Int,
1289
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
1290
0
    ) async throws -> Output {
1291
0
        let eventLoop = self.group.next()
1292
0
        return try await self.connect(
1293
0
            host: host,
1294
0
            port: port,
1295
0
            eventLoop: eventLoop,
1296
0
            channelInitializer: channelInitializer,
1297
0
            postRegisterTransformation: { output, eventLoop in
1298
0
                eventLoop.makeSucceededFuture(output)
1299
0
            }
1300
0
        )
1301
0
    }
1302
1303
    /// Specify the `address` to connect to for the TCP `Channel` that will be established.
1304
    ///
1305
    /// - Parameters:
1306
    ///   - address: The address to connect to.
1307
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
1308
    ///   method.
1309
    /// - Returns: The result of the channel initializer.
1310
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1311
    public func connect<Output: Sendable>(
1312
        to address: SocketAddress,
1313
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
1314
0
    ) async throws -> Output {
1315
0
        let eventLoop = self.group.next()
1316
0
        let connectTimeout = self.connectTimeout
1317
0
        return try await self.initializeAndRegisterNewChannel(
1318
0
            eventLoop: eventLoop,
1319
0
            protocolFamily: address.protocol,
1320
0
            channelInitializer: channelInitializer,
1321
0
            postRegisterTransformation: { output, eventLoop in
1322
0
                eventLoop.makeSucceededFuture(output)
1323
0
            },
1324
0
            { channel in
1325
0
                Self.connect(freshChannel: channel, address: address, connectTimeout: connectTimeout)
1326
0
            }
1327
0
        ).get().1
1328
0
    }
1329
1330
    /// Specify the `unixDomainSocket` path to connect to for the UDS `Channel` that will be established.
1331
    ///
1332
    /// - Parameters:
1333
    ///   - unixDomainSocketPath: The _Unix domain socket_ path to connect to.
1334
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
1335
    ///   method.
1336
    /// - Returns: The result of the channel initializer.
1337
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1338
    public func connect<Output: Sendable>(
1339
        unixDomainSocketPath: String,
1340
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
1341
0
    ) async throws -> Output {
1342
0
        let address = try SocketAddress(unixDomainSocketPath: unixDomainSocketPath)
1343
0
        return try await self.connect(
1344
0
            to: address,
1345
0
            channelInitializer: channelInitializer
1346
0
        )
1347
0
    }
1348
1349
    /// Specify the VSOCK address to connect to for the `Channel`.
1350
    ///
1351
    /// - Parameters:
1352
    ///   - address: The VSOCK address to connect to.
1353
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
1354
    ///   method.
1355
    /// - Returns: The result of the channel initializer.
1356
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1357
    public func connect<Output: Sendable>(
1358
        to address: VsockAddress,
1359
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
1360
0
    ) async throws -> Output {
1361
0
        let connectTimeout = self.connectTimeout
1362
0
        return try await self.initializeAndRegisterNewChannel(
1363
0
            eventLoop: self.group.next(),
1364
0
            protocolFamily: NIOBSDSocket.ProtocolFamily.vsock,
1365
0
            channelInitializer: channelInitializer,
1366
0
            postRegisterTransformation: { result, eventLoop in
1367
0
                eventLoop.makeSucceededFuture(result)
1368
0
            }
1369
0
        ) { channel in
1370
0
            let connectPromise = channel.eventLoop.makePromise(of: Void.self)
1371
0
            channel.triggerUserOutboundEvent(VsockChannelEvents.ConnectToAddress(address), promise: connectPromise)
1372
0
1373
0
            let cancelTask = channel.eventLoop.scheduleTask(in: connectTimeout) {
1374
0
                connectPromise.fail(ChannelError.connectTimeout(connectTimeout))
1375
0
                channel.close(promise: nil)
1376
0
            }
1377
0
            connectPromise.futureResult.whenComplete { (_: Result<Void, Error>) in
1378
0
                cancelTask.cancel()
1379
0
            }
1380
0
1381
0
            return connectPromise.futureResult
1382
0
        }.get().1
1383
0
    }
1384
1385
    /// Use the existing connected socket file descriptor.
1386
    ///
1387
    /// - Parameters:
1388
    ///   - socket: The _Unix file descriptor_ representing the connected stream socket.
1389
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
1390
    ///   method.
1391
    /// - Returns: The result of the channel initializer.
1392
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1393
    public func withConnectedSocket<Output: Sendable>(
1394
        _ socket: NIOBSDSocket.Handle,
1395
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
1396
0
    ) async throws -> Output {
1397
0
        let eventLoop = group.next()
1398
0
        return try await self.withConnectedSocket(
1399
0
            eventLoop: eventLoop,
1400
0
            socket: socket,
1401
0
            channelInitializer: channelInitializer,
1402
0
            postRegisterTransformation: { output, eventLoop in
1403
0
                eventLoop.makeSucceededFuture(output)
1404
0
            }
1405
0
        )
1406
0
    }
1407
1408
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1409
    func connect<ChannelInitializerResult: Sendable, PostRegistrationTransformationResult: Sendable>(
1410
        host: String,
1411
        port: Int,
1412
        eventLoop: EventLoop,
1413
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>,
1414
        postRegisterTransformation:
1415
            @escaping @Sendable (ChannelInitializerResult, EventLoop) -> EventLoopFuture<
1416
                PostRegistrationTransformationResult
1417
            >
1418
0
    ) async throws -> PostRegistrationTransformationResult {
1419
0
        let resolver =
1420
0
            self.resolver
1421
0
            ?? GetaddrinfoResolver(
1422
0
                loop: eventLoop,
1423
0
                aiSocktype: .stream,
1424
0
                aiProtocol: .tcp
1425
0
            )
1426
0
1427
0
        let enableMPTCP = self.enableMPTCP
1428
0
        let bootstrapChannelInitializer = self.channelInitializer
1429
0
        let channelOptions = self._channelOptions
1430
0
        let bindTarget = self.bindTarget
1431
0
1432
0
        let connector = HappyEyeballsConnector<PostRegistrationTransformationResult>(
1433
0
            resolver: resolver,
1434
0
            loop: eventLoop,
1435
0
            host: host,
1436
0
            port: port,
1437
0
            connectTimeout: self.connectTimeout
1438
0
        ) { eventLoop, protocolFamily in
1439
0
            Self.initializeAndRegisterNewChannel(
1440
0
                eventLoop: eventLoop,
1441
0
                protocolFamily: protocolFamily,
1442
0
                enableMPTPCP: enableMPTCP,
1443
0
                bootstrapChannelInitializer: bootstrapChannelInitializer,
1444
0
                channelOptions: channelOptions,
1445
0
                bindTarget: bindTarget,
1446
0
                channelInitializer: channelInitializer,
1447
0
                postRegisterTransformation: postRegisterTransformation
1448
0
            ) {
1449
0
                $0.eventLoop.makeSucceededFuture(())
1450
0
            }
1451
0
        }
1452
0
        return try await connector.resolveAndConnect().map { $0.1 }.get()
1453
0
    }
1454
1455
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1456
    private func withConnectedSocket<
1457
        ChannelInitializerResult: Sendable,
1458
        PostRegistrationTransformationResult: Sendable
1459
    >(
1460
        eventLoop: EventLoop,
1461
        socket: NIOBSDSocket.Handle,
1462
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>,
1463
        postRegisterTransformation:
1464
            @escaping @Sendable (ChannelInitializerResult, EventLoop) -> EventLoopFuture<
1465
                PostRegistrationTransformationResult
1466
            >
1467
0
    ) async throws -> PostRegistrationTransformationResult {
1468
0
        let channel = try SocketChannel(eventLoop: eventLoop as! SelectableEventLoop, socket: socket)
1469
0
1470
0
        return try await self.initializeAndRegisterChannel(
1471
0
            channel: channel,
1472
0
            channelInitializer: channelInitializer,
1473
0
            registration: { channel in
1474
0
                let promise = eventLoop.makePromise(of: Void.self)
1475
0
                channel.registerAlreadyConfigured0(promise: promise)
1476
0
                return promise.futureResult
1477
0
            },
1478
0
            postRegisterTransformation: postRegisterTransformation
1479
0
        ).get()
1480
0
    }
1481
1482
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1483
    private func initializeAndRegisterNewChannel<
1484
        ChannelInitializerResult: Sendable,
1485
        PostRegistrationTransformationResult: Sendable
1486
    >(
1487
        eventLoop: EventLoop,
1488
        protocolFamily: NIOBSDSocket.ProtocolFamily,
1489
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>,
1490
        postRegisterTransformation:
1491
            @escaping @Sendable (ChannelInitializerResult, EventLoop) -> EventLoopFuture<
1492
                PostRegistrationTransformationResult
1493
            >,
1494
        _ body: @escaping @Sendable (Channel) -> EventLoopFuture<Void>
1495
0
    ) -> EventLoopFuture<(Channel, PostRegistrationTransformationResult)> {
1496
0
        let channel: SocketChannel
1497
0
        do {
1498
0
            channel = try self.makeSocketChannel(eventLoop: eventLoop, protocolFamily: protocolFamily)
1499
0
        } catch {
1500
0
            return eventLoop.makeFailedFuture(error)
1501
0
        }
1502
0
        return self.initializeAndRegisterChannel(
1503
0
            channel: channel,
1504
0
            channelInitializer: channelInitializer,
1505
0
            registration: { channel in
1506
0
                channel.registerAndDoSynchronously(body)
1507
0
            },
1508
0
            postRegisterTransformation: postRegisterTransformation
1509
0
        ).map { (channel, $0) }
1510
0
    }
1511
1512
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1513
    private static func initializeAndRegisterNewChannel<
1514
        ChannelInitializerResult: Sendable,
1515
        PostRegistrationTransformationResult: Sendable
1516
    >(
1517
        eventLoop: EventLoop,
1518
        protocolFamily: NIOBSDSocket.ProtocolFamily,
1519
        enableMPTPCP: Bool,
1520
        bootstrapChannelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Void>,
1521
        channelOptions: ChannelOptions.Storage,
1522
        bindTarget: SocketAddress?,
1523
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>,
1524
        postRegisterTransformation:
1525
            @escaping @Sendable (ChannelInitializerResult, EventLoop) -> EventLoopFuture<
1526
                PostRegistrationTransformationResult
1527
            >,
1528
        _ body: @escaping @Sendable (Channel) -> EventLoopFuture<Void>
1529
0
    ) -> EventLoopFuture<(Channel, PostRegistrationTransformationResult)> {
1530
0
        let channel: SocketChannel
1531
0
        do {
1532
0
            channel = try Self.makeSocketChannel(
1533
0
                eventLoop: eventLoop,
1534
0
                protocolFamily: protocolFamily,
1535
0
                enableMPTCP: enableMPTPCP
1536
0
            )
1537
0
        } catch {
1538
0
            return eventLoop.makeFailedFuture(error)
1539
0
        }
1540
0
        return Self.initializeAndRegisterChannel(
1541
0
            channel: channel,
1542
0
            bootstrapChannelInitializer: bootstrapChannelInitializer,
1543
0
            channelOptions: channelOptions,
1544
0
            bindTarget: bindTarget,
1545
0
            channelInitializer: channelInitializer,
1546
0
            registration: { channel in
1547
0
                channel.registerAndDoSynchronously(body)
1548
0
            },
1549
0
            postRegisterTransformation: postRegisterTransformation
1550
0
        ).map { (channel, $0) }
1551
0
    }
1552
1553
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1554
    private func initializeAndRegisterChannel<
1555
        ChannelInitializerResult: Sendable,
1556
        PostRegistrationTransformationResult: Sendable
1557
    >(
1558
        channel: SocketChannel,
1559
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>,
1560
        registration: @escaping @Sendable (SocketChannel) -> EventLoopFuture<Void>,
1561
        postRegisterTransformation:
1562
            @escaping @Sendable (ChannelInitializerResult, EventLoop) -> EventLoopFuture<
1563
                PostRegistrationTransformationResult
1564
            >
1565
0
    ) -> EventLoopFuture<PostRegistrationTransformationResult> {
1566
0
        Self.initializeAndRegisterChannel(
1567
0
            channel: channel,
1568
0
            bootstrapChannelInitializer: self.channelInitializer,
1569
0
            channelOptions: self._channelOptions,
1570
0
            bindTarget: self.bindTarget,
1571
0
            channelInitializer: channelInitializer,
1572
0
            registration: registration,
1573
0
            postRegisterTransformation: postRegisterTransformation
1574
0
        )
1575
0
    }
1576
1577
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1578
    private static func initializeAndRegisterChannel<
1579
        ChannelInitializerResult: Sendable,
1580
        PostRegistrationTransformationResult: Sendable
1581
    >(
1582
        channel: SocketChannel,
1583
        bootstrapChannelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Void>,
1584
        channelOptions: ChannelOptions.Storage,
1585
        bindTarget: SocketAddress?,
1586
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>,
1587
        registration: @escaping @Sendable (SocketChannel) -> EventLoopFuture<Void>,
1588
        postRegisterTransformation:
1589
            @escaping @Sendable (ChannelInitializerResult, EventLoop) -> EventLoopFuture<
1590
                PostRegistrationTransformationResult
1591
            >
1592
0
    ) -> EventLoopFuture<PostRegistrationTransformationResult> {
1593
0
        let channelInitializer = { @Sendable channel in
1594
0
            bootstrapChannelInitializer(channel).hop(to: channel.eventLoop)
1595
0
                .assumeIsolated()
1596
0
                .flatMap { channelInitializer(channel) }
1597
0
                .nonisolated()
1598
0
        }
1599
0
        let eventLoop = channel.eventLoop
1600
0
1601
0
        @inline(__always)
1602
0
        @Sendable
1603
0
        func setupChannel() -> EventLoopFuture<PostRegistrationTransformationResult> {
1604
0
            eventLoop.assertInEventLoop()
1605
0
            return
1606
0
                channelOptions
1607
0
                .applyAllChannelOptions(to: channel)
1608
0
                .assumeIsolated()
1609
0
                .flatMap {
1610
0
                    if let bindTarget = bindTarget {
1611
0
                        return
1612
0
                            channel
1613
0
                            .bind(to: bindTarget)
1614
0
                            .flatMap {
1615
0
                                channelInitializer(channel)
1616
0
                            }
1617
0
                    } else {
1618
0
                        return channelInitializer(channel)
1619
0
                    }
1620
0
                }.flatMap { (result: ChannelInitializerResult) in
1621
0
                    eventLoop.assertInEventLoop()
1622
0
                    return registration(channel).map {
1623
0
                        result
1624
0
                    }
1625
0
                }.flatMap {
1626
0
                    (result: ChannelInitializerResult) -> EventLoopFuture<PostRegistrationTransformationResult> in
1627
0
                    postRegisterTransformation(result, eventLoop)
1628
0
                }.flatMapError { error in
1629
0
                    eventLoop.assertInEventLoop()
1630
0
                    channel.close0(error: error, mode: .all, promise: nil)
1631
0
                    return channel.eventLoop.makeFailedFuture(error)
1632
0
                }
1633
0
                .nonisolated()
1634
0
        }
1635
0
1636
0
        if eventLoop.inEventLoop {
1637
0
            return setupChannel()
1638
0
        } else {
1639
0
            return eventLoop.flatSubmit {
1640
0
                setupChannel()
1641
0
            }
1642
0
        }
1643
0
    }
1644
}
1645
1646
@available(*, unavailable)
1647
extension ClientBootstrap: Sendable {}
1648
1649
/// A `DatagramBootstrap` is an easy way to bootstrap a `DatagramChannel` when creating datagram clients
1650
/// and servers.
1651
///
1652
/// Example:
1653
///
1654
/// ```swift
1655
///     let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
1656
///     defer {
1657
///         try! group.syncShutdownGracefully()
1658
///     }
1659
///     let bootstrap = DatagramBootstrap(group: group)
1660
///         // Enable SO_REUSEADDR.
1661
///         .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
1662
///         .channelInitializer { channel in
1663
///             channel.pipeline.addHandler(MyChannelHandler())
1664
///         }
1665
///     let channel = try! bootstrap.bind(host: "127.0.0.1", port: 53).wait()
1666
///     /* the Channel is now ready to send/receive datagrams */
1667
///
1668
///     try channel.closeFuture.wait()  // Wait until the channel un-binds.
1669
/// ```
1670
///
1671
/// The `DatagramChannel` will operate on `AddressedEnvelope<ByteBuffer>` as inbound and outbound messages.
1672
public final class DatagramBootstrap {
1673
1674
    private let group: EventLoopGroup
1675
    private var channelInitializer: Optional<ChannelInitializerCallback>
1676
    @usableFromInline
1677
    internal var _channelOptions: ChannelOptions.Storage
1678
0
    private var proto: NIOBSDSocket.ProtocolSubtype = .default
1679
1680
    /// Create a `DatagramBootstrap` on the `EventLoopGroup` `group`.
1681
    ///
1682
    /// The `EventLoopGroup` `group` must be compatible, otherwise the program will crash. `DatagramBootstrap` is
1683
    /// compatible only with `MultiThreadedEventLoopGroup` as well as the `EventLoop`s returned by
1684
    /// `MultiThreadedEventLoopGroup.next`. See `init(validatingGroup:)` for a fallible initializer for
1685
    /// situations where it's impossible to tell ahead of time if the `EventLoopGroup` is compatible or not.
1686
    ///
1687
    /// - Parameters:
1688
    ///   - group: The `EventLoopGroup` to use.
1689
0
    public convenience init(group: EventLoopGroup) {
1690
0
        guard NIOOnSocketsBootstraps.isCompatible(group: group) else {
1691
0
            preconditionFailure(
1692
0
                "DatagramBootstrap is only compatible with MultiThreadedEventLoopGroup and "
1693
0
                    + "SelectableEventLoop. You tried constructing one with \(group) which is incompatible."
1694
0
            )
1695
0
        }
1696
0
        self.init(validatingGroup: group)!
1697
0
    }
1698
1699
    /// Create a `DatagramBootstrap` on the `EventLoopGroup` `group`, validating that `group` is compatible.
1700
    ///
1701
    /// - Parameters:
1702
    ///   - group: The `EventLoopGroup` to use.
1703
0
    public init?(validatingGroup group: EventLoopGroup) {
1704
0
        guard NIOOnSocketsBootstraps.isCompatible(group: group) else {
1705
0
            return nil
1706
0
        }
1707
0
        self._channelOptions = ChannelOptions.Storage()
1708
0
        self.group = group
1709
0
        self.channelInitializer = nil
1710
0
    }
1711
1712
    /// Initialize the bound `DatagramChannel` with `initializer`. The most common task in initializer is to add
1713
    /// `ChannelHandler`s to the `ChannelPipeline`.
1714
    ///
1715
    /// - Parameters:
1716
    ///   - handler: A closure that initializes the provided `Channel`.
1717
    @preconcurrency
1718
0
    public func channelInitializer(_ handler: @escaping @Sendable (Channel) -> EventLoopFuture<Void>) -> Self {
1719
0
        self.channelInitializer = handler
1720
0
        return self
1721
0
    }
1722
1723
    /// Specifies a `ChannelOption` to be applied to the `DatagramChannel`.
1724
    ///
1725
    /// - Parameters:
1726
    ///   - option: The option to be applied.
1727
    ///   - value: The value for the option.
1728
    @inlinable
1729
0
    public func channelOption<Option: ChannelOption>(_ option: Option, value: Option.Value) -> Self {
1730
0
        self._channelOptions.append(key: option, value: value)
1731
0
        return self
1732
0
    }
1733
1734
0
    public func protocolSubtype(_ subtype: NIOBSDSocket.ProtocolSubtype) -> Self {
1735
0
        self.proto = subtype
1736
0
        return self
1737
0
    }
1738
1739
    #if !os(Windows)
1740
    /// Use the existing bound socket file descriptor.
1741
    ///
1742
    /// - Parameters:
1743
    ///   - descriptor: The _Unix file descriptor_ representing the bound datagram socket.
1744
    @available(*, deprecated, renamed: "withBoundSocket(_:)")
1745
0
    public func withBoundSocket(descriptor: CInt) -> EventLoopFuture<Channel> {
1746
0
        self.withBoundSocket(descriptor)
1747
0
    }
1748
    #endif
1749
1750
    /// Use the existing bound socket file descriptor.
1751
    ///
1752
    /// - Parameters:
1753
    ///   - socket: The _Unix file descriptor_ representing the bound datagram socket.
1754
0
    public func withBoundSocket(_ socket: NIOBSDSocket.Handle) -> EventLoopFuture<Channel> {
1755
0
        func makeChannel(_ eventLoop: SelectableEventLoop) throws -> DatagramChannel {
1756
0
            try DatagramChannel(eventLoop: eventLoop, socket: socket)
1757
0
        }
1758
0
        return withNewChannel(makeChannel: makeChannel) { eventLoop, channel in
1759
0
            let promise = eventLoop.makePromise(of: Void.self)
1760
0
            channel.registerAlreadyConfigured0(promise: promise)
1761
0
            return promise.futureResult
1762
0
        }
1763
0
    }
1764
1765
    /// Bind the `DatagramChannel` to `host` and `port`.
1766
    ///
1767
    /// - Parameters:
1768
    ///   - host: The host to bind on.
1769
    ///   - port: The port to bind on.
1770
0
    public func bind(host: String, port: Int) -> EventLoopFuture<Channel> {
1771
0
        bind0 {
1772
0
            try SocketAddress.makeAddressResolvingHost(host, port: port)
1773
0
        }
1774
0
    }
1775
1776
    /// Bind the `DatagramChannel` to `address`.
1777
    ///
1778
    /// - Parameters:
1779
    ///   - address: The `SocketAddress` to bind on.
1780
0
    public func bind(to address: SocketAddress) -> EventLoopFuture<Channel> {
1781
0
        bind0 { address }
1782
0
    }
1783
1784
    /// Bind the `DatagramChannel` to a UNIX Domain Socket.
1785
    ///
1786
    /// - Parameters:
1787
    ///   - unixDomainSocketPath: The path of the UNIX Domain Socket to bind on. `path` must not exist, it will be created by the system.
1788
0
    public func bind(unixDomainSocketPath: String) -> EventLoopFuture<Channel> {
1789
0
        bind0 {
1790
0
            try SocketAddress(unixDomainSocketPath: unixDomainSocketPath)
1791
0
        }
1792
0
    }
1793
1794
    /// Bind the `DatagramChannel` to a UNIX Domain Socket.
1795
    ///
1796
    /// - Parameters:
1797
    ///   - unixDomainSocketPath: The path of the UNIX Domain Socket to bind on. The`unixDomainSocketPath` must not exist,
1798
    ///     unless `cleanupExistingSocketFile`is set to `true`.
1799
    ///   - cleanupExistingSocketFile: Whether to cleanup an existing socket file at `unixDomainSocketPath`.
1800
0
    public func bind(unixDomainSocketPath: String, cleanupExistingSocketFile: Bool) -> EventLoopFuture<Channel> {
1801
0
        if cleanupExistingSocketFile {
1802
0
            do {
1803
0
                try BaseSocket.cleanupSocket(unixDomainSocketPath: unixDomainSocketPath)
1804
0
            } catch {
1805
0
                return group.next().makeFailedFuture(error)
1806
0
            }
1807
0
        }
1808
0
1809
0
        return self.bind(unixDomainSocketPath: unixDomainSocketPath)
1810
0
    }
1811
1812
0
    private func bind0(_ makeSocketAddress: () throws -> SocketAddress) -> EventLoopFuture<Channel> {
1813
0
        let subtype = self.proto
1814
0
        let address: SocketAddress
1815
0
        do {
1816
0
            address = try makeSocketAddress()
1817
0
        } catch {
1818
0
            return group.next().makeFailedFuture(error)
1819
0
        }
1820
0
        func makeChannel(_ eventLoop: SelectableEventLoop) throws -> DatagramChannel {
1821
0
            try DatagramChannel(
1822
0
                eventLoop: eventLoop,
1823
0
                protocolFamily: address.protocol,
1824
0
                protocolSubtype: subtype
1825
0
            )
1826
0
        }
1827
0
        return withNewChannel(makeChannel: makeChannel) { _, channel in
1828
0
            channel.register().flatMap {
1829
0
                channel.bind(to: address)
1830
0
            }
1831
0
        }
1832
0
    }
1833
1834
    /// Connect the `DatagramChannel` to `host` and `port`.
1835
    ///
1836
    /// - Parameters:
1837
    ///   - host: The host to connect to.
1838
    ///   - port: The port to connect to.
1839
0
    public func connect(host: String, port: Int) -> EventLoopFuture<Channel> {
1840
0
        connect0 {
1841
0
            try SocketAddress.makeAddressResolvingHost(host, port: port)
1842
0
        }
1843
0
    }
1844
1845
    /// Connect the `DatagramChannel` to `address`.
1846
    ///
1847
    /// - Parameters:
1848
    ///   - address: The `SocketAddress` to connect to.
1849
0
    public func connect(to address: SocketAddress) -> EventLoopFuture<Channel> {
1850
0
        connect0 { address }
1851
0
    }
1852
1853
    /// Connect the `DatagramChannel` to a UNIX Domain Socket.
1854
    ///
1855
    /// - Parameters:
1856
    ///   - unixDomainSocketPath: The path of the UNIX Domain Socket to connect to. `path` must not exist, it will be created by the system.
1857
0
    public func connect(unixDomainSocketPath: String) -> EventLoopFuture<Channel> {
1858
0
        connect0 {
1859
0
            try SocketAddress(unixDomainSocketPath: unixDomainSocketPath)
1860
0
        }
1861
0
    }
1862
1863
0
    private func connect0(_ makeSocketAddress: () throws -> SocketAddress) -> EventLoopFuture<Channel> {
1864
0
        let subtype = self.proto
1865
0
        let address: SocketAddress
1866
0
        do {
1867
0
            address = try makeSocketAddress()
1868
0
        } catch {
1869
0
            return group.next().makeFailedFuture(error)
1870
0
        }
1871
0
        func makeChannel(_ eventLoop: SelectableEventLoop) throws -> DatagramChannel {
1872
0
            try DatagramChannel(
1873
0
                eventLoop: eventLoop,
1874
0
                protocolFamily: address.protocol,
1875
0
                protocolSubtype: subtype
1876
0
            )
1877
0
        }
1878
0
        return withNewChannel(makeChannel: makeChannel) { _, channel in
1879
0
            channel.register().flatMap {
1880
0
                channel.connect(to: address)
1881
0
            }
1882
0
        }
1883
0
    }
1884
1885
    private func withNewChannel(
1886
        makeChannel: (_ eventLoop: SelectableEventLoop) throws -> DatagramChannel,
1887
        _ bringup: @escaping @Sendable (EventLoop, DatagramChannel) -> EventLoopFuture<Void>
1888
0
    ) -> EventLoopFuture<Channel> {
1889
0
        let eventLoop = self.group.next()
1890
0
        let channelInitializer = self.channelInitializer ?? { @Sendable _ in eventLoop.makeSucceededFuture(()) }
Unexecuted instantiation: $s8NIOPosix17DatagramBootstrapC14withNewChannel33_F13D6D61D4A528D08BD67995505C2487LL04makeF0_7NIOCore15EventLoopFutureCyAG0F0_pGAA0bF0CAA010SelectableqR0CKXE_AIyytGAG0qR0_p_AMtYbctFApgJ_pYbcyKXEfu_
Unexecuted instantiation: $s8NIOPosix17DatagramBootstrapC14withNewChannel33_F13D6D61D4A528D08BD67995505C2487LL04makeF0_7NIOCore15EventLoopFutureCyAG0F0_pGAA0bF0CAA010SelectableqR0CKXE_AIyytGAG0qR0_p_AMtYbctFApgJ_pYbcyKXEfu_ApgJ_pYbcfU_
1891
0
        let channelOptions = self._channelOptions
1892
0
1893
0
        let channel: DatagramChannel
1894
0
        do {
1895
0
            channel = try makeChannel(eventLoop as! SelectableEventLoop)
1896
0
        } catch {
1897
0
            return eventLoop.makeFailedFuture(error)
1898
0
        }
1899
0
1900
0
        @Sendable
1901
0
        func setupChannel() -> EventLoopFuture<Channel> {
1902
0
            eventLoop.assertInEventLoop()
1903
0
            return channelOptions.applyAllChannelOptions(to: channel).flatMap {
1904
0
                channelInitializer(channel)
1905
0
            }.flatMap {
1906
0
                eventLoop.assertInEventLoop()
1907
0
                return bringup(eventLoop, channel)
1908
0
            }.map {
1909
0
                channel
1910
0
            }.flatMapError { error in
1911
0
                eventLoop.makeFailedFuture(error)
1912
0
            }
1913
0
        }
1914
0
1915
0
        if eventLoop.inEventLoop {
1916
0
            return setupChannel()
1917
0
        } else {
1918
0
            return eventLoop.flatSubmit {
1919
0
                setupChannel()
1920
0
            }
1921
0
        }
1922
0
    }
1923
}
1924
1925
// MARK: Async connect/bind methods
1926
1927
extension DatagramBootstrap {
1928
    /// Use the existing bound socket file descriptor.
1929
    ///
1930
    /// - Parameters:
1931
    ///   - socket: The _Unix file descriptor_ representing the bound stream socket.
1932
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
1933
    ///   method.
1934
    /// - Returns: The result of the channel initializer.
1935
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1936
    public func withBoundSocket<Output: Sendable>(
1937
        _ socket: NIOBSDSocket.Handle,
1938
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
1939
0
    ) async throws -> Output {
1940
0
        func makeChannel(_ eventLoop: SelectableEventLoop) throws -> DatagramChannel {
1941
0
            try DatagramChannel(eventLoop: eventLoop, socket: socket)
1942
0
        }
1943
0
        return try await self.makeConfiguredChannel(
1944
0
            makeChannel: makeChannel(_:),
1945
0
            channelInitializer: channelInitializer,
1946
0
            registration: { channel in
1947
0
                let promise = channel.eventLoop.makePromise(of: Void.self)
1948
0
                channel.registerAlreadyConfigured0(promise: promise)
1949
0
                return promise.futureResult
1950
0
            },
1951
0
            postRegisterTransformation: { output, eventLoop in
1952
0
                eventLoop.makeSucceededFuture(output)
1953
0
            }
1954
0
        ).get()
1955
0
    }
1956
1957
    /// Bind the `DatagramChannel` to `host` and `port`.
1958
    ///
1959
    /// - Parameters:
1960
    ///   - host: The host to bind on.
1961
    ///   - port: The port to bind on.
1962
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
1963
    ///   method.
1964
    /// - Returns: The result of the channel initializer.
1965
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1966
    public func bind<Output: Sendable>(
1967
        host: String,
1968
        port: Int,
1969
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
1970
0
    ) async throws -> Output {
1971
0
        try await self.bind0(
1972
0
            makeSocketAddress: {
1973
0
                try SocketAddress.makeAddressResolvingHost(host, port: port)
1974
0
            },
1975
0
            channelInitializer: channelInitializer,
1976
0
            postRegisterTransformation: { output, eventLoop in
1977
0
                eventLoop.makeSucceededFuture(output)
1978
0
            }
1979
0
        )
1980
0
    }
1981
1982
    /// Bind the `DatagramChannel` to the `address`.
1983
    ///
1984
    /// - Parameters:
1985
    ///   - address: The `SocketAddress` to bind on.
1986
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
1987
    ///   method.
1988
    /// - Returns: The result of the channel initializer.
1989
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
1990
    public func bind<Output: Sendable>(
1991
        to address: SocketAddress,
1992
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
1993
0
    ) async throws -> Output {
1994
0
        try await self.bind0(
1995
0
            makeSocketAddress: {
1996
0
                address
1997
0
            },
1998
0
            channelInitializer: channelInitializer,
1999
0
            postRegisterTransformation: { output, eventLoop in
2000
0
                eventLoop.makeSucceededFuture(output)
2001
0
            }
2002
0
        )
2003
0
    }
2004
2005
    /// Bind the `DatagramChannel` to the `unixDomainSocketPath`.
2006
    ///
2007
    /// - Parameters:
2008
    ///   - unixDomainSocketPath: The path of the UNIX Domain Socket to bind on. The`unixDomainSocketPath` must not exist,
2009
    ///     unless `cleanupExistingSocketFile`is set to `true`.
2010
    ///   - cleanupExistingSocketFile: Whether to cleanup an existing socket file at `unixDomainSocketPath`.
2011
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
2012
    ///   method.
2013
    /// - Returns: The result of the channel initializer.
2014
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2015
    public func bind<Output: Sendable>(
2016
        unixDomainSocketPath: String,
2017
        cleanupExistingSocketFile: Bool = false,
2018
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
2019
0
    ) async throws -> Output {
2020
0
        if cleanupExistingSocketFile {
2021
0
            try BaseSocket.cleanupSocket(unixDomainSocketPath: unixDomainSocketPath)
2022
0
        }
2023
0
2024
0
        return try await self.bind0(
2025
0
            makeSocketAddress: {
2026
0
                try SocketAddress(unixDomainSocketPath: unixDomainSocketPath)
2027
0
            },
2028
0
            channelInitializer: channelInitializer,
2029
0
            postRegisterTransformation: { output, eventLoop in
2030
0
                eventLoop.makeSucceededFuture(output)
2031
0
            }
2032
0
        )
2033
0
    }
2034
2035
    /// Connect the `DatagramChannel` to `host` and `port`.
2036
    ///
2037
    /// - Parameters:
2038
    ///   - host: The host to connect to.
2039
    ///   - port: The port to connect to.
2040
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
2041
    ///   method.
2042
    /// - Returns: The result of the channel initializer.
2043
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2044
    public func connect<Output: Sendable>(
2045
        host: String,
2046
        port: Int,
2047
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
2048
0
    ) async throws -> Output {
2049
0
        try await self.connect0(
2050
0
            makeSocketAddress: {
2051
0
                try SocketAddress.makeAddressResolvingHost(host, port: port)
2052
0
            },
2053
0
            channelInitializer: channelInitializer,
2054
0
            postRegisterTransformation: { output, eventLoop in
2055
0
                eventLoop.makeSucceededFuture(output)
2056
0
            }
2057
0
        )
2058
0
    }
2059
2060
    /// Connect the `DatagramChannel` to the `address`.
2061
    ///
2062
    /// - Parameters:
2063
    ///   - address: The `SocketAddress` to connect to.
2064
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
2065
    ///   method.
2066
    /// - Returns: The result of the channel initializer.
2067
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2068
    public func connect<Output: Sendable>(
2069
        to address: SocketAddress,
2070
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
2071
0
    ) async throws -> Output {
2072
0
        try await self.connect0(
2073
0
            makeSocketAddress: {
2074
0
                address
2075
0
            },
2076
0
            channelInitializer: channelInitializer,
2077
0
            postRegisterTransformation: { output, eventLoop in
2078
0
                eventLoop.makeSucceededFuture(output)
2079
0
            }
2080
0
        )
2081
0
    }
2082
2083
    /// Connect the `DatagramChannel` to the `unixDomainSocketPath`.
2084
    ///
2085
    /// - Parameters:
2086
    ///   - unixDomainSocketPath: The path of the UNIX Domain Socket to connect to. `path` must not exist, it will be created by the system.
2087
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
2088
    ///   method.
2089
    /// - Returns: The result of the channel initializer.
2090
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2091
    public func connect<Output: Sendable>(
2092
        unixDomainSocketPath: String,
2093
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
2094
0
    ) async throws -> Output {
2095
0
        try await self.connect0(
2096
0
            makeSocketAddress: {
2097
0
                try SocketAddress(unixDomainSocketPath: unixDomainSocketPath)
2098
0
            },
2099
0
            channelInitializer: channelInitializer,
2100
0
            postRegisterTransformation: { output, eventLoop in
2101
0
                eventLoop.makeSucceededFuture(output)
2102
0
            }
2103
0
        )
2104
0
    }
2105
2106
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2107
    private func connect0<ChannelInitializerResult: Sendable, PostRegistrationTransformationResult: Sendable>(
2108
        makeSocketAddress: () throws -> SocketAddress,
2109
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>,
2110
        postRegisterTransformation:
2111
            @escaping @Sendable (ChannelInitializerResult, EventLoop) -> EventLoopFuture<
2112
                PostRegistrationTransformationResult
2113
            >
2114
0
    ) async throws -> PostRegistrationTransformationResult {
2115
0
        let address = try makeSocketAddress()
2116
0
        let subtype = self.proto
2117
0
2118
0
        func makeChannel(_ eventLoop: SelectableEventLoop) throws -> DatagramChannel {
2119
0
            try DatagramChannel(
2120
0
                eventLoop: eventLoop,
2121
0
                protocolFamily: address.protocol,
2122
0
                protocolSubtype: subtype
2123
0
            )
2124
0
        }
2125
0
2126
0
        return try await self.makeConfiguredChannel(
2127
0
            makeChannel: makeChannel(_:),
2128
0
            channelInitializer: channelInitializer,
2129
0
            registration: { channel in
2130
0
                channel.register().flatMap {
2131
0
                    channel.connect(to: address)
2132
0
                }
2133
0
            },
2134
0
            postRegisterTransformation: postRegisterTransformation
2135
0
        ).get()
2136
0
    }
2137
2138
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2139
    private func bind0<ChannelInitializerResult: Sendable, PostRegistrationTransformationResult: Sendable>(
2140
        makeSocketAddress: () throws -> SocketAddress,
2141
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>,
2142
        postRegisterTransformation:
2143
            @escaping @Sendable (ChannelInitializerResult, EventLoop) -> EventLoopFuture<
2144
                PostRegistrationTransformationResult
2145
            >
2146
0
    ) async throws -> PostRegistrationTransformationResult {
2147
0
        let address = try makeSocketAddress()
2148
0
        let subtype = self.proto
2149
0
2150
0
        func makeChannel(_ eventLoop: SelectableEventLoop) throws -> DatagramChannel {
2151
0
            try DatagramChannel(
2152
0
                eventLoop: eventLoop,
2153
0
                protocolFamily: address.protocol,
2154
0
                protocolSubtype: subtype
2155
0
            )
2156
0
        }
2157
0
2158
0
        return try await self.makeConfiguredChannel(
2159
0
            makeChannel: makeChannel(_:),
2160
0
            channelInitializer: channelInitializer,
2161
0
            registration: { channel in
2162
0
                channel.register().flatMap {
2163
0
                    channel.bind(to: address)
2164
0
                }
2165
0
            },
2166
0
            postRegisterTransformation: postRegisterTransformation
2167
0
        ).get()
2168
0
    }
2169
2170
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2171
    private func makeConfiguredChannel<
2172
        ChannelInitializerResult: Sendable,
2173
        PostRegistrationTransformationResult: Sendable
2174
    >(
2175
        makeChannel: (_ eventLoop: SelectableEventLoop) throws -> DatagramChannel,
2176
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>,
2177
        registration: @escaping @Sendable (Channel) -> EventLoopFuture<Void>,
2178
        postRegisterTransformation:
2179
            @escaping @Sendable (ChannelInitializerResult, EventLoop) -> EventLoopFuture<
2180
                PostRegistrationTransformationResult
2181
            >
2182
0
    ) -> EventLoopFuture<PostRegistrationTransformationResult> {
2183
0
        let eventLoop = self.group.next()
2184
0
        let bootstrapChannelInitializer =
2185
0
            self.channelInitializer ?? { @Sendable _ in eventLoop.makeSucceededFuture(()) }
Unexecuted instantiation: $s8NIOPosix17DatagramBootstrapC21makeConfiguredChannel33_F13D6D61D4A528D08BD67995505C2487LL0dF018channelInitializer12registration26postRegisterTransformation7NIOCore15EventLoopFutureCyq_GAA0bF0CAA010SelectablevW0CKXE_ALyxGAJ0F0_pYbcALyytGAjS_pYbcAMx_AJ0vW0_ptYbcts8SendableRzsAVR_r0_lFAtjS_pYbcyKXEfu_
Unexecuted instantiation: $s8NIOPosix17DatagramBootstrapC21makeConfiguredChannel33_F13D6D61D4A528D08BD67995505C2487LL0dF018channelInitializer12registration26postRegisterTransformation7NIOCore15EventLoopFutureCyq_GAA0bF0CAA010SelectablevW0CKXE_ALyxGAJ0F0_pYbcALyytGAjS_pYbcAMx_AJ0vW0_ptYbcts8SendableRzsAVR_r0_lFAtjS_pYbcyKXEfu_AtjS_pYbcfU_
2186
0
        let channelInitializer = { @Sendable (channel: Channel) -> EventLoopFuture<ChannelInitializerResult> in
2187
0
            bootstrapChannelInitializer(channel)
2188
0
                .hop(to: channel.eventLoop)
2189
0
                .assumeIsolated()
2190
0
                .flatMap { channelInitializer(channel) }
2191
0
                .nonisolated()
2192
0
        }
2193
0
        let channelOptions = self._channelOptions
2194
0
2195
0
        let channel: DatagramChannel
2196
0
        do {
2197
0
            channel = try makeChannel(eventLoop as! SelectableEventLoop)
2198
0
        } catch {
2199
0
            return eventLoop.makeFailedFuture(error)
2200
0
        }
2201
0
2202
0
        @Sendable
2203
0
        func setupChannel() -> EventLoopFuture<PostRegistrationTransformationResult> {
2204
0
            eventLoop.assertInEventLoop()
2205
0
            return channelOptions.applyAllChannelOptions(to: channel).flatMap {
2206
0
                channelInitializer(channel)
2207
0
            }.flatMap { (result: ChannelInitializerResult) in
2208
0
                eventLoop.assertInEventLoop()
2209
0
                return registration(channel).map {
2210
0
                    result
2211
0
                }
2212
0
            }.flatMap { (result: ChannelInitializerResult) -> EventLoopFuture<PostRegistrationTransformationResult> in
2213
0
                postRegisterTransformation(result, eventLoop)
2214
0
            }.flatMapError { error in
2215
0
                eventLoop.assertInEventLoop()
2216
0
                channel.close0(error: error, mode: .all, promise: nil)
2217
0
                return channel.eventLoop.makeFailedFuture(error)
2218
0
            }
2219
0
        }
2220
0
2221
0
        if eventLoop.inEventLoop {
2222
0
            return setupChannel()
2223
0
        } else {
2224
0
            return eventLoop.flatSubmit {
2225
0
                setupChannel()
2226
0
            }
2227
0
        }
2228
0
    }
2229
}
2230
2231
@available(*, unavailable)
2232
extension DatagramBootstrap: Sendable {}
2233
2234
/// A `NIOPipeBootstrap` is an easy way to bootstrap a `PipeChannel` which uses two (uni-directional) UNIX pipes
2235
/// and makes a `Channel` out of them.
2236
///
2237
/// Example bootstrapping a `Channel` using `stdin` and `stdout`:
2238
///
2239
///     let channel = try NIOPipeBootstrap(group: group)
2240
///                       .channelInitializer { channel in
2241
///                           channel.pipeline.addHandler(MyChannelHandler())
2242
///                       }
2243
///                       .takingOwnershipOfDescriptors(input: STDIN_FILENO, output: STDOUT_FILENO)
2244
///
2245
public final class NIOPipeBootstrap {
2246
    private let group: EventLoopGroup
2247
    private var channelInitializer: Optional<ChannelInitializerCallback>
2248
    @usableFromInline
2249
    internal var _channelOptions: ChannelOptions.Storage
2250
    private let hooks: any NIOPipeBootstrapHooks
2251
2252
    /// Create a `NIOPipeBootstrap` on the `EventLoopGroup` `group`.
2253
    ///
2254
    /// The `EventLoopGroup` `group` must be compatible, otherwise the program will crash. `NIOPipeBootstrap` is
2255
    /// compatible only with `MultiThreadedEventLoopGroup` as well as the `EventLoop`s returned by
2256
    /// `MultiThreadedEventLoopGroup.next`. See `init(validatingGroup:)` for a fallible initializer for
2257
    /// situations where it's impossible to tell ahead of time if the `EventLoopGroup`s are compatible or not.
2258
    ///
2259
    /// - Parameters:
2260
    ///   - group: The `EventLoopGroup` to use.
2261
0
    public convenience init(group: EventLoopGroup) {
2262
0
        guard NIOOnSocketsBootstraps.isCompatible(group: group) else {
2263
0
            preconditionFailure(
2264
0
                "NIOPipeBootstrap is only compatible with MultiThreadedEventLoopGroup and "
2265
0
                    + "SelectableEventLoop. You tried constructing one with \(group) which is incompatible."
2266
0
            )
2267
0
        }
2268
0
        self.init(validatingGroup: group)!
2269
0
    }
2270
2271
    /// Create a `NIOPipeBootstrap` on the `EventLoopGroup` `group`, validating that `group` is compatible.
2272
    ///
2273
    /// - Parameters:
2274
    ///   - group: The `EventLoopGroup` to use.
2275
0
    public init?(validatingGroup group: EventLoopGroup) {
2276
0
        guard NIOOnSocketsBootstraps.isCompatible(group: group) else {
2277
0
            return nil
2278
0
        }
2279
0
2280
0
        self._channelOptions = ChannelOptions.Storage()
2281
0
        self.group = group
2282
0
        self.channelInitializer = nil
2283
0
        self.hooks = DefaultNIOPipeBootstrapHooks()
2284
0
    }
2285
2286
    /// Initialiser for hooked testing
2287
0
    init?(validatingGroup group: EventLoopGroup, hooks: any NIOPipeBootstrapHooks) {
2288
0
        guard NIOOnSocketsBootstraps.isCompatible(group: group) else {
2289
0
            return nil
2290
0
        }
2291
0
2292
0
        self._channelOptions = ChannelOptions.Storage()
2293
0
        self.group = group
2294
0
        self.channelInitializer = nil
2295
0
        self.hooks = hooks
2296
0
    }
2297
2298
    /// Initialize the connected `PipeChannel` with `initializer`. The most common task in initializer is to add
2299
    /// `ChannelHandler`s to the `ChannelPipeline`.
2300
    ///
2301
    /// The connected `Channel` will operate on `ByteBuffer` as inbound and outbound messages. Please note that
2302
    /// `IOData.fileRegion` is _not_ supported for `PipeChannel`s because `sendfile` only works on sockets.
2303
    ///
2304
    /// - Parameters:
2305
    ///   - handler: A closure that initializes the provided `Channel`.
2306
    @preconcurrency
2307
0
    public func channelInitializer(_ handler: @escaping @Sendable (Channel) -> EventLoopFuture<Void>) -> Self {
2308
0
        self.channelInitializer = handler
2309
0
        return self
2310
0
    }
2311
2312
    /// Specifies a `ChannelOption` to be applied to the `PipeChannel`.
2313
    ///
2314
    /// - Parameters:
2315
    ///   - option: The option to be applied.
2316
    ///   - value: The value for the option.
2317
    @inlinable
2318
0
    public func channelOption<Option: ChannelOption>(_ option: Option, value: Option.Value) -> Self {
2319
0
        self._channelOptions.append(key: option, value: value)
2320
0
        return self
2321
0
    }
2322
2323
0
    private func validateFileDescriptorIsNotAFile(_ descriptor: CInt) throws {
2324
        #if os(Windows)
2325
        // NOTE: this is a *non-owning* handle, do *NOT* call `CloseHandle`
2326
        let hFile: HANDLE = HANDLE(bitPattern: _get_osfhandle(descriptor))!
2327
        if hFile == INVALID_HANDLE_VALUE {
2328
            throw IOError(errnoCode: EBADF, reason: "_get_osfhandle")
2329
        }
2330
2331
        // The check here is different from other platforms as the file types on
2332
        // Windows are different.  SOCKETs and files are different domains, and
2333
        // as a result we know that the descriptor is not a socket.  The only
2334
        // other type of file it could be is either character or disk, neither
2335
        // of which support the operations here.
2336
        switch GetFileType(hFile) {
2337
        case DWORD(FILE_TYPE_PIPE):
2338
            break
2339
        default:
2340
            throw ChannelError._operationUnsupported
2341
        }
2342
        #else
2343
0
        var s: stat = .init()
2344
0
        try withUnsafeMutablePointer(to: &s) { ptr in
2345
0
            try Posix.fstat(descriptor: descriptor, outStat: ptr)
2346
0
        }
2347
0
        switch s.st_mode & S_IFMT {
2348
0
        case S_IFREG, S_IFDIR, S_IFLNK, S_IFBLK:
2349
0
            throw ChannelError._operationUnsupported
2350
0
        default:
2351
0
            ()  // Let's default to ok
2352
0
        }
2353
        #endif
2354
0
    }
2355
2356
    /// Create the `PipeChannel` with the provided file descriptor which is used for both input & output.
2357
    ///
2358
    /// This method is useful for specialilsed use-cases where you want to use `NIOPipeBootstrap` for say a serial line.
2359
    ///
2360
    /// - Note: If this method returns a succeeded future, SwiftNIO will close `inputOutput` when the `Channel`
2361
    ///         becomes inactive. You _must not_ do any further operations with `inputOutput`, including `close`.
2362
    ///         If this method returns a failed future, you still own the file descriptor and are responsible for
2363
    ///         closing it.
2364
    ///
2365
    /// - Parameters:
2366
    ///   - inputOutput: The _Unix file descriptor_ for the input & output.
2367
    /// - Returns: an `EventLoopFuture<Channel>` to deliver the `Channel`.
2368
0
    public func takingOwnershipOfDescriptor(inputOutput: CInt) -> EventLoopFuture<Channel> {
2369
        #if os(Windows)
2370
        fatalError(missingPipeSupportWindows)
2371
        #else
2372
0
        let inputFD = inputOutput
2373
0
        let outputFD = try! Posix.dup(descriptor: inputOutput)
2374
0
2375
0
        return self.takingOwnershipOfDescriptors(input: inputFD, output: outputFD).flatMapErrorThrowing { error in
2376
0
            try! Posix.close(descriptor: outputFD)
2377
0
            throw error
2378
0
        }
2379
        #endif
2380
0
    }
2381
2382
    /// Create the `PipeChannel` with the provided input and output file descriptors.
2383
    ///
2384
    /// The input and output file descriptors must be distinct. If you have a single file descriptor, consider using
2385
    /// `ClientBootstrap.withConnectedSocket(descriptor:)` if it's a socket or
2386
    /// `NIOPipeBootstrap.takingOwnershipOfDescriptor` if it is not a socket.
2387
    ///
2388
    /// - Note: If this method returns a succeeded future, SwiftNIO will close `input` and `output`
2389
    ///         when the `Channel` becomes inactive. You _must not_ do any further operations `input` or
2390
    ///         `output`, including `close`.
2391
    ///         If this method returns a failed future, you still own the file descriptors and are responsible for
2392
    ///         closing them.
2393
    ///
2394
    /// - Parameters:
2395
    ///   - input: The _Unix file descriptor_ for the input (ie. the read side).
2396
    ///   - output: The _Unix file descriptor_ for the output (ie. the write side).
2397
    /// - Returns: an `EventLoopFuture<Channel>` to deliver the `Channel`.
2398
0
    public func takingOwnershipOfDescriptors(input: CInt, output: CInt) -> EventLoopFuture<Channel> {
2399
0
        self._takingOwnershipOfDescriptors(input: input, output: output)
2400
0
    }
2401
2402
    /// Create the `PipeChannel` with the provided input file descriptor.
2403
    ///
2404
    /// The input file descriptor must be distinct.
2405
    ///
2406
    /// - Note: If this method returns a succeeded future, SwiftNIO will close `input` when the `Channel`
2407
    ///         becomes inactive. You _must not_ do any further operations to `input`, including `close`.
2408
    ///         If this method returns a failed future, you still own the file descriptor and are responsible for
2409
    ///         closing them.
2410
    ///
2411
    /// - Parameters:
2412
    ///   - input: The _Unix file descriptor_ for the input (ie. the read side).
2413
    /// - Returns: an `EventLoopFuture<Channel>` to deliver the `Channel`.
2414
    public func takingOwnershipOfDescriptor(
2415
        input: CInt
2416
0
    ) -> EventLoopFuture<Channel> {
2417
0
        self._takingOwnershipOfDescriptors(input: input, output: nil)
2418
0
    }
2419
2420
    /// Create the `PipeChannel` with the provided output file descriptor.
2421
    ///
2422
    /// The output file descriptor must be distinct.
2423
    ///
2424
    /// - Note: If this method returns a succeeded future, SwiftNIO will close `output` when the `Channel`
2425
    ///         becomes inactive. You _must not_ do any further operations to `output`, including `close`.
2426
    ///         If this method returns a failed future, you still own the file descriptor and are responsible for
2427
    ///         closing them.
2428
    ///
2429
    /// - Parameters:
2430
    ///   - output: The _Unix file descriptor_ for the output (ie. the write side).
2431
    /// - Returns: an `EventLoopFuture<Channel>` to deliver the `Channel`.
2432
    public func takingOwnershipOfDescriptor(
2433
        output: CInt
2434
0
    ) -> EventLoopFuture<Channel> {
2435
0
        self._takingOwnershipOfDescriptors(input: nil, output: output)
2436
0
    }
2437
2438
0
    private func _takingOwnershipOfDescriptors(input: CInt?, output: CInt?) -> EventLoopFuture<Channel> {
2439
0
        self._takingOwnershipOfDescriptors(
2440
0
            input: input,
2441
0
            output: output
2442
0
        ) { channel in
2443
0
            channel.eventLoop.makeSucceededFuture(channel)
2444
0
        }
2445
0
    }
2446
2447
    @available(*, deprecated, renamed: "takingOwnershipOfDescriptor(inputOutput:)")
2448
0
    public func withInputOutputDescriptor(_ fileDescriptor: CInt) -> EventLoopFuture<Channel> {
2449
0
        self.takingOwnershipOfDescriptor(inputOutput: fileDescriptor)
2450
0
    }
2451
2452
    @available(*, deprecated, renamed: "takingOwnershipOfDescriptors(input:output:)")
2453
0
    public func withPipes(inputDescriptor: CInt, outputDescriptor: CInt) -> EventLoopFuture<Channel> {
2454
0
        self.takingOwnershipOfDescriptors(input: inputDescriptor, output: outputDescriptor)
2455
0
    }
2456
}
2457
2458
// MARK: Arbitrary payload
2459
2460
extension NIOPipeBootstrap {
2461
    /// Create the `PipeChannel` with the provided file descriptor which is used for both input & output.
2462
    ///
2463
    /// This method is useful for specialilsed use-cases where you want to use `NIOPipeBootstrap` for say a serial line.
2464
    ///
2465
    /// - Note: If this method returns a succeeded future, SwiftNIO will close `inputOutput` when the `Channel`
2466
    ///         becomes inactive. You _must not_ do any further operations with `inputOutput`, including `close`.
2467
    ///         If this method returns a failed future, you still own the file descriptor and are responsible for
2468
    ///         closing it.
2469
    ///
2470
    /// - Parameters:
2471
    ///   - inputOutput: The _Unix file descriptor_ for the input & output.
2472
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
2473
    ///   method.
2474
    /// - Returns: The result of the channel initializer.
2475
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2476
    public func takingOwnershipOfDescriptor<Output: Sendable>(
2477
        inputOutput: CInt,
2478
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
2479
0
    ) async throws -> Output {
2480
        #if os(Windows)
2481
        fatalError(missingPipeSupportWindows)
2482
        #else
2483
0
        let inputFD = inputOutput
2484
0
        let outputFD = try! Posix.dup(descriptor: inputOutput)
2485
0
2486
0
        do {
2487
0
            return try await self.takingOwnershipOfDescriptors(
2488
0
                input: inputFD,
2489
0
                output: outputFD,
2490
0
                channelInitializer: channelInitializer
2491
0
            )
2492
0
        } catch {
2493
0
            try! Posix.close(descriptor: outputFD)
2494
0
            throw error
2495
0
        }
2496
        #endif
2497
0
    }
2498
2499
    /// Create the `PipeChannel` with the provided input and output file descriptors.
2500
    ///
2501
    /// The input and output file descriptors must be distinct.
2502
    ///
2503
    /// - Note: If this method returns a succeeded future, SwiftNIO will close `input` and `output`
2504
    ///         when the `Channel` becomes inactive. You _must not_ do any further operations `input` or
2505
    ///         `output`, including `close`.
2506
    ///         If this method returns a failed future, you still own the file descriptors and are responsible for
2507
    ///         closing them.
2508
    ///
2509
    /// - Parameters:
2510
    ///   - input: The _Unix file descriptor_ for the input (ie. the read side).
2511
    ///   - output: The _Unix file descriptor_ for the output (ie. the write side).
2512
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
2513
    ///   method.
2514
    /// - Returns: The result of the channel initializer.
2515
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2516
    public func takingOwnershipOfDescriptors<Output: Sendable>(
2517
        input: CInt,
2518
        output: CInt,
2519
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
2520
0
    ) async throws -> Output {
2521
0
        try await self._takingOwnershipOfDescriptors(
2522
0
            input: input,
2523
0
            output: output,
2524
0
            channelInitializer: channelInitializer
2525
0
        )
2526
0
    }
2527
2528
    /// Create the `PipeChannel` with the provided input file descriptor.
2529
    ///
2530
    /// The input file descriptor must be distinct.
2531
    ///
2532
    /// - Note: If this method returns a succeeded future, SwiftNIO will close `input` when the `Channel`
2533
    ///         becomes inactive. You _must not_ do any further operations to `input`, including `close`.
2534
    ///         If this method returns a failed future, you still own the file descriptor and are responsible for
2535
    ///         closing them.
2536
    ///
2537
    /// - Parameters:
2538
    ///   - input: The _Unix file descriptor_ for the input (ie. the read side).
2539
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
2540
    ///   method.
2541
    /// - Returns: The result of the channel initializer.
2542
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2543
    public func takingOwnershipOfDescriptor<Output: Sendable>(
2544
        input: CInt,
2545
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
2546
0
    ) async throws -> Output {
2547
0
        try await self._takingOwnershipOfDescriptors(
2548
0
            input: input,
2549
0
            output: nil,
2550
0
            channelInitializer: channelInitializer
2551
0
        )
2552
0
    }
2553
2554
    /// Create the `PipeChannel` with the provided output file descriptor.
2555
    ///
2556
    /// The output file descriptor must be distinct.
2557
    ///
2558
    /// - Note: If this method returns a succeeded future, SwiftNIO will close `output` when the `Channel`
2559
    ///         becomes inactive. You _must not_ do any further operations to `output`, including `close`.
2560
    ///         If this method returns a failed future, you still own the file descriptor and are responsible for
2561
    ///         closing them.
2562
    ///
2563
    /// - Parameters:
2564
    ///   - output: The _Unix file descriptor_ for the output (ie. the write side).
2565
    ///   - channelInitializer: A closure to initialize the channel. The return value of this closure is returned from the `connect`
2566
    ///   method.
2567
    /// - Returns: The result of the channel initializer.
2568
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2569
    public func takingOwnershipOfDescriptor<Output: Sendable>(
2570
        output: CInt,
2571
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
2572
0
    ) async throws -> Output {
2573
0
        try await self._takingOwnershipOfDescriptors(
2574
0
            input: nil,
2575
0
            output: output,
2576
0
            channelInitializer: channelInitializer
2577
0
        )
2578
0
    }
2579
2580
    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2581
    func _takingOwnershipOfDescriptors<ChannelInitializerResult: Sendable>(
2582
        input: CInt?,
2583
        output: CInt?,
2584
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>
2585
0
    ) async throws -> ChannelInitializerResult {
2586
0
        try await self._takingOwnershipOfDescriptors(
2587
0
            input: input,
2588
0
            output: output,
2589
0
            channelInitializer: channelInitializer
2590
0
        ).get()
2591
0
    }
2592
2593
    func _takingOwnershipOfDescriptors<ChannelInitializerResult: Sendable>(
2594
        input: CInt?,
2595
        output: CInt?,
2596
        channelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<ChannelInitializerResult>
2597
0
    ) -> EventLoopFuture<ChannelInitializerResult> {
2598
        #if os(Windows)
2599
        fatalError(missingPipeSupportWindows)
2600
        #else
2601
0
        precondition(
2602
0
            input ?? 0 >= 0 && output ?? 0 >= 0 && input != output,
2603
0
            "illegal file descriptor pair. The file descriptors \(String(describing: input)), \(String(describing: output)) "
2604
0
                + "must be distinct and both positive integers."
2605
0
        )
2606
0
        precondition(!(input == nil && output == nil), "Either input or output has to be set")
2607
0
        let eventLoop = group.next()
2608
0
        let channelOptions = self._channelOptions
2609
0
2610
0
        let channel: PipeChannel
2611
0
        let pipeChannelInput: SelectablePipeHandle?
2612
0
        let pipeChannelOutput: SelectablePipeHandle?
2613
0
        let hasNoInputPipe: Bool
2614
0
        let hasNoOutputPipe: Bool
2615
0
        let bootstrapChannelInitializer = self.channelInitializer
2616
0
        do {
2617
0
            if let input = input {
2618
0
                try self.validateFileDescriptorIsNotAFile(input)
2619
0
            }
2620
0
            if let output = output {
2621
0
                try self.validateFileDescriptorIsNotAFile(output)
2622
0
            }
2623
0
2624
0
            pipeChannelInput = input.flatMap { SelectablePipeHandle(takingOwnershipOfDescriptor: $0) }
2625
0
            pipeChannelOutput = output.flatMap { SelectablePipeHandle(takingOwnershipOfDescriptor: $0) }
2626
0
            hasNoInputPipe = pipeChannelInput == nil
2627
0
            hasNoOutputPipe = pipeChannelOutput == nil
2628
0
            do {
2629
0
                channel = try self.hooks.makePipeChannel(
2630
0
                    eventLoop: eventLoop as! SelectableEventLoop,
2631
0
                    input: pipeChannelInput,
2632
0
                    output: pipeChannelOutput
2633
0
                )
2634
0
            } catch {
2635
0
                // Release file handles back to the caller in case of failure.
2636
0
                _ = try? pipeChannelInput?.takeDescriptorOwnership()
2637
0
                _ = try? pipeChannelOutput?.takeDescriptorOwnership()
2638
0
                throw error
2639
0
            }
2640
0
        } catch {
2641
0
            return eventLoop.makeFailedFuture(error)
2642
0
        }
2643
0
2644
0
        @Sendable
2645
0
        func setupChannel() -> EventLoopFuture<ChannelInitializerResult> {
2646
0
            eventLoop.assertInEventLoop()
2647
0
            return channelOptions.applyAllChannelOptions(to: channel).flatMap {
2648
0
                if let bootstrapChannelInitializer {
2649
0
                    bootstrapChannelInitializer(channel)
2650
0
                } else {
2651
0
                    channel.eventLoop.makeSucceededVoidFuture()
2652
0
                }
2653
0
            }
2654
0
            .flatMap {
2655
0
                _ -> EventLoopFuture<ChannelInitializerResult> in
2656
0
                channelInitializer(channel)
2657
0
            }.flatMap { result in
2658
0
                eventLoop.assertInEventLoop()
2659
0
                let promise = eventLoop.makePromise(of: Void.self)
2660
0
                channel.registerAlreadyConfigured0(promise: promise)
2661
0
                return promise.futureResult.map { result }
2662
0
            }.flatMap { result -> EventLoopFuture<ChannelInitializerResult> in
2663
0
                if hasNoInputPipe {
2664
0
                    return channel.close(mode: .input).map { result }
2665
0
                }
2666
0
                if hasNoOutputPipe {
2667
0
                    return channel.close(mode: .output).map { result }
2668
0
                }
2669
0
                return channel.selectableEventLoop.makeSucceededFuture(result)
2670
0
            }.flatMapError { error in
2671
0
                channel.close0(error: error, mode: .all, promise: nil)
2672
0
                return channel.eventLoop.makeFailedFuture(error)
2673
0
            }
2674
0
        }
2675
0
2676
0
        if eventLoop.inEventLoop {
2677
0
            return setupChannel()
2678
0
        } else {
2679
0
            return eventLoop.flatSubmit {
2680
0
                setupChannel()
2681
0
            }
2682
0
        }
2683
        #endif
2684
0
    }
2685
}
2686
2687
@available(*, unavailable)
2688
extension NIOPipeBootstrap: Sendable {}
2689
2690
protocol NIOPipeBootstrapHooks {
2691
    func makePipeChannel(
2692
        eventLoop: SelectableEventLoop,
2693
        input: SelectablePipeHandle?,
2694
        output: SelectablePipeHandle?
2695
    ) throws -> PipeChannel
2696
}
2697
2698
private struct DefaultNIOPipeBootstrapHooks: NIOPipeBootstrapHooks {
2699
    func makePipeChannel(
2700
        eventLoop: SelectableEventLoop,
2701
        input: SelectablePipeHandle?,
2702
        output: SelectablePipeHandle?
2703
0
    ) throws -> PipeChannel {
2704
0
        try PipeChannel(eventLoop: eventLoop, input: input, output: output)
2705
0
    }
2706
}
2707
#endif  // !os(WASI)