add option for toggling double encoding utf8

This commit is contained in:
Erik 2016-02-15 10:43:21 -05:00
parent 6fa5ce9168
commit fe9ee602e7
8 changed files with 84 additions and 104 deletions

View File

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

View File

@ -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 = "<group>"; };
74171E561C10CD240062D398 /* SocketEngineSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEngineSpec.swift; path = Source/SocketEngineSpec.swift; sourceTree = "<group>"; };
74171E571C10CD240062D398 /* SocketEventHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEventHandler.swift; path = Source/SocketEventHandler.swift; sourceTree = "<group>"; };
74171E581C10CD240062D398 /* SocketFixUTF8.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketFixUTF8.swift; path = Source/SocketFixUTF8.swift; sourceTree = "<group>"; };
74171E591C10CD240062D398 /* SocketIOClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClient.swift; path = Source/SocketIOClient.swift; sourceTree = "<group>"; };
74171E5A1C10CD240062D398 /* SocketIOClientOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientOption.swift; path = Source/SocketIOClientOption.swift; sourceTree = "<group>"; };
74171E5B1C10CD240062D398 /* SocketIOClientStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientStatus.swift; path = Source/SocketIOClientStatus.swift; sourceTree = "<group>"; };
@ -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 */,

View File

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

View File

@ -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() {

View File

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

View File

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

View File

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

View File

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