Files
firezone/swift/apple/FirezoneNetworkExtension/SessionEventLoop.swift
Thomas Eizinger 0e48d27b5a feat(ffi): make all calls infallible (#10621)
In the spirit of making Firezone as robust as possible, we make the FFI
calls infallible and complete as much of the task as possible. For
example, we don't fail `setDns` entirely just because we cannot parse a
single DNS server's IP.

Resolves: #10611
2025-10-20 01:03:26 +00:00

75 lines
1.9 KiB
Swift

import FirezoneKit
import Foundation
/// Commands that can be sent to the Session.
enum SessionCommand {
case disconnect
case setInternetResourceState(Bool)
case setDns([String])
case reset(String)
}
/// Runs the session event loop, owning the Session lifecycle.
///
/// When either task completes, both are cancelled and the function returns.
/// This ensures the Session's Drop is called on the Rust side.
func runSessionEventLoop(
session: Session,
commandReceiver: Receiver<SessionCommand>,
eventSender: Sender<Event>
) async {
// Multiplex between commands and events
await withTaskGroup(of: Void.self) { group in
group.addTask {
await forwardEvents(from: session, to: eventSender)
}
group.addTask {
await forwardCommands(from: commandReceiver, to: session)
}
// Wait for first task to complete, then cancel all
_ = await group.next()
group.cancelAll()
}
}
/// Forwards events from the session to the event sender.
private func forwardEvents(from session: Session, to eventSender: Sender<Event>) async {
while !Task.isCancelled {
guard let event = await session.nextEvent() else {
Log.log("Event stream ended")
break
}
eventSender.send(event)
}
}
/// Forwards commands from the command receiver to the session.
private func forwardCommands(from commandReceiver: Receiver<SessionCommand>, to session: Session) async {
for await command in commandReceiver.stream {
if Task.isCancelled {
Log.log("Command forwarding cancelled")
break
}
switch command {
case .disconnect:
session.disconnect()
case .setInternetResourceState(let active):
session.setInternetResourceState(active: active)
case .setDns(let servers):
session.setDns(dnsServers: servers)
case .reset(let reason):
session.reset(reason: reason)
}
}
Log.log("Command stream ended")
}