diff --git a/Primus/Core/PrimusConnectOptions.h b/Primus/Core/PrimusConnectOptions.h index ae8e5d9..f226a6b 100644 --- a/Primus/Core/PrimusConnectOptions.h +++ b/Primus/Core/PrimusConnectOptions.h @@ -12,9 +12,8 @@ @property (nonatomic) PrimusReconnectOptions *reconnect; // Stores the back off configuration @property (nonatomic) NSArray *strategy; // Default reconnect strategies -@property (nonatomic) NSTimeInterval timeout; // Connection timeout duration -@property (nonatomic) NSTimeInterval ping; // Heartbeat ping interval -@property (nonatomic) NSTimeInterval pong; // Heartbeat pong response timeout. +@property (nonatomic) NSTimeInterval pingInterval; // Interval at which heartbeats are sent +@property (nonatomic) NSTimeInterval pingTimeout; // Max time to wait for a server ping @property (nonatomic) BOOL autodetect; // Autodetect transformer and parser @property (nonatomic) BOOL manual; // Manual connection @property (nonatomic) BOOL stayConnectedInBackground; // Stay connected while app is in background diff --git a/Primus/Core/PrimusConnectOptions.m b/Primus/Core/PrimusConnectOptions.m index 8879e55..18474bb 100644 --- a/Primus/Core/PrimusConnectOptions.m +++ b/Primus/Core/PrimusConnectOptions.m @@ -32,9 +32,8 @@ - (id)initWithTransformerClass:(Class)transformerClass andStrategy:(NSArray *)st if (self) { _reconnect = [[PrimusReconnectOptions alloc] init]; _strategy = strategy ?: [NSArray array]; - _timeout = 10; - _ping = 25; - _pong = 10; + _pingTimeout = 45; + _pingInterval = 30; _autodetect = YES; _manual = NO; _stayConnectedInBackground = [[NSBundle.mainBundle objectForInfoDictionaryKey:@"UIBackgroundModes"] containsObject:@"voip"]; @@ -49,7 +48,7 @@ - (id)initWithTransformerClass:(Class)transformerClass andStrategy:(NSArray *)st // Set the ping time to 10 minutes and 25 seconds if (_stayConnectedInBackground) { - _ping = 625; + _pingTimeout = 625; } } diff --git a/Primus/Core/PrimusTimers.h b/Primus/Core/PrimusTimers.h index f9555e4..7f6b336 100644 --- a/Primus/Core/PrimusTimers.h +++ b/Primus/Core/PrimusTimers.h @@ -11,8 +11,7 @@ @interface PrimusTimers : NSObject @property (nonatomic) GCDTimer *open; -@property (nonatomic) GCDTimer *ping; -@property (nonatomic) GCDTimer *pong; +@property (nonatomic) GCDTimer *heartbeat; @property (nonatomic) GCDTimer *connect; @property (nonatomic) GCDTimer *reconnect; diff --git a/Primus/Core/PrimusTimers.m b/Primus/Core/PrimusTimers.m index 6bc2630..0c56f82 100644 --- a/Primus/Core/PrimusTimers.m +++ b/Primus/Core/PrimusTimers.m @@ -13,8 +13,7 @@ @implementation PrimusTimers - (void)invalidateAll { [self.open invalidate]; - [self.ping invalidate]; - [self.pong invalidate]; + [self.heartbeat invalidate]; [self.connect invalidate]; [self.reconnect invalidate]; } @@ -24,8 +23,7 @@ - (void)clearAll [self invalidateAll]; self.open = nil; - self.ping = nil; - self.pong = nil; + self.heartbeat = nil; self.connect = nil; self.reconnect = nil; } diff --git a/Primus/Primus.m b/Primus/Primus.m index 4d4efe5..3b1f90f 100644 --- a/Primus/Primus.m +++ b/Primus/Primus.m @@ -96,12 +96,6 @@ - (void)bindRealtimeEvents _attemptOptions = nil; - [_timers.ping invalidate]; - _timers.ping = nil; - - [_timers.pong invalidate]; - _timers.pong = nil; - [self emit:@"open"]; [self startHeartbeat]; @@ -115,13 +109,14 @@ - (void)bindRealtimeEvents } }]; - [self on:@"incoming::pong" listener:^(NSNumber *time) { + [self on:@"incoming::ping" listener:^(NSNumber *time) { _online = YES; - [_timers.pong invalidate]; - _timers.pong = nil; - [self startHeartbeat]; + + [self emit:@"outgoing::pong", time]; + + [self write:[NSString stringWithFormat:@"primus::pong::%li", [time integerValue]]]; }]; [self on:@"incoming::error" listener:^(NSError *error) { @@ -151,8 +146,8 @@ - (void)bindRealtimeEvents return [self end]; } - if ([data hasPrefix:@"primus::pong::"]) { - return [self emit:@"incoming::pong", [data substringFromIndex:14]]; + if ([data hasPrefix:@"primus::ping::"]) { + return [self emit:@"incoming::ping", [data substringFromIndex:14]]; } if ([data hasPrefix:@"primus::id::"]) { @@ -246,7 +241,7 @@ - (void)bindSystemEvents // Send a keep-alive ping every 10 minutes while in background [UIApplication.sharedApplication setKeepAliveTimeout:kBackgroundFetchIntervalMinimum handler:^{ - [self ping]; + [self startHeartbeat]; }]; }]; @@ -265,7 +260,7 @@ - (void)bindSystemEvents // Reconnect to the server after resuming from background if ([self.options.reconnect.strategies containsObject:@(kPrimusReconnectionStrategyOnline)]) { - [self ping]; + [self startHeartbeat]; } }]; #endif @@ -292,16 +287,21 @@ - (void)initialize if (!parserClass) { parserClass = NSClassFromString([spec[@"parser"] uppercaseString]); } - - // Subtract 10 seconds from the maximum server-side timeout, as per the - // official Primus server-side documentation. - NSTimeInterval timeout = ((NSNumber *)spec[@"timeout"]).doubleValue - 10e3; - - self.options.ping = MAX(MIN(self.options.ping, timeout / 1000.0f), 0); + + // As we're given a timeout value on the server side, we need to update the + // As we're given a `pingInterval` value on the server side, we need to update + // the `pingTimeout` on the client. + + if (self.options.pingInterval) { + NSTimeInterval value = self.options.pingInterval + round(self.options.pingInterval / 2); + + self.options.pingTimeout = value; + } else { + self.options.pingTimeout = 45; + } } // If the calculated ping is smaller than the minimum allowed interval, disable background. - if (self.options.ping < kBackgroundFetchIntervalMinimum) { + if (self.options.pingTimeout < kBackgroundFetchIntervalMinimum) { self.options.stayConnectedInBackground = NO; } @@ -449,47 +449,26 @@ - (void)id:(PrimusIdCallback)fn [self once:@"incoming::id" listener:fn]; } -- (void)pong -{ - [_timers.pong invalidate]; - _timers.pong = nil; - - if (self.online) { - return; - } - - _online = NO; - - [self emit:@"offline"]; - [self emit:@"incoming::end", nil]; -} - -- (void)ping -{ - [_timers.ping invalidate]; - _timers.ping = nil; - - [self write:[NSString stringWithFormat:@"primus::ping::%f", [[NSDate date] timeIntervalSince1970]]]; - [self emit:@"outgoing::ping"]; - - _timers.pong = [GCDTimer scheduledTimerWithTimeInterval:self.options.pong repeats:NO block:^{ - [self pong]; - }]; -} - /** - * Send a new heartbeat over the connection to ensure that we're still - * connected and our internet connection didn't drop. We cannot use server side - * heartbeats for this unfortunately. + * Set a timer that, upon expiration, closes the client. */ - (void)startHeartbeat { - if (! self.options.ping) { + if (! self.options.pingTimeout) { return; } - _timers.ping = [GCDTimer scheduledTimerWithTimeInterval:self.options.ping repeats:NO block:^{ - [self ping]; + [_timers.heartbeat invalidate]; + _timers.heartbeat = nil; + + _timers.heartbeat = [GCDTimer scheduledTimerWithTimeInterval:self.options.pingTimeout repeats:NO block:^{ + if (!self.online) { + return; + } + + _online = NO; + [self emit:@"offline"]; + [self emit:@"incoming::end", @"Heartbeat timed out"]; }]; } @@ -503,7 +482,7 @@ - (void)startTimeout _timers.connect = nil; }; - _timers.connect = [GCDTimer scheduledTimerWithTimeInterval:self.options.timeout repeats:NO block:^{ + _timers.connect = [GCDTimer scheduledTimerWithTimeInterval:self.options.pingInterval repeats:NO block:^{ stop(); if (kPrimusReadyStateOpen == self.readyState || _attemptOptions) {