From 57b252fa7df8f191f20085c665f4ba257f90184c Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 1 Dec 2015 15:17:18 -0500 Subject: [PATCH 1/4] refactor engine, don't re-add engine on reconnect, other improvements --- SocketIOClientSwift/SocketEngine.swift | 100 +++++++++++++----- SocketIOClientSwift/SocketIOClient.swift | 26 ++--- .../SocketIOClientOption.swift | 3 + 3 files changed, 90 insertions(+), 39 deletions(-) diff --git a/SocketIOClientSwift/SocketEngine.swift b/SocketIOClientSwift/SocketEngine.swift index f714059..91d3143 100644 --- a/SocketIOClientSwift/SocketEngine.swift +++ b/SocketIOClientSwift/SocketEngine.swift @@ -33,6 +33,8 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { public private(set) var ws: WebSocket? public weak var client: SocketEngineClient? + + private weak var sessionDelegate: NSURLSessionDelegate? private typealias Probe = (msg: String, type: SocketEnginePacketType, data: [NSData]) private typealias ProbeWaitQueue = [Probe] @@ -45,6 +47,7 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { private let url: String private let workQueue = NSOperationQueue() + private var connectParams: [String: AnyObject]? private var closed = false private var extraHeaders: [String: String]? private var fastUpgrade = false @@ -64,12 +67,11 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { private var probing = false private var probeWait = ProbeWaitQueue() private var secure = false - private var session: NSURLSession! + private var session: NSURLSession? private var voipEnabled = false private var waitingForPoll = false private var waitingForPost = false private var websocketConnected = false - private(set) var connected = false private(set) var polling = true private(set) var websocket = false @@ -81,9 +83,7 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { for option in options { switch option { case .SessionDelegate(let delegate): - session = NSURLSession(configuration: .defaultSessionConfiguration(), - delegate: delegate, - delegateQueue: workQueue) + sessionDelegate = delegate case .ForcePolling(let force): forcePolling = force case .ForceWebsockets(let force): @@ -102,12 +102,6 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { continue } } - - if session == nil { - session = NSURLSession(configuration: .defaultSessionConfiguration(), - delegate: nil, - delegateQueue: workQueue) - } } public convenience init(client: SocketEngineClient, url: String, options: NSDictionary?) { @@ -120,6 +114,35 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { closed = true stopPolling() } + + private func checkAndHandleEngineError(msg: String) { + guard let stringData = msg.dataUsingEncoding(NSUTF8StringEncoding, + allowLossyConversion: false) else { return } + + do { + if let dict = try NSJSONSerialization.JSONObjectWithData(stringData, + options: NSJSONReadingOptions.MutableContainers) as? NSDictionary { + guard let code = dict["code"] as? Int else { return } + guard let error = dict["message"] as? String else { return } + + switch code { + case 0: // Unknown transport + logAndError(error) + case 1: // Unknown sid. clear and retry connect + sid = "" + open(connectParams) + case 2: // Bad handshake request + logAndError(error) + case 3: // Bad request + logAndError(error) + default: + logAndError(error) + } + } + } catch { + DefaultSocketLogger.Logger.error("Got message: %@", type: logType, args: msg) + } + } private func checkIfMessageIsBase64Binary(var message: String) -> Bool { if message.hasPrefix("b4") { @@ -143,6 +166,7 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { pingTimer?.invalidate() closed = true + connected = false if websocket { sendWebSocketMessage("", withType: .Close) @@ -341,11 +365,18 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { client?.engineDidClose(reason) } } + + private func logAndError(error: String) { + DefaultSocketLogger.Logger.error(error, type: logType) + client?.didError(error) + } public func open(opts: [String: AnyObject]? = nil) { + connectParams = opts + if connected { DefaultSocketLogger.Logger.error("Tried to open while connected", type: logType) - client?.didError("Tried to open while connected") + client?.didError("Tried to open engine while connected") return } @@ -353,7 +384,7 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { DefaultSocketLogger.Logger.log("Starting engine", type: logType) DefaultSocketLogger.Logger.log("Handshaking", type: logType) - closed = false + resetEngine() (urlPolling, urlWebSocket) = createURLs(opts) @@ -388,19 +419,15 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { private func parseEngineMessage(var message: String, fromPolling: Bool) { DefaultSocketLogger.Logger.log("Got message: %@", type: logType, args: message) - func handleOther(msg: String) -> SocketEnginePacketType { - if checkIfMessageIsBase64Binary(msg) { - return .Noop - } else { - DefaultSocketLogger.Logger.error("Got message: %@", type: logType, args: msg) - return .Close - } - } - let reader = SocketStringReader(message: message) - let type = SocketEnginePacketType(rawValue: Int(reader.currentCharacter) ?? -1) - ?? handleOther(message) + guard let type = SocketEnginePacketType(rawValue: Int(reader.currentCharacter) ?? -1) else { + if !checkIfMessageIsBase64Binary(message) { + checkAndHandleEngineError(message) + } + + return + } if fromPolling && type != .Noop { fixDoubleUTF8(&message) @@ -429,7 +456,25 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { sendWebSocketMessage("probe", withType: .Ping) } } - + + + private func resetEngine() { + closed = false + connected = false + fastUpgrade = false + polling = true + probing = false + invalidated = false + session = NSURLSession(configuration: .defaultSessionConfiguration(), + delegate: sessionDelegate, + delegateQueue: workQueue) + sid = "" + waitingForPoll = false + waitingForPost = false + websocket = false + websocketConnected = false + } + /// Send an engine message (4) public func send(msg: String, withData datas: [NSData]) { if probing { @@ -525,13 +570,14 @@ extension SocketEngine { private func doRequest(req: NSMutableURLRequest, withCallback callback: (NSData?, NSURLResponse?, NSError?) -> Void) { if !polling || closed || invalidated { + DefaultSocketLogger.Logger.error("Tried to do polling request when not supposed to", type: logType) return } DefaultSocketLogger.Logger.log("Doing polling request", type: logType) req.cachePolicy = .ReloadIgnoringLocalAndRemoteCacheData - session.dataTaskWithRequest(req, completionHandler: callback).resume() + session?.dataTaskWithRequest(req, completionHandler: callback).resume() } private func doLongPoll(req: NSMutableURLRequest) { @@ -684,7 +730,7 @@ extension SocketEngine { private func stopPolling() { invalidated = true - session.finishTasksAndInvalidate() + session?.finishTasksAndInvalidate() } } diff --git a/SocketIOClientSwift/SocketIOClient.swift b/SocketIOClientSwift/SocketIOClient.swift index 4213061..51dcb3f 100644 --- a/SocketIOClientSwift/SocketIOClient.swift +++ b/SocketIOClientSwift/SocketIOClient.swift @@ -30,6 +30,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient { public private(set) var engine: SocketEngineSpec? public private(set) var status = SocketIOClientStatus.NotConnected + public var forceNew = false public var nsp = "/" public var options: Set public var reconnects = true @@ -88,6 +89,8 @@ public final class SocketIOClient: NSObject, SocketEngineClient { DefaultSocketLogger.Logger = logger case .HandleQueue(let queue): handleQueue = queue + case .ForceNew(let force): + forceNew = force default: continue } @@ -109,7 +112,6 @@ public final class SocketIOClient: NSObject, SocketEngineClient { deinit { DefaultSocketLogger.Logger.log("Client is being deinit", type: logType) - DefaultSocketLogger.Logger.log = false engine?.close() } @@ -154,21 +156,21 @@ public final class SocketIOClient: NSObject, SocketEngineClient { assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)") guard status != .Connected else { - return - } - - if status == .Closed { - DefaultSocketLogger.Logger.log("Warning! This socket was previously closed. This might be dangerous!", + DefaultSocketLogger.Logger.log("Tried connecting on an already connected socket", type: logType) - } - - status = SocketIOClientStatus.Connecting - addEngine().open(connectParams) - - guard timeoutAfter != 0 else { return } + status = .Connecting + + if engine == nil || forceNew { + addEngine().open(connectParams) + } else { + engine?.open(connectParams) + } + + guard timeoutAfter != 0 else { return } + let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeoutAfter) * Int64(NSEC_PER_SEC)) dispatch_after(time, handleQueue) {[weak self] in diff --git a/SocketIOClientSwift/SocketIOClientOption.swift b/SocketIOClientSwift/SocketIOClientOption.swift index 6036486..784a06b 100644 --- a/SocketIOClientSwift/SocketIOClientOption.swift +++ b/SocketIOClientSwift/SocketIOClientOption.swift @@ -32,6 +32,7 @@ public enum SocketIOClientOption: ClientOption { case ConnectParams([String: AnyObject]) case Cookies([NSHTTPCookie]) case ExtraHeaders([String: String]) + case ForceNew(Bool) case ForcePolling(Bool) case ForceWebsockets(Bool) case HandleQueue(dispatch_queue_t) @@ -68,6 +69,8 @@ public enum SocketIOClientOption: ClientOption { return .ReconnectAttempts(value as! Int) case "reconnectWait" where value is Int: return .ReconnectWait(value as! Int) + case "forceNew" where value is Bool: + return .ForceNew(value as! Bool) case "forcePolling" where value is Bool: return .ForcePolling(value as! Bool) case "forceWebsockets" where value is Bool: From 0acb7421cc0fca88f223851a5e68ce3e072c6285 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 2 Dec 2015 12:16:35 -0500 Subject: [PATCH 2/4] add case for opening poll --- SocketIOClientSwift/SocketEngine.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/SocketIOClientSwift/SocketEngine.swift b/SocketIOClientSwift/SocketEngine.swift index 91d3143..a623d1e 100644 --- a/SocketIOClientSwift/SocketEngine.swift +++ b/SocketIOClientSwift/SocketEngine.swift @@ -333,6 +333,7 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { } } catch { DefaultSocketLogger.Logger.error("Error parsing open packet", type: logType) + client?.didError("Error parsing open packet") return } @@ -408,7 +409,7 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { } } - doLongPoll(reqPolling) + doLongPoll(reqPolling, open: true) } private func parseEngineData(data: NSData) { @@ -580,7 +581,7 @@ extension SocketEngine { session?.dataTaskWithRequest(req, completionHandler: callback).resume() } - private func doLongPoll(req: NSMutableURLRequest) { + private func doLongPoll(req: NSMutableURLRequest, open: Bool = false) { doRequest(req) {[weak self] data, res, err in guard let this = self else {return} @@ -598,7 +599,11 @@ extension SocketEngine { if let str = NSString(data: data!, encoding: NSUTF8StringEncoding) as? String { dispatch_async(this.parseQueue) {[weak this] in - this?.parsePollingMessage(str) + if open { + this?.handleOpen(str) + } else { + this?.parsePollingMessage(str) + } } } From c74858fd5d56a554bea8efe46f1f67a66c4168f3 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 2 Dec 2015 12:32:31 -0500 Subject: [PATCH 3/4] Revert "add case for opening poll" This reverts commit 0acb7421cc0fca88f223851a5e68ce3e072c6285. --- SocketIOClientSwift/SocketEngine.swift | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/SocketIOClientSwift/SocketEngine.swift b/SocketIOClientSwift/SocketEngine.swift index a623d1e..91d3143 100644 --- a/SocketIOClientSwift/SocketEngine.swift +++ b/SocketIOClientSwift/SocketEngine.swift @@ -333,7 +333,6 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { } } catch { DefaultSocketLogger.Logger.error("Error parsing open packet", type: logType) - client?.didError("Error parsing open packet") return } @@ -409,7 +408,7 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { } } - doLongPoll(reqPolling, open: true) + doLongPoll(reqPolling) } private func parseEngineData(data: NSData) { @@ -581,7 +580,7 @@ extension SocketEngine { session?.dataTaskWithRequest(req, completionHandler: callback).resume() } - private func doLongPoll(req: NSMutableURLRequest, open: Bool = false) { + private func doLongPoll(req: NSMutableURLRequest) { doRequest(req) {[weak self] data, res, err in guard let this = self else {return} @@ -599,11 +598,7 @@ extension SocketEngine { if let str = NSString(data: data!, encoding: NSUTF8StringEncoding) as? String { dispatch_async(this.parseQueue) {[weak this] in - if open { - this?.handleOpen(str) - } else { - this?.parsePollingMessage(str) - } + this?.parsePollingMessage(str) } } From 04d49fe7cb37f64831601605e7e40d0c5b7d029c Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 2 Dec 2015 12:46:54 -0500 Subject: [PATCH 4/4] fix socketio/socket.io-client-swift#246 --- SocketIOClientSwift/SocketEngine.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SocketIOClientSwift/SocketEngine.swift b/SocketIOClientSwift/SocketEngine.swift index 91d3143..d570d21 100644 --- a/SocketIOClientSwift/SocketEngine.swift +++ b/SocketIOClientSwift/SocketEngine.swift @@ -140,7 +140,7 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate { } } } catch { - DefaultSocketLogger.Logger.error("Got message: %@", type: logType, args: msg) + logAndError("Got unknown error from server") } }