diff --git a/README.md b/README.md index 0143782..99fe812 100644 --- a/README.md +++ b/README.md @@ -147,24 +147,24 @@ All options are a case of SocketIOClientOption. To get the Objective-C Option, c ```swift case ConnectParams([String: AnyObject]) // Dictionary whose contents will be passed with the connection. -case Reconnects(Bool) // Whether to reconnect on server lose. Default is `true` -case ReconnectAttempts(Int) // How many times to reconnect. Default is `-1` (infinite tries) -case ReconnectWait(Int) // Amount of time to wait between reconnects. Default is `10` +case Cookies([NSHTTPCookie]) // An array of NSHTTPCookies. Passed during the handshake. Default is nil. +case DoubleEncodeUTF8(Bool) // Whether or not to double encode utf8. If using the node based server this should be true. Default is true. +case ExtraHeaders([String: String]) // Adds custom headers to the initial request. Default is nil. case ForcePolling(Bool) // `true` forces the client to use xhr-polling. Default is `false` case ForceNew(Bool) // Will a create a new engine for each connect. Useful if you find a bug in the engine related to reconnects case ForceWebsockets(Bool) // `true` forces the client to use WebSockets. Default is `false` -case Nsp(String) // The namespace to connect to. Must begin with /. Default is `/` -case Cookies([NSHTTPCookie]) // An array of NSHTTPCookies. Passed during the handshake. Default is nil. +case HandleQueue(dispatch_queue_t) // The dispatch queue that handlers are run on. Default is the main queue. case Log(Bool) // If `true` socket will log debug messages. Default is false. case Logger(SocketLogger) // Custom logger that conforms to SocketLogger. Will use the default logging otherwise. -case SessionDelegate(NSURLSessionDelegate) // Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs. Default is nil. +case Nsp(String) // The namespace to connect to. Must begin with /. Default is `/` case Path(String) // If the server uses a custom path. ex: `"/swift/"`. Default is `""` -case ExtraHeaders([String: String]) // Adds custom headers to the initial request. Default is nil. -case HandleQueue(dispatch_queue_t) // The dispatch queue that handlers are run on. Default is the main queue. -case VoipEnabled(Bool) // Only use this option if you're using the client with VoIP services. Changes the way the WebSocket is created. Default is false +case Reconnects(Bool) // Whether to reconnect on server lose. Default is `true` +case ReconnectAttempts(Int) // How many times to reconnect. Default is `-1` (infinite tries) +case ReconnectWait(Int) // Amount of time to wait between reconnects. Default is `10` +case SessionDelegate(NSURLSessionDelegate) // Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs. Default is nil. case Secure(Bool) // If the connection should use TLS. Default is false. case SelfSigned(Bool) // Sets WebSocket.selfSignedSSL (Don't do this, iOS will yell at you) - +case VoipEnabled(Bool) // Only use this option if you're using the client with VoIP services. Changes the way the WebSocket is created. Default is false ``` Methods ------- diff --git a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj index 88aa8d7..edd4894 100644 --- a/Socket.IO-Client-Swift.xcodeproj/project.pbxproj +++ b/Socket.IO-Client-Swift.xcodeproj/project.pbxproj @@ -60,11 +60,6 @@ 74171E8F1C10CD240062D398 /* SocketEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E571C10CD240062D398 /* SocketEventHandler.swift */; }; 74171E911C10CD240062D398 /* SocketEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E571C10CD240062D398 /* SocketEventHandler.swift */; }; 74171E921C10CD240062D398 /* SocketEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E571C10CD240062D398 /* SocketEventHandler.swift */; }; - 74171E931C10CD240062D398 /* SocketFixUTF8.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E581C10CD240062D398 /* SocketFixUTF8.swift */; }; - 74171E941C10CD240062D398 /* SocketFixUTF8.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E581C10CD240062D398 /* SocketFixUTF8.swift */; }; - 74171E951C10CD240062D398 /* SocketFixUTF8.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E581C10CD240062D398 /* SocketFixUTF8.swift */; }; - 74171E971C10CD240062D398 /* SocketFixUTF8.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E581C10CD240062D398 /* SocketFixUTF8.swift */; }; - 74171E981C10CD240062D398 /* SocketFixUTF8.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E581C10CD240062D398 /* SocketFixUTF8.swift */; }; 74171E991C10CD240062D398 /* SocketIOClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E591C10CD240062D398 /* SocketIOClient.swift */; }; 74171E9A1C10CD240062D398 /* SocketIOClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E591C10CD240062D398 /* SocketIOClient.swift */; }; 74171E9B1C10CD240062D398 /* SocketIOClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E591C10CD240062D398 /* SocketIOClient.swift */; }; @@ -180,7 +175,6 @@ 74171E551C10CD240062D398 /* SocketEnginePacketType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEnginePacketType.swift; path = Source/SocketEnginePacketType.swift; sourceTree = ""; }; 74171E561C10CD240062D398 /* SocketEngineSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEngineSpec.swift; path = Source/SocketEngineSpec.swift; sourceTree = ""; }; 74171E571C10CD240062D398 /* SocketEventHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEventHandler.swift; path = Source/SocketEventHandler.swift; sourceTree = ""; }; - 74171E581C10CD240062D398 /* SocketFixUTF8.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketFixUTF8.swift; path = Source/SocketFixUTF8.swift; sourceTree = ""; }; 74171E591C10CD240062D398 /* SocketIOClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClient.swift; path = Source/SocketIOClient.swift; sourceTree = ""; }; 74171E5A1C10CD240062D398 /* SocketIOClientOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientOption.swift; path = Source/SocketIOClientOption.swift; sourceTree = ""; }; 74171E5B1C10CD240062D398 /* SocketIOClientStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientStatus.swift; path = Source/SocketIOClientStatus.swift; sourceTree = ""; }; @@ -361,7 +355,6 @@ 74171E561C10CD240062D398 /* SocketEngineSpec.swift */, 740CA11F1C496EEB00CB98F4 /* SocketEngineWebsocket.swift */, 74171E571C10CD240062D398 /* SocketEventHandler.swift */, - 74171E581C10CD240062D398 /* SocketFixUTF8.swift */, 74171E591C10CD240062D398 /* SocketIOClient.swift */, 74171E5A1C10CD240062D398 /* SocketIOClientOption.swift */, 74171E5B1C10CD240062D398 /* SocketIOClientStatus.swift */, @@ -610,7 +603,6 @@ buildActionMask = 2147483647; files = ( 740CA1221C496EF700CB98F4 /* SocketEngineWebsocket.swift in Sources */, - 74171E931C10CD240062D398 /* SocketFixUTF8.swift in Sources */, 74171EA51C10CD240062D398 /* SocketIOClientStatus.swift in Sources */, 74171E751C10CD240062D398 /* SocketEngine.swift in Sources */, 74171E691C10CD240062D398 /* SocketAckManager.swift in Sources */, @@ -647,7 +639,6 @@ 74171EC41C10CD240062D398 /* SocketTypes.swift in Sources */, 74171E8E1C10CD240062D398 /* SocketEventHandler.swift in Sources */, 74171E7C1C10CD240062D398 /* SocketEngineClient.swift in Sources */, - 74171E941C10CD240062D398 /* SocketFixUTF8.swift in Sources */, 74171E821C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, 74171ECA1C10CD240062D398 /* SwiftRegex.swift in Sources */, 74171EB21C10CD240062D398 /* SocketPacket.swift in Sources */, @@ -670,7 +661,6 @@ files = ( 740CA1211C496EF200CB98F4 /* SocketEngineWebsocket.swift in Sources */, 7471CCEA1C39926300364B59 /* SocketClientSpec.swift in Sources */, - 74171E951C10CD240062D398 /* SocketFixUTF8.swift in Sources */, 74171EA71C10CD240062D398 /* SocketIOClientStatus.swift in Sources */, 74171E771C10CD240062D398 /* SocketEngine.swift in Sources */, 7420CB7A1C49629E00956AA4 /* SocketEnginePollable.swift in Sources */, @@ -713,7 +703,6 @@ files = ( 740CA1201C496EEB00CB98F4 /* SocketEngineWebsocket.swift in Sources */, 7471CCEB1C39926C00364B59 /* SocketClientSpec.swift in Sources */, - 74171E971C10CD240062D398 /* SocketFixUTF8.swift in Sources */, 74171EA91C10CD240062D398 /* SocketIOClientStatus.swift in Sources */, 74171E791C10CD240062D398 /* SocketEngine.swift in Sources */, 7420CB7B1C49629E00956AA4 /* SocketEnginePollable.swift in Sources */, @@ -749,7 +738,6 @@ 74171EC81C10CD240062D398 /* SocketTypes.swift in Sources */, 74171E921C10CD240062D398 /* SocketEventHandler.swift in Sources */, 74171E801C10CD240062D398 /* SocketEngineClient.swift in Sources */, - 74171E981C10CD240062D398 /* SocketFixUTF8.swift in Sources */, 74171E861C10CD240062D398 /* SocketEnginePacketType.swift in Sources */, 74171ECE1C10CD240062D398 /* SwiftRegex.swift in Sources */, 74171EB61C10CD240062D398 /* SocketPacket.swift in Sources */, diff --git a/Source/SocketEngine.swift b/Source/SocketEngine.swift index 9ba7e36..f08b67a 100644 --- a/Source/SocketEngine.swift +++ b/Source/SocketEngine.swift @@ -41,6 +41,7 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb public private(set) var closed = false public private(set) var connected = false public private(set) var cookies: [NSHTTPCookie]? + public private(set) var doubleEncodeUTF8 = true public private(set) var extraHeaders: [String: String]? public private(set) var fastUpgrade = false public private(set) var forcePolling = false @@ -88,18 +89,20 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb switch option { case let .ConnectParams(params): connectParams = params + case let .Cookies(cookies): + self.cookies = cookies + case let .DoubleEncodeUTF8(encode): + doubleEncodeUTF8 = encode + case let .ExtraHeaders(headers): + extraHeaders = headers case let .SessionDelegate(delegate): sessionDelegate = delegate case let .ForcePolling(force): forcePolling = force case let .ForceWebsockets(force): forceWebsockets = force - case let .Cookies(cookies): - self.cookies = cookies case let .Path(path): socketPath = path - case let .ExtraHeaders(headers): - extraHeaders = headers case let .VoipEnabled(enable): voipEnabled = enable case let .Secure(secure): @@ -432,7 +435,7 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb return } - if fromPolling && type != .Noop { + if fromPolling && type != .Noop && doubleEncodeUTF8 { fixedString = fixDoubleUTF8(message) } else { fixedString = message diff --git a/Source/SocketEnginePollable.swift b/Source/SocketEnginePollable.swift index b871e2d..249f4e4 100644 --- a/Source/SocketEnginePollable.swift +++ b/Source/SocketEnginePollable.swift @@ -206,21 +206,28 @@ extension SocketEnginePollable { /// Send polling message. /// Only call on emitQueue public func sendPollMessage(message: String, withType type: SocketEnginePacketType, withData datas: [NSData]) { - DefaultSocketLogger.Logger.log("Sending poll: %@ as type: %@", type: "SocketEnginePolling", args: message, type.rawValue) - let fixedMessage = doubleEncodeUTF8(message) - let strMsg = "\(type.rawValue)\(fixedMessage)" - - postWait.append(strMsg) - - for data in datas { - if case let .Right(bin) = createBinaryDataForSend(data) { - postWait.append(bin) - } - } - - if !waitingForPost { - flushWaitingForPost() + DefaultSocketLogger.Logger.log("Sending poll: %@ as type: %@", type: "SocketEnginePolling", args: message, type.rawValue) + let fixedMessage: String + + if doubleEncodeUTF8 { + fixedMessage = doubleEncodeUTF8(message) + } else { + fixedMessage = message + } + + let strMsg = "\(type.rawValue)\(fixedMessage)" + + postWait.append(strMsg) + + for data in datas { + if case let .Right(bin) = createBinaryDataForSend(data) { + postWait.append(bin) } + } + + if !waitingForPost { + flushWaitingForPost() + } } public func stopPolling() { diff --git a/Source/SocketEngineSpec.swift b/Source/SocketEngineSpec.swift index d89ce42..cc8c4c4 100644 --- a/Source/SocketEngineSpec.swift +++ b/Source/SocketEngineSpec.swift @@ -30,6 +30,7 @@ import Foundation var closed: Bool { get } var connected: Bool { get } var connectParams: [String: AnyObject]? { get set } + var doubleEncodeUTF8: Bool { get } var cookies: [NSHTTPCookie]? { get } var extraHeaders: [String: String]? { get } var fastUpgrade: Bool { get } @@ -89,6 +90,24 @@ extension SocketEngineSpec { } } + func doubleEncodeUTF8(string: String) -> String { + if let latin1 = string.dataUsingEncoding(NSUTF8StringEncoding), + utf8 = NSString(data: latin1, encoding: NSISOLatin1StringEncoding) { + return utf8 as String + } else { + return string + } + } + + func fixDoubleUTF8(string: String) -> String { + if let utf8 = string.dataUsingEncoding(NSISOLatin1StringEncoding), + latin1 = NSString(data: utf8, encoding: NSUTF8StringEncoding) { + return latin1 as String + } else { + return string + } + } + /// Send an engine message (4) func send(msg: String, withData datas: [NSData]) { write(msg, withType: .Message, withData: datas) diff --git a/Source/SocketFixUTF8.swift b/Source/SocketFixUTF8.swift deleted file mode 100644 index 5986003..0000000 --- a/Source/SocketFixUTF8.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// SocketFixUTF8.swift -// Socket.IO-Client-Swift -// -// Created by Erik Little on 3/16/15. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -import Foundation - -func fixDoubleUTF8(string: String) -> String { - if let utf8 = string.dataUsingEncoding(NSISOLatin1StringEncoding), - latin1 = NSString(data: utf8, encoding: NSUTF8StringEncoding) { - return latin1 as String - } else { - return string - } -} - -func doubleEncodeUTF8(string: String) -> String { - if let latin1 = string.dataUsingEncoding(NSUTF8StringEncoding), - utf8 = NSString(data: latin1, encoding: NSISOLatin1StringEncoding) { - return utf8 as String - } else { - return string - } -} diff --git a/Source/SocketIOClient.swift b/Source/SocketIOClient.swift index 1a17829..643ab7d 100644 --- a/Source/SocketIOClient.swift +++ b/Source/SocketIOClient.swift @@ -165,7 +165,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable } else { engine?.open() } - + guard timeoutAfter != 0 else { return } let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeoutAfter) * Int64(NSEC_PER_SEC)) diff --git a/Source/SocketIOClientOption.swift b/Source/SocketIOClientOption.swift index c43db7f..89a83bd 100644 --- a/Source/SocketIOClientOption.swift +++ b/Source/SocketIOClientOption.swift @@ -31,6 +31,7 @@ protocol ClientOption: CustomStringConvertible, Hashable { public enum SocketIOClientOption: ClientOption { case ConnectParams([String: AnyObject]) case Cookies([NSHTTPCookie]) + case DoubleEncodeUTF8(Bool) case ExtraHeaders([String: String]) case ForceNew(Bool) case ForcePolling(Bool) @@ -56,6 +57,8 @@ public enum SocketIOClientOption: ClientOption { description = "connectParams" case .Cookies: description = "cookies" + case .DoubleEncodeUTF8: + description = "doubleEncodeUTF8" case .ExtraHeaders: description = "extraHeaders" case .ForceNew: @@ -105,6 +108,8 @@ public enum SocketIOClientOption: ClientOption { value = params case let .Cookies(cookies): value = cookies + case let .DoubleEncodeUTF8(encode): + value = encode case let .ExtraHeaders(headers): value = headers case let .ForceNew(force): @@ -160,40 +165,42 @@ extension NSDictionary { switch (key, value) { case let ("connectParams", params as [String: AnyObject]): return .ConnectParams(params) - case let ("reconnects", reconnects as Bool): - return .Reconnects(reconnects) - case let ("reconnectAttempts", attempts as Int): - return .ReconnectAttempts(attempts) - case let ("reconnectWait", wait as Int): - return .ReconnectWait(wait) + case let ("cookies", cookies as [NSHTTPCookie]): + return .Cookies(cookies) + case let ("doubleEncodeUTF8", encode as Bool): + return .DoubleEncodeUTF8(encode) + case let ("extraHeaders", headers as [String: String]): + return .ExtraHeaders(headers) case let ("forceNew", force as Bool): return .ForceNew(force) case let ("forcePolling", force as Bool): return .ForcePolling(force) case let ("forceWebsockets", force as Bool): return .ForceWebsockets(force) - case let ("nsp", nsp as String): - return .Nsp(nsp) - case let ("cookies", cookies as [NSHTTPCookie]): - return .Cookies(cookies) + case let ("handleQueue", queue as dispatch_queue_t): + return .HandleQueue(queue) case let ("log", log as Bool): return .Log(log) case let ("logger", logger as SocketLogger): return .Logger(logger) - case let ("sessionDelegate", delegate as NSURLSessionDelegate): - return .SessionDelegate(delegate) + case let ("nsp", nsp as String): + return .Nsp(nsp) case let ("path", path as String): return .Path(path) - case let ("extraHeaders", headers as [String: String]): - return .ExtraHeaders(headers) - case let ("handleQueue", queue as dispatch_queue_t): - return .HandleQueue(queue) - case let ("voipEnabled", enable as Bool): - return .VoipEnabled(enable) + case let ("reconnects", reconnects as Bool): + return .Reconnects(reconnects) + case let ("reconnectAttempts", attempts as Int): + return .ReconnectAttempts(attempts) + case let ("reconnectWait", wait as Int): + return .ReconnectWait(wait) case let ("secure", secure as Bool): return .Secure(secure) case let ("selfSigned", selfSigned as Bool): return .SelfSigned(selfSigned) + case let ("sessionDelegate", delegate as NSURLSessionDelegate): + return .SessionDelegate(delegate) + case let ("voipEnabled", enable as Bool): + return .VoipEnabled(enable) default: return nil }