Merge pull request #248 from socketio/engine-refactoring

Engine refactoring
This commit is contained in:
Erik Little 2015-12-03 14:16:24 -05:00
commit 9c1a3edeb9
3 changed files with 90 additions and 39 deletions

View File

@ -34,6 +34,8 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
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?) {
@ -121,6 +115,35 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
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 {
logAndError("Got unknown error from server")
}
}
private func checkIfMessageIsBase64Binary(var message: String) -> Bool {
if message.hasPrefix("b4") {
// binary in base64 string
@ -143,6 +166,7 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
pingTimer?.invalidate()
closed = true
connected = false
if websocket {
sendWebSocketMessage("", withType: .Close)
@ -342,10 +366,17 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
}
}
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)
@ -430,6 +457,24 @@ public final class SocketEngine: NSObject, SocketEngineSpec, WebSocketDelegate {
}
}
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()
}
}

View File

@ -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<SocketIOClientOption>
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

View File

@ -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: