* 'swift-2' of https://github.com/socketio/socket.io-client-swift: use optionals instead of try/catch clean up code style disable locale test clean up using guard fix join namespace parse refactor, make use of try catch complete use of parser using parser for namespace use new parser for binary clean up code refactor clean up code socket parser Test SocketAckManagerTest
310 lines
9.3 KiB
Swift
310 lines
9.3 KiB
Swift
//
|
|
// SocketPacket.swift
|
|
// Socket.IO-Client-Swift
|
|
//
|
|
// Created by Erik Little on 1/18/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
|
|
|
|
struct SocketPacket {
|
|
private let placeholders: Int
|
|
private var currentPlace = 0
|
|
|
|
let nsp: String
|
|
let id: Int
|
|
let type: PacketType
|
|
|
|
enum PacketType: Int {
|
|
case Connect, Disconnect, Event, Ack, Error, BinaryEvent, BinaryAck
|
|
|
|
init?(str: String) {
|
|
if let int = Int(str), raw = PacketType(rawValue: int) {
|
|
self = raw
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
var args: [AnyObject]? {
|
|
var arr = data
|
|
|
|
if data.count == 0 {
|
|
return nil
|
|
} else {
|
|
if type == PacketType.Event || type == PacketType.BinaryEvent {
|
|
arr.removeAtIndex(0)
|
|
return arr
|
|
} else {
|
|
return arr
|
|
}
|
|
}
|
|
}
|
|
|
|
var binary: [NSData]
|
|
var data: [AnyObject]
|
|
var description: String {
|
|
return "SocketPacket {type: \(String(type.rawValue)); data: \(String(data)); id: \(id); placeholders: \(placeholders);}"
|
|
}
|
|
|
|
var event: String {
|
|
return data[0] as! String
|
|
}
|
|
|
|
var packetString: String {
|
|
return createPacketString()
|
|
}
|
|
|
|
init(type: SocketPacket.PacketType, data: [AnyObject] = [AnyObject](), id: Int = -1,
|
|
nsp: String, placeholders: Int = 0, binary: [NSData] = [NSData]()) {
|
|
self.data = data
|
|
self.id = id
|
|
self.nsp = nsp
|
|
self.type = type
|
|
self.placeholders = placeholders
|
|
self.binary = binary
|
|
}
|
|
|
|
mutating func addData(data: NSData) -> Bool {
|
|
if placeholders == currentPlace {
|
|
return true
|
|
}
|
|
|
|
binary.append(data)
|
|
currentPlace++
|
|
|
|
if placeholders == currentPlace {
|
|
currentPlace = 0
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
private func completeMessage(var message: String, ack: Bool) -> String {
|
|
if data.count == 0 {
|
|
return message + "]"
|
|
}
|
|
|
|
for arg in data {
|
|
if arg is NSDictionary || arg is [AnyObject] {
|
|
do {
|
|
let jsonSend = try NSJSONSerialization.dataWithJSONObject(arg,
|
|
options: NSJSONWritingOptions(rawValue: 0))
|
|
let jsonString = NSString(data: jsonSend, encoding: NSUTF8StringEncoding)
|
|
|
|
message += jsonString! as String + ","
|
|
} catch {
|
|
print("Error creating JSON object in SocketPacket.completeMessage")
|
|
}
|
|
} else if var str = arg as? String {
|
|
str = str["\n"] ~= "\\\\n"
|
|
str = str["\r"] ~= "\\\\r"
|
|
|
|
message += "\"\(str)\","
|
|
} else if arg is NSNull {
|
|
message += "null,"
|
|
} else {
|
|
message += "\(arg),"
|
|
}
|
|
}
|
|
|
|
if message != "" {
|
|
message.removeAtIndex(message.endIndex.predecessor())
|
|
}
|
|
|
|
return message + "]"
|
|
}
|
|
|
|
private func createAck() -> String {
|
|
let msg: String
|
|
|
|
if type == PacketType.Ack {
|
|
if nsp == "/" {
|
|
msg = "3\(id)["
|
|
} else {
|
|
msg = "3\(nsp),\(id)["
|
|
}
|
|
} else {
|
|
if nsp == "/" {
|
|
msg = "6\(binary.count)-\(id)["
|
|
} else {
|
|
msg = "6\(binary.count)-/\(nsp),\(id)["
|
|
}
|
|
}
|
|
|
|
return completeMessage(msg, ack: true)
|
|
}
|
|
|
|
|
|
private func createMessageForEvent() -> String {
|
|
let message: String
|
|
|
|
if type == PacketType.Event {
|
|
if nsp == "/" {
|
|
if id == -1 {
|
|
message = "2["
|
|
} else {
|
|
message = "2\(id)["
|
|
}
|
|
} else {
|
|
if id == -1 {
|
|
message = "2\(nsp),["
|
|
} else {
|
|
message = "2\(nsp),\(id)["
|
|
}
|
|
}
|
|
} else {
|
|
if nsp == "/" {
|
|
if id == -1 {
|
|
message = "5\(binary.count)-["
|
|
} else {
|
|
message = "5\(binary.count)-\(id)["
|
|
}
|
|
} else {
|
|
if id == -1 {
|
|
message = "5\(binary.count)-\(nsp),["
|
|
} else {
|
|
message = "5\(binary.count)-\(nsp),\(id)["
|
|
}
|
|
}
|
|
}
|
|
|
|
return completeMessage(message, ack: false)
|
|
}
|
|
|
|
private func createPacketString() -> String {
|
|
let str: String
|
|
|
|
if type == PacketType.Event || type == PacketType.BinaryEvent {
|
|
str = createMessageForEvent()
|
|
} else {
|
|
str = createAck()
|
|
}
|
|
|
|
return str
|
|
}
|
|
|
|
mutating func fillInPlaceholders() {
|
|
for i in 0..<data.count {
|
|
if let str = data[i] as? String, num = str["~~(\\d)"].groups() {
|
|
data[i] = binary[Int(num[1])!]
|
|
} else if data[i] is NSDictionary || data[i] is NSArray {
|
|
data[i] = _fillInPlaceholders(data[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
private mutating func _fillInPlaceholders(data: AnyObject) -> AnyObject {
|
|
if let str = data as? String {
|
|
if let num = str["~~(\\d)"].groups() {
|
|
return binary[Int(num[1])!]
|
|
} else {
|
|
return str
|
|
}
|
|
} else if let dict = data as? NSDictionary {
|
|
let newDict = NSMutableDictionary(dictionary: dict)
|
|
|
|
for (key, value) in dict {
|
|
newDict[key as! NSCopying] = _fillInPlaceholders(value)
|
|
}
|
|
|
|
return newDict
|
|
} else if let arr = data as? NSArray {
|
|
let newArr = NSMutableArray(array: arr)
|
|
|
|
for i in 0..<arr.count {
|
|
newArr[i] = _fillInPlaceholders(arr[i])
|
|
}
|
|
|
|
return newArr
|
|
} else {
|
|
return data
|
|
}
|
|
}
|
|
}
|
|
|
|
extension SocketPacket {
|
|
private static func findType(binCount: Int, ack: Bool) -> PacketType {
|
|
switch binCount {
|
|
case 0 where !ack:
|
|
return PacketType.Event
|
|
case 0 where ack:
|
|
return PacketType.Ack
|
|
case _ where !ack:
|
|
return PacketType.BinaryEvent
|
|
case _ where ack:
|
|
return PacketType.BinaryAck
|
|
default:
|
|
return PacketType.Error
|
|
}
|
|
}
|
|
|
|
static func packetFromEmit(items: [AnyObject], id: Int, nsp: String, ack: Bool) -> SocketPacket {
|
|
let (parsedData, binary) = deconstructData(items)
|
|
let packet = SocketPacket(type: findType(binary.count, ack: ack), data: parsedData,
|
|
id: id, nsp: nsp, placeholders: -1, binary: binary)
|
|
|
|
return packet
|
|
}
|
|
}
|
|
|
|
private extension SocketPacket {
|
|
static func shred(data: AnyObject, inout binary: [NSData]) -> AnyObject {
|
|
if let bin = data as? NSData {
|
|
let placeholder = ["_placeholder" :true, "num": binary.count]
|
|
|
|
binary.append(bin)
|
|
|
|
return placeholder
|
|
} else if var arr = data as? [AnyObject] {
|
|
for i in 0..<arr.count {
|
|
arr[i] = shred(arr[i], binary: &binary)
|
|
}
|
|
|
|
return arr
|
|
} else if let dict = data as? NSMutableDictionary {
|
|
for (key, value) in dict {
|
|
dict[key as! NSCopying] = shred(value, binary: &binary)
|
|
}
|
|
|
|
return dict
|
|
} else {
|
|
return data
|
|
}
|
|
}
|
|
|
|
static func deconstructData(var data: [AnyObject]) -> ([AnyObject], [NSData]) {
|
|
var binary = [NSData]()
|
|
|
|
for i in 0..<data.count {
|
|
if data[i] is NSArray || data[i] is NSDictionary {
|
|
data[i] = shred(data[i], binary: &binary)
|
|
} else if let bin = data[i] as? NSData {
|
|
data[i] = ["_placeholder" :true, "num": binary.count]
|
|
binary.append(bin)
|
|
}
|
|
}
|
|
|
|
return (data, binary)
|
|
}
|
|
}
|