merge master

This commit is contained in:
Erik 2016-06-17 10:16:52 -04:00
commit be76a1607c
17 changed files with 562 additions and 460 deletions

View File

@ -88,7 +88,7 @@ Carthage
----------------- -----------------
Add this line to your `Cartfile`: Add this line to your `Cartfile`:
``` ```
github "socketio/socket.io-client-swift" ~> 6.1.2 # Or latest version github "socketio/socket.io-client-swift" ~> 6.1.3 # Or latest version
``` ```
Run `carthage update --platform ios,macosx`. Run `carthage update --platform ios,macosx`.
@ -102,7 +102,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0' platform :ios, '8.0'
use_frameworks! use_frameworks!
pod 'Socket.IO-Client-Swift', '~> 6.1.2' # Or latest version pod 'Socket.IO-Client-Swift', '~> 6.1.3' # Or latest version
``` ```
Install pods: Install pods:
@ -130,7 +130,7 @@ CocoaSeeds
Add this line to your `Seedfile`: Add this line to your `Seedfile`:
``` ```
github "socketio/socket.io-client-swift", "v6.1.2", :files => "Source/*.swift" # Or latest version github "socketio/socket.io-client-swift", "v6.1.3", :files => "Source/*.swift" # Or latest version
``` ```
Run `seed install`. Run `seed install`.
@ -165,6 +165,7 @@ case ReconnectAttempts(Int) // How many times to reconnect. Default is `-1` (inf
case ReconnectWait(Int) // Amount of time to wait between reconnects. Default is `10` case ReconnectWait(Int) // Amount of time to wait between reconnects. Default is `10`
case SessionDelegate(NSURLSessionDelegate) // Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs. Default is nil. case SessionDelegate(NSURLSessionDelegate) // Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs. Default is nil.
case Secure(Bool) // If the connection should use TLS. Default is false. case Secure(Bool) // If the connection should use TLS. Default is false.
case Security(SSLSecurity) // Allows you to set which certs are valid. Useful for SSL pinning.
case SelfSigned(Bool) // Sets WebSocket.selfSignedSSL (Don't do this, iOS will yell at you) case SelfSigned(Bool) // Sets WebSocket.selfSignedSSL (Don't do this, iOS will yell at you)
case VoipEnabled(Bool) // Only use this option if you're using the client with VoIP services. Changes the way the WebSocket is created. Default is false case VoipEnabled(Bool) // Only use this option if you're using the client with VoIP services. Changes the way the WebSocket is created. Default is false
``` ```

View File

@ -1,7 +1,7 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "Socket.IO-Client-Swift" s.name = "Socket.IO-Client-Swift"
s.module_name = "SocketIOClientSwift" s.module_name = "SocketIOClientSwift"
s.version = "6.1.2" s.version = "6.1.3"
s.summary = "Socket.IO-client for iOS and OS X" s.summary = "Socket.IO-client for iOS and OS X"
s.description = <<-DESC s.description = <<-DESC
Socket.IO-client for iOS and OS X. Socket.IO-client for iOS and OS X.
@ -14,7 +14,7 @@ Pod::Spec.new do |s|
s.ios.deployment_target = '8.0' s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.10' s.osx.deployment_target = '10.10'
s.tvos.deployment_target = '9.0' s.tvos.deployment_target = '9.0'
s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v6.1.2' } s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v6.1.3' }
s.source_files = "Source/**/*.swift" s.source_files = "Source/**/*.swift"
s.requires_arc = true s.requires_arc = true
# s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files

View File

