Skip to content

Add WsClient and HttpClient implementations for livekit-net. - #1073

Open
jhugman wants to merge 5 commits into
livekit:mainfrom
jhugman:jhugman/initialize-livekit-net
Open

Add WsClient and HttpClient implementations for livekit-net.#1073
jhugman wants to merge 5 commits into
livekit:mainfrom
jhugman:jhugman/initialize-livekit-net

Conversation

@jhugman

@jhugman jhugman commented Jul 30, 2026

Copy link
Copy Markdown

Initialize livekit-net with a Swift implementation of the WsClient and HttpClient. Production versions of these use URLSession.

This requires livekit/rust-sdks#1290 to be built and released.

For testing, there is a self-test which calls through Rust into dummy implementations of WsClient and HttpClient.

@devin-ai-integration devin-ai-integration Bot left a comment

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.

Devin Review found 2 potential issues.

View 2 additional findings in Devin Review.

Open in Devin Review

Comment on lines +76 to +87
func recv() async throws -> Data? {
guard task.closeCode == .invalid else { return nil }
do {
let message: URLSessionWebSocketTask.Message = try await withCheckedThrowingContinuation { cont in
task.receive { cont.resume(with: $0) }
}
return Self.decode(message)
} catch {
if task.closeCode != .invalid { return nil }
throw Self.map(error)
}
}

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.

🟡 Cancelled websocket operations never finish, keeping the socket and its session alive

The websocket receive and connect steps wait on a callback with no cancellation path (withCheckedThrowingContinuation at Sources/LiveKit/Net/LKNetWSClient.swift:79-81), so when the caller cancels, the wait never ends on an idle socket and the connection is held open forever.
Impact: Cancelled network work can hang indefinitely and leak the underlying socket and session for the lifetime of the app.

Missing cooperative cancellation compared to the existing WebSocket implementation

Sources/LiveKit/Support/Network/WebSocket.swift:57-64 wraps the connect continuation in withTaskCancellationHandler and also has Delegate.cancelConnection() (Sources/LiveKit/Support/Network/WebSocket.swift:71-74,122-127) so a cancelled connect resumes the continuation; WebSocket.AsyncIterator.next() (Sources/LiveKit/Support/Network/WebSocket.swift:83-101) wraps task.receive in withTaskCancellationHandler and returns nil when Task.isCancelled.

The new LKNetWSConnection.init (Sources/LiveKit/Net/LKNetWSClient.swift:48-56) and recv() have neither: on cancellation the continuation stays suspended until the underlying URLSession callback eventually fires. For recv() on an idle, healthy socket no callback ever fires, so the suspended async frame keeps self (and thus the URLSessionWebSocketTask and the URLSession created with a delegate) alive indefinitely, and deinit's cleanup never runs. The same lack of cancellation applies to LKNetHTTPClientLive.request (Sources/LiveKit/Net/LKNetHTTPClient.swift:22-29), where the data task is not cancelled when the awaiting task is cancelled.

AGENTS.md: "Long-running Task requires cooperative cancellation to avoid memory leaks".

Prompt for agents
LKNetWSConnection.recv() and LKNetWSConnection.init(), plus LKNetHTTPClientLive.request(), await callback-based URLSession APIs via withCheckedThrowingContinuation without any cancellation handling. Mirror the pattern already used in Sources/LiveKit/Support/Network/WebSocket.swift: wrap the continuations in withTaskCancellationHandler, cancel/close the underlying task (or resume a stored connect continuation via a cancelConnection()-style method on WSConnectDelegate) from the onCancel closure, and treat cancellation in recv() as end-of-stream (return nil) as the existing AsyncIterator does. For the HTTP client, cancel the URLSessionDataTask in onCancel so the continuation is always resumed.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@@ -0,0 +1,23 @@
// Copyright 2026 LiveKit (Apache-2.0)

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.

🟡 New source and test files are missing the required license header

The newly added files start with a one-line copyright comment instead of the mandated Apache header block (// Copyright 2026 LiveKit (Apache-2.0) at Sources/LiveKit/Net/NetBootstrap.swift:1), so the repository's formatting check will rewrite/fail on them.
Impact: The formatting/lint gate flags these files, blocking a clean build of the change.

Header enforced by .swiftformat

.swiftformat declares --header "/*\n * Copyright {year} LiveKit\n * ... Apache License ... */", and every existing source file (e.g. Sources/LiveKit/Support/Network/WebSocket.swift:1-16) carries that block. All six new files use the abbreviated one-liner instead: Sources/LiveKit/Net/NetBootstrap.swift:1, Sources/LiveKit/Net/LKNetHTTPClient.swift:1, Sources/LiveKit/Net/LKNetWSClient.swift:1, Tests/LiveKitCoreTests/Net/LKNetHTTPClientLiveTests.swift:1, Tests/LiveKitCoreTests/Net/LKNetWSConnectionTests.swift:1, Tests/LiveKitCoreTests/Net/NetBootstrapTests.swift:1, Tests/LiveKitCoreTests/Net/NetFFIRoundTripTests.swift:1.

Suggested change
// Copyright 2026 LiveKit (Apache-2.0)
/*
* Copyright 2026 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@pblazej

pblazej commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Nice, but how it's consumed on the Rust side now? It's not?

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.

2 participants