Skip to content

Conversation

@marcprux
Copy link
Contributor

@marcprux marcprux commented Dec 23, 2025

As reported at skiptools/skip#559 and https://github.com/orgs/skiptools/discussions/568, the Swift SDK for Android's libcurl used by FoundationNetworking is not being built with websockets support, resulting in a fatalError when trying to use URLSession.websocketTask() or URLSessionWebSocketTask:

marc@zap wsdemo % skip android run WSDemo
[0/1] Planning build
Building for debugging...
[0/4] Write swift-version-26D6868F3D3D2BFA.txt
[2/6] Compiling WSDemo WSDemo.swift
[3/6] Emitting module WSDemo
[4/7] Wrapping AST for WSDemo for debugging
[5/7] Write Objects.LinkFileList
[6/7] Linking WSDemo
Build complete! (0.54s)
[✓] Check Swift Package (0.2s)
[✓] Connecting to Android (0.08s)
[✓] Copying executable files (0.29s)
Swift/ErrorType.swift:254: Fatal error: Error raised at top level: Error Domain=NSURLErrorDomain Code=-1002 "(null)"UserInfo={NSErrorFailingURLKey=wss://echo.websocket.org, NSLocalizedDescription=WebSockets not supported by libcurl, NSErrorFailingURLStringKey=wss://echo.websocket.org}
Current stack trace:
0    libswiftCore.so                    0x000000769f68d3c8 swift_reportError + 44
1    libswiftCore.so                    0x000000769f727248 _swift_stdlib_reportFatalErrorInFile + 128
2    libswiftCore.so                    0x000000769f3dc92c <unavailable> + 1841452
3    libswiftCore.so                    0x000000769f538d14 _assertionFailure(_:_:file:line:flags:) + 160
4    libswiftCore.so                    0x000000769f586110 swift_errorInMain + 628
5    WSDemo                             0x0000005ac350f2b8 <unavailable> + 12984
6    libswift_Concurrency.so            0x00000076a20d6640 <unavailable> + 558656
7    libswift_Concurrency.so            0x00000076a20d7284 swift_job_run + 164
8    libdispatch.so                     0x00000076a1f10bf0 <unavailable> + 240624
9    libdispatch.so                     0x00000076a1f11604 <unavailable> + 243204
10   libdispatch.so                     0x00000076a1f176e8 <unavailable> + 268008
11   libc.so                            0x00000076a51256ac <unavailable> + 833196
12   libc.so                            0x00000076a50c2220 <unavailable> + 426528
Trap 

All we need to do is turn it on with ENABLE_WEBSOCKETS=ON. When building this PR separately (see artifacts at https://github.com/swift-android-sdk/swift-docker/actions/runs/20467831234) and copying over the new libFoundationNetworking.so, the test passes:

marc@zap wsdemo % skip android run WSDemo
Building for debugging...
[0/3] Write swift-version-26D6868F3D3D2BFA.txt
Build complete! (0.10s)
[✓] Check Swift Package (0.21s)
[✓] Connecting to Android (0.02s)
[✓] Copying executable files (0.28s)
received string: Request served by 4d896d95b55478
received string: Hello from Swift!

CC @swiftlang/android-workgroup
@al45tair: you may want to do the same for the Static SDK, since I expect you would also see similar websocket failures.

Here's the full test program for reference:

import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
#if canImport(AndroidNative)
import AndroidNative
#endif

@main
struct WSDemo {
    static func main() async throws {
        #if os(Android)
        try AndroidBootstrap.setupCACerts() // needed in order to use https
        #endif

        let session = URLSession(configuration: .default)
        let url = URL(string: "wss://echo.websocket.org")!
        let webSocketTask = session.webSocketTask(with: url)
        webSocketTask.resume()
        defer { webSocketTask.cancel(with: .normalClosure, reason: nil) }

        _ = try await receiveString() // initial connect message like "Request served by 4d896d95b55478"

        let messageToSend = "Hello from Swift!"
        let message = URLSessionWebSocketTask.Message.string(messageToSend)
        try await webSocketTask.send(message)
        let response = try await receiveString()
        assert(messageToSend == response)

        func receiveString() async throws -> String {
            let receivedMessage = try await webSocketTask.receive()
            switch receivedMessage {
            case .string(let string):
                print("received string: \(string)")
                return string
            case .data(let data):
                fatalError("received data: \(data)")
            @unknown default:
                fatalError("Received unknown message type")
            }
        }
    }
}

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.

1 participant