diff --git a/README.md b/README.md index 056b0f5..ca6b3a3 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ Carthage ----------------- 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`. @@ -102,7 +102,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' 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: @@ -130,7 +130,7 @@ CocoaSeeds 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`. @@ -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 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 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 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 ``` diff --git a/Socket.IO-Client-Swift.podspec b/Socket.IO-Client-Swift.podspec index ddf7a2d..eb2c5bb 100644 --- a/Socket.IO-Client-Swift.podspec +++ b/Socket.IO-Client-Swift.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "Socket.IO-Client-Swift" 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.description = <<-DESC Socket.IO-client for iOS and OS X. @@ -14,7 +14,7 @@ Pod::Spec.new do |s| s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.10' 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.requires_arc = true # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files diff --git a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj index 86bfd7a..4edf63b 100644 --- a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj +++ b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj @@ -69,9 +69,7 @@ 74171EC31C10CD240062D398 /* 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 */; }; - 74171ECF1C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; }; - 74171ED11C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; }; - 74171ED31C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; }; + 74171EC81C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; }; 741F39EE1BD025D80026C9CC /* 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 */; }; @@ -87,6 +85,15 @@ 7472C65F1BCAC46E003CA70D /* 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 */; }; + 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 */; }; 74F124F11BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.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 = ""; }; 572EF21E1B51F16C00EEBB58 /* SocketIO-iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketIO-iOS.h"; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; 572EF23C1B51F18A00EEBB58 /* SocketIO-Mac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketIO-Mac.h"; sourceTree = ""; }; @@ -151,7 +157,6 @@ 74171E5E1C10CD240062D398 /* SocketParsable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketParsable.swift; path = Source/SocketParsable.swift; sourceTree = ""; }; 74171E5F1C10CD240062D398 /* SocketStringReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketStringReader.swift; path = Source/SocketStringReader.swift; sourceTree = ""; }; 74171E601C10CD240062D398 /* SocketTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketTypes.swift; path = Source/SocketTypes.swift; sourceTree = ""; }; - 74171E621C10CD240062D398 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket.swift; sourceTree = ""; }; 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = ""; }; 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEnginePollable.swift; path = Source/SocketEnginePollable.swift; sourceTree = ""; }; 742D150B1CA5794B00BD987D /* SocketObjectiveCTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketObjectiveCTest.m; sourceTree = ""; }; @@ -160,6 +165,9 @@ 7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespacePacketTest.swift; sourceTree = ""; }; 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketSideEffectTest.swift; sourceTree = ""; }; 74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientSpec.swift; path = Source/SocketIOClientSpec.swift; sourceTree = ""; }; + 74B4AD1C1D09A5D80062A523 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket/WebSocket.swift; sourceTree = ""; }; + 74B4AD201D09A6190062A523 /* SSLSecurity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SSLSecurity.swift; path = Source/WebSocket/SSLSecurity.swift; sourceTree = ""; }; + 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketClientManager.swift; path = Source/SocketClientManager.swift; sourceTree = ""; }; 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketBasicPacketTest.swift; sourceTree = ""; }; CEBA56951CDA0B7700BA0389 /* NSCharacterSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSCharacterSet.swift; path = Source/NSCharacterSet.swift; sourceTree = ""; }; CEBA56991CDA0B8200BA0389 /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = String.swift; path = Source/String.swift; sourceTree = ""; }; @@ -219,7 +227,6 @@ children = ( 5764DF7B1B51F24A004FF46E /* Source */, 572EF21B1B51F16C00EEBB58 /* SocketIO-iOS */, - 572EF2281B51F16C00EEBB58 /* SocketIO-iOSTests */, 572EF2391B51F18A00EEBB58 /* SocketIO-Mac */, 572EF2461B51F18A00EEBB58 /* SocketIO-MacTests */, 572EF21A1B51F16C00EEBB58 /* Products */, @@ -256,22 +263,6 @@ name = "Supporting Files"; sourceTree = ""; }; - 572EF2281B51F16C00EEBB58 /* SocketIO-iOSTests */ = { - isa = PBXGroup; - children = ( - 572EF2291B51F16C00EEBB58 /* Supporting Files */, - ); - path = "SocketIO-iOSTests"; - sourceTree = ""; - }; - 572EF2291B51F16C00EEBB58 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 572EF22A1B51F16C00EEBB58 /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; 572EF2391B51F18A00EEBB58 /* SocketIO-Mac */ = { isa = PBXGroup; children = ( @@ -315,10 +306,10 @@ 5764DF7B1B51F24A004FF46E /* Source */ = { isa = PBXGroup; children = ( - CEBA569E1CDA0C0C00BA0389 /* utils */, 74171E501C10CD240062D398 /* SocketAckEmitter.swift */, 74171E511C10CD240062D398 /* SocketAckManager.swift */, 74171E521C10CD240062D398 /* SocketAnyEvent.swift */, + 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */, 74171E531C10CD240062D398 /* SocketEngine.swift */, 74171E541C10CD240062D398 /* SocketEngineClient.swift */, 74171E551C10CD240062D398 /* SocketEnginePacketType.swift */, @@ -335,11 +326,21 @@ 74171E5E1C10CD240062D398 /* SocketParsable.swift */, 74171E5F1C10CD240062D398 /* SocketStringReader.swift */, 74171E601C10CD240062D398 /* SocketTypes.swift */, - 74171E621C10CD240062D398 /* WebSocket.swift */, + CEBA569E1CDA0C0C00BA0389 /* utils */, + 74B4AD1B1D09A5C30062A523 /* Websocket */, ); name = Source; sourceTree = ""; }; + 74B4AD1B1D09A5C30062A523 /* Websocket */ = { + isa = PBXGroup; + children = ( + 74B4AD1C1D09A5D80062A523 /* WebSocket.swift */, + 74B4AD201D09A6190062A523 /* SSLSecurity.swift */, + ); + name = Websocket; + sourceTree = ""; + }; CEBA569E1CDA0C0C00BA0389 /* utils */ = { isa = PBXGroup; children = ( @@ -595,6 +596,7 @@ 740CA1221C496EF700CB98F4 /* SocketEngineWebsocket.swift in Sources */, 74171EA51C10CD240062D398 /* SocketIOClientStatus.swift in Sources */, CEBA569A1CDA0B8200BA0389 /* String.swift in Sources */, + 74B4AD1D1D09A5D80062A523 /* WebSocket.swift in Sources */, 74171E751C10CD240062D398 /* SocketEngine.swift in Sources */, 74171E691C10CD240062D398 /* SocketAckManager.swift in Sources */, 7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */, @@ -602,18 +604,19 @@ 74ABF7771C3991C10078C657 /* SocketIOClientSpec.swift in Sources */, 74171E871C10CD240062D398 /* SocketEngineSpec.swift in Sources */, 74171E631C10CD240062D398 /* SocketAckEmitter.swift in Sources */, + 74B4AD211D09A6190062A523 /* SSLSecurity.swift in Sources */, 74171EBD1C10CD240062D398 /* SocketStringReader.swift in Sources */, 74171EC31C10CD240062D398 /* SocketTypes.swift in Sources */, 74171EAB1C10CD240062D398 /* SocketLogger.swift in Sources */, 74171E991C10CD240062D398 /* SocketIOClient.swift in Sources */, 74171E8D1C10CD240062D398 /* SocketEventHandler.swift in Sources */, 74171E7B1C10CD240062D398 /* SocketEngineClient.swift in Sources */, - 74171ECF1C10CD240062D398 /* WebSocket.swift in Sources */, 74171EB11C10CD240062D398 /* SocketPacket.swift in Sources */, 74171EB71C10CD240062D398 /* SocketParsable.swift in Sources */, 74171E811C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, 74171E6F1C10CD240062D398 /* SocketAnyEvent.swift in Sources */, 74171E9F1C10CD240062D398 /* SocketIOClientOption.swift in Sources */, + 74BC45AB1D0C6675008CC431 /* SocketClientManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -622,6 +625,15 @@ buildActionMask = 2147483647; files = ( 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 */, 74F124F01BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */, 7472C65C1BCAB53E003CA70D /* SocketNamespacePacketTest.swift in Sources */, @@ -632,6 +644,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 74B4AD241D09A6450062A523 /* WebSocket.swift in Sources */, 740CA1211C496EF200CB98F4 /* SocketEngineWebsocket.swift in Sources */, 7471CCEA1C39926300364B59 /* SocketIOClientSpec.swift in Sources */, CEBA569B1CDA0B8200BA0389 /* String.swift in Sources */, @@ -648,12 +661,13 @@ 74171E9B1C10CD240062D398 /* SocketIOClient.swift in Sources */, 74171E8F1C10CD240062D398 /* SocketEventHandler.swift in Sources */, 74171E7D1C10CD240062D398 /* SocketEngineClient.swift in Sources */, - 74171ED11C10CD240062D398 /* WebSocket.swift in Sources */, + 74B4AD221D09A6190062A523 /* SSLSecurity.swift in Sources */, 74171EB31C10CD240062D398 /* SocketPacket.swift in Sources */, 74171EB91C10CD240062D398 /* SocketParsable.swift in Sources */, 74171E831C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, 74171E711C10CD240062D398 /* SocketAnyEvent.swift in Sources */, 74171EA11C10CD240062D398 /* SocketIOClientOption.swift in Sources */, + 74BC45AC1D0C6675008CC431 /* SocketClientManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -676,6 +690,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 74B4AD251D09A6490062A523 /* WebSocket.swift in Sources */, 740CA1201C496EEB00CB98F4 /* SocketEngineWebsocket.swift in Sources */, 7471CCEB1C39926C00364B59 /* SocketIOClientSpec.swift in Sources */, CEBA569C1CDA0B8200BA0389 /* String.swift in Sources */, @@ -692,12 +707,13 @@ 74171E9D1C10CD240062D398 /* SocketIOClient.swift in Sources */, 74171E911C10CD240062D398 /* SocketEventHandler.swift in Sources */, 74171E7F1C10CD240062D398 /* SocketEngineClient.swift in Sources */, - 74171ED31C10CD240062D398 /* WebSocket.swift in Sources */, + 74B4AD231D09A6190062A523 /* SSLSecurity.swift in Sources */, 74171EB51C10CD240062D398 /* SocketPacket.swift in Sources */, 74171EBB1C10CD240062D398 /* SocketParsable.swift in Sources */, 74171E851C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, 74171E731C10CD240062D398 /* SocketAnyEvent.swift in Sources */, 74171EA31C10CD240062D398 /* SocketIOClientOption.swift in Sources */, + 74BC45AD1D0C6675008CC431 /* SocketClientManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -706,6 +722,15 @@ buildActionMask = 2147483647; files = ( 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 */, 57634A2F1BD9B46D00E19CD7 /* SocketBasicPacketTest.swift in Sources */, 57634A321BD9B46D00E19CD7 /* SocketNamespacePacketTest.swift in Sources */, diff --git a/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO-tvOS.xcscheme b/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO-tvOS.xcscheme index bbc6c17..634fa38 100644 --- a/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO-tvOS.xcscheme +++ b/Socket.IO-Client-Swift.xcodeproj/xcshareddata/xcschemes/SocketIO-tvOS.xcscheme @@ -15,7 +15,7 @@ @@ -57,7 +57,7 @@ @@ -79,7 +79,7 @@ @@ -97,7 +97,7 @@ diff --git a/SocketIO-MacTests/SocketObjectiveCTest.m b/SocketIO-MacTests/SocketObjectiveCTest.m index f3f70e3..b2f7832 100644 --- a/SocketIO-MacTests/SocketObjectiveCTest.m +++ b/SocketIO-MacTests/SocketObjectiveCTest.m @@ -44,4 +44,10 @@ [self.socket off:@"test"]; } +- (void)testSocketManager { + SocketClientManager* manager = [SocketClientManager sharedManager]; + [manager addSocket:self.socket labeledAs:@"test"]; + [manager removeSocketWithLabel:@"test"]; +} + @end diff --git a/SocketIO-MacTests/SocketParserTest.swift b/SocketIO-MacTests/SocketParserTest.swift index c8bea5c..7307cc0 100644 --- a/SocketIO-MacTests/SocketParserTest.swift +++ b/SocketIO-MacTests/SocketParserTest.swift @@ -13,7 +13,7 @@ class SocketParserTest: XCTestCase { let testSocket = SocketIOClient(socketURL: NSURL()) //Format key: message; namespace-data-binary-id - static let packetTypes: Dictionary = [ + static let packetTypes: [String: (String, [AnyObject], [NSData], Int)] = [ "0": ("/", [], [], -1), "1": ("/", [], [], -1), "25[\"test\"]": ("/", ["test"], [], 5), "2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1), diff --git a/SocketIO-MacTests/SocketSideEffectTest.swift b/SocketIO-MacTests/SocketSideEffectTest.swift index 9c5b503..bae130b 100644 --- a/SocketIO-MacTests/SocketSideEffectTest.swift +++ b/SocketIO-MacTests/SocketSideEffectTest.swift @@ -153,4 +153,16 @@ class SocketSideEffectTest: XCTestCase { socket.parseBinaryData(data2) 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") + + } } diff --git a/SocketIO-iOSTests/Info.plist b/SocketIO-iOSTests/Info.plist deleted file mode 100644 index ba72822..0000000 --- a/SocketIO-iOSTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/Source/SocketClientManager.swift b/Source/SocketClientManager.swift new file mode 100644 index 0000000..3cf1c34 --- /dev/null +++ b/Source/SocketClientManager.swift @@ -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() + } +} diff --git a/Source/SocketEngine.swift b/Source/SocketEngine.swift index ab3ddd6..890c4e1 100644 --- a/Source/SocketEngine.swift +++ b/Source/SocketEngine.swift @@ -76,6 +76,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe private var pongsMissedMax = 0 private var probeWait = ProbeWaitQueue() private var secure = false + private var security: SSLSecurity? private var selfSigned = false private var voipEnabled = false @@ -105,6 +106,8 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe voipEnabled = enable case let .Secure(secure): self.secure = secure + case let .Security(security): + self.security = security case let .SelfSigned(selfSigned): self.selfSigned = selfSigned default: @@ -153,9 +156,8 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe // binary in base64 string let noPrefix = message[message.startIndex.advancedBy(2).. 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 { - let str: String + let typeString = String(type.rawValue) + let binaryCountString: String + let nspString: String + let idString: String - if type == .Event || type == .BinaryEvent { - str = createMessageForEvent() + if type == .BinaryEvent || type == .BinaryAck { + binaryCountString = typeString + String(binary.count) + "-" } 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 diff --git a/Source/SocketParsable.swift b/Source/SocketParsable.swift index b6228e2..577722a 100644 --- a/Source/SocketParsable.swift +++ b/Source/SocketParsable.swift @@ -96,14 +96,14 @@ extension SocketParsable { if type == .Error { parser.advanceIndexBy(-1) - } - - while parser.hasNext && type != .Error { - if let int = Int(parser.read(1)) { - idString += String(int) - } else { - parser.advanceIndexBy(-2) - break + } else { + while parser.hasNext { + if let int = Int(parser.read(1)) { + idString += String(int) + } else { + parser.advanceIndexBy(-2) + break + } } } @@ -162,9 +162,7 @@ extension SocketParsable { } // Should execute event? - guard waitingPackets[waitingPackets.count - 1].addData(data) else { - return - } + guard waitingPackets[waitingPackets.count - 1].addData(data) else { return } let packet = waitingPackets.removeLast() diff --git a/Source/WebSocket/SSLSecurity.swift b/Source/WebSocket/SSLSecurity.swift new file mode 100644 index 0000000..d6c4204 --- /dev/null +++ b/Source/WebSocket/SSLSecurity.swift @@ -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.. [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.. [SecKeyRef] in + var keys = keys + let cert = SecTrustGetCertificateAtIndex(trust, index) + if let key = extractPublicKeyFromCert(cert!, policy: policy!) { + keys.append(key) + } + + return keys + } + + return keys + } + + +} diff --git a/Source/WebSocket.swift b/Source/WebSocket/WebSocket.swift similarity index 80% rename from Source/WebSocket.swift rename to Source/WebSocket/WebSocket.swift index c259dd2..f5ad958 100644 --- a/Source/WebSocket.swift +++ b/Source/WebSocket/WebSocket.swift @@ -23,6 +23,10 @@ import Foundation import CoreFoundation import Security +public let WebsocketDidConnectNotification = "WebsocketDidConnectNotification" +public let WebsocketDidDisconnectNotification = "WebsocketDidDisconnectNotification" +public let WebsocketDisconnectionErrorKeyName = "WebsocketDisconnectionErrorKeyName" + public protocol WebSocketDelegate: class { func websocketDidConnect(socket: WebSocket) func websocketDidDisconnect(socket: WebSocket, error: NSError?) @@ -35,7 +39,7 @@ public protocol WebSocketPongDelegate: class { } public class WebSocket : NSObject, NSStreamDelegate { - + enum OpCode : UInt8 { case ContinueFrame = 0x0 case TextFrame = 0x1 @@ -46,7 +50,7 @@ public class WebSocket : NSObject, NSStreamDelegate { case Pong = 0xA //B-F reserved. } - + public enum CloseCode : UInt16 { case Normal = 1000 case GoingAway = 1001 @@ -59,17 +63,17 @@ public class WebSocket : NSObject, NSStreamDelegate { case PolicyViolated = 1008 case MessageTooBig = 1009 } - + public static let ErrorDomain = "WebSocket" - + enum InternalErrorCode : UInt16 { // 0-999 WebSocket status codes not used case OutputStreamWriteError = 1 } - + //Where the callback is executed. It defaults to the main UI thread queue. public var queue = dispatch_get_main_queue() - + var optionalProtocols : [String]? //Constant Values. let headerWSUpgradeName = "Upgrade" @@ -90,7 +94,7 @@ public class WebSocket : NSObject, NSStreamDelegate { let MaskMask: UInt8 = 0x80 let PayloadLenMask: UInt8 = 0x7F let MaxFrameSize: Int = 32 - + class WSResponse { var isFin = false var code: OpCode = .ContinueFrame @@ -98,7 +102,7 @@ public class WebSocket : NSObject, NSStreamDelegate { var frameCount = 0 var buffer: NSMutableData? } - + public weak var delegate: WebSocketDelegate? public weak var pongDelegate: WebSocketPongDelegate? public var onConnect: ((Void) -> Void)? @@ -130,6 +134,7 @@ public class WebSocket : NSObject, NSStreamDelegate { private var didDisconnect = false private var readyToWrite = false private let mutex = NSLock() + private let notificationCenter = NSNotificationCenter.defaultCenter() private var canDispatch: Bool { mutex.lock() let canWork = readyToWrite @@ -138,7 +143,7 @@ public class WebSocket : NSObject, NSStreamDelegate { } //the shared processing queue used for all websocket private static let sharedWorkQueue = dispatch_queue_create("com.vluxe.starscream.websocket", DISPATCH_QUEUE_SERIAL) - + //used for setting protocols. public init(url: NSURL, protocols: [String]? = nil) { self.url = url @@ -146,7 +151,7 @@ public class WebSocket : NSObject, NSStreamDelegate { writeQueue.maxConcurrentOperationCount = 1 optionalProtocols = protocols } - + ///Connect to the websocket server on a background thread public func connect() { guard !isCreated else { return } @@ -155,14 +160,14 @@ public class WebSocket : NSObject, NSStreamDelegate { createHTTPRequest() 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. - + 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. - + - Parameter forceTimeout: Maximum time to wait for the server to close the socket. */ public func disconnect(forceTimeout forceTimeout: NSTimeInterval? = nil) { @@ -174,17 +179,18 @@ public class WebSocket : NSObject, NSStreamDelegate { fallthrough case .None: writeError(CloseCode.Normal.rawValue) - + default: self.disconnectStream(nil) break } } - + /** 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. + - parameter str: The string to write. - parameter completion: The (optional) completion handler. */ @@ -192,11 +198,12 @@ public class WebSocket : NSObject, NSStreamDelegate { guard isConnected else { return } dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame, writeCompletion: completion) } - + /** 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. + - parameter data: The data to write. - parameter completion: The (optional) completion handler. */ @@ -204,20 +211,20 @@ public class WebSocket : NSObject, NSStreamDelegate { guard isConnected else { return } dequeueWrite(data, code: .BinaryFrame, writeCompletion: completion) } - + //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 public func writePing(data: NSData, completion: (() -> ())? = nil) { guard isConnected else { return } dequeueWrite(data, code: .Ping, writeCompletion: completion) } - + //private method that starts the connection private func createHTTPRequest() { - + let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET", url, kCFHTTPVersion1_1).takeRetainedValue() - + var port = url.port if port == nil { if let scheme = url.scheme where ["wss", "https"].contains(scheme) { @@ -245,12 +252,12 @@ public class WebSocket : NSObject, NSStreamDelegate { initStreamsWithData(serializedRequest, Int(port!)) } } - + //Add a header to the CFHTTPMessage by using the NSString bridges to CFString private func addHeader(urlRequest: CFHTTPMessage, key: NSString, val: NSString) { CFHTTPMessageSetHeaderFieldValue(urlRequest, key, val) } - + //generate a websocket key as needed in rfc private func generateWebSocketKey() -> String { var key = "" @@ -263,12 +270,12 @@ public class WebSocket : NSObject, NSStreamDelegate { let baseKey = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) return baseKey! } - + //Start the stream connection and write the data to the output stream private func initStreamsWithData(data: NSData, _ port: Int) { //higher level API we will cut over to at some point //NSStream.getStreamsToHostWithName(url.host, port: url.port.integerValue, inputStream: &inputStream, outputStream: &outputStream) - + var readStream: Unmanaged? var writeStream: Unmanaged? let h: NSString = url.host! @@ -314,11 +321,11 @@ public class WebSocket : NSObject, NSStreamDelegate { CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue) inStream.open() outStream.open() - + self.mutex.lock() self.readyToWrite = true self.mutex.unlock() - + let bytes = UnsafePointer(data.bytes) var out = timeout * 1000000 //wait 5 seconds before giving up writeQueue.addOperationWithBlock { [weak self] in @@ -338,7 +345,7 @@ public class WebSocket : NSObject, NSStreamDelegate { } //delegate for the stream methods. Processes incoming bytes public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) { - + if let sec = security where !certValidated && [.HasBytesAvailable, .HasSpaceAvailable].contains(eventCode) { let possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as String) if let trust: AnyObject = possibleTrust { @@ -372,7 +379,7 @@ public class WebSocket : NSObject, NSStreamDelegate { cleanupStream() doDisconnect(error) } - + private func cleanupStream() { outputStream?.delegate = nil inputStream?.delegate = nil @@ -387,13 +394,13 @@ public class WebSocket : NSObject, NSStreamDelegate { outputStream = nil inputStream = nil } - + ///handles the incoming bytes and sending them to the proper processing method private func processInputStream() { let buf = NSMutableData(capacity: BUFFER_MAX) let buffer = UnsafeMutablePointer(buf!.bytes) let length = inputStream!.read(buffer, maxLength: BUFFER_MAX) - + guard length > 0 else { return } var process = false if inputQueue.count == 0 { @@ -425,7 +432,7 @@ public class WebSocket : NSObject, NSStreamDelegate { inputQueue = inputQueue.filter{$0 != data} } } - + //handle checking the inital connection status private func processTCPHandshake(buffer: UnsafePointer, bufferLen: Int) { let code = processHTTP(buffer, bufferLen: bufferLen) @@ -437,6 +444,7 @@ public class WebSocket : NSObject, NSStreamDelegate { guard let s = self else { return } s.onConnect?() s.delegate?.websocketDidConnect(s) + s.notificationCenter.postNotificationName(WebsocketDidConnectNotification, object: self) } case -1: 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 } - + ///validates the HTTP is a 101 as per the RFC spec private func validateResponse(buffer: UnsafePointer, bufferLen: Int) -> Int { let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue() @@ -494,12 +502,12 @@ public class WebSocket : NSObject, NSStreamDelegate { } return -1 } - + ///read a 16 bit big endian value from a buffer private static func readUint16(buffer: UnsafePointer, offset: Int) -> UInt16 { return (UInt16(buffer[offset + 0]) << 8) | UInt16(buffer[offset + 1]) } - + ///read a 64 bit big endian value from a buffer private static func readUint64(buffer: UnsafePointer, offset: Int) -> UInt64 { var value = UInt64(0) @@ -508,20 +516,20 @@ public class WebSocket : NSObject, NSStreamDelegate { } return value } - + ///write a 16 bit big endian value to a buffer private static func writeUint16(buffer: UnsafeMutablePointer, offset: Int, value: UInt16) { buffer[offset + 0] = UInt8(value >> 8) buffer[offset + 1] = UInt8(value & 0xff) } - + ///write a 64 bit big endian value to a buffer private static func writeUint64(buffer: UnsafeMutablePointer, offset: Int, value: UInt64) { for i in 0...7 { 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. @warn_unused_result private func processOneRawMessage(inBuffer buffer: UnsafeBufferPointer) -> UnsafeBufferPointer { @@ -676,12 +684,12 @@ public class WebSocket : NSObject, NSStreamDelegate { } processResponse(response) } - + let step = Int(offset+numericCast(len)) return buffer.fromOffset(step) } } - + /// Process all messages in the buffer if possible. private func processRawMessagesInBuffer(pointer: UnsafePointer, bufferLen: Int) { var buffer = UnsafeBufferPointer(start: pointer, count: bufferLen) @@ -692,7 +700,7 @@ public class WebSocket : NSObject, NSStreamDelegate { fragBuffer = NSData(buffer: buffer) } } - + ///process the finished response of a buffer private func processResponse(response: WSResponse) -> Bool { if response.isFin && response.bytesLeft <= 0 { @@ -727,14 +735,14 @@ public class WebSocket : NSObject, NSStreamDelegate { } return false } - + ///Create an error private func errorWithDetail(detail: String, code: UInt16) -> NSError { var details = [String: String]() details[NSLocalizedDescriptionKey] = detail return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details) } - + ///write a an error to the socket private func writeError(code: UInt16) { let buf = NSMutableData(capacity: sizeof(UInt16)) @@ -768,7 +776,7 @@ public class WebSocket : NSObject, NSStreamDelegate { let maskKey = UnsafeMutablePointer(buffer + offset) SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey) offset += sizeof(UInt32) - + for i in 0..) { self.init(bytes: buffer.baseAddress, length: buffer.count) } - + } private extension UnsafeBufferPointer { - + func fromOffset(offset: Int) -> UnsafeBufferPointer { return UnsafeBufferPointer(start: baseAddress.advancedBy(offset), count: count - offset) } - + } private let emptyBuffer = UnsafeBufferPointer(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.. [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.. [SecKeyRef] in - var keys = keys - let cert = SecTrustGetCertificateAtIndex(trust, index) - if let key = extractPublicKeyFromCert(cert!, policy: policy) { - keys.append(key) - } - - return keys - } - - return keys - } - - -}