Merge pull request #344 from socketio/development

Development
This commit is contained in:
Erik Little 2016-04-11 18:55:27 -04:00
commit c9f653ef2c
19 changed files with 164 additions and 149 deletions

View File

@ -116,6 +116,7 @@
7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */; };
7420CB7A1C49629E00956AA4 /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */; };
7420CB7B1C49629E00956AA4 /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */; };
742D150C1CA5794B00BD987D /* SocketObjectiveCTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 742D150B1CA5794B00BD987D /* SocketObjectiveCTest.m */; };
74321DCB1C2D939A00CF6F43 /* SocketAckManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74321DC91C2D939A00CF6F43 /* SocketAckManagerTest.swift */; };
74321DCC1C2D939A00CF6F43 /* SocketParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74321DCA1C2D939A00CF6F43 /* SocketParserTest.swift */; };
7471CCEA1C39926300364B59 /* SocketClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74ABF7761C3991C10078C657 /* SocketClientSpec.swift */; };
@ -187,6 +188,7 @@
74171E621C10CD240062D398 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket.swift; sourceTree = "<group>"; };
741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = "<group>"; };
7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEnginePollable.swift; path = Source/SocketEnginePollable.swift; sourceTree = "<group>"; };
742D150B1CA5794B00BD987D /* SocketObjectiveCTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketObjectiveCTest.m; sourceTree = "<group>"; };
74321DC91C2D939A00CF6F43 /* SocketAckManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketAckManagerTest.swift; sourceTree = "<group>"; };
74321DCA1C2D939A00CF6F43 /* SocketParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocketParserTest.swift; sourceTree = "<group>"; };
7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespacePacketTest.swift; sourceTree = "<group>"; };
@ -326,6 +328,7 @@
74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */,
741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */,
7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */,
742D150B1CA5794B00BD987D /* SocketObjectiveCTest.m */,
74321DCA1C2D939A00CF6F43 /* SocketParserTest.swift */,
7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */,
572EF2471B51F18A00EEBB58 /* Supporting Files */,
@ -513,7 +516,7 @@
572EF20E1B51F12F00EEBB58 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0710;
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0720;
TargetAttributes = {
572EF2181B51F16C00EEBB58 = {
@ -687,6 +690,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
742D150C1CA5794B00BD987D /* SocketObjectiveCTest.m in Sources */,
74321DCB1C2D939A00CF6F43 /* SocketAckManagerTest.swift in Sources */,
74321DCC1C2D939A00CF6F43 /* SocketParserTest.swift in Sources */,
7472C6601BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */,
@ -1169,6 +1173,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "io.socket.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};

View File

@ -0,0 +1,45 @@
//
// SocketObjectiveCTest.m
// Socket.IO-Client-Swift
//
// Created by Erik Little on 3/25/16.
//
// Merely tests whether the Objective-C api breaks
//
#import <XCTest/XCTest.h>
@import SocketIOClientSwift;
@interface SocketObjectiveCTest : XCTestCase
@property SocketIOClient* socket;
@end
@implementation SocketObjectiveCTest
- (void)setUp {
[super setUp];
NSURL* url = [[NSURL alloc] initWithString:@"http://localhost"];
self.socket = [[SocketIOClient alloc] initWithSocketURL:url options:nil];
}
- (void)testOnSyntax {
[self.socket on:@"someCallback" callback:^(NSArray* data, SocketAckEmitter* ack) {
[ack with:@[@1]];
}];
}
- (void)testEmitSyntax {
[self.socket emit:@"testEmit" withItems:@[@YES]];
}
- (void)testEmitWithAckSyntax {
[self.socket emitWithAck:@"testAckEmit" withItems:@[@YES]];
}
- (void)testOffSyntax {
[self.socket off:@"test"];
}
@end

View File

@ -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)

View File

@ -24,7 +24,7 @@
import Foundation
public final class SocketAckEmitter: NSObject {
public final class SocketAckEmitter : NSObject {
let socket: SocketIOClient
let ackNum: Int

View File

@ -24,7 +24,7 @@
import Foundation
private struct SocketAck: Hashable, Equatable {
private struct SocketAck : Hashable, Equatable {
let ack: Int
var callback: AckCallback!
var hashValue: Int {

View File

@ -24,7 +24,7 @@
import Foundation
public final class SocketAnyEvent: NSObject {
public final class SocketAnyEvent : NSObject {
public let event: String
public let items: NSArray?
override public var description: String {

View File

@ -22,7 +22,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
protocol SocketClientSpec: class {
protocol SocketClientSpec : class {
var nsp: String { get set }
var waitingPackets: [SocketPacket] { get set }

View File

@ -24,7 +24,7 @@
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 handleQueue = dispatch_queue_create("com.socketio.engineHandleQueue", DISPATCH_QUEUE_SERIAL)
public let parseQueue = dispatch_queue_create("com.socketio.engineParseQueue", DISPATCH_QUEUE_SERIAL)
@ -47,7 +47,6 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb
public private(set) var forcePolling = false
public private(set) var forceWebsockets = false
public private(set) var invalidated = false
public private(set) var pingTimer: NSTimer?
public private(set) var polling = true
public private(set) var probing = false
public private(set) var session: NSURLSession?
@ -123,18 +122,6 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb
self.init(client: client, url: url, options: options?.toSocketOptionsSet() ?? [])
}
@available(*, deprecated=5.3, message="Please use the NSURL based init")
public convenience init(client: SocketEngineClient, urlString: String, options: Set<SocketIOClientOption>) {
guard let url = NSURL(string: urlString) else { fatalError("Incorrect url") }
self.init(client: client, url: url, options: options)
}
@available(*, deprecated=5.3, message="Please use the NSURL based init")
public convenience init(client: SocketEngineClient, urlString: String, options: NSDictionary?) {
guard let url = NSURL(string: urlString) else { fatalError("Incorrect url") }
self.init(client: client, url: url, options: options?.toSocketOptionsSet() ?? [])
}
deinit {
DefaultSocketLogger.Logger.log("Engine is being released", type: logType)
closed = true
@ -185,15 +172,11 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb
}
}
public func close(reason: String) {
disconnect(reason)
}
/// Starts the connection to the server
public func connect() {
if connected {
DefaultSocketLogger.Logger.error("Engine tried opening while connected. Assuming this was a reconnect", type: logType)
close("reconnect")
disconnect("reconnect")
}
DefaultSocketLogger.Logger.log("Starting engine", type: logType)
@ -285,7 +268,7 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb
public func didError(error: String) {
DefaultSocketLogger.Logger.error(error, type: logType)
client?.engineDidError(error)
close(error)
disconnect(error)
}
public func disconnect(reason: String) {
@ -295,7 +278,6 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb
invalidated = true
connected = false
pingTimer?.invalidate()
ws?.disconnect()
stopPolling()
client?.engineDidClose(reason)
@ -401,8 +383,7 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb
createWebsocketAndConnect()
}
startPingTimer()
sendPing()
if !forceWebsockets {
doPoll()
@ -425,10 +406,6 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb
}
}
public func open() {
connect()
}
public func parseEngineData(data: NSData) {
DefaultSocketLogger.Logger.log("Got binary data: %@", type: "SocketEngine", args: data)
client?.parseEngineBinaryData(data.subdataWithRange(NSMakeRange(1, data.length - 1)))
@ -487,26 +464,24 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb
websocket = false
}
@objc private func sendPing() {
private func sendPing() {
if !connected {
return
}
//Server is not responding
if pongsMissed > pongsMissedMax {
pingTimer?.invalidate()
client?.engineDidClose("Ping timeout")
return
}
pongsMissed += 1
write("", withType: .Ping, withData: [])
}
private func startPingTimer() {
if let pingInterval = pingInterval {
pingTimer?.invalidate()
pingTimer = nil
pongsMissed += 1
write("", withType: .Ping, withData: [])
dispatch_async(dispatch_get_main_queue()) {
self.pingTimer = NSTimer.scheduledTimerWithTimeInterval(pingInterval, target: self,
selector: #selector(SocketEngine.sendPing), userInfo: nil, repeats: true)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(pingInterval * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {[weak self] in
self?.sendPing()
}
}
}
@ -562,7 +537,6 @@ public final class SocketEngine: NSObject, SocketEnginePollable, SocketEngineWeb
}
if websocket {
pingTimer?.invalidate()
connected = false
websocket = false

View File

@ -25,6 +25,6 @@
import Foundation
@objc public enum SocketEnginePacketType: Int {
@objc public enum SocketEnginePacketType : Int {
case Open, Close, Ping, Pong, Message, Upgrade, Noop
}

View File

@ -25,7 +25,7 @@
import Foundation
/// Protocol that is used to implement socket.io polling support
public protocol SocketEnginePollable: SocketEngineSpec {
public protocol SocketEnginePollable : SocketEngineSpec {
var invalidated: Bool { get }
/// Holds strings waiting to be sent over polling.
/// You shouldn't need to mess with this.

View File

@ -37,7 +37,6 @@ import Foundation
var forcePolling: Bool { get }
var forceWebsockets: Bool { get }
var parseQueue: dispatch_queue_t! { get }
var pingTimer: NSTimer? { get }
var polling: Bool { get }
var probing: Bool { get }
var emitQueue: dispatch_queue_t! { get }
@ -50,13 +49,11 @@ import Foundation
init(client: SocketEngineClient, url: NSURL, options: NSDictionary?)
@available(*, deprecated=5.5, message="Please use disconnect") func close(reason: String)
func connect()
func didError(error: String)
func disconnect(reason: String)
func doFastUpgrade()
func flushWaitingForPostToWebSocket()
@available(*, deprecated=5.5, message="Please use connect") func open()
func parseEngineData(data: NSData)
func parseEngineMessage(message: String, fromPolling: Bool)
func write(msg: String, withType type: SocketEnginePacketType, withData data: [NSData])

View File

@ -26,7 +26,7 @@
import Foundation
/// Protocol that is used to implement socket.io WebSocket support
public protocol SocketEngineWebsocket: SocketEngineSpec, WebSocketDelegate {
public protocol SocketEngineWebsocket : SocketEngineSpec, WebSocketDelegate {
var ws: WebSocket? { get }
func sendWebSocketMessage(str: String, withType type: SocketEnginePacketType, withData datas: [NSData])

View File

@ -24,7 +24,7 @@
import Foundation
public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable {
public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable {
public let socketURL: NSURL
public private(set) var engine: SocketEngineSpec?
@ -46,7 +46,7 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
public var reconnects = true
public var reconnectWait = 10
public var sid: String? {
return engine?.sid
return nsp + "#" + (engine?.sid ?? "")
}
private let emitQueue = dispatch_queue_create("com.socketio.emitQueue", DISPATCH_QUEUE_SERIAL)
@ -108,18 +108,6 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
self.init(socketURL: socketURL, options: options?.toSocketOptionsSet() ?? [])
}
@available(*, deprecated=5.3, message="Please use the NSURL based init")
public convenience init(socketURLString: String, options: Set<SocketIOClientOption> = []) {
guard let url = NSURL(string: socketURLString) else { fatalError("Incorrect url") }
self.init(socketURL: url, options: options)
}
@available(*, deprecated=5.3, message="Please use the NSURL based init")
public convenience init(socketURLString: String, options: NSDictionary?) {
guard let url = NSURL(string: socketURLString) else { fatalError("Incorrect url") }
self.init(socketURL: url, options: options?.toSocketOptionsSet() ?? [])
}
deinit {
DefaultSocketLogger.Logger.log("Client is being released", type: logType)
engine?.disconnect("Client Deinit")
@ -133,11 +121,6 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
return engine!
}
@available(*, deprecated=5.3, message="Please use disconnect()")
public func close() {
disconnect()
}
/// Connect to the server.
public func connect() {
connect(timeoutAfter: 0, withTimeoutHandler: nil)
@ -181,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))
@ -240,38 +220,34 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
return
}
dispatch_async(emitQueue) {[emitData = [event] + items] in
self._emit(emitData)
}
_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)
}
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
@ -312,7 +288,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 ?? "")
@ -402,11 +378,6 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
anyHandler = handler
}
@available(*, deprecated=5.3, message="Please use one of the connect methods)")
public func open() {
connect()
}
public func parseEngineMessage(msg: String) {
DefaultSocketLogger.Logger.log("Should parse message: %@", type: "SocketIOClient", args: msg)
@ -443,15 +414,13 @@ public final class SocketIOClient: NSObject, SocketEngineClient, SocketParsable
}
}
@objc private func _tryReconnect() {
private func _tryReconnect() {
if !reconnecting {
return
}
if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts || !reconnects {
didDisconnect("Reconnect Failed")
return
return didDisconnect("Reconnect Failed")
}
DefaultSocketLogger.Logger.log("Trying to reconnect", type: logType)

View File

@ -24,11 +24,11 @@
import Foundation
protocol ClientOption: CustomStringConvertible, Hashable {
protocol ClientOption : CustomStringConvertible, Hashable {
func getSocketIOOptionValue() -> AnyObject
}
public enum SocketIOClientOption: ClientOption {
public enum SocketIOClientOption : ClientOption {
case ConnectParams([String: AnyObject])
case Cookies([NSHTTPCookie])
case DoubleEncodeUTF8(Bool)
@ -152,7 +152,7 @@ public func ==(lhs: SocketIOClientOption, rhs: SocketIOClientOption) -> Bool {
return lhs.description == rhs.description
}
extension Set where Element: ClientOption {
extension Set where Element : ClientOption {
mutating func insertIgnore(element: Element) {
if !contains(element) {
insert(element)

View File

@ -24,7 +24,7 @@
import Foundation
@objc public enum SocketIOClientStatus: Int, CustomStringConvertible {
@objc public enum SocketIOClientStatus : Int, CustomStringConvertible {
case NotConnected, Closed, Connecting, Connected
public var description: String {

View File

@ -24,7 +24,7 @@
import Foundation
public protocol SocketLogger: class {
public protocol SocketLogger : class {
/// Whether to log or not
var log: Bool {get set}
@ -54,7 +54,7 @@ public extension SocketLogger {
}
}
class DefaultSocketLogger: SocketLogger {
class DefaultSocketLogger : SocketLogger {
static var Logger: SocketLogger = DefaultSocketLogger()
var log = false

View File

@ -22,7 +22,7 @@
import Foundation
protocol SocketParsable: SocketClientSpec {
protocol SocketParsable : SocketClientSpec {
func parseBinaryData(data: NSData)
func parseSocketMessage(message: String)
}
@ -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 {
@ -116,26 +116,27 @@ 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))
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<String, [AnyObject]> {
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")
}

View File

@ -18,7 +18,7 @@ infix operator <~ { associativity none precedence 130 }
private let lock = dispatch_semaphore_create(1)
private var swiftRegexCache = [String: NSRegularExpression]()
internal final class SwiftRegex: NSObject, BooleanType {
internal final class SwiftRegex : NSObject, BooleanType {
var target: String
var regex: NSRegularExpression

View File

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