Coverage Report

Created: 2025-09-08 06:42

/src/swift-nio/Sources/NIOPosix/SocketProtocols.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-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
import NIOCore
15
16
#if canImport(WinSDK)
17
import struct WinSDK.socklen_t
18
#endif
19
20
protocol BaseSocketProtocol: CustomStringConvertible, _NIOPosixSendableMetatype {
21
    associatedtype SelectableType: Selectable
22
23
    var isOpen: Bool { get }
24
25
    func close() throws
26
27
    func bind(to address: SocketAddress) throws
28
29
    func localAddress() throws -> SocketAddress
30
31
    func remoteAddress() throws -> SocketAddress
32
33
    func setOption<T>(level: NIOBSDSocket.OptionLevel, name: NIOBSDSocket.Option, value: T) throws
34
35
    func getOption<T>(level: NIOBSDSocket.OptionLevel, name: NIOBSDSocket.Option) throws -> T
36
}
37
38
protocol ServerSocketProtocol: BaseSocketProtocol {
39
    func listen(backlog: Int32) throws
40
41
    func accept(setNonBlocking: Bool) throws -> Socket?
42
}
43
44
protocol SocketProtocol: BaseSocketProtocol {
45
    func connect(to address: SocketAddress) throws -> Bool
46
47
    func finishConnect() throws
48
49
    func write(pointer: UnsafeRawBufferPointer) throws -> IOResult<Int>
50
51
    func writev(iovecs: UnsafeBufferPointer<IOVector>) throws -> IOResult<Int>
52
53
    func read(pointer: UnsafeMutableRawBufferPointer) throws -> IOResult<Int>
54
55
    func recvmsg(
56
        pointer: UnsafeMutableRawBufferPointer,
57
        storage: inout sockaddr_storage,
58
        storageLen: inout socklen_t,
59
        controlBytes: inout UnsafeReceivedControlBytes
60
    ) throws -> IOResult<Int>
61
62
    func sendmsg(
63
        pointer: UnsafeRawBufferPointer,
64
        destinationPtr: UnsafePointer<sockaddr>?,
65
        destinationSize: socklen_t,
66
        controlBytes: UnsafeMutableRawBufferPointer
67
    ) throws -> IOResult<Int>
68
69
    func sendFile(fd: CInt, offset: Int, count: Int) throws -> IOResult<Int>
70
71
    func recvmmsg(msgs: UnsafeMutableBufferPointer<MMsgHdr>) throws -> IOResult<Int>
72
73
    func sendmmsg(msgs: UnsafeMutableBufferPointer<MMsgHdr>) throws -> IOResult<Int>
74
75
    func shutdown(how: Shutdown) throws
76
77
    func ignoreSIGPIPE() throws
78
}
79
80
#if os(Linux) || os(Android)
81
// This is a lazily initialised global variable that when read for the first time, will ignore SIGPIPE.
82
0
private let globallyIgnoredSIGPIPE: Bool = {
83
0
    // no F_SETNOSIGPIPE on Linux :(
84
0
    #if canImport(Glibc)
85
0
    _ = Glibc.signal(SIGPIPE, SIG_IGN)
86
0
    #elseif canImport(Musl)
87
0
    _ = Musl.signal(SIGPIPE, SIG_IGN)
88
0
    #elseif canImport(Android)
89
0
    _ = Android.signal(SIGPIPE, SIG_IGN)
90
0
    #else
91
0
    #error("Don't know which stdlib to use")
92
0
    #endif
93
0
    return true
94
0
}()
95
#endif
96
97
extension BaseSocketProtocol {
98
    // used by `BaseSocket` and `PipePair`.
99
0
    internal static func ignoreSIGPIPE(descriptor fd: CInt) throws {
100
0
        #if os(Linux) || os(Android)
101
0
        let haveWeIgnoredSIGPIEThisIsHereToTriggerIgnoringIt = globallyIgnoredSIGPIPE
102
0
        guard haveWeIgnoredSIGPIEThisIsHereToTriggerIgnoringIt else {
103
0
            fatalError("BUG in NIO. We did not ignore SIGPIPE, this code path should definitely not be reachable.")
104
0
        }
105
0
        #elseif os(Windows)
106
0
        // Deliberately empty: SIGPIPE just ain't a thing on Windows
107
0
        #else
108
0
        assert(fd >= 0, "illegal file descriptor \(fd)")
109
0
        do {
110
0
            try Posix.fcntl(descriptor: fd, command: F_SETNOSIGPIPE, value: 1)
111
0
        } catch let error as IOError {
112
0
            try? Posix.close(descriptor: fd)  // don't care about failure here
113
0
            if error.errnoCode == EINVAL {
114
0
                // Darwin seems to sometimes do this despite the docs claiming it can't happen
115
0
                throw NIOFcntlFailedError()
116
0
            }
117
0
            throw error
118
0
        }
119
0
        #endif
120
0
    }
121
122
0
    internal static func ignoreSIGPIPE(socket handle: NIOBSDSocket.Handle) throws {
123
0
        #if os(Windows)
124
0
        // Deliberately empty: SIGPIPE just ain't a thing on Windows
125
0
        #else
126
0
        try ignoreSIGPIPE(descriptor: handle)
127
0
        #endif
128
0
    }
129
}