@ -69,9 +69,7 @@
74171EC31C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; }; 74171EC31C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; };
74171EC51C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; }; 74171EC51C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; };
74171EC71C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; }; 74171EC71C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; };
74171ECF1C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; }; 74171EC81C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; };
74171ED11C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; };
74171ED31C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; };
741F39EE1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; }; 741F39EE1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; };
741F39EF1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; }; 741F39EF1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; };
7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */; }; 7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */; };
@ -87,6 +85,15 @@
7472C65F1BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */; }; 7472C65F1BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */; };
7472C6601BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */; }; 7472C6601BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */; };
74ABF7771C3991C10078C657 /* SocketIOClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */; }; 74ABF7771C3991C10078C657 /* SocketIOClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */; };
74B4AD1D1D09A5D80062A523 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD1C1D09A5D80062A523 /* WebSocket.swift */; };
74B4AD211D09A6190062A523 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD201D09A6190062A523 /* SSLSecurity.swift */; };
74B4AD221D09A6190062A523 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD201D09A6190062A523 /* SSLSecurity.swift */; };
74B4AD231D09A6190062A523 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD201D09A6190062A523 /* SSLSecurity.swift */; };
74B4AD241D09A6450062A523 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD1C1D09A5D80062A523 /* WebSocket.swift */; };
74B4AD251D09A6490062A523 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD1C1D09A5D80062A523 /* WebSocket.swift */; };
74BC45AB1D0C6675008CC431 /* SocketClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */; };
74BC45AC1D0C6675008CC431 /* SocketClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */; };
74BC45AD1D0C6675008CC431 /* SocketClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */; };
74F124F01BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; }; 74F124F01BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; };
74F124F11BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; }; 74F124F11BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; };
CEBA56961CDA0B7700BA0389 /* NSCharacterSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBA56951CDA0B7700BA0389 /* NSCharacterSet.swift */; }; CEBA56961CDA0B7700BA0389 /* NSCharacterSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBA56951CDA0B7700BA0389 /* NSCharacterSet.swift */; };
@ -126,7 +133,6 @@
572EF21D1B51F16C00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 572EF21D1B51F16C00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
572EF21E1B51F16C00EEBB58 /* SocketIO-iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketIO-iOS.h"; sourceTree = "<group>"; }; 572EF21E1B51F16C00EEBB58 /* SocketIO-iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketIO-iOS.h"; sourceTree = "<group>"; };
572EF2241B51F16C00EEBB58 /* SocketIO-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SocketIO-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 572EF2241B51F16C00EEBB58 /* SocketIO-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SocketIO-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
572EF22A1B51F16C00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
572EF2381B51F18A00EEBB58 /* SocketIOClientSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketIOClientSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 572EF2381B51F18A00EEBB58 /* SocketIOClientSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketIOClientSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
572EF23B1B51F18A00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 572EF23B1B51F18A00EEBB58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
572EF23C1B51F18A00EEBB58 /* SocketIO-Mac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketIO-Mac.h"; sourceTree = "<group>"; }; 572EF23C1B51F18A00EEBB58 /* SocketIO-Mac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketIO-Mac.h"; sourceTree = "<group>"; };
@ -151,7 +157,6 @@
74171E5E1C10CD240062D398 /* SocketParsable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketParsable.swift; path = Source/SocketParsable.swift; sourceTree = "<group>"; }; 74171E5E1C10CD240062D398 /* SocketParsable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketParsable.swift; path = Source/SocketParsable.swift; sourceTree = "<group>"; };
74171E5F1C10CD240062D398 /* SocketStringReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketStringReader.swift; path = Source/SocketStringReader.swift; sourceTree = "<group>"; }; 74171E5F1C10CD240062D398 /* SocketStringReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketStringReader.swift; path = Source/SocketStringReader.swift; sourceTree = "<group>"; };
74171E601C10CD240062D398 /* SocketTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketTypes.swift; path = Source/SocketTypes.swift; sourceTree = "<group>"; }; 74171E601C10CD240062D398 /* SocketTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketTypes.swift; path = Source/SocketTypes.swift; sourceTree = "<group>"; };
74171E621C10CD240062D398 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket.swift; sourceTree = "<group>"; };
741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = "<group>"; }; 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = "<group>"; };
7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEnginePollable.swift; path = Source/SocketEnginePollable.swift; sourceTree = "<group>"; }; 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEnginePollable.swift; path = Source/SocketEnginePollable.swift; sourceTree = "<group>"; };
742D150B1CA5794B00BD987D /* SocketObjectiveCTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketObjectiveCTest.m; sourceTree = "<group>"; }; 742D150B1CA5794B00BD987D /* SocketObjectiveCTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketObjectiveCTest.m; sourceTree = "<group>"; };
@ -160,6 +165,9 @@
7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespacePacketTest.swift; sourceTree = "<group>"; }; 7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespacePacketTest.swift; sourceTree = "<group>"; };
7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketSideEffectTest.swift; sourceTree = "<group>"; }; 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketSideEffectTest.swift; sourceTree = "<group>"; };
74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientSpec.swift; path = Source/SocketIOClientSpec.swift; sourceTree = "<group>"; }; 74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientSpec.swift; path = Source/SocketIOClientSpec.swift; sourceTree = "<group>"; };
74B4AD1C1D09A5D80062A523 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket/WebSocket.swift; sourceTree = "<group>"; };
74B4AD201D09A6190062A523 /* SSLSecurity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SSLSecurity.swift; path = Source/WebSocket/SSLSecurity.swift; sourceTree = "<group>"; };
74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketClientManager.swift; path = Source/SocketClientManager.swift; sourceTree = "<group>"; };
74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketBasicPacketTest.swift; sourceTree = "<group>"; }; 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketBasicPacketTest.swift; sourceTree = "<group>"; };
CEBA56951CDA0B7700BA0389 /* NSCharacterSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSCharacterSet.swift; path = Source/NSCharacterSet.swift; sourceTree = "<group>"; }; CEBA56951CDA0B7700BA0389 /* NSCharacterSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSCharacterSet.swift; path = Source/NSCharacterSet.swift; sourceTree = "<group>"; };
CEBA56991CDA0B8200BA0389 /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = String.swift; path = Source/String.swift; sourceTree = "<group>"; }; CEBA56991CDA0B8200BA0389 /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = String.swift; path = Source/String.swift; sourceTree = "<group>"; };
@ -219,7 +227,6 @@
children = ( children = (
5764DF7B1B51F24A004FF46E /* Source */, 5764DF7B1B51F24A004FF46E /* Source */,
572EF21B1B51F16C00EEBB58 /* SocketIO-iOS */, 572EF21B1B51F16C00EEBB58 /* SocketIO-iOS */,
572EF2281B51F16C00EEBB58 /* SocketIO-iOSTests */,
572EF2391B51F18A00EEBB58 /* SocketIO-Mac */, 572EF2391B51F18A00EEBB58 /* SocketIO-Mac */,
572EF2461B51F18A00EEBB58 /* SocketIO-MacTests */, 572EF2461B51F18A00EEBB58 /* SocketIO-MacTests */,
572EF21A1B51F16C00EEBB58 /* Products */, 572EF21A1B51F16C00EEBB58 /* Products */,
@ -256,22 +263,6 @@
name = "Supporting Files"; name = "Supporting Files";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
572EF2281B51F16C00EEBB58 /* SocketIO-iOSTests */ = {
isa = PBXGroup;
children = (
572EF2291B51F16C00EEBB58 /* Supporting Files */,
);
path = "SocketIO-iOSTests";
sourceTree = "<group>";
};
572EF2291B51F16C00EEBB58 /* Supporting Files */ = {
isa = PBXGroup;
children = (
572EF22A1B51F16C00EEBB58 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
572EF2391B51F18A00EEBB58 /* SocketIO-Mac */ = { 572EF2391B51F18A00EEBB58 /* SocketIO-Mac */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -315,10 +306,10 @@
5764DF7B1B51F24A004FF46E /* Source */ = { 5764DF7B1B51F24A004FF46E /* Source */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CEBA569E1CDA0C0C00BA0389 /* utils */,
74171E501C10CD240062D398 /* SocketAckEmitter.swift */, 74171E501C10CD240062D398 /* SocketAckEmitter.swift */,
74171E511C10CD240062D398 /* SocketAckManager.swift */, 74171E511C10CD240062D398 /* SocketAckManager.swift */,
74171E521C10CD240062D398 /* SocketAnyEvent.swift */, 74171E521C10CD240062D398 /* SocketAnyEvent.swift */,
74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */,
74171E531C10CD240062D398 /* SocketEngine.swift */, 74171E531C10CD240062D398 /* SocketEngine.swift */,
74171E541C10CD240062D398 /* SocketEngineClient.swift */, 74171E541C10CD240062D398 /* SocketEngineClient.swift */,
74171E551C10CD240062D398 /* SocketEnginePacketType.swift */, 74171E551C10CD240062D398 /* SocketEnginePacketType.swift */,
@ -335,11 +326,21 @@
74171E5E1C10CD240062D398 /* SocketParsable.swift */, 74171E5E1C10CD240062D398 /* SocketParsable.swift */,
74171E5F1C10CD240062D398 /* SocketStringReader.swift */, 74171E5F1C10CD240062D398 /* SocketStringReader.swift */,
74171E601C10CD240062D398 /* SocketTypes.swift */, 74171E601C10CD240062D398 /* SocketTypes.swift */,
74171E621C10CD240062D398 /* WebSocket.swift */, CEBA569E1CDA0C0C00BA0389 /* utils */,
74B4AD1B1D09A5C30062A523 /* Websocket */,
); );
name = Source; name = Source;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
74B4AD1B1D09A5C30062A523 /* Websocket */ = {
isa = PBXGroup;
children = (
74B4AD1C1D09A5D80062A523 /* WebSocket.swift */,
74B4AD201D09A6190062A523 /* SSLSecurity.swift */,
);
name = Websocket;
sourceTree = "<group>";
};
CEBA569E1CDA0C0C00BA0389 /* utils */ = { CEBA569E1CDA0C0C00BA0389 /* utils */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -595,6 +596,7 @@
740CA1221C496EF700CB98F4 /* SocketEngineWebsocket.swift in Sources */, 740CA1221C496EF700CB98F4 /* SocketEngineWebsocket.swift in Sources */,
74171EA51C10CD240062D398 /* SocketIOClientStatus.swift in Sources */, 74171EA51C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
CEBA569A1CDA0B8200BA0389 /* String.swift in Sources */, CEBA569A1CDA0B8200BA0389 /* String.swift in Sources */,
74B4AD1D1D09A5D80062A523 /* WebSocket.swift in Sources */,
74171E751C10CD240062D398 /* SocketEngine.swift in Sources */, 74171E751C10CD240062D398 /* SocketEngine.swift in Sources */,
74171E691C10CD240062D398 /* SocketAckManager.swift in Sources */, 74171E691C10CD240062D398 /* SocketAckManager.swift in Sources */,
7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */, 7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */,
@ -602,18 +604,19 @@
74ABF7771C3991C10078C657 /* SocketIOClientSpec.swift in Sources */, 74ABF7771C3991C10078C657 /* SocketIOClientSpec.swift in Sources */,
74171E871C10CD240062D398 /* SocketEngineSpec.swift in Sources */, 74171E871C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171E631C10CD240062D398 /* SocketAckEmitter.swift in Sources */, 74171E631C10CD240062D398 /* SocketAckEmitter.swift in Sources */,
74B4AD211D09A6190062A523 /* SSLSecurity.swift in Sources */,
74171EBD1C10CD240062D398 /* SocketStringReader.swift in Sources */, 74171EBD1C10CD240062D398 /* SocketStringReader.swift in Sources */,
74171EC31C10CD240062D398 /* SocketTypes.swift in Sources */, 74171EC31C10CD240062D398 /* SocketTypes.swift in Sources */,
74171EAB1C10CD240062D398 /* SocketLogger.swift in Sources */, 74171EAB1C10CD240062D398 /* SocketLogger.swift in Sources */,
74171E991C10CD240062D398 /* SocketIOClient.swift in Sources */, 74171E991C10CD240062D398 /* SocketIOClient.swift in Sources */,
74171E8D1C10CD240062D398 /* SocketEventHandler.swift in Sources */, 74171E8D1C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E7B1C10CD240062D398 /* SocketEngineClient.swift in Sources */, 74171E7B1C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171ECF1C10CD240062D398 /* WebSocket.swift in Sources */,
74171EB11C10CD240062D398 /* SocketPacket.swift in Sources */, 74171EB11C10CD240062D398 /* SocketPacket.swift in Sources */,
74171EB71C10CD240062D398 /* SocketParsable.swift in Sources */, 74171EB71C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E811C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, 74171E811C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74171E6F1C10CD240062D398 /* SocketAnyEvent.swift in Sources */, 74171E6F1C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171E9F1C10CD240062D398 /* SocketIOClientOption.swift in Sources */, 74171E9F1C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74BC45AB1D0C6675008CC431 /* SocketClientManager.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -622,6 +625,15 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7472C65F1BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */, 7472C65F1BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */,
74171EA61C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
74171E881C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171EA01C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74171E701C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171EC41C10CD240062D398 /* SocketTypes.swift in Sources */,
74171E8E1C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E7C1C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171E821C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74171EB21C10CD240062D398 /* SocketPacket.swift in Sources */,
741F39EE1BD025D80026C9CC /* SocketEngineTest.swift in Sources */, 741F39EE1BD025D80026C9CC /* SocketEngineTest.swift in Sources */,
74F124F01BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */, 74F124F01BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */,
7472C65C1BCAB53E003CA70D /* SocketNamespacePacketTest.swift in Sources */, 7472C65C1BCAB53E003CA70D /* SocketNamespacePacketTest.swift in Sources */,
@ -632,6 +644,7 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
74B4AD241D09A6450062A523 /* WebSocket.swift in Sources */,
740CA1211C496EF200CB98F4 /* SocketEngineWebsocket.swift in Sources */, 740CA1211C496EF200CB98F4 /* SocketEngineWebsocket.swift in Sources */,
7471CCEA1C39926300364B59 /* SocketIOClientSpec.swift in Sources */, 7471CCEA1C39926300364B59 /* SocketIOClientSpec.swift in Sources */,
CEBA569B1CDA0B8200BA0389 /* String.swift in Sources */, CEBA569B1CDA0B8200BA0389 /* String.swift in Sources */,
@ -648,12 +661,13 @@
74171E9B1C10CD240062D398 /* SocketIOClient.swift in Sources */, 74171E9B1C10CD240062D398 /* SocketIOClient.swift in Sources */,
74171E8F1C10CD240062D398 /* SocketEventHandler.swift in Sources */, 74171E8F1C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E7D1C10CD240062D398 /* SocketEngineClient.swift in Sources */, 74171E7D1C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171ED11C10CD240062D398 /* WebSocket.swift in Sources */, 74B4AD221D09A6190062A523 /* SSLSecurity.swift in Sources */,
74171EB31C10CD240062D398 /* SocketPacket.swift in Sources */, 74171EB31C10CD240062D398 /* SocketPacket.swift in Sources */,
74171EB91C10CD240062D398 /* SocketParsable.swift in Sources */, 74171EB91C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E831C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, 74171E831C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74171E711C10CD240062D398 /* SocketAnyEvent.swift in Sources */, 74171E711C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171EA11C10CD240062D398 /* SocketIOClientOption.swift in Sources */, 74171EA11C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74BC45AC1D0C6675008CC431 /* SocketClientManager.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -676,6 +690,7 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
74B4AD251D09A6490062A523 /* WebSocket.swift in Sources */,
740CA1201C496EEB00CB98F4 /* SocketEngineWebsocket.swift in Sources */, 740CA1201C496EEB00CB98F4 /* SocketEngineWebsocket.swift in Sources */,
7471CCEB1C39926C00364B59 /* SocketIOClientSpec.swift in Sources */, 7471CCEB1C39926C00364B59 /* SocketIOClientSpec.swift in Sources */,
CEBA569C1CDA0B8200BA0389 /* String.swift in Sources */, CEBA569C1CDA0B8200BA0389 /* String.swift in Sources */,
@ -692,12 +707,13 @@
74171E9D1C10CD240062D398 /* SocketIOClient.swift in Sources */, 74171E9D1C10CD240062D398 /* SocketIOClient.swift in Sources */,
74171E911C10CD240062D398 /* SocketEventHandler.swift in Sources */, 74171E911C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E7F1C10CD240062D398 /* SocketEngineClient.swift in Sources */, 74171E7F1C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171ED31C10CD240062D398 /* WebSocket.swift in Sources */, 74B4AD231D09A6190062A523 /* SSLSecurity.swift in Sources */,
74171EB51C10CD240062D398 /* SocketPacket.swift in Sources */, 74171EB51C10CD240062D398 /* SocketPacket.swift in Sources */,
74171EBB1C10CD240062D398 /* SocketParsable.swift in Sources */, 74171EBB1C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E851C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, 74171E851C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74171E731C10CD240062D398 /* SocketAnyEvent.swift in Sources */, 74171E731C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171EA31C10CD240062D398 /* SocketIOClientOption.swift in Sources */, 74171EA31C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74BC45AD1D0C6675008CC431 /* SocketClientManager.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -706,6 +722,15 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
57634A231BD9B46D00E19CD7 /* SocketSideEffectTest.swift in Sources */, 57634A231BD9B46D00E19CD7 /* SocketSideEffectTest.swift in Sources */,
74171EAA1C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
74171E8C1C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171EA41C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74171E741C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171EC81C10CD240062D398 /* SocketTypes.swift in Sources */,
74171E921C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E801C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171E861C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74171EB61C10CD240062D398 /* SocketPacket.swift in Sources */,
57634A2A1BD9B46D00E19CD7 /* SocketEngineTest.swift in Sources */, 57634A2A1BD9B46D00E19CD7 /* SocketEngineTest.swift in Sources */,
57634A2F1BD9B46D00E19CD7 /* SocketBasicPacketTest.swift in Sources */, 57634A2F1BD9B46D00E19CD7 /* SocketBasicPacketTest.swift in Sources */,
57634A321BD9B46D00E19CD7 /* SocketNamespacePacketTest.swift in Sources */, 57634A321BD9B46D00E19CD7 /* SocketNamespacePacketTest.swift in Sources */,

View File

@ -15,7 +15,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7" BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-tvOS" BlueprintName = "SocketIO-tvOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>
@ -57,7 +57,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7" BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-tvOS" BlueprintName = "SocketIO-tvOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>
@ -79,7 +79,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7" BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-tvOS" BlueprintName = "SocketIO-tvOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>
@ -97,7 +97,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "576349FA1BD9B46A00E19CD7" BlueprintIdentifier = "576349FA1BD9B46A00E19CD7"
BuildableName = "SocketIO.framework" BuildableName = "SocketIOClientSwift.framework"
BlueprintName = "SocketIO-tvOS" BlueprintName = "SocketIO-tvOS"
ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj"> ReferencedContainer = "container:Socket.IO-Client-Swift.xcodeproj">
</BuildableReference> </BuildableReference>

View File

@ -44,4 +44,10 @@
[self.socket off:@"test"]; [self.socket off:@"test"];
} }
- (void)testSocketManager {
SocketClientManager* manager = [SocketClientManager sharedManager];
[manager addSocket:self.socket labeledAs:@"test"];
[manager removeSocketWithLabel:@"test"];
}
@end @end

View File

@ -13,7 +13,7 @@ class SocketParserTest: XCTestCase {
let testSocket = SocketIOClient(socketURL: NSURL()) let testSocket = SocketIOClient(socketURL: NSURL())
//Format key: message; namespace-data-binary-id //Format key: message; namespace-data-binary-id
static let packetTypes: Dictionary<String, (String, [AnyObject], [NSData], Int)> = [ static let packetTypes: [String: (String, [AnyObject], [NSData], Int)] = [
"0": ("/", [], [], -1), "1": ("/", [], [], -1), "0": ("/", [], [], -1), "1": ("/", [], [], -1),
"25[\"test\"]": ("/", ["test"], [], 5), "25[\"test\"]": ("/", ["test"], [], 5),
"2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1), "2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1),

View File

@ -153,4 +153,16 @@ class SocketSideEffectTest: XCTestCase {
socket.parseBinaryData(data2) socket.parseBinaryData(data2)
waitForExpectationsWithTimeout(3, handler: nil) waitForExpectationsWithTimeout(3, handler: nil)
} }
func testSocketManager() {
let manager = SocketClientManager.sharedManager
manager["test"] = socket
XCTAssert(manager["test"] === socket, "failed to get socket")
manager["test"] = nil
XCTAssert(manager["test"] == nil, "socket not removed")
}
} }

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -0,0 +1,82 @@
//
// SocketClientManager.swift
// Socket.IO-Client-Swift
//
// Created by Erik Little on 6/11/16.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
/**
Experimental socket manager.
API subject to change.
Can be used to persist sockets across ViewControllers.
Sockets are strongly stored, so be sure to remove them once they are no
longer needed.
Example usage:
```
let manager = SocketClientManager.sharedManager
manager["room1"] = socket1
manager["room2"] = socket2
manager.removeSocket(socket: socket2)
manager["room1"]?.emit("hello")
```
*/
public final class SocketClientManager : NSObject {
public static let sharedManager = SocketClientManager()
private var sockets = [String: SocketIOClient]()
public subscript(string: String) -> SocketIOClient? {
get {
return sockets[string]
}
set(socket) {
sockets[string] = socket
}
}
public func addSocket(socket: SocketIOClient, labeledAs label: String) {
sockets[label] = socket
}
public func removeSocket(withLabel label: String) -> SocketIOClient? {
return sockets.removeValueForKey(label)
}
public func removeSocket(socket socket: SocketIOClient) -> SocketIOClient? {
var returnSocket: SocketIOClient?
for (label, dictSocket) in sockets where dictSocket === socket {
returnSocket = sockets.removeValueForKey(label)
}
return returnSocket
}
public func removeSockets() {
sockets.removeAll()
}
}

