Merge branch 'development'

* development:
  Update readme to reflect that 10.0+ only support socket.io 2.0+
  Remove more doubleEncode stuff
  Remove doubleEncodeUTF8 from NSDictionary translation
  Fix unicode for socket.io 2.0
  Use guard statements in polling
  More Objective-c tests
  Use Data instead of NSData
  code style
  Make addHeaders inout
This commit is contained in:
Erik 2017-05-27 12:01:34 -04:00
commit 033a509541
No known key found for this signature in database
GPG Key ID: 4930B7C5FBC1A69D
12 changed files with 157 additions and 158 deletions

View File

@ -51,7 +51,7 @@ SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{
``` ```
## Features ## Features
- Supports socket.io 1.0+ - Supports socket.io 2.0+ (For socket.io 1.0 use v9.x)
- Supports binary - Supports binary
- Supports Polling and WebSockets - Supports Polling and WebSockets
- Supports TLS/SSL - Supports TLS/SSL

View File

@ -17,28 +17,28 @@ class SocketEngineTest: XCTestCase {
super.setUp() super.setUp()
client = SocketIOClient(socketURL: URL(string: "http://localhost")!) client = SocketIOClient(socketURL: URL(string: "http://localhost")!)
engine = SocketEngine(client: client, url: URL(string: "http://localhost")!, options: nil) engine = SocketEngine(client: client, url: URL(string: "http://localhost")!, options: nil)
client.setTestable() client.setTestable()
} }
func testBasicPollingMessage() { func testBasicPollingMessage() {
let expect = expectation(description: "Basic polling test") let expect = expectation(description: "Basic polling test")
client.on("blankTest") {data, ack in client.on("blankTest") {data, ack in
expect.fulfill() expect.fulfill()
} }
engine.parsePollingMessage("15:42[\"blankTest\"]") engine.parsePollingMessage("15:42[\"blankTest\"]")
waitForExpectations(timeout: 3, handler: nil) waitForExpectations(timeout: 3, handler: nil)
} }
func testTwoPacketsInOnePollTest() { func testTwoPacketsInOnePollTest() {
let finalExpectation = expectation(description: "Final packet in poll test") let finalExpectation = expectation(description: "Final packet in poll test")
var gotBlank = false var gotBlank = false
client.on("blankTest") {data, ack in client.on("blankTest") {data, ack in
gotBlank = true gotBlank = true
} }
client.on("stringTest") {data, ack in client.on("stringTest") {data, ack in
if let str = data[0] as? String, gotBlank { if let str = data[0] as? String, gotBlank {
if str == "hello" { if str == "hello" {
@ -46,44 +46,46 @@ class SocketEngineTest: XCTestCase {
} }
} }
} }
engine.parsePollingMessage("15:42[\"blankTest\"]24:42[\"stringTest\",\"hello\"]") engine.parsePollingMessage("15:42[\"blankTest\"]24:42[\"stringTest\",\"hello\"]")
waitForExpectations(timeout: 3, handler: nil) waitForExpectations(timeout: 3, handler: nil)
} }
func testEngineDoesErrorOnUnknownTransport() { func testEngineDoesErrorOnUnknownTransport() {
let finalExpectation = expectation(description: "Unknown Transport") let finalExpectation = expectation(description: "Unknown Transport")
client.on("error") {data, ack in client.on("error") {data, ack in
if let error = data[0] as? String, error == "Unknown transport" { if let error = data[0] as? String, error == "Unknown transport" {
finalExpectation.fulfill() finalExpectation.fulfill()
} }
} }
engine.parseEngineMessage("{\"code\": 0, \"message\": \"Unknown transport\"}", fromPolling: false) engine.parseEngineMessage("{\"code\": 0, \"message\": \"Unknown transport\"}")
waitForExpectations(timeout: 3, handler: nil) waitForExpectations(timeout: 3, handler: nil)
} }
func testEngineDoesErrorOnUnknownMessage() { func testEngineDoesErrorOnUnknownMessage() {
let finalExpectation = expectation(description: "Engine Errors") let finalExpectation = expectation(description: "Engine Errors")
client.on("error") {data, ack in client.on("error") {data, ack in
finalExpectation.fulfill() finalExpectation.fulfill()
} }
engine.parseEngineMessage("afafafda", fromPolling: false) engine.parseEngineMessage("afafafda")
waitForExpectations(timeout: 3, handler: nil) waitForExpectations(timeout: 3, handler: nil)
} }
func testEngineDecodesUTF8Properly() { func testEngineDecodesUTF8Properly() {
let expect = expectation(description: "Engine Decodes utf8") let expect = expectation(description: "Engine Decodes utf8")
client.on("stringTest") {data, ack in client.on("stringTest") {data, ack in
XCTAssertEqual(data[0] as? String, "lïne one\nlīne \rtwo", "Failed string test") XCTAssertEqual(data[0] as? String, "lïne one\nlīne \rtwo𦅙𦅛", "Failed string test")
expect.fulfill() expect.fulfill()
} }
engine.parsePollingMessage("41:42[\"stringTest\",\"lïne one\\nlÄ«ne \\rtwo\"]") let stringMessage = "42[\"stringTest\",\"lïne one\\nlīne \\rtwo𦅙𦅛\"]"
engine.parsePollingMessage("\(stringMessage.utf16.count):\(stringMessage)")
waitForExpectations(timeout: 3, handler: nil) waitForExpectations(timeout: 3, handler: nil)
} }
@ -102,23 +104,23 @@ class SocketEngineTest: XCTestCase {
XCTAssertEqual(engine.urlPolling.query, "transport=polling&b64=1&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D") XCTAssertEqual(engine.urlPolling.query, "transport=polling&b64=1&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D")
XCTAssertEqual(engine.urlWebSocket.query, "transport=websocket&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D") XCTAssertEqual(engine.urlWebSocket.query, "transport=websocket&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D")
} }
func testBase64Data() { func testBase64Data() {
let expect = expectation(description: "Engine Decodes base64 data") let expect = expectation(description: "Engine Decodes base64 data")
let b64String = "b4aGVsbG8NCg==" let b64String = "b4aGVsbG8NCg=="
let packetString = "451-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]" let packetString = "451-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]"
client.on("test") {data, ack in client.on("test") {data, ack in
if let data = data[0] as? Data, let string = String(data: data, encoding: .utf8) { if let data = data[0] as? Data, let string = String(data: data, encoding: .utf8) {
XCTAssertEqual(string, "hello") XCTAssertEqual(string, "hello")
} }
expect.fulfill() expect.fulfill()
} }
engine.parseEngineMessage(packetString, fromPolling: false) engine.parseEngineMessage(packetString)
engine.parseEngineMessage(b64String, fromPolling: false) engine.parseEngineMessage(b64String)
waitForExpectations(timeout: 3, handler: nil) waitForExpectations(timeout: 3, handler: nil)
} }
} }

