/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) |