diff --git a/SocketIO-MacTests/SocketBasicPacketTest.swift b/SocketIO-MacTests/SocketBasicPacketTest.swift index 91fe75e..25c5670 100644 --- a/SocketIO-MacTests/SocketBasicPacketTest.swift +++ b/SocketIO-MacTests/SocketBasicPacketTest.swift @@ -62,7 +62,7 @@ class SocketBasicPacketTest: XCTestCase { } func testBinaryEmit() { - let expectedSendString = "51-[\"test\",{\"num\":0,\"_placeholder\":true}]" + let expectedSendString = "51-[\"test\",{\"_placeholder\":true,\"num\":0}]" let sendData = ["test", data] let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/", ack: false) @@ -71,7 +71,7 @@ class SocketBasicPacketTest: XCTestCase { } func testMultipleBinaryEmit() { - let expectedSendString = "52-[\"test\",{\"data1\":{\"num\":0,\"_placeholder\":true},\"data2\":{\"num\":1,\"_placeholder\":true}}]" + let expectedSendString = "52-[\"test\",{\"data1\":{\"_placeholder\":true,\"num\":0},\"data2\":{\"_placeholder\":true,\"num\":1}}]" let sendData = ["test", ["data1": data, "data2": data2]] let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/", ack: false) @@ -88,7 +88,7 @@ class SocketBasicPacketTest: XCTestCase { } func testEmitDataWithAck() { - let expectedSendString = "51-0[\"test\",{\"num\":0,\"_placeholder\":true}]" + let expectedSendString = "51-0[\"test\",{\"_placeholder\":true,\"num\":0}]" let sendData = ["test", data] let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/", ack: false) @@ -129,7 +129,7 @@ class SocketBasicPacketTest: XCTestCase { } func testBinaryAck() { - let expectedSendString = "61-0[{\"num\":0,\"_placeholder\":true}]" + let expectedSendString = "61-0[{\"_placeholder\":true,\"num\":0}]" let sendData = [data] let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/", ack: true) @@ -138,7 +138,7 @@ class SocketBasicPacketTest: XCTestCase { } func testMultipleBinaryAck() { - let expectedSendString = "62-0[{\"data2\":{\"num\":0,\"_placeholder\":true},\"data1\":{\"num\":1,\"_placeholder\":true}}]" + let expectedSendString = "62-0[{\"data2\":{\"_placeholder\":true,\"num\":0},\"data1\":{\"_placeholder\":true,\"num\":1}}]" let sendData = [["data1": data, "data2": data2]] let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/", ack: true) @@ -147,7 +147,7 @@ class SocketBasicPacketTest: XCTestCase { } func testBinaryStringPlaceholderInMessage() { - let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"num\":1,\"_placeholder\":true}]" + let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"_placeholder\":true,\"num\":1}]" let socket = SocketIOClient(socketURL: NSURL()) socket.setTestable() diff --git a/SocketIO-MacTests/SocketNamespacePacketTest.swift b/SocketIO-MacTests/SocketNamespacePacketTest.swift index 4f5315b..29dbeac 100644 --- a/SocketIO-MacTests/SocketNamespacePacketTest.swift +++ b/SocketIO-MacTests/SocketNamespacePacketTest.swift @@ -54,7 +54,7 @@ class SocketNamespacePacketTest: XCTestCase { } func testBinaryEmit() { - let expectedSendString = "51-/swift,[\"test\",{\"num\":0,\"_placeholder\":true}]" + let expectedSendString = "51-/swift,[\"test\",{\"_placeholder\":true,\"num\":0}]" let sendData = ["test", data] let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/swift", ack: false) @@ -63,7 +63,7 @@ class SocketNamespacePacketTest: XCTestCase { } func testMultipleBinaryEmit() { - let expectedSendString = "52-/swift,[\"test\",{\"data1\":{\"num\":0,\"_placeholder\":true},\"data2\":{\"num\":1,\"_placeholder\":true}}]" + let expectedSendString = "52-/swift,[\"test\",{\"data1\":{\"_placeholder\":true,\"num\":0},\"data2\":{\"_placeholder\":true,\"num\":1}}]" let sendData = ["test", ["data1": data, "data2": data2]] let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/swift", ack: false) @@ -80,7 +80,7 @@ class SocketNamespacePacketTest: XCTestCase { } func testEmitDataWithAck() { - let expectedSendString = "51-/swift,0[\"test\",{\"num\":0,\"_placeholder\":true}]" + let expectedSendString = "51-/swift,0[\"test\",{\"_placeholder\":true,\"num\":0}]" let sendData = ["test", data] let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/swift", ack: false) @@ -121,7 +121,7 @@ class SocketNamespacePacketTest: XCTestCase { } func testBinaryAck() { - let expectedSendString = "61-/swift,0[{\"num\":0,\"_placeholder\":true}]" + let expectedSendString = "61-/swift,0[{\"_placeholder\":true,\"num\":0}]" let sendData = [data] let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/swift", ack: true) @@ -130,7 +130,7 @@ class SocketNamespacePacketTest: XCTestCase { } func testMultipleBinaryAck() { - let expectedSendString = "62-/swift,0[{\"data2\":{\"num\":0,\"_placeholder\":true},\"data1\":{\"num\":1,\"_placeholder\":true}}]" + let expectedSendString = "62-/swift,0[{\"data2\":{\"_placeholder\":true,\"num\":0},\"data1\":{\"_placeholder\":true,\"num\":1}}]" let sendData = [["data1": data, "data2": data2]] let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/swift", ack: true) diff --git a/SocketIO-MacTests/SocketParserTest.swift b/SocketIO-MacTests/SocketParserTest.swift index 43e66d1..5526f95 100644 --- a/SocketIO-MacTests/SocketParserTest.swift +++ b/SocketIO-MacTests/SocketParserTest.swift @@ -120,7 +120,7 @@ class SocketParserTest: XCTestCase { func validateParseResult(message: String) { let validValues = SocketParserTest.packetTypes[message]! let packet = testSocket.parseString(message) - let type = message.substring(with: Range(message.startIndex..(message.startIndex.. Bool { if message.hasPrefix("b4") { // binary in base64 string - let noPrefix = message[message.startIndex.advanced(by: 2).. Void)? private var currentReconnectAttempt = 0 @@ -60,7 +60,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable private var reconnecting = false private(set) var currentAck = -1 - private(set) var handleQueue = dispatch_get_main_queue() + private(set) var handleQueue = dispatch_get_main_queue()! private(set) var reconnectAttempts = -1 var waitingPackets = [SocketPacket]() @@ -114,7 +114,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable } private func addEngine() -> SocketEngineSpec { - DefaultSocketLogger.Logger.log("Adding engine", type: logType) + DefaultSocketLogger.Logger.log("Adding engine", type: logType, args: "") engine = SocketEngine(client: self, url: socketURL, options: options) @@ -191,11 +191,10 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable DefaultSocketLogger.Logger.log("Disconnected: %@", type: logType, args: reason) status = .disconnected - reconnects = false // Make sure the engine is actually dead. engine?.disconnect(reason: reason) - handleEvent("disconnect", data: [reason], isInternalMessage: true) + handleEvent("disconnect", data: [reason as AnyObject], isInternalMessage: true) } /// Disconnects the socket. Only reconnect the same socket if you know what you're doing. @@ -205,7 +204,6 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable DefaultSocketLogger.Logger.log("Closing socket", type: logType) - reconnects = false didDisconnect(reason: "Disconnect") } @@ -217,11 +215,11 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable /// Same as emit, but meant for Objective-C public func emit(_ event: String, with items: [AnyObject]) { guard status == .connected else { - handleEvent("error", data: ["Tried emitting \(event) when not connected"], isInternalMessage: true) + handleEvent("error", data: ["Tried emitting \(event) when not connected" as AnyObject], isInternalMessage: true) return } - _emit(data: [event] + items) + _emit(data: [event as AnyObject] + items) } /// Sends a message to the server, requesting an ack. Use the onAck method of SocketAckHandler to add @@ -232,7 +230,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable /// Same as emitWithAck, but for Objective-C public func emitWithAck(_ event: String, with items: [AnyObject]) -> OnAckCallback { - return createOnAck(items: [event] + items) + return createOnAck(items: [event as AnyObject] + items) } private func _emit(data: [AnyObject], ack: Int? = nil) { @@ -284,23 +282,25 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable public func engineDidError(reason: String) { DefaultSocketLogger.Logger.error("%@", type: logType, args: reason) - handleEvent("error", data: [reason], isInternalMessage: true) + handleEvent("error", data: [reason as AnyObject], isInternalMessage: true) + } + + public func engineDidOpen(reason: String) { + DefaultSocketLogger.Logger.log(reason, type: "SocketEngineClient") } // Called when the socket gets an ack for something it sent func handleAck(_ ack: Int, data: [AnyObject]) { guard status == .connected else { return } - DefaultSocketLogger.Logger.log("Handling ack: %@ with data: %@", type: logType, args: ack, data ?? "") + DefaultSocketLogger.Logger.log("Handling ack: %@ with data: %@", type: logType, args: ack, data) ackHandlers.executeAck(ack, items: data) } /// Causes an event to be handled. Only use if you know what you're doing. public func handleEvent(_ event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int = -1) { - guard status == .connected || isInternalMessage else { - return - } + guard status == .connected || isInternalMessage else { return } DefaultSocketLogger.Logger.log("Handling event: %@ with data: %@", type: logType, args: event, data ?? "") @@ -335,14 +335,14 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable public func off(event: String) { DefaultSocketLogger.Logger.log("Removing handler for event: %@", type: logType, args: event) - handlers = handlers.filter { $0.event != event } + handlers = handlers.filter({ $0.event != event }) } /// Removes a handler with the specified UUID gotten from an `on` or `once` public func off(id: NSUUID) { DefaultSocketLogger.Logger.log("Removing handler with id: %@", type: logType, args: id) - handlers = handlers.filter { $0.id != id } + handlers = handlers.filter({ $0.id != id }) } /// Adds a handler for an event. @@ -409,7 +409,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable private func tryReconnect(reason: String) { if reconnecting { DefaultSocketLogger.Logger.log("Starting reconnect", type: logType) - handleEvent("reconnect", data: [reason], isInternalMessage: true) + handleEvent("reconnect", data: [reason as AnyObject], isInternalMessage: true) _tryReconnect() } @@ -425,7 +425,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable } DefaultSocketLogger.Logger.log("Trying to reconnect", type: logType) - handleEvent("reconnectAttempt", data: [reconnectAttempts - currentReconnectAttempt], + handleEvent("reconnectAttempt", data: [(reconnectAttempts - currentReconnectAttempt) as AnyObject], isInternalMessage: true) currentReconnectAttempt += 1 @@ -452,6 +452,6 @@ extension SocketIOClient { } func emitTest(event: String, _ data: AnyObject...) { - self._emit(data: [event] + data) + self._emit(data: [event as AnyObject] + data) } } diff --git a/Source/SocketIOClientOption.swift b/Source/SocketIOClientOption.swift index 6142e88..17eab48 100644 --- a/Source/SocketIOClientOption.swift +++ b/Source/SocketIOClientOption.swift @@ -105,43 +105,43 @@ public enum SocketIOClientOption : ClientOption { switch self { case let .connectParams(params): - value = params + value = params as AnyObject case let .cookies(cookies): - value = cookies + value = cookies as AnyObject case let .doubleEncodeUTF8(encode): - value = encode + value = encode as AnyObject case let .extraHeaders(headers): - value = headers + value = headers as AnyObject case let .forceNew(force): - value = force + value = force as AnyObject case let .forcePolling(force): - value = force + value = force as AnyObject case let .forceWebsockets(force): - value = force + value = force as AnyObject case let .handleQueue(queue): - value = queue + value = queue as AnyObject case let .log(log): - value = log + value = log as AnyObject case let .logger(logger): - value = logger + value = logger as AnyObject case let .nsp(nsp): - value = nsp + value = nsp as AnyObject case let .path(path): - value = path + value = path as AnyObject case let .reconnects(reconnects): - value = reconnects + value = reconnects as AnyObject case let .reconnectAttempts(attempts): - value = attempts + value = attempts as AnyObject case let .reconnectWait(wait): - value = wait + value = wait as AnyObject case let .secure(secure): - value = secure + value = secure as AnyObject case let .selfSigned(signed): - value = signed + value = signed as AnyObject case let .sessionDelegate(delegate): - value = delegate + value = delegate as AnyObject case let .voipEnabled(enabled): - value = enabled + value = enabled as AnyObject } return value diff --git a/Source/SocketIOClientSpec.swift b/Source/SocketIOClientSpec.swift index f3dd863..1e2051c 100644 --- a/Source/SocketIOClientSpec.swift +++ b/Source/SocketIOClientSpec.swift @@ -38,6 +38,6 @@ extension SocketIOClientSpec { func didError(reason: String) { DefaultSocketLogger.Logger.error("%@", type: "SocketIOClient", args: reason) - handleEvent("error", data: [reason], isInternalMessage: true, withAck: -1) + handleEvent("error", data: [reason as AnyObject], isInternalMessage: true, withAck: -1) } } diff --git a/Source/SocketLogger.swift b/Source/SocketLogger.swift index bc4cf37..d40d121 100644 --- a/Source/SocketLogger.swift +++ b/Source/SocketLogger.swift @@ -29,28 +29,25 @@ public protocol SocketLogger : class { var log: Bool { get set } /// Normal log messages - func log(_ message: String, type: String, args: AnyObject...) + func log(_ message: String, type: String, args: Any...) /// Error Messages - func error(_ message: String, type: String, args: AnyObject...) + func error(_ message: String, type: String, args: Any...) } public extension SocketLogger { - func log(_ message: String, type: String, args: AnyObject...) { + func log(_ message: String, type: String, args: Any...) { abstractLog("LOG", message: message, type: type, args: args) } - func error(_ message: String, type: String, args: AnyObject...) { + func error(_ message: String, type: String, args: Any...) { abstractLog("ERROR", message: message, type: type, args: args) } - private func abstractLog(_ logType: String, message: String, type: String, args: [AnyObject]) { + private func abstractLog(_ logType: String, message: String, type: String, args: [Any]) { guard log else { return } - let newArgs = args.map({arg -> CVarArg in String(arg)}) - let replaced = String(format: message, arguments: newArgs) - - NSLog("%@ %@: %@", logType, type, replaced) + NSLog("\(logType) \(type): \(args)") } } diff --git a/Source/SocketPacket.swift b/Source/SocketPacket.swift index 23468f6..98c9a96 100644 --- a/Source/SocketPacket.swift +++ b/Source/SocketPacket.swift @@ -61,7 +61,7 @@ struct SocketPacket { return createPacketString() } - init(type: SocketPacket.PacketType, data: [AnyObject] = [AnyObject](), id: Int = -1, + init(type: PacketType, data: [AnyObject] = [AnyObject](), id: Int = -1, nsp: String, placeholders: Int = 0, binary: [NSData] = [NSData]()) { self.data = data self.id = id @@ -94,10 +94,10 @@ struct SocketPacket { } do { - let jsonSend = try NSJSONSerialization.data(withJSONObject: data, + let jsonSend = try NSJSONSerialization.data(withJSONObject: data as AnyObject, options: NSJSONWritingOptions(rawValue: 0)) guard let jsonString = String(data: jsonSend, encoding: NSUTF8StringEncoding) else { - return "[]" + return message + "[]" } restOfMessage = jsonString @@ -189,7 +189,7 @@ struct SocketPacket { // Helper method that looks for placeholders // If object is a collection it will recurse - // Returns the object if it is not a placeholder string or the corresponding + // Returns the object if it is not a placeholder or the corresponding // binary data private func _fillInPlaceholders(_ object: AnyObject) -> AnyObject { switch object { @@ -203,7 +203,7 @@ struct SocketPacket { }) } case let arr as [AnyObject]: - return arr.map(_fillInPlaceholders) + return arr.map(_fillInPlaceholders) as AnyObject default: return object } @@ -211,7 +211,7 @@ struct SocketPacket { } extension SocketPacket { - private static func findType(binCount: Int, ack: Bool) -> PacketType { + private static func findType(_ binCount: Int, ack: Bool) -> PacketType { switch binCount { case 0 where !ack: return .Event @@ -228,8 +228,8 @@ extension SocketPacket { static func packetFromEmit(items: [AnyObject], id: Int, nsp: String, ack: Bool) -> SocketPacket { let (parsedData, binary) = deconstructData(items) - let packet = SocketPacket(type: findType(binCount: binary.count, ack: ack), data: parsedData, - id: id, nsp: nsp, placeholders: -1, binary: binary) + let packet = SocketPacket(type: findType(binary.count, ack: ack), data: parsedData, + id: id, nsp: nsp, binary: binary) return packet } @@ -238,14 +238,14 @@ extension SocketPacket { private extension SocketPacket { // Recursive function that looks for NSData in collections static func shred(_ data: AnyObject, binary: inout [NSData]) -> AnyObject { - let placeholder = ["_placeholder": true, "num": binary.count] + let placeholder = ["_placeholder": true, "num": binary.count as AnyObject] switch data { case let bin as NSData: binary.append(bin) - return placeholder + return placeholder as AnyObject case let arr as [AnyObject]: - return arr.map({shred($0, binary: &binary)}) + return arr.map({shred($0, binary: &binary)}) as AnyObject case let dict as NSDictionary: return dict.reduce(NSMutableDictionary(), combine: {cur, keyValue in cur[keyValue.0 as! NSCopying] = shred(keyValue.1, binary: &binary) diff --git a/Source/SocketParsable.swift b/Source/SocketParsable.swift index d4cf386..e4d0c40 100644 --- a/Source/SocketParsable.swift +++ b/Source/SocketParsable.swift @@ -35,8 +35,6 @@ extension SocketParsable { private func handleConnect(_ p: SocketPacket) { if p.nsp == "/" && nsp != "/" { joinNamespace(nsp) - } else if p.nsp != "/" && nsp == "/" { - didConnect() } else { didConnect() } @@ -91,8 +89,7 @@ extension SocketParsable { } if !parser.hasNext { - return .Right(SocketPacket(type: type, id: -1, - nsp: namespace ?? "/", placeholders: placeholders)) + return .Right(SocketPacket(type: type, nsp: namespace, placeholders: placeholders)) } var idString = "" @@ -110,7 +107,7 @@ extension SocketParsable { } } - let d = message[parser.currentIndex.advanced(by: 1).. String.Index { + currentIndex = message.characters.index(currentIndex, offsetBy: by) + + return currentIndex } mutating func read(length: Int) -> String { - let readString = message[currentIndex.. String { - return read(length: currentIndex.distance(to: message.endIndex)) + return read(length: message.characters.distance(from: currentIndex, to: message.endIndex)) } } diff --git a/Source/SocketTypes.swift b/Source/SocketTypes.swift index b8840be..7f27c42 100644 --- a/Source/SocketTypes.swift +++ b/Source/SocketTypes.swift @@ -28,6 +28,9 @@ public typealias AckCallback = ([AnyObject]) -> Void public typealias NormalCallback = ([AnyObject], SocketAckEmitter) -> Void public typealias OnAckCallback = (timeoutAfter: UInt64, callback: AckCallback) -> Void +typealias Probe = (msg: String, type: SocketEnginePacketType, data: [NSData]) +typealias ProbeWaitQueue = [Probe] + enum Either { case Left(E) case Right(V) diff --git a/Source/WebSocket.swift b/Source/WebSocket.swift index c91ec04..b676fce 100644 --- a/Source/WebSocket.swift +++ b/Source/WebSocket.swift @@ -112,12 +112,10 @@ public class WebSocket : NSObject, NSStreamDelegate { public var headers = [String: String]() public var voipEnabled = false public var selfSignedSSL = false - #if swift(>=3) - #else public var security: SSLSecurity? - #endif public var enabledSSLCipherSuites: [SSLCipherSuite]? public var origin: String? + public var timeout = 5 public var isConnected :Bool { return connected } @@ -173,7 +171,7 @@ public class WebSocket : NSObject, NSStreamDelegate { public func disconnect(forceTimeout: NSTimeInterval? = nil) { switch forceTimeout { case .some(let seconds) where seconds > 0: - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))), queue) { [weak self] in + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))), queue!) { [weak self] in self?.disconnectStream(error: nil) } fallthrough @@ -231,19 +229,19 @@ public class WebSocket : NSObject, NSStreamDelegate { port = 80 } } - addHeader(urlRequest: urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue) - addHeader(urlRequest: urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue) + addHeader(urlRequest: urlRequest, key: headerWSUpgradeName as NSString, val: headerWSUpgradeValue as NSString) + addHeader(urlRequest: urlRequest, key: headerWSConnectionName as NSString, val: headerWSConnectionValue as NSString) if let protocols = optionalProtocols { - addHeader(urlRequest: urlRequest, key: headerWSProtocolName, val: protocols.joined(separator: ",")) + addHeader(urlRequest: urlRequest, key: headerWSProtocolName as NSString, val: protocols.joined(separator: ",") as NSString) } - addHeader(urlRequest: urlRequest, key: headerWSVersionName, val: headerWSVersionValue) - addHeader(urlRequest: urlRequest, key: headerWSKeyName, val: generateWebSocketKey()) + addHeader(urlRequest: urlRequest, key: headerWSVersionName as NSString, val: headerWSVersionValue as NSString) + addHeader(urlRequest: urlRequest, key: headerWSKeyName as NSString, val: generateWebSocketKey() as NSString) if let origin = origin { - addHeader(urlRequest: urlRequest, key: headerOriginName, val: origin) + addHeader(urlRequest: urlRequest, key: headerOriginName as NSString, val: origin as NSString) } - addHeader(urlRequest: urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)") + addHeader(urlRequest: urlRequest, key: headerWSHostName as NSString, val: "\(url.host!):\(port!)" as NSString) for (key,value) in headers { - addHeader(urlRequest: urlRequest, key: key, val: value) + addHeader(urlRequest: urlRequest, key: key as NSString, val: value as NSString) } if let cfHTTPMessage = CFHTTPMessageCopySerializedMessage(urlRequest) { let serializedRequest = cfHTTPMessage.takeRetainedValue() @@ -276,48 +274,45 @@ public class WebSocket : NSObject, NSStreamDelegate { var readStream: Unmanaged? var writeStream: Unmanaged? - let h: NSString = url.host! - CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream) + let h = url.host! + CFStreamCreatePairWithSocketToHost(nil, h as NSString, UInt32(port), &readStream, &writeStream) inputStream = readStream!.takeRetainedValue() outputStream = writeStream!.takeRetainedValue() guard let inStream = inputStream, let outStream = outputStream else { return } inStream.delegate = self outStream.delegate = self if ["wss", "https"].contains(url.scheme) { - inStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey) - outStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey) + inStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL as NSString, forKey: NSStreamSocketSecurityLevelKey) + outStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL as NSString, forKey: NSStreamSocketSecurityLevelKey) } else { certValidated = true //not a https session, so no need to check SSL pinning } if voipEnabled { - inStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType) - outStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType) + inStream.setProperty(NSStreamNetworkServiceTypeVoIP as NSString, forKey: NSStreamNetworkServiceType) + outStream.setProperty(NSStreamNetworkServiceTypeVoIP as NSString, forKey: NSStreamNetworkServiceType) } if selfSignedSSL { let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(value: false), kCFStreamSSLPeerName: kCFNull] - inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String) - outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String) + inStream.setProperty(settings as AnyObject?, forKey: kCFStreamPropertySSLSettings as String) + outStream.setProperty(settings as AnyObject?, forKey: kCFStreamPropertySSLSettings as String) } - #if swift(>=3) - #else if let cipherSuites = self.enabledSSLCipherSuites { - if let sslContextIn = CFReadStreamCopyProperty(inputStream, kCFStreamPropertySSLContext) as! SSLContextRef?, - sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContextRef? { + if let sslContextIn = CFReadStreamCopyProperty(inputStream, kCFStreamPropertySSLContext) as! SSLContext?, + sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContext? { let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count) let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count) if resIn != errSecSuccess { - let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn)) - disconnectStream(error) + let error = self.errorWithDetail(detail: "Error setting ingoing cypher suites", code: UInt16(resIn)) + disconnectStream(error: error) return } if resOut != errSecSuccess { - let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut)) - disconnectStream(error) + let error = self.errorWithDetail(detail: "Error setting outgoing cypher suites", code: UInt16(resOut)) + disconnectStream(error: error) return } } } - #endif CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue) CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue) inStream.open() @@ -328,12 +323,12 @@ public class WebSocket : NSObject, NSStreamDelegate { self.mutex.unlock() let bytes = UnsafePointer(data.bytes) - var timeout = 5000000 //wait 5 seconds before giving up + var out = timeout * 1000000 //wait 5 seconds before giving up writeQueue.addOperation { [weak self] in while !outStream.hasSpaceAvailable { usleep(100) //wait until the socket is ready - timeout -= 100 - if timeout < 0 { + out -= 100 + if out < 0 { self?.cleanupStream() self?.doDisconnect(error: self?.errorWithDetail(detail: "write wait timed out", code: 2)) return @@ -344,24 +339,22 @@ public class WebSocket : NSObject, NSStreamDelegate { outStream.write(bytes, maxLength: data.length) } } + //delegate for the stream methods. Processes incoming bytes public func stream(aStream: NSStream, handle eventCode: NSStreamEvent) { - #if swift(>=3) - #else - if let sec = security where !certValidated && [.HasBytesAvailable, .HasSpaceAvailable].contains(eventCode) { + if let sec = security where !certValidated && [.hasBytesAvailable, .hasSpaceAvailable].contains(eventCode) { let possibleTrust: AnyObject? = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as String) if let trust: AnyObject = possibleTrust { let domain: AnyObject? = aStream.property(forKey: kCFStreamSSLPeerName as String) - if sec.isValid(trust as! SecTrustRef, domain: domain as! String?) { + if sec.isValid(trust: trust as! SecTrust, domain: domain as! String?) { certValidated = true } else { - let error = errorWithDetail("Invalid SSL certificate", code: 1) - disconnectStream(error) + let error = errorWithDetail(detail: "Invalid SSL certificate", code: 1) + disconnectStream(error: error) return } } } - #endif if eventCode == .hasBytesAvailable { if aStream == inputStream { processInputStream() @@ -416,25 +409,24 @@ public class WebSocket : NSObject, NSStreamDelegate { } ///dequeue the incoming input so it is processed in order private func dequeueInput() { - guard !inputQueue.isEmpty else { return } - - let data = inputQueue[0] - var work = data - if let fragBuffer = fragBuffer { - let combine = NSMutableData(data: fragBuffer) - combine.append(data) - work = combine - self.fragBuffer = nil + while !inputQueue.isEmpty { + let data = inputQueue[0] + var work = data + if let fragBuffer = fragBuffer { + let combine = NSMutableData(data: fragBuffer) + combine.append(data) + work = combine + self.fragBuffer = nil + } + let buffer = UnsafePointer(work.bytes) + let length = work.length + if !connected { + processTCPHandshake(buffer: buffer, bufferLen: length) + } else { + processRawMessagesInBuffer(pointer: buffer, bufferLen: length) + } + inputQueue = inputQueue.filter{$0 != data} } - let buffer = UnsafePointer(work.bytes) - let length = work.length - if !connected { - processTCPHandshake(buffer: buffer, bufferLen: length) - } else { - processRawMessage(buffer: buffer, bufferLen: length) - } - inputQueue = inputQueue.filter{$0 != data} - dequeueInput() } //handle checking the inital connection status @@ -444,7 +436,7 @@ public class WebSocket : NSObject, NSStreamDelegate { case 0: connected = true guard canDispatch else {return} - dispatch_async(queue) { [weak self] in + dispatch_async(queue!) { [weak self] in guard let s = self else { return } s.onConnect?() s.delegate?.websocketDidConnect(socket: s) @@ -480,7 +472,7 @@ public class WebSocket : NSObject, NSStreamDelegate { totalSize += 1 //skip the last \n let restSize = bufferLen - totalSize if restSize > 0 { - processRawMessage(buffer: (buffer+totalSize),bufferLen: restSize) + processRawMessagesInBuffer(pointer: buffer + totalSize, bufferLen: restSize) } return 0 //success } @@ -497,7 +489,7 @@ public class WebSocket : NSObject, NSStreamDelegate { } if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) { let headers = cfHeaders.takeRetainedValue() as NSDictionary - if let acceptKey = headers[headerWSAcceptName] as? NSString { + if let acceptKey = headers[headerWSAcceptName as NSString] as? NSString { if acceptKey.length > 0 { return 0 } @@ -533,12 +525,15 @@ public class WebSocket : NSObject, NSStreamDelegate { } } - ///process the websocket data - private func processRawMessage(buffer: UnsafePointer, bufferLen: Int) { + /// 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 { let response = readStack.last + guard let baseAddress = buffer.baseAddress else { fatalError("") } + let bufferLen = buffer.count if response != nil && bufferLen < 2 { - fragBuffer = NSData(bytes: buffer, length: bufferLen) - return + fragBuffer = NSData(buffer: buffer) + return emptyBuffer } if let response = response where response.bytesLeft > 0 { var len = response.bytesLeft @@ -548,24 +543,20 @@ public class WebSocket : NSObject, NSStreamDelegate { extra = 0 } response.bytesLeft -= len - response.buffer?.append(NSData(bytes: buffer, length: len)) + response.buffer?.append(NSData(bytes: baseAddress, length: len)) processResponse(response: response) - let offset = bufferLen - extra - if extra > 0 { - processExtra(buffer: (buffer+offset), bufferLen: extra) - } - return + return buffer.fromOffset(offset: bufferLen - extra) } else { - let isFin = (FinMask & buffer[0]) - let receivedOpcode = OpCode(rawValue: (OpCodeMask & buffer[0])) - let isMasked = (MaskMask & buffer[1]) - let payloadLen = (PayloadLenMask & buffer[1]) + let isFin = (FinMask & baseAddress[0]) + let receivedOpcode = OpCode(rawValue: (OpCodeMask & baseAddress[0])) + let isMasked = (MaskMask & baseAddress[1]) + let payloadLen = (PayloadLenMask & baseAddress[1]) var offset = 2 - if (isMasked > 0 || (RSVMask & buffer[0]) > 0) && receivedOpcode != .Pong { + if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .Pong { let errCode = CloseCode.ProtocolError.rawValue doDisconnect(error: errorWithDetail(detail: "masked and rsv data is not currently supported", code: errCode)) writeError(code: errCode) - return + return emptyBuffer } let isControlFrame = (receivedOpcode == .ConnectionClose || receivedOpcode == .Ping) if !isControlFrame && (receivedOpcode != .BinaryFrame && receivedOpcode != .ContinueFrame && @@ -573,20 +564,20 @@ public class WebSocket : NSObject, NSStreamDelegate { let errCode = CloseCode.ProtocolError.rawValue doDisconnect(error: errorWithDetail(detail: "unknown opcode: \(receivedOpcode)", code: errCode)) writeError(code: errCode) - return + return emptyBuffer } if isControlFrame && isFin == 0 { let errCode = CloseCode.ProtocolError.rawValue doDisconnect(error: errorWithDetail(detail: "control frames can't be fragmented", code: errCode)) writeError(code: errCode) - return + return emptyBuffer } if receivedOpcode == .ConnectionClose { var code = CloseCode.Normal.rawValue if payloadLen == 1 { code = CloseCode.ProtocolError.rawValue } else if payloadLen > 1 { - code = WebSocket.readUint16(buffer: buffer, offset: offset) + code = WebSocket.readUint16(buffer: baseAddress, offset: offset) if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) { code = CloseCode.ProtocolError.rawValue } @@ -595,7 +586,7 @@ public class WebSocket : NSObject, NSStreamDelegate { if payloadLen > 2 { let len = Int(payloadLen-2) if len > 0 { - let bytes = UnsafePointer((buffer+offset)) + let bytes = baseAddress + offset let str: NSString? = NSString(data: NSData(bytes: bytes, length: len), encoding: NSUTF8StringEncoding) if str == nil { code = CloseCode.ProtocolError.rawValue @@ -604,23 +595,23 @@ public class WebSocket : NSObject, NSStreamDelegate { } doDisconnect(error: errorWithDetail(detail: "connection closed by server", code: code)) writeError(code: code) - return + return emptyBuffer } if isControlFrame && payloadLen > 125 { writeError(code: CloseCode.ProtocolError.rawValue) - return + return emptyBuffer } var dataLength = UInt64(payloadLen) if dataLength == 127 { - dataLength = WebSocket.readUint64(buffer: buffer, offset: offset) + dataLength = WebSocket.readUint64(buffer: baseAddress, offset: offset) offset += sizeof(UInt64) } else if dataLength == 126 { - dataLength = UInt64(WebSocket.readUint16(buffer: buffer, offset: offset)) + dataLength = UInt64(WebSocket.readUint16(buffer: baseAddress, offset: offset)) offset += sizeof(UInt16) } if bufferLen < offset || UInt64(bufferLen - offset) < dataLength { - fragBuffer = NSData(bytes: buffer, length: bufferLen) - return + fragBuffer = NSData(bytes: baseAddress, length: bufferLen) + return emptyBuffer } var len = dataLength if dataLength > UInt64(bufferLen) { @@ -631,22 +622,17 @@ public class WebSocket : NSObject, NSStreamDelegate { len = 0 data = NSData() } else { - data = NSData(bytes: UnsafePointer((buffer+offset)), length: Int(len)) + data = NSData(bytes: baseAddress+offset, length: Int(len)) } if receivedOpcode == .Pong { if canDispatch { - dispatch_async(queue) { [weak self] in + dispatch_async(queue!) { [weak self] in guard let s = self else { return } s.onPong?() s.pongDelegate?.websocketDidReceivePong(socket: s) } } - let step = Int(offset+numericCast(len)) - let extra = bufferLen-step - if extra > 0 { - processRawMessage(buffer: (buffer+step), bufferLen: extra) - } - return + return buffer.fromOffset(offset: offset + Int(len)) } var response = readStack.last if isControlFrame { @@ -656,7 +642,7 @@ public class WebSocket : NSObject, NSStreamDelegate { let errCode = CloseCode.ProtocolError.rawValue doDisconnect(error: errorWithDetail(detail: "continue frame before a binary or text frame", code: errCode)) writeError(code: errCode) - return + return emptyBuffer } var isNew = false if response == nil { @@ -665,7 +651,7 @@ public class WebSocket : NSObject, NSStreamDelegate { doDisconnect(error: errorWithDetail(detail: "first frame can't be a continue frame", code: errCode)) writeError(code: errCode) - return + return emptyBuffer } isNew = true response = WSResponse() @@ -680,7 +666,7 @@ public class WebSocket : NSObject, NSStreamDelegate { doDisconnect(error: errorWithDetail(detail: "second and beyond of fragment message must be a continue frame", code: errCode)) writeError(code: errCode) - return + return emptyBuffer } response!.buffer!.append(data) } @@ -695,20 +681,18 @@ public class WebSocket : NSObject, NSStreamDelegate { } let step = Int(offset+numericCast(len)) - let extra = bufferLen-step - if extra > 0 { - processExtra(buffer: (buffer+step), bufferLen: extra) - } + return buffer.fromOffset(offset: step) } - } - ///process the extra of a buffer - private func processExtra(buffer: UnsafePointer, bufferLen: Int) { - if bufferLen < 2 { - fragBuffer = NSData(bytes: buffer, length: bufferLen) - } else { - processRawMessage(buffer: buffer, bufferLen: bufferLen) + /// Process all messages in the buffer if possible. + private func processRawMessagesInBuffer(pointer: UnsafePointer, bufferLen: Int) { + var buffer = UnsafeBufferPointer(start: pointer, count: bufferLen) + repeat { + buffer = processOneRawMessage(inBuffer: buffer) + } while buffer.count >= 2 + if buffer.count > 0 { + fragBuffer = NSData(buffer: buffer) } } @@ -725,7 +709,7 @@ public class WebSocket : NSObject, NSStreamDelegate { return false } if canDispatch { - dispatch_async(queue) { [weak self] in + dispatch_async(queue!) { [weak self] in guard let s = self else { return } s.onText?(str! as String) s.delegate?.websocketDidReceiveMessage(socket: s, text: str! as String) @@ -734,7 +718,7 @@ public class WebSocket : NSObject, NSStreamDelegate { } else if response.code == .BinaryFrame { if canDispatch { let data = response.buffer! //local copy so it is perverse for writing - dispatch_async(queue) { [weak self] in + dispatch_async(queue!) { [weak self] in guard let s = self else { return } s.onData?(data) s.delegate?.websocketDidReceiveData(socket: s, data: data) @@ -751,8 +735,9 @@ public class WebSocket : NSObject, NSStreamDelegate { private func errorWithDetail(detail: String, code: UInt16) -> NSError { var details = [String: String]() details[NSLocalizedDescriptionKey] = detail + #if swift(>=3) - return NSError(domain: "April 12 build of Swift 3 broke this property", code: Int(code), userInfo: details) + return NSError(domain: "This is still broken", code: Int(code), userInfo: details) #else return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details) #endif @@ -834,7 +819,7 @@ public class WebSocket : NSObject, NSStreamDelegate { didDisconnect = true connected = false guard canDispatch else {return} - dispatch_async(queue) { [weak self] in + dispatch_async(queue!) { [weak self] in guard let s = self else { return } s.onDisconnect?(error) s.delegate?.websocketDidDisconnect(socket: s, error: error) @@ -850,11 +835,27 @@ public class WebSocket : NSObject, NSStreamDelegate { } -#if swift(>=3) -#else +private extension NSData { + + convenience init(buffer: UnsafeBufferPointer) { + self.init(bytes: buffer.baseAddress, length: buffer.count) + } + +} + +private extension UnsafeBufferPointer { + + func fromOffset(offset: Int) -> UnsafeBufferPointer { + return UnsafeBufferPointer(start: baseAddress!.advanced(by: offset), count: count - offset) + } + +} + +private let emptyBuffer = UnsafeBufferPointer(start: nil, count: 0) + public class SSLCert { var certData: NSData? - var key: SecKeyRef? + var key: SecKey? /** Designated init for certificates @@ -874,7 +875,7 @@ public class SSLCert { - returns: a representation security object to be used with */ - public init(key: SecKeyRef) { + public init(key: SecKey) { self.key = key } } @@ -884,7 +885,7 @@ public class SSLSecurity { var isReady = false //is the key processing done? var certificates: [NSData]? //the certificates - var pubKeys: [SecKeyRef]? //the public keys + var pubKeys: [SecKey]? //the public keys var usePublicKeys = false //use public keys or certificate validation? /** @@ -895,7 +896,7 @@ public class SSLSecurity { - returns: a representation security object to be used with */ public convenience init(usePublicKeys: Bool = false) { - let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".") + let paths = NSBundle.main().pathsForResources(ofType: "cer", inDirectory: ".") let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in var certs = certs @@ -921,10 +922,10 @@ public class SSLSecurity { 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 + let pubKeys = certs.reduce([SecKey]()) { (pubKeys: [SecKey], cert: SSLCert) -> [SecKey] in var pubKeys = pubKeys if let data = cert.certData where cert.key == nil { - cert.key = self.extractPublicKey(data) + cert.key = self.extractPublicKey(data: data) } if let key = cert.key { pubKeys.append(key) @@ -956,7 +957,7 @@ public class SSLSecurity { - returns: if the key was successfully validated */ - public func isValid(trust: SecTrustRef, domain: String?) -> Bool { + public func isValid(trust: SecTrust, domain: String?) -> Bool { var tries = 0 while(!self.isReady) { @@ -966,16 +967,16 @@ public class SSLSecurity { return false //doesn't appear it is going to ever be ready... } } - var policy: SecPolicyRef + var policy: SecPolicy if self.validatedDN { - policy = SecPolicyCreateSSL(true, domain) + policy = SecPolicyCreateSSL(true, (domain as! CFString)) } else { policy = SecPolicyCreateBasicX509() } SecTrustSetPolicies(trust,policy) if self.usePublicKeys { if let keys = self.pubKeys { - let serverPubKeys = publicKeyChainForTrust(trust) + let serverPubKeys = publicKeyChainForTrust(trust: trust) for serverKey in serverPubKeys as [AnyObject] { for key in keys as [AnyObject] { if serverKey.isEqual(key) { @@ -985,12 +986,12 @@ public class SSLSecurity { } } } else if let certs = self.certificates { - let serverCerts = certificateChainForTrust(trust) + let serverCerts = certificateChainForTrust(trust: trust) var collect = [SecCertificate]() for cert in certs { collect.append(SecCertificateCreateWithData(nil,cert)!) } - SecTrustSetAnchorCertificates(trust,collect) + SecTrustSetAnchorCertificates(trust,collect as CFArray) var result: SecTrustResultType = 0 SecTrustEvaluate(trust,&result) let r = Int(result) @@ -1019,10 +1020,10 @@ public class SSLSecurity { - returns: a public key */ - func extractPublicKey(data: NSData) -> SecKeyRef? { + func extractPublicKey(data: NSData) -> SecKey? { guard let cert = SecCertificateCreateWithData(nil, data) else { return nil } - return extractPublicKeyFromCert(cert, policy: SecPolicyCreateBasicX509()) + return extractPublicKeyFromCert(cert: cert, policy: SecPolicyCreateBasicX509()) } /** @@ -1032,7 +1033,7 @@ public class SSLSecurity { - returns: a public key */ - func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? { + func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKey? { var possibleTrust: SecTrust? SecTrustCreateWithCertificates(cert, policy, &possibleTrust) @@ -1050,7 +1051,7 @@ public class SSLSecurity { - returns: the certificate chain for the trust */ - func certificateChainForTrust(trust: SecTrustRef) -> [NSData] { + func certificateChainForTrust(trust: SecTrust) -> [NSData] { let certificates = (0.. [NSData] in var certificates = certificates let cert = SecTrustGetCertificateAtIndex(trust, index) @@ -1068,12 +1069,12 @@ public class SSLSecurity { - returns: the public keys from the certifcate chain for the trust */ - func publicKeyChainForTrust(trust: SecTrustRef) -> [SecKeyRef] { + func publicKeyChainForTrust(trust: SecTrust) -> [SecKey] { let policy = SecPolicyCreateBasicX509() - let keys = (0.. [SecKeyRef] in + let keys = (0.. [SecKey] in var keys = keys let cert = SecTrustGetCertificateAtIndex(trust, index) - if let key = extractPublicKeyFromCert(cert!, policy: policy) { + if let key = extractPublicKeyFromCert(cert: cert!, policy: policy) { keys.append(key) } @@ -1085,4 +1086,3 @@ public class SSLSecurity { } -#endif \ No newline at end of file