update for latest swift

This commit is contained in:
Erik 2016-05-21 11:45:35 -04:00
commit f90ad8caeb
15 changed files with 230 additions and 231 deletions

View File

@ -62,7 +62,7 @@ class SocketBasicPacketTest: XCTestCase {
} }
func testBinaryEmit() { func testBinaryEmit() {
let expectedSendString = "51-[\"test\",{\"num\":0,\"_placeholder\":true}]" let expectedSendString = "51-[\"test\",{\"_placeholder\":true,\"num\":0}]"
let sendData = ["test", data] let sendData = ["test", data]
let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/", ack: false) let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/", ack: false)
@ -71,7 +71,7 @@ class SocketBasicPacketTest: XCTestCase {
} }
func testMultipleBinaryEmit() { func testMultipleBinaryEmit() {
let expectedSendString = "52-[\"test\",{\"data1\":{\"num\":0,\"_placeholder\":true},\"data2\":{\"num\":1,\"_placeholder\":true}}]" let expectedSendString = "52-[\"test\",{\"data1\":{\"_placeholder\":true,\"num\":0},\"data2\":{\"_placeholder\":true,\"num\":1}}]"
let sendData = ["test", ["data1": data, "data2": data2]] let sendData = ["test", ["data1": data, "data2": data2]]
let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/", ack: false) let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/", ack: false)
@ -88,7 +88,7 @@ class SocketBasicPacketTest: XCTestCase {
} }
func testEmitDataWithAck() { func testEmitDataWithAck() {
let expectedSendString = "51-0[\"test\",{\"num\":0,\"_placeholder\":true}]" let expectedSendString = "51-0[\"test\",{\"_placeholder\":true,\"num\":0}]"
let sendData = ["test", data] let sendData = ["test", data]
let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/", ack: false) let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/", ack: false)
@ -129,7 +129,7 @@ class SocketBasicPacketTest: XCTestCase {
} }
func testBinaryAck() { func testBinaryAck() {
let expectedSendString = "61-0[{\"num\":0,\"_placeholder\":true}]" let expectedSendString = "61-0[{\"_placeholder\":true,\"num\":0}]"
let sendData = [data] let sendData = [data]
let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/", ack: true) let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/", ack: true)
@ -138,7 +138,7 @@ class SocketBasicPacketTest: XCTestCase {
} }
func testMultipleBinaryAck() { func testMultipleBinaryAck() {
let expectedSendString = "62-0[{\"data2\":{\"num\":0,\"_placeholder\":true},\"data1\":{\"num\":1,\"_placeholder\":true}}]" let expectedSendString = "62-0[{\"data2\":{\"_placeholder\":true,\"num\":0},\"data1\":{\"_placeholder\":true,\"num\":1}}]"
let sendData = [["data1": data, "data2": data2]] let sendData = [["data1": data, "data2": data2]]
let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/", ack: true) let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/", ack: true)
@ -147,7 +147,7 @@ class SocketBasicPacketTest: XCTestCase {
} }
func testBinaryStringPlaceholderInMessage() { func testBinaryStringPlaceholderInMessage() {
let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"num\":1,\"_placeholder\":true}]" let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"_placeholder\":true,\"num\":1}]"
let socket = SocketIOClient(socketURL: NSURL()) let socket = SocketIOClient(socketURL: NSURL())
socket.setTestable() socket.setTestable()

View File

@ -54,7 +54,7 @@ class SocketNamespacePacketTest: XCTestCase {
} }
func testBinaryEmit() { func testBinaryEmit() {
let expectedSendString = "51-/swift,[\"test\",{\"num\":0,\"_placeholder\":true}]" let expectedSendString = "51-/swift,[\"test\",{\"_placeholder\":true,\"num\":0}]"
let sendData = ["test", data] let sendData = ["test", data]
let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/swift", ack: false) let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/swift", ack: false)
@ -63,7 +63,7 @@ class SocketNamespacePacketTest: XCTestCase {
} }
func testMultipleBinaryEmit() { func testMultipleBinaryEmit() {
let expectedSendString = "52-/swift,[\"test\",{\"data1\":{\"num\":0,\"_placeholder\":true},\"data2\":{\"num\":1,\"_placeholder\":true}}]" let expectedSendString = "52-/swift,[\"test\",{\"data1\":{\"_placeholder\":true,\"num\":0},\"data2\":{\"_placeholder\":true,\"num\":1}}]"
let sendData = ["test", ["data1": data, "data2": data2]] let sendData = ["test", ["data1": data, "data2": data2]]
let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/swift", ack: false) let packet = SocketPacket.packetFromEmit(items: sendData, id: -1, nsp: "/swift", ack: false)
@ -80,7 +80,7 @@ class SocketNamespacePacketTest: XCTestCase {
} }
func testEmitDataWithAck() { func testEmitDataWithAck() {
let expectedSendString = "51-/swift,0[\"test\",{\"num\":0,\"_placeholder\":true}]" let expectedSendString = "51-/swift,0[\"test\",{\"_placeholder\":true,\"num\":0}]"
let sendData = ["test", data] let sendData = ["test", data]
let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/swift", ack: false) let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/swift", ack: false)
@ -121,7 +121,7 @@ class SocketNamespacePacketTest: XCTestCase {
} }
func testBinaryAck() { func testBinaryAck() {
let expectedSendString = "61-/swift,0[{\"num\":0,\"_placeholder\":true}]" let expectedSendString = "61-/swift,0[{\"_placeholder\":true,\"num\":0}]"
let sendData = [data] let sendData = [data]
let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/swift", ack: true) let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/swift", ack: true)
@ -130,7 +130,7 @@ class SocketNamespacePacketTest: XCTestCase {
} }
func testMultipleBinaryAck() { func testMultipleBinaryAck() {
let expectedSendString = "62-/swift,0[{\"data2\":{\"num\":0,\"_placeholder\":true},\"data1\":{\"num\":1,\"_placeholder\":true}}]" let expectedSendString = "62-/swift,0[{\"data2\":{\"_placeholder\":true,\"num\":0},\"data1\":{\"_placeholder\":true,\"num\":1}}]"
let sendData = [["data1": data, "data2": data2]] let sendData = [["data1": data, "data2": data2]]
let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/swift", ack: true) let packet = SocketPacket.packetFromEmit(items: sendData, id: 0, nsp: "/swift", ack: true)

View File

@ -120,7 +120,7 @@ class SocketParserTest: XCTestCase {
func validateParseResult(message: String) { func validateParseResult(message: String) {
let validValues = SocketParserTest.packetTypes[message]! let validValues = SocketParserTest.packetTypes[message]!
let packet = testSocket.parseString(message) let packet = testSocket.parseString(message)
let type = message.substring(with: Range<String.Index>(message.startIndex..<message.startIndex.advanced(by: 1))) let type = message.substring(with: Range<String.Index>(message.startIndex..<message.characters.index(message.startIndex, offsetBy: 1)))
if case let .Right(packet) = packet { if case let .Right(packet) = packet {
XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!) XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!)
XCTAssertEqual(packet.nsp, validValues.0) XCTAssertEqual(packet.nsp, validValues.0)

View File

@ -26,12 +26,12 @@ import Foundation
public final class SocketAnyEvent : NSObject { public final class SocketAnyEvent : NSObject {
public let event: String public let event: String
public let items: NSArray? public let items: [AnyObject]?
override public var description: String { override public var description: String {
return "SocketAnyEvent: Event: \(event) items: \(items ?? nil)" return "SocketAnyEvent: Event: \(event) items: \(items ?? nil)"
} }
init(event: String, items: NSArray?) { init(event: String, items: [AnyObject]?) {
self.event = event self.event = event
self.items = items self.items = items
} }

View File

@ -25,9 +25,9 @@
import Foundation import Foundation
public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWebsocket { public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWebsocket {
public let emitQueue = dispatch_queue_create("com.socketio.engineEmitQueue", DISPATCH_QUEUE_SERIAL) public let emitQueue = dispatch_queue_create("com.socketio.engineEmitQueue", DISPATCH_QUEUE_SERIAL)!
public let handleQueue = dispatch_queue_create("com.socketio.engineHandleQueue", DISPATCH_QUEUE_SERIAL) public let handleQueue = dispatch_queue_create("com.socketio.engineHandleQueue", DISPATCH_QUEUE_SERIAL)!
public let parseQueue = dispatch_queue_create("com.socketio.engineParseQueue", DISPATCH_QUEUE_SERIAL) public let parseQueue = dispatch_queue_create("com.socketio.engineParseQueue", DISPATCH_QUEUE_SERIAL)!
public var connectParams: [String: AnyObject]? { public var connectParams: [String: AnyObject]? {
didSet { didSet {
@ -62,9 +62,6 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
private weak var sessionDelegate: NSURLSessionDelegate? private weak var sessionDelegate: NSURLSessionDelegate?
private typealias Probe = (msg: String, type: SocketEnginePacketType, data: [NSData])
private typealias ProbeWaitQueue = [Probe]
private let logType = "SocketEngine" private let logType = "SocketEngine"
private let url: NSURL private let url: NSURL
@ -74,6 +71,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
pongsMissedMax = Int(pingTimeout / (pingInterval ?? 25)) pongsMissedMax = Int(pingTimeout / (pingInterval ?? 25))
} }
} }
private var pongsMissed = 0 private var pongsMissed = 0
private var pongsMissedMax = 0 private var pongsMissedMax = 0
private var probeWait = ProbeWaitQueue() private var probeWait = ProbeWaitQueue()
@ -153,7 +151,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
private func checkIfMessageIsBase64Binary(_ message: String) -> Bool { private func checkIfMessageIsBase64Binary(_ message: String) -> Bool {
if message.hasPrefix("b4") { if message.hasPrefix("b4") {
// binary in base64 string // binary in base64 string
let noPrefix = message[message.startIndex.advanced(by: 2)..<message.endIndex] let noPrefix = message[message.characters.index(message.startIndex, offsetBy: 2)..<message.endIndex]
if let data = NSData(base64Encoded: noPrefix, if let data = NSData(base64Encoded: noPrefix,
options: .ignoreUnknownCharacters) { options: .ignoreUnknownCharacters) {
@ -387,7 +385,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
doPoll() doPoll()
} }
client?.engineDidOpen?(reason: "Connect") client?.engineDidOpen(reason: "Connect")
} }
} catch { } catch {
didError(error: "Error parsing open packet") didError(error: "Error parsing open packet")
@ -430,13 +428,15 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
switch type { switch type {
case .message: case .message:
handleMessage(fixedString[fixedString.startIndex.successor()..<fixedString.endIndex]) handleMessage(
fixedString[fixedString.characters.index(after: fixedString.characters.startIndex)..<fixedString.endIndex])
case .noop: case .noop:
handleNOOP() handleNOOP()
case .pong: case .pong:
handlePong(pongMessage: fixedString) handlePong(pongMessage: fixedString)
case .open: case .open:
handleOpen(openMessage: fixedString[fixedString.startIndex.successor()..<fixedString.endIndex]) handleOpen(openMessage:
fixedString[fixedString.characters.index(after: fixedString.characters.startIndex)..<fixedString.endIndex])
case .close: case .close:
handleClose(reason: fixedString) handleClose(reason: fixedString)
default: default:
@ -452,7 +452,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
polling = true polling = true
probing = false probing = false
invalidated = false invalidated = false
session = NSURLSession(configuration: .defaultSessionConfiguration(), session = NSURLSession(configuration: .default(),
delegate: sessionDelegate, delegate: sessionDelegate,
delegateQueue: NSOperationQueue()) delegateQueue: NSOperationQueue())
sid = "" sid = ""

View File

@ -28,7 +28,7 @@ import Foundation
@objc public protocol SocketEngineClient { @objc public protocol SocketEngineClient {
func engineDidError(reason: String) func engineDidError(reason: String)
func engineDidClose(reason: String) func engineDidClose(reason: String)
optional func engineDidOpen(reason: String) func engineDidOpen(reason: String)
func parseEngineMessage(_ msg: String) func parseEngineMessage(_ msg: String)
func parseEngineBinaryData(_ data: NSData) func parseEngineBinaryData(_ data: NSData)
} }

View File

@ -49,9 +49,9 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
return nsp + "#" + (engine?.sid ?? "") return nsp + "#" + (engine?.sid ?? "")
} }
private let emitQueue = dispatch_queue_create("com.socketio.emitQueue", DISPATCH_QUEUE_SERIAL) private let emitQueue = dispatch_queue_create("com.socketio.emitQueue", DISPATCH_QUEUE_SERIAL)!
private let logType = "SocketIOClient" private let logType = "SocketIOClient"
private let parseQueue = dispatch_queue_create("com.socketio.parseQueue", DISPATCH_QUEUE_SERIAL) private let parseQueue = dispatch_queue_create("com.socketio.parseQueue", DISPATCH_QUEUE_SERIAL)!
private var anyHandler: ((SocketAnyEvent) -> Void)? private var anyHandler: ((SocketAnyEvent) -> Void)?
private var currentReconnectAttempt = 0 private var currentReconnectAttempt = 0
@ -60,7 +60,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
private var reconnecting = false private var reconnecting = false
private(set) var currentAck = -1 private(set) var currentAck = -1
private(set) var handleQueue = dispatch_get_main_queue() private(set) var handleQueue = dispatch_get_main_queue()!
private(set) var reconnectAttempts = -1 private(set) var reconnectAttempts = -1
var waitingPackets = [SocketPacket]() var waitingPackets = [SocketPacket]()
@ -114,7 +114,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
} }
private func addEngine() -> SocketEngineSpec { private func addEngine() -> SocketEngineSpec {
DefaultSocketLogger.Logger.log("Adding engine", type: logType) DefaultSocketLogger.Logger.log("Adding engine", type: logType, args: "")
engine = SocketEngine(client: self, url: socketURL, options: options) engine = SocketEngine(client: self, url: socketURL, options: options)
@ -191,11 +191,10 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
DefaultSocketLogger.Logger.log("Disconnected: %@", type: logType, args: reason) DefaultSocketLogger.Logger.log("Disconnected: %@", type: logType, args: reason)
status = .disconnected status = .disconnected
reconnects = false
// Make sure the engine is actually dead. // Make sure the engine is actually dead.
engine?.disconnect(reason: reason) engine?.disconnect(reason: reason)
handleEvent("disconnect", data: [reason], isInternalMessage: true) handleEvent("disconnect", data: [reason as AnyObject], isInternalMessage: true)
} }
/// Disconnects the socket. Only reconnect the same socket if you know what you're doing. /// Disconnects the socket. Only reconnect the same socket if you know what you're doing.
@ -205,7 +204,6 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
DefaultSocketLogger.Logger.log("Closing socket", type: logType) DefaultSocketLogger.Logger.log("Closing socket", type: logType)
reconnects = false
didDisconnect(reason: "Disconnect") didDisconnect(reason: "Disconnect")
} }
@ -217,11 +215,11 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
/// Same as emit, but meant for Objective-C /// Same as emit, but meant for Objective-C
public func emit(_ event: String, with items: [AnyObject]) { public func emit(_ event: String, with items: [AnyObject]) {
guard status == .connected else { guard status == .connected else {
handleEvent("error", data: ["Tried emitting \(event) when not connected"], isInternalMessage: true) handleEvent("error", data: ["Tried emitting \(event) when not connected" as AnyObject], isInternalMessage: true)
return return
} }
_emit(data: [event] + items) _emit(data: [event as AnyObject] + items)
} }
/// Sends a message to the server, requesting an ack. Use the onAck method of SocketAckHandler to add /// Sends a message to the server, requesting an ack. Use the onAck method of SocketAckHandler to add
@ -232,7 +230,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
/// Same as emitWithAck, but for Objective-C /// Same as emitWithAck, but for Objective-C
public func emitWithAck(_ event: String, with items: [AnyObject]) -> OnAckCallback { public func emitWithAck(_ event: String, with items: [AnyObject]) -> OnAckCallback {
return createOnAck(items: [event] + items) return createOnAck(items: [event as AnyObject] + items)
} }
private func _emit(data: [AnyObject], ack: Int? = nil) { private func _emit(data: [AnyObject], ack: Int? = nil) {
@ -284,23 +282,25 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
public func engineDidError(reason: String) { public func engineDidError(reason: String) {
DefaultSocketLogger.Logger.error("%@", type: logType, args: reason) DefaultSocketLogger.Logger.error("%@", type: logType, args: reason)
handleEvent("error", data: [reason], isInternalMessage: true) handleEvent("error", data: [reason as AnyObject], isInternalMessage: true)
}
public func engineDidOpen(reason: String) {
DefaultSocketLogger.Logger.log(reason, type: "SocketEngineClient")
} }
// Called when the socket gets an ack for something it sent // Called when the socket gets an ack for something it sent
func handleAck(_ ack: Int, data: [AnyObject]) { func handleAck(_ ack: Int, data: [AnyObject]) {
guard status == .connected else { return } guard status == .connected else { return }
DefaultSocketLogger.Logger.log("Handling ack: %@ with data: %@", type: logType, args: ack, data ?? "") DefaultSocketLogger.Logger.log("Handling ack: %@ with data: %@", type: logType, args: ack, data)
ackHandlers.executeAck(ack, items: data) ackHandlers.executeAck(ack, items: data)
} }
/// Causes an event to be handled. Only use if you know what you're doing. /// Causes an event to be handled. Only use if you know what you're doing.
public func handleEvent(_ event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int = -1) { public func handleEvent(_ event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int = -1) {
guard status == .connected || isInternalMessage else { guard status == .connected || isInternalMessage else { return }
return
}
DefaultSocketLogger.Logger.log("Handling event: %@ with data: %@", type: logType, args: event, data ?? "") DefaultSocketLogger.Logger.log("Handling event: %@ with data: %@", type: logType, args: event, data ?? "")
@ -335,14 +335,14 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
public func off(event: String) { public func off(event: String) {
DefaultSocketLogger.Logger.log("Removing handler for event: %@", type: logType, args: event) DefaultSocketLogger.Logger.log("Removing handler for event: %@", type: logType, args: event)
handlers = handlers.filter { $0.event != event } handlers = handlers.filter({ $0.event != event })
} }
/// Removes a handler with the specified UUID gotten from an `on` or `once` /// Removes a handler with the specified UUID gotten from an `on` or `once`
public func off(id: NSUUID) { public func off(id: NSUUID) {
DefaultSocketLogger.Logger.log("Removing handler with id: %@", type: logType, args: id) DefaultSocketLogger.Logger.log("Removing handler with id: %@", type: logType, args: id)
handlers = handlers.filter { $0.id != id } handlers = handlers.filter({ $0.id != id })
} }
/// Adds a handler for an event. /// Adds a handler for an event.
@ -409,7 +409,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
private func tryReconnect(reason: String) { private func tryReconnect(reason: String) {
if reconnecting { if reconnecting {
DefaultSocketLogger.Logger.log("Starting reconnect", type: logType) DefaultSocketLogger.Logger.log("Starting reconnect", type: logType)
handleEvent("reconnect", data: [reason], isInternalMessage: true) handleEvent("reconnect", data: [reason as AnyObject], isInternalMessage: true)
_tryReconnect() _tryReconnect()
} }
@ -425,7 +425,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
} }
DefaultSocketLogger.Logger.log("Trying to reconnect", type: logType) DefaultSocketLogger.Logger.log("Trying to reconnect", type: logType)
handleEvent("reconnectAttempt", data: [reconnectAttempts - currentReconnectAttempt], handleEvent("reconnectAttempt", data: [(reconnectAttempts - currentReconnectAttempt) as AnyObject],
isInternalMessage: true) isInternalMessage: true)
currentReconnectAttempt += 1 currentReconnectAttempt += 1
@ -452,6 +452,6 @@ extension SocketIOClient {
} }
func emitTest(event: String, _ data: AnyObject...) { func emitTest(event: String, _ data: AnyObject...) {
self._emit(data: [event] + data) self._emit(data: [event as AnyObject] + data)
} }
} }

View File

@ -105,43 +105,43 @@ public enum SocketIOClientOption : ClientOption {
switch self { switch self {
case let .connectParams(params): case let .connectParams(params):
value = params value = params as AnyObject
case let .cookies(cookies): case let .cookies(cookies):
value = cookies value = cookies as AnyObject
case let .doubleEncodeUTF8(encode): case let .doubleEncodeUTF8(encode):
value = encode value = encode as AnyObject
case let .extraHeaders(headers): case let .extraHeaders(headers):
value = headers value = headers as AnyObject
case let .forceNew(force): case let .forceNew(force):
value = force value = force as AnyObject
case let .forcePolling(force): case let .forcePolling(force):
value = force value = force as AnyObject
case let .forceWebsockets(force): case let .forceWebsockets(force):
value = force value = force as AnyObject
case let .handleQueue(queue): case let .handleQueue(queue):
value = queue value = queue as AnyObject
case let .log(log): case let .log(log):
value = log value = log as AnyObject
case let .logger(logger): case let .logger(logger):
value = logger value = logger as AnyObject
case let .nsp(nsp): case let .nsp(nsp):
value = nsp value = nsp as AnyObject
case let .path(path): case let .path(path):
value = path value = path as AnyObject
case let .reconnects(reconnects): case let .reconnects(reconnects):
value = reconnects value = reconnects as AnyObject
case let .reconnectAttempts(attempts): case let .reconnectAttempts(attempts):
value = attempts value = attempts as AnyObject
case let .reconnectWait(wait): case let .reconnectWait(wait):
value = wait value = wait as AnyObject
case let .secure(secure): case let .secure(secure):
value = secure value = secure as AnyObject
case let .selfSigned(signed): case let .selfSigned(signed):
value = signed value = signed as AnyObject
case let .sessionDelegate(delegate): case let .sessionDelegate(delegate):
value = delegate value = delegate as AnyObject
case let .voipEnabled(enabled): case let .voipEnabled(enabled):
value = enabled value = enabled as AnyObject
} }
return value return value

View File

@ -38,6 +38,6 @@ extension SocketIOClientSpec {
func didError(reason: String) { func didError(reason: String) {
DefaultSocketLogger.Logger.error("%@", type: "SocketIOClient", args: reason) DefaultSocketLogger.Logger.error("%@", type: "SocketIOClient", args: reason)
handleEvent("error", data: [reason], isInternalMessage: true, withAck: -1) handleEvent("error", data: [reason as AnyObject], isInternalMessage: true, withAck: -1)
} }
} }

View File

@ -29,28 +29,25 @@ public protocol SocketLogger : class {
var log: Bool { get set } var log: Bool { get set }
/// Normal log messages /// Normal log messages
func log(_ message: String, type: String, args: AnyObject...) func log(_ message: String, type: String, args: Any...)
/// Error Messages /// Error Messages
func error(_ message: String, type: String, args: AnyObject...) func error(_ message: String, type: String, args: Any...)
} }
public extension SocketLogger { public extension SocketLogger {
func log(_ message: String, type: String, args: AnyObject...) { func log(_ message: String, type: String, args: Any...) {
abstractLog("LOG", message: message, type: type, args: args) abstractLog("LOG", message: message, type: type, args: args)
} }
func error(_ message: String, type: String, args: AnyObject...) { func error(_ message: String, type: String, args: Any...) {
abstractLog("ERROR", message: message, type: type, args: args) abstractLog("ERROR", message: message, type: type, args: args)
} }
private func abstractLog(_ logType: String, message: String, type: String, args: [AnyObject]) { private func abstractLog(_ logType: String, message: String, type: String, args: [Any]) {
guard log else { return } guard log else { return }
let newArgs = args.map({arg -> CVarArg in String(arg)}) NSLog("\(logType) \(type): \(args)")
let replaced = String(format: message, arguments: newArgs)
NSLog("%@ %@: %@", logType, type, replaced)
} }
} }

View File

@ -61,7 +61,7 @@ struct SocketPacket {
return createPacketString() return createPacketString()
} }
init(type: SocketPacket.PacketType, data: [AnyObject] = [AnyObject](), id: Int = -1, init(type: PacketType, data: [AnyObject] = [AnyObject](), id: Int = -1,
nsp: String, placeholders: Int = 0, binary: [NSData] = [NSData]()) { nsp: String, placeholders: Int = 0, binary: [NSData] = [NSData]()) {
self.data = data self.data = data
self.id = id self.id = id
@ -94,10 +94,10 @@ struct SocketPacket {
} }
do { do {
let jsonSend = try NSJSONSerialization.data(withJSONObject: data, let jsonSend = try NSJSONSerialization.data(withJSONObject: data as AnyObject,
options: NSJSONWritingOptions(rawValue: 0)) options: NSJSONWritingOptions(rawValue: 0))
guard let jsonString = String(data: jsonSend, encoding: NSUTF8StringEncoding) else { guard let jsonString = String(data: jsonSend, encoding: NSUTF8StringEncoding) else {
return "[]" return message + "[]"
} }
restOfMessage = jsonString restOfMessage = jsonString
@ -189,7 +189,7 @@ struct SocketPacket {
// Helper method that looks for placeholders // Helper method that looks for placeholders
// If object is a collection it will recurse // If object is a collection it will recurse
// Returns the object if it is not a placeholder string or the corresponding // Returns the object if it is not a placeholder or the corresponding
// binary data // binary data
private func _fillInPlaceholders(_ object: AnyObject) -> AnyObject { private func _fillInPlaceholders(_ object: AnyObject) -> AnyObject {
switch object { switch object {
@ -203,7 +203,7 @@ struct SocketPacket {
}) })
} }
case let arr as [AnyObject]: case let arr as [AnyObject]:
return arr.map(_fillInPlaceholders) return arr.map(_fillInPlaceholders) as AnyObject
default: default:
return object return object
} }
@ -211,7 +211,7 @@ struct SocketPacket {
} }
extension SocketPacket { extension SocketPacket {
private static func findType(binCount: Int, ack: Bool) -> PacketType { private static func findType(_ binCount: Int, ack: Bool) -> PacketType {
switch binCount { switch binCount {
case 0 where !ack: case 0 where !ack:
return .Event return .Event
@ -228,8 +228,8 @@ extension SocketPacket {
static func packetFromEmit(items: [AnyObject], id: Int, nsp: String, ack: Bool) -> SocketPacket { static func packetFromEmit(items: [AnyObject], id: Int, nsp: String, ack: Bool) -> SocketPacket {
let (parsedData, binary) = deconstructData(items) let (parsedData, binary) = deconstructData(items)
let packet = SocketPacket(type: findType(binCount: binary.count, ack: ack), data: parsedData, let packet = SocketPacket(type: findType(binary.count, ack: ack), data: parsedData,
id: id, nsp: nsp, placeholders: -1, binary: binary) id: id, nsp: nsp, binary: binary)
return packet return packet
} }
@ -238,14 +238,14 @@ extension SocketPacket {
private extension SocketPacket { private extension SocketPacket {
// Recursive function that looks for NSData in collections // Recursive function that looks for NSData in collections
static func shred(_ data: AnyObject, binary: inout [NSData]) -> AnyObject { static func shred(_ data: AnyObject, binary: inout [NSData]) -> AnyObject {
let placeholder = ["_placeholder": true, "num": binary.count] let placeholder = ["_placeholder": true, "num": binary.count as AnyObject]
switch data { switch data {
case let bin as NSData: case let bin as NSData:
binary.append(bin) binary.append(bin)
return placeholder return placeholder as AnyObject
case let arr as [AnyObject]: case let arr as [AnyObject]:
return arr.map({shred($0, binary: &binary)}) return arr.map({shred($0, binary: &binary)}) as AnyObject
case let dict as NSDictionary: case let dict as NSDictionary:
return dict.reduce(NSMutableDictionary(), combine: {cur, keyValue in return dict.reduce(NSMutableDictionary(), combine: {cur, keyValue in
cur[keyValue.0 as! NSCopying] = shred(keyValue.1, binary: &binary) cur[keyValue.0 as! NSCopying] = shred(keyValue.1, binary: &binary)

View File

@ -35,8 +35,6 @@ extension SocketParsable {
private func handleConnect(_ p: SocketPacket) { private func handleConnect(_ p: SocketPacket) {
if p.nsp == "/" && nsp != "/" { if p.nsp == "/" && nsp != "/" {
joinNamespace(nsp) joinNamespace(nsp)
} else if p.nsp != "/" && nsp == "/" {
didConnect()
} else { } else {
didConnect() didConnect()
} }
@ -91,8 +89,7 @@ extension SocketParsable {
} }
if !parser.hasNext { if !parser.hasNext {
return .Right(SocketPacket(type: type, id: -1, return .Right(SocketPacket(type: type, nsp: namespace, placeholders: placeholders))
nsp: namespace ?? "/", placeholders: placeholders))
} }
var idString = "" var idString = ""
@ -110,7 +107,7 @@ extension SocketParsable {
} }
} }
let d = message[parser.currentIndex.advanced(by: 1)..<message.endIndex] let d = message[parser.advance(by: 1)..<message.endIndex]
switch parseData(d) { switch parseData(d) {
case let .Left(err): case let .Left(err):

View File

@ -38,12 +38,14 @@ struct SocketStringReader {
currentIndex = message.startIndex currentIndex = message.startIndex
} }
mutating func advance(by: Int) { mutating func advance(by: Int) -> String.Index {
currentIndex = currentIndex.advanced(by: by) currentIndex = message.characters.index(currentIndex, offsetBy: by)
return currentIndex
} }
mutating func read(length: Int) -> String { mutating func read(length: Int) -> String {
let readString = message[currentIndex..<currentIndex.advanced(by: length)] let readString = message[currentIndex..<message.characters.index(currentIndex, offsetBy: length)]
advance(by: length) advance(by: length)
return readString return readString
@ -57,12 +59,12 @@ struct SocketStringReader {
return substring return substring
} }
advance(by: message.startIndex.distance(to: foundRange.startIndex) + 1) advance(by: message.characters.distance(from: message.characters.startIndex, to: foundRange.lowerBound) + 1)
return substring.substring(to: foundRange.startIndex) return substring.substring(to: foundRange.lowerBound)
} }
mutating func readUntilEnd() -> String { mutating func readUntilEnd() -> String {
return read(length: currentIndex.distance(to: message.endIndex)) return read(length: message.characters.distance(from: currentIndex, to: message.endIndex))
} }
} }

View File

@ -28,6 +28,9 @@ public typealias AckCallback = ([AnyObject]) -> Void
public typealias NormalCallback = ([AnyObject], SocketAckEmitter) -> Void public typealias NormalCallback = ([AnyObject], SocketAckEmitter) -> Void
public typealias OnAckCallback = (timeoutAfter: UInt64, callback: AckCallback) -> Void public typealias OnAckCallback = (timeoutAfter: UInt64, callback: AckCallback) -> Void
typealias Probe = (msg: String, type: SocketEnginePacketType, data: [NSData])
typealias ProbeWaitQueue = [Probe]
enum Either<E, V> { enum Either<E, V> {
case Left(E) case Left(E)
case Right(V) case Right(V)

View File

@ -112,12 +112,10 @@ public class WebSocket : NSObject, NSStreamDelegate {
public var headers = [String: String]() public var headers = [String: String]()
public var voipEnabled = false public var voipEnabled = false
public var selfSignedSSL = false public var selfSignedSSL = false
#if swift(>=3)
#else
public var security: SSLSecurity? public var security: SSLSecurity?
#endif
public var enabledSSLCipherSuites: [SSLCipherSuite]? public var enabledSSLCipherSuites: [SSLCipherSuite]?
public var origin: String? public var origin: String?
public var timeout = 5
public var isConnected :Bool { public var isConnected :Bool {
return connected return connected
} }
@ -173,7 +171,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
public func disconnect(forceTimeout: NSTimeInterval? = nil) { public func disconnect(forceTimeout: NSTimeInterval? = nil) {
switch forceTimeout { switch forceTimeout {
case .some(let seconds) where seconds > 0: case .some(let seconds) where seconds > 0:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))), queue) { [weak self] in dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))), queue!) { [weak self] in
self?.disconnectStream(error: nil) self?.disconnectStream(error: nil)
} }
fallthrough fallthrough
@ -231,19 +229,19 @@ public class WebSocket : NSObject, NSStreamDelegate {
port = 80 port = 80
} }
} }
addHeader(urlRequest: urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue) addHeader(urlRequest: urlRequest, key: headerWSUpgradeName as NSString, val: headerWSUpgradeValue as NSString)
addHeader(urlRequest: urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue) addHeader(urlRequest: urlRequest, key: headerWSConnectionName as NSString, val: headerWSConnectionValue as NSString)
if let protocols = optionalProtocols { if let protocols = optionalProtocols {
addHeader(urlRequest: urlRequest, key: headerWSProtocolName, val: protocols.joined(separator: ",")) addHeader(urlRequest: urlRequest, key: headerWSProtocolName as NSString, val: protocols.joined(separator: ",") as NSString)
} }
addHeader(urlRequest: urlRequest, key: headerWSVersionName, val: headerWSVersionValue) addHeader(urlRequest: urlRequest, key: headerWSVersionName as NSString, val: headerWSVersionValue as NSString)
addHeader(urlRequest: urlRequest, key: headerWSKeyName, val: generateWebSocketKey()) addHeader(urlRequest: urlRequest, key: headerWSKeyName as NSString, val: generateWebSocketKey() as NSString)
if let origin = origin { if let origin = origin {
addHeader(urlRequest: urlRequest, key: headerOriginName, val: origin) addHeader(urlRequest: urlRequest, key: headerOriginName as NSString, val: origin as NSString)
} }
addHeader(urlRequest: urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)") addHeader(urlRequest: urlRequest, key: headerWSHostName as NSString, val: "\(url.host!):\(port!)" as NSString)
for (key,value) in headers { for (key,value) in headers {
addHeader(urlRequest: urlRequest, key: key, val: value) addHeader(urlRequest: urlRequest, key: key as NSString, val: value as NSString)
} }
if let cfHTTPMessage = CFHTTPMessageCopySerializedMessage(urlRequest) { if let cfHTTPMessage = CFHTTPMessageCopySerializedMessage(urlRequest) {
let serializedRequest = cfHTTPMessage.takeRetainedValue() let serializedRequest = cfHTTPMessage.takeRetainedValue()
@ -276,48 +274,45 @@ public class WebSocket : NSObject, NSStreamDelegate {
var readStream: Unmanaged<CFReadStream>? var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>? var writeStream: Unmanaged<CFWriteStream>?
let h: NSString = url.host! let h = url.host!
CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream) CFStreamCreatePairWithSocketToHost(nil, h as NSString, UInt32(port), &readStream, &writeStream)
inputStream = readStream!.takeRetainedValue() inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue() outputStream = writeStream!.takeRetainedValue()
guard let inStream = inputStream, let outStream = outputStream else { return } guard let inStream = inputStream, let outStream = outputStream else { return }
inStream.delegate = self inStream.delegate = self
outStream.delegate = self outStream.delegate = self
if ["wss", "https"].contains(url.scheme) { if ["wss", "https"].contains(url.scheme) {
inStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey) inStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL as NSString, forKey: NSStreamSocketSecurityLevelKey)
outStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey) outStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL as NSString, forKey: NSStreamSocketSecurityLevelKey)
} else { } else {
certValidated = true //not a https session, so no need to check SSL pinning certValidated = true //not a https session, so no need to check SSL pinning
} }
if voipEnabled { if voipEnabled {
inStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType) inStream.setProperty(NSStreamNetworkServiceTypeVoIP as NSString, forKey: NSStreamNetworkServiceType)
outStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType) outStream.setProperty(NSStreamNetworkServiceTypeVoIP as NSString, forKey: NSStreamNetworkServiceType)
} }
if selfSignedSSL { if selfSignedSSL {
let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(value: false), kCFStreamSSLPeerName: kCFNull] let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(value: false), kCFStreamSSLPeerName: kCFNull]
inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String) inStream.setProperty(settings as AnyObject?, forKey: kCFStreamPropertySSLSettings as String)
outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String) outStream.setProperty(settings as AnyObject?, forKey: kCFStreamPropertySSLSettings as String)
} }
#if swift(>=3)
#else
if let cipherSuites = self.enabledSSLCipherSuites { if let cipherSuites = self.enabledSSLCipherSuites {
if let sslContextIn = CFReadStreamCopyProperty(inputStream, kCFStreamPropertySSLContext) as! SSLContextRef?, if let sslContextIn = CFReadStreamCopyProperty(inputStream, kCFStreamPropertySSLContext) as! SSLContext?,
sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContextRef? { sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContext? {
let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count) let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count)
let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count) let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count)
if resIn != errSecSuccess { if resIn != errSecSuccess {
let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn)) let error = self.errorWithDetail(detail: "Error setting ingoing cypher suites", code: UInt16(resIn))
disconnectStream(error) disconnectStream(error: error)
return return
} }
if resOut != errSecSuccess { if resOut != errSecSuccess {
let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut)) let error = self.errorWithDetail(detail: "Error setting outgoing cypher suites", code: UInt16(resOut))
disconnectStream(error) disconnectStream(error: error)
return return
} }
} }
} }
#endif
CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue) CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue)
CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue) CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue)
inStream.open() inStream.open()
@ -328,12 +323,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
self.mutex.unlock() self.mutex.unlock()
let bytes = UnsafePointer<UInt8>(data.bytes) let bytes = UnsafePointer<UInt8>(data.bytes)
var timeout = 5000000 //wait 5 seconds before giving up var out = timeout * 1000000 //wait 5 seconds before giving up
writeQueue.addOperation { [weak self] in writeQueue.addOperation { [weak self] in
while !outStream.hasSpaceAvailable { while !outStream.hasSpaceAvailable {
usleep(100) //wait until the socket is ready usleep(100) //wait until the socket is ready
timeout -= 100 out -= 100
if timeout < 0 { if out < 0 {
self?.cleanupStream() self?.cleanupStream()
self?.doDisconnect(error: self?.errorWithDetail(detail: "write wait timed out", code: 2)) self?.doDisconnect(error: self?.errorWithDetail(detail: "write wait timed out", code: 2))
return return
@ -344,24 +339,22 @@ public class WebSocket : NSObject, NSStreamDelegate {
outStream.write(bytes, maxLength: data.length) outStream.write(bytes, maxLength: data.length)
} }
} }
//delegate for the stream methods. Processes incoming bytes //delegate for the stream methods. Processes incoming bytes
public func stream(aStream: NSStream, handle eventCode: NSStreamEvent) { public func stream(aStream: NSStream, handle eventCode: NSStreamEvent) {
#if swift(>=3) if let sec = security where !certValidated && [.hasBytesAvailable, .hasSpaceAvailable].contains(eventCode) {
#else
if let sec = security where !certValidated && [.HasBytesAvailable, .HasSpaceAvailable].contains(eventCode) {
let possibleTrust: AnyObject? = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as String) let possibleTrust: AnyObject? = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as String)
if let trust: AnyObject = possibleTrust { if let trust: AnyObject = possibleTrust {
let domain: AnyObject? = aStream.property(forKey: kCFStreamSSLPeerName as String) let domain: AnyObject? = aStream.property(forKey: kCFStreamSSLPeerName as String)
if sec.isValid(trust as! SecTrustRef, domain: domain as! String?) { if sec.isValid(trust: trust as! SecTrust, domain: domain as! String?) {
certValidated = true certValidated = true
} else { } else {
let error = errorWithDetail("Invalid SSL certificate", code: 1) let error = errorWithDetail(detail: "Invalid SSL certificate", code: 1)
disconnectStream(error) disconnectStream(error: error)
return return
} }
} }
} }
#endif
if eventCode == .hasBytesAvailable { if eventCode == .hasBytesAvailable {
if aStream == inputStream { if aStream == inputStream {
processInputStream() processInputStream()
@ -416,25 +409,24 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
///dequeue the incoming input so it is processed in order ///dequeue the incoming input so it is processed in order
private func dequeueInput() { private func dequeueInput() {
guard !inputQueue.isEmpty else { return } while !inputQueue.isEmpty {
let data = inputQueue[0]
let data = inputQueue[0] var work = data
var work = data if let fragBuffer = fragBuffer {
if let fragBuffer = fragBuffer { let combine = NSMutableData(data: fragBuffer)
let combine = NSMutableData(data: fragBuffer) combine.append(data)
combine.append(data) work = combine
work = combine self.fragBuffer = nil
self.fragBuffer = nil }
let buffer = UnsafePointer<UInt8>(work.bytes)
let length = work.length
if !connected {
processTCPHandshake(buffer: buffer, bufferLen: length)
} else {
processRawMessagesInBuffer(pointer: buffer, bufferLen: length)
}
inputQueue = inputQueue.filter{$0 != data}
} }
let buffer = UnsafePointer<UInt8>(work.bytes)
let length = work.length
if !connected {
processTCPHandshake(buffer: buffer, bufferLen: length)
} else {
processRawMessage(buffer: buffer, bufferLen: length)
}
inputQueue = inputQueue.filter{$0 != data}
dequeueInput()
} }
//handle checking the inital connection status //handle checking the inital connection status
@ -444,7 +436,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
case 0: case 0:
connected = true connected = true
guard canDispatch else {return} guard canDispatch else {return}
dispatch_async(queue) { [weak self] in dispatch_async(queue!) { [weak self] in
guard let s = self else { return } guard let s = self else { return }
s.onConnect?() s.onConnect?()
s.delegate?.websocketDidConnect(socket: s) s.delegate?.websocketDidConnect(socket: s)
@ -480,7 +472,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
totalSize += 1 //skip the last \n totalSize += 1 //skip the last \n
let restSize = bufferLen - totalSize let restSize = bufferLen - totalSize
if restSize > 0 { if restSize > 0 {
processRawMessage(buffer: (buffer+totalSize),bufferLen: restSize) processRawMessagesInBuffer(pointer: buffer + totalSize, bufferLen: restSize)
} }
return 0 //success return 0 //success
} }
@ -497,7 +489,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) { if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
let headers = cfHeaders.takeRetainedValue() as NSDictionary let headers = cfHeaders.takeRetainedValue() as NSDictionary
if let acceptKey = headers[headerWSAcceptName] as? NSString { if let acceptKey = headers[headerWSAcceptName as NSString] as? NSString {
if acceptKey.length > 0 { if acceptKey.length > 0 {
return 0 return 0
} }
@ -533,12 +525,15 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
} }
///process the websocket data /// Process one message at the start of `buffer`. Return another buffer (sharing storage) that contains the leftover contents of `buffer` that I didn't process.
private func processRawMessage(buffer: UnsafePointer<UInt8>, bufferLen: Int) { @warn_unused_result
private func processOneRawMessage(inBuffer buffer: UnsafeBufferPointer<UInt8>) -> UnsafeBufferPointer<UInt8> {
let response = readStack.last let response = readStack.last
guard let baseAddress = buffer.baseAddress else { fatalError("") }
let bufferLen = buffer.count
if response != nil && bufferLen < 2 { if response != nil && bufferLen < 2 {
fragBuffer = NSData(bytes: buffer, length: bufferLen) fragBuffer = NSData(buffer: buffer)
return return emptyBuffer
} }
if let response = response where response.bytesLeft > 0 { if let response = response where response.bytesLeft > 0 {
var len = response.bytesLeft var len = response.bytesLeft
@ -548,24 +543,20 @@ public class WebSocket : NSObject, NSStreamDelegate {
extra = 0 extra = 0
} }
response.bytesLeft -= len response.bytesLeft -= len
response.buffer?.append(NSData(bytes: buffer, length: len)) response.buffer?.append(NSData(bytes: baseAddress, length: len))
processResponse(response: response) processResponse(response: response)
let offset = bufferLen - extra return buffer.fromOffset(offset: bufferLen - extra)
if extra > 0 {
processExtra(buffer: (buffer+offset), bufferLen: extra)
}
return
} else { } else {
let isFin = (FinMask & buffer[0]) let isFin = (FinMask & baseAddress[0])
let receivedOpcode = OpCode(rawValue: (OpCodeMask & buffer[0])) let receivedOpcode = OpCode(rawValue: (OpCodeMask & baseAddress[0]))
let isMasked = (MaskMask & buffer[1]) let isMasked = (MaskMask & baseAddress[1])
let payloadLen = (PayloadLenMask & buffer[1]) let payloadLen = (PayloadLenMask & baseAddress[1])
var offset = 2 var offset = 2
if (isMasked > 0 || (RSVMask & buffer[0]) > 0) && receivedOpcode != .Pong { if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .Pong {
let errCode = CloseCode.ProtocolError.rawValue let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(error: errorWithDetail(detail: "masked and rsv data is not currently supported", code: errCode)) doDisconnect(error: errorWithDetail(detail: "masked and rsv data is not currently supported", code: errCode))
writeError(code: errCode) writeError(code: errCode)
return return emptyBuffer
} }
let isControlFrame = (receivedOpcode == .ConnectionClose || receivedOpcode == .Ping) let isControlFrame = (receivedOpcode == .ConnectionClose || receivedOpcode == .Ping)
if !isControlFrame && (receivedOpcode != .BinaryFrame && receivedOpcode != .ContinueFrame && if !isControlFrame && (receivedOpcode != .BinaryFrame && receivedOpcode != .ContinueFrame &&
@ -573,20 +564,20 @@ public class WebSocket : NSObject, NSStreamDelegate {
let errCode = CloseCode.ProtocolError.rawValue let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(error: errorWithDetail(detail: "unknown opcode: \(receivedOpcode)", code: errCode)) doDisconnect(error: errorWithDetail(detail: "unknown opcode: \(receivedOpcode)", code: errCode))
writeError(code: errCode) writeError(code: errCode)
return return emptyBuffer
} }
if isControlFrame && isFin == 0 { if isControlFrame && isFin == 0 {
let errCode = CloseCode.ProtocolError.rawValue let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(error: errorWithDetail(detail: "control frames can't be fragmented", code: errCode)) doDisconnect(error: errorWithDetail(detail: "control frames can't be fragmented", code: errCode))
writeError(code: errCode) writeError(code: errCode)
return return emptyBuffer
} }
if receivedOpcode == .ConnectionClose { if receivedOpcode == .ConnectionClose {
var code = CloseCode.Normal.rawValue var code = CloseCode.Normal.rawValue
if payloadLen == 1 { if payloadLen == 1 {
code = CloseCode.ProtocolError.rawValue code = CloseCode.ProtocolError.rawValue
} else if payloadLen > 1 { } else if payloadLen > 1 {
code = WebSocket.readUint16(buffer: buffer, offset: offset) code = WebSocket.readUint16(buffer: baseAddress, offset: offset)
if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) { if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) {
code = CloseCode.ProtocolError.rawValue code = CloseCode.ProtocolError.rawValue
} }
@ -595,7 +586,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
if payloadLen > 2 { if payloadLen > 2 {
let len = Int(payloadLen-2) let len = Int(payloadLen-2)
if len > 0 { if len > 0 {
let bytes = UnsafePointer<UInt8>((buffer+offset)) let bytes = baseAddress + offset
let str: NSString? = NSString(data: NSData(bytes: bytes, length: len), encoding: NSUTF8StringEncoding) let str: NSString? = NSString(data: NSData(bytes: bytes, length: len), encoding: NSUTF8StringEncoding)
if str == nil { if str == nil {
code = CloseCode.ProtocolError.rawValue code = CloseCode.ProtocolError.rawValue
@ -604,23 +595,23 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
doDisconnect(error: errorWithDetail(detail: "connection closed by server", code: code)) doDisconnect(error: errorWithDetail(detail: "connection closed by server", code: code))
writeError(code: code) writeError(code: code)
return return emptyBuffer
} }
if isControlFrame && payloadLen > 125 { if isControlFrame && payloadLen > 125 {
writeError(code: CloseCode.ProtocolError.rawValue) writeError(code: CloseCode.ProtocolError.rawValue)
return return emptyBuffer
} }
var dataLength = UInt64(payloadLen) var dataLength = UInt64(payloadLen)
if dataLength == 127 { if dataLength == 127 {
dataLength = WebSocket.readUint64(buffer: buffer, offset: offset) dataLength = WebSocket.readUint64(buffer: baseAddress, offset: offset)
offset += sizeof(UInt64) offset += sizeof(UInt64)
} else if dataLength == 126 { } else if dataLength == 126 {
dataLength = UInt64(WebSocket.readUint16(buffer: buffer, offset: offset)) dataLength = UInt64(WebSocket.readUint16(buffer: baseAddress, offset: offset))
offset += sizeof(UInt16) offset += sizeof(UInt16)
} }
if bufferLen < offset || UInt64(bufferLen - offset) < dataLength { if bufferLen < offset || UInt64(bufferLen - offset) < dataLength {
fragBuffer = NSData(bytes: buffer, length: bufferLen) fragBuffer = NSData(bytes: baseAddress, length: bufferLen)
return return emptyBuffer
} }
var len = dataLength var len = dataLength
if dataLength > UInt64(bufferLen) { if dataLength > UInt64(bufferLen) {
@ -631,22 +622,17 @@ public class WebSocket : NSObject, NSStreamDelegate {
len = 0 len = 0
data = NSData() data = NSData()
} else { } else {
data = NSData(bytes: UnsafePointer<UInt8>((buffer+offset)), length: Int(len)) data = NSData(bytes: baseAddress+offset, length: Int(len))
} }
if receivedOpcode == .Pong { if receivedOpcode == .Pong {
if canDispatch { if canDispatch {
dispatch_async(queue) { [weak self] in dispatch_async(queue!) { [weak self] in
guard let s = self else { return } guard let s = self else { return }
s.onPong?() s.onPong?()
s.pongDelegate?.websocketDidReceivePong(socket: s) s.pongDelegate?.websocketDidReceivePong(socket: s)
} }
} }
let step = Int(offset+numericCast(len)) return buffer.fromOffset(offset: offset + Int(len))
let extra = bufferLen-step
if extra > 0 {
processRawMessage(buffer: (buffer+step), bufferLen: extra)
}
return
} }
var response = readStack.last var response = readStack.last
if isControlFrame { if isControlFrame {
@ -656,7 +642,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
let errCode = CloseCode.ProtocolError.rawValue let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(error: errorWithDetail(detail: "continue frame before a binary or text frame", code: errCode)) doDisconnect(error: errorWithDetail(detail: "continue frame before a binary or text frame", code: errCode))
writeError(code: errCode) writeError(code: errCode)
return return emptyBuffer
} }
var isNew = false var isNew = false
if response == nil { if response == nil {
@ -665,7 +651,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
doDisconnect(error: errorWithDetail(detail: "first frame can't be a continue frame", doDisconnect(error: errorWithDetail(detail: "first frame can't be a continue frame",
code: errCode)) code: errCode))
writeError(code: errCode) writeError(code: errCode)
return return emptyBuffer
} }
isNew = true isNew = true
response = WSResponse() response = WSResponse()
@ -680,7 +666,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
doDisconnect(error: errorWithDetail(detail: "second and beyond of fragment message must be a continue frame", doDisconnect(error: errorWithDetail(detail: "second and beyond of fragment message must be a continue frame",
code: errCode)) code: errCode))
writeError(code: errCode) writeError(code: errCode)
return return emptyBuffer
} }
response!.buffer!.append(data) response!.buffer!.append(data)
} }
@ -695,20 +681,18 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
let step = Int(offset+numericCast(len)) let step = Int(offset+numericCast(len))
let extra = bufferLen-step return buffer.fromOffset(offset: step)
if extra > 0 {
processExtra(buffer: (buffer+step), bufferLen: extra)
}
} }
} }
///process the extra of a buffer /// Process all messages in the buffer if possible.
private func processExtra(buffer: UnsafePointer<UInt8>, bufferLen: Int) { private func processRawMessagesInBuffer(pointer: UnsafePointer<UInt8>, bufferLen: Int) {
if bufferLen < 2 { var buffer = UnsafeBufferPointer(start: pointer, count: bufferLen)
fragBuffer = NSData(bytes: buffer, length: bufferLen) repeat {
} else { buffer = processOneRawMessage(inBuffer: buffer)
processRawMessage(buffer: buffer, bufferLen: bufferLen) } while buffer.count >= 2
if buffer.count > 0 {
fragBuffer = NSData(buffer: buffer)
} }
} }
@ -725,7 +709,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
return false return false
} }
if canDispatch { if canDispatch {
dispatch_async(queue) { [weak self] in dispatch_async(queue!) { [weak self] in
guard let s = self else { return } guard let s = self else { return }
s.onText?(str! as String) s.onText?(str! as String)
s.delegate?.websocketDidReceiveMessage(socket: s, text: str! as String) s.delegate?.websocketDidReceiveMessage(socket: s, text: str! as String)
@ -734,7 +718,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
} else if response.code == .BinaryFrame { } else if response.code == .BinaryFrame {
if canDispatch { if canDispatch {
let data = response.buffer! //local copy so it is perverse for writing let data = response.buffer! //local copy so it is perverse for writing
dispatch_async(queue) { [weak self] in dispatch_async(queue!) { [weak self] in
guard let s = self else { return } guard let s = self else { return }
s.onData?(data) s.onData?(data)
s.delegate?.websocketDidReceiveData(socket: s, data: data) s.delegate?.websocketDidReceiveData(socket: s, data: data)
@ -751,8 +735,9 @@ public class WebSocket : NSObject, NSStreamDelegate {
private func errorWithDetail(detail: String, code: UInt16) -> NSError { private func errorWithDetail(detail: String, code: UInt16) -> NSError {
var details = [String: String]() var details = [String: String]()
details[NSLocalizedDescriptionKey] = detail details[NSLocalizedDescriptionKey] = detail
#if swift(>=3) #if swift(>=3)
return NSError(domain: "April 12 build of Swift 3 broke this property", code: Int(code), userInfo: details) return NSError(domain: "This is still broken", code: Int(code), userInfo: details)
#else #else
return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details) return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details)
#endif #endif
@ -834,7 +819,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
didDisconnect = true didDisconnect = true
connected = false connected = false
guard canDispatch else {return} guard canDispatch else {return}
dispatch_async(queue) { [weak self] in dispatch_async(queue!) { [weak self] in
guard let s = self else { return } guard let s = self else { return }
s.onDisconnect?(error) s.onDisconnect?(error)
s.delegate?.websocketDidDisconnect(socket: s, error: error) s.delegate?.websocketDidDisconnect(socket: s, error: error)
@ -850,11 +835,27 @@ public class WebSocket : NSObject, NSStreamDelegate {
} }
#if swift(>=3) private extension NSData {
#else
convenience init(buffer: UnsafeBufferPointer<UInt8>) {
self.init(bytes: buffer.baseAddress, length: buffer.count)
}
}
private extension UnsafeBufferPointer {
func fromOffset(offset: Int) -> UnsafeBufferPointer<Element> {
return UnsafeBufferPointer<Element>(start: baseAddress!.advanced(by: offset), count: count - offset)
}
}
private let emptyBuffer = UnsafeBufferPointer<UInt8>(start: nil, count: 0)
public class SSLCert { public class SSLCert {
var certData: NSData? var certData: NSData?
var key: SecKeyRef? var key: SecKey?
/** /**
Designated init for certificates Designated init for certificates
@ -874,7 +875,7 @@ public class SSLCert {
- returns: a representation security object to be used with - returns: a representation security object to be used with
*/ */
public init(key: SecKeyRef) { public init(key: SecKey) {
self.key = key self.key = key
} }
} }
@ -884,7 +885,7 @@ public class SSLSecurity {
var isReady = false //is the key processing done? var isReady = false //is the key processing done?
var certificates: [NSData]? //the certificates var certificates: [NSData]? //the certificates
var pubKeys: [SecKeyRef]? //the public keys var pubKeys: [SecKey]? //the public keys
var usePublicKeys = false //use public keys or certificate validation? var usePublicKeys = false //use public keys or certificate validation?
/** /**
@ -895,7 +896,7 @@ public class SSLSecurity {
- returns: a representation security object to be used with - returns: a representation security object to be used with
*/ */
public convenience init(usePublicKeys: Bool = false) { public convenience init(usePublicKeys: Bool = false) {
let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".") let paths = NSBundle.main().pathsForResources(ofType: "cer", inDirectory: ".")
let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in
var certs = certs var certs = certs
@ -921,10 +922,10 @@ public class SSLSecurity {
if self.usePublicKeys { if self.usePublicKeys {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) {
let pubKeys = certs.reduce([SecKeyRef]()) { (pubKeys: [SecKeyRef], cert: SSLCert) -> [SecKeyRef] in let pubKeys = certs.reduce([SecKey]()) { (pubKeys: [SecKey], cert: SSLCert) -> [SecKey] in
var pubKeys = pubKeys var pubKeys = pubKeys
if let data = cert.certData where cert.key == nil { if let data = cert.certData where cert.key == nil {
cert.key = self.extractPublicKey(data) cert.key = self.extractPublicKey(data: data)
} }
if let key = cert.key { if let key = cert.key {
pubKeys.append(key) pubKeys.append(key)
@ -956,7 +957,7 @@ public class SSLSecurity {
- returns: if the key was successfully validated - returns: if the key was successfully validated
*/ */
public func isValid(trust: SecTrustRef, domain: String?) -> Bool { public func isValid(trust: SecTrust, domain: String?) -> Bool {
var tries = 0 var tries = 0
while(!self.isReady) { while(!self.isReady) {
@ -966,16 +967,16 @@ public class SSLSecurity {
return false //doesn't appear it is going to ever be ready... return false //doesn't appear it is going to ever be ready...
} }
} }
var policy: SecPolicyRef var policy: SecPolicy
if self.validatedDN { if self.validatedDN {
policy = SecPolicyCreateSSL(true, domain) policy = SecPolicyCreateSSL(true, (domain as! CFString))
} else { } else {
policy = SecPolicyCreateBasicX509() policy = SecPolicyCreateBasicX509()
} }
SecTrustSetPolicies(trust,policy) SecTrustSetPolicies(trust,policy)
if self.usePublicKeys { if self.usePublicKeys {
if let keys = self.pubKeys { if let keys = self.pubKeys {
let serverPubKeys = publicKeyChainForTrust(trust) let serverPubKeys = publicKeyChainForTrust(trust: trust)
for serverKey in serverPubKeys as [AnyObject] { for serverKey in serverPubKeys as [AnyObject] {
for key in keys as [AnyObject] { for key in keys as [AnyObject] {
if serverKey.isEqual(key) { if serverKey.isEqual(key) {
@ -985,12 +986,12 @@ public class SSLSecurity {
} }
} }
} else if let certs = self.certificates { } else if let certs = self.certificates {
let serverCerts = certificateChainForTrust(trust) let serverCerts = certificateChainForTrust(trust: trust)
var collect = [SecCertificate]() var collect = [SecCertificate]()
for cert in certs { for cert in certs {
collect.append(SecCertificateCreateWithData(nil,cert)!) collect.append(SecCertificateCreateWithData(nil,cert)!)
} }
SecTrustSetAnchorCertificates(trust,collect) SecTrustSetAnchorCertificates(trust,collect as CFArray)
var result: SecTrustResultType = 0 var result: SecTrustResultType = 0
SecTrustEvaluate(trust,&result) SecTrustEvaluate(trust,&result)
let r = Int(result) let r = Int(result)
@ -1019,10 +1020,10 @@ public class SSLSecurity {
- returns: a public key - returns: a public key
*/ */
func extractPublicKey(data: NSData) -> SecKeyRef? { func extractPublicKey(data: NSData) -> SecKey? {
guard let cert = SecCertificateCreateWithData(nil, data) else { return nil } guard let cert = SecCertificateCreateWithData(nil, data) else { return nil }
return extractPublicKeyFromCert(cert, policy: SecPolicyCreateBasicX509()) return extractPublicKeyFromCert(cert: cert, policy: SecPolicyCreateBasicX509())
} }
/** /**
@ -1032,7 +1033,7 @@ public class SSLSecurity {
- returns: a public key - returns: a public key
*/ */
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? { func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKey? {
var possibleTrust: SecTrust? var possibleTrust: SecTrust?
SecTrustCreateWithCertificates(cert, policy, &possibleTrust) SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
@ -1050,7 +1051,7 @@ public class SSLSecurity {
- returns: the certificate chain for the trust - returns: the certificate chain for the trust
*/ */
func certificateChainForTrust(trust: SecTrustRef) -> [NSData] { func certificateChainForTrust(trust: SecTrust) -> [NSData] {
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (certificates: [NSData], index: Int) -> [NSData] in let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (certificates: [NSData], index: Int) -> [NSData] in
var certificates = certificates var certificates = certificates
let cert = SecTrustGetCertificateAtIndex(trust, index) let cert = SecTrustGetCertificateAtIndex(trust, index)
@ -1068,12 +1069,12 @@ public class SSLSecurity {
- returns: the public keys from the certifcate chain for the trust - returns: the public keys from the certifcate chain for the trust
*/ */
func publicKeyChainForTrust(trust: SecTrustRef) -> [SecKeyRef] { func publicKeyChainForTrust(trust: SecTrust) -> [SecKey] {
let policy = SecPolicyCreateBasicX509() let policy = SecPolicyCreateBasicX509()
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKeyRef]()) { (keys: [SecKeyRef], index: Int) -> [SecKeyRef] in let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKey]()) { (keys: [SecKey], index: Int) -> [SecKey] in
var keys = keys var keys = keys
let cert = SecTrustGetCertificateAtIndex(trust, index) let cert = SecTrustGetCertificateAtIndex(trust, index)
if let key = extractPublicKeyFromCert(cert!, policy: policy) { if let key = extractPublicKeyFromCert(cert: cert!, policy: policy) {
keys.append(key) keys.append(key)
} }
@ -1085,4 +1086,3 @@ public class SSLSecurity {
} }
#endif