diff --git a/Podfile b/Podfile
index 7ed01693..30db80c0 100644
--- a/Podfile
+++ b/Podfile
@@ -1,4 +1,4 @@
-xcodeproj 'SignalR.Client.ObjC/SignalR.Client.ObjC'
+project 'SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj'
workspace 'SignalR.Client.ObjC'
target "SignalR.Client.iOS" do
@@ -23,4 +23,4 @@ target :"SignalR.Client.OSX" do
target :"SignalR.Client.OSXTests" do
pod 'OCMock'
end
-end
\ No newline at end of file
+end
diff --git a/Podfile.lock b/Podfile.lock
index 3c53754a..2af314c5 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -28,9 +28,17 @@ DEPENDENCIES:
- OCMock
- SocketRocket (= 0.4.2)
+SPEC REPOS:
+ https://github.com/cocoapods/specs.git:
+ - AFNetworking
+ - OCMock
+ - SocketRocket
+
SPEC CHECKSUMS:
AFNetworking: cb8d14a848e831097108418f5d49217339d4eb60
OCMock: 18c9b7e67d4c2770e95bb77a9cc1ae0c91fe3835
SocketRocket: ffe08119b00ef982f6c37052a4705a057c8494ad
-COCOAPODS: 0.39.0
+PODFILE CHECKSUM: b3a3a58c75f13f9a520b6c950906fdd364953a26
+
+COCOAPODS: 1.5.3
diff --git a/SignalR.Client.ObjC.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SignalR.Client.ObjC.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/SignalR.Client.ObjC.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/SignalR.Client.ObjC.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/SignalR.Client.ObjC.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..0c67376e
--- /dev/null
+++ b/SignalR.Client.ObjC.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj b/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj
index a8f1b0cc..6246c7d6 100644
--- a/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj
+++ b/SignalR.Client.ObjC/SignalR.Client.ObjC.xcodeproj/project.pbxproj
@@ -53,7 +53,6 @@
39302A211C69768D0061C6B5 /* SRServerSentEventsTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = 3920786115AF13F6009B959E /* SRServerSentEventsTransport.m */; };
39302A221C69768D0061C6B5 /* SRWebSocketTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = 39AF0D7D17138E3800E13E6E /* SRWebSocketTransport.m */; };
39302A231C6976A30061C6B5 /* SRSubscription.m in Sources */ = {isa = PBXBuildFile; fileRef = 3920783615AF13F6009B959E /* SRSubscription.m */; };
- 39302A271C6977800061C6B5 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 39302A251C6977800061C6B5 /* Info.plist */; };
39302A281C6977800061C6B5 /* SignalR.h in Headers */ = {isa = PBXBuildFile; fileRef = 39302A261C6977800061C6B5 /* SignalR.h */; };
39302A381C6978AC0061C6B5 /* SignalR.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39302A2E1C6978AB0061C6B5 /* SignalR.framework */; };
39302A451C6979E10061C6B5 /* SRHubConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 3920782715AF13F6009B959E /* SRHubConnection.m */; };
@@ -522,12 +521,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 39302A051C6974C20061C6B5 /* Build configuration list for PBXNativeTarget "SignalR.Client.OSX" */;
buildPhases = (
+ 759DD48E5A906D4D2714F305 /* [CP] Check Pods Manifest.lock */,
BE773CA6D603623DBCDFD6AF /* Check Pods Manifest.lock */,
393029EF1C6974C10061C6B5 /* Sources */,
393029F01C6974C10061C6B5 /* Frameworks */,
393029F11C6974C10061C6B5 /* Headers */,
393029F21C6974C10061C6B5 /* Resources */,
- 7C5BAF419D1DB75581A41B87 /* Copy Pods Resources */,
);
buildRules = (
);
@@ -542,12 +541,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 39302A081C6974C20061C6B5 /* Build configuration list for PBXNativeTarget "SignalR.Client.OSXTests" */;
buildPhases = (
+ 29F8A99392528EEAD6A859C3 /* [CP] Check Pods Manifest.lock */,
537C4819A81E84FE2982C1CF /* Check Pods Manifest.lock */,
393029F91C6974C20061C6B5 /* Sources */,
393029FA1C6974C20061C6B5 /* Frameworks */,
393029FB1C6974C20061C6B5 /* Resources */,
67E90B84652BAD667E4611D1 /* Embed Pods Frameworks */,
- 0B029DDB5BC07F36BE28C47C /* Copy Pods Resources */,
+ ED3F4313A751EA87E66F745B /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -563,12 +563,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 39302A3F1C6978AC0061C6B5 /* Build configuration list for PBXNativeTarget "SignalR.Client.iOS" */;
buildPhases = (
+ A48F4A4D58939ECEEB8E687E /* [CP] Check Pods Manifest.lock */,
42E081C85DE4F4916D0F3D75 /* Check Pods Manifest.lock */,
39302A291C6978AB0061C6B5 /* Sources */,
39302A2A1C6978AB0061C6B5 /* Frameworks */,
39302A2B1C6978AB0061C6B5 /* Headers */,
39302A2C1C6978AB0061C6B5 /* Resources */,
- 796268F885EFEC8F1356854A /* Copy Pods Resources */,
);
buildRules = (
);
@@ -583,12 +583,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 39302A421C6978AC0061C6B5 /* Build configuration list for PBXNativeTarget "SignalR.Client.iOSTests" */;
buildPhases = (
+ EF8CD3D9CD825628C4319223 /* [CP] Check Pods Manifest.lock */,
21C56E6B6FDFA0220435E7AD /* Check Pods Manifest.lock */,
39302A331C6978AC0061C6B5 /* Sources */,
39302A341C6978AC0061C6B5 /* Frameworks */,
39302A351C6978AC0061C6B5 /* Resources */,
2AC719B5534D658A6E1038BA /* Embed Pods Frameworks */,
- 1A11AFB25C13B6568AF7E084 /* Copy Pods Resources */,
+ 239C11727851DAB652DA48A3 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -648,7 +649,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 39302A271C6977800061C6B5 /* Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -676,49 +676,67 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 0B029DDB5BC07F36BE28C47C /* Copy Pods Resources */ = {
+ 21C56E6B6FDFA0220435E7AD /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "Copy Pods Resources";
+ name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.OSX-SignalR.Client.OSXTests/Pods-SignalR.Client.OSX-SignalR.Client.OSXTests-resources.sh\"\n";
+ shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
- 1A11AFB25C13B6568AF7E084 /* Copy Pods Resources */ = {
+ 239C11727851DAB652DA48A3 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
+ inputFileListPaths = (
+ );
inputPaths = (
+ "${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.iOS-SignalR.Client.iOSTests/Pods-SignalR.Client.iOS-SignalR.Client.iOSTests-frameworks.sh",
+ "${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework",
+ "${BUILT_PRODUCTS_DIR}/SocketRocket-iOS/SocketRocket.framework",
+ "${BUILT_PRODUCTS_DIR}/OCMock-iOS/OCMock.framework",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
);
- name = "Copy Pods Resources";
outputPaths = (
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AFNetworking.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.iOS-SignalR.Client.iOSTests/Pods-SignalR.Client.iOS-SignalR.Client.iOSTests-resources.sh\"\n";
+ shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.iOS-SignalR.Client.iOSTests/Pods-SignalR.Client.iOS-SignalR.Client.iOSTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- 21C56E6B6FDFA0220435E7AD /* Check Pods Manifest.lock */ = {
+ 29F8A99392528EEAD6A859C3 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
+ inputFileListPaths = (
+ );
inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
);
- name = "Check Pods Manifest.lock";
outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-SignalR.Client.OSX-SignalR.Client.OSXTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
2AC719B5534D658A6E1038BA /* Embed Pods Frameworks */ = {
@@ -781,34 +799,48 @@
shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.OSX-SignalR.Client.OSXTests/Pods-SignalR.Client.OSX-SignalR.Client.OSXTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- 796268F885EFEC8F1356854A /* Copy Pods Resources */ = {
+ 759DD48E5A906D4D2714F305 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
+ inputFileListPaths = (
+ );
inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
);
- name = "Copy Pods Resources";
outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-SignalR.Client.OSX-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.iOS/Pods-SignalR.Client.iOS-resources.sh\"\n";
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
- 7C5BAF419D1DB75581A41B87 /* Copy Pods Resources */ = {
+ A48F4A4D58939ECEEB8E687E /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
+ inputFileListPaths = (
+ );
inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
);
- name = "Copy Pods Resources";
outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-SignalR.Client.iOS-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.OSX/Pods-SignalR.Client.OSX-resources.sh\"\n";
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
BE773CA6D603623DBCDFD6AF /* Check Pods Manifest.lock */ = {
@@ -826,6 +858,54 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
+ ED3F4313A751EA87E66F745B /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.OSX-SignalR.Client.OSXTests/Pods-SignalR.Client.OSX-SignalR.Client.OSXTests-frameworks.sh",
+ "${BUILT_PRODUCTS_DIR}/AFNetworking-93a4f461/AFNetworking.framework",
+ "${BUILT_PRODUCTS_DIR}/SocketRocket-macOS/SocketRocket.framework",
+ "${BUILT_PRODUCTS_DIR}/OCMock-macOS/OCMock.framework",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AFNetworking.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/../Pods/Target Support Files/Pods-SignalR.Client.OSX-SignalR.Client.OSXTests/Pods-SignalR.Client.OSX-SignalR.Client.OSXTests-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ EF8CD3D9CD825628C4319223 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-SignalR.Client.iOS-SignalR.Client.iOSTests-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
diff --git a/SignalR.Client/SRConnection.m b/SignalR.Client/SRConnection.m
index fcd23734..f9cd5b94 100644
--- a/SignalR.Client/SRConnection.m
+++ b/SignalR.Client/SRConnection.m
@@ -45,6 +45,10 @@ @interface SRConnection ()
@property (strong, nonatomic, readwrite) NSString *connectionData;
@property (strong, nonatomic, readwrite) SRHeartbeatMonitor *monitor;
+@property (strong, nonatomic, readwrite) id transport;
+@property (strong, nonatomic, readwrite) NSDate* lastActive;
+@property (strong, nonatomic, readwrite) NSNumber* reconnectWindow;
+
- (void)negotiate:(id )transport;
- (void)verifyProtocolVersion:(NSString *)versionString;
- (NSString *)createUserAgentString:(NSString *)client;
@@ -67,6 +71,9 @@ @implementation SRConnection
@synthesize transport = _transport;
@synthesize credentials = _credentials;
@synthesize headers = _headers;
+@synthesize lastActive = _lastActive;
+@synthesize reconnectWindow = _reconnectWindow;
+
#pragma mark -
#pragma mark Initialization
@@ -146,7 +153,10 @@ - (void)negotiate:(id)transport {
// If we have a keep alive
if (negotiationResponse.keepAliveTimeout != nil) {
- _keepAliveData = [[SRKeepAliveData alloc] initWithTimeout:negotiationResponse.keepAliveTimeout];
+ strongSelf.keepAliveData = [[SRKeepAliveData alloc] initWithTimeout:negotiationResponse.keepAliveTimeout];
+ strongSelf.reconnectWindow = @([strongSelf.disconnectTimeout floatValue] + [strongSelf.keepAliveData.timeout floatValue]);
+ } else {
+ strongSelf.reconnectWindow = strongSelf.disconnectTimeout;
}
[strongSelf startTransport];
@@ -416,6 +426,28 @@ - (void)updateLastKeepAlive {
}
}
+-(void)markActive{
+ if([self verifyLastActive]){
+ _lastActive = [NSDate date];
+ }
+}
+
+- (BOOL) verifyLastActive {
+ if (_lastActive == nil || _reconnectWindow == nil){
+ //todo figure out what really should go here
+ _lastActive = [NSDate date];
+ return YES;
+ }
+ NSTimeInterval timeElapsed = [[NSDate date] timeIntervalSinceDate: _lastActive];//number of seconds
+ if (timeElapsed > [_reconnectWindow floatValue]){
+ SRLogConnectionDebug(@"There has not been an active server connection for an extended period of time. Stopping connection.");
+ [self stop:_defaultAbortTimeout];
+ return NO;
+ }
+ return YES;
+}
+
+
- (void)prepareRequest:(NSMutableURLRequest *)request {
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
[request addValue:[self createUserAgentString:NSLocalizedString(@"SignalR.Client.iOS",@"")] forHTTPHeaderField:@"User-Agent"];
diff --git a/SignalR.Client/SRConnectionInterface.h b/SignalR.Client/SRConnectionInterface.h
index 92d72ee1..2785fa41 100644
--- a/SignalR.Client/SRConnectionInterface.h
+++ b/SignalR.Client/SRConnectionInterface.h
@@ -53,6 +53,8 @@
///-------------------------------
- (NSString *)onSending;//TODO: this just encapsulates connectionData. can we pull this into a getUrl like js client does?
+- (BOOL) verifyLastActive;
+- (void) markActive;
///-------------------------------
/// @name Connection Management
diff --git a/SignalR.Client/SRHeartbeatMonitor.h b/SignalR.Client/SRHeartbeatMonitor.h
index 8a0a4d4b..b999ea0a 100644
--- a/SignalR.Client/SRHeartbeatMonitor.h
+++ b/SignalR.Client/SRHeartbeatMonitor.h
@@ -28,6 +28,7 @@
@property (assign, nonatomic, readonly, getter = hasBeenWarned) BOOL beenWarned;
@property (assign, nonatomic, readonly) BOOL timedOut;
+@property (assign, nonatomic, readonly) BOOL monitorKeepAlive;
- (instancetype)initWithConnection:(id )connection;
- (void)start;
diff --git a/SignalR.Client/SRHeartbeatMonitor.m b/SignalR.Client/SRHeartbeatMonitor.m
index 3417c29f..5388969f 100644
--- a/SignalR.Client/SRHeartbeatMonitor.m
+++ b/SignalR.Client/SRHeartbeatMonitor.m
@@ -44,7 +44,10 @@ - (instancetype)initWithConnection:(id )connection {
}
- (void)start {
- [_connection updateLastKeepAlive];
+ _monitorKeepAlive = [_connection keepAliveData] && [_connection.transport supportsKeepAlive];
+ if (_monitorKeepAlive){
+ [_connection updateLastKeepAlive];
+ }
_beenWarned = NO;
_timedOut = NO;
_timer = [NSTimer scheduledTimerWithTimeInterval:[[[_connection keepAliveData] checkInterval] integerValue]
@@ -60,26 +63,29 @@ - (void)heartbeat:(NSTimer *)timer {
}
- (void)beat:(NSInteger)timeElapsed {
- if (_connection.state == connected) {
- if (timeElapsed >= [[[_connection keepAliveData] timeout] integerValue]) {
- if (!self.timedOut) {
- // Connection has been lost
- SRLogConnectionWarn(@"Connection Timed-out : Transport Lost Connection");
- _timedOut = true;
- [[_connection transport] lostConnection:_connection];
- }
- } else if (timeElapsed >= [[[_connection keepAliveData] timeoutWarning] integerValue]) {
- if (!self.hasBeenWarned) {
- // Inform user and set HasBeenWarned to true
- SRLogConnectionWarn(@"Connection Timeout Warning : Notifying user");
- _beenWarned = true;
- [_connection connectionDidSlow];
+ if (_monitorKeepAlive){
+ if (_connection.state == connected) {
+ if (timeElapsed >= [[[_connection keepAliveData] timeout] integerValue]) {
+ if (!self.timedOut) {
+ // Connection has been lost
+ SRLogConnectionWarn(@"Connection Timed-out : Transport Lost Connection");
+ _timedOut = true;
+ [[_connection transport] lostConnection:_connection];
+ }
+ } else if (timeElapsed >= [[[_connection keepAliveData] timeoutWarning] integerValue]) {
+ if (!self.hasBeenWarned) {
+ // Inform user and set HasBeenWarned to true
+ SRLogConnectionWarn(@"Connection Timeout Warning : Notifying user");
+ _beenWarned = true;
+ [_connection connectionDidSlow];
+ }
+ } else {
+ _beenWarned = false;
+ _timedOut = false;
}
- } else {
- _beenWarned = false;
- _timedOut = false;
}
}
+ [_connection markActive];
}
- (void)stop {
diff --git a/SignalR.Client/Transports/SRLongPollingTransport.m b/SignalR.Client/Transports/SRLongPollingTransport.m
index 11c7b48c..06a6590a 100644
--- a/SignalR.Client/Transports/SRLongPollingTransport.m
+++ b/SignalR.Client/Transports/SRLongPollingTransport.m
@@ -27,11 +27,13 @@
#import "SRLog.h"
#import "SRLongPollingTransport.h"
- @interface SRLongPollingTransport()
-
- @property (strong, nonatomic, readwrite) NSOperationQueue *pollingOperationQueue;
-
- @end
+@interface SRLongPollingTransport()
+
+@property (strong, nonatomic, readwrite) NSOperationQueue *pollingOperationQueue;
+@property (nonatomic, readwrite) int reconnectErrors;
+@property (strong, nonatomic, readwrite) NSBlockOperation *reconnectTimeout;
+
+@end
@implementation SRLongPollingTransport
@@ -63,6 +65,7 @@ - (void)negotiate:(id)connection connectionData:(NSString
- (void)start:(id)connection connectionData:(NSString *)connectionData completionHandler:(void (^)(id response, NSError *error))block {
SRLogLPDebug(@"longPolling will connect with connectionData %@", connectionData);
+ self.reconnectErrors = 0;
[self poll:connection connectionData:connectionData completionHandler:block];
}
@@ -73,6 +76,9 @@ - (void)send:(id)connection data:(NSString *)data connect
- (void)abort:(id)connection timeout:(NSNumber *)timeout connectionData:(NSString *)connectionData {
SRLogLPDebug(@"longPolling will abort");
+ [[self reconnectTimeout] cancel];
+ self.reconnectTimeout = nil;
+
[super abort:connection timeout:timeout connectionData:connectionData];
}
@@ -85,8 +91,6 @@ - (void)lostConnection:(id)connection {
- (void)poll:(id)connection connectionData:(NSString *)connectionData completionHandler:(void (^)(id response, NSError *error))block {
- __block NSNumber *canReconnect = @(YES);
-
NSString *url = connection.url;
if(connection.messageId == nil) {
url = [url stringByAppendingString:@"connect"];
@@ -96,7 +100,7 @@ - (void)poll:(id)connection connectionData:(NSString *)co
url = [url stringByAppendingString:@"poll"];
}
- [self delayConnectionReconnect:connection canReconnect:canReconnect];
+ [self delayConnectionReconnect:connection];
__weak __typeof(&*self)weakSelf = self;
__weak __typeof(&*connection)weakConnection = connection;
@@ -128,9 +132,12 @@ - (void)poll:(id)connection connectionData:(NSString *)co
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
__strong __typeof(&*weakSelf)strongSelf = weakSelf;
__strong __typeof(&*weakConnection)strongConnection = weakConnection;
-
+
BOOL shouldReconnect = NO;
BOOL disconnectedReceived = NO;
+ strongSelf.reconnectErrors = 0;
+ [[strongSelf reconnectTimeout] cancel];
+ strongSelf.reconnectTimeout = nil;
SRLogLPInfo(@"longPolling did receive: %@", operation.responseString);
@@ -143,7 +150,7 @@ - (void)poll:(id)connection connectionData:(NSString *)co
// If the timeout for the reconnect hasn't fired as yet just fire the
// event here before any incoming messages are processed
SRLogLPWarn(@"reconnecting");
- [strongSelf connectionReconnect:strongConnection canReconnect:canReconnect];
+ [strongSelf connectionReconnect:strongConnection];
}
if (shouldReconnect) {
@@ -159,7 +166,6 @@ - (void)poll:(id)connection connectionData:(NSString *)co
if (![strongSelf tryCompleteAbort]) {
//Abort has not been called so continue polling...
- canReconnect = @(YES);
[strongSelf poll:strongConnection connectionData:connectionData completionHandler:nil];
} else {
SRLogLPWarn(@"longPolling has shutdown due to abort");
@@ -167,10 +173,17 @@ - (void)poll:(id)connection connectionData:(NSString *)co
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
__strong __typeof(&*weakSelf)strongSelf = weakSelf;
__strong __typeof(&*weakConnection)strongConnection = weakConnection;
-
+
SRLogLPError(@"longPolling did fail with error %@", error);
- canReconnect = @(NO);
+ strongSelf.reconnectErrors++;
+ [[strongSelf reconnectTimeout] cancel];//cancel the isalive reconnect so we dont falsely assume we succeeded
+ strongSelf.reconnectTimeout = nil;
+
+
+ if (![strongConnection verifyLastActive]){
+ return;//connection will have aborted above
+ }
// Transition into reconnecting state
[SRConnection ensureReconnecting:strongConnection];
@@ -181,8 +194,6 @@ - (void)poll:(id)connection connectionData:(NSString *)co
SRLogLPDebug(@"will poll again in %ld seconds",(long)[_errorDelay integerValue]);
- canReconnect = @(YES);
-
[[NSBlockOperation blockOperationWithBlock:^{
[strongSelf poll:strongConnection connectionData:connectionData completionHandler:nil];
}] performSelector:@selector(start) withObject:nil afterDelay:[strongSelf.errorDelay integerValue]];
@@ -197,30 +208,29 @@ - (void)poll:(id)connection connectionData:(NSString *)co
[self.pollingOperationQueue addOperation:operation];
}
-- (void)delayConnectionReconnect:(id)connection canReconnect:(NSNumber *)canReconnect {
+- (void)delayConnectionReconnect:(id)connection {
if ([self isConnectionReconnecting:connection]) {
__weak __typeof(&*self)weakSelf = self;
__weak __typeof(&*connection)weakConnection = connection;
- __weak __typeof(&*canReconnect)weakCanReconnect = canReconnect;
SRLogLPDebug(@"will reconnect in %@", self.reconnectDelay);
- [[NSBlockOperation blockOperationWithBlock:^{
+ //in order to force the retry timeout, we increase backoff for time
+ int isAliveDelay = MIN( [self.reconnectDelay integerValue] + pow(2.0, self.reconnectErrors), 360000);//NOTE: one hour will be much longer than connection's reconnect timeout. this is not indicating how long we wait to reconnect
+ self.reconnectTimeout = [NSBlockOperation blockOperationWithBlock:^{
__strong __typeof(&*weakSelf)strongSelf = weakSelf;
__strong __typeof(&*weakConnection)strongConnection = weakConnection;
- __strong __typeof(&*weakCanReconnect)strongCanReconnect = weakCanReconnect;
SRLogLPWarn(@"reconnecting");
- [strongSelf connectionReconnect:strongConnection canReconnect:strongCanReconnect];
+ [strongSelf connectionReconnect:strongConnection];
- }] performSelector:@selector(start) withObject:nil afterDelay:[self.reconnectDelay integerValue]];
+ }];
+ //note: we rely on our error and success callback to cancel this.
+ [self.reconnectTimeout performSelector:@selector(start) withObject:nil afterDelay:isAliveDelay];
}
}
-- (void)connectionReconnect:(id)connection canReconnect:(NSNumber *)canReconnect {
- if ([canReconnect boolValue]) {
- canReconnect = @(NO);
- // Mark the connection as connected
- if ([connection changeState:reconnecting toState:connected]) {
- [connection didReconnect];
- }
+- (void)connectionReconnect:(id)connection {
+ // Mark the connection as connected
+ if ([connection changeState:reconnecting toState:connected]) {
+ [connection didReconnect];
}
}
diff --git a/SignalR.Client/Transports/SRServerSentEventsTransport.m b/SignalR.Client/Transports/SRServerSentEventsTransport.m
index 281eb463..d1f856b1 100644
--- a/SignalR.Client/Transports/SRServerSentEventsTransport.m
+++ b/SignalR.Client/Transports/SRServerSentEventsTransport.m
@@ -67,7 +67,7 @@ - (BOOL)supportsKeepAlive {
- (void)negotiate:(id)connection connectionData:(NSString *)connectionData completionHandler:(void (^)(SRNegotiationResponse * response, NSError *error))block {
SRLogSSEDebug(@"serverSentEvents will negotiate");
- [super negotiate:connection connectionData:connectionData completionHandler:nil];
+ [super negotiate:connection connectionData:connectionData completionHandler:block];
}
- (void)start:(id)connection connectionData:(NSString *)connectionData completionHandler:(void (^)(id response, NSError *error))block {
@@ -86,6 +86,11 @@ - (void)start:(id)connection connectionData:(NSString *)c
};
NSError *timeout = [[NSError alloc]initWithDomain:[NSString stringWithFormat:NSLocalizedString(@"com.SignalR.SignalR-ObjC.%@",@""),NSStringFromClass([self class])] code:NSURLErrorTimedOut userInfo:userInfo];
SRLogSSEError(@"serverSentEvents failed to receive initialized message before timeout");
+ strongSelf.stop = YES;
+ strongSelf.eventSource.opened = nil;
+ strongSelf.eventSource.message = nil;
+ strongSelf.eventSource.closed = nil;
+ [strongSelf.eventSource close];
strongSelf.completionHandler(nil, timeout);
strongSelf.completionHandler = nil;
}
@@ -150,14 +155,7 @@ - (void)open:(id )connection connectionData:(NSString *)c
//operation.securityPolicy = self.securityPolicy;
_eventSource = [[SREventSourceStreamReader alloc] initWithStream:operation.outputStream];
_eventSource.opened = ^() {
- __strong __typeof(&*weakConnection)strongConnection = weakConnection;
SRLogSSEInfo(@"serverSentEvents did open eventSource");
-
- // This will noop if we're not in the reconnecting state
- if([strongConnection changeState:reconnecting toState:connected]) {
- // Raise the reconnect event if the connection comes back up
- [strongConnection didReconnect];
- }
};
_eventSource.message = ^(SRServerSentEvent * sseEvent) {
__strong __typeof(&*weakSelf)strongSelf = weakSelf;
@@ -167,6 +165,15 @@ - (void)open:(id )connection connectionData:(NSString *)c
NSString *data = [[NSString alloc] initWithData:sseEvent.data encoding:NSUTF8StringEncoding];
SRLogSSEInfo(@"serverSentEvents did receive: %@", data);
if([data caseInsensitiveCompare:@"initialized"] == NSOrderedSame) {
+ // SSE can get a 503 error, terminated connection, etc
+ // which should not trigger reconnect, so instead the server sends
+ // down initialized. This is sufficient for knowing we have
+ // reconnected.
+ // This will noop if we're not in the reconnecting state
+ if([strongConnection changeState:reconnecting toState:connected]) {
+ // Raise the reconnect event if the connection comes back up
+ [strongConnection didReconnect];
+ }
return;
}
@@ -215,8 +222,7 @@ - (void)open:(id )connection connectionData:(NSString *)c
[strongSelf completeAbort];
}
else if ([strongSelf tryCompleteAbort]) {
- }
- else {
+ } else if ([strongConnection verifyLastActive]){//check in if we should abandon
[strongSelf reconnect:strongConnection data:connectionData];
}
};
@@ -228,7 +234,7 @@ - (void)open:(id )connection connectionData:(NSString *)c
if (strongSelf.stop) {
[strongSelf completeAbort];
} else if ([strongSelf tryCompleteAbort]) {
- } else {
+ } else if ([strongConnection verifyLastActive]){//check in if we should abandon
[strongSelf reconnect:strongConnection data:connectionData];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
@@ -245,9 +251,14 @@ - (void)open:(id )connection connectionData:(NSString *)c
selector:@selector(start)
object:nil];
self.connectTimeoutOperation = nil;
-
+ strongSelf.stop = YES;
+ strongSelf.eventSource.opened = nil;
+ strongSelf.eventSource.message = nil;
+ strongSelf.eventSource.closed = nil;
+ [strongSelf.eventSource close];
strongSelf.completionHandler(nil, error);
strongSelf.completionHandler = nil;
+ strongSelf.eventSource = nil;
} else if (!isReconnecting){//failure should first attempt to reconect
SRLogSSEWarn(@"will reconnect from errors: %@", error);
} else {//failure while reconnecting should error
diff --git a/Tests/Mocks/SRMockSSENetworkStream.h b/Tests/Mocks/SRMockSSENetworkStream.h
index 0d0680f4..8cd04589 100644
--- a/Tests/Mocks/SRMockSSENetworkStream.h
+++ b/Tests/Mocks/SRMockSSENetworkStream.h
@@ -8,14 +8,19 @@
#import
#import
+#import
+@class SRMockWaitBlockOperation;
@interface SRMockSSENetworkStream : NSObject
+@property (strong, nonatomic, readonly) NSOutputStream * stream;
+
- (void)prepareForOpeningResponse:(void (^)())then;
- (void)prepareForOpeningResponse:(NSString *)response then:(void (^)())then;
- (void)prepareForNextResponse:(NSString *)response then:(void (^)())then;
- (void)prepareForClose;
- (void)prepareForError:(NSError *)error;
+- (void)prepareForConnectTimeout:(NSInteger)timeout beforeCaptureTimeout:(void (^)(SRMockWaitBlockOperation *transportConnectTimeout))beforeCaptureTimeout afterCaptureTimeout:(void (^)(SRMockWaitBlockOperation *transportConnectTimeout))afterCaptureTimeout;
- (void)stopMocking;
diff --git a/Tests/Mocks/SRMockSSENetworkStream.m b/Tests/Mocks/SRMockSSENetworkStream.m
index 2bf008e8..1352e437 100644
--- a/Tests/Mocks/SRMockSSENetworkStream.m
+++ b/Tests/Mocks/SRMockSSENetworkStream.m
@@ -7,6 +7,7 @@
//
#import "SRMockSSENetworkStream.h"
+#import "SRMockWaitBlockOperation.h"
#import
@interface SRMockSSENetworkStream ()
@@ -14,6 +15,7 @@ @interface SRMockSSENetworkStream ()
@property (readwrite, nonatomic, strong) NSData* lastData;
@property (readwrite, nonatomic, strong) id dataDelegate;
@property (readwrite, nonatomic, strong) id mock;
+@property (readwrite, nonatomic, strong) NSOutputStream* outputStream;
//only call this directly if you don't want to trigger the stream.opened callback
@property (readwrite, nonatomic, copy) void (^onSuccess)(AFHTTPRequestOperation *operation, id responseObject);
@property (readwrite, nonatomic, copy) void (^onFailure)(AFHTTPRequestOperation *operation, NSError *error);
@@ -47,6 +49,10 @@ - (instancetype)init {
return self;
}
+- (NSOutputStream *)stream {
+ return self.outputStream;
+}
+
- (void)prepareForOpeningResponse:(void (^)())then {
return [self prepareForOpeningResponse:nil then:then];
}
@@ -54,7 +60,8 @@ - (void)prepareForOpeningResponse:(void (^)())then {
- (void)prepareForOpeningResponse:(NSString *)response then:(void (^)())then {
NSOutputStream* dataStream = [[NSOutputStream alloc] initToMemory];
[[[self.mock stub] andReturn: dataStream] outputStream];
-
+ _outputStream = dataStream;
+
if (!response) {
response = @"";
}
@@ -74,6 +81,24 @@ - (void)prepareForOpeningResponse:(NSString *)response then:(void (^)())then {
}
}
+
+- (void)prepareForConnectTimeout:(NSInteger)timeout beforeCaptureTimeout:(void (^)(SRMockWaitBlockOperation *))beforeCaptureTimeout afterCaptureTimeout:(void (^)(SRMockWaitBlockOperation *))afterCaptureTimeout{
+ //note: even though it's a connect timeout, we want an outputstream
+ //so that we can verify it closes
+ _outputStream = [[NSOutputStream alloc] initToMemory];
+ [_outputStream open];
+ [[[self.mock stub] andReturn: _outputStream] outputStream];
+
+ SRMockWaitBlockOperation* transportConnectTimeout = [[SRMockWaitBlockOperation alloc] initWithWaitTime:10];
+ if (beforeCaptureTimeout) {
+ beforeCaptureTimeout(transportConnectTimeout);
+ }
+ [transportConnectTimeout stopMocking];
+ if (afterCaptureTimeout) {
+ afterCaptureTimeout(transportConnectTimeout);
+ }
+}
+
- (void)prepareForNextResponse:(NSString *)response then:(void (^)())then {
NSMutableData* prior = [[NSMutableData alloc] initWithData: _lastData];
NSData* data = [response dataUsingEncoding:NSUTF8StringEncoding];
diff --git a/Tests/Tests/SRAutoTransportTests.m b/Tests/Tests/SRAutoTransportTests.m
index 9688777f..425230c4 100644
--- a/Tests/Tests/SRAutoTransportTests.m
+++ b/Tests/Tests/SRAutoTransportTests.m
@@ -14,10 +14,16 @@
#import "SRAutoTransport.h"
#import "SRWebSocketTransport.h"
#import "SRServerSentEventsTransport.h"
+#import "SRLongPollingTransport.h"
#import "SRMockClientTransport.h"
#import "SRMockWaitBlockOperation.h"
#import "SRMockWSNetworkStream.h"
#import "SRMockSSENetworkStream.h"
+#import "SRMockNetwork.h"
+
+@interface SRLongPollingTransport ()
+@property (strong, nonatomic, readwrite) NSOperationQueue *pollingOperationQueue;
+@end
@interface SRAutoTransport (UnitTest)
@@ -109,5 +115,71 @@ - (void)testSlowToInitializeWebsocketCleansUpAndTriesNextTransport {
XCTAssert([[autoTransport name] isEqualToString:[sse name]]);
}
+- (void)testSlowToInitializeServerSentEventsCleansUpAndTriesNextTransport {
+ XCTestExpectation *initialized = [self expectationWithDescription:@"Handler called"];
+
+ SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"];
+
+ SRServerSentEventsTransport *sse = [[SRServerSentEventsTransport alloc] init];
+ [sse setServerSentEventsOperationQueue:nil];
+
+ id mockSSETransport = [OCMockObject partialMockForObject:sse];
+ [[[mockSSETransport expect] andForwardToRealObject] start:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]];
+
+ SRLongPollingTransport *lp = [[SRLongPollingTransport alloc] init];
+ lp.pollingOperationQueue = nil;////set to nil to get around weird ARC OCMock bugs http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access
+
+ id mockLPTransport = [OCMockObject partialMockForObject:lp];
+ [[[mockLPTransport expect] andForwardToRealObject] start:[OCMArg any] connectionData:[OCMArg any] completionHandler:[OCMArg any]];
+
+ SRAutoTransport* autoTransport = [[SRAutoTransport alloc] initWithTransports:@[sse, lp]];
+
+ id json = @{
+ @"ConnectionId": @"10101",
+ @"ConnectionToken": @"10101010101",
+ @"DisconnectTimeout": @30,
+ @"ProtocolVersion": @"1.3.0.0",
+ @"TransportConnectTimeout": @10,
+ @"TryWebSockets": @NO
+ };
+ [SRMockClientTransport negotiateForTransport:autoTransport statusCode:@200 json:json];
+ __block id connect = nil;
+
+ connection.started = ^{
+ [initialized fulfill];//note, we wont fulfill this till we've fallen back to longPolling
+ [connect stopMocking];//if we dont do this we create a tight loop as longpolling will
+ //instantly return more data
+ };
+
+ SRMockSSENetworkStream* sseNetworkStream = [[SRMockSSENetworkStream alloc] init];
+ //cannot OCMock bridge free classes
+ // [[[sseNetworkStream stream] expect] close];
+
+
+ //Setup SSE to get timed out so we fallback
+ [sseNetworkStream prepareForConnectTimeout:10 beforeCaptureTimeout:^(SRMockWaitBlockOperation *transportConnectTimeout){
+ //Start the connection and capture the performSelector blocks
+ //for use later
+ [connection start:autoTransport];
+ } afterCaptureTimeout:^(SRMockWaitBlockOperation *transportConnectTimeout){
+ //this is before the timeout has occurred, so last minute mock
+ //the request
+ connect = [SRMockNetwork mockHttpRequestOperationForClass:[AFHTTPRequestOperation class]
+ statusCode:@200
+ responseString:@"abcdefg"];
+ transportConnectTimeout.afterWait();//calls the timeout method, afterWait is the block passed
+ //at this point we should have fallen back to longPolling
+ }];
+
+ [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
+ if (error) {
+ NSLog(@"Timeout Error: %@", error);
+ }
+ }];
+ [mockSSETransport verify];
+ XCTAssertEqual([[sseNetworkStream stream] streamStatus], NSStreamStatusClosed);
+
+ XCTAssert([[autoTransport name] isEqualToString:[lp name]]);
+}
@end
diff --git a/Tests/Tests/SRServerSentEventsTransportTests.m b/Tests/Tests/SRServerSentEventsTransportTests.m
index 120a6c21..8d36c44f 100644
--- a/Tests/Tests/SRServerSentEventsTransportTests.m
+++ b/Tests/Tests/SRServerSentEventsTransportTests.m
@@ -52,17 +52,17 @@ - (void)testStartCallsTheCompletionHandlerAfterSuccess {
SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"];
connection.connectionToken = @"10101010101";
connection.connectionId = @"10101";
+ connection.transportConnectTimeout = @10;
[connection changeState:disconnected toState:connected];
SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init];
sse.serverSentEventsOperationQueue = nil;//set to nil to get around weird ARC OCMock bugs http://stackoverflow.com/questions/18121902/using-ocmock-on-nsoperation-gives-bad-access
-
- [NetworkMock prepareForOpeningResponse:^{
+ //note: SSE sends down initialized but it gets ignored in all clients
+ [NetworkMock prepareForOpeningResponse:@"data: initialized\n\ndata: {}\n" then:^{
[sse start: connection connectionData:@"12345" completionHandler:^(id response, NSError *error){
[expectation fulfill];
}];
}];
-
[self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
if (error) {
NSLog(@"Timeout Error: %@", error);
@@ -78,6 +78,7 @@ - (void)testParsesInitialBuffer {
SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"];
connection.connectionToken = @"10101010101";
connection.connectionId = @"10101";
+ connection.transportConnectTimeout = @10;
[connection changeState:disconnected toState:connected];
SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init];
@@ -104,6 +105,7 @@ - (void)testIgnoresInitializedAndEmptyLinesWhenParsingMessages {
SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"];
connection.connectionToken = @"10101010101";
connection.connectionId = @"10101";
+ connection.transportConnectTimeout = @10;
[connection changeState:disconnected toState:connected];
connection.received = ^(NSDictionary * data){
@@ -134,6 +136,7 @@ - (void)testConnectionInitialFailureUsesCallback {
SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"];
connection.connectionToken = @"10101010101";
connection.connectionId = @"10101";
+ connection.transportConnectTimeout = @10;
[connection changeState:disconnected toState:connected];
SRServerSentEventsTransport* sse = [[SRServerSentEventsTransport alloc] init];
@@ -196,7 +199,7 @@ - (void)testConnectionErrorRetries__RetriesAfterADelay__CommunicatesLifeCycleVia
[NetworkMock stopMocking];
SRMockSSENetworkStream* NetworkReconnectMock = [[SRMockSSENetworkStream alloc]init];
[reconnectDelay.mock stopMocking];//dont want to accidentally get other blocks
- [NetworkReconnectMock prepareForOpeningResponse:^{
+ [NetworkReconnectMock prepareForOpeningResponse:@"data: initialized\n\n" then:^{
reconnectDelay.afterWait();
}];
@@ -256,7 +259,7 @@ - (void)testLostConnectionAbortsAllConnectionsAndReconnects {
[NetworkMock stopMocking];
SRMockSSENetworkStream* NetworkReconnectMock = [[SRMockSSENetworkStream alloc]init];
[reconnectDelay.mock stopMocking];//dont want to accidentally get other blocks
- [NetworkReconnectMock prepareForOpeningResponse:^{
+ [NetworkReconnectMock prepareForOpeningResponse:@"data: initialized\n\n" then:^{
reconnectDelay.afterWait();
}];
@@ -354,6 +357,7 @@ - (void)testHandlesExtraEmptyLinesWhenParsingMessages {
SRConnection* connection = [[SRConnection alloc] initWithURLString:@"http://localhost:0000"];
connection.connectionToken = @"10101010101";
connection.connectionId = @"10101";
+ connection.transportConnectTimeout = @10;
[connection changeState:disconnected toState:connected];
connection.received = ^(NSString * data){
@@ -383,6 +387,7 @@ - (void)testHandlesNewLinesSpreadOutOverReads {
SRConnection* connection = [[SRConnection alloc]initWithURLString:@"http://localhost:0000"];
connection.connectionToken = @"10101010101";
connection.connectionId = @"10101";
+ connection.transportConnectTimeout = @10;
[connection changeState:disconnected toState:connected];
connection.received = ^(NSDictionary * data){
@@ -850,7 +855,7 @@ - (void)testStreamClosesCleanlyShouldReconnect {
[NetworkMock stopMocking];
SRMockSSENetworkStream* NetworkReconnectMock = [[SRMockSSENetworkStream alloc]init];
[reconnectDelay stopMocking];
- [NetworkReconnectMock prepareForOpeningResponse:^{
+ [NetworkReconnectMock prepareForOpeningResponse:@"data: initialized\n\n" then:^{
reconnectDelay.afterWait();
}];