File: ServerSocket.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (133 lines) | stat: -rw-r--r-- 5,136 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

/// A server socket that can accept new connections.
/* final but tests */ class ServerSocket: BaseSocket, ServerSocketProtocol {
    typealias SocketType = ServerSocket
    private let cleanupOnClose: Bool

    public final class func bootstrap(protocolFamily: NIOBSDSocket.ProtocolFamily, host: String, port: Int) throws -> ServerSocket {
        let socket = try ServerSocket(protocolFamily: protocolFamily)
        try socket.bind(to: SocketAddress.makeAddressResolvingHost(host, port: port))
        try socket.listen()
        return socket
    }

    /// Create a new instance.
    ///
    /// - parameters:
    ///     - protocolFamily: The protocol family to use (usually `AF_INET6` or `AF_INET`).
    ///     - setNonBlocking: Set non-blocking mode on the socket.
    /// - throws: An `IOError` if creation of the socket failed.
    init(protocolFamily: NIOBSDSocket.ProtocolFamily, setNonBlocking: Bool = false) throws {
        let sock = try BaseSocket.makeSocket(protocolFamily: protocolFamily, type: .stream, setNonBlocking: setNonBlocking)
        switch protocolFamily {
        case .unix:
            cleanupOnClose = true
        default:
            cleanupOnClose = false
        }
        try super.init(socket: sock)
    }

    /// Create a new instance.
    ///
    /// - parameters:
    ///     - descriptor: The _Unix file descriptor_ representing the bound socket.
    ///     - setNonBlocking: Set non-blocking mode on the socket.
    /// - throws: An `IOError` if socket is invalid.
    #if !os(Windows)
        @available(*, deprecated, renamed: "init(socket:setNonBlocking:)")
        convenience init(descriptor: CInt, setNonBlocking: Bool = false) throws {
          try self.init(socket: descriptor, setNonBlocking: setNonBlocking)
        }
    #endif

    /// Create a new instance.
    ///
    /// - parameters:
    ///     - descriptor: The _Unix file descriptor_ representing the bound socket.
    ///     - setNonBlocking: Set non-blocking mode on the socket.
    /// - throws: An `IOError` if socket is invalid.
    init(socket: NIOBSDSocket.Handle, setNonBlocking: Bool = false) throws {
        cleanupOnClose = false  // socket already bound, owner must clean up
        try super.init(socket: socket)
        if setNonBlocking {
            try self.setNonBlocking()
        }
    }

    /// Start to listen for new connections.
    ///
    /// - parameters:
    ///     - backlog: The backlog to use.
    /// - throws: An `IOError` if creation of the socket failed.
    func listen(backlog: Int32 = 128) throws {
        try withUnsafeHandle {
            _ = try NIOBSDSocket.listen(socket: $0, backlog: backlog)
        }
    }

    /// Accept a new connection
    ///
    /// - parameters:
    ///     - setNonBlocking: set non-blocking mode on the returned `Socket`. On Linux this will use accept4 with SOCK_NONBLOCK to save a system call.
    /// - returns: A `Socket` once a new connection was established or `nil` if this `ServerSocket` is in non-blocking mode and there is no new connection that can be accepted when this method is called.
    /// - throws: An `IOError` if the operation failed.
    func accept(setNonBlocking: Bool = false) throws -> Socket? {
        return try withUnsafeHandle { fd in
            #if os(Linux)
            let flags: Int32
            if setNonBlocking {
                flags = Linux.SOCK_NONBLOCK
            } else {
                flags = 0
            }
            let result = try Linux.accept4(descriptor: fd, addr: nil, len: nil, flags: flags)
            #else
            let result = try NIOBSDSocket.accept(socket: fd, address: nil, address_len: nil)
            #endif

            guard let fd = result else {
                return nil
            }
            let sock = try Socket(socket: fd)
            #if !os(Linux)
            if setNonBlocking {
                do {
                    try sock.setNonBlocking()
                } catch {
                    // best effort
                    try? sock.close()
                    throw error
                }
            }
            #endif
            return sock
        }
    }
    
    /// Close the socket.
    ///
    /// After the socket was closed all other methods will throw an `IOError` when called.
    ///
    /// - throws: An `IOError` if the operation failed.
    override func close() throws {
        let maybePathname = self.cleanupOnClose ? (try? self.localAddress().pathname) : nil
        try super.close()
        if let socketPath = maybePathname {
            try BaseSocket.cleanupSocket(unixDomainSocketPath: socketPath)
        }
    }
}