Skip to content

Commit 66998ae

Browse files
committed
chore: handle waking from device sleep
1 parent 84d6ad3 commit 66998ae

File tree

3 files changed

+31
-24
lines changed

3 files changed

+31
-24
lines changed

Coder Desktop/Coder Desktop/Views/VPNMenu.swift

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@ struct VPNMenu<VPN: VPNService, S: Session>: View {
55
@EnvironmentObject var session: S
66
@Environment(\.openSettings) private var openSettings
77

8-
// There appears to be a race between the VPN service reporting itself as disconnected,
9-
// and the system extension process exiting. When the VPN is toggled off and on quickly,
10-
// an error is shown: "The VPN session failed because an internal error occurred".
11-
// This forces the user to wait a few seconds before they can toggle the VPN back on.
12-
@State private var waitCleanup = false
13-
private var waitCleanupDuration: Duration = .seconds(6)
14-
158
let inspection = Inspection<Self>()
169

1710
var body: some View {
@@ -23,7 +16,7 @@ struct VPNMenu<VPN: VPNService, S: Session>: View {
2316
Toggle(isOn: Binding(
2417
get: { vpn.state == .connected || vpn.state == .connecting },
2518
set: { isOn in Task {
26-
if isOn { await vpn.start() } else { await stop() }
19+
if isOn { await vpn.start() } else { await vpn.stop() }
2720
}
2821
}
2922
)) {
@@ -93,21 +86,11 @@ struct VPNMenu<VPN: VPNService, S: Session>: View {
9386
}
9487

9588
private var vpnDisabled: Bool {
96-
waitCleanup ||
97-
!session.hasSession ||
89+
!session.hasSession ||
9890
vpn.state == .connecting ||
9991
vpn.state == .disconnecting ||
10092
vpn.state == .failed(.systemExtensionError(.needsUserApproval))
10193
}
102-
103-
private func stop() async {
104-
await vpn.stop()
105-
waitCleanup = true
106-
Task {
107-
try? await Task.sleep(for: waitCleanupDuration)
108-
waitCleanup = false
109-
}
110-
}
11194
}
11295

11396
func openSystemExtensionSettings() {

Coder Desktop/VPN/PacketTunnelProvider.swift

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
4747
options _: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void
4848
) {
4949
logger.info("startTunnel called")
50+
start(completionHandler)
51+
}
52+
53+
// called by `startTunnel` and on `wake`
54+
func start(_ completionHandler: @escaping (Error?) -> Void) {
5055
guard manager == nil else {
5156
logger.error("startTunnel called with non-nil Manager")
5257
completionHandler(makeNSError(suffix: "PTP", desc: "Already running"))
@@ -95,8 +100,13 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
95100
with _: NEProviderStopReason, completionHandler: @escaping () -> Void
96101
) {
97102
logger.debug("stopTunnel called")
103+
teardown(completionHandler)
104+
}
105+
106+
// called by `stopTunnel` and `sleep`
107+
func teardown(_ completionHandler: @escaping () -> Void) {
98108
guard let manager else {
99-
logger.error("stopTunnel called with nil Manager")
109+
logger.error("teardown called with nil Manager")
100110
completionHandler()
101111
return
102112
}
@@ -121,15 +131,25 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
121131
}
122132
}
123133

134+
// sleep and wake reference: https://developer.apple.com/forums/thread/95988
124135
override func sleep(completionHandler: @escaping () -> Void) {
125-
// Add code here to get ready to sleep.
126136
logger.debug("sleep called")
127-
completionHandler()
137+
teardown(completionHandler)
128138
}
129139

130140
override func wake() {
131-
// Add code here to wake up.
132141
logger.debug("wake called")
142+
reasserting = true
143+
currentSettings = .init(tunnelRemoteAddress: "127.0.0.1")
144+
setTunnelNetworkSettings(nil)
145+
start { error in
146+
if let error {
147+
self.logger.error("error starting tunnel after wake: \(error.localizedDescription)")
148+
self.cancelTunnelWithError(error)
149+
} else {
150+
self.reasserting = false
151+
}
152+
}
133153
}
134154

135155
// Wrapper around `setTunnelNetworkSettings` that supports merging updates

Coder Desktop/VPNLib/Download.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,14 @@ public func download(src: URL, dest: URL) async throws(DownloadError) {
109109
}
110110
}
111111
// TODO: Add Content-Length headers to coderd, add download progress delegate
112+
let sessionConfig = URLSessionConfiguration.default
113+
// The tunnel might be asked to start before the network interfaces have woken up from sleep
114+
sessionConfig.waitsForConnectivity = true
115+
let session = URLSession(configuration: sessionConfig)
112116
let tempURL: URL
113117
let response: URLResponse
114118
do {
115-
(tempURL, response) = try await URLSession.shared.download(for: req)
119+
(tempURL, response) = try await session.download(for: req)
116120
} catch {
117121
throw .networkError(error)
118122
}

0 commit comments

Comments
 (0)