From baca9fc8da134f8ec8ef39caac72368f7bae4288 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 31 Mar 2016 21:31:24 -0400 Subject: [PATCH 1/3] small refactors --- Source/SocketIOClient.swift | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/Source/SocketIOClient.swift b/Source/SocketIOClient.swift index 12bd49d..183bcc3 100644 --- a/Source/SocketIOClient.swift +++ b/Source/SocketIOClient.swift @@ -164,10 +164,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable return {[weak self, ack = currentAck] timeout, callback in if let this = self { this.ackHandlers.addAck(ack, callback: callback) - - dispatch_async(this.emitQueue) { - this._emit(items, ack: ack) - } + this._emit(items, ack: ack) if timeout != 0 { let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * NSEC_PER_SEC)) @@ -223,9 +220,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable return } - dispatch_async(emitQueue) {[emitData = [event] + items] in - self._emit(emitData) - } + _emit([event] + items) } /** @@ -244,17 +239,19 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable } private func _emit(data: [AnyObject], ack: Int? = nil) { - guard status == .Connected else { - handleEvent("error", data: ["Tried emitting when not connected"], isInternalMessage: true) - return + dispatch_async(emitQueue) { + guard self.status == .Connected else { + self.handleEvent("error", data: ["Tried emitting when not connected"], isInternalMessage: true) + return + } + + let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: self.nsp, ack: false) + let str = packet.packetString + + DefaultSocketLogger.Logger.log("Emitting: %@", type: self.logType, args: str) + + self.engine?.send(str, withData: packet.binary) } - - let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: nsp, ack: false) - let str = packet.packetString - - DefaultSocketLogger.Logger.log("Emitting: %@", type: logType, args: str) - - engine?.send(str, withData: packet.binary) } // If the server wants to know that the client received data @@ -295,7 +292,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable // Called when the socket gets an ack for something it sent 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 ?? "") @@ -427,9 +424,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable } if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts || !reconnects { - didDisconnect("Reconnect Failed") - - return + return didDisconnect("Reconnect Failed") } DefaultSocketLogger.Logger.log("Trying to reconnect", type: logType) From 4ee196c5fb7f6ccfea4f7dc0d7c0139b3067ea7b Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 4 Apr 2016 11:28:20 -0400 Subject: [PATCH 2/3] add default value to namespace in socketparsable --- Source/SocketIOClient.swift | 10 +++------- Source/SocketParsable.swift | 11 ++++++----- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Source/SocketIOClient.swift b/Source/SocketIOClient.swift index 183bcc3..5d36444 100644 --- a/Source/SocketIOClient.swift +++ b/Source/SocketIOClient.swift @@ -223,17 +223,13 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable _emit([event] + items) } - /** - Sends a message to the server, requesting an ack. Use the onAck method of SocketAckHandler to add - an ack. - */ + /// Sends a message to the server, requesting an ack. Use the onAck method of SocketAckHandler to add + /// an ack. public func emitWithAck(event: String, _ items: AnyObject...) -> OnAckCallback { return emitWithAck(event, withItems: items) } - /** - Same as emitWithAck, but for Objective-C - */ + /// Same as emitWithAck, but for Objective-C public func emitWithAck(event: String, withItems items: [AnyObject]) -> OnAckCallback { return createOnAck([event] + items) } diff --git a/Source/SocketParsable.swift b/Source/SocketParsable.swift index 537a614..cd539a1 100644 --- a/Source/SocketParsable.swift +++ b/Source/SocketParsable.swift @@ -76,7 +76,7 @@ extension SocketParsable { return .Right(SocketPacket(type: type, nsp: "/")) } - var namespace: String? + var namespace = "/" var placeholders = -1 if type == .BinaryEvent || type == .BinaryAck { @@ -119,23 +119,24 @@ extension SocketParsable { // If first you don't succeed, try again if case let .Right(data) = parseData("\([noPlaceholders as AnyObject])") { return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1, - nsp: namespace ?? "/", placeholders: placeholders)) + nsp: namespace, placeholders: placeholders)) } else { return .Left(err) } case let .Right(data): return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1, - nsp: namespace ?? "/", placeholders: placeholders)) + nsp: namespace, placeholders: placeholders)) } } // Parses data for events private func parseData(data: String) -> Either { let stringData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) + do { if let arr = try NSJSONSerialization.JSONObjectWithData(stringData!, - options: NSJSONReadingOptions.MutableContainers) as? [AnyObject] { - return .Right(arr) + options: NSJSONReadingOptions.MutableContainers) as? [AnyObject] { + return .Right(arr) } else { return .Left("Expected data array") } From ed3d49001f34d09768b983f677b0a2276ed7f245 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 10 Apr 2016 13:59:21 -0400 Subject: [PATCH 3/3] bump websocket version --- SocketIO-MacTests/SocketParserTest.swift | 6 ++ Source/SocketParsable.swift | 2 +- Source/WebSocket.swift | 74 +++++++++++++++--------- 3 files changed, 53 insertions(+), 29 deletions(-) diff --git a/SocketIO-MacTests/SocketParserTest.swift b/SocketIO-MacTests/SocketParserTest.swift index 9513fcd..b205556 100644 --- a/SocketIO-MacTests/SocketParserTest.swift +++ b/SocketIO-MacTests/SocketParserTest.swift @@ -25,6 +25,7 @@ class SocketParserTest: XCTestCase { "0/swift": ("/swift", [], [], -1), "1/swift": ("/swift", [], [], -1), "4\"ERROR\"": ("/", ["ERROR"], [], -1), + "4{\"test\":2}": ("/", [["test": 2]], [], -1), "41": ("/", [1], [], -1)] func testDisconnect() { @@ -87,6 +88,11 @@ class SocketParserTest: XCTestCase { validateParseResult(message) } + func testErrorTypeDictionary() { + let message = "4{\"test\":2}" + validateParseResult(message) + } + func testErrorTypeInt() { let message = "41" validateParseResult(message) diff --git a/Source/SocketParsable.swift b/Source/SocketParsable.swift index cd539a1..1c4084a 100644 --- a/Source/SocketParsable.swift +++ b/Source/SocketParsable.swift @@ -116,7 +116,7 @@ extension SocketParsable { switch parseData(noPlaceholders) { case let .Left(err): - // If first you don't succeed, try again + // Errors aren't always enclosed in an array if case let .Right(data) = parseData("\([noPlaceholders as AnyObject])") { return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1, nsp: namespace, placeholders: placeholders)) diff --git a/Source/WebSocket.swift b/Source/WebSocket.swift index 163ce7a..833eece 100644 --- a/Source/WebSocket.swift +++ b/Source/WebSocket.swift @@ -180,30 +180,42 @@ public class WebSocket : NSObject, NSStreamDelegate { } } - ///write a string to the websocket. This sends it as a text frame. - public func writeString(str: String) { + /** + Write a string to the websocket. This sends it as a text frame. + + If you supply a non-nil completion block, I will perform it when the write completes. + - parameter str: The string to write. + - parameter completion: The (optional) completion handler. + */ + public func writeString(str: String, completion: (() -> ())? = nil) { guard isConnected else { return } - dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame) + dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame, writeCompletion: completion) } - ///write binary data to the websocket. This sends it as a binary frame. - public func writeData(data: NSData) { + /** + Write binary data to the websocket. This sends it as a binary frame. + + If you supply a non-nil completion block, I will perform it when the write completes. + - parameter data: The data to write. + - parameter completion: The (optional) completion handler. + */ + public func writeData(data: NSData, completion: (() -> ())? = nil) { guard isConnected else { return } - dequeueWrite(data, code: .BinaryFrame) + dequeueWrite(data, code: .BinaryFrame, writeCompletion: completion) } //write a ping to the websocket. This sends it as a control frame. //yodel a sound to the planet. This sends it as an astroid. http://youtu.be/Eu5ZJELRiJ8?t=42s - public func writePing(data: NSData) { + public func writePing(data: NSData, completion: (() -> ())? = nil) { guard isConnected else { return } - dequeueWrite(data, code: .Ping) + dequeueWrite(data, code: .Ping, writeCompletion: completion) } //private method that starts the connection private func createHTTPRequest() { let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET", - url, kCFHTTPVersion1_1).takeRetainedValue() + url, kCFHTTPVersion1_1).takeRetainedValue() var port = url.port if port == nil { @@ -283,18 +295,18 @@ public class WebSocket : NSObject, NSStreamDelegate { if let cipherSuites = self.enabledSSLCipherSuites { if let sslContextIn = CFReadStreamCopyProperty(inputStream, kCFStreamPropertySSLContext) as! SSLContextRef?, sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContextRef? { - let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count) - let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count) - if resIn != errSecSuccess { - let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn)) - disconnectStream(error) - return - } - if resOut != errSecSuccess { - let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut)) - disconnectStream(error) - return - } + let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count) + let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count) + if resIn != errSecSuccess { + let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn)) + disconnectStream(error) + return + } + if resOut != errSecSuccess { + let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut)) + disconnectStream(error) + return + } } } CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue) @@ -428,7 +440,7 @@ public class WebSocket : NSObject, NSStreamDelegate { } case -1: fragBuffer = NSData(bytes: buffer, length: bufferLen) - break //do nothing, we are going to collect more data + break //do nothing, we are going to collect more data default: doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: UInt16(code))) } @@ -547,10 +559,10 @@ public class WebSocket : NSObject, NSStreamDelegate { let isControlFrame = (receivedOpcode == .ConnectionClose || receivedOpcode == .Ping) if !isControlFrame && (receivedOpcode != .BinaryFrame && receivedOpcode != .ContinueFrame && receivedOpcode != .TextFrame && receivedOpcode != .Pong) { - let errCode = CloseCode.ProtocolError.rawValue - doDisconnect(errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode)) - writeError(errCode) - return + let errCode = CloseCode.ProtocolError.rawValue + doDisconnect(errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode)) + writeError(errCode) + return } if isControlFrame && isFin == 0 { let errCode = CloseCode.ProtocolError.rawValue @@ -603,7 +615,7 @@ public class WebSocket : NSObject, NSStreamDelegate { if dataLength > UInt64(bufferLen) { len = UInt64(bufferLen-offset) } - var data: NSData! + let data: NSData if len < 0 { len = 0 data = NSData() @@ -739,7 +751,7 @@ public class WebSocket : NSObject, NSStreamDelegate { dequeueWrite(NSData(bytes: buffer, length: sizeof(UInt16)), code: .ConnectionClose) } ///used to write things to the stream - private func dequeueWrite(data: NSData, code: OpCode) { + private func dequeueWrite(data: NSData, code: OpCode, writeCompletion: (() -> ())? = nil) { writeQueue.addOperationWithBlock { [weak self] in //stream isn't ready, let's wait guard let s = self else { return } @@ -788,6 +800,12 @@ public class WebSocket : NSObject, NSStreamDelegate { total += len } if total >= offset { + if let queue = self?.queue, callback = writeCompletion { + dispatch_async(queue) { + callback() + } + } + break } }