diff --git a/Source/SocketIO/Client/SocketIOClient.swift b/Source/SocketIO/Client/SocketIOClient.swift index 4debd560..16420fb9 100644 --- a/Source/SocketIO/Client/SocketIOClient.swift +++ b/Source/SocketIO/Client/SocketIOClient.swift @@ -182,7 +182,7 @@ open class SocketIOClient: NSObject, SocketIOClientSpec { /// Called when the client has disconnected from socket.io. /// /// - parameter reason: The reason for the disconnection. - open func didDisconnect(reason: String) { + open func didDisconnect(reason: SocketConnectionChangeReason) { guard status != .disconnected else { return } DefaultSocketLogger.Logger.log("Disconnected: \(reason)", type: logType) @@ -307,11 +307,11 @@ open class SocketIOClient: NSObject, SocketIOClientSpec { guard status == .connected else { wrappedCompletion?() - handleClientEvent(.error, data: ["Tried emitting when not connected"]) + handleClientEvent(.error, data: [SocketError.triedEmittingWhenNotConnected]) return } - let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: nsp, ack: isAck, checkForBinary: binary) + let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: nsp, ack: isAck, checkForBinary: binary, disableEventMessageParsing: self.manager?.disableEventMessageParsing ?? false) let str = packet.packetString DefaultSocketLogger.Logger.log("Emitting: \(str), Ack: \(isAck)", type: logType) @@ -382,7 +382,7 @@ open class SocketIOClient: NSObject, SocketIOClientSpec { case .connect: didConnect(toNamespace: nsp, payload: packet.data.isEmpty ? nil : packet.data[0] as? [String: Any]) case .disconnect: - didDisconnect(reason: "Got Disconnect") + didDisconnect(reason: .gotDisconnectPacket) case .error: handleEvent("error", data: packet.data, isInternalMessage: true, withAck: packet.id) } @@ -522,7 +522,7 @@ open class SocketIOClient: NSObject, SocketIOClientSpec { /// Called when the manager detects a broken connection, or when a manual reconnect is triggered. /// /// - parameter reason: The reason this socket is reconnecting. - open func setReconnecting(reason: String) { + open func setReconnecting(reason: SocketConnectionChangeReason) { status = .connecting handleClientEvent(.reconnect, data: [reason]) diff --git a/Source/SocketIO/Client/SocketIOClientOption.swift b/Source/SocketIO/Client/SocketIOClientOption.swift index ac1a032b..5ca896eb 100644 --- a/Source/SocketIO/Client/SocketIOClientOption.swift +++ b/Source/SocketIO/Client/SocketIOClientOption.swift @@ -77,6 +77,10 @@ public enum SocketIOClientOption : ClientOption { /// Used to pass in a custom logger. case logger(SocketLogger) + /// If passed `true`, incoming and outgoing message data will not be parsed, and all message events will be received with + /// `event` value of `"rawMessage"`; listen with `socketClient.on("rawMessage") { ... }` + case disableEventMessageParsing(Bool) + /// A custom path to socket.io. Only use this if the socket.io server is configured to look for this path. case path(String) @@ -124,6 +128,8 @@ public enum SocketIOClientOption : ClientOption { description = "connectParams" case .cookies: description = "cookies" + case .disableEventMessageParsing: + description = "disableEventMessageParsing" case .extraHeaders: description = "extraHeaders" case .forceNew: @@ -177,6 +183,8 @@ public enum SocketIOClientOption : ClientOption { value = params case let .cookies(cookies): value = cookies + case let .disableEventMessageParsing(disable): + value = disable case let .extraHeaders(headers): value = headers case let .forceNew(force): diff --git a/Source/SocketIO/Client/SocketIOClientSpec.swift b/Source/SocketIO/Client/SocketIOClientSpec.swift index 04b62faa..64a83497 100644 --- a/Source/SocketIO/Client/SocketIOClientSpec.swift +++ b/Source/SocketIO/Client/SocketIOClientSpec.swift @@ -88,12 +88,12 @@ public protocol SocketIOClientSpec : AnyObject { /// Called when the client has disconnected from socket.io. /// /// - parameter reason: The reason for the disconnection. - func didDisconnect(reason: String) + func didDisconnect(reason: SocketConnectionChangeReason) /// Called when the client encounters an error. /// /// - parameter reason: The reason for the disconnection. - func didError(reason: String) + func didError(error: SocketError) /// Disconnects the socket. func disconnect() @@ -271,15 +271,15 @@ public protocol SocketIOClientSpec : AnyObject { /// Called when the manager detects a broken connection, or when a manual reconnect is triggered. /// /// parameter reason: The reason this socket is going reconnecting. - func setReconnecting(reason: String) + func setReconnecting(reason: SocketConnectionChangeReason) } public extension SocketIOClientSpec { /// Default implementation. - func didError(reason: String) { - DefaultSocketLogger.Logger.error("\(reason)", type: "SocketIOClient") + func didError(error: SocketError) { + DefaultSocketLogger.Logger.error("\(error)", type: "SocketIOClient") - handleClientEvent(.error, data: [reason]) + handleClientEvent(.error, data: [error]) } } diff --git a/Source/SocketIO/Engine/SocketEngine.swift b/Source/SocketIO/Engine/SocketEngine.swift index cd261166..5154858f 100644 --- a/Source/SocketIO/Engine/SocketEngine.swift +++ b/Source/SocketIO/Engine/SocketEngine.swift @@ -198,9 +198,9 @@ open class SocketEngine: 2: Bad handshake request 3: Bad request */ - didError(reason: error) + didError(error: .engineErrorMessage(error)) } catch { - client?.engineDidError(reason: "Got unknown error from server \(msg)") + client?.engineDidError(error: .engineUnknownMessage(msg)) } } @@ -214,12 +214,13 @@ open class SocketEngine: } } - private func closeOutEngine(reason: String) { + private func closeOutEngine(reason: SocketConnectionChangeReason) { sid = "" closed = true invalidated = true connected = false - + // nil out the delegate here; we're getting callbacks from the old socket when closing and recreating the WS + ws?.delegate = nil ws?.disconnect() stopPolling() client?.engineDidClose(reason: reason) @@ -236,7 +237,7 @@ open class SocketEngine: if connected { DefaultSocketLogger.Logger.error("Engine tried opening while connected. Assuming this was a reconnect", type: SocketEngine.logType) - _disconnect(reason: "reconnect") + _disconnect(reason: .socketError(.triedOpeningWhileConnected)) } DefaultSocketLogger.Logger.log("Starting engine. Server: \(url)", type: SocketEngine.logType) @@ -315,25 +316,25 @@ open class SocketEngine: } /// Called when an error happens during execution. Causes a disconnection. - open func didError(reason: String) { - DefaultSocketLogger.Logger.error("\(reason)", type: SocketEngine.logType) - client?.engineDidError(reason: reason) - disconnect(reason: reason) + open func didError(error: SocketError) { + DefaultSocketLogger.Logger.error("\(error)", type: SocketEngine.logType) + client?.engineDidError(error: error) + disconnect(reason: .socketError(error)) } /// Disconnects from the server. /// /// - parameter reason: The reason for the disconnection. This is communicated up to the client. - open func disconnect(reason: String) { + open func disconnect(reason: SocketConnectionChangeReason) { engineQueue.async { self._disconnect(reason: reason) } } - private func _disconnect(reason: String) { + private func _disconnect(reason: SocketConnectionChangeReason) { guard connected && !closed else { return closeOutEngine(reason: reason) } - DefaultSocketLogger.Logger.log("Engine is being closed.", type: SocketEngine.logType) + DefaultSocketLogger.Logger.log("Engine is being closed. \(reason)", type: SocketEngine.logType) if polling { disconnectPolling(reason: reason) @@ -345,7 +346,7 @@ open class SocketEngine: // 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(reason: String) { + private func disconnectPolling(reason: SocketConnectionChangeReason) { postWait.append((String(SocketEnginePacketType.close.rawValue), {})) doRequest(for: createRequestForPostWithPostWait()) {_, _, _ in } @@ -402,7 +403,7 @@ open class SocketEngine: postWait.removeAll(keepingCapacity: false) } - private func handleClose(_ reason: String) { + private func handleClose(_ reason: SocketConnectionChangeReason) { client?.engineDidClose(reason: reason) } @@ -416,13 +417,13 @@ open class SocketEngine: private func handleOpen(openData: String) { guard let json = try? openData.toDictionary() else { - didError(reason: "Error parsing open packet") + didError(error: .openPacketUnparseable) return } guard let sid = json["sid"] as? String else { - didError(reason: "Open packet contained no sid") + didError(error: .openPacketMissingSID) return } @@ -458,7 +459,7 @@ open class SocketEngine: doPoll() } - client?.engineDidOpen(reason: "Connect") + client?.engineDidOpen(reason: .engineOpen) } private func handlePong(with message: String) { @@ -492,8 +493,9 @@ open class SocketEngine: // Make sure not to ping old connections guard let this = self, this.sid == id else { return } - if abs(this.lastCommunication?.timeIntervalSinceNow ?? deadlineMs) >= deadlineMs { - this.closeOutEngine(reason: "Ping timeout") + let actualTime = this.lastCommunication?.timeIntervalSinceNow ?? deadlineMs + if abs(actualTime) >= deadlineMs { + this.closeOutEngine(reason: .socketError(.pingTimeout(actualTime))) } else { this.checkPings() } @@ -541,7 +543,7 @@ open class SocketEngine: case .open: handleOpen(openData: String(message.dropFirst())) case .close: - handleClose(message) + handleClose(.engineCloseMessage(message)) default: DefaultSocketLogger.Logger.log("Got unknown packet type", type: SocketEngine.logType) } @@ -571,7 +573,7 @@ open class SocketEngine: // Server is not responding if pongsMissed > pongsMissedMax { - closeOutEngine(reason: "Ping timeout") + closeOutEngine(reason: .socketError(.pongsMissed(pongsMissed))) return } @@ -687,12 +689,11 @@ open class SocketEngine: } } - private func websocketDidDisconnect(error: Error?) { + private func websocketDidDisconnect(reason: SocketConnectionChangeReason) { probing = false if closed { - client?.engineDidClose(reason: "Disconnect") - + client?.engineDidClose(reason: reason) return } @@ -705,12 +706,13 @@ open class SocketEngine: connected = false polling = true - if let error = error as? WSError { - didError(reason: "\(error.message). code=\(error.code), type=\(error.type)") - } else if let reason = error?.localizedDescription { - didError(reason: reason) - } else { - client?.engineDidClose(reason: "Socket Disconnected") + //following existing patterns for these, just cleaning up a bit + switch(reason) { + case .socketError(let error): + didError(error: error) + default: + //following existing pattern, we close ourselves out here + client?.engineDidClose(reason: reason) } } @@ -728,13 +730,13 @@ extension SocketEngine { public func URLSession(session: URLSession, didBecomeInvalidWithError error: NSError?) { DefaultSocketLogger.Logger.error("Engine URLSession became invalid", type: "SocketEngine") - didError(reason: "Engine URLSession became invalid") + didError(error: .urlSessionBecameInvalid(error)) } } -enum EngineError: Error { - case canceled -} +//enum EngineError: Error { +// case canceled +//} extension SocketEngine { /// Delegate method for WebSocketDelegate. @@ -742,7 +744,11 @@ extension SocketEngine { /// - Parameters: /// - event: WS Event /// - _: - public func didReceive(event: WebSocketEvent, client _: WebSocket) { + public func didReceive(event: WebSocketEvent, client websocket: WebSocket) { + guard websocket === self.ws else { + DefaultSocketLogger.Logger.log("Ignoring websocket event from wrong client: \(event)", type: SocketEngine.logType) + return + } switch event { case let .connected(headers): wsConnected = true @@ -750,15 +756,21 @@ extension SocketEngine { websocketDidConnect() case .cancelled: wsConnected = false - websocketDidDisconnect(error: EngineError.canceled) + websocketDidDisconnect(reason: .websocketEngineCanceled) case let .disconnected(reason, code): wsConnected = false - websocketDidDisconnect(error: nil) + websocketDidDisconnect(reason: .socketError(.websocketEngineDisconnected(reason, Int(code)))) case let .text(msg): parseEngineMessage(msg) case let .binary(data): parseEngineData(data) + case let .error(error): + DefaultSocketLogger.Logger.error("didReceive WebSocket error \(error as Any)", type: "SocketEngine") + //share the error with the clients. + client?.engineDidError(error: .websocketEngineError(error)) + case _: + //TODO: Handle or log other cases? break } } diff --git a/Source/SocketIO/Engine/SocketEngineClient.swift b/Source/SocketIO/Engine/SocketEngineClient.swift index 903fa6d8..2cd92278 100644 --- a/Source/SocketIO/Engine/SocketEngineClient.swift +++ b/Source/SocketIO/Engine/SocketEngineClient.swift @@ -26,23 +26,23 @@ import Foundation /// Declares that a type will be a delegate to an engine. -@objc public protocol SocketEngineClient { +public protocol SocketEngineClient: AnyObject { // MARK: Methods /// Called when the engine errors. /// /// - parameter reason: The reason the engine errored. - func engineDidError(reason: String) + func engineDidError(error: SocketError) /// Called when the engine closes. /// /// - parameter reason: The reason that the engine closed. - func engineDidClose(reason: String) + func engineDidClose(reason: SocketConnectionChangeReason) /// Called when the engine opens. /// /// - parameter reason: The reason the engine opened. - func engineDidOpen(reason: String) + func engineDidOpen(reason: SocketConnectionChangeReason) /// Called when the engine receives a ping message. Only called in socket.io >3. func engineDidReceivePing() diff --git a/Source/SocketIO/Engine/SocketEnginePollable.swift b/Source/SocketIO/Engine/SocketEnginePollable.swift index a5ee0736..d6ffaff9 100644 --- a/Source/SocketIO/Engine/SocketEnginePollable.swift +++ b/Source/SocketIO/Engine/SocketEnginePollable.swift @@ -137,7 +137,7 @@ extension SocketEnginePollable { } if this.polling { - this.didError(reason: err?.localizedDescription ?? "Error") + this.didError(error: .urlSessionError(err)) } return @@ -173,17 +173,17 @@ extension SocketEnginePollable { DefaultSocketLogger.Logger.log("POSTing", type: "SocketEnginePolling") - doRequest(for: req) {[weak self] _, res, err in + doRequest(for: req) {[weak self] _, responseOpt, errorOpt in guard let this = self else { return } - guard let res = res as? HTTPURLResponse, res.statusCode == 200 else { - if let err = err { - DefaultSocketLogger.Logger.error(err.localizedDescription, type: "SocketEnginePolling") + guard let response = responseOpt as? HTTPURLResponse, response.statusCode == 200 else { + if let error = errorOpt { + DefaultSocketLogger.Logger.error(error.localizedDescription, type: "SocketEnginePolling") } else { - DefaultSocketLogger.Logger.error("Error flushing waiting posts", type: "SocketEnginePolling") + DefaultSocketLogger.Logger.error("Error flushing waiting posts: \((responseOpt as? HTTPURLResponse)?.statusCode ?? -1)", type: "SocketEnginePolling") } if this.polling { - this.didError(reason: err?.localizedDescription ?? "Error") + this.didError(error: .urlSessionError(errorOpt)) } return diff --git a/Source/SocketIO/Engine/SocketEngineSpec.swift b/Source/SocketIO/Engine/SocketEngineSpec.swift index 1eecffd3..696f3729 100644 --- a/Source/SocketIO/Engine/SocketEngineSpec.swift +++ b/Source/SocketIO/Engine/SocketEngineSpec.swift @@ -106,12 +106,12 @@ public protocol SocketEngineSpec: class { func connect() /// Called when an error happens during execution. Causes a disconnection. - func didError(reason: String) + func didError(error: SocketError) /// Disconnects from the server. /// /// - parameter reason: The reason for the disconnection. This is communicated up to the client. - func disconnect(reason: String) + func disconnect(reason: SocketConnectionChangeReason) /// Called to switch from HTTP long-polling to WebSockets. After calling this method the engine will be in /// WebSocket mode. diff --git a/Source/SocketIO/Error/SocketError.swift b/Source/SocketIO/Error/SocketError.swift new file mode 100644 index 00000000..9961abc0 --- /dev/null +++ b/Source/SocketIO/Error/SocketError.swift @@ -0,0 +1,40 @@ +// +// SocketError.swift +// Socket.IO-Client-Swift +// +// Created by Jake Lavenberg on 5/10/21. +// + +import Foundation +import Starscream + +public enum SocketConnectionChangeReason { + case socketError(_ error: SocketError) + case calledDisconnectSocket + case calledDisconnectManager + case calledReconnect + case gotDisconnectPacket + case managerDeinit + case addingNewEngine + case engineOpen + case engineCloseMessage(_ message: String) + case websocketEngineCanceled +} + +public enum SocketError: Error { + case triedEmittingWhenNotConnected + case autoReconnectFailed(_ attempt: Int) + case urlSessionBecameInvalid(_ error: NSError?) + case urlSessionError(_ error: Error?) + case triedOpeningWhileConnected +// case websocketError(_ error: WSError) + case openPacketUnparseable + case openPacketMissingSID + case engineUnknownMessage(_ message: String) + case engineErrorMessage(_ message: String) + case nsError(_ error: NSError) + case pingTimeout(_ actualTime: TimeInterval) + case pongsMissed(_ pongsMissed: Int) + case websocketEngineDisconnected(_ reason: String, _ code: Int) + case websocketEngineError(_ error: Error?) +} diff --git a/Source/SocketIO/Manager/SocketManager.swift b/Source/SocketIO/Manager/SocketManager.swift index c45c5f56..659915f9 100644 --- a/Source/SocketIO/Manager/SocketManager.swift +++ b/Source/SocketIO/Manager/SocketManager.swift @@ -79,6 +79,11 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat } } + + /// If passed `true`, event message data will not be parsed, and all message events will be received with + /// `event` = "rawMessage" + public var disableEventMessageParsing = false + /// The engine for this manager. public var engine: SocketEngineSpec? @@ -164,7 +169,7 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat deinit { DefaultSocketLogger.Logger.log("Manager is being released", type: SocketManager.logType) - engine?.disconnect(reason: "Manager Deinit") + engine?.disconnect(reason: .managerDeinit) } // MARK: Methods @@ -176,7 +181,7 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat self.engine?.client = nil // Close old engine so it will not leak because of URLSession if in polling mode - self.engine?.disconnect(reason: "Adding new engine") + self.engine?.disconnect(reason: .addingNewEngine) } engine = SocketEngine(client: self, url: socketURL, config: config) @@ -228,7 +233,7 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat /// Called when the manager has disconnected from socket.io. /// /// - parameter reason: The reason for the disconnection. - open func didDisconnect(reason: String) { + open func didDisconnect(reason: SocketConnectionChangeReason) { forAll {socket in socket.didDisconnect(reason: reason) } @@ -240,7 +245,7 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat status = .disconnected - engine?.disconnect(reason: "Disconnect") + engine?.disconnect(reason: .calledDisconnectManager) } /// Disconnects the given socket. @@ -252,7 +257,7 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat open func disconnectSocket(_ socket: SocketIOClient) { engine?.send("1\(socket.nsp),", withData: []) - socket.didDisconnect(reason: "Namespace leave") + socket.didDisconnect(reason: .calledDisconnectSocket) } /// Disconnects the socket associated with `forNamespace`. @@ -301,13 +306,13 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat /// Called when the engine closes. /// /// - parameter reason: The reason that the engine closed. - open func engineDidClose(reason: String) { + open func engineDidClose(reason: SocketConnectionChangeReason) { handleQueue.async { self._engineDidClose(reason: reason) } } - private func _engineDidClose(reason: String) { + private func _engineDidClose(reason: SocketConnectionChangeReason) { waitingPackets.removeAll() if status != .disconnected { @@ -322,31 +327,31 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat } } - /// Called when the engine errors. + /// Called when the engine errors. Emits the .error event /// /// - parameter reason: The reason the engine errored. - open func engineDidError(reason: String) { + open func engineDidError(error: SocketError) { handleQueue.async { - self._engineDidError(reason: reason) + self._engineDidError(error: error) } } - private func _engineDidError(reason: String) { - DefaultSocketLogger.Logger.error("\(reason)", type: SocketManager.logType) + private func _engineDidError(error: SocketError) { + DefaultSocketLogger.Logger.error("\(error)", type: SocketManager.logType) - emitAll(clientEvent: .error, data: [reason]) + emitAll(clientEvent: .error, data: [error]) } /// Called when the engine opens. /// /// - parameter reason: The reason the engine opened. - open func engineDidOpen(reason: String) { + open func engineDidOpen(reason: SocketConnectionChangeReason) { handleQueue.async { self._engineDidOpen(reason: reason) } } - private func _engineDidOpen(reason: String) { + private func _engineDidOpen(reason: SocketConnectionChangeReason) { DefaultSocketLogger.Logger.log("Engine opened \(reason)", type: SocketManager.logType) status = .connected @@ -468,7 +473,7 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat open func reconnect() { guard !reconnecting else { return } - engine?.disconnect(reason: "manual reconnect") + engine?.disconnect(reason: .calledReconnect) } /// Removes the socket from the manager's control. One of the disconnect methods should be called before calling this @@ -483,10 +488,10 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat return nsps.removeValue(forKey: socket.nsp) } - private func tryReconnect(reason: String) { + private func tryReconnect(reason: SocketConnectionChangeReason) { guard reconnecting else { return } - DefaultSocketLogger.Logger.log("Starting reconnect", type: SocketManager.logType) + DefaultSocketLogger.Logger.log("Starting reconnect because \(reason)", type: SocketManager.logType) // Set status to connecting and emit reconnect for all sockets forAll {socket in @@ -502,7 +507,7 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat guard reconnects && reconnecting && status != .disconnected else { return } if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts { - return didDisconnect(reason: "Reconnect Failed") + return didDisconnect(reason: .socketError(.autoReconnectFailed(currentReconnectAttempt+1))) } DefaultSocketLogger.Logger.log("Trying to reconnect", type: SocketManager.logType) @@ -539,6 +544,8 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat open func setConfigs(_ config: SocketIOClientConfiguration) { for option in config { switch option { + case let .disableEventMessageParsing(disable): + disableEventMessageParsing = disable case let .forceNew(new): forceNew = new case let .handleQueue(queue): diff --git a/Source/SocketIO/Manager/SocketManagerSpec.swift b/Source/SocketIO/Manager/SocketManagerSpec.swift index 87be545e..78e10176 100644 --- a/Source/SocketIO/Manager/SocketManagerSpec.swift +++ b/Source/SocketIO/Manager/SocketManagerSpec.swift @@ -51,6 +51,10 @@ public protocol SocketManagerSpec : AnyObject, SocketEngineClient { /// Returns the socket associated with the default namespace ("/"). var defaultSocket: SocketIOClient { get } + /// If passed `true`, event message data will not be parsed, all message events will be received with + /// `event` = "rawMessage", and the eventName will be ignored on `emit()`. + var disableEventMessageParsing: Bool { get set } + /// The engine for this manager. var engine: SocketEngineSpec? { get set } @@ -100,7 +104,7 @@ public protocol SocketManagerSpec : AnyObject, SocketEngineClient { /// Called when the manager has disconnected from socket.io. /// /// - parameter reason: The reason for the disconnection. - func didDisconnect(reason: String) + func didDisconnect(reason: SocketConnectionChangeReason) /// Disconnects the manager and all associated sockets. func disconnect() diff --git a/Source/SocketIO/Parse/SocketPacket.swift b/Source/SocketIO/Parse/SocketPacket.swift index 1d818dbb..9b223907 100644 --- a/Source/SocketIO/Parse/SocketPacket.swift +++ b/Source/SocketIO/Parse/SocketPacket.swift @@ -58,6 +58,9 @@ public struct SocketPacket : CustomStringConvertible { } } + /// don't do any additional encoding on non-binary events + private let disableEventMessageParsing: Bool + private let placeholders: Int /// A string representation of this packet. @@ -77,13 +80,14 @@ public struct SocketPacket : CustomStringConvertible { } init(type: PacketType, data: [Any] = [Any](), id: Int = -1, nsp: String, placeholders: Int = 0, - binary: [Data] = [Data]()) { + binary: [Data] = [Data](), disableEventMessageParsing: Bool = false) { self.data = data self.id = id self.nsp = nsp self.type = type self.placeholders = placeholders self.binary = binary + self.disableEventMessageParsing = disableEventMessageParsing } mutating func addData(_ data: Data) -> Bool { @@ -102,6 +106,10 @@ public struct SocketPacket : CustomStringConvertible { } private func completeMessage(_ message: String) -> String { + if type == .event && disableEventMessageParsing { + return message + (args.first as? String ?? "[]") + } + guard data.count != 0 else { return message + "[]" } guard let jsonSend = try? data.toJSON(), let jsonString = String(data: jsonSend, encoding: .utf8) else { DefaultSocketLogger.Logger.error("Error creating JSON object in SocketPacket.completeMessage", @@ -207,14 +215,15 @@ extension SocketPacket { } } - static func packetFromEmit(_ items: [Any], id: Int, nsp: String, ack: Bool, checkForBinary: Bool = true) -> SocketPacket { + static func packetFromEmit(_ items: [Any], id: Int, nsp: String, ack: Bool, checkForBinary: Bool = true, disableEventMessageParsing: Bool = false) -> SocketPacket { if checkForBinary { let (parsedData, binary) = deconstructData(items) return SocketPacket(type: findType(binary.count, ack: ack), data: parsedData, id: id, nsp: nsp, - binary: binary) + binary: binary, disableEventMessageParsing: disableEventMessageParsing) } else { - return SocketPacket(type: findType(0, ack: ack), data: items, id: id, nsp: nsp) + return SocketPacket(type: findType(0, ack: ack), data: items, id: id, nsp: nsp, + disableEventMessageParsing: disableEventMessageParsing) } } } diff --git a/Source/SocketIO/Parse/SocketParsable.swift b/Source/SocketIO/Parse/SocketParsable.swift index 4462ce2d..bef53943 100644 --- a/Source/SocketIO/Parse/SocketParsable.swift +++ b/Source/SocketIO/Parse/SocketParsable.swift @@ -24,6 +24,9 @@ import Foundation /// Defines that a type will be able to parse socket.io-protocol messages. public protocol SocketParsable : AnyObject { + /// if `true`, disable parsing event payloads + var disableEventMessageParsing: Bool { get } + // MARK: Methods /// Called when the engine has received some binary data that should be attached to a packet. @@ -118,6 +121,10 @@ public extension SocketParsable where Self: SocketManagerSpec & SocketDataBuffer var dataArray = String(message.utf16[message.utf16.index(reader.currentIndex, offsetBy: 1)...])! + if type == .event, self.disableEventMessageParsing { + return SocketPacket(type: type, data: ["rawMessage", dataArray], id: Int(idString) ?? -1, nsp: namespace, placeholders: placeholders) + } + if (type == .error || type == .connect) && !dataArray.hasPrefix("[") && !dataArray.hasSuffix("]") { dataArray = "[" + dataArray + "]" }