Merge pull request #248 from socketio/engine-refactoring
Engine refactoring
This commit is contained in:
commit
9c1a3edeb9
@ -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 {
|
||||
logAndError("Got unknown error from server")
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user