Skip to content

Commit

Permalink
Fix RTN20.
Browse files Browse the repository at this point in the history
I've tested the ARTOSReachability "live" manually (by switching on/off
the real Internet connection on my machine) and it works as expected,
but it needs #385 to properly handle transport errors.
  • Loading branch information
tcard committed Aug 26, 2016
1 parent 098bbc9 commit ecd18f5
Show file tree
Hide file tree
Showing 10 changed files with 330 additions and 79 deletions.
18 changes: 18 additions & 0 deletions Ably.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@
EB1AE0CE1C5C3A4900D62250 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1AE0CD1C5C3A4900D62250 /* Utilities.swift */; };
EB20F8D71C653F2300EF3978 /* ARTPresence+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB20F8D61C653F1E00EF3978 /* ARTPresence+Private.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB2D5A911CC941A700AD1A67 /* ARTRealtimeTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D5A901CC941A700AD1A67 /* ARTRealtimeTransport.m */; };
EB2D5A921CC941A700AD1A67 /* ARTRealtimeTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D5A901CC941A700AD1A67 /* ARTRealtimeTransport.m */; };
EB2D5A931CC941A700AD1A67 /* ARTRealtimeTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D5A901CC941A700AD1A67 /* ARTRealtimeTransport.m */; };
EB2D84F71CD75CCE00F23CDA /* ARTReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = EB2D84F61CD75CCE00F23CDA /* ARTReachability.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB2D84FD1CD769B800F23CDA /* ARTOSReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D84FC1CD769B700F23CDA /* ARTOSReachability.m */; };
EB2D84FE1CD769B800F23CDA /* ARTOSReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D84FC1CD769B700F23CDA /* ARTOSReachability.m */; };
EB2D84FF1CD769B800F23CDA /* ARTOSReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D84FC1CD769B700F23CDA /* ARTOSReachability.m */; };
EB2D85011CD769C800F23CDA /* ARTOSReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = EB2D85001CD769C800F23CDA /* ARTOSReachability.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB503C881C7E4A090053AF00 /* ARTClientOptions+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB503C871C7E4A090053AF00 /* ARTClientOptions+Private.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB503C8A1C7F1FE40053AF00 /* ARTLog+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB503C891C7F1FE40053AF00 /* ARTLog+Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
EB5E058D1C77027600A48B39 /* ARTCrypto+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB5E058C1C77027600A48B39 /* ARTCrypto+Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
Expand Down Expand Up @@ -389,6 +396,9 @@
EB1AE0CD1C5C3A4900D62250 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
EB20F8D61C653F1E00EF3978 /* ARTPresence+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ARTPresence+Private.h"; sourceTree = "<group>"; };
EB2D5A901CC941A700AD1A67 /* ARTRealtimeTransport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeTransport.m; sourceTree = "<group>"; };
EB2D84F61CD75CCE00F23CDA /* ARTReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTReachability.h; sourceTree = "<group>"; };
EB2D84FC1CD769B700F23CDA /* ARTOSReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTOSReachability.m; sourceTree = "<group>"; };
EB2D85001CD769C800F23CDA /* ARTOSReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTOSReachability.h; sourceTree = "<group>"; };
EB3239421C59AB0400892664 /* ARTDataEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTDataEncoder.m; sourceTree = "<group>"; };
EB3239461C59AB2C00892664 /* ARTDataEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTDataEncoder.h; sourceTree = "<group>"; };
EB503C871C7E4A090053AF00 /* ARTClientOptions+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTClientOptions+Private.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -725,6 +735,9 @@
967A43201A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m */,
96A507A31A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h */,
96A507A41A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.m */,
EB2D84F61CD75CCE00F23CDA /* ARTReachability.h */,
EB2D84FC1CD769B700F23CDA /* ARTOSReachability.m */,
EB2D85001CD769C800F23CDA /* ARTOSReachability.h */,
);
name = Utilities;
sourceTree = "<group>";
Expand Down Expand Up @@ -775,6 +788,7 @@
961343D81A42E0B7006DC822 /* ARTClientOptions.h in Headers */,
96BF615E1A35C1C8004CF2B3 /* ARTTypes.h in Headers */,
EB503C881C7E4A090053AF00 /* ARTClientOptions+Private.h in Headers */,
EB2D84F71CD75CCE00F23CDA /* ARTReachability.h in Headers */,
1C1EC3FA1AE26A8B00AAADD7 /* ARTStatus.h in Headers */,
D70EAAED1BC3376200CD8B9E /* ARTRestChannel.h in Headers */,
96BF61581A35B52C004CF2B3 /* ARTHttp.h in Headers */,
Expand Down Expand Up @@ -827,6 +841,7 @@
D7F1D3731BF4DE07001A4B5E /* ARTRestPresence.h in Headers */,
D7B17EE31C07208B00A6958E /* ARTConnectionDetails.h in Headers */,
EBFA366E1D58B05000B09AA7 /* ARTRestPresence+Private.h in Headers */,
EB2D85011CD769C800F23CDA /* ARTOSReachability.h in Headers */,
960D07971A46FFC300ED8C8C /* ARTRest+Private.h in Headers */,
1C05CF201AC1D7EB00687AC9 /* ARTRealtime+Private.h in Headers */,
D7F1D37A1BF4E33A001A4B5E /* ARTRestChannel+Private.h in Headers */,
Expand Down Expand Up @@ -1112,6 +1127,7 @@
D746AE2C1BBB625E003ECEF8 /* RestClientChannel.swift in Sources */,
D71D30041C5F7B2F002115B0 /* RealtimeClientChannels.swift in Sources */,
D714A63E1C74D4B2002F2CA0 /* NSObject+TestSuite.swift in Sources */,
EB2D84FF1CD769B800F23CDA /* ARTOSReachability.m in Sources */,
856AAC971B6E30C800B07119 /* TestUtilities.swift in Sources */,
D72768211C9C19040022F8B2 /* RestClientPresence.swift in Sources */,
D780846E1C68B3E50083009D /* NSObject+TestSuite.m in Sources */,
Expand All @@ -1138,6 +1154,7 @@
D746AE391BBC3201003ECEF8 /* ARTMessage.m in Sources */,
EB89D40B1C61C6EA007FA5B7 /* ARTRealtimeChannels.m in Sources */,
D746AE231BBB60EE003ECEF8 /* ARTChannel.m in Sources */,
EB2D84FD1CD769B800F23CDA /* ARTOSReachability.m in Sources */,
D746AE481BBD6FE9003ECEF8 /* ARTQueuedMessage.m in Sources */,
D746AE3D1BBC5AE1003ECEF8 /* ARTRealtimeChannel.m in Sources */,
96A507A21A377AA50077CDF8 /* ARTPresenceMessage.m in Sources */,
Expand Down Expand Up @@ -1187,6 +1204,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
EB2D84FE1CD769B800F23CDA /* ARTOSReachability.m in Sources */,
96E4083C1A38622D00087F77 /* ARTTestUtil.m in Sources */,
EBAB9A711C69719900AF036B /* ARTReadmeExamples.m in Sources */,
1C1E52EE1AB32EB9004A690F /* ARTRestCapabilityTest.m in Sources */,
Expand Down
17 changes: 17 additions & 0 deletions Source/ARTOSReachability.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// ARTOSReachability.h
// Ably
//
// Created by Toni Cárdenas on 2/5/16.
// Copyright © 2016 Ably. All rights reserved.
//