View File

@ -7,7 +7,9 @@
// Merely tests whether the Objective-C api breaks // Merely tests whether the Objective-C api breaks
// //
#import <XCTest/XCTest.h> @import Dispatch;
@import Foundation;
@import XCTest;
@import SocketIO; @import SocketIO;
@interface SocketObjectiveCTest : XCTestCase @interface SocketObjectiveCTest : XCTestCase
@ -24,20 +26,66 @@
self.socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{@"log": @NO, @"forcePolling": @YES}]; self.socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{@"log": @NO, @"forcePolling": @YES}];
} }
- (void)testProperties {
NSURL* url = nil;
url = self.socket.socketURL;
self.socket.forceNew = false;
self.socket.handleQueue = dispatch_get_main_queue();
self.socket.nsp = @"/objective-c";
self.socket.reconnects = false;
self.socket.reconnectWait = 1;
}
- (void)testOnSyntax { - (void)testOnSyntax {
[self.socket on:@"someCallback" callback:^(NSArray* data, SocketAckEmitter* ack) { [self.socket on:@"someCallback" callback:^(NSArray* data, SocketAckEmitter* ack) {
[ack with:@[@1]]; [ack with:@[@1]];
}]; }];
} }
- (void)testConnectSyntax {
[self.socket connect];
}
- (void)testConnectTimeoutAfterSyntax {
[self.socket connectWithTimeoutAfter:1 withHandler: ^() { }];
}
- (void)testDisconnectSyntax {
[self.socket disconnect];
}
- (void)testLeaveNamespaceSyntax {
[self.socket leaveNamespace];
}
- (void)testJoinNamespaceSyntax {
[self.socket joinNamespace:@"/objective-c"];
}
- (void)testOnAnySyntax {
[self.socket onAny:^(SocketAnyEvent* any) {
NSString* event = any.event;
NSArray* data = any.items;
[self.socket emit:event with:data];
}];
}
- (void)testReconnectSyntax {
[self.socket reconnect];
}
- (void)testRemoveAllHandlersSyntax {
[self.socket removeAllHandlers];
}
- (void)testEmitSyntax { - (void)testEmitSyntax {
[self.socket emit:@"testEmit" with:@[@YES]]; [self.socket emit:@"testEmit" with:@[@YES]];
} }
- (void)testEmitWithAckSyntax { - (void)testEmitWithAckSyntax {
[[self.socket emitWithAck:@"testAckEmit" with:@[@YES]] timingOutAfter:0 callback:^(NSArray* data) { [[self.socket emitWithAck:@"testAckEmit" with:@[@YES]] timingOutAfter:0 callback:^(NSArray* data) { }];
}];
} }
- (void)testOffSyntax { - (void)testOffSyntax {

View File

@ -66,10 +66,6 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
/// An array of HTTPCookies that are sent during the connection. /// An array of HTTPCookies that are sent during the connection.
public private(set) var cookies: [HTTPCookie]? public private(set) var cookies: [HTTPCookie]?
/// Set to `true` if using the node.js version of socket.io. The node.js version of socket.io
/// handles utf8 incorrectly.
public private(set) var doubleEncodeUTF8 = true
/// A dictionary of extra http headers that will be set during connection. /// A dictionary of extra http headers that will be set during connection.
public private(set) var extraHeaders: [String: String]? public private(set) var extraHeaders: [String: String]?
@ -153,8 +149,6 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
connectParams = params connectParams = params
case let .cookies(cookies): case let .cookies(cookies):
self.cookies = cookies self.cookies = cookies
case let .doubleEncodeUTF8(encode):
doubleEncodeUTF8 = encode
case let .extraHeaders(headers): case let .extraHeaders(headers):
extraHeaders = headers extraHeaders = headers
case let .sessionDelegate(delegate): case let .sessionDelegate(delegate):
@ -227,8 +221,8 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
// binary in base64 string // binary in base64 string
let noPrefix = message[message.index(message.startIndex, offsetBy: 2)..<message.endIndex] let noPrefix = message[message.index(message.startIndex, offsetBy: 2)..<message.endIndex]
if let data = NSData(base64Encoded: noPrefix, options: .ignoreUnknownCharacters) { if let data = Data(base64Encoded: noPrefix, options: .ignoreUnknownCharacters) {
client?.parseEngineBinaryData(data as Data) client?.parseEngineBinaryData(data)
} }
} }
@ -511,11 +505,10 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
/// - parameter message: The message to parse. /// - parameter message: The message to parse.
/// - parameter fromPolling: Whether this message is from long-polling. /// - parameter fromPolling: Whether this message is from long-polling.
/// If `true` we might have to fix utf8 encoding. /// If `true` we might have to fix utf8 encoding.
public func parseEngineMessage(_ message: String, fromPolling: Bool) { public func parseEngineMessage(_ message: String) {
DefaultSocketLogger.Logger.log("Got message: %@", type: logType, args: message) DefaultSocketLogger.Logger.log("Got message: %@", type: logType, args: message)
let reader = SocketStringReader(message: message) let reader = SocketStringReader(message: message)
let fixedString: String
if message.hasPrefix("b4") { if message.hasPrefix("b4") {
return handleBase64(message: message) return handleBase64(message: message)
@ -527,23 +520,17 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
return return
} }
if fromPolling && type != .noop && doubleEncodeUTF8 {
fixedString = fixDoubleUTF8(message)
} else {
fixedString = message
}
switch type { switch type {
case .message: case .message:
handleMessage(String(fixedString.characters.dropFirst())) handleMessage(String(message.characters.dropFirst()))
case .noop: case .noop:
handleNOOP() handleNOOP()
case .pong: case .pong:
handlePong(with: fixedString) handlePong(with: message)
case .open: case .open:
handleOpen(openData: String(fixedString.characters.dropFirst())) handleOpen(openData: String(message.characters.dropFirst()))
case .close: case .close:
handleClose(fixedString) handleClose(message)
default: default:
DefaultSocketLogger.Logger.log("Got unknown packet type", type: logType) DefaultSocketLogger.Logger.log("Got unknown packet type", type: logType)
} }
@ -643,17 +630,19 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
return return
} }
if websocket { guard websocket else {
connected = false
websocket = false
if let reason = error?.localizedDescription {
didError(reason: reason)
} else {
client?.engineDidClose(reason: "Socket Disconnected")
}
} else {
flushProbeWait() flushProbeWait()
return
}
connected = false
websocket = false
if let reason = error?.localizedDescription {
didError(reason: reason)
} else {
client?.engineDidClose(reason: "Socket Disconnected")
} }
} }
} }