View File

@ -76,6 +76,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
private var pongsMissedMax = 0 private var pongsMissedMax = 0
private var probeWait = ProbeWaitQueue() private var probeWait = ProbeWaitQueue()
private var secure = false private var secure = false
private var security: SSLSecurity?
private var selfSigned = false private var selfSigned = false
private var voipEnabled = false private var voipEnabled = false
@ -105,6 +106,8 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
voipEnabled = enable voipEnabled = enable
case let .Secure(secure): case let .Secure(secure):
self.secure = secure self.secure = secure
case let .Security(security):
self.security = security
case let .SelfSigned(selfSigned): case let .SelfSigned(selfSigned):
self.selfSigned = selfSigned self.selfSigned = selfSigned
default: default:
@ -153,9 +156,8 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
// binary in base64 string // binary in base64 string
let noPrefix = message[message.startIndex.advancedBy(2)..<message.endIndex] let noPrefix = message[message.startIndex.advancedBy(2)..<message.endIndex]
if let data = NSData(base64EncodedString: noPrefix, if let data = NSData(base64EncodedString: noPrefix, options: .IgnoreUnknownCharacters) {
options: .IgnoreUnknownCharacters) { client?.parseEngineBinaryData(data)
client?.parseEngineBinaryData(data)
} }
return true return true
@ -164,6 +166,16 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
} }
} }
private func closeOutEngine() {
sid = ""
closed = true
invalidated = true
connected = false
ws?.disconnect()
stopPolling()
}
/// Starts the connection to the server /// Starts the connection to the server
public func connect() { public func connect() {
if connected { if connected {
@ -171,7 +183,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
disconnect("reconnect") disconnect("reconnect")
} }
DefaultSocketLogger.Logger.log("Starting engine", type: logType) DefaultSocketLogger.Logger.log("Starting engine. Server: %@", type: logType, args: url)
DefaultSocketLogger.Logger.log("Handshaking", type: logType) DefaultSocketLogger.Logger.log("Handshaking", type: logType)
resetEngine() resetEngine()
@ -256,6 +268,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
ws?.voipEnabled = voipEnabled ws?.voipEnabled = voipEnabled
ws?.delegate = self ws?.delegate = self
ws?.selfSignedSSL = selfSigned ws?.selfSignedSSL = selfSigned
ws?.security = security
ws?.connect() ws?.connect()
} }
@ -267,35 +280,32 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
} }
public func disconnect(reason: String) { public func disconnect(reason: String) {
func postSendClose(data: NSData?, _ res: NSURLResponse?, _ err: NSError?) { guard connected else { return closeOutEngine() }
sid = ""
closed = true
invalidated = true
connected = false
ws?.disconnect()
stopPolling()
client?.engineDidClose(reason)
}
DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType) DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType)
if closed { if closed {
closeOutEngine()
client?.engineDidClose(reason) client?.engineDidClose(reason)
return return
} }
if websocket { if websocket {
sendWebSocketMessage("", withType: .Close, withData: []) sendWebSocketMessage("", withType: .Close, withData: [])
postSendClose(nil, nil, nil) closeOutEngine()
} else { } else {
// We need to take special care when we're polling that we send it ASAP disconnectPolling()
// Also make sure we're on the emitQueue since we're touching postWait }
dispatch_sync(emitQueue) { }
self.postWait.append(String(SocketEnginePacketType.Close.rawValue))
let req = self.createRequestForPostWithPostWait() // We need to take special care when we're polling that we send it ASAP
self.doRequest(req, withCallback: postSendClose) // Also make sure we're on the emitQueue since we're touching postWait
} private func disconnectPolling() {
dispatch_sync(emitQueue) {
self.postWait.append(String(SocketEnginePacketType.Close.rawValue))
let req = self.createRequestForPostWithPostWait()
self.doRequest(req) {_, _, _ in }
self.closeOutEngine()
} }
} }
@ -335,7 +345,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
guard let ws = self.ws else { return } guard let ws = self.ws else { return }
for msg in postWait { for msg in postWait {
ws.writeString(fixDoubleUTF8(msg)) ws.writeString(msg)
} }
postWait.removeAll(keepCapacity: true) postWait.removeAll(keepCapacity: true)
@ -535,13 +545,11 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
connected = false connected = false
websocket = false websocket = false
let reason = error?.localizedDescription ?? "Socket Disconnected" if let reason = error?.localizedDescription {
if error != nil {
didError(reason) didError(reason)
} else {
client?.engineDidClose("Socket Disconnected")
} }
client?.engineDidClose(reason)
} else { } else {
flushProbeWait() flushProbeWait()
} }

