Coverage Report

Created: 2025-09-08 06:42

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