#ifndef ARTOSReachability_h
#define ARTOSReachability_h

#import "ARTReachability.h"

@interface ARTOSReachability : NSObject <ARTReachability>
@end

#endif /* ARTOSReachability_h */
87 changes: 87 additions & 0 deletions Source/ARTOSReachability.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// ARTReachability.m
// Ably
//
// Created by Toni Cárdenas on 2/5/16.
// Copyright © 2016 Ably. All rights reserved.
//

#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
#import <sys/socket.h>
#import <CoreFoundation/CoreFoundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <netinet/in.h>

#import "ARTOSReachability.h"

@implementation ARTOSReachability {
ARTLog *_logger;
NSString *_host;
void (^_callback)(BOOL);
SCNetworkReachabilityRef _reachabilityRef;
NSMutableDictionary *_instances;
}

- (instancetype)initWithLogger:(ARTLog *)logger {
self = [super self];
if (self) {
_logger = logger;
if (ARTOSReachability_instances == nil) {
_instances = [[NSMutableDictionary alloc] init];
ARTOSReachability_instances = _instances;
} else {
_instances = ARTOSReachability_instances;
}
}
return self;
}

__weak NSMutableDictionary *ARTOSReachability_instances;

static void ARTOSReachability_Callback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) {
[(ARTOSReachability *)ARTOSReachability_instances[[NSValue valueWithPointer:target]] internalCallback:flags & kSCNetworkReachabilityFlagsReachable];
}

