Skip to content

Commit dd96829

Browse files
authored
support stateful reconnect (#96)
1 parent 733af27 commit dd96829

File tree

9 files changed

+844
-70
lines changed

9 files changed

+844
-70
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
enum ConnectionFeature: String, CaseIterable {
5+
case Reconnect = "reconnect"
6+
case Resend = "resend"
7+
case Disconnected = "disconnected"
8+
}
9+
410
protocol ConnectionProtocol: AnyObject, Sendable {
511
func onReceive(_ handler: @escaping Transport.OnReceiveHandler) async
612
func onClose(_ handler: @escaping Transport.OnCloseHander) async
713
func start(transferFormat: TransferFormat) async throws
814
func send(_ data: StringOrData) async throws
915
func stop(error: Error?) async
1016
var inherentKeepAlive: Bool { get async }
17+
var features: [ConnectionFeature: Any] { get async }
18+
func setFeature(feature: ConnectionFeature, value: Any) async
1119
}

Sources/SignalRClient/HttpConnection.swift

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ actor HttpConnection: ConnectionProtocol {
8484
private var accessTokenFactory: (@Sendable () async throws -> String?)?
8585
private var inherentKeepAlivePrivate: Bool = false
8686

87-
public var features: [String: Any] = [:]
87+
public var features: [ConnectionFeature: Any] = [:]
8888
public var baseUrl: String
8989
public var connectionId: String?
9090
public var inherentKeepAlive: Bool {
@@ -359,9 +359,39 @@ actor HttpConnection: ConnectionProtocol {
359359

360360
private func startTransport(url: String, transferFormat: TransferFormat) async throws {
361361
await transport!.onReceive(self.onReceive)
362-
await transport!.onClose { [weak self] error in
363-
guard let self = self else { return }
364-
await self.handleConnectionClose(error: error)
362+
if (self.features[ConnectionFeature.Reconnect] as? Bool) == true {
363+
await transport!.onClose { [weak self] error in
364+
var callStop = false;
365+
guard let self = self else { return }
366+
if await (self.features[ConnectionFeature.Reconnect] as? Bool) == true {
367+
do {
368+
if let disconnectedHandler = await self.features[ConnectionFeature.Disconnected] as? () async -> Void {
369+
Task {
370+
await disconnectedHandler();
371+
}
372+
}
373+
try await self.transport?.connect(url: url, transferFormat: transferFormat);
374+
if let resendHandler = await self.features[ConnectionFeature.Resend] as? () -> Void {
375+
resendHandler();
376+
}
377+
} catch {
378+
callStop = true;
379+
}
380+
}
381+
else {
382+
await self.handleConnectionClose(error: error);
383+
return;
384+
}
385+
if callStop {
386+
await self.handleConnectionClose(error: error);
387+
}
388+
}
389+
}
390+
else {
391+
await transport!.onClose { [weak self] error in
392+
guard let self = self else { return }
393+
await self.handleConnectionClose(error: error)
394+
}
365395
}
366396

367397
do {
@@ -435,6 +465,12 @@ actor HttpConnection: ConnectionProtocol {
435465
if let useStatefulReconnect = options.useStatefulReconnect, useStatefulReconnect {
436466
queryItems.append(URLQueryItem(name: "useStatefulReconnect", value: "true"))
437467
}
468+
else {
469+
if (queryItems.first(where: { $0.name == "useStatefulReconnect" })?.value ?? "false") == "true" {
470+
options.useStatefulReconnect = true;
471+
}
472+
}
473+
438474
negotiateUrlComponents.queryItems = queryItems
439475
return negotiateUrlComponents.url!.absoluteString
440476
}
@@ -476,7 +512,7 @@ actor HttpConnection: ConnectionProtocol {
476512
let transferFormats = endpoint.transferFormats.compactMap { TransferFormat($0) }
477513
if transferFormats.contains(requestedTransferFormat) {
478514
do {
479-
features["reconnect"] = (transportType == .webSockets && useStatefulReconnect) ? true : nil
515+
self.features[ConnectionFeature.Reconnect] = (transportType == .webSockets && useStatefulReconnect) ? true : false;
480516
let constructedTransport = try await constructTransport(transport: transportType)
481517
return constructedTransport
482518
} catch {
@@ -511,4 +547,8 @@ actor HttpConnection: ConnectionProtocol {
511547
}
512548
return urlRequest
513549
}
550+
551+
public func setFeature(feature: ConnectionFeature, value: Any) async {
552+
features[feature] = value
553+
}
514554
}

0 commit comments

Comments
 (0)