From e57bcba47d1595b854d9ad1c7ab847c79fbca34d Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 6 Jun 2016 15:41:19 -0400 Subject: [PATCH 1/5] encoding should already be done at this point --- Source/SocketEngine.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/SocketEngine.swift b/Source/SocketEngine.swift index f22f73f..17f2b5c 100644 --- a/Source/SocketEngine.swift +++ b/Source/SocketEngine.swift @@ -279,8 +279,8 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType) - if closed { - client?.engineDidClose(reason) + if closed || !connected { + postSendClose(nil, nil, nil) return } @@ -334,7 +334,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe guard let ws = self.ws else { return } for msg in postWait { - ws.writeString(fixDoubleUTF8(msg)) + ws.writeString(msg) } postWait.removeAll(keepCapacity: true) From 02f30a81ddbbfbfb62c92982e890bf129254eb05 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 6 Jun 2016 16:25:37 -0400 Subject: [PATCH 2/5] fix test --- SocketIO-MacTests/SocketParserTest.swift | 2 +- Source/SocketEngine.swift | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) 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/Source/SocketEngine.swift b/Source/SocketEngine.swift index 17f2b5c..92843e5 100644 --- a/Source/SocketEngine.swift +++ b/Source/SocketEngine.swift @@ -274,13 +274,15 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe ws?.disconnect() stopPolling() - client?.engineDidClose(reason) } + guard connected else { return postSendClose(nil, nil, nil) } + DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType) - if closed || !connected { + if closed { postSendClose(nil, nil, nil) + client?.engineDidClose(reason) return } From b919d3e41113c2bb27158c50b1789ea001d29059 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 7 Jun 2016 13:12:34 -0400 Subject: [PATCH 3/5] hopefully fix socket.io/socket.io-client-swift#374 --- Source/SocketEngine.swift | 45 +++++++++++++++++-------------- Source/SocketEnginePollable.swift | 2 +- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Source/SocketEngine.swift b/Source/SocketEngine.swift index 92843e5..5572bf8 100644 --- a/Source/SocketEngine.swift +++ b/Source/SocketEngine.swift @@ -163,6 +163,16 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe } } + private func closeOutSocket() { + sid = "" + closed = true + invalidated = true + connected = false + + ws?.disconnect() + stopPolling() + } + /// Starts the connection to the server public func connect() { if connected { @@ -266,37 +276,32 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe } public func disconnect(reason: String) { - func postSendClose(data: NSData?, _ res: NSURLResponse?, _ err: NSError?) { - sid = "" - closed = true - invalidated = true - connected = false - - ws?.disconnect() - stopPolling() - } - - guard connected else { return postSendClose(nil, nil, nil) } + guard connected else { return closeOutSocket() } DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType) if closed { - postSendClose(nil, nil, nil) + closeOutSocket() client?.engineDidClose(reason) return } if websocket { sendWebSocketMessage("", withType: .Close, withData: []) - postSendClose(nil, nil, nil) + closeOutSocket() } else { - // We need to take special care when we're polling that we send it ASAP - // Also make sure we're on the emitQueue since we're touching postWait - dispatch_sync(emitQueue) { - self.postWait.append(String(SocketEnginePacketType.Close.rawValue)) - let req = self.createRequestForPostWithPostWait() - self.doRequest(req, withCallback: postSendClose) - } + disconnectPolling() + } + } + + // We need to take special care when we're polling that we send it ASAP + // Also make sure we're on the emitQueue since we're touching postWait + private func disconnectPolling() { + dispatch_sync(emitQueue) { + self.postWait.append(String(SocketEnginePacketType.Close.rawValue)) + let req = self.createRequestForPostWithPostWait() + self.doRequest(req) {_, _, _ in } + self.closeOutSocket() } } diff --git a/Source/SocketEnginePollable.swift b/Source/SocketEnginePollable.swift index 3d400cf..e8d1718 100644 --- a/Source/SocketEnginePollable.swift +++ b/Source/SocketEnginePollable.swift @@ -123,7 +123,7 @@ extension SocketEnginePollable { return } - + DefaultSocketLogger.Logger.log("Got polling response", type: "SocketEnginePolling") if let str = String(data: data!, encoding: NSUTF8StringEncoding) { From cb0a6f54607af10ffe56934ffc67e5568a8a68ae Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 7 Jun 2016 13:20:23 -0400 Subject: [PATCH 4/5] rename method --- Source/SocketEngine.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/SocketEngine.swift b/Source/SocketEngine.swift index 5572bf8..ac1a864 100644 --- a/Source/SocketEngine.swift +++ b/Source/SocketEngine.swift @@ -163,7 +163,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe } } - private func closeOutSocket() { + private func closeOutEngine() { sid = "" closed = true invalidated = true @@ -276,19 +276,19 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe } public func disconnect(reason: String) { - guard connected else { return closeOutSocket() } + guard connected else { return closeOutEngine() } DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType) if closed { - closeOutSocket() + closeOutEngine() client?.engineDidClose(reason) return } if websocket { sendWebSocketMessage("", withType: .Close, withData: []) - closeOutSocket() + closeOutEngine() } else { disconnectPolling() } @@ -301,7 +301,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe self.postWait.append(String(SocketEnginePacketType.Close.rawValue)) let req = self.createRequestForPostWithPostWait() self.doRequest(req) {_, _, _ in } - self.closeOutSocket() + self.closeOutEngine() } } From 58a5a1746478f35242b60dc8a43ed5c2628ca9cb Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 9 Jun 2016 09:28:40 -0400 Subject: [PATCH 5/5] Move websocket to subfolder, don't combine websocket files --- .../project.pbxproj | 38 ++- Source/WebSocket/SSLSecurity.swift | 258 ++++++++++++++++++ Source/{ => WebSocket}/WebSocket.swift | 245 +---------------- 3 files changed, 293 insertions(+), 248 deletions(-) create mode 100644 Source/WebSocket/SSLSecurity.swift rename Source/{ => WebSocket}/WebSocket.swift (81%) diff --git a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj index 9f5a682..3ff316f 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,12 @@ 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 */; }; 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 */; }; @@ -184,7 +185,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 = ""; }; @@ -193,6 +193,8 @@ 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 = ""; }; 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 = ""; }; @@ -331,7 +333,6 @@ 5764DF7B1B51F24A004FF46E /* Source */ = { isa = PBXGroup; children = ( - CEBA569E1CDA0C0C00BA0389 /* utils */, 74171E501C10CD240062D398 /* SocketAckEmitter.swift */, 74171E511C10CD240062D398 /* SocketAckManager.swift */, 74171E521C10CD240062D398 /* SocketAnyEvent.swift */, @@ -351,11 +352,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 = ( @@ -601,6 +612,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 */, @@ -608,13 +620,13 @@ 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 */, @@ -630,7 +642,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 */, @@ -655,6 +666,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 */, @@ -671,7 +683,7 @@ 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 */, @@ -699,6 +711,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 */, @@ -715,7 +728,7 @@ 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 */, @@ -731,7 +744,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/Source/WebSocket/SSLSecurity.swift b/Source/WebSocket/SSLSecurity.swift new file mode 100644 index 0000000..b5d03ef --- /dev/null +++ b/Source/WebSocket/SSLSecurity.swift @@ -0,0 +1,258 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// 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 { + 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 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