diff --git a/README.md b/README.md index 056b0f5..94d0a3c 100644 --- a/README.md +++ b/README.md @@ -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.xcodeproj/project.pbxproj b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj index c373933..7d543f1 100644 --- a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj +++ b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj @@ -101,11 +101,6 @@ 74171EC51C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; }; 74171EC71C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; }; 74171EC81C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; }; - 74171ECF1C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; }; - 74171ED01C10CD240062D398 /* 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 */; }; - 74171ED41C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.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 */; }; @@ -121,6 +116,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 */; }; @@ -160,7 +164,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 = ""; }; @@ -185,7 +188,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 = ""; }; @@ -194,6 +196,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 = ""; }; @@ -253,7 +258,6 @@ children = ( 5764DF7B1B51F24A004FF46E /* Source */, 572EF21B1B51F16C00EEBB58 /* SocketIO-iOS */, - 572EF2281B51F16C00EEBB58 /* SocketIO-iOSTests */, 572EF2391B51F18A00EEBB58 /* SocketIO-Mac */, 572EF2461B51F18A00EEBB58 /* SocketIO-MacTests */, 572EF21A1B51F16C00EEBB58 /* Products */, @@ -290,22 +294,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 = ( @@ -349,10 +337,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 */, @@ -369,11 +357,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 = ( @@ -619,6 +617,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 */, @@ -626,18 +625,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; }; @@ -648,7 +648,6 @@ 7472C65F1BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */, 74171EA61C10CD240062D398 /* SocketIOClientStatus.swift in Sources */, 74171E881C10CD240062D398 /* SocketEngineSpec.swift in Sources */, - 74171ED01C10CD240062D398 /* WebSocket.swift in Sources */, 74171EA01C10CD240062D398 /* SocketIOClientOption.swift in Sources */, 74171E701C10CD240062D398 /* SocketAnyEvent.swift in Sources */, 74171EC41C10CD240062D398 /* SocketTypes.swift in Sources */, @@ -673,6 +672,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 */, @@ -689,12 +689,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; }; @@ -717,6 +718,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 */, @@ -733,12 +735,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; }; @@ -749,7 +752,6 @@ 57634A231BD9B46D00E19CD7 /* SocketSideEffectTest.swift in Sources */, 74171EAA1C10CD240062D398 /* SocketIOClientStatus.swift in Sources */, 74171E8C1C10CD240062D398 /* SocketEngineSpec.swift in Sources */, - 74171ED41C10CD240062D398 /* WebSocket.swift in Sources */, 74171EA41C10CD240062D398 /* SocketIOClientOption.swift in Sources */, 74171E741C10CD240062D398 /* SocketAnyEvent.swift in Sources */, 74171EC81C10CD240062D398 /* SocketTypes.swift in Sources */, 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 e01e3b9..80443ec 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..968e4eb --- /dev/null +++ b/Source/WebSocket/SSLSecurity.swift @@ -0,0 +1,260 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// 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 = 0 + SecTrustEvaluate(trust,&result) + let r = Int(result) + if r == kSecTrustResultUnspecified || r == kSecTrustResultProceed { + 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 = 0 + 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 + } + + +} \ No newline at end of file diff --git a/Source/WebSocket.swift b/Source/WebSocket/WebSocket.swift similarity index 81% rename from Source/WebSocket.swift rename to Source/WebSocket/WebSocket.swift index 8db427b..1204a78 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?) @@ -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 @@ -185,6 +190,7 @@ public class WebSocket : NSObject, NSStreamDelegate { 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. */ @@ -197,6 +203,7 @@ public class WebSocket : NSObject, NSStreamDelegate { 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. */ @@ -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) @@ -815,6 +823,8 @@ public class WebSocket : NSObject, NSStreamDelegate { guard let s = self else { return } s.onDisconnect?(error) s.delegate?.websocketDidDisconnect(s, error: error) + let userInfo = error.map({ [WebsocketDisconnectionErrorKeyName: $0] }) + s.notificationCenter.postNotificationName(WebsocketDidDisconnectNotification, object: self, userInfo: userInfo) } } @@ -844,238 +854,3 @@ private extension UnsafeBufferPointer { } 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 { - 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 = 0 - SecTrustEvaluate(trust,&result) - let r = Int(result) - if r == kSecTrustResultUnspecified || r == kSecTrustResultProceed { - 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 = 0 - 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 - } - - -} \ No newline at end of file