Coverage Report

Created: 2026-04-29 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-nio/Sources/NIOPosix/PipePair.swift
Line
Count
Source
1
//===----------------------------------------------------------------------===//
2
//
3
// This source file is part of the SwiftNIO open source project
4
//
5
// Copyright (c) 2019-2021 Apple Inc. and the SwiftNIO project authors
6
// Licensed under Apache License v2.0
7
//
8
// See LICENSE.txt for license information
9
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
10
//
11
// SPDX-License-Identifier: Apache-2.0
12
//
13
//===----------------------------------------------------------------------===//
14
15
#if !os(WASI)
16
17
import NIOCore
18
19
#if canImport(WinSDK)
20
import struct WinSDK.socklen_t
21
#endif
22
23
final class SelectablePipeHandle {
24
    var fileDescriptor: NIOBSDSocket.Handle
25
26
0
    var isOpen: Bool {
27
0
        self.fileDescriptor >= 0
28
0
    }
29
30
0
    init(takingOwnershipOfDescriptor fd: NIOBSDSocket.Handle) {
31
0
        precondition(fd >= 0)
32
0
        self.fileDescriptor = fd
33
0
    }
34
35
0
    func close() throws {
36
        #if os(Windows)
37
        fatalError(missingPipeSupportWindows)
38
        #else
39
0
        let fd = try self.takeDescriptorOwnership()
40
0
        try Posix.close(descriptor: fd)
41
        #endif
42
0
    }
43
44
0
    func takeDescriptorOwnership() throws -> NIOBSDSocket.Handle {
45
        #if os(Windows)
46
        fatalError(missingPipeSupportWindows)
47
        #else
48
0
        guard self.isOpen else {
49
0
            throw IOError(errnoCode: EBADF, reason: "SelectablePipeHandle already closed [in close]")
50
0
        }
51
0
        defer {
52
0
            self.fileDescriptor = -1
53
0
        }
54
0
        return self.fileDescriptor
55
        #endif
56
0
    }
57
58
0
    deinit {
59
0
        assert(!self.isOpen, "leaking \(self)")
60
0
    }
61
}
62
63
extension SelectablePipeHandle: Selectable {
64
0
    func withUnsafeHandle<T>(_ body: (NIOBSDSocket.Handle) throws -> T) throws -> T {
65
0
        guard self.isOpen else {
66
0
            throw IOError(errnoCode: EBADF, reason: "SelectablePipeHandle already closed [in wUH]")
67
0
        }
68
0
        return try body(self.fileDescriptor)
69
0
    }
70
}
71
72
extension SelectablePipeHandle: CustomStringConvertible {
73
0
    public var description: String {
74
0
        "SelectableFileHandle(isOpen: \(self.isOpen), fd: \(self.fileDescriptor))"
75
0
    }
76
}
77
78
final class PipePair: SocketProtocol {
79
    typealias SelectableType = SelectablePipeHandle
80
81
    let input: SelectablePipeHandle?
82
    let output: SelectablePipeHandle?
83
84
0
    init(input: SelectablePipeHandle?, output: SelectablePipeHandle?) throws {
85
        #if os(Windows)
86
        fatalError(missingPipeSupportWindows)
87
        #else
88
0
        self.input = input
89
0
        self.output = output
90
0
        try self.ignoreSIGPIPE()
91
0
        for fh in [input, output].compactMap({ $0 }) {
92
0
            try fh.withUnsafeHandle { fd in
93
0
                try NIOFileHandle.setNonBlocking(fileDescriptor: fd)
94
0
            }
95
0
        }
96
        #endif
97
0
    }
98
99
0
    func ignoreSIGPIPE() throws {
100
        #if os(Windows)
101
        fatalError(missingPipeSupportWindows)
102
        #else
103
0
        for fileHandle in [self.input, self.output].compactMap({ $0 }) {
104
0
            try fileHandle.withUnsafeHandle {
105
0
                try PipePair.ignoreSIGPIPE(descriptor: $0)
106
0
            }
107
0
        }
108
        #endif
109
0
    }
110
111
0
    var description: String {
112
0
        "PipePair { in=\(String(describing: self.input)), out=\(String(describing: self.output)) }"
113
0
    }
114
115
0
    func connect(to address: SocketAddress) throws -> Bool {
116
0
        throw ChannelError._operationUnsupported
117
0
    }
118
119
0
    func finishConnect() throws {
120
0
        throw ChannelError._operationUnsupported
121
0
    }
122
123
0
    func write(pointer: UnsafeRawBufferPointer) throws -> IOResult<Int> {
124
        #if os(Windows)
125
        fatalError(missingPipeSupportWindows)
126
        #else
127
0
        guard let outputSPH = self.output else {
128
0
            fatalError("Internal inconsistency inside NIO: outputSPH closed on write. Please file a bug")
129
0
        }
130
0
        return try outputSPH.withUnsafeHandle {
131
0
            try Posix.write(descriptor: $0, pointer: pointer.baseAddress!, size: pointer.count)
132
0
        }
133
        #endif
134
0
    }
135
136
0
    func writev(iovecs: UnsafeBufferPointer<IOVector>) throws -> IOResult<Int> {
137
        #if os(Windows)
138
        fatalError(missingPipeSupportWindows)
139
        #else
140
0
        guard let outputSPH = self.output else {
141
0
            fatalError("Internal inconsistency inside NIO: outputSPH closed on writev. Please file a bug")
142
0
        }
143
0
        return try outputSPH.withUnsafeHandle {
144
0
            try Posix.writev(descriptor: $0, iovecs: iovecs)
145
0
        }
146
        #endif
147
0
    }
148
149
0
    func read(pointer: UnsafeMutableRawBufferPointer) throws -> IOResult<Int> {
150
        #if os(Windows)
151
        fatalError(missingPipeSupportWindows)
152
        #else
153
0
        guard let inputSPH = self.input else {
154
0
            fatalError("Internal inconsistency inside NIO: inputSPH closed on read. Please file a bug")
155
0
        }
156
0
        return try inputSPH.withUnsafeHandle {
157
0
            try Posix.read(descriptor: $0, pointer: pointer.baseAddress!, size: pointer.count)
158
0
        }
159
        #endif
160
0
    }
161
162
    func recvmsg(
163
        pointer: UnsafeMutableRawBufferPointer,
164
        storage: inout sockaddr_storage,
165
        storageLen: inout socklen_t,
166
        controlBytes: inout UnsafeReceivedControlBytes
167
0
    ) throws -> IOResult<Int> {
168
0
        throw ChannelError._operationUnsupported
169
0
    }
170
171
    func sendmsg(
172
        pointer: UnsafeRawBufferPointer,
173
        destinationPtr: UnsafePointer<sockaddr>?,
174
        destinationSize: socklen_t,
175
        controlBytes: UnsafeMutableRawBufferPointer
176
0
    ) throws -> IOResult<Int> {
177
0
        throw ChannelError._operationUnsupported
178
0
    }
179
180
0
    func sendFile(fd: CInt, offset: Int, count: Int) throws -> IOResult<Int> {
181
0
        throw ChannelError._operationUnsupported
182
0
    }
183
184
0
    func recvmmsg(msgs: UnsafeMutableBufferPointer<MMsgHdr>) throws -> IOResult<Int> {
185
0
        throw ChannelError._operationUnsupported
186
0
    }
187
188
0
    func sendmmsg(msgs: UnsafeMutableBufferPointer<MMsgHdr>) throws -> IOResult<Int> {
189
0
        throw ChannelError._operationUnsupported
190
0
    }
191
192
0
    func shutdown(how: Shutdown) throws {
193
0
        switch how {
194
0
        case .RD:
195
0
            try self.input?.close()
196
0
        case .WR:
197
0
            try self.output?.close()
198
0
        case .RDWR:
199
0
            try self.close()
200
0
        }
201
0
    }
202
203
0
    var isOpen: Bool {
204
0
        self.input?.isOpen ?? false || self.output?.isOpen ?? false
205
0
    }
206
207
0
    func close() throws {
208
0
        guard self.isOpen else {
209
0
            throw ChannelError._alreadyClosed
210
0
        }
211
0
        let r1 = Result {
212
0
            if let inputFD = self.input, inputFD.isOpen {
213
0
                try inputFD.close()
214
0
            }
215
0
        }
216
0
        let r2 = Result {
217
0
            if let outputFD = self.output, outputFD.isOpen {
218
0
                try outputFD.close()
219
0
            }
220
0
        }
221
0
        try r1.get()
222
0
        try r2.get()
223
0
    }
224
225
0
    func bind(to address: SocketAddress) throws {
226
0
        throw ChannelError._operationUnsupported
227
0
    }
228
229
0
    func localAddress() throws -> SocketAddress {
230
0
        throw ChannelError._operationUnsupported
231
0
    }
232
233
0
    func remoteAddress() throws -> SocketAddress {
234
0
        throw ChannelError._operationUnsupported
235
0
    }
236
237
0
    func setOption<T>(level: NIOBSDSocket.OptionLevel, name: NIOBSDSocket.Option, value: T) throws {
238
0
        throw ChannelError._operationUnsupported
239
0
    }
240
241
0
    func getOption<T>(level: NIOBSDSocket.OptionLevel, name: NIOBSDSocket.Option) throws -> T {
242
0
        throw ChannelError._operationUnsupported
243
0
    }
244
}
245
#endif  // !os(WASI)