- (void)listenForHost:(NSString *)host callback:(void (^)(BOOL))callback {
[self off];
_host = host;
_callback = callback;

_reachabilityRef = SCNetworkReachabilityCreateWithName(NULL, [host UTF8String]);
SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};

if (SCNetworkReachabilitySetCallback(_reachabilityRef, ARTOSReachability_Callback, &context)) {
[ARTOSReachability_instances setObject:self forKey:[NSValue valueWithPointer:_reachabilityRef]];
if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
[_logger info:@"Reachability: started listening for host %@", _host];
} else {
[_logger warn:@"Reachability: failed starting listener for host %@", _host];
}
}
}

- (void)off {
if (_reachabilityRef != NULL) {
[ARTOSReachability_instances removeObjectForKey:[NSValue valueWithPointer:_reachabilityRef]];
SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
[_logger info:@"Reachability: stopped listening for host %@", _host];
}
_callback = nil;
_host = nil;
}

- (void)internalCallback:(BOOL)reachable {
[_logger info:@"Reachability: host %@: %d", _host, reachable];
_callback(reachable);
}

- (void)dealloc {
[self off];
if (_reachabilityRef != NULL) {
CFRelease(_reachabilityRef);
}
}

@end
28 changes: 28 additions & 0 deletions Source/ARTReachability.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// ARTReachability.h
// Ably
//
// Created by Toni Cárdenas on 2/5/16.
// Copyright © 2016 Ably. All rights reserved.
//

#ifndef ARTReachability_h
#define ARTReachability_h

#import "CompatibilityMacros.h"
#import "ARTLog.h"

ART_ASSUME_NONNULL_BEGIN

@protocol ARTReachability <NSObject>

- (instancetype)initWithLogger:(ARTLog *)logger;

- (void)listenForHost:(NSString *)host callback:(void (^)(BOOL))callback;
- (void)off;

@end

ART_ASSUME_NONNULL_END

#endif /* ARTReachability_h */
4 changes: 3 additions & 1 deletion Source/ARTRealtime+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import "ARTTypes.h"
#import "ARTQueuedMessage.h"
#import "ARTProtocolMessage.h"
#import "ARTReachability.h"

#import "ARTRealtimeTransport.h"

Expand Down Expand Up @@ -45,6 +46,7 @@ ART_ASSUME_NONNULL_BEGIN

@property (readwrite, strong, nonatomic) ARTRest *rest;
@property (readonly, getter=getTransport) id<ARTRealtimeTransport> transport;
@property (readonly, strong, nonatomic, art_nonnull) id<ARTReachability> reachability;
@property (readonly, getter=getLogger) ARTLog *logger;

/// Current protocol `msgSerial`. Starts at zero.
Expand Down Expand Up @@ -81,8 +83,8 @@ ART_ASSUME_NONNULL_BEGIN
- (void)onNack:(ARTProtocolMessage *)message;
- (void)onChannelMessage:(ARTProtocolMessage *)message;

// FIXME: Connection should manage the transport
- (void)setTransportClass:(Class)transportClass;
- (void)setReachabilityClass:(Class)reachabilityClass;

- (void)resetEventEmitter;

