diff --git a/NextcloudTalk/Calls/NCCallController.swift b/NextcloudTalk/Calls/NCCallController.swift index d8f8f6459..06b4e33e2 100644 --- a/NextcloudTalk/Calls/NCCallController.swift +++ b/NextcloudTalk/Calls/NCCallController.swift @@ -1510,6 +1510,13 @@ internal class NCCallController: NSObject, NCPeerConnectionDelegate, NCSignaling } } + func signalingControllerSessionExpired(_ signalingController: NCSignalingController!) { + NCLog.log("Internal signaling session expired for \(self.room.token), ending call") + + // The join failure path already shows the reason in the foreground and ends via CallKit in the background + self.delegate?.callControllerDidFailedJoiningCall(self, statusCode: 404, errorReason: self.getJoinCallErrorReason(404)) + } + // MARK: - NCCameraController delegate func didDrawFirstFrameOnLocalView() { diff --git a/NextcloudTalk/WebRTC/NCSignalingController.h b/NextcloudTalk/WebRTC/NCSignalingController.h index c48d44f8b..f71af52b7 100644 --- a/NextcloudTalk/WebRTC/NCSignalingController.h +++ b/NextcloudTalk/WebRTC/NCSignalingController.h @@ -15,6 +15,11 @@ - (void)signalingController:(NCSignalingController *)signalingController didReceiveSignalingMessage:(NSDictionary *)message; +@optional + +// The server no longer knows our session in this room, which polling cannot recover from +- (void)signalingControllerSessionExpired:(NCSignalingController *)signalingController; + @end typedef void (^SignalingSettingsUpdatedCompletionBlock)(SignalingSettings *signalingSettings); diff --git a/NextcloudTalk/WebRTC/NCSignalingController.m b/NextcloudTalk/WebRTC/NCSignalingController.m index 5f91add10..194e33763 100644 --- a/NextcloudTalk/WebRTC/NCSignalingController.m +++ b/NextcloudTalk/WebRTC/NCSignalingController.m @@ -15,6 +15,8 @@ @interface NCSignalingController() { NCRoom *_room; BOOL _shouldStopPullingMessages; + NSInteger _consecutivePullFailures; + NSInteger _consecutiveSessionGoneFailures; SignalingSettings *_signalingSettings; NSURLSessionTask *_getSignalingSettingsTask; NSURLSessionTask *_pullSignalingMessagesTask; @@ -22,6 +24,9 @@ @interface NCSignalingController() @end +// About 45s of retrying with the backoff below +static NSInteger const kMaxConsecutiveSessionGoneFailures = 5; + @implementation NCSignalingController - (instancetype)initForRoom:(NCRoom *)room @@ -92,6 +97,8 @@ - (void)startPullingSignalingMessages [NCLog log:[NSString stringWithFormat:@"Start pulling internal signaling messages for token %@", _room.token]]; _shouldStopPullingMessages = NO; + _consecutivePullFailures = 0; + _consecutiveSessionGoneFailures = 0; [self pullSignalingMessages]; } @@ -108,6 +115,47 @@ - (void)pullSignalingMessages return; } + if (error) { + self->_consecutivePullFailures += 1; + + // A 404 means our session is gone server side, which polling cannot recover from + if (error.responseStatusCode == 404) { + self->_consecutiveSessionGoneFailures += 1; + + if (self->_consecutiveSessionGoneFailures >= kMaxConsecutiveSessionGoneFailures) { + [NCLog log:[NSString stringWithFormat:@"Internal signaling has no session for token %@ anymore, ending the call", self->_room.token]]; + + self->_shouldStopPullingMessages = YES; + + if ([self.observer respondsToSelector:@selector(signalingControllerSessionExpired:)]) { + [self.observer signalingControllerSessionExpired:self]; + } + + return; + } + } else { + self->_consecutiveSessionGoneFailures = 0; + } + + // The request is only re-armed from here, so retrying immediately would busy loop + NSTimeInterval delay = MIN(pow(2, MIN(self->_consecutivePullFailures, 4)), 16); + + [NCLog log:[NSString stringWithFormat:@"Could not pull internal signaling messages (failure %ld, status %ld), retrying in %.0fs", (long)self->_consecutivePullFailures, (long)error.responseStatusCode, delay]]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + if (self->_shouldStopPullingMessages) { + return; + } + + [self pullSignalingMessages]; + }); + + return; + } + + self->_consecutivePullFailures = 0; + self->_consecutiveSessionGoneFailures = 0; + for (NSDictionary *message in messages) { if ([self.observer respondsToSelector:@selector(signalingController:didReceiveSignalingMessage:)]) { [self.observer signalingController:self didReceiveSignalingMessage:message];