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