Expand Down
64 changes: 45 additions & 19 deletions Source/ARTRealtime.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#import "ARTChannelOptions.h"
#import "ARTPresenceMessage.h"
#import "ARTWebSocketTransport.h"
#import "ARTOSReachability.h"
#import "ARTNSArray+ARTFunctional.h"
#import "ARTPresenceMap.h"
#import "ARTProtocolMessage.h"
Expand Down Expand Up @@ -46,6 +47,7 @@ @implementation ARTRealtime {
NSDate *_startedReconnection;
NSTimeInterval _connectionStateTtl;
Class _transportClass;
Class _reachabilityClass;
id<ARTRealtimeTransport> _transport;
ARTFallback *_fallbacks;
}
Expand All @@ -71,6 +73,7 @@ - (instancetype)initWithOptions:(ARTClientOptions *)options {
_channels = [[ARTRealtimeChannels alloc] initWithRealtime:self];
_transport = nil;
_transportClass = [ARTWebSocketTransport class];
_reachabilityClass = [ARTOSReachability class];
_msgSerial = 0;
_queuedMessages = [NSMutableArray array];
_pendingMessages = [NSMutableArray array];
Expand Down Expand Up @@ -232,6 +235,36 @@ - (void)transitionSideEffects:(ARTConnectionStateChange *)stateChange {
[self transition:ARTRealtimeDisconnected withErrorInfo:[ARTErrorInfo createWithCode:0 status:ARTStateConnectionFailed message:@"timed out"]];
}];

if (!_reachability) {
_reachability = [[_reachabilityClass alloc] initWithLogger:self.logger];
}

// TODO: Do also for fallback hosts once https://github.com/ably/ably-ios/pull/385
// is merged.
[_reachability listenForHost:self.options.realtimeHost callback:^(BOOL reachable) {
if (reachable) {
switch (_connection.state) {
case ARTRealtimeDisconnected:
case ARTRealtimeSuspended:
[self transition:ARTRealtimeConnecting];
default:
break;
}
} else {
switch (_connection.state) {
case ARTRealtimeConnecting:
case ARTRealtimeConnected: {
// TODO: Trigger host fallback behavior.
ARTErrorInfo *unreachable = [ARTErrorInfo createWithCode:-1003 message:@"unreachable host"];
[self transition:ARTRealtimeDisconnected withErrorInfo:unreachable];
break;
}
default:
break;
}
}
}];

if (!_transport) {
NSString *resumeKey = nil;
NSNumber *connectionSerial = nil;
Expand Down Expand Up @@ -430,26 +463,15 @@ - (void)onDisconnected {

- (void)onDisconnected:(ARTProtocolMessage *)message {
[self.logger info:@"R:%p ARTRealtime disconnected", self];
switch (self.connection.state) {
case ARTRealtimeConnected: {
ARTErrorInfo *error;
if (message) {
error = message.error;
}
if (!_renewingToken && error && error.statusCode == 401 && error.code >= 40140 && error.code < 40150 && [self isTokenRenewable]) {
[self connectWithRenewedToken];
return;
}
if (error) {

}
[self transition:ARTRealtimeDisconnected withErrorInfo:error];
break;
}
default:
NSAssert(false, @"Invalid Realtime state transitioning to Disconnected: expected Connected");
break;
ARTErrorInfo *error;
if (message) {
error = message.error;
}
if (!_renewingToken && error && error.statusCode == 401 && error.code >= 40140 && error.code < 40150 && [self isTokenRenewable]) {
[self connectWithRenewedToken];
return;
}
[self transition:ARTRealtimeDisconnected withErrorInfo:error];
}

- (void)onClosed {
Expand Down Expand Up @@ -815,6 +837,10 @@ - (void)setTransportClass:(Class)transportClass {
_transportClass = transportClass;
}

- (void)setReachabilityClass:(Class)reachabilityClass {
_reachabilityClass = reachabilityClass;
}

+ (NSString *)protocolStr:(ARTProtocolMessageAction) action {
switch(action) {
case ARTProtocolMessageHeartbeat:
Expand Down
2 changes: 2 additions & 0 deletions Source/AblyRealtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ FOUNDATION_EXPORT const unsigned char ablyVersionString[];
#import "ARTJsonEncoder.h"
#import "ARTMsgpackEncoder.h"
#import "ARTPaginatedResult.h"
#import "ARTReachability.h"
#import "ARTOSReachability.h"

#import "ARTNSDictionary+ARTDictionaryUtil.h"
#import "ARTNSDate+ARTUtil.h"
Expand Down
Loading

0 comments on commit ecd18f5

Please sign in to comment.