diff --git a/Xcode/Sources/HttpServerIO.swift b/Xcode/Sources/HttpServerIO.swift index 65c6e95a..5d4b2473 100644 --- a/Xcode/Sources/HttpServerIO.swift +++ b/Xcode/Sources/HttpServerIO.swift @@ -8,6 +8,11 @@ import Foundation import Dispatch +#if os(Windows) +import WinSDK +public typealias in_port_t = UInt16 +#endif + public protocol HttpServerIODelegate: AnyObject { func socketConnectionReceived(_ socket: Socket) } @@ -16,7 +21,7 @@ open class HttpServerIO { public weak var delegate: HttpServerIODelegate? - private var socket = Socket(socketFileDescriptor: -1) + private var socket = Socket(socketFileDescriptor: invalidPlatformSocketFD) private var sockets = Set() public enum HttpServerIOState: Int32 { @@ -33,7 +38,7 @@ open class HttpServerIO { return HttpServerIOState(rawValue: stateValue)! } set(state) { - #if !os(Linux) + #if !os(Linux) && !os(Windows) OSAtomicCompareAndSwapInt(self.state.rawValue, state.rawValue, &stateValue) #else self.stateValue = state.rawValue diff --git a/Xcode/Sources/Process.swift b/Xcode/Sources/Process.swift index f6c214a8..927c4af9 100644 --- a/Xcode/Sources/Process.swift +++ b/Xcode/Sources/Process.swift @@ -7,6 +7,10 @@ import Foundation +#if os(Windows) +import WinSDK +#endif + public class Process { public static var pid: Int { @@ -16,6 +20,8 @@ public class Process { public static var tid: UInt64 { #if os(Linux) return UInt64(pthread_self()) + #elseif os(Windows) + return UInt64(GetCurrentThreadId()) #else var tid: __uint64_t = 0 pthread_threadid_np(nil, &tid) @@ -28,7 +34,12 @@ public class Process { public static func watchSignals(_ callback: @escaping (Int32) -> Void) { if !signalsObserved { - [SIGTERM, SIGHUP, SIGSTOP, SIGINT].forEach { item in + #if os(Windows) + let signals = [SIGTERM, SIGINT] + #else + let signals = [SIGTERM, SIGHUP, SIGSTOP, SIGINT] + #endif + signals.forEach { item in signal(item) { signum in Process.signalsWatchers.forEach { $0(signum) } } diff --git a/Xcode/Sources/Socket+File.swift b/Xcode/Sources/Socket+File.swift index e0a9e82a..26abcfcd 100644 --- a/Xcode/Sources/Socket+File.swift +++ b/Xcode/Sources/Socket+File.swift @@ -7,11 +7,15 @@ import Foundation -#if os(iOS) || os(tvOS) || os (Linux) +#if os(Windows) +import WinSDK +#endif + +#if os(iOS) || os(tvOS) || os (Linux) || os(Windows) // swiftlint:disable type_name function_parameter_count struct sf_hdtr { } - private func sendfileImpl(_ source: UnsafeMutablePointer, _ target: Int32, _: off_t, _: UnsafeMutablePointer, _: UnsafeMutablePointer, _: Int32) -> Int32 { + private func sendfileImpl(_ source: UnsafeMutablePointer, _ target: PlatformSocketFD, _: off_t, _: UnsafeMutablePointer, _: UnsafeMutablePointer, _: Int32) -> Int32 { var buffer = [UInt8](repeating: 0, count: 1024) while true { let readResult = fread(&buffer, 1, buffer.count, source) @@ -25,6 +29,8 @@ import Foundation let len = readResult - writeCounter #if os(Linux) return send(target, start, len, Int32(MSG_NOSIGNAL)) + #elseif os(Windows) + return Int(send(target, start, Int32(len), 0)) #else return write(target, start, len) #endif @@ -44,7 +50,7 @@ extension Socket { var offset: off_t = 0 var sf: sf_hdtr = sf_hdtr() - #if os(iOS) || os(tvOS) || os (Linux) + #if os(iOS) || os(tvOS) || os (Linux) || os(Windows) let result = sendfileImpl(file.pointer, self.socketFileDescriptor, 0, &offset, &sf, 0) #else let result = sendfile(fileno(file.pointer), self.socketFileDescriptor, 0, &offset, &sf, 0) diff --git a/Xcode/Sources/Socket+Server.swift b/Xcode/Sources/Socket+Server.swift index 11076a1e..3feb2299 100644 --- a/Xcode/Sources/Socket+Server.swift +++ b/Xcode/Sources/Socket+Server.swift @@ -7,6 +7,10 @@ import Foundation +#if os(Windows) +import WinSDK +#endif + extension Socket { // swiftlint:disable function_body_length @@ -15,14 +19,13 @@ extension Socket { /// connections from. It should be in IPv4 format if forceIPv4 == true, /// otherwise - in IPv6. public class func tcpSocketForListen(_ port: in_port_t, _ forceIPv4: Bool = false, _ maxPendingConnection: Int32 = SOMAXCONN, _ listenAddress: String? = nil) throws -> Socket { - #if os(Linux) let socketFileDescriptor = socket(forceIPv4 ? AF_INET : AF_INET6, Int32(SOCK_STREAM.rawValue), 0) #else let socketFileDescriptor = socket(forceIPv4 ? AF_INET : AF_INET6, SOCK_STREAM, 0) #endif - if socketFileDescriptor == -1 { + if socketFileDescriptor == invalidPlatformSocketFD { throw SocketError.socketCreationFailed(Errno.description()) } @@ -42,6 +45,12 @@ extension Socket { sin_port: port.bigEndian, sin_addr: in_addr(s_addr: in_addr_t(0)), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) + #elseif os(Windows) + var addr = sockaddr_in( + sin_family: ADDRESS_FAMILY(AF_INET), + sin_port: port.bigEndian, + sin_addr: .init(), + sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) #else var addr = sockaddr_in( sin_len: UInt8(MemoryLayout.stride), @@ -68,6 +77,13 @@ extension Socket { sin6_flowinfo: 0, sin6_addr: in6addr_any, sin6_scope_id: 0) + #elseif os(Windows) + var addr = sockaddr_in6( + sin6_family: ADDRESS_FAMILY(AF_INET6), + sin6_port: port.bigEndian, + sin6_flowinfo: 0, + sin6_addr: in6addr_any, + sockaddr_in6.__Unnamed_union___Anonymous_field4()) #else var addr = sockaddr_in6( sin6_len: UInt8(MemoryLayout.stride), @@ -104,10 +120,8 @@ extension Socket { } public func acceptClientSocket() throws -> Socket { - var addr = sockaddr() - var len: socklen_t = 0 - let clientSocket = accept(self.socketFileDescriptor, &addr, &len) - if clientSocket == -1 { + let clientSocket = accept(self.socketFileDescriptor, nil, nil) + if clientSocket == invalidPlatformSocketFD { throw SocketError.acceptFailed(Errno.description()) } Socket.setNoSigPipe(clientSocket) diff --git a/Xcode/Sources/Socket.swift b/Xcode/Sources/Socket.swift index 1a9887e9..62c553ca 100644 --- a/Xcode/Sources/Socket.swift +++ b/Xcode/Sources/Socket.swift @@ -7,6 +7,18 @@ import Foundation +#if os(Windows) +import WinSDK +#endif + +#if os(Windows) +public typealias PlatformSocketFD = SOCKET +public let invalidPlatformSocketFD = INVALID_SOCKET +#else +public typealias PlatformSocketFD = Int32 +public let invalidPlatformSocketFD = Int32(-1) +#endif + public enum SocketError: Error { case socketCreationFailed(String) case socketSettingReUseAddrFailed(String) @@ -23,11 +35,10 @@ public enum SocketError: Error { // swiftlint: disable identifier_name open class Socket: Hashable, Equatable { - - let socketFileDescriptor: Int32 + let socketFileDescriptor: PlatformSocketFD private var shutdown = false - public init(socketFileDescriptor: Int32) { + public init(socketFileDescriptor: PlatformSocketFD) { self.socketFileDescriptor = socketFileDescriptor } @@ -55,7 +66,7 @@ open class Socket: Hashable, Equatable { throw SocketError.getSockNameFailed(Errno.description()) } let sin_port = pointer.pointee.sin_port - #if os(Linux) + #if os(Linux) || os(Windows) return ntohs(sin_port) #else return Int(OSHostByteOrder()) != OSLittleEndian ? sin_port.littleEndian : sin_port.bigEndian @@ -112,6 +123,8 @@ open class Socket: Hashable, Equatable { while sent < length { #if os(Linux) let result = send(self.socketFileDescriptor, pointer + sent, Int(length - sent), Int32(MSG_NOSIGNAL)) + #elseif os(Windows) + let result = Int(send(self.socketFileDescriptor, pointer + sent, Int32(length - sent), 0)) #else let result = write(self.socketFileDescriptor, pointer + sent, Int(length - sent)) #endif @@ -133,6 +146,8 @@ open class Socket: Hashable, Equatable { #if os(Linux) let count = Glibc.read(self.socketFileDescriptor as Int32, &byte, 1) + #elseif os(Windows) + let count = recv(self.socketFileDescriptor, &byte, 1, 0) #else let count = Darwin.read(self.socketFileDescriptor as Int32, &byte, 1) #endif @@ -172,6 +187,8 @@ open class Socket: Hashable, Equatable { #if os(Linux) let bytesRead = Glibc.read(self.socketFileDescriptor as Int32, baseAddress + offset, readLength) + #elseif os(Windows) + let bytesRead = Int(recv(self.socketFileDescriptor, baseAddress + offset, Int32(readLength), 0)) #else let bytesRead = Darwin.read(self.socketFileDescriptor as Int32, baseAddress + offset, readLength) #endif @@ -205,14 +222,19 @@ open class Socket: Hashable, Equatable { throw SocketError.getPeerNameFailed(Errno.description()) } var hostBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST)) - if getnameinfo(&addr, len, &hostBuffer, socklen_t(hostBuffer.count), nil, 0, NI_NUMERICHOST) != 0 { + #if os(Windows) + let hostBufferSize = DWORD(hostBuffer.count) + #else + let hostBufferSize = socklen_t(hostBuffer.count) + #endif + if getnameinfo(&addr, len, &hostBuffer, hostBufferSize, nil, 0, NI_NUMERICHOST) != 0 { throw SocketError.getNameInfoFailed(Errno.description()) } return String(cString: hostBuffer) } - public class func setNoSigPipe(_ socket: Int32) { - #if os(Linux) + public class func setNoSigPipe(_ socket: PlatformSocketFD) { + #if os(Linux) || os(Windows) // There is no SO_NOSIGPIPE in Linux (nor some other systems). You can instead use the MSG_NOSIGNAL flag when calling send(), // or use signal(SIGPIPE, SIG_IGN) to make your entire application ignore SIGPIPE. #else @@ -222,9 +244,11 @@ open class Socket: Hashable, Equatable { #endif } - public class func close(_ socket: Int32) { + public class func close(_ socket: PlatformSocketFD) { #if os(Linux) _ = Glibc.close(socket) + #elseif os(Windows) + _ = closesocket(socket) #else _ = Darwin.close(socket) #endif diff --git a/Xcode/Sources/String+File.swift b/Xcode/Sources/String+File.swift index 15fb7fb7..9f0f8b57 100644 --- a/Xcode/Sources/String+File.swift +++ b/Xcode/Sources/String+File.swift @@ -7,6 +7,10 @@ import Foundation +#if os(Windows) +import WinSDK +#endif + extension String { public enum FileError: Error { @@ -25,7 +29,7 @@ extension String { fclose(pointer) } - public func seek(_ offset: Int) -> Bool { + public func seek(_ offset: Int32) -> Bool { return (fseek(pointer, offset, SEEK_SET) == 0) } @@ -98,18 +102,45 @@ extension String { public func directory() throws -> Bool { return try self.withStat { if let stat = $0 { + #if os(Windows) + // Need to disambiguate here. + return Int32(stat.st_mode) & ucrt.S_IFMT == ucrt.S_IFDIR + #else return stat.st_mode & S_IFMT == S_IFDIR + #endif } return false } } public func files() throws -> [String] { + var results = [String]() + #if os(Windows) + var data = WIN32_FIND_DATAW() + let handle = self.withCString(encodedAs: UTF16.self) { + return FindFirstFileW($0, &data) + } + guard handle != INVALID_HANDLE_VALUE else { + throw FileError.error(Int32(GetLastError())) + } + defer { FindClose(handle) } + let appendToResults = { + let fileName = withUnsafePointer(to: &data.cFileName) { (ptr) -> String in + ptr.withMemoryRebound(to: unichar.self, capacity: Int(MAX_PATH * 2)) { + String(utf16CodeUnits: $0, count: wcslen($0)) + } + } + results.append(fileName) + } + appendToResults() + while FindNextFileW(handle, &data) { + appendToResults() + } + #else guard let dir = self.withCString({ opendir($0) }) else { throw FileError.error(errno) } defer { closedir(dir) } - var results = [String]() while let ent = readdir(dir) { var name = ent.pointee.d_name let fileName = withUnsafePointer(to: &name) { (ptr) -> String? in @@ -129,6 +160,7 @@ extension String { results.append(fileName) } } + #endif return results }