Skip to content

Add support for Windows - #567

Open
Joannis wants to merge 8 commits into
apple:mainfrom
Joannis:jo/windows-support
Open

Add support for Windows#567
Joannis wants to merge 8 commits into
apple:mainfrom
Joannis:jo/windows-support

Conversation

@Joannis

@Joannis Joannis commented Nov 4, 2025

Copy link
Copy Markdown

Fix compilation of NIOSSL on Windows
Note: This code adds some things we might not want to keep around.

  • OPENSSL_NO_ASM flag which I neededto get it to work
  • Windows directory listing using FindFirstFileA (not properly tested)
  • mlock -> VirtualLock on Windows (doesn't yet check if locking was successful)

Fix compilation of NIOSSL on Windows
Note: This code adds some things we might not want to keep around.
- OPENSSL_NO_ASM flag which I neededto get it to work
- Windows directory listing using FindFirstFileA (not properly tested)
- mlock -> VirtualLock on Windows (doesn't yet check if locking was successful)
@Joannis
Joannis marked this pull request as ready for review December 11, 2025 21:20
@Joannis Joannis changed the title DRAFT: Add support for Windows Add support for Windows Dec 11, 2025
@Joannis

Joannis commented Dec 11, 2025

Copy link
Copy Markdown
Author

Still needs to set up Windows CI, but if possible I'd like to ensure the tests still succeed on existing platforms

// Force a reference to _tls_used to make the linker create the TLS directory
// if it's not already there. (E.g. if __declspec(thread) is not used). Force
// a reference to p_thread_callback_boringssl to prevent whole program
// a reference to CNIOBoringSSL_p_thread_callback_boringssl to prevent whole program

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We aren't taking any changes to BoringSSL itself. They shouldn't be necessary.

FindNextFileA(dir, &fileData)
return self.path + name
}
#else

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need an #if around the whole block? It'd be nicer to factor the calls out to helpers that can have a small #if.

Motivation:

NIOSSL is the last missing piece for building projects such as SwiftMail
on Windows: swift-nio itself already builds there, but every NIOSSL file
with a platform import ladder ends in #error("unsupported os").

Modifications:

- Define OPENSSL_NO_ASM for CNIOBoringSSL on Windows: the vendored
  assembly is only generated for ELF/Mach-O targets, and SwiftPM cannot
  build the NASM sources BoringSSL uses for Windows. No vendored
  BoringSSL sources are modified.
- Extend the platform import ladders with ucrt/WinSDK branches.
- PosixPort: read errno through _errno() (the errno macro is not
  importable on Windows), keep readlink/lstat POSIX-only, and implement
  Posix.mlock/munlock on Windows via VirtualLock/VirtualUnlock with
  proper error propagation.
- DirectoryContents: factor the platform-specific directory iteration
  into a small nextEntryName() helper; implement Windows iteration with
  FindFirstFileW/FindNextFileW (wide-char APIs, correct wildcard pattern
  and exhaustion handling).
- Skip the c_rehash symlink check on Windows, where openssl rehash
  creates plain copies instead of symlinks.
- Convert BIO_get_mem_data lengths (C long, 32-bit on Windows) to Int
  explicitly.
- Handle st_mode being CUnsignedShort on Windows.
- Pass inet_ntop's buffer size as size_t on Windows via a small helper.
- Replace raw SOL_SOCKET/TCP_NODELAY channel options in the example
  executables with the cross-platform typed options from NIOCore.
- Enable a Windows 6.3 build job in CI, mirroring apple/swift-nio.

Result:

swift build succeeds on Windows (Swift 6.3.1); no functional change on
any other platform.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
odrobnik and others added 3 commits July 18, 2026 21:49
- Use the typed .socketOption(.so_reuseaddr) channel option instead of
  raw SOL_SOCKET/SO_REUSEADDR.
- Map O_CLOEXEC to O_NOINHERIT on Windows.
- makeTemporaryFile: mkstemps does not exist on Windows; use a random
  file name there.
- randomSerialNumber: read from BoringSSL's CSPRNG instead of
  /dev/urandom, which is portable to all platforms.
- Replace usleep with Thread.sleep(forTimeInterval:).
- Import WinSDK for in_addr/inet_pton in the SAN test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Define _CRT_SECURE_NO_WARNINGS for the vendored BoringSSL sources.
- Use fopen_s/strerror_s in PosixPort (and print the actual strerror
  message in the asserts rather than the pointer).
- Give the tests non-deprecated unlink/fdopen wrappers and an
  errnoDescription helper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…support

Uses the ours strategy deliberately: this branch already carries all of
jo/windows-support's changes, reimplemented on current main (and drops
the vendored-BoringSSL edits per review). Merging it makes the PR into
jo/windows-support fast-forward-clean without reintroducing any of the
old branch's content.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Windows support rebased on main, review feedback addressed
arun2030 pushed a commit to arun2030/swift-nio-ssl that referenced this pull request Jul 24, 2026
…t ucrt

