From 2b93d3f04ea2108056e6379a9e4fe0ea5aee0597 Mon Sep 17 00:00:00 2001 From: Joannis Orlandos Date: Mon, 3 Nov 2025 16:55:25 +0100 Subject: [PATCH 01/10] Import `ucrt` on Windows if available --- Sources/NIOExtras/DebugInboundEventsHandler.swift | 2 ++ Sources/NIOExtras/DebugOutboundEventsHandler.swift | 2 ++ Sources/NIOExtras/WritePCAPHandler.swift | 2 ++ Sources/NIOSOCKS/Messages/SOCKSRequest.swift | 2 ++ 4 files changed, 8 insertions(+) diff --git a/Sources/NIOExtras/DebugInboundEventsHandler.swift b/Sources/NIOExtras/DebugInboundEventsHandler.swift index 1c322635..b7a89174 100644 --- a/Sources/NIOExtras/DebugInboundEventsHandler.swift +++ b/Sources/NIOExtras/DebugInboundEventsHandler.swift @@ -19,6 +19,8 @@ import Darwin @preconcurrency import Musl #elseif canImport(Android) @preconcurrency import Android +#elseif canImport(ucrt) +@preconcurrency import ucrt #else @preconcurrency import Glibc #endif diff --git a/Sources/NIOExtras/DebugOutboundEventsHandler.swift b/Sources/NIOExtras/DebugOutboundEventsHandler.swift index b3a1f7eb..08eca93e 100644 --- a/Sources/NIOExtras/DebugOutboundEventsHandler.swift +++ b/Sources/NIOExtras/DebugOutboundEventsHandler.swift @@ -20,6 +20,8 @@ import Darwin @preconcurrency import Musl #elseif canImport(Android) @preconcurrency import Android +#elseif canImport(ucrt) +@preconcurrency import ucrt #else @preconcurrency import Glibc #endif diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index 25d12d05..23460610 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -22,6 +22,8 @@ import Darwin import Musl #elseif canImport(Android) import Android +#elseif canImport(ucrt) +import ucrt #else import Glibc #endif diff --git a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift index c507e858..1c5f0815 100644 --- a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift +++ b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift @@ -21,6 +21,8 @@ import Darwin import Musl #elseif canImport(Android) import Android +#elseif canImport(ucrt) +import ucrt #else import Glibc #endif From 18f04a17c0b75a7fb8c770a176d45ee161dd2dfb Mon Sep 17 00:00:00 2001 From: Joannis Orlandos Date: Mon, 3 Nov 2025 17:15:52 +0100 Subject: [PATCH 02/10] Unwind `stdout` macro on Windows for fflush(stdout) --- Sources/NIOExtras/DebugInboundEventsHandler.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/NIOExtras/DebugInboundEventsHandler.swift b/Sources/NIOExtras/DebugInboundEventsHandler.swift index b7a89174..b1a55374 100644 --- a/Sources/NIOExtras/DebugInboundEventsHandler.swift +++ b/Sources/NIOExtras/DebugInboundEventsHandler.swift @@ -177,6 +177,10 @@ public class DebugInboundEventsHandler: ChannelInboundHandler { message = "Channel caught error: \(error)" } print(message + " in \(context.name)") + + #if os(Windows) + let stdout = __acrt_iob_func(1) + #endif fflush(stdout) } } From 07377dc1aadc81fba72ca221b30ef58cde9aab77 Mon Sep 17 00:00:00 2001 From: Joannis Orlandos Date: Mon, 3 Nov 2025 17:20:08 +0100 Subject: [PATCH 03/10] Also import WinSDK for sin6_addr --- Sources/NIOExtras/WritePCAPHandler.swift | 1 + Sources/NIOSOCKS/Messages/SOCKSRequest.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index 23460610..e451d971 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -24,6 +24,7 @@ import Musl import Android #elseif canImport(ucrt) import ucrt +import WinSDK #else import Glibc #endif diff --git a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift index 1c5f0815..3b48597a 100644 --- a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift +++ b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift @@ -23,6 +23,7 @@ import Musl import Android #elseif canImport(ucrt) import ucrt +import WinSDK #else import Glibc #endif From 91d8bfbeb01371b7310550df023a1251f54cde2d Mon Sep 17 00:00:00 2001 From: Joannis Orlandos Date: Mon, 3 Nov 2025 17:24:42 +0100 Subject: [PATCH 04/10] Fix PCAP and DebugOutboundEventsHandler on Windows --- .../NIOExtras/DebugOutboundEventsHandler.swift | 3 +++ Sources/NIOExtras/WritePCAPHandler.swift | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Sources/NIOExtras/DebugOutboundEventsHandler.swift b/Sources/NIOExtras/DebugOutboundEventsHandler.swift index 08eca93e..1f16f070 100644 --- a/Sources/NIOExtras/DebugOutboundEventsHandler.swift +++ b/Sources/NIOExtras/DebugOutboundEventsHandler.swift @@ -173,6 +173,9 @@ public class DebugOutboundEventsHandler: ChannelOutboundHandler { message = "Triggering user outbound event: { \(event) }" } print(message + " in \(context.name)") + #if os(Windows) + let stdout = __acrt_iob_func(1) + #endif fflush(stdout) } } diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index e451d971..3e28f1f2 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -738,7 +738,12 @@ extension NIOWritePCAPHandler { if fileWritingMode == .createNewPCAPFile { let writeOk = NIOWritePCAPHandler.pcapFileHeader.withUnsafeReadableBytes { ptr in - sysWrite(fd, ptr.baseAddress, ptr.count) == ptr.count + #if os(Windows) + let size = UInt32(ptr.count) + #else + let size = ptr.count + #endif + sysWrite(fd, ptr.baseAddress, size) == size } guard writeOk else { throw SynchronizedFileSink.Error(errorCode: Error.ErrorCode.cannotWriteToFileError.rawValue) @@ -797,8 +802,13 @@ extension NIOWritePCAPHandler { var buffer = buffer while buffer.readableBytes > 0 { try buffer.readWithUnsafeReadableBytes { dataPtr in - let r = sysWrite(fd, dataPtr.baseAddress, dataPtr.count) - assert(r != 0, "write returned 0 but we tried to write \(dataPtr.count) bytes") + #if os(Windows) + let size = UInt32(dataPtr.count) + #else + let size = dataPtr.count + #endif + let r = sysWrite(fd, dataPtr.baseAddress, size) + assert(r != 0, "write returned 0 but we tried to write \(size) bytes") guard r > 0 else { throw Error.init(errorCode: Error.ErrorCode.cannotWriteToFileError.rawValue) } From 57c1b60c40bef079b2f2f7b3500344463d2385cd Mon Sep 17 00:00:00 2001 From: Joannis Orlandos Date: Mon, 3 Nov 2025 17:28:47 +0100 Subject: [PATCH 05/10] sin_addr fixes on Windows --- Sources/NIOExtras/WritePCAPHandler.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index 3e28f1f2..81fb4f9b 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -617,8 +617,13 @@ extension ByteBuffer { self.writeInteger(.max, as: UInt8.self) // TTL, `.max` as we don't care about the TTL self.writeInteger(6, as: UInt8.self) // TCP self.writeInteger(0, as: UInt16.self) // checksum + #if os(Windows) + self.writeInteger(la.address.sin_addr.S_un.S_addr, endianness: .host, as: UInt32.self) + self.writeInteger(ra.address.sin_addr.S_un.S_addr, endianness: .host, as: UInt32.self) + #else self.writeInteger(la.address.sin_addr.s_addr, endianness: .host, as: UInt32.self) self.writeInteger(ra.address.sin_addr.s_addr, endianness: .host, as: UInt32.self) + #endif case .v6(let la, let ra): let ipv6PayloadLength = tcpLength let recordLength = ipv6PayloadLength + 4 + 40 // IPv6 header length (+4 gives 32 bits for protocol id) @@ -812,7 +817,7 @@ extension NIOWritePCAPHandler { guard r > 0 else { throw Error.init(errorCode: Error.ErrorCode.cannotWriteToFileError.rawValue) } - return r + return Int(r) } } } From f97b67f73221b6e6ef4d1f432f4e4579f3758fcf Mon Sep 17 00:00:00 2001 From: Joannis Orlandos Date: Mon, 3 Nov 2025 18:27:21 +0100 Subject: [PATCH 06/10] Provide a shim for gettimeofday on windows, plus an alternative to `open` --- Sources/NIOExtras/WritePCAPHandler.swift | 34 +++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index 81fb4f9b..bc8a4edc 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -25,6 +25,24 @@ import Android #elseif canImport(ucrt) import ucrt import WinSDK + +fileprivate func gettimeofday(_ tp: inout timeval) { + var file_time = FILETIME() + var system_time = SYSTEMTIME() + var time: UInt64 = 0 + + // This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC) + // until 00:00:00 January 1, 1970 + let epoch: UInt64 = 116444736000000000 + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = UInt64(file_time.dwLowDateTime) + time += UInt64(file_time.dwHighDateTime) << 32 + + tp.tv_sec = Int32((time - epoch) / 10000000) + tp.tv_usec = Int32(system_time.wMilliseconds * 1000) +} #else import Glibc #endif @@ -114,7 +132,11 @@ struct PCAPRecordHeader { init(payloadLength: Int, addresses: AddressTuple, tcp: TCPHeader) { var tv = timeval() + #if os(Windows) + gettimeofday(&tv) + #else gettimeofday(&tv, nil) + #endif self = .init(payloadLength: payloadLength, addresses: addresses, time: tv, tcp: tcp) } } @@ -732,6 +754,15 @@ extension NIOWritePCAPHandler { fileWritingMode: FileWritingMode = .createNewPCAPFile, errorHandler: @escaping (Swift.Error) -> Void ) throws -> SynchronizedFileSink { + #if os(Windows) + let fd = try path.withCString(encodedAs: UTF16.self) { pathPtr -> CInt in + let fd = _wsopen_s(nil, pathPtr, _O_WRONLY | (fileWritingMode == FileWritingMode.createNewPCAPFile ? (_O_TRUNC | _O_CREAT) : _O_APPEND), _SH_DENYNO, _S_IREAD | _S_IWRITE) + guard fd >= 0 else { + throw SynchronizedFileSink.Error(errorCode: Error.ErrorCode.cannotOpenFileError.rawValue) + } + return fd + } + #else let oflag: CInt = fileWritingMode == FileWritingMode.createNewPCAPFile ? (O_TRUNC | O_CREAT) : O_APPEND let fd = try path.withCString { pathPtr -> CInt in let fd = open(pathPtr, O_WRONLY | oflag, 0o600) @@ -740,6 +771,7 @@ extension NIOWritePCAPHandler { } return fd } + #endif if fileWritingMode == .createNewPCAPFile { let writeOk = NIOWritePCAPHandler.pcapFileHeader.withUnsafeReadableBytes { ptr in @@ -748,7 +780,7 @@ extension NIOWritePCAPHandler { #else let size = ptr.count #endif - sysWrite(fd, ptr.baseAddress, size) == size + return sysWrite(fd, ptr.baseAddress, size) == size } guard writeOk else { throw SynchronizedFileSink.Error(errorCode: Error.ErrorCode.cannotWriteToFileError.rawValue) From dcbc5f37a4029070bd892c64c0c656c17eeb19f1 Mon Sep 17 00:00:00 2001 From: Joannis Orlandos Date: Tue, 4 Nov 2025 20:00:09 +0100 Subject: [PATCH 07/10] Change #if canImport(ucrt) to os(Windows) --- Sources/NIOSOCKS/Messages/SOCKSRequest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift index 3b48597a..63d34b9f 100644 --- a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift +++ b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift @@ -21,7 +21,7 @@ import Darwin import Musl #elseif canImport(Android) import Android -#elseif canImport(ucrt) +#elseif os(Windows) import ucrt import WinSDK #else From ab940272e7446df4ad5bd3ec266228b78410a7fd Mon Sep 17 00:00:00 2001 From: Joannis Orlandos Date: Tue, 4 Nov 2025 21:20:09 +0100 Subject: [PATCH 08/10] Fix lib on Windows --- Package.swift | 6 +++++- Sources/HTTPServerWithQuiescingDemo/main.swift | 5 +++++ Sources/NIOHTTPCompression/HTTPCompression.swift | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index dc3b90f4..0707fe62 100644 --- a/Package.swift +++ b/Package.swift @@ -110,8 +110,12 @@ var targets: [PackageDescription.Target] = [ .target( name: "CNIOExtrasZlib", dependencies: [], + cSettings: [ + .define("_CRT_SECURE_NO_WARNINGS", .when(platforms: [.windows])), + ], linkerSettings: [ - .linkedLibrary("z") + .linkedLibrary("z", .when(platforms: [.linux, .macOS, .iOS, .tvOS, .watchOS, .visionOS])), + .linkedLibrary("zlib", .when(platforms: [.windows])), ] ), .testTarget( diff --git a/Sources/HTTPServerWithQuiescingDemo/main.swift b/Sources/HTTPServerWithQuiescingDemo/main.swift index 23e0d0ef..16697943 100644 --- a/Sources/HTTPServerWithQuiescingDemo/main.swift +++ b/Sources/HTTPServerWithQuiescingDemo/main.swift @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#if !os(Windows) import Dispatch import NIOCore import NIOExtras @@ -133,3 +134,7 @@ private func runServer() throws { } try runServer() +#else +print("This demo is not supported on Windows.") +exit(1) +#endif \ No newline at end of file diff --git a/Sources/NIOHTTPCompression/HTTPCompression.swift b/Sources/NIOHTTPCompression/HTTPCompression.swift index f6968715..d85d090d 100644 --- a/Sources/NIOHTTPCompression/HTTPCompression.swift +++ b/Sources/NIOHTTPCompression/HTTPCompression.swift @@ -107,7 +107,7 @@ public enum NIOCompression: Sendable { // some compression algorithms and so it should be used only when necessary. This completes the current deflate block and // follows it with an empty stored block that is three bits plus filler bits to the next byte, followed by four bytes // (00 00 ff ff). - let bufferSize = Int(deflateBound(&stream, UInt(inputBuffer.readableBytes))) + let bufferSize = Int(deflateBound(&stream, .init(inputBuffer.readableBytes))) var outputBuffer = allocator.buffer(capacity: bufferSize + 5) stream.oneShotDeflate(from: &inputBuffer, to: &outputBuffer, flag: flags) return outputBuffer From 04c6ca7c4e13f526fd77594d20b00406fe191a42 Mon Sep 17 00:00:00 2001 From: Joannis Orlandos Date: Tue, 4 Nov 2025 21:58:52 +0100 Subject: [PATCH 09/10] Don't exit --- Sources/HTTPServerWithQuiescingDemo/main.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/HTTPServerWithQuiescingDemo/main.swift b/Sources/HTTPServerWithQuiescingDemo/main.swift index 16697943..41c14585 100644 --- a/Sources/HTTPServerWithQuiescingDemo/main.swift +++ b/Sources/HTTPServerWithQuiescingDemo/main.swift @@ -136,5 +136,4 @@ private func runServer() throws { try runServer() #else print("This demo is not supported on Windows.") -exit(1) #endif \ No newline at end of file From 004bc1f301bf1c1d263e243525e1becf367c20bb Mon Sep 17 00:00:00 2001 From: Joannis Orlandos Date: Tue, 4 Nov 2025 22:00:56 +0100 Subject: [PATCH 10/10] Use cory's suggestion of makingthe `gettimeofday` signatures similar between Windows and POSIX --- Sources/NIOExtras/WritePCAPHandler.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index bc8a4edc..2e02cac3 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -26,7 +26,7 @@ import Android import ucrt import WinSDK -fileprivate func gettimeofday(_ tp: inout timeval) { +fileprivate func gettimeofday(_ tp: inout timeval, _ tzp: Never?) { var file_time = FILETIME() var system_time = SYSTEMTIME() var time: UInt64 = 0 @@ -132,11 +132,7 @@ struct PCAPRecordHeader { init(payloadLength: Int, addresses: AddressTuple, tcp: TCPHeader) { var tv = timeval() - #if os(Windows) - gettimeofday(&tv) - #else gettimeofday(&tv, nil) - #endif self = .init(payloadLength: payloadLength, addresses: addresses, time: tv, tcp: tcp) } }