View File

@ -71,9 +71,7 @@ public protocol SocketEnginePollable : SocketEngineSpec {
// Default polling methods // Default polling methods
extension SocketEnginePollable { extension SocketEnginePollable {
private func addHeaders(for req: URLRequest) -> URLRequest { private func addHeaders(to req: inout URLRequest) {
var req = req
if cookies != nil { if cookies != nil {
let headers = HTTPCookie.requestHeaderFields(with: cookies!) let headers = HTTPCookie.requestHeaderFields(with: cookies!)
req.allHTTPHeaderFields = headers req.allHTTPHeaderFields = headers
@ -84,8 +82,6 @@ extension SocketEnginePollable {
req.setValue(value, forHTTPHeaderField: headerName) req.setValue(value, forHTTPHeaderField: headerName)
} }
} }
return req
} }
func createRequestForPostWithPostWait() -> URLRequest { func createRequestForPostWithPostWait() -> URLRequest {
@ -94,9 +90,7 @@ extension SocketEnginePollable {
var postStr = "" var postStr = ""
for packet in postWait { for packet in postWait {
let len = packet.characters.count postStr += "\(packet.utf16.count):\(packet)"
postStr += "\(len):\(packet)"
} }
DefaultSocketLogger.Logger.log("Created POST string: %@", type: "SocketEnginePolling", args: postStr) DefaultSocketLogger.Logger.log("Created POST string: %@", type: "SocketEnginePolling", args: postStr)
@ -104,11 +98,10 @@ extension SocketEnginePollable {
var req = URLRequest(url: urlPollingWithSid) var req = URLRequest(url: urlPollingWithSid)
let postData = postStr.data(using: .utf8, allowLossyConversion: false)! let postData = postStr.data(using: .utf8, allowLossyConversion: false)!
req = addHeaders(for: req) addHeaders(to: &req)
req.httpMethod = "POST" req.httpMethod = "POST"
req.setValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type") req.setValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type")
req.httpBody = postData req.httpBody = postData
req.setValue(String(postData.count), forHTTPHeaderField: "Content-Length") req.setValue(String(postData.count), forHTTPHeaderField: "Content-Length")
@ -119,20 +112,16 @@ extension SocketEnginePollable {
/// ///
/// You shouldn't need to call this directly, the engine should automatically maintain a long-poll request. /// You shouldn't need to call this directly, the engine should automatically maintain a long-poll request.
public func doPoll() { public func doPoll() {
if websocket || waitingForPoll || !connected || closed { guard !websocket && !waitingForPoll && connected && !closed else { return }
return
}
var req = URLRequest(url: urlPollingWithSid) var req = URLRequest(url: urlPollingWithSid)
req = addHeaders(for: req) addHeaders(to: &req)
doLongPoll(for: req ) doLongPoll(for: req)
} }
func doRequest(for req: URLRequest, callbackWith callback: @escaping (Data?, URLResponse?, Error?) -> Void) { func doRequest(for req: URLRequest, callbackWith callback: @escaping (Data?, URLResponse?, Error?) -> Void) {
if !polling || closed || invalidated || fastUpgrade { guard polling && !closed && !invalidated && !fastUpgrade else { return }
return
}
DefaultSocketLogger.Logger.log("Doing polling %@ %@", type: "SocketEnginePolling", DefaultSocketLogger.Logger.log("Doing polling %@ %@", type: "SocketEnginePolling",
args: req.httpMethod ?? "", req) args: req.httpMethod ?? "", req)
@ -173,10 +162,10 @@ extension SocketEnginePollable {
} }
private func flushWaitingForPost() { private func flushWaitingForPost() {
if postWait.count == 0 || !connected { guard postWait.count != 0 && connected else { return }
return guard !websocket else {
} else if websocket {
flushWaitingForPostToWebSocket() flushWaitingForPostToWebSocket()
return return
} }
@ -211,13 +200,15 @@ extension SocketEnginePollable {
func parsePollingMessage(_ str: String) { func parsePollingMessage(_ str: String) {
guard str.characters.count != 1 else { return } guard str.characters.count != 1 else { return }
DefaultSocketLogger.Logger.log("Got poll message: %@", type: "SocketEnginePolling", args: str)
var reader = SocketStringReader(message: str) var reader = SocketStringReader(message: str)
while reader.hasNext { while reader.hasNext {
if let n = Int(reader.readUntilOccurence(of: ":")) { if let n = Int(reader.readUntilOccurence(of: ":")) {
parseEngineMessage(reader.read(count: n), fromPolling: true) parseEngineMessage(reader.read(count: n))
} else { } else {
parseEngineMessage(str, fromPolling: true) parseEngineMessage(str)
break break
} }
} }
@ -232,15 +223,8 @@ extension SocketEnginePollable {
/// - parameter withData: The data associated with this message. /// - parameter withData: The data associated with this message.
public func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data]) { public func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data]) {
DefaultSocketLogger.Logger.log("Sending poll: %@ as type: %@", type: "SocketEnginePolling", args: message, type.rawValue) DefaultSocketLogger.Logger.log("Sending poll: %@ as type: %@", type: "SocketEnginePolling", args: message, type.rawValue)
let fixedMessage: String
if doubleEncodeUTF8 { postWait.append(String(type.rawValue) + message)
fixedMessage = doubleEncodeUTF8(message)
} else {
fixedMessage = message
}
postWait.append(String(type.rawValue) + fixedMessage)
for data in datas { for data in datas {
if case let .right(bin) = createBinaryDataForSend(using: data) { if case let .right(bin) = createBinaryDataForSend(using: data) {

View File

@ -39,10 +39,6 @@ import Foundation
/// The connect parameters sent during a connect. /// The connect parameters sent during a connect.
var connectParams: [String: Any]? { get set } var connectParams: [String: Any]? { get set }
/// Set to `true` if using the node.js version of socket.io. The node.js version of socket.io
/// handles utf8 incorrectly.
var doubleEncodeUTF8: Bool { get }
/// An array of HTTPCookies that are sent during the connection. /// An array of HTTPCookies that are sent during the connection.
var cookies: [HTTPCookie]? { get } var cookies: [HTTPCookie]? { get }
@ -125,7 +121,7 @@ import Foundation
/// - parameter message: The message to parse. /// - parameter message: The message to parse.
/// - parameter fromPolling: Whether this message is from long-polling. /// - parameter fromPolling: Whether this message is from long-polling.
/// If `true` we might have to fix utf8 encoding. /// If `true` we might have to fix utf8 encoding.
func parseEngineMessage(_ message: String, fromPolling: Bool) func parseEngineMessage(_ message: String)
/// Writes a message to engine.io, independent of transport. /// Writes a message to engine.io, independent of transport.
/// ///
@ -165,24 +161,6 @@ extension SocketEngineSpec {
} }
} }
func doubleEncodeUTF8(_ string: String) -> String {
if let latin1 = string.data(using: String.Encoding.utf8),
let utf8 = NSString(data: latin1, encoding: String.Encoding.isoLatin1.rawValue) {
return utf8 as String
} else {
return string
}
}
func fixDoubleUTF8(_ string: String) -> String {
if let utf8 = string.data(using: String.Encoding.isoLatin1),
let latin1 = NSString(data: utf8, encoding: String.Encoding.utf8.rawValue) {
return latin1 as String
} else {
return string
}
}
/// Send an engine message (4) /// Send an engine message (4)
func send(_ msg: String, withData datas: [Data]) { func send(_ msg: String, withData datas: [Data]) {
write(msg, withType: .message, withData: datas) write(msg, withType: .message, withData: datas)

View File

@ -68,7 +68,7 @@ extension SocketEngineWebsocket {
/// Delegate method for when a message is received. /// Delegate method for when a message is received.
public func websocketDidReceiveMessage(socket: WebSocket, text: String) { public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
parseEngineMessage(text, fromPolling: false) parseEngineMessage(text)
} }
/// Delegate method for when binary is received. /// Delegate method for when binary is received.

View File

@ -48,8 +48,6 @@ extension NSDictionary {
return .connectParams(params) return .connectParams(params)
case let ("cookies", cookies as [HTTPCookie]): case let ("cookies", cookies as [HTTPCookie]):
return .cookies(cookies) return .cookies(cookies)
case let ("doubleEncodeUTF8", encode as Bool):
return .doubleEncodeUTF8(encode)
case let ("extraHeaders", headers as [String: String]): case let ("extraHeaders", headers as [String: String]):
return .extraHeaders(headers) return .extraHeaders(headers)
case let ("forceNew", force as Bool): case let ("forceNew", force as Bool):
@ -88,39 +86,39 @@ extension NSDictionary {
return nil return nil
} }
} }
func toSocketConfiguration() -> SocketIOClientConfiguration { func toSocketConfiguration() -> SocketIOClientConfiguration {
var options = [] as SocketIOClientConfiguration var options = [] as SocketIOClientConfiguration
for (rawKey, value) in self { for (rawKey, value) in self {
if let key = rawKey as? String, let opt = NSDictionary.keyValueToSocketIOClientOption(key: key, value: value) { if let key = rawKey as? String, let opt = NSDictionary.keyValueToSocketIOClientOption(key: key, value: value) {
options.insert(opt) options.insert(opt)
} }
} }
return options return options
} }
} }
extension String { extension String {
func toArray() throws -> [Any] { func toArray() throws -> [Any] {
guard let stringData = data(using: .utf8, allowLossyConversion: false) else { return [] } guard let stringData = data(using: .utf16, allowLossyConversion: false) else { return [] }
guard let array = try JSONSerialization.jsonObject(with: stringData, options: .mutableContainers) as? [Any] else { guard let array = try JSONSerialization.jsonObject(with: stringData, options: .mutableContainers) as? [Any] else {
throw JSONError.notArray throw JSONError.notArray
} }
return array return array
} }
func toNSDictionary() throws -> NSDictionary { func toNSDictionary() throws -> NSDictionary {
guard let binData = data(using: .utf8, allowLossyConversion: false) else { return [:] } guard let binData = data(using: .utf16, allowLossyConversion: false) else { return [:] }
guard let json = try JSONSerialization.jsonObject(with: binData, options: .allowFragments) as? NSDictionary else { guard let json = try JSONSerialization.jsonObject(with: binData, options: .allowFragments) as? NSDictionary else {
throw JSONError.notNSDictionary throw JSONError.notNSDictionary
} }
return json return json
} }
func urlEncode() -> String? { func urlEncode() -> String? {
return addingPercentEncoding(withAllowedCharacters: .allowedURLCharacterSet) return addingPercentEncoding(withAllowedCharacters: .allowedURLCharacterSet)
} }

View File

@ -237,7 +237,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
/// - parameter items: The items to send with this event. May be left out. /// - parameter items: The items to send with this event. May be left out.
open func emit(_ event: String, _ items: SocketData...) { open func emit(_ event: String, _ items: SocketData...) {
do { do {
emit(event, with: try items.map({ try $0.socketRepresentation() })) try emit(event, with: items.map({ try $0.socketRepresentation() }))
} catch let err { } catch let err {
DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)", DefaultSocketLogger.Logger.error("Error creating socketRepresentation for emit: \(event), \(items)",
type: logType) type: logType)

View File

@ -36,8 +36,8 @@ public enum SocketIOClientOption : ClientOption {
/// An array of cookies that will be sent during the initial connection. /// An array of cookies that will be sent during the initial connection.
case cookies([HTTPCookie]) case cookies([HTTPCookie])
/// The node.js socket.io currently does funky things to unicode when doing HTTP long-polling. Passing `true` in /// Deprecated
/// this option causes the client to try and fix any bad unicode that might be sent. @available(*, deprecated, message: "No longer needed in socket.io 2.0+")
case doubleEncodeUTF8(Bool) case doubleEncodeUTF8(Bool)
/// Any extra HTTP headers that should be sent during the initial connection. /// Any extra HTTP headers that should be sent during the initial connection.

View File

@ -107,7 +107,7 @@ extension SocketParsable where Self: SocketIOClientSpec {
} }
} }
var dataArray = message[message.characters.index(reader.currentIndex, offsetBy: 1)..<message.endIndex] var dataArray = String(message.utf16[message.utf16.index(reader.currentIndex, offsetBy: 1)..<message.utf16.endIndex])!
if type == .error && !dataArray.hasPrefix("[") && !dataArray.hasSuffix("]") { if type == .error && !dataArray.hasPrefix("[") && !dataArray.hasSuffix("]") {
dataArray = "[" + dataArray + "]" dataArray = "[" + dataArray + "]"

View File

@ -24,50 +24,50 @@
struct SocketStringReader { struct SocketStringReader {
let message: String let message: String
var currentIndex: String.Index var currentIndex: String.UTF16View.Index
var hasNext: Bool { var hasNext: Bool {
return currentIndex != message.endIndex return currentIndex != message.utf16.endIndex
} }
var currentCharacter: String { var currentCharacter: String {
return String(message[currentIndex]) return String(UnicodeScalar(message.utf16[currentIndex])!)
} }
init(message: String) { init(message: String) {
self.message = message self.message = message
currentIndex = message.startIndex currentIndex = message.utf16.startIndex
} }
@discardableResult @discardableResult
mutating func advance(by: Int) -> String.Index { mutating func advance(by: Int) -> String.UTF16View.Index {
currentIndex = message.characters.index(currentIndex, offsetBy: by) currentIndex = message.utf16.index(currentIndex, offsetBy: by)
return currentIndex return currentIndex
} }
mutating func read(count: Int) -> String { mutating func read(count: Int) -> String {
let readString = message[currentIndex..<message.characters.index(currentIndex, offsetBy: count)] let readString = String(message.utf16[currentIndex..<message.utf16.index(currentIndex, offsetBy: count)])!
advance(by: count) advance(by: count)
return readString return readString
} }
mutating func readUntilOccurence(of string: String) -> String { mutating func readUntilOccurence(of string: String) -> String {
let substring = message[currentIndex..<message.endIndex] let substring = message.utf16[currentIndex..<message.utf16.endIndex]
guard let foundRange = substring.range(of: string) else { guard let foundIndex = substring.index(of: string.utf16.first!) else {
currentIndex = message.endIndex currentIndex = message.utf16.endIndex
return substring return String(substring)!
} }
advance(by: message.characters.distance(from: message.characters.startIndex, to: foundRange.lowerBound) + 1) advance(by: substring.distance(from: substring.startIndex, to: foundIndex) + 1)
return substring.substring(to: foundRange.lowerBound) return String(substring[substring.startIndex..<foundIndex])!
} }
mutating func readUntilEnd() -> String { mutating func readUntilEnd() -> String {
return read(count: message.characters.distance(from: currentIndex, to: message.endIndex)) return read(count: message.utf16.distance(from: currentIndex, to: message.utf16.endIndex))
} }
} }