Real Windows CI run (DataDock AI, run 30087599273) failed compiling this
file with:
  cannot find 'in6_addr'/'inet_ntop'/'AF_INET'/'AF_INET6'/'socklen_t' in scope

Root cause: this file's `#elseif os(Windows) / import ucrt` branch (added by
upstream PR apple#567, "Add support for Windows") only imports the C runtime.
in_addr/in6_addr/inet_ntop/AF_INET/AF_INET6 are Winsock2 symbols that live in
WinSDK, not ucrt -- PR apple#567 covered other files needing Windows support but
missed this one (confirmed: no `canImport(WinSDK)`/`os(Windows)` symbol
handling existed anywhere else in this file before this commit).

Fix mirrors swift-nio's own already-Windows-portable equivalent code
(NIOCore/BSDSocketAPI.swift, NIOCore/SocketAddresses.swift): import the
specific WinSDK symbols needed via Swift's per-symbol import syntax, and
alias them locally to the POSIX names (`in_addr`, `in6_addr`) this file
already uses, so the rest of the file needs no changes.

`socklen_t` in the error was a red herring from the same missing-import
family, not a separate gap -- ipv4ToString/ipv6ToString already special-case
Windows to pass `pointer.count` (an Int, matching Winsock's `size_t`
StringBufSize parameter) instead of casting to `socklen_t`, so no
`socklen_t` typealias is needed here.
@odrobnik

Copy link
Copy Markdown
Contributor

@Lukasa Maybe give it another look now? We’ve worked in your review comments.

@glbrntt
glbrntt requested a review from Lukasa August 6, 2026 07:34
@Budoman

Budoman commented Aug 21, 2026

Copy link
Copy Markdown

Independent Windows CI datapoints for this PR, from a clean windows-2022 GitHub runner (Swift 6.3.3), two runs.

Run 1, this branch as-is: builds with zero errors (main scores 1,633 errors on the same runner, so this PR takes Windows from unbuildable to clean). The test suite is green across the unit suites (ByteBufferBIO, CertificateVerification, ClientSNI, CustomPrivateKey, IdentityVerification, ALPN, and the rest). Five failures, all in NIOSSLIntegrationTest and all in the close/shutdown family:
testCloseModeOutputServerAndClient, testCloseModeOutputTriggersFlush, testMultipleCloseOutput, testReceivingGibberishAfterAttemptingToClose, testSubsequentWritesFailAfterCloseModeOutput (errors: "Already closed", "uncleanShutdown", one XCTAssertNotNil).

Run 2, same branch with swift-nio overridden to apple/swift-nio#3704's head (the Windows data-path backpressure fix): identical result, same five failures. So these are not downstream of the known WSAEWOULDBLOCK data-path bug; they look like Windows half-close/shutdown semantics in their own right.

Happy to share the workflow or raw logs, and to re-run against any revision if that helps get this landed. Even as-is, this PR takes swift-nio-ssl from 1,633 errors to a clean build with a green core suite on Windows.

@Budoman

Budoman commented Aug 21, 2026

Copy link
Copy Markdown

Follow-up on the five failures: root-caused, and none of them is this PR's fault.

Four are a swift-nio defect. SelectorWSAPoll maps POLLHUP to .reset, but WSAPoll has no POLLRDHUP, so an orderly peer FIN and an abortive RST arrive identically and both take the terminal reset path: the selector can never emit .readEOF, which makes allowRemoteHalfClosure unimplementable on Windows, and a live half-closed channel gets force-closed. Instrumented on windows-2022: 8 POLLHUP deliveries, 4 of them force-closing an open, active channel. One-line fix, mapping POLLHUP to .readEOF so recv becomes the authority (0 = orderly FIN honouring half-closure; WSAECONNRESET/WSAECONNABORTED = genuine abort): Budoman/swift-nio@windows-half-closure (7fc88bf, stacked on apple/swift-nio#3704). Validated: the four close-mode tests pass, the full swift-nio-ssl suite is green, and swift-nio's own testHalfCloseOwnOutput flips from failing to passing, with zero new failures and macOS unaffected.

The fifth (testReceivingGibberishAfterAttemptingToClose) is a test-only issue: its regex requires a forward slash before BoringSSL's __FILE__ path, which is backslash-separated on Windows. Test-only fix, one hunk: Budoman/swift-nio-ssl@windows-close-mode (803d461, based on jo/windows-support), free to lift straight into this branch.

With both applied, this PR's suite is fully green on windows-2022 / Swift 6.3.3. One structural note: Windows half-closure currently has no upstream CI guard because StreamChannelTest is skipped wholesale (NIOPosix has no PipeChannel on Windows), which is why the selector defect could ship undetected. We are opening the swift-nio fix as a PR so it can be reviewed properly.

@Budoman

Budoman commented Aug 21, 2026

Copy link
Copy Markdown

One more independent Windows datapoint from assembling a real server on top of this branch: a duplicate-symbol collision between CNIOBoringSSL and swift-crypto's CCryptoBoringSSL that will hit anyone linking both into one Windows binary (which any TLS server using swift-crypto for application crypto will do).

Symptom (lld, windows-2022, Swift 6.3.3):

lld-link: error: duplicate symbol: p_thread_callback_boringssl
>>> defined at .build\checkouts\swift-crypto\Sources\CCryptoBoringSSL\crypto\thread_win.cc:157
>>> defined at .build\checkouts\swift-nio-ssl\Sources\CNIOBoringSSL\crypto\thread_win.cc:159

Root cause: both projects vendor BoringSSL behind a symbol prefix, but p_thread_callback_boringssl in thread_win.cc escapes both prefix schemes. It is declared extern "C" with deliberately external linkage (the .CRT$XLC TLS-callback registration plus a /INCLUDE: linker pragma depend on that), and the prefix headers do not cover it; the comment in the file even anticipates this: "in the prefixed build, |p_thread_callback_boringssl| may be a macro", but no such macro is defined in either project. The two copies therefore collide at link time. macOS and Linux never see this because the symbol only exists under _WIN32.

Fix that worked for us: rename the symbol in one copy. We renamed CNIOBoringSSL's to p_thread_callback_nio_boringssl (all nine occurrences in thread_win.cc; the /INCLUDE: pragmas stringify the same identifier, so the rename carries through and the CRT registration stays intact): Budoman/swift-nio-ssl@3baecb7. With that one file changed, our full multi-product build (server with NIOSSL TLS + swift-crypto, several CLI tools) completes cleanly on windows-2022.

A more principled home for the fix might be BoringSSL's prefixing mechanism itself (adding the symbol to the prefix list), which would fix it for every downstream vendored copy at once. Happy to turn either form into a PR if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants