From 4679cd5bdf5cf23008c3bbda59311c467a26ad57 Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Tue, 21 Jul 2020 01:23:28 +0200 Subject: [PATCH 01/13] Make `Data` available for read/write operations. --- Sources/Connection.swift | 3 +- Sources/FileDescriptor.swift | 6 ++-- Sources/ReadableFileDescriptor.swift | 41 +++++++++++++++----------- Sources/WritableFileDescriptor.swift | 31 +++++++++++-------- Tests/fdTests/FileDescriptorSpec.swift | 4 +-- 5 files changed, 46 insertions(+), 39 deletions(-) diff --git a/Sources/Connection.swift b/Sources/Connection.swift index 74c9364..5659013 100644 --- a/Sources/Connection.swift +++ b/Sources/Connection.swift @@ -1,2 +1 @@ -public protocol Connection : ReadableFileDescriptor, WritableFileDescriptor { -} +public protocol Connection : ReadableFileDescriptor, WritableFileDescriptor {} diff --git a/Sources/FileDescriptor.swift b/Sources/FileDescriptor.swift index 11d82ac..575bffb 100644 --- a/Sources/FileDescriptor.swift +++ b/Sources/FileDescriptor.swift @@ -7,19 +7,17 @@ private let system_close = Darwin.close #endif -public typealias Byte = Int8 +public typealias Byte = UInt8 public typealias FileNumber = Int32 public protocol FileDescriptor { - var fileNumber: FileNumber { get } + var fileNumber: FileNumber { get } } - struct FileDescriptorError : Error { } - extension FileDescriptor { /// Close deletes the file descriptor from the per-process object reference table public func close() throws { diff --git a/Sources/ReadableFileDescriptor.swift b/Sources/ReadableFileDescriptor.swift index 7fa0c98..1f65ea6 100644 --- a/Sources/ReadableFileDescriptor.swift +++ b/Sources/ReadableFileDescriptor.swift @@ -1,3 +1,4 @@ +import Foundation #if os(Linux) import Glibc private let system_read = Glibc.read @@ -6,25 +7,29 @@ import Darwin private let system_read = Darwin.read #endif - -public protocol ReadableFileDescriptor : FileDescriptor { -} - +public protocol ReadableFileDescriptor : FileDescriptor {} extension ReadableFileDescriptor { - /// Read attempts to read the given size from the file descriptor - public func read(_ bufferSize: Int) throws -> [Byte] { - let buffer = UnsafeMutableRawPointer(malloc(bufferSize)) - defer { free(buffer) } - let size = system_read(fileNumber, buffer!, bufferSize) - - if size > 0 { - let readSize = min(size, bufferSize) - var bytes = [Byte](repeating: 0, count: readSize) - memcpy(&bytes, buffer!, readSize) - return bytes + /// Read attempts to read the given size from the file descriptor + public func read(_ bufferSize: Int) throws -> [Byte] { + let buffer = UnsafeMutableRawPointer(malloc(bufferSize)) + defer { free(buffer) } + let size = system_read(fileNumber, buffer!, bufferSize) + + guard size != -1 else { + throw FileDescriptorError(kind: .readError, errno: errno) + } + + let readSize = min(size, bufferSize) + var bytes = [Byte](repeating: 0, count: readSize) + memcpy(&bytes, buffer!, readSize) + return bytes + } + + /// Read attempts to read the given size from the file descriptor + public func read(_ bufferSize: Int) throws -> Data { + let bytes: [Byte] = try read(bufferSize) + + return Data(bytes) } - - throw FileDescriptorError() - } } diff --git a/Sources/WritableFileDescriptor.swift b/Sources/WritableFileDescriptor.swift index d3165a9..31d38d7 100644 --- a/Sources/WritableFileDescriptor.swift +++ b/Sources/WritableFileDescriptor.swift @@ -1,3 +1,4 @@ +import Foundation #if os(Linux) import Glibc private let system_write = Glibc.write @@ -6,20 +7,24 @@ import Darwin private let system_write = Darwin.write #endif - -public protocol WritableFileDescriptor : FileDescriptor { -} - +public protocol WritableFileDescriptor : FileDescriptor {} extension WritableFileDescriptor { - /// Write attemps to write the given bytes to the file descriptor - public func write(_ bytes: [Byte]) throws -> Int { - let size = system_write(fileNumber, bytes, bytes.count) - - if size == -1 { - throw FileDescriptorError() + /// Write attemps to write the given bytes to the file descriptor + public func write(_ bytes: [Byte]) throws -> Int { + let size = system_write(fileNumber, bytes, bytes.count) + + guard size != -1 else { + throw FileDescriptorError(kind: .writeError, errno: errno) + } + + return size + } + + /// Write attemps to write the given data to the file descriptor + public func write(_ data: Data) throws -> Int { + let bytes = [Byte](data) + + return try write(bytes) } - - return size - } } diff --git a/Tests/fdTests/FileDescriptorSpec.swift b/Tests/fdTests/FileDescriptorSpec.swift index db91048..5c061f0 100644 --- a/Tests/fdTests/FileDescriptorSpec.swift +++ b/Tests/fdTests/FileDescriptorSpec.swift @@ -45,7 +45,7 @@ public func testFileDescriptor() { let (read, write) = try pipe() try expect(try write.write([1, 2, 3])) == 3 - let bytes = try read.read(3) + let bytes: [Byte] = try read.read(3) try expect(bytes.count) == 3 try expect(bytes[0]) == 1 try expect(bytes[1]) == 2 @@ -59,7 +59,7 @@ public func testFileDescriptor() { $0.it("errors while reading from an invalid file descriptor") { let descriptor = TestFileDescriptor(fileNumber: -1) - try expect { try descriptor.read(1) }.toThrow() + try expect { try descriptor.read(1) as [Byte] }.toThrow() } } } From 069c7931db837e1c4b51dd1fa73e43709c011136 Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Tue, 21 Jul 2020 01:28:15 +0200 Subject: [PATCH 02/13] Introduce `UNIXSocket` class and split `UNIXListener` into `UNIXServerSocket` and `UNIXClientSocket`. Use custom Errors to make error codes/messages available at top level. Use `Listener` protocol. --- Sources/FileDescriptor.swift | 44 ++++++++++---- Sources/Listener.swift | 2 +- Sources/TCPListener.swift | 105 +++++++++++++++------------------ Sources/UNIXClientSocket.swift | 55 +++++++++++++++++ Sources/UNIXConnection.swift | 10 ++-- Sources/UNIXListener.swift | 77 ------------------------ Sources/UNIXServerSocket.swift | 70 ++++++++++++++++++++++ Sources/UNIXSocket.swift | 57 ++++++++++++++++++ 8 files changed, 268 insertions(+), 152 deletions(-) create mode 100644 Sources/UNIXClientSocket.swift delete mode 100644 Sources/UNIXListener.swift create mode 100644 Sources/UNIXServerSocket.swift create mode 100644 Sources/UNIXSocket.swift diff --git a/Sources/FileDescriptor.swift b/Sources/FileDescriptor.swift index 575bffb..5d4f64d 100644 --- a/Sources/FileDescriptor.swift +++ b/Sources/FileDescriptor.swift @@ -16,21 +16,41 @@ public protocol FileDescriptor { } struct FileDescriptorError : Error { + enum ErrorKind: String { + case readError, writeError, selectError, pipeError, closeError + } + + let kind: ErrorKind + let message: String? + + var localizedDescription: String { + "FileDescriptorError of kind \(kind.rawValue)\(message != nil ? "\nmessage: \(message!)" : "")" + } + + init(kind: ErrorKind, message: String? = nil) { + self.kind = kind + self.message = message + } + + init(kind: ErrorKind, errno: Int32) { + let message = String(utf8String: strerror(errno)) + self.init(kind: kind, message: message) + } } extension FileDescriptor { - /// Close deletes the file descriptor from the per-process object reference table - public func close() throws { - if system_close(fileNumber) == -1 { - throw FileDescriptorError() + /// Close deletes the file descriptor from the per-process object reference table + public func close() throws { + if system_close(fileNumber) == -1 { + throw FileDescriptorError(kind: .closeError, errno: errno) + } } - } - - public var isClosed: Bool { - if fcntl(fileNumber, F_GETFL) == -1 { - return errno == EBADF + + public var isClosed: Bool { + if fcntl(fileNumber, F_GETFL) == -1 { + return errno == EBADF + } + + return false } - - return false - } } diff --git a/Sources/Listener.swift b/Sources/Listener.swift index 60e1846..51936e9 100644 --- a/Sources/Listener.swift +++ b/Sources/Listener.swift @@ -1,3 +1,3 @@ public protocol Listener : FileDescriptor { - func accept() throws -> Connection + func accept() throws -> C } diff --git a/Sources/TCPListener.swift b/Sources/TCPListener.swift index f67f52d..e3bcb0b 100644 --- a/Sources/TCPListener.swift +++ b/Sources/TCPListener.swift @@ -12,67 +12,58 @@ private let system_bind = Darwin.bind private let sock_stream = SOCK_STREAM #endif - -public class TCPListener : FileDescriptor { - public let fileNumber: FileNumber - - init(fileNumber: FileNumber) { - self.fileNumber = fileNumber - } - - public init(address: String, port: UInt16) throws { - fileNumber = socket(AF_INET, sock_stream, 0) - if fileNumber == -1 { - throw FileDescriptorError() +public class TCPListener : UNIXSocket, Listener { + + init(fileNumber: FileNumber) { + self.fileNumber = fileNumber } - - do { - try bind(address, port: port) - } catch { - try close() - throw error + + public init(address: String, port: UInt16) throws { + try super.init(kind: AF_INET) + + do { + try bind(address, port: port) + } catch { + try close() + throw error + } } - } - - deinit { - let _ = try? close() - } - - fileprivate func bind(_ address: String, port: UInt16) throws { - var addr = sockaddr_in() - addr.sin_family = sa_family_t(AF_INET) - addr.sin_port = in_port_t(htons(in_port_t(port))) - addr.sin_addr = in_addr(s_addr: address.withCString { inet_addr($0) }) - addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0) - - let len = socklen_t(UInt8(MemoryLayout.size)) - - try withUnsafePointer(to: &addr) { - try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { - guard system_bind(fileNumber, $0, len) != -1 else { - throw FileDescriptorError() + + private func bind(_ address: String, port: UInt16) throws { + var addr = sockaddr_in() + addr.sin_family = sa_family_t(AF_INET) + addr.sin_port = in_port_t(htons(in_port_t(port))) + addr.sin_addr = in_addr(s_addr: address.withCString { inet_addr($0) }) + addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0) + + let len = socklen_t(UInt8(MemoryLayout.size)) + + try withUnsafePointer(to: &addr) { + try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { + guard system_bind(fileNumber, $0, len) != -1 else { + throw UNIXSocketError(kind: .bindError, errno: errno) + } + } } - } } - } - - fileprivate func listen(backlog: Int32) throws { - if system_listen(fileNumber, backlog) == -1 { - throw FileDescriptorError() + + private func listen(backlog: Int32) throws { + guard system_listen(fileNumber, backlog) != -1 else { + throw UNIXSocketError(kind: .listenError, errno: errno) + } } - } - - fileprivate func htons(_ value: CUnsignedShort) -> CUnsignedShort { - return (value << 8) + (value >> 8) - } - - /// Accepts a connection socket - open func accept() throws -> TCPConnection { - let fileNumber = system_accept(self.fileNumber, nil, nil) - if fileNumber == -1 { - throw FileDescriptorError() + + private func htons(_ value: CUnsignedShort) -> CUnsignedShort { + return (value << 8) + (value >> 8) + } + + /// Accepts a connection socket + public func accept() throws -> TCPConnection { + let fileNumber = system_accept(self.fileNumber, nil, nil) + guard fileNumber != -1 else { + throw UNIXSocketError(kind: .acceptError, errno: errno) + } + + return TCPConnection(fileNumber: fileNumber) } - - return TCPConnection(fileNumber: fileNumber) - } } diff --git a/Sources/UNIXClientSocket.swift b/Sources/UNIXClientSocket.swift new file mode 100644 index 0000000..c83f466 --- /dev/null +++ b/Sources/UNIXClientSocket.swift @@ -0,0 +1,55 @@ +import Foundation +#if os(Linux) +import Glibc +private let system_accept = Glibc.accept +private let system_listen = Glibc.listen +private let system_connect = Glibc.connect +#else +import Darwin +private let system_accept = Darwin.accept +private let system_listen = Darwin.listen +private let system_connect = Darwin.connect +#endif + +public class UNIXClientSocket: UNIXSocket, Listener { + + override public init(path: URL) throws { + try super.init(path: path) + + do { + try connect(path) + } catch { + try close() + throw error + } + } + + private func connect(_ path: URL) throws { + var addr = sockaddr_un() + addr.sun_family = sa_family_t(AF_UNIX) + + let lengthOfPath = path.relativePath.withCString { Int(strlen($0)) } + + guard lengthOfPath < MemoryLayout.size(ofValue: addr.sun_path) else { + throw UNIXSocketError(kind: .pathLength, message: "Path too long!") + } + + _ = withUnsafeMutablePointer(to: &addr.sun_path.0) { pointer in + path.relativePath.withCString { + strncpy(pointer, $0, lengthOfPath) + } + } + + try withUnsafePointer(to: &addr) { pointer in + try pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { + guard system_connect(fileNumber, $0, UInt32(MemoryLayout.stride)) != -1 else { + throw UNIXSocketError(kind: .connectError, errno: errno) + } + } + } + } + + public func accept() throws -> UNIXConnection { + return UNIXConnection(fileNumber: fileNumber) + } +} diff --git a/Sources/UNIXConnection.swift b/Sources/UNIXConnection.swift index 2f59971..cdb4ef4 100644 --- a/Sources/UNIXConnection.swift +++ b/Sources/UNIXConnection.swift @@ -1,7 +1,7 @@ public class UNIXConnection : FileDescriptor, Connection { - public let fileNumber: FileNumber - - init(fileNumber: FileNumber) { - self.fileNumber = fileNumber - } + public let fileNumber: FileNumber + + init(fileNumber: FileNumber) { + self.fileNumber = fileNumber + } } diff --git a/Sources/UNIXListener.swift b/Sources/UNIXListener.swift deleted file mode 100644 index e18e6ef..0000000 --- a/Sources/UNIXListener.swift +++ /dev/null @@ -1,77 +0,0 @@ -#if os(Linux) -import Glibc -private let system_accept = Glibc.accept -private let system_listen = Glibc.listen -private let sock_stream = Int32(SOCK_STREAM.rawValue) -private let system_bind = Glibc.bind -#else -import Darwin -private let system_accept = Darwin.accept -private let system_listen = Darwin.listen -private let sock_stream = SOCK_STREAM -private let system_bind = Darwin.bind -#endif - - -public class UNIXListener : FileDescriptor { - public let fileNumber: FileNumber - - public init(path: String) throws { - fileNumber = socket(AF_UNIX, sock_stream, 0) - if fileNumber == -1 { - throw FileDescriptorError() - } - - do { - try bind(path) - } catch { - try close() - throw error - } - } - - deinit { - let _ = try? close() - } - - fileprivate func bind(_ path: String) throws { - var addr = sockaddr_un() - addr.sun_family = sa_family_t(AF_UNIX) - - let lengthOfPath = path.withCString { Int(strlen($0)) } - - guard lengthOfPath < MemoryLayout.size(ofValue: addr.sun_path) else { - throw FileDescriptorError() - } - - _ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in - path.withCString { - strncpy(ptr, $0, lengthOfPath) - } - } - - try withUnsafePointer(to: &addr) { - try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { - guard system_bind(fileNumber, $0, UInt32(MemoryLayout.stride)) != -1 else { - throw FileDescriptorError() - } - } - } - } - - fileprivate func listen(backlog: Int32) throws { - if system_listen(fileNumber, backlog) == -1 { - throw FileDescriptorError() - } - } - - /// Accepts a connection socket - public func accept() throws -> UNIXConnection { - let fileNumber = system_accept(self.fileNumber, nil, nil) - if fileNumber == -1 { - throw FileDescriptorError() - } - - return UNIXConnection(fileNumber: fileNumber) - } -} diff --git a/Sources/UNIXServerSocket.swift b/Sources/UNIXServerSocket.swift new file mode 100644 index 0000000..fbf72de --- /dev/null +++ b/Sources/UNIXServerSocket.swift @@ -0,0 +1,70 @@ +import Foundation +#if os(Linux) +import Glibc +private let system_accept = Glibc.accept +private let system_listen = Glibc.listen +private let sock_stream = Int32(SOCK_STREAM.rawValue) +private let system_bind = Glibc.bind +#else +import Darwin +private let system_accept = Darwin.accept +private let system_listen = Darwin.listen +private let sock_stream = SOCK_STREAM +private let system_bind = Darwin.bind +#endif + + +public class UNIXServerSocket: UNIXSocket, Listener { + + override public init(path: URL) throws { + try super.init(path: path) + + do { + try bind(path) + } catch { + try close() + throw error + } + } + + private func bind(_ path: URL) throws { + var addr = sockaddr_un() + addr.sun_family = sa_family_t(AF_UNIX) + + let lengthOfPath = path.relativePath.withCString { Int(strlen($0)) } + + guard lengthOfPath < MemoryLayout.size(ofValue: addr.sun_path) else { + throw UNIXSocketError(kind: .pathLength, message: "Path too long!") + } + + _ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in + path.relativePath.withCString { + strncpy(ptr, $0, lengthOfPath) + } + } + + try withUnsafePointer(to: &addr) { + try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { + guard system_bind(fileNumber, $0, UInt32(MemoryLayout.stride)) != -1 else { + throw UNIXSocketError(kind: .bindError, errno: errno) + } + } + } + } + + private func listen(backlog: Int32) throws { + guard system_listen(fileNumber, backlog) != -1 else { + throw UNIXSocketError(kind: .listenError, errno: errno) + } + } + + /// Accepts a connection socket + public func accept() throws -> UNIXConnection { + let fileNumber = system_accept(self.fileNumber, nil, nil) + guard fileNumber != -1 else { + throw UNIXSocketError(kind: .acceptError, errno: errno) + } + + return UNIXConnection(fileNumber: fileNumber) + } +} diff --git a/Sources/UNIXSocket.swift b/Sources/UNIXSocket.swift new file mode 100644 index 0000000..fd331da --- /dev/null +++ b/Sources/UNIXSocket.swift @@ -0,0 +1,57 @@ +import Foundation +#if os(Linux) +import Glibc +private let sock_stream = Int32(SOCK_STREAM.rawValue) +#else +import Darwin +private let sock_stream = SOCK_STREAM +#endif + +struct UNIXSocketError: Error { + enum ErrorKind: String { + case noSocket, creationError, pathLength, bindError, listenError, acceptError, connectError, unknownError + } + + let kind: ErrorKind + let message: String? + + var localizedDescription: String { + "UNIXSocketError of kind \(kind.rawValue)\(message != nil ? "\nmessage: \(message!)" : "")" + } + + init(kind: ErrorKind, message: String? = nil) { + self.kind = kind + self.message = message + } + + init(kind: ErrorKind, errno: Int32) { + let message = String(utf8String: strerror(errno)) + self.init(kind: kind, message: message) + } +} + +public class UNIXSocket: FileDescriptor { + public var fileNumber: FileNumber + + init(path: URL) throws { + guard let type = try? path.resourceValues(forKeys: [.fileResourceTypeKey]).fileResourceType, type == .socket else { + throw UNIXSocketError(kind: .noSocket) + } + + fileNumber = socket(AF_UNIX, sock_stream, 0) + guard fileNumber != -1 else { + throw UNIXSocketError(kind: .creationError, errno: errno) + } + } + + init(kind: Int32) throws { + fileNumber = socket(kind, sock_stream, 0) + guard fileNumber != -1 else { + throw UNIXSocketError(kind: .creationError, errno: errno) + } + } + + deinit { + let _ = try? close() + } +} From 714c306b169e9df3669f84f714ad27427b2b7c0d Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Tue, 21 Jul 2020 01:28:41 +0200 Subject: [PATCH 03/13] Formatting --- Sources/FDSet.swift | 68 ++++++++++++++++----------------- Sources/Pipe.swift | 46 +++++++++++------------ Sources/TCPConnection.swift | 10 ++--- Sources/select.swift | 75 ++++++++++++++++++------------------- 4 files changed, 99 insertions(+), 100 deletions(-) diff --git a/Sources/FDSet.swift b/Sources/FDSet.swift index b3bc3ab..27dd738 100644 --- a/Sources/FDSet.swift +++ b/Sources/FDSet.swift @@ -6,20 +6,20 @@ import Darwin func fdZero(_ set: inout fd_set) { -#if os(Linux) - set.__fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) -#else - set.fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) -#endif + #if os(Linux) + set.__fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + #else + set.fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + #endif } func fdSet(_ descriptor: FileNumber, _ set: inout fd_set) { -#if os(Linux) - let intOffset = Int(descriptor / 16) - let bitOffset = Int(descriptor % 16) - let mask = 1 << bitOffset - switch intOffset { + #if os(Linux) + let intOffset = Int(descriptor / 16) + let bitOffset = Int(descriptor % 16) + let mask = 1 << bitOffset + switch intOffset { case 0: set.__fds_bits.0 = set.__fds_bits.0 | mask case 1: set.__fds_bits.1 = set.__fds_bits.1 | mask case 2: set.__fds_bits.2 = set.__fds_bits.2 | mask @@ -37,13 +37,13 @@ func fdSet(_ descriptor: FileNumber, _ set: inout fd_set) { case 14: set.__fds_bits.14 = set.__fds_bits.14 | mask case 15: set.__fds_bits.15 = set.__fds_bits.15 | mask default: break - } -#else - let intOffset = Int32(descriptor / 16) - let bitOffset = Int32(descriptor % 16) - let mask: Int32 = 1 << bitOffset - - switch intOffset { + } + #else + let intOffset = Int32(descriptor / 16) + let bitOffset = Int32(descriptor % 16) + let mask: Int32 = 1 << bitOffset + + switch intOffset { case 0: set.fds_bits.0 = set.fds_bits.0 | mask case 1: set.fds_bits.1 = set.fds_bits.1 | mask case 2: set.fds_bits.2 = set.fds_bits.2 | mask @@ -61,18 +61,18 @@ func fdSet(_ descriptor: FileNumber, _ set: inout fd_set) { case 14: set.fds_bits.14 = set.fds_bits.14 | mask case 15: set.fds_bits.15 = set.fds_bits.15 | mask default: break - } -#endif + } + #endif } func fdIsSet(_ descriptor: FileNumber, _ set: inout fd_set) -> Bool { -#if os(Linux) - let intOffset = Int(descriptor / 32) - let bitOffset = Int(descriptor % 32) - let mask = Int(1 << bitOffset) - - switch intOffset { + #if os(Linux) + let intOffset = Int(descriptor / 32) + let bitOffset = Int(descriptor % 32) + let mask = Int(1 << bitOffset) + + switch intOffset { case 0: return set.__fds_bits.0 & mask != 0 case 1: return set.__fds_bits.1 & mask != 0 case 2: return set.__fds_bits.2 & mask != 0 @@ -90,13 +90,13 @@ func fdIsSet(_ descriptor: FileNumber, _ set: inout fd_set) -> Bool { case 14: return set.__fds_bits.14 & mask != 0 case 15: return set.__fds_bits.15 & mask != 0 default: return false - } -#else - let intOffset = Int32(descriptor / 32) - let bitOffset = Int32(descriptor % 32) - let mask = Int32(1 << bitOffset) - - switch intOffset { + } + #else + let intOffset = Int32(descriptor / 32) + let bitOffset = Int32(descriptor % 32) + let mask = Int32(1 << bitOffset) + + switch intOffset { case 0: return set.fds_bits.0 & mask != 0 case 1: return set.fds_bits.1 & mask != 0 case 2: return set.fds_bits.2 & mask != 0 @@ -130,6 +130,6 @@ func fdIsSet(_ descriptor: FileNumber, _ set: inout fd_set) -> Bool { case 30: return set.fds_bits.30 & mask != 0 case 31: return set.fds_bits.31 & mask != 0 default: return false - } -#endif + } + #endif } diff --git a/Sources/Pipe.swift b/Sources/Pipe.swift index 8e419e4..4bc1f1a 100644 --- a/Sources/Pipe.swift +++ b/Sources/Pipe.swift @@ -8,35 +8,35 @@ private let system_pipe = Darwin.pipe class PipeReadFileDescriptor : ReadableFileDescriptor { - let fileNumber: FileNumber - - init(fileNumber: FileNumber) { - self.fileNumber = fileNumber - } - - deinit { - let _ = try? close() - } + let fileNumber: FileNumber + + init(fileNumber: FileNumber) { + self.fileNumber = fileNumber + } + + deinit { + let _ = try? close() + } } class PipeWriteFileDescriptor : WritableFileDescriptor { - let fileNumber: FileNumber - - init(fileNumber: FileNumber) { - self.fileNumber = fileNumber - } - - deinit { - let _ = try? close() - } + let fileNumber: FileNumber + + init(fileNumber: FileNumber) { + self.fileNumber = fileNumber + } + + deinit { + let _ = try? close() + } } public func pipe() throws -> (reader: ReadableFileDescriptor, writer: WritableFileDescriptor) { - var fileNumbers: [FileNumber] = [0, 0] - if system_pipe(&fileNumbers) == -1 { - throw FileDescriptorError() - } - return (PipeReadFileDescriptor(fileNumber: fileNumbers[0]), PipeWriteFileDescriptor(fileNumber: fileNumbers[1])) + var fileNumbers: [FileNumber] = [0, 0] + if system_pipe(&fileNumbers) == -1 { + throw FileDescriptorError(kind: .pipeError, errno: errno) + } + return (PipeReadFileDescriptor(fileNumber: fileNumbers[0]), PipeWriteFileDescriptor(fileNumber: fileNumbers[1])) } diff --git a/Sources/TCPConnection.swift b/Sources/TCPConnection.swift index 05eb0c5..536f569 100644 --- a/Sources/TCPConnection.swift +++ b/Sources/TCPConnection.swift @@ -1,7 +1,7 @@ public class TCPConnection : FileDescriptor, Connection { - public let fileNumber: FileNumber - - public init(fileNumber: FileNumber) { - self.fileNumber = fileNumber - } + public let fileNumber: FileNumber + + public init(fileNumber: FileNumber) { + self.fileNumber = fileNumber + } } diff --git a/Sources/select.swift b/Sources/select.swift index 7f54be0..7b92ed3 100644 --- a/Sources/select.swift +++ b/Sources/select.swift @@ -8,46 +8,45 @@ private let system_select = Darwin.select func filter(_ sockets: [T]?, _ set: inout fd_set) -> [T] { - return sockets?.filter { - fdIsSet($0.fileNumber, &set) - } ?? [] + return sockets?.filter { + fdIsSet($0.fileNumber, &set) + } ?? [] } public func select(reads: [R] = [], writes: [W] = [], errors: [E] = [], timeout: timeval? = nil) throws -> (reads: [R], writes: [W], errors: [E]) { - var readFDs = fd_set() - fdZero(&readFDs) - reads.forEach { fdSet($0.fileNumber, &readFDs) } - - var writeFDs = fd_set() - fdZero(&writeFDs) - writes.forEach { fdSet($0.fileNumber, &writeFDs) } - - var errorFDs = fd_set() - fdZero(&errorFDs) - errors.forEach { fdSet($0.fileNumber, &errorFDs) } - - let readFDNumbers = reads.map { $0.fileNumber } - let writeFDNumbers = writes.map { $0.fileNumber } - let errorFDNumbers = errors.map { $0.fileNumber } - let maxFD = (readFDNumbers + writeFDNumbers + errorFDNumbers).reduce(0, max) - let result: Int32 - if let timeout = timeout { - var timeout = timeout - result = system_select(maxFD + 1, &readFDs, &writeFDs, &errorFDs, &timeout) - } else { - result = system_select(maxFD + 1, &readFDs, &writeFDs, &errorFDs, nil) - } - - if result == 0 { - return ([], [], []) - } else if result > 0 { - return ( - filter(reads, &readFDs), - filter(writes, &writeFDs), - filter(errors, &errorFDs) - ) - } - - throw FileDescriptorError() + var readFDs = fd_set() + fdZero(&readFDs) + reads.forEach { fdSet($0.fileNumber, &readFDs) } + + var writeFDs = fd_set() + fdZero(&writeFDs) + writes.forEach { fdSet($0.fileNumber, &writeFDs) } + + var errorFDs = fd_set() + fdZero(&errorFDs) + errors.forEach { fdSet($0.fileNumber, &errorFDs) } + + let readFDNumbers = reads.map { $0.fileNumber } + let writeFDNumbers = writes.map { $0.fileNumber } + let errorFDNumbers = errors.map { $0.fileNumber } + let maxFD = (readFDNumbers + writeFDNumbers + errorFDNumbers).reduce(0, max) + let result: Int32 + if var timeout = timeout { + result = system_select(maxFD + 1, &readFDs, &writeFDs, &errorFDs, &timeout) + } else { + result = system_select(maxFD + 1, &readFDs, &writeFDs, &errorFDs, nil) + } + + if result == 0 { + return ([], [], []) + } else if result > 0 { + return ( + filter(reads, &readFDs), + filter(writes, &writeFDs), + filter(errors, &errorFDs) + ) + } + + throw FileDescriptorError(kind: .selectError, errno: errno) } From 7b044526599289d88de3ad929f1fc57511066670 Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Tue, 21 Jul 2020 01:45:10 +0200 Subject: [PATCH 04/13] Move fileNumber initialiser to `UNIXSocket`. --- Sources/TCPListener.swift | 4 ---- Sources/UNIXSocket.swift | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/TCPListener.swift b/Sources/TCPListener.swift index e3bcb0b..fb3b4d8 100644 --- a/Sources/TCPListener.swift +++ b/Sources/TCPListener.swift @@ -14,10 +14,6 @@ private let sock_stream = SOCK_STREAM public class TCPListener : UNIXSocket, Listener { - init(fileNumber: FileNumber) { - self.fileNumber = fileNumber - } - public init(address: String, port: UInt16) throws { try super.init(kind: AF_INET) diff --git a/Sources/UNIXSocket.swift b/Sources/UNIXSocket.swift index fd331da..8b2bf60 100644 --- a/Sources/UNIXSocket.swift +++ b/Sources/UNIXSocket.swift @@ -51,6 +51,10 @@ public class UNIXSocket: FileDescriptor { } } + init(fileNumber: FileNumber) { + self.fileNumber = fileNumber + } + deinit { let _ = try? close() } From ddf0cc711cd8a0fe3f8984cf3188498cb6dcf113 Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Tue, 21 Jul 2020 01:45:42 +0200 Subject: [PATCH 05/13] FIx `Listener` protocol --- Sources/Listener.swift | 4 +++- Sources/TCPListener.swift | 2 +- Sources/UNIXClientSocket.swift | 2 +- Sources/UNIXServerSocket.swift | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Sources/Listener.swift b/Sources/Listener.swift index 51936e9..ffcf27f 100644 --- a/Sources/Listener.swift +++ b/Sources/Listener.swift @@ -1,3 +1,5 @@ public protocol Listener : FileDescriptor { - func accept() throws -> C + associatedtype AnyConnection + + func accept() throws -> AnyConnection } diff --git a/Sources/TCPListener.swift b/Sources/TCPListener.swift index fb3b4d8..5c85ced 100644 --- a/Sources/TCPListener.swift +++ b/Sources/TCPListener.swift @@ -54,7 +54,7 @@ public class TCPListener : UNIXSocket, Listener { } /// Accepts a connection socket - public func accept() throws -> TCPConnection { + public func accept() throws -> TCPConnection { let fileNumber = system_accept(self.fileNumber, nil, nil) guard fileNumber != -1 else { throw UNIXSocketError(kind: .acceptError, errno: errno) diff --git a/Sources/UNIXClientSocket.swift b/Sources/UNIXClientSocket.swift index c83f466..4c5c069 100644 --- a/Sources/UNIXClientSocket.swift +++ b/Sources/UNIXClientSocket.swift @@ -49,7 +49,7 @@ public class UNIXClientSocket: UNIXSocket, Listener { } } - public func accept() throws -> UNIXConnection { + public func accept() throws -> UNIXConnection { return UNIXConnection(fileNumber: fileNumber) } } diff --git a/Sources/UNIXServerSocket.swift b/Sources/UNIXServerSocket.swift index fbf72de..29e5785 100644 --- a/Sources/UNIXServerSocket.swift +++ b/Sources/UNIXServerSocket.swift @@ -59,7 +59,7 @@ public class UNIXServerSocket: UNIXSocket, Listener { } /// Accepts a connection socket - public func accept() throws -> UNIXConnection { + public func accept() throws -> UNIXConnection { let fileNumber = system_accept(self.fileNumber, nil, nil) guard fileNumber != -1 else { throw UNIXSocketError(kind: .acceptError, errno: errno) From 9522100d1550dad3c0e0c00a4da2ff02678f32ed Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Tue, 21 Jul 2020 01:46:52 +0200 Subject: [PATCH 06/13] adapt test to new class --- Tests/fdTests/UNIXListenerSpec.swift | 12 ------------ Tests/fdTests/UNIXServerSocketSpec.swift | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 12 deletions(-) delete mode 100644 Tests/fdTests/UNIXListenerSpec.swift create mode 100644 Tests/fdTests/UNIXServerSocketSpec.swift diff --git a/Tests/fdTests/UNIXListenerSpec.swift b/Tests/fdTests/UNIXListenerSpec.swift deleted file mode 100644 index bb5da8f..0000000 --- a/Tests/fdTests/UNIXListenerSpec.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Spectre -import fd - - -public func testUNIXListener() { - describe("UNIXListener") { - $0.it("may be initialised with a path") { - let listener = try UNIXListener(path: "/tmp/fd-unixlistener-test") - try expect(listener.fileNumber) != -1 - } - } -} diff --git a/Tests/fdTests/UNIXServerSocketSpec.swift b/Tests/fdTests/UNIXServerSocketSpec.swift new file mode 100644 index 0000000..bcabfe8 --- /dev/null +++ b/Tests/fdTests/UNIXServerSocketSpec.swift @@ -0,0 +1,14 @@ +import Foundation +import Spectre +import fd + + +public func testUNIXServerSocket() { + describe("UNIXListener") { + $0.it("may be initialised with a path") { + let path = URL(string: "/tmp/fd-unixlistener-test")! + let listener = try UNIXServerSocket(path: path) + try expect(listener.fileNumber) != -1 + } + } +} From 9f40f2a37b79edfec513ec24ff7174a86fc9f096 Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Wed, 22 Jul 2020 15:30:33 +0200 Subject: [PATCH 07/13] Add readAll function. --- Sources/ReadableFileDescriptor.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/ReadableFileDescriptor.swift b/Sources/ReadableFileDescriptor.swift index 1f65ea6..af16428 100644 --- a/Sources/ReadableFileDescriptor.swift +++ b/Sources/ReadableFileDescriptor.swift @@ -30,6 +30,11 @@ extension ReadableFileDescriptor { public func read(_ bufferSize: Int) throws -> Data { let bytes: [Byte] = try read(bufferSize) - return Data(bytes) + return Data(bytes: bytes, count: bytes.count) + } + + public func readAll() throws -> Data { + let size = Int(lseek(fileNumber, 0, SEEK_END)) + return read(size) } } From 53fd930e8b83e350f7168a6432e48b89efe391f2 Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Wed, 22 Jul 2020 15:32:44 +0200 Subject: [PATCH 08/13] Add missing try. --- Sources/ReadableFileDescriptor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ReadableFileDescriptor.swift b/Sources/ReadableFileDescriptor.swift index af16428..2044a8a 100644 --- a/Sources/ReadableFileDescriptor.swift +++ b/Sources/ReadableFileDescriptor.swift @@ -35,6 +35,6 @@ extension ReadableFileDescriptor { public func readAll() throws -> Data { let size = Int(lseek(fileNumber, 0, SEEK_END)) - return read(size) + return try read(size) } } From 9c62f52cea31868c0956e58f50016dda8602a76d Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Wed, 22 Jul 2020 15:37:41 +0200 Subject: [PATCH 09/13] reset file pointer after getting size --- Sources/ReadableFileDescriptor.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/ReadableFileDescriptor.swift b/Sources/ReadableFileDescriptor.swift index 2044a8a..876a7e7 100644 --- a/Sources/ReadableFileDescriptor.swift +++ b/Sources/ReadableFileDescriptor.swift @@ -34,7 +34,9 @@ extension ReadableFileDescriptor { } public func readAll() throws -> Data { + let current = lseek(fileNumber, 0, SEEK_CUR) let size = Int(lseek(fileNumber, 0, SEEK_END)) + lseek(fileNumber, current, SEEK_SET) return try read(size) } } From 6f40a0bb61a03cc0b8919421e82d941dc2fb4b22 Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Wed, 22 Jul 2020 15:38:03 +0200 Subject: [PATCH 10/13] Fix wrong method reference. --- Tests/fdTests/XCTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/fdTests/XCTest.swift b/Tests/fdTests/XCTest.swift index bb6f48a..fcebb36 100644 --- a/Tests/fdTests/XCTest.swift +++ b/Tests/fdTests/XCTest.swift @@ -8,6 +8,6 @@ class FDTests: XCTestCase { testTCPConnection() testTCPListener() testUNIXConnection() - testUNIXListener() + testUNIXServerSocket() } } From f7b39c560053d41b56e570c0c35611724bcb044c Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Wed, 22 Jul 2020 15:50:52 +0200 Subject: [PATCH 11/13] Deal with errors in `readAll` function. --- Sources/FileDescriptor.swift | 2 +- Sources/ReadableFileDescriptor.swift | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Sources/FileDescriptor.swift b/Sources/FileDescriptor.swift index 5d4f64d..e9bda4e 100644 --- a/Sources/FileDescriptor.swift +++ b/Sources/FileDescriptor.swift @@ -17,7 +17,7 @@ public protocol FileDescriptor { struct FileDescriptorError : Error { enum ErrorKind: String { - case readError, writeError, selectError, pipeError, closeError + case readError, writeError, selectError, pipeError, closeError, unknown } let kind: ErrorKind diff --git a/Sources/ReadableFileDescriptor.swift b/Sources/ReadableFileDescriptor.swift index 876a7e7..4717eb2 100644 --- a/Sources/ReadableFileDescriptor.swift +++ b/Sources/ReadableFileDescriptor.swift @@ -35,8 +35,16 @@ extension ReadableFileDescriptor { public func readAll() throws -> Data { let current = lseek(fileNumber, 0, SEEK_CUR) + guard current != -1 else { + throw FileDescriptorError(kind: .unknown, errno: errno) + } let size = Int(lseek(fileNumber, 0, SEEK_END)) - lseek(fileNumber, current, SEEK_SET) + guard size != -1 else { + throw FileDescriptorError(kind: .unknown, errno: errno) + } + guard lseek(fileNumber, current, SEEK_SET) != -1 else { + throw FileDescriptorError(kind: .unkown, errno: errno) + } return try read(size) } } From ff10a697fa93ad4d9a066289b39fb756ac7fc9e8 Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Wed, 22 Jul 2020 15:51:47 +0200 Subject: [PATCH 12/13] fix typo --- Sources/ReadableFileDescriptor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ReadableFileDescriptor.swift b/Sources/ReadableFileDescriptor.swift index 4717eb2..288f296 100644 --- a/Sources/ReadableFileDescriptor.swift +++ b/Sources/ReadableFileDescriptor.swift @@ -43,7 +43,7 @@ extension ReadableFileDescriptor { throw FileDescriptorError(kind: .unknown, errno: errno) } guard lseek(fileNumber, current, SEEK_SET) != -1 else { - throw FileDescriptorError(kind: .unkown, errno: errno) + throw FileDescriptorError(kind: .unknown, errno: errno) } return try read(size) } From c8be3bce5dd713e84d650bdc36c93ac38640ae5e Mon Sep 17 00:00:00 2001 From: Julian Gentges Date: Wed, 22 Jul 2020 17:53:45 +0200 Subject: [PATCH 13/13] New implementation of `readAll`. --- Sources/ReadableFileDescriptor.swift | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Sources/ReadableFileDescriptor.swift b/Sources/ReadableFileDescriptor.swift index 288f296..db3f4ce 100644 --- a/Sources/ReadableFileDescriptor.swift +++ b/Sources/ReadableFileDescriptor.swift @@ -34,17 +34,18 @@ extension ReadableFileDescriptor { } public func readAll() throws -> Data { - let current = lseek(fileNumber, 0, SEEK_CUR) - guard current != -1 else { - throw FileDescriptorError(kind: .unknown, errno: errno) - } - let size = Int(lseek(fileNumber, 0, SEEK_END)) - guard size != -1 else { - throw FileDescriptorError(kind: .unknown, errno: errno) + var result = Data() + var data = CChar() + var data_read: size_t + + while (((data_read = recv(fileNumber, &data, 1, 0)), data_read > 0).1 && !Character(UnicodeScalar(UInt8(bitPattern: data))).isNewline) { + result.append(contentsOf: [data].map(UInt8.init)) } - guard lseek(fileNumber, current, SEEK_SET) != -1 else { + + guard data_read != -1 else { throw FileDescriptorError(kind: .unknown, errno: errno) } - return try read(size) + + return result } }