View File

@ -123,7 +123,7 @@ extension SocketEnginePollable {
return return
} }
DefaultSocketLogger.Logger.log("Got polling response", type: "SocketEnginePolling") DefaultSocketLogger.Logger.log("Got polling response", type: "SocketEnginePolling")
if let str = String(data: data!, encoding: NSUTF8StringEncoding) { if let str = String(data: data!, encoding: NSUTF8StringEncoding) {
@ -213,9 +213,7 @@ extension SocketEnginePollable {
fixedMessage = message fixedMessage = message
} }
let strMsg = "\(type.rawValue)\(fixedMessage)" postWait.append(String(type.rawValue) + fixedMessage)
postWait.append(strMsg)
for data in datas { for data in datas {
if case let .Right(bin) = createBinaryDataForSend(data) { if case let .Right(bin) = createBinaryDataForSend(data) {

View File

@ -197,8 +197,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
handleEvent("disconnect", data: [reason], isInternalMessage: true) handleEvent("disconnect", data: [reason], isInternalMessage: true)
} }
/// Disconnects the socket. Only reconnect the same socket if you know what you're doing. /// Disconnects the socket.
/// Will turn off automatic reconnects.
public func disconnect() { public func disconnect() {
assert(status != .NotConnected, "Tried closing a NotConnected client") assert(status != .NotConnected, "Tried closing a NotConnected client")

View File

@ -45,6 +45,7 @@ public enum SocketIOClientOption : ClientOption {
case ReconnectAttempts(Int) case ReconnectAttempts(Int)
case ReconnectWait(Int) case ReconnectWait(Int)
case Secure(Bool) case Secure(Bool)
case Security(SSLSecurity)
case SelfSigned(Bool) case SelfSigned(Bool)
case SessionDelegate(NSURLSessionDelegate) case SessionDelegate(NSURLSessionDelegate)
case VoipEnabled(Bool) case VoipEnabled(Bool)
@ -85,6 +86,8 @@ public enum SocketIOClientOption : ClientOption {
description = "reconnectWait" description = "reconnectWait"
case .Secure: case .Secure:
description = "secure" description = "secure"
case .Security:
description = "security"
case .SelfSigned: case .SelfSigned:
description = "selfSigned" description = "selfSigned"
case .SessionDelegate: case .SessionDelegate:
@ -136,6 +139,8 @@ public enum SocketIOClientOption : ClientOption {
value = wait value = wait
case let .Secure(secure): case let .Secure(secure):
value = secure value = secure
case let .Security(security):
value = security
case let .SelfSigned(signed): case let .SelfSigned(signed):
value = signed value = signed
case let .SessionDelegate(delegate): case let .SessionDelegate(delegate):
@ -195,6 +200,8 @@ extension NSDictionary {
return .ReconnectWait(wait) return .ReconnectWait(wait)
case let ("secure", secure as Bool): case let ("secure", secure as Bool):
return .Secure(secure) return .Secure(secure)
case let ("security", security as SSLSecurity):
return .Security(security)
case let ("selfSigned", selfSigned as Bool): case let ("selfSigned", selfSigned as Bool):
return .SelfSigned(selfSigned) return .SelfSigned(selfSigned)
case let ("sessionDelegate", delegate as NSURLSessionDelegate): case let ("sessionDelegate", delegate as NSURLSessionDelegate):

View File

@ -111,73 +111,31 @@ struct SocketPacket {
return message + restOfMessage return message + restOfMessage
} }
private func createAck() -> String {
let message: String
if type == .Ack {
if nsp == "/" {
message = "3\(id)"
} else {
message = "3\(nsp),\(id)"
}
} else {
if nsp == "/" {
message = "6\(binary.count)-\(id)"
} else {
message = "6\(binary.count)-\(nsp),\(id)"
}
}
return completeMessage(message)
}
private func createMessageForEvent() -> String {
let message: String
if type == .Event {
if nsp == "/" {
if id == -1 {
message = "2"
} else {
message = "2\(id)"
}
} else {
if id == -1 {
message = "2\(nsp),"
} else {
message = "2\(nsp),\(id)"
}
}
} else {
if nsp == "/" {
if id == -1 {
message = "5\(binary.count)-"
} else {
message = "5\(binary.count)-\(id)"
}
} else {
if id == -1 {
message = "5\(binary.count)-\(nsp),"
} else {
message = "5\(binary.count)-\(nsp),\(id)"
}
}
}
return completeMessage(message)
}
private func createPacketString() -> String { private func createPacketString() -> String {
let str: String let typeString = String(type.rawValue)
let binaryCountString: String
let nspString: String
let idString: String
if type == .Event || type == .BinaryEvent { if type == .BinaryEvent || type == .BinaryAck {
str = createMessageForEvent() binaryCountString = typeString + String(binary.count) + "-"
} else { } else {
str = createAck() binaryCountString = typeString
} }
return str if nsp != "/" {
nspString = binaryCountString + nsp + ","
} else {
nspString = binaryCountString
}
if id != -1 {
idString = nspString + String(id)
} else {
idString = nspString
}
return completeMessage(idString)
} }
// Called when we have all the binary data for a packet // Called when we have all the binary data for a packet

View File

@ -96,14 +96,14 @@ extension SocketParsable {
if type == .Error { if type == .Error {
parser.advanceIndexBy(-1) parser.advanceIndexBy(-1)
} } else {
while parser.hasNext {
while parser.hasNext && type != .Error { if let int = Int(parser.read(1)) {
if let int = Int(parser.read(1)) { idString += String(int)
idString += String(int) } else {
} else { parser.advanceIndexBy(-2)
parser.advanceIndexBy(-2) break
break }
} }
} }
@ -162,9 +162,7 @@ extension SocketParsable {
} }
// Should execute event? // Should execute event?
guard waitingPackets[waitingPackets.count - 1].addData(data) else { guard waitingPackets[waitingPackets.count - 1].addData(data) else { return }
return
}
let packet = waitingPackets.removeLast() let packet = waitingPackets.removeLast()

View File

@ -0,0 +1,259 @@
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// SSLSecurity.swift
// Starscream
//
// Created by Dalton Cherry on 5/16/15.
// Copyright (c) 2014-2015 Dalton Cherry.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//////////////////////////////////////////////////////////////////////////////////////////////////
import Foundation
import Security
public class SSLCert : NSObject {
var certData: NSData?
var key: SecKeyRef?
/**
Designated init for certificates
- parameter data: is the binary data of the certificate
- returns: a representation security object to be used with
*/
public init(data: NSData) {
self.certData = data
}
/**
Designated init for public keys
- parameter key: is the public key to be used
- returns: a representation security object to be used with
*/
public init(key: SecKeyRef) {
self.key = key
}
}
public class SSLSecurity : NSObject {
public var validatedDN = true //should the domain name be validated?
var isReady = false //is the key processing done?
var certificates: [NSData]? //the certificates
var pubKeys: [SecKeyRef]? //the public keys
var usePublicKeys = false //use public keys or certificate validation?
/**
Use certs from main app bundle
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
- returns: a representation security object to be used with
*/
public convenience init(usePublicKeys: Bool = false) {
let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".")
let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in
var certs = certs
if let data = NSData(contentsOfFile: path) {
certs.append(SSLCert(data: data))
}
return certs
}
self.init(certs: certs, usePublicKeys: usePublicKeys)
}
/**
Designated init
- parameter keys: is the certificates or public keys to use
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
- returns: a representation security object to be used with
*/
public init(certs: [SSLCert], usePublicKeys: Bool) {
super.init()
self.usePublicKeys = usePublicKeys
if self.usePublicKeys {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) {
let pubKeys = certs.reduce([SecKeyRef]()) { (pubKeys: [SecKeyRef], cert: SSLCert) -> [SecKeyRef] in
var pubKeys = pubKeys
if let data = cert.certData where cert.key == nil {
cert.key = self.extractPublicKey(data)
}
if let key = cert.key {
pubKeys.append(key)
}
return pubKeys
}
self.pubKeys = pubKeys
self.isReady = true
}
} else {
let certificates = certs.reduce([NSData]()) { (certificates: [NSData], cert: SSLCert) -> [NSData] in
var certificates = certificates
if let data = cert.certData {
certificates.append(data)
}
return certificates
}
self.certificates = certificates
self.isReady = true
}
}
/**
Valid the trust and domain name.
- parameter trust: is the serverTrust to validate
- parameter domain: is the CN domain to validate
- returns: if the key was successfully validated
*/
public func isValid(trust: SecTrustRef, domain: String?) -> Bool {
var tries = 0
while(!self.isReady) {
usleep(1000)
tries += 1
if tries > 5 {
return false //doesn't appear it is going to ever be ready...
}
}
var policy: SecPolicyRef
if self.validatedDN {
policy = SecPolicyCreateSSL(true, domain)!
} else {
policy = SecPolicyCreateBasicX509()!
}
SecTrustSetPolicies(trust,policy)
if self.usePublicKeys {
if let keys = self.pubKeys {
let serverPubKeys = publicKeyChainForTrust(trust)
for serverKey in serverPubKeys as [AnyObject] {
for key in keys as [AnyObject] {
if serverKey.isEqual(key) {
return true
}
}
}
}
} else if let certs = self.certificates {
let serverCerts = certificateChainForTrust(trust)
var collect = [SecCertificate]()
for cert in certs {
collect.append(SecCertificateCreateWithData(nil,cert)!)
}
SecTrustSetAnchorCertificates(trust,collect)
var result = SecTrustResultType.Invalid
SecTrustEvaluate(trust,&result)
if result == .Unspecified || result == .Proceed {
var trustedCount = 0
for serverCert in serverCerts {
for cert in certs {
if cert == serverCert {
trustedCount += 1
break
}
}
}
if trustedCount == serverCerts.count {
return true
}
}
}
return false
}
/**
Get the public key from a certificate data
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKey(data: NSData) -> SecKeyRef? {
guard let cert = SecCertificateCreateWithData(nil, data) else { return nil }
return extractPublicKeyFromCert(cert, policy: SecPolicyCreateBasicX509()!)
}
/**
Get the public key from a certificate
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? {
var possibleTrust: SecTrust?
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
guard let trust = possibleTrust else { return nil }
var result = SecTrustResultType.Invalid
SecTrustEvaluate(trust, &result)
return SecTrustCopyPublicKey(trust)
}
/**
Get the certificate chain for the trust
- parameter trust: is the trust to lookup the certificate chain for
- returns: the certificate chain for the trust
*/
func certificateChainForTrust(trust: SecTrustRef) -> [NSData] {
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (certificates: [NSData], index: Int) -> [NSData] in
var certificates = certificates
let cert = SecTrustGetCertificateAtIndex(trust, index)
certificates.append(SecCertificateCopyData(cert!))
return certificates
}
return certificates
}
/**
Get the public key chain for the trust
- parameter trust: is the trust to lookup the certificate chain and extract the public keys
- returns: the public keys from the certifcate chain for the trust
*/
func publicKeyChainForTrust(trust: SecTrustRef) -> [SecKeyRef] {
let policy = SecPolicyCreateBasicX509()
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKeyRef]()) { (keys: [SecKeyRef], index: Int) -> [SecKeyRef] in
var keys = keys
let cert = SecTrustGetCertificateAtIndex(trust, index)
if let key = extractPublicKeyFromCert(cert!, policy: policy!) {
keys.append(key)
}
return keys
}
return keys
}
}

View File

@ -23,6 +23,10 @@ import Foundation
import CoreFoundation import CoreFoundation
import Security import Security
public let WebsocketDidConnectNotification = "WebsocketDidConnectNotification"
public let WebsocketDidDisconnectNotification = "WebsocketDidDisconnectNotification"
public let WebsocketDisconnectionErrorKeyName = "WebsocketDisconnectionErrorKeyName"
public protocol WebSocketDelegate: class { public protocol WebSocketDelegate: class {
func websocketDidConnect(socket: WebSocket) func websocketDidConnect(socket: WebSocket)
func websocketDidDisconnect(socket: WebSocket, error: NSError?) func websocketDidDisconnect(socket: WebSocket, error: NSError?)
@ -35,7 +39,7 @@ public protocol WebSocketPongDelegate: class {
} }
public class WebSocket : NSObject, NSStreamDelegate { public class WebSocket : NSObject, NSStreamDelegate {
enum OpCode : UInt8 { enum OpCode : UInt8 {
case ContinueFrame = 0x0 case ContinueFrame = 0x0
case TextFrame = 0x1 case TextFrame = 0x1
@ -46,7 +50,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
case Pong = 0xA case Pong = 0xA
//B-F reserved. //B-F reserved.
} }
public enum CloseCode : UInt16 { public enum CloseCode : UInt16 {
case Normal = 1000 case Normal = 1000
case GoingAway = 1001 case GoingAway = 1001
@ -59,17 +63,17 @@ public class WebSocket : NSObject, NSStreamDelegate {
case PolicyViolated = 1008 case PolicyViolated = 1008
case MessageTooBig = 1009 case MessageTooBig = 1009
} }
public static let ErrorDomain = "WebSocket" public static let ErrorDomain = "WebSocket"
enum InternalErrorCode : UInt16 { enum InternalErrorCode : UInt16 {
// 0-999 WebSocket status codes not used // 0-999 WebSocket status codes not used
case OutputStreamWriteError = 1 case OutputStreamWriteError = 1
} }
//Where the callback is executed. It defaults to the main UI thread queue. //Where the callback is executed. It defaults to the main UI thread queue.
public var queue = dispatch_get_main_queue() public var queue = dispatch_get_main_queue()
var optionalProtocols : [String]? var optionalProtocols : [String]?
//Constant Values. //Constant Values.
let headerWSUpgradeName = "Upgrade" let headerWSUpgradeName = "Upgrade"
@ -90,7 +94,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
let MaskMask: UInt8 = 0x80 let MaskMask: UInt8 = 0x80
let PayloadLenMask: UInt8 = 0x7F let PayloadLenMask: UInt8 = 0x7F
let MaxFrameSize: Int = 32 let MaxFrameSize: Int = 32
class WSResponse { class WSResponse {
var isFin = false var isFin = false
var code: OpCode = .ContinueFrame var code: OpCode = .ContinueFrame
@ -98,7 +102,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
var frameCount = 0 var frameCount = 0
var buffer: NSMutableData? var buffer: NSMutableData?
} }
public weak var delegate: WebSocketDelegate? public weak var delegate: WebSocketDelegate?
public weak var pongDelegate: WebSocketPongDelegate? public weak var pongDelegate: WebSocketPongDelegate?
public var onConnect: ((Void) -> Void)? public var onConnect: ((Void) -> Void)?
@ -130,6 +134,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
private var didDisconnect = false private var didDisconnect = false
private var readyToWrite = false private var readyToWrite = false
private let mutex = NSLock() private let mutex = NSLock()
private let notificationCenter = NSNotificationCenter.defaultCenter()
private var canDispatch: Bool { private var canDispatch: Bool {
mutex.lock() mutex.lock()
let canWork = readyToWrite let canWork = readyToWrite
@ -138,7 +143,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
//the shared processing queue used for all websocket //the shared processing queue used for all websocket
private static let sharedWorkQueue = dispatch_queue_create("com.vluxe.starscream.websocket", DISPATCH_QUEUE_SERIAL) private static let sharedWorkQueue = dispatch_queue_create("com.vluxe.starscream.websocket", DISPATCH_QUEUE_SERIAL)
//used for setting protocols. //used for setting protocols.
public init(url: NSURL, protocols: [String]? = nil) { public init(url: NSURL, protocols: [String]? = nil) {
self.url = url self.url = url
@ -146,7 +151,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
writeQueue.maxConcurrentOperationCount = 1 writeQueue.maxConcurrentOperationCount = 1
optionalProtocols = protocols optionalProtocols = protocols
} }
///Connect to the websocket server on a background thread ///Connect to the websocket server on a background thread
public func connect() { public func connect() {
guard !isCreated else { return } guard !isCreated else { return }
@ -155,14 +160,14 @@ public class WebSocket : NSObject, NSStreamDelegate {
createHTTPRequest() createHTTPRequest()
isCreated = false isCreated = false
} }
/** /**
Disconnect from the server. I send a Close control frame to the server, then expect the server to respond with a Close control frame and close the socket from its end. I notify my delegate once the socket has been closed. Disconnect from the server. I send a Close control frame to the server, then expect the server to respond with a Close control frame and close the socket from its end. I notify my delegate once the socket has been closed.
If you supply a non-nil `forceTimeout`, I wait at most that long (in seconds) for the server to close the socket. After the timeout expires, I close the socket and notify my delegate. If you supply a non-nil `forceTimeout`, I wait at most that long (in seconds) for the server to close the socket. After the timeout expires, I close the socket and notify my delegate.
If you supply a zero (or negative) `forceTimeout`, I immediately close the socket (without sending a Close control frame) and notify my delegate. If you supply a zero (or negative) `forceTimeout`, I immediately close the socket (without sending a Close control frame) and notify my delegate.
- Parameter forceTimeout: Maximum time to wait for the server to close the socket. - Parameter forceTimeout: Maximum time to wait for the server to close the socket.
*/ */
public func disconnect(forceTimeout forceTimeout: NSTimeInterval? = nil) { public func disconnect(forceTimeout forceTimeout: NSTimeInterval? = nil) {
@ -174,17 +179,18 @@ public class WebSocket : NSObject, NSStreamDelegate {
fallthrough fallthrough
case .None: case .None:
writeError(CloseCode.Normal.rawValue) writeError(CloseCode.Normal.rawValue)
default: default:
self.disconnectStream(nil) self.disconnectStream(nil)
break break
} }
} }
/** /**
Write a string to the websocket. This sends it as a text frame. Write a string to the websocket. This sends it as a text frame.
If you supply a non-nil completion block, I will perform it when the write completes. If you supply a non-nil completion block, I will perform it when the write completes.
- parameter str: The string to write. - parameter str: The string to write.
- parameter completion: The (optional) completion handler. - parameter completion: The (optional) completion handler.
*/ */
@ -192,11 +198,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
guard isConnected else { return } guard isConnected else { return }
dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame, writeCompletion: completion) dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame, writeCompletion: completion)
} }
/** /**
Write binary data to the websocket. This sends it as a binary frame. Write binary data to the websocket. This sends it as a binary frame.
If you supply a non-nil completion block, I will perform it when the write completes. If you supply a non-nil completion block, I will perform it when the write completes.
- parameter data: The data to write. - parameter data: The data to write.
- parameter completion: The (optional) completion handler. - parameter completion: The (optional) completion handler.
*/ */
@ -204,20 +211,20 @@ public class WebSocket : NSObject, NSStreamDelegate {
guard isConnected else { return } guard isConnected else { return }
dequeueWrite(data, code: .BinaryFrame, writeCompletion: completion) dequeueWrite(data, code: .BinaryFrame, writeCompletion: completion)
} }
//write a ping to the websocket. This sends it as a control frame. //write a ping to the websocket. This sends it as a control frame.
//yodel a sound to the planet. This sends it as an astroid. http://youtu.be/Eu5ZJELRiJ8?t=42s //yodel a sound to the planet. This sends it as an astroid. http://youtu.be/Eu5ZJELRiJ8?t=42s
public func writePing(data: NSData, completion: (() -> ())? = nil) { public func writePing(data: NSData, completion: (() -> ())? = nil) {
guard isConnected else { return } guard isConnected else { return }
dequeueWrite(data, code: .Ping, writeCompletion: completion) dequeueWrite(data, code: .Ping, writeCompletion: completion)
} }
//private method that starts the connection //private method that starts the connection
private func createHTTPRequest() { private func createHTTPRequest() {
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET", let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET",
url, kCFHTTPVersion1_1).takeRetainedValue() url, kCFHTTPVersion1_1).takeRetainedValue()
var port = url.port var port = url.port
if port == nil { if port == nil {
if let scheme = url.scheme where ["wss", "https"].contains(scheme) { if let scheme = url.scheme where ["wss", "https"].contains(scheme) {
@ -245,12 +252,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
initStreamsWithData(serializedRequest, Int(port!)) initStreamsWithData(serializedRequest, Int(port!))
} }
} }
//Add a header to the CFHTTPMessage by using the NSString bridges to CFString //Add a header to the CFHTTPMessage by using the NSString bridges to CFString
private func addHeader(urlRequest: CFHTTPMessage, key: NSString, val: NSString) { private func addHeader(urlRequest: CFHTTPMessage, key: NSString, val: NSString) {
CFHTTPMessageSetHeaderFieldValue(urlRequest, key, val) CFHTTPMessageSetHeaderFieldValue(urlRequest, key, val)
} }
//generate a websocket key as needed in rfc //generate a websocket key as needed in rfc
private func generateWebSocketKey() -> String { private func generateWebSocketKey() -> String {
var key = "" var key = ""
@ -263,12 +270,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
let baseKey = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) let baseKey = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
return baseKey! return baseKey!
} }
//Start the stream connection and write the data to the output stream //Start the stream connection and write the data to the output stream
private func initStreamsWithData(data: NSData, _ port: Int) { private func initStreamsWithData(data: NSData, _ port: Int) {
//higher level API we will cut over to at some point //higher level API we will cut over to at some point
//NSStream.getStreamsToHostWithName(url.host, port: url.port.integerValue, inputStream: &inputStream, outputStream: &outputStream) //NSStream.getStreamsToHostWithName(url.host, port: url.port.integerValue, inputStream: &inputStream, outputStream: &outputStream)
var readStream: Unmanaged<CFReadStream>? var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>? var writeStream: Unmanaged<CFWriteStream>?
let h: NSString = url.host! let h: NSString = url.host!
@ -314,11 +321,11 @@ public class WebSocket : NSObject, NSStreamDelegate {
CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue) CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue)
inStream.open() inStream.open()
outStream.open() outStream.open()
self.mutex.lock() self.mutex.lock()
self.readyToWrite = true self.readyToWrite = true
self.mutex.unlock() self.mutex.unlock()
let bytes = UnsafePointer<UInt8>(data.bytes) let bytes = UnsafePointer<UInt8>(data.bytes)
var out = timeout * 1000000 //wait 5 seconds before giving up var out = timeout * 1000000 //wait 5 seconds before giving up
writeQueue.addOperationWithBlock { [weak self] in writeQueue.addOperationWithBlock { [weak self] in
@ -338,7 +345,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
//delegate for the stream methods. Processes incoming bytes //delegate for the stream methods. Processes incoming bytes
public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) { public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
if let sec = security where !certValidated && [.HasBytesAvailable, .HasSpaceAvailable].contains(eventCode) { if let sec = security where !certValidated && [.HasBytesAvailable, .HasSpaceAvailable].contains(eventCode) {
let possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as String) let possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as String)
if let trust: AnyObject = possibleTrust { if let trust: AnyObject = possibleTrust {
@ -372,7 +379,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
cleanupStream() cleanupStream()
doDisconnect(error) doDisconnect(error)
} }
private func cleanupStream() { private func cleanupStream() {
outputStream?.delegate = nil outputStream?.delegate = nil
inputStream?.delegate = nil inputStream?.delegate = nil
@ -387,13 +394,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
outputStream = nil outputStream = nil
inputStream = nil inputStream = nil
} }
///handles the incoming bytes and sending them to the proper processing method ///handles the incoming bytes and sending them to the proper processing method
private func processInputStream() { private func processInputStream() {
let buf = NSMutableData(capacity: BUFFER_MAX) let buf = NSMutableData(capacity: BUFFER_MAX)
let buffer = UnsafeMutablePointer<UInt8>(buf!.bytes) let buffer = UnsafeMutablePointer<UInt8>(buf!.bytes)
let length = inputStream!.read(buffer, maxLength: BUFFER_MAX) let length = inputStream!.read(buffer, maxLength: BUFFER_MAX)
guard length > 0 else { return } guard length > 0 else { return }
var process = false var process = false
if inputQueue.count == 0 { if inputQueue.count == 0 {
@ -425,7 +432,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
inputQueue = inputQueue.filter{$0 != data} inputQueue = inputQueue.filter{$0 != data}
} }
} }
//handle checking the inital connection status //handle checking the inital connection status
private func processTCPHandshake(buffer: UnsafePointer<UInt8>, bufferLen: Int) { private func processTCPHandshake(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
let code = processHTTP(buffer, bufferLen: bufferLen) let code = processHTTP(buffer, bufferLen: bufferLen)
@ -437,6 +444,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
guard let s = self else { return } guard let s = self else { return }
s.onConnect?() s.onConnect?()
s.delegate?.websocketDidConnect(s) s.delegate?.websocketDidConnect(s)
s.notificationCenter.postNotificationName(WebsocketDidConnectNotification, object: self)
} }
case -1: case -1:
fragBuffer = NSData(bytes: buffer, length: bufferLen) fragBuffer = NSData(bytes: buffer, length: bufferLen)
@ -475,7 +483,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
return -1 //was unable to find the full TCP header return -1 //was unable to find the full TCP header
} }
///validates the HTTP is a 101 as per the RFC spec ///validates the HTTP is a 101 as per the RFC spec
private func validateResponse(buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Int { private func validateResponse(buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Int {
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue() let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue()
@ -494,12 +502,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
return -1 return -1
} }
///read a 16 bit big endian value from a buffer ///read a 16 bit big endian value from a buffer
private static func readUint16(buffer: UnsafePointer<UInt8>, offset: Int) -> UInt16 { private static func readUint16(buffer: UnsafePointer<UInt8>, offset: Int) -> UInt16 {
return (UInt16(buffer[offset + 0]) << 8) | UInt16(buffer[offset + 1]) return (UInt16(buffer[offset + 0]) << 8) | UInt16(buffer[offset + 1])
} }
///read a 64 bit big endian value from a buffer ///read a 64 bit big endian value from a buffer
private static func readUint64(buffer: UnsafePointer<UInt8>, offset: Int) -> UInt64 { private static func readUint64(buffer: UnsafePointer<UInt8>, offset: Int) -> UInt64 {
var value = UInt64(0) var value = UInt64(0)
@ -508,20 +516,20 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
return value return value
} }
///write a 16 bit big endian value to a buffer ///write a 16 bit big endian value to a buffer
private static func writeUint16(buffer: UnsafeMutablePointer<UInt8>, offset: Int, value: UInt16) { private static func writeUint16(buffer: UnsafeMutablePointer<UInt8>, offset: Int, value: UInt16) {
buffer[offset + 0] = UInt8(value >> 8) buffer[offset + 0] = UInt8(value >> 8)
buffer[offset + 1] = UInt8(value & 0xff) buffer[offset + 1] = UInt8(value & 0xff)
} }
///write a 64 bit big endian value to a buffer ///write a 64 bit big endian value to a buffer
private static func writeUint64(buffer: UnsafeMutablePointer<UInt8>, offset: Int, value: UInt64) { private static func writeUint64(buffer: UnsafeMutablePointer<UInt8>, offset: Int, value: UInt64) {
for i in 0...7 { for i in 0...7 {
buffer[offset + i] = UInt8((value >> (8*UInt64(7 - i))) & 0xff) buffer[offset + i] = UInt8((value >> (8*UInt64(7 - i))) & 0xff)
} }
} }
/// Process one message at the start of `buffer`. Return another buffer (sharing storage) that contains the leftover contents of `buffer` that I didn't process. /// Process one message at the start of `buffer`. Return another buffer (sharing storage) that contains the leftover contents of `buffer` that I didn't process.
@warn_unused_result @warn_unused_result
private func processOneRawMessage(inBuffer buffer: UnsafeBufferPointer<UInt8>) -> UnsafeBufferPointer<UInt8> { private func processOneRawMessage(inBuffer buffer: UnsafeBufferPointer<UInt8>) -> UnsafeBufferPointer<UInt8> {
@ -676,12 +684,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
processResponse(response) processResponse(response)
} }
let step = Int(offset+numericCast(len)) let step = Int(offset+numericCast(len))
return buffer.fromOffset(step) return buffer.fromOffset(step)
} }
} }
/// Process all messages in the buffer if possible. /// Process all messages in the buffer if possible.
private func processRawMessagesInBuffer(pointer: UnsafePointer<UInt8>, bufferLen: Int) { private func processRawMessagesInBuffer(pointer: UnsafePointer<UInt8>, bufferLen: Int) {
var buffer = UnsafeBufferPointer(start: pointer, count: bufferLen) var buffer = UnsafeBufferPointer(start: pointer, count: bufferLen)
@ -692,7 +700,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
fragBuffer = NSData(buffer: buffer) fragBuffer = NSData(buffer: buffer)
} }
} }
///process the finished response of a buffer ///process the finished response of a buffer
private func processResponse(response: WSResponse) -> Bool { private func processResponse(response: WSResponse) -> Bool {
if response.isFin && response.bytesLeft <= 0 { if response.isFin && response.bytesLeft <= 0 {
@ -727,14 +735,14 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
return false return false
} }
///Create an error ///Create an error
private func errorWithDetail(detail: String, code: UInt16) -> NSError { private func errorWithDetail(detail: String, code: UInt16) -> NSError {
var details = [String: String]() var details = [String: String]()
details[NSLocalizedDescriptionKey] = detail details[NSLocalizedDescriptionKey] = detail
return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details) return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details)
} }
///write a an error to the socket ///write a an error to the socket
private func writeError(code: UInt16) { private func writeError(code: UInt16) {
let buf = NSMutableData(capacity: sizeof(UInt16)) let buf = NSMutableData(capacity: sizeof(UInt16))
@ -768,7 +776,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset) let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey) SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey)
offset += sizeof(UInt32) offset += sizeof(UInt32)
for i in 0..<dataLength { for i in 0..<dataLength {
buffer[offset] = bytes[i] ^ maskKey[i % sizeof(UInt32)] buffer[offset] = bytes[i] ^ maskKey[i % sizeof(UInt32)]
offset += 1 offset += 1
@ -797,14 +805,14 @@ public class WebSocket : NSObject, NSStreamDelegate {
callback() callback()
} }
} }
break break
} }
} }
} }
} }
///used to preform the disconnect delegate ///used to preform the disconnect delegate
private func doDisconnect(error: NSError?) { private func doDisconnect(error: NSError?) {
guard !didDisconnect else { return } guard !didDisconnect else { return }
@ -815,269 +823,34 @@ public class WebSocket : NSObject, NSStreamDelegate {
guard let s = self else { return } guard let s = self else { return }
s.onDisconnect?(error) s.onDisconnect?(error)
s.delegate?.websocketDidDisconnect(s, error: error) s.delegate?.websocketDidDisconnect(s, error: error)
let userInfo = error.map({ [WebsocketDisconnectionErrorKeyName: $0] })
s.notificationCenter.postNotificationName(WebsocketDidDisconnectNotification, object: self, userInfo: userInfo)
} }
} }
deinit { deinit {
mutex.lock() mutex.lock()
readyToWrite = false readyToWrite = false
mutex.unlock() mutex.unlock()
cleanupStream() cleanupStream()
} }
} }
private extension NSData { private extension NSData {
convenience init(buffer: UnsafeBufferPointer<UInt8>) { convenience init(buffer: UnsafeBufferPointer<UInt8>) {
self.init(bytes: buffer.baseAddress, length: buffer.count) self.init(bytes: buffer.baseAddress, length: buffer.count)
} }
} }
private extension UnsafeBufferPointer { private extension UnsafeBufferPointer {
func fromOffset(offset: Int) -> UnsafeBufferPointer<Element> { func fromOffset(offset: Int) -> UnsafeBufferPointer<Element> {
return UnsafeBufferPointer<Element>(start: baseAddress.advancedBy(offset), count: count - offset) return UnsafeBufferPointer<Element>(start: baseAddress.advancedBy(offset), count: count - offset)
} }
} }
private let emptyBuffer = UnsafeBufferPointer<UInt8>(start: nil, count: 0) private let emptyBuffer = UnsafeBufferPointer<UInt8>(start: nil, count: 0)
public class SSLCert {
var certData: NSData?
var key: SecKeyRef?
/**
Designated init for certificates
- parameter data: is the binary data of the certificate
- returns: a representation security object to be used with
*/
public init(data: NSData) {
self.certData = data
}
/**
Designated init for public keys
- parameter key: is the public key to be used
- returns: a representation security object to be used with
*/
public init(key: SecKeyRef) {
self.key = key
}
}
public class SSLSecurity {
public var validatedDN = true //should the domain name be validated?
var isReady = false //is the key processing done?
var certificates: [NSData]? //the certificates
var pubKeys: [SecKeyRef]? //the public keys
var usePublicKeys = false //use public keys or certificate validation?
/**
Use certs from main app bundle
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
- returns: a representation security object to be used with
*/
public convenience init(usePublicKeys: Bool = false) {
let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".")
let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in
var certs = certs
if let data = NSData(contentsOfFile: path) {
certs.append(SSLCert(data: data))
}
return certs
}
self.init(certs: certs, usePublicKeys: usePublicKeys)
}
/**
Designated init
- parameter keys: is the certificates or public keys to use
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
- returns: a representation security object to be used with
*/
public init(certs: [SSLCert], usePublicKeys: Bool) {
self.usePublicKeys = usePublicKeys
if self.usePublicKeys {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) {
let pubKeys = certs.reduce([SecKeyRef]()) { (pubKeys: [SecKeyRef], cert: SSLCert) -> [SecKeyRef] in
var pubKeys = pubKeys
if let data = cert.certData where cert.key == nil {
cert.key = self.extractPublicKey(data)
}
if let key = cert.key {
pubKeys.append(key)
}
return pubKeys
}
self.pubKeys = pubKeys
self.isReady = true
}
} else {
let certificates = certs.reduce([NSData]()) { (certificates: [NSData], cert: SSLCert) -> [NSData] in
var certificates = certificates
if let data = cert.certData {
certificates.append(data)
}
return certificates
}
self.certificates = certificates
self.isReady = true
}
}
/**
Valid the trust and domain name.
- parameter trust: is the serverTrust to validate
- parameter domain: is the CN domain to validate
- returns: if the key was successfully validated
*/
public func isValid(trust: SecTrustRef, domain: String?) -> Bool {
var tries = 0
while(!self.isReady) {
usleep(1000)
tries += 1
if tries > 5 {
return false //doesn't appear it is going to ever be ready...
}
}
var policy: SecPolicyRef
if self.validatedDN {
guard let aPolicy = SecPolicyCreateSSL(true, domain) else { return false }
policy = aPolicy
} else {
guard let aPolicy = SecPolicyCreateBasicX509() else { return false }
policy = aPolicy
}
SecTrustSetPolicies(trust,policy)
if self.usePublicKeys {
if let keys = self.pubKeys {
let serverPubKeys = publicKeyChainForTrust(trust)
for serverKey in serverPubKeys as [AnyObject] {
for key in keys as [AnyObject] {
if serverKey.isEqual(key) {
return true
}
}
}
}
} else if let certs = self.certificates {
let serverCerts = certificateChainForTrust(trust)
var collect = [SecCertificate]()
for cert in certs {
collect.append(SecCertificateCreateWithData(nil,cert)!)
}
SecTrustSetAnchorCertificates(trust,collect)
var result = SecTrustResultType.Invalid
SecTrustEvaluate(trust,&result)
if result == .Unspecified || result == .Proceed {
var trustedCount = 0
for serverCert in serverCerts {
for cert in certs {
if cert == serverCert {
trustedCount += 1
break
}
}
}
if trustedCount == serverCerts.count {
return true
}
}
}
return false
}
/**
Get the public key from a certificate data
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKey(data: NSData) -> SecKeyRef? {
guard let cert = SecCertificateCreateWithData(nil, data) else { return nil }
guard let policy = SecPolicyCreateBasicX509() else { return nil }
return extractPublicKeyFromCert(cert, policy: policy)
}
/**
Get the public key from a certificate
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? {
var possibleTrust: SecTrust?
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
guard let trust = possibleTrust else { return nil }
var result: SecTrustResultType = .Invalid
SecTrustEvaluate(trust, &result)
return SecTrustCopyPublicKey(trust)
}
/**
Get the certificate chain for the trust
- parameter trust: is the trust to lookup the certificate chain for
- returns: the certificate chain for the trust
*/
func certificateChainForTrust(trust: SecTrustRef) -> [NSData] {
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (certificates: [NSData], index: Int) -> [NSData] in
var certificates = certificates
let cert = SecTrustGetCertificateAtIndex(trust, index)
certificates.append(SecCertificateCopyData(cert!))
return certificates
}
return certificates
}
/**
Get the public key chain for the trust
- parameter trust: is the trust to lookup the certificate chain and extract the public keys
- returns: the public keys from the certifcate chain for the trust
*/
func publicKeyChainForTrust(trust: SecTrustRef) -> [SecKeyRef] {
guard let policy = SecPolicyCreateBasicX509() else { return [] }
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKeyRef]()) { (keys: [SecKeyRef], index: Int) -> [SecKeyRef] in
var keys = keys
let cert = SecTrustGetCertificateAtIndex(trust, index)
if let key = extractPublicKeyFromCert(cert!, policy: policy) {
keys.append(key)
}
return keys
}
return keys
}
}