Merge branch 'development'
* development: (54 commits) refactor refactors clean up some things before xcode8 release remove testing of test branch Use xcodebuild until xctool is updated travis supports the gm update readme Turn off travis until it's updated Fix tests Fix truncation of binary data, use range operator for clarity Update for Xcode 8 GM Don't parse twice for errors use guarded try change spm name Modify mac version base mac os 10.9 open socket manager refactor Fix swift3 errors that happened during merge Switch to using Any instead of AnyObject varios tweaks ...
This commit is contained in:
commit
f051962e81
@ -1,9 +1,13 @@
|
||||
language: objective-c
|
||||
xcode_project: Socket.IO-Client-Swift.xcodeproj # path to your xcodeproj folder
|
||||
xcode_scheme: SocketIO-iOS
|
||||
osx_image: xcode7.3
|
||||
osx_image: xcode8
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- development
|
||||
script: xctool -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO-Mac build test -parallelize
|
||||
before_install:
|
||||
- brew update
|
||||
- brew outdated xctool || brew upgrade xctool
|
||||
# script: xctool -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO-Mac build test -parallelize
|
||||
script: xcodebuild -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO-Mac build test
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "SocketIOClientSwift"
|
||||
name: "SocketIO"
|
||||
)
|
||||
|
||||
69
README.md
69
README.md
@ -6,8 +6,7 @@ Socket.IO-client for iOS/OS X.
|
||||
##Example
|
||||
```swift
|
||||
import SocketIO
|
||||
|
||||
let socket = SocketIOClient(socketURL: NSURL(string: "http://localhost:8080")!, config: [.Log(true), .ForcePolling(true)])
|
||||
let socket = SocketIOClient(socketURL: NSURL(string: "http://localhost:8080")!, config: [.log(true), .forcePolling(true)])
|
||||
|
||||
socket.on("connect") {data, ack in
|
||||
print("socket connected")
|
||||
@ -58,13 +57,15 @@ SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{
|
||||
- Can be used from Objective-C
|
||||
|
||||
##Installation
|
||||
Requires Swift 2.2/Xcode 7.3
|
||||
Requires Swift 3/Xcode 8.x
|
||||
|
||||
If you need Swift 2.1/Xcode 7.2 use v5.5.0 (Pre-Swift 2.2 support is no longer maintained)
|
||||
If you need swift 2.2 use 7.x (Pre-Swift 3 support is no longer maintained)
|
||||
|
||||
If you need Swift 1.2/Xcode 6.3/4 use v2.4.5 (Pre-Swift 2 support is no longer maintained)
|
||||
If you need Swift 2.1 use v5.5.0 (Pre-Swift 2.2 support is no longer maintained)
|
||||
|
||||
If you need Swift 1.1/Xcode 6.2 use v1.5.2. (Pre-Swift 1.2 support is no longer maintained)
|
||||
If you need Swift 1.2 use v2.4.5 (Pre-Swift 2 support is no longer maintained)
|
||||
|
||||
If you need Swift 1.1 use v1.5.2. (Pre-Swift 1.2 support is no longer maintained)
|
||||
|
||||
Manually (iOS 7+)
|
||||
-----------------
|
||||
@ -151,43 +152,43 @@ Options
|
||||
All options are a case of SocketIOClientOption. To get the Objective-C Option, convert the name to lowerCamelCase.
|
||||
|
||||
```swift
|
||||
case ConnectParams([String: AnyObject]) // Dictionary whose contents will be passed with the connection.
|
||||
case Cookies([NSHTTPCookie]) // An array of NSHTTPCookies. Passed during the handshake. Default is nil.
|
||||
case DoubleEncodeUTF8(Bool) // Whether or not to double encode utf8. If using the node based server this should be true. Default is true.
|
||||
case ExtraHeaders([String: String]) // Adds custom headers to the initial request. Default is nil.
|
||||
case ForcePolling(Bool) // `true` forces the client to use xhr-polling. Default is `false`
|
||||
case ForceNew(Bool) // Will a create a new engine for each connect. Useful if you find a bug in the engine related to reconnects
|
||||
case ForceWebsockets(Bool) // `true` forces the client to use WebSockets. Default is `false`
|
||||
case HandleQueue(dispatch_queue_t) // The dispatch queue that handlers are run on. Default is the main queue.
|
||||
case Log(Bool) // If `true` socket will log debug messages. Default is false.
|
||||
case Logger(SocketLogger) // Custom logger that conforms to SocketLogger. Will use the default logging otherwise.
|
||||
case Nsp(String) // The namespace to connect to. Must begin with /. Default is `/`
|
||||
case Path(String) // If the server uses a custom path. ex: `"/swift/"`. Default is `""`
|
||||
case Reconnects(Bool) // Whether to reconnect on server lose. Default is `true`
|
||||
case ReconnectAttempts(Int) // How many times to reconnect. Default is `-1` (infinite tries)
|
||||
case ReconnectWait(Int) // Amount of time to wait between reconnects. Default is `10`
|
||||
case SessionDelegate(NSURLSessionDelegate) // Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs. Default is nil.
|
||||
case Secure(Bool) // If the connection should use TLS. Default is false.
|
||||
case Security(SSLSecurity) // Allows you to set which certs are valid. Useful for SSL pinning.
|
||||
case SelfSigned(Bool) // Sets WebSocket.selfSignedSSL. Use this if you're using self-signed certs.
|
||||
case VoipEnabled(Bool) // Only use this option if you're using the client with VoIP services. Changes the way the WebSocket is created. Default is false
|
||||
case connectParams([String: AnyObject]) // Dictionary whose contents will be passed with the connection.
|
||||
case cookies([NSHTTPCookie]) // An array of NSHTTPCookies. Passed during the handshake. Default is nil.
|
||||
case doubleEncodeUTF8(Bool) // Whether or not to double encode utf8. If using the node based server this should be true. Default is true.
|
||||
case extraHeaders([String: String]) // Adds custom headers to the initial request. Default is nil.
|
||||
case forcePolling(Bool) // `true` forces the client to use xhr-polling. Default is `false`
|
||||
case forceNew(Bool) // Will a create a new engine for each connect. Useful if you find a bug in the engine related to reconnects
|
||||
case forceWebsockets(Bool) // `true` forces the client to use WebSockets. Default is `false`
|
||||
case handleQueue(dispatch_queue_t) // The dispatch queue that handlers are run on. Default is the main queue.
|
||||
case log(Bool) // If `true` socket will log debug messages. Default is false.
|
||||
case logger(SocketLogger) // Custom logger that conforms to SocketLogger. Will use the default logging otherwise.
|
||||
case nsp(String) // The namespace to connect to. Must begin with /. Default is `/`
|
||||
case path(String) // If the server uses a custom path. ex: `"/swift/"`. Default is `""`
|
||||
case reconnects(Bool) // Whether to reconnect on server lose. Default is `true`
|
||||
case reconnectAttempts(Int) // How many times to reconnect. Default is `-1` (infinite tries)
|
||||
case reconnectWait(Int) // Amount of time to wait between reconnects. Default is `10`
|
||||
case sessionDelegate(NSURLSessionDelegate) // Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs. Default is nil.
|
||||
case secure(Bool) // If the connection should use TLS. Default is false.
|
||||
case security(SSLSecurity) // Allows you to set which certs are valid. Useful for SSL pinning.
|
||||
case selfSigned(Bool) // Sets WebSocket.selfSignedSSL. Use this if you're using self-signed certs.
|
||||
case voipEnabled(Bool) // Only use this option if you're using the client with VoIP services. Changes the way the WebSocket is created. Default is false
|
||||
```
|
||||
Methods
|
||||
-------
|
||||
1. `on(event: String, callback: NormalCallback) -> NSUUID` - Adds a handler for an event. Items are passed by an array. `ack` can be used to send an ack when one is requested. See example. Returns a unique id for the handler.
|
||||
2. `once(event: String, callback: NormalCallback) -> NSUUID` - Adds a handler that will only be executed once. Returns a unique id for the handler.
|
||||
1. `on(_ event: String, callback: NormalCallback) -> NSUUID` - Adds a handler for an event. Items are passed by an array. `ack` can be used to send an ack when one is requested. See example. Returns a unique id for the handler.
|
||||
2. `once(_ event: String, callback: NormalCallback) -> NSUUID` - Adds a handler that will only be executed once. Returns a unique id for the handler.
|
||||
3. `onAny(callback:((event: String, items: AnyObject?)) -> Void)` - Adds a handler for all events. It will be called on any received event.
|
||||
4. `emit(event: String, _ items: AnyObject...)` - Sends a message. Can send multiple items.
|
||||
5. `emit(event: String, withItems items: [AnyObject])` - `emit` for Objective-C
|
||||
6. `emitWithAck(event: String, _ items: AnyObject...) -> (timeoutAfter: UInt64, callback: (NSArray?) -> Void) -> Void` - Sends a message that requests an acknowledgement from the server. Returns a function which you can use to add a handler. See example. Note: The message is not sent until you call the returned function.
|
||||
7. `emitWithAck(event: String, withItems items: [AnyObject]) -> (UInt64, (NSArray?) -> Void) -> Void` - `emitWithAck` for Objective-C. Note: The message is not sent until you call the returned function.
|
||||
4. `emit(_ event: String, _ items: AnyObject...)` - Sends a message. Can send multiple items.
|
||||
5. `emit(_ event: String, withItems items: [AnyObject])` - `emit` for Objective-C
|
||||
6. `emitWithAck(_ event: String, _ items: AnyObject...) -> (timeoutAfter: UInt64, callback: (NSArray?) -> Void) -> Void` - Sends a message that requests an acknowledgement from the server. Returns a function which you can use to add a handler. See example. Note: The message is not sent until you call the returned function.
|
||||
7. `emitWithAck(_ event: String, withItems items: [AnyObject]) -> (UInt64, (NSArray?) -> Void) -> Void` - `emitWithAck` for Objective-C. Note: The message is not sent until you call the returned function.
|
||||
8. `connect()` - Establishes a connection to the server. A "connect" event is fired upon successful connection.
|
||||
9. `connect(timeoutAfter timeoutAfter: Int, withTimeoutHandler handler: (() -> Void)?)` - Connect to the server. If it isn't connected after timeoutAfter seconds, the handler is called.
|
||||
10. `disconnect()` - Closes the socket. Reopening a disconnected socket is not fully tested.
|
||||
11. `reconnect()` - Causes the client to reconnect to the server.
|
||||
12. `joinNamespace(namespace: String)` - Causes the client to join namespace. Shouldn't need to be called unless you change namespaces manually.
|
||||
12. `joinNamespace(_ namespace: String)` - Causes the client to join namespace. Shouldn't need to be called unless you change namespaces manually.
|
||||
13. `leaveNamespace()` - Causes the client to leave the nsp and go back to /
|
||||
14. `off(event: String)` - Removes all event handlers for event.
|
||||
14. `off(_ event: String)` - Removes all event handlers for event.
|
||||
15. `off(id id: NSUUID)` - Removes the event that corresponds to id.
|
||||
16. `removeAllHandlers()` - Removes all handlers.
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ Pod::Spec.new do |s|
|
||||
s.license = { :type => 'MIT' }
|
||||
s.author = { "Erik" => "nuclear.ace@gmail.com" }
|
||||
s.ios.deployment_target = '8.0'
|
||||
s.osx.deployment_target = '10.10'
|
||||
s.osx.deployment_target = '10.9'
|
||||
s.tvos.deployment_target = '9.0'
|
||||
s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v7.0.3' }
|
||||
s.source_files = "Source/**/*.swift"
|
||||
|
||||
@ -531,8 +531,9 @@
|
||||
572EF20E1B51F12F00EEBB58 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftMigration = 0730;
|
||||
LastSwiftUpdateCheck = 0730;
|
||||
LastUpgradeCheck = 0720;
|
||||
LastUpgradeCheck = 0800;
|
||||
TargetAttributes = {
|
||||
572EF2181B51F16C00EEBB58 = {
|
||||
CreatedOnToolsVersion = 6.4;
|
||||
@ -542,9 +543,11 @@
|
||||
};
|
||||
572EF2371B51F18A00EEBB58 = {
|
||||
CreatedOnToolsVersion = 6.4;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
572EF2411B51F18A00EEBB58 = {
|
||||
CreatedOnToolsVersion = 6.4;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -771,15 +774,31 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BITCODE_GENERATION_MODE = bitcode;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = SocketIO;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 2.0;
|
||||
};
|
||||
@ -789,12 +808,28 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BITCODE_GENERATION_MODE = bitcode;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
PRODUCT_NAME = SocketIO;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 3.0;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
WATCHOS_DEPLOYMENT_TARGET = 2.0;
|
||||
};
|
||||
@ -851,6 +886,7 @@
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@ -901,6 +937,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.socket.SocketIOClientSwift;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 3.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@ -1057,6 +1094,7 @@
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
@ -1108,6 +1146,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "io.socket.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 3.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
@ -1164,6 +1203,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -1210,6 +1250,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "io.socket.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0720"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0720"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0720"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@ -13,14 +13,14 @@ class SocketAckManagerTest: XCTestCase {
|
||||
var ackManager = SocketAckManager()
|
||||
|
||||
func testAddAcks() {
|
||||
let callbackExpection = self.expectationWithDescription("callbackExpection")
|
||||
let callbackExpection = self.expectation(description: "callbackExpection")
|
||||
let itemsArray = ["Hi", "ho"]
|
||||
func callback(items: [AnyObject]) {
|
||||
func callback(_ items: [Any]) {
|
||||
callbackExpection.fulfill()
|
||||
}
|
||||
ackManager.addAck(1, callback: callback)
|
||||
ackManager.executeAck(1, items: itemsArray, onQueue: dispatch_get_main_queue())
|
||||
waitForExpectationsWithTimeout(3.0, handler: nil)
|
||||
|
||||
ackManager.executeAck(1, with: itemsArray, onQueue: DispatchQueue.main)
|
||||
|
||||
waitForExpectations(timeout: 3.0, handler: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,8 +10,8 @@ import XCTest
|
||||
@testable import SocketIO
|
||||
|
||||
class SocketBasicPacketTest: XCTestCase {
|
||||
let data = "test".dataUsingEncoding(NSUTF8StringEncoding)!
|
||||
let data2 = "test2".dataUsingEncoding(NSUTF8StringEncoding)!
|
||||
let data = "test".data(using: String.Encoding.utf8)!
|
||||
let data2 = "test2".data(using: String.Encoding.utf8)!
|
||||
|
||||
func testEmpyEmit() {
|
||||
let expectedSendString = "2[\"test\"]"
|
||||
@ -23,7 +23,7 @@ class SocketBasicPacketTest: XCTestCase {
|
||||
|
||||
func testNullEmit() {
|
||||
let expectedSendString = "2[\"test\",null]"
|
||||
let sendData = ["test", NSNull()]
|
||||
let sendData: [Any] = ["test", NSNull()]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
@ -46,8 +46,8 @@ class SocketBasicPacketTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testJSONEmit() {
|
||||
let expectedSendString = "2[\"test\",{\"test\":\"hello\",\"hello\":1,\"foobar\":true,\"null\":null}]"
|
||||
let sendData = ["test", ["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]]
|
||||
let expectedSendString = "2[\"test\",{\"null\":null,\"hello\":1,\"test\":\"hello\",\"foobar\":true}]"
|
||||
let sendData: [Any] = ["test", ["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
@ -55,15 +55,15 @@ class SocketBasicPacketTest: XCTestCase {
|
||||
|
||||
func testArrayEmit() {
|
||||
let expectedSendString = "2[\"test\",[\"hello\",1,{\"test\":\"test\"}]]"
|
||||
let sendData = ["test", ["hello", 1, ["test": "test"]]]
|
||||
let sendData: [Any] = ["test", ["hello", 1, ["test": "test"]]]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
}
|
||||
|
||||
func testBinaryEmit() {
|
||||
let expectedSendString = "51-[\"test\",{\"num\":0,\"_placeholder\":true}]"
|
||||
let sendData = ["test", data]
|
||||
let expectedSendString = "51-[\"test\",{\"_placeholder\":true,\"num\":0}]"
|
||||
let sendData: [Any] = ["test", data]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
@ -71,12 +71,12 @@ class SocketBasicPacketTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testMultipleBinaryEmit() {
|
||||
let expectedSendString = "52-[\"test\",{\"data1\":{\"num\":0,\"_placeholder\":true},\"data2\":{\"num\":1,\"_placeholder\":true}}]"
|
||||
let sendData = ["test", ["data1": data, "data2": data2]]
|
||||
let expectedSendString = "52-[\"test\",{\"data2\":{\"_placeholder\":true,\"num\":0},\"data1\":{\"_placeholder\":true,\"num\":1}}]"
|
||||
let sendData: [Any] = ["test", ["data1": data, "data2": data2] as NSDictionary]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
XCTAssertEqual(packet.binary, [data, data2])
|
||||
XCTAssertEqual(packet.binary, [data2, data])
|
||||
}
|
||||
|
||||
func testEmitWithAck() {
|
||||
@ -84,12 +84,14 @@ class SocketBasicPacketTest: XCTestCase {
|
||||
let sendData = ["test"]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
XCTAssertEqual(packet.packetString,
|
||||
|
||||
expectedSendString)
|
||||
}
|
||||
|
||||
func testEmitDataWithAck() {
|
||||
let expectedSendString = "51-0[\"test\",{\"num\":0,\"_placeholder\":true}]"
|
||||
let sendData = ["test", data]
|
||||
let expectedSendString = "51-0[\"test\",{\"_placeholder\":true,\"num\":0}]"
|
||||
let sendData: [Any] = ["test", data]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
@ -121,7 +123,7 @@ class SocketBasicPacketTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testJSONAck() {
|
||||
let expectedSendString = "30[{\"test\":\"hello\",\"hello\":1,\"foobar\":true,\"null\":null}]"
|
||||
let expectedSendString = "30[{\"null\":null,\"hello\":1,\"test\":\"hello\",\"foobar\":true}]"
|
||||
let sendData = [["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: true)
|
||||
|
||||
@ -129,7 +131,7 @@ class SocketBasicPacketTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testBinaryAck() {
|
||||
let expectedSendString = "61-0[{\"num\":0,\"_placeholder\":true}]"
|
||||
let expectedSendString = "61-0[{\"_placeholder\":true,\"num\":0}]"
|
||||
let sendData = [data]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: true)
|
||||
|
||||
@ -138,7 +140,7 @@ class SocketBasicPacketTest: XCTestCase {
|
||||
}
|
||||
|
||||
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 packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: true)
|
||||
|
||||
@ -147,15 +149,15 @@ class SocketBasicPacketTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testBinaryStringPlaceholderInMessage() {
|
||||
let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"num\":1,\"_placeholder\":true}]"
|
||||
let socket = SocketIOClient(socketURL: NSURL())
|
||||
let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"_placeholder\":true,\"num\":1}]"
|
||||
let socket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
|
||||
socket.setTestable()
|
||||
|
||||
if case let .Right(packet) = socket.parseString(engineString) {
|
||||
if case let .right(packet) = socket.parseString(engineString) {
|
||||
var packet = packet
|
||||
XCTAssertEqual(packet.event, "test")
|
||||
packet.addData(data)
|
||||
packet.addData(data2)
|
||||
_ = packet.addData(data)
|
||||
_ = packet.addData(data2)
|
||||
XCTAssertEqual(packet.args[0] as? String, "~~0")
|
||||
} else {
|
||||
XCTFail()
|
||||
|
||||
@ -15,24 +15,24 @@ class SocketEngineTest: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
client = SocketIOClient(socketURL: NSURL(string: "http://localhost")!)
|
||||
engine = SocketEngine(client: client, url: NSURL(string: "http://localhost")!, options: nil)
|
||||
client = SocketIOClient(socketURL: URL(string: "http://localhost")!)
|
||||
engine = SocketEngine(client: client, url: URL(string: "http://localhost")!, options: nil)
|
||||
|
||||
client.setTestable()
|
||||
}
|
||||
|
||||
func testBasicPollingMessage() {
|
||||
let expectation = expectationWithDescription("Basic polling test")
|
||||
let expect = expectation(description: "Basic polling test")
|
||||
client.on("blankTest") {data, ack in
|
||||
expectation.fulfill()
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
engine.parsePollingMessage("15:42[\"blankTest\"]")
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testTwoPacketsInOnePollTest() {
|
||||
let finalExpectation = expectationWithDescription("Final packet in poll test")
|
||||
let finalExpectation = expectation(description: "Final packet in poll test")
|
||||
var gotBlank = false
|
||||
|
||||
client.on("blankTest") {data, ack in
|
||||
@ -40,7 +40,7 @@ class SocketEngineTest: XCTestCase {
|
||||
}
|
||||
|
||||
client.on("stringTest") {data, ack in
|
||||
if let str = data[0] as? String where gotBlank {
|
||||
if let str = data[0] as? String, gotBlank {
|
||||
if str == "hello" {
|
||||
finalExpectation.fulfill()
|
||||
}
|
||||
@ -48,43 +48,43 @@ class SocketEngineTest: XCTestCase {
|
||||
}
|
||||
|
||||
engine.parsePollingMessage("15:42[\"blankTest\"]24:42[\"stringTest\",\"hello\"]")
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testEngineDoesErrorOnUnknownTransport() {
|
||||
let finalExpectation = expectationWithDescription("Unknown Transport")
|
||||
let finalExpectation = expectation(description: "Unknown Transport")
|
||||
|
||||
client.on("error") {data, ack in
|
||||
if let error = data[0] as? String where error == "Unknown transport" {
|
||||
if let error = data[0] as? String, error == "Unknown transport" {
|
||||
finalExpectation.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
engine.parseEngineMessage("{\"code\": 0, \"message\": \"Unknown transport\"}", fromPolling: false)
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testEngineDoesErrorOnUnknownMessage() {
|
||||
let finalExpectation = expectationWithDescription("Engine Errors")
|
||||
let finalExpectation = expectation(description: "Engine Errors")
|
||||
|
||||
client.on("error") {data, ack in
|
||||
finalExpectation.fulfill()
|
||||
}
|
||||
|
||||
engine.parseEngineMessage("afafafda", fromPolling: false)
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testEngineDecodesUTF8Properly() {
|
||||
let expectation = expectationWithDescription("Engine Decodes utf8")
|
||||
let expect = expectation(description: "Engine Decodes utf8")
|
||||
|
||||
client.on("stringTest") {data, ack in
|
||||
XCTAssertEqual(data[0] as? String, "lïne one\nlīne \rtwo", "Failed string test")
|
||||
expectation.fulfill()
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
engine.parsePollingMessage("41:42[\"stringTest\",\"lïne one\\nlīne \\rtwo\"]")
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testEncodeURLProperly() {
|
||||
@ -102,4 +102,23 @@ class SocketEngineTest: XCTestCase {
|
||||
XCTAssertEqual(engine.urlPolling.query, "transport=polling&b64=1&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D")
|
||||
XCTAssertEqual(engine.urlWebSocket.query, "transport=websocket&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D")
|
||||
}
|
||||
|
||||
func testBase64Data() {
|
||||
let expect = expectation(description: "Engine Decodes base64 data")
|
||||
let b64String = "b4aGVsbG8NCg=="
|
||||
let packetString = "451-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]"
|
||||
|
||||
client.on("test") {data, ack in
|
||||
if let data = data[0] as? Data, let string = String(data: data, encoding: .utf8) {
|
||||
XCTAssertEqual(string, "hello")
|
||||
}
|
||||
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
engine.parseEngineMessage(packetString, fromPolling: false)
|
||||
engine.parseEngineMessage(b64String, fromPolling: false)
|
||||
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,16 +15,16 @@ class TestSocketIOClientConfiguration: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
config = [.Log(false), .ForceNew(true)]
|
||||
config = [.log(false), .forceNew(true)]
|
||||
}
|
||||
|
||||
func testReplaceSameOption() {
|
||||
config.insert(.Log(true))
|
||||
config.insert(.log(true))
|
||||
|
||||
XCTAssertEqual(config.count, 2)
|
||||
|
||||
switch config[0] {
|
||||
case let .Log(log):
|
||||
case let .log(log):
|
||||
XCTAssertTrue(log)
|
||||
default:
|
||||
XCTFail()
|
||||
@ -32,12 +32,12 @@ class TestSocketIOClientConfiguration: XCTestCase {
|
||||
}
|
||||
|
||||
func testIgnoreIfExisting() {
|
||||
config.insert(.ForceNew(false), replacing: false)
|
||||
config.insert(.forceNew(false), replacing: false)
|
||||
|
||||
XCTAssertEqual(config.count, 2)
|
||||
|
||||
switch config[1] {
|
||||
case let .ForceNew(new):
|
||||
case let .forceNew(new):
|
||||
XCTAssertTrue(new)
|
||||
default:
|
||||
XCTFail()
|
||||
|
||||
@ -10,12 +10,12 @@ import XCTest
|
||||
@testable import SocketIO
|
||||
|
||||
class SocketNamespacePacketTest: XCTestCase {
|
||||
let data = "test".dataUsingEncoding(NSUTF8StringEncoding)!
|
||||
let data2 = "test2".dataUsingEncoding(NSUTF8StringEncoding)!
|
||||
let data = "test".data(using: String.Encoding.utf8)!
|
||||
let data2 = "test2".data(using: String.Encoding.utf8)!
|
||||
|
||||
func testEmpyEmit() {
|
||||
let expectedSendString = "2/swift,[\"test\"]"
|
||||
let sendData = ["test"]
|
||||
let sendData: [Any] = ["test"]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
@ -23,7 +23,7 @@ class SocketNamespacePacketTest: XCTestCase {
|
||||
|
||||
func testNullEmit() {
|
||||
let expectedSendString = "2/swift,[\"test\",null]"
|
||||
let sendData = ["test", NSNull()]
|
||||
let sendData: [Any] = ["test", NSNull()]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
@ -31,31 +31,32 @@ class SocketNamespacePacketTest: XCTestCase {
|
||||
|
||||
func testStringEmit() {
|
||||
let expectedSendString = "2/swift,[\"test\",\"foo bar\"]"
|
||||
let sendData = ["test", "foo bar"]
|
||||
let sendData: [Any] = ["test", "foo bar"]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
}
|
||||
|
||||
func testJSONEmit() {
|
||||
let expectedSendString = "2/swift,[\"test\",{\"test\":\"hello\",\"hello\":1,\"foobar\":true,\"null\":null}]"
|
||||
let sendData = ["test", ["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]]
|
||||
let expectedSendString = "2/swift,[\"test\",{\"null\":null,\"test\":\"hello\",\"hello\":1,\"foobar\":true}]"
|
||||
let sendData: [Any] = ["test", ["foobar": true, "hello": 1, "test": "hello", "null": NSNull()] as NSDictionary]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
}
|
||||
|
||||
func testArrayEmit() {
|
||||
let expectedSendString = "2/swift,[\"test\",[\"hello\",1,{\"test\":\"test\"}]]"
|
||||
let sendData = ["test", ["hello", 1, ["test": "test"]]]
|
||||
let expectedSendString = "2/swift,[\"test\",[\"hello\",1,{\"test\":\"test\"},true]]"
|
||||
let sendData: [Any] = ["test", ["hello", 1, ["test": "test"], true]]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
|
||||
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
}
|
||||
|
||||
func testBinaryEmit() {
|
||||
let expectedSendString = "51-/swift,[\"test\",{\"num\":0,\"_placeholder\":true}]"
|
||||
let sendData = ["test", data]
|
||||
let expectedSendString = "51-/swift,[\"test\",{\"_placeholder\":true,\"num\":0}]"
|
||||
let sendData: [Any] = ["test", data]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
@ -63,12 +64,12 @@ class SocketNamespacePacketTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testMultipleBinaryEmit() {
|
||||
let expectedSendString = "52-/swift,[\"test\",{\"data1\":{\"num\":0,\"_placeholder\":true},\"data2\":{\"num\":1,\"_placeholder\":true}}]"
|
||||
let sendData = ["test", ["data1": data, "data2": data2]]
|
||||
let expectedSendString = "52-/swift,[\"test\",{\"data2\":{\"_placeholder\":true,\"num\":0},\"data1\":{\"_placeholder\":true,\"num\":1}}]"
|
||||
let sendData: [Any] = ["test", ["data1": data, "data2": data2] as NSDictionary]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
XCTAssertEqual(packet.binary, [data, data2])
|
||||
XCTAssertEqual(packet.binary, [data2, data])
|
||||
}
|
||||
|
||||
func testEmitWithAck() {
|
||||
@ -80,8 +81,8 @@ class SocketNamespacePacketTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testEmitDataWithAck() {
|
||||
let expectedSendString = "51-/swift,0[\"test\",{\"num\":0,\"_placeholder\":true}]"
|
||||
let sendData = ["test", data]
|
||||
let expectedSendString = "51-/swift,0[\"test\",{\"_placeholder\":true,\"num\":0}]"
|
||||
let sendData: [Any] = ["test", data]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: false)
|
||||
|
||||
XCTAssertEqual(packet.packetString, expectedSendString)
|
||||
@ -113,7 +114,7 @@ class SocketNamespacePacketTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testJSONAck() {
|
||||
let expectedSendString = "3/swift,0[{\"test\":\"hello\",\"hello\":1,\"foobar\":true,\"null\":null}]"
|
||||
let expectedSendString = "3/swift,0[{\"null\":null,\"hello\":1,\"test\":\"hello\",\"foobar\":true}]"
|
||||
let sendData = [["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: true)
|
||||
|
||||
@ -121,7 +122,7 @@ class SocketNamespacePacketTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testBinaryAck() {
|
||||
let expectedSendString = "61-/swift,0[{\"num\":0,\"_placeholder\":true}]"
|
||||
let expectedSendString = "61-/swift,0[{\"_placeholder\":true,\"num\":0}]"
|
||||
let sendData = [data]
|
||||
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: true)
|
||||
|
||||
@ -130,7 +131,7 @@ class SocketNamespacePacketTest: XCTestCase {
|
||||
}
|
||||
|
||||
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 packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: true)
|
||||
|
||||
|
||||
@ -31,11 +31,11 @@
|
||||
}
|
||||
|
||||
- (void)testEmitSyntax {
|
||||
[self.socket emit:@"testEmit" withItems:@[@YES]];
|
||||
[self.socket emit:@"testEmit" with:@[@YES]];
|
||||
}
|
||||
|
||||
- (void)testEmitWithAckSyntax {
|
||||
[self.socket emitWithAck:@"testAckEmit" withItems:@[@YES]](0, ^(NSArray* data) {
|
||||
[self.socket emitWithAck:@"testAckEmit" with:@[@YES]](0, ^(NSArray* data) {
|
||||
|
||||
});
|
||||
}
|
||||
@ -47,7 +47,7 @@
|
||||
- (void)testSocketManager {
|
||||
SocketClientManager* manager = [SocketClientManager sharedManager];
|
||||
[manager addSocket:self.socket labeledAs:@"test"];
|
||||
[manager removeSocketWithLabel:@"test"];
|
||||
[manager removeSocketWithLabel:@"test"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -10,24 +10,25 @@ import XCTest
|
||||
@testable import SocketIO
|
||||
|
||||
class SocketParserTest: XCTestCase {
|
||||
let testSocket = SocketIOClient(socketURL: NSURL())
|
||||
let testSocket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
|
||||
|
||||
//Format key: message; namespace-data-binary-id
|
||||
static let packetTypes: [String: (String, [AnyObject], [NSData], Int)] = [
|
||||
static let packetTypes: [String: (String, [Any], [Data], Int)] = [
|
||||
"0": ("/", [], [], -1), "1": ("/", [], [], -1),
|
||||
"25[\"test\"]": ("/", ["test"], [], 5),
|
||||
"2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1),
|
||||
"2/swift,[\"testArrayEmitReturn\",[\"test3\",\"test4\"]]": ("/swift", ["testArrayEmitReturn", ["test3", "test4"]], [], -1),
|
||||
"51-/swift,[\"testMultipleItemsWithBufferEmitReturn\",[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", ["testMultipleItemsWithBufferEmitReturn", [1, 2], ["test": "bob"], 25, "polo", ["_placeholder": true, "num": 0]], [], -1),
|
||||
"3/swift,0[[\"test3\",\"test4\"]]": ("/swift", [["test3", "test4"]], [], 0),
|
||||
"2/swift,[\"testArrayEmitReturn\",[\"test3\",\"test4\"]]": ("/swift", ["testArrayEmitReturn", ["test3", "test4"] as NSArray], [], -1),
|
||||
"51-/swift,[\"testMultipleItemsWithBufferEmitReturn\",[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", ["testMultipleItemsWithBufferEmitReturn", [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], -1),
|
||||
"3/swift,0[[\"test3\",\"test4\"]]": ("/swift", [["test3", "test4"] as NSArray], [], 0),
|
||||
"61-/swift,19[[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]":
|
||||
("/swift", [ [1, 2], ["test": "bob"], 25, "polo", ["_placeholder": true, "num": 0]], [], 19),
|
||||
("/swift", [ [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], 19),
|
||||
"4/swift,": ("/swift", [], [], -1),
|
||||
"0/swift": ("/swift", [], [], -1),
|
||||
"1/swift": ("/swift", [], [], -1),
|
||||
"4\"ERROR\"": ("/", ["ERROR"], [], -1),
|
||||
"4{\"test\":2}": ("/", [["test": 2]], [], -1),
|
||||
"41": ("/", [1], [], -1)]
|
||||
"41": ("/", [1], [], -1),
|
||||
"4[1, \"hello\"]": ("/", [1, "hello"], [], -1)]
|
||||
|
||||
func testDisconnect() {
|
||||
let message = "1"
|
||||
@ -99,33 +100,38 @@ class SocketParserTest: XCTestCase {
|
||||
validateParseResult(message)
|
||||
}
|
||||
|
||||
func testErrorTypeArray() {
|
||||
let message = "4[1, \"hello\"]"
|
||||
validateParseResult(message)
|
||||
}
|
||||
|
||||
func testInvalidInput() {
|
||||
let message = "8"
|
||||
switch testSocket.parseString(message) {
|
||||
case .Left(_):
|
||||
case .left(_):
|
||||
return
|
||||
case .Right(_):
|
||||
case .right(_):
|
||||
XCTFail("Created packet when shouldn't have")
|
||||
}
|
||||
}
|
||||
|
||||
func testGenericParser() {
|
||||
var parser = SocketStringReader(message: "61-/swift,")
|
||||
XCTAssertEqual(parser.read(1), "6")
|
||||
XCTAssertEqual(parser.read(count: 1), "6")
|
||||
XCTAssertEqual(parser.currentCharacter, "1")
|
||||
XCTAssertEqual(parser.readUntilStringOccurence("-"), "1")
|
||||
XCTAssertEqual(parser.readUntilOccurence(of: "-"), "1")
|
||||
XCTAssertEqual(parser.currentCharacter, "/")
|
||||
}
|
||||
|
||||
func validateParseResult(message: String) {
|
||||
func validateParseResult(_ message: String) {
|
||||
let validValues = SocketParserTest.packetTypes[message]!
|
||||
let packet = testSocket.parseString(message)
|
||||
let type = message.substringWithRange(Range<String.Index>(message.startIndex..<message.startIndex.advancedBy(1)))
|
||||
if case let .Right(packet) = packet {
|
||||
let type = String(message.characters.prefix(1))
|
||||
if case let .right(packet) = packet {
|
||||
XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!)
|
||||
XCTAssertEqual(packet.nsp, validValues.0)
|
||||
XCTAssertTrue((packet.data as NSArray).isEqualToArray(validValues.1), "\(packet.data)")
|
||||
XCTAssertTrue((packet.binary as NSArray).isEqualToArray(validValues.2), "\(packet.binary)")
|
||||
XCTAssertTrue((packet.data as NSArray).isEqual(to: validValues.1), "\(packet.data)")
|
||||
XCTAssertTrue((packet.binary as NSArray).isEqual(to: validValues.2), "\(packet.binary)")
|
||||
XCTAssertEqual(packet.id, validValues.3)
|
||||
} else {
|
||||
XCTFail()
|
||||
@ -134,10 +140,10 @@ class SocketParserTest: XCTestCase {
|
||||
|
||||
func testParsePerformance() {
|
||||
let keys = Array(SocketParserTest.packetTypes.keys)
|
||||
measureBlock({
|
||||
for item in keys.enumerate() {
|
||||
self.testSocket.parseString(item.element)
|
||||
measure {
|
||||
for item in keys.enumerated() {
|
||||
_ = self.testSocket.parseString(item.element)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,13 +10,13 @@ import XCTest
|
||||
@testable import SocketIO
|
||||
|
||||
class SocketSideEffectTest: XCTestCase {
|
||||
let data = "test".dataUsingEncoding(NSUTF8StringEncoding)!
|
||||
let data2 = "test2".dataUsingEncoding(NSUTF8StringEncoding)!
|
||||
let data = "test".data(using: String.Encoding.utf8)!
|
||||
let data2 = "test2".data(using: String.Encoding.utf8)!
|
||||
private var socket: SocketIOClient!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
socket = SocketIOClient(socketURL: NSURL())
|
||||
socket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
|
||||
socket.setTestable()
|
||||
}
|
||||
|
||||
@ -25,72 +25,72 @@ class SocketSideEffectTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testFirstAck() {
|
||||
socket.emitWithAck("test")(timeoutAfter: 0) {data in}
|
||||
socket.emitWithAck("test")(0) {data in}
|
||||
XCTAssertEqual(socket.currentAck, 0)
|
||||
}
|
||||
|
||||
func testSecondAck() {
|
||||
socket.emitWithAck("test")(timeoutAfter: 0) {data in}
|
||||
socket.emitWithAck("test")(timeoutAfter: 0) {data in}
|
||||
socket.emitWithAck("test")(0) {data in}
|
||||
socket.emitWithAck("test")(0) {data in}
|
||||
|
||||
XCTAssertEqual(socket.currentAck, 1)
|
||||
}
|
||||
|
||||
func testHandleAck() {
|
||||
let expectation = expectationWithDescription("handled ack")
|
||||
socket.emitWithAck("test")(timeoutAfter: 0) {data in
|
||||
let expect = expectation(description: "handled ack")
|
||||
socket.emitWithAck("test")(0) {data in
|
||||
XCTAssertEqual(data[0] as? String, "hello world")
|
||||
expectation.fulfill()
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
socket.parseSocketMessage("30[\"hello world\"]")
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testHandleAck2() {
|
||||
let expectation = expectationWithDescription("handled ack2")
|
||||
socket.emitWithAck("test")(timeoutAfter: 0) {data in
|
||||
let expect = expectation(description: "handled ack2")
|
||||
socket.emitWithAck("test")(0) {data in
|
||||
XCTAssertTrue(data.count == 2, "Wrong number of ack items")
|
||||
expectation.fulfill()
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
socket.parseSocketMessage("61-0[{\"_placeholder\":true,\"num\":0},{\"test\":true}]")
|
||||
socket.parseBinaryData(NSData())
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
socket.parseBinaryData(Data())
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testHandleEvent() {
|
||||
let expectation = expectationWithDescription("handled event")
|
||||
let expect = expectation(description: "handled event")
|
||||
socket.on("test") {data, ack in
|
||||
XCTAssertEqual(data[0] as? String, "hello world")
|
||||
expectation.fulfill()
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
socket.parseSocketMessage("2[\"test\",\"hello world\"]")
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testHandleStringEventWithQuotes() {
|
||||
let expectation = expectationWithDescription("handled event")
|
||||
let expect = expectation(description: "handled event")
|
||||
socket.on("test") {data, ack in
|
||||
XCTAssertEqual(data[0] as? String, "\"hello world\"")
|
||||
expectation.fulfill()
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
socket.parseSocketMessage("2[\"test\",\"\\\"hello world\\\"\"]")
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testHandleOnceEvent() {
|
||||
let expectation = expectationWithDescription("handled event")
|
||||
let expect = expectation(description: "handled event")
|
||||
socket.once("test") {data, ack in
|
||||
XCTAssertEqual(data[0] as? String, "hello world")
|
||||
XCTAssertEqual(self.socket.testHandlers.count, 0)
|
||||
expectation.fulfill()
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
socket.parseSocketMessage("2[\"test\",\"hello world\"]")
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testOffWithEvent() {
|
||||
@ -112,46 +112,46 @@ class SocketSideEffectTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testHandlesErrorPacket() {
|
||||
let expectation = expectationWithDescription("Handled error")
|
||||
let expect = expectation(description: "Handled error")
|
||||
socket.on("error") {data, ack in
|
||||
if let error = data[0] as? String where error == "test error" {
|
||||
expectation.fulfill()
|
||||
if let error = data[0] as? String, error == "test error" {
|
||||
expect.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
socket.parseSocketMessage("4\"test error\"")
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testHandleBinaryEvent() {
|
||||
let expectation = expectationWithDescription("handled binary event")
|
||||
let expect = expectation(description: "handled binary event")
|
||||
socket.on("test") {data, ack in
|
||||
if let dict = data[0] as? NSDictionary, data = dict["test"] as? NSData {
|
||||
XCTAssertEqual(data, self.data)
|
||||
expectation.fulfill()
|
||||
if let dict = data[0] as? NSDictionary, let data = dict["test"] as? NSData {
|
||||
XCTAssertEqual(data as Data, self.data)
|
||||
expect.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
socket.parseSocketMessage("51-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]")
|
||||
socket.parseBinaryData(data)
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testHandleMultipleBinaryEvent() {
|
||||
let expectation = expectationWithDescription("handled multiple binary event")
|
||||
let expect = expectation(description: "handled multiple binary event")
|
||||
socket.on("test") {data, ack in
|
||||
if let dict = data[0] as? NSDictionary, data = dict["test"] as? NSData,
|
||||
data2 = dict["test2"] as? NSData {
|
||||
XCTAssertEqual(data, self.data)
|
||||
XCTAssertEqual(data2, self.data2)
|
||||
expectation.fulfill()
|
||||
if let dict = data[0] as? NSDictionary, let data = dict["test"] as? NSData,
|
||||
let data2 = dict["test2"] as? NSData {
|
||||
XCTAssertEqual(data as Data, self.data)
|
||||
XCTAssertEqual(data2 as Data, self.data2)
|
||||
expect.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
socket.parseSocketMessage("52-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0},\"test2\":{\"_placeholder\":true,\"num\":1}}]")
|
||||
socket.parseBinaryData(data)
|
||||
socket.parseBinaryData(data2)
|
||||
waitForExpectationsWithTimeout(3, handler: nil)
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
func testSocketManager() {
|
||||
|
||||
@ -24,8 +24,8 @@ import Foundation
|
||||
import Security
|
||||
|
||||
public class SSLCert : NSObject {
|
||||
var certData: NSData?
|
||||
var key: SecKeyRef?
|
||||
var certData: Data?
|
||||
var key: SecKey?
|
||||
|
||||
/**
|
||||
Designated init for certificates
|
||||
@ -34,7 +34,7 @@ public class SSLCert : NSObject {
|
||||
|
||||
- returns: a representation security object to be used with
|
||||
*/
|
||||
public init(data: NSData) {
|
||||
public init(data: Data) {
|
||||
self.certData = data
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ public class SSLCert : NSObject {
|
||||
|
||||
- returns: a representation security object to be used with
|
||||
*/
|
||||
public init(key: SecKeyRef) {
|
||||
public init(key: SecKey) {
|
||||
self.key = key
|
||||
}
|
||||
}
|
||||
@ -55,7 +55,7 @@ public class SSLSecurity : NSObject {
|
||||
|
||||
var isReady = false //is the key processing done?
|
||||
var certificates: [NSData]? //the certificates
|
||||
@nonobjc var pubKeys: [SecKeyRef]? //the public keys
|
||||
@nonobjc var pubKeys: [SecKey]? //the public keys
|
||||
var usePublicKeys = false //use public keys or certificate validation?
|
||||
|
||||
/**
|
||||
@ -66,12 +66,12 @@ public class SSLSecurity : NSObject {
|
||||
- returns: a representation security object to be used with
|
||||
*/
|
||||
public convenience init(usePublicKeys: Bool = false) {
|
||||
let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".")
|
||||
let paths = Bundle.main.paths(forResourcesOfType: "cer", inDirectory: ".")
|
||||
|
||||
let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in
|
||||
var certs = certs
|
||||
if let data = NSData(contentsOfFile: path) {
|
||||
certs.append(SSLCert(data: data))
|
||||
certs.append(SSLCert(data: data as Data))
|
||||
}
|
||||
return certs
|
||||
}
|
||||
@ -93,10 +93,10 @@ public class SSLSecurity : NSObject {
|
||||
super.init()
|
||||
|
||||
if self.usePublicKeys {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) {
|
||||
let pubKeys = certs.reduce([SecKeyRef]()) { (pubKeys: [SecKeyRef], cert: SSLCert) -> [SecKeyRef] in
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
let pubKeys = certs.reduce([SecKey]()) { (pubKeys: [SecKey], cert: SSLCert) -> [SecKey] in
|
||||
var pubKeys = pubKeys
|
||||
if let data = cert.certData where cert.key == nil {
|
||||
if let data = cert.certData, cert.key == nil {
|
||||
cert.key = self.extractPublicKey(data)
|
||||
}
|
||||
if let key = cert.key {
|
||||
@ -109,14 +109,14 @@ public class SSLSecurity : NSObject {
|
||||
self.isReady = true
|
||||
}
|
||||
} else {
|
||||
let certificates = certs.reduce([NSData]()) { (certificates: [NSData], cert: SSLCert) -> [NSData] in
|
||||
let certificates = certs.reduce([Data]()) { (certificates: [Data], cert: SSLCert) -> [Data] in
|
||||
var certificates = certificates
|
||||
if let data = cert.certData {
|
||||
certificates.append(data)
|
||||
}
|
||||
return certificates
|
||||
}
|
||||
self.certificates = certificates
|
||||
self.certificates = certificates as [NSData]
|
||||
self.isReady = true
|
||||
}
|
||||
}
|
||||
@ -129,7 +129,7 @@ public class SSLSecurity : NSObject {
|
||||
|
||||
- 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
|
||||
while(!self.isReady) {
|
||||
@ -139,19 +139,19 @@ public class SSLSecurity : NSObject {
|
||||
return false //doesn't appear it is going to ever be ready...
|
||||
}
|
||||
}
|
||||
var policy: SecPolicyRef
|
||||
var policy: SecPolicy
|
||||
if self.validatedDN {
|
||||
policy = SecPolicyCreateSSL(true, domain)
|
||||
policy = SecPolicyCreateSSL(true, domain as CFString?)
|
||||
} else {
|
||||
policy = SecPolicyCreateBasicX509()
|
||||
}
|
||||
SecTrustSetPolicies(trust,policy)
|
||||
if self.usePublicKeys {
|
||||
if let keys = self.pubKeys {
|
||||
let serverPubKeys = publicKeyChainForTrust(trust)
|
||||
for serverKey in serverPubKeys as [AnyObject] {
|
||||
for key in keys as [AnyObject] {
|
||||
if serverKey.isEqual(key) {
|
||||
let serverPubKeys = publicKeyChainForTrust(trust: trust)
|
||||
for serverKey in serverPubKeys {
|
||||
for key in keys {
|
||||
if CFEqual(serverKey, key) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -163,15 +163,15 @@ public class SSLSecurity : NSObject {
|
||||
for cert in certs {
|
||||
collect.append(SecCertificateCreateWithData(nil,cert)!)
|
||||
}
|
||||
SecTrustSetAnchorCertificates(trust,collect)
|
||||
var result: SecTrustResultType = 0
|
||||
SecTrustSetAnchorCertificates(trust,collect as CFArray)
|
||||
var result = SecTrustResultType(rawValue: 0)!
|
||||
SecTrustEvaluate(trust,&result)
|
||||
let r = Int(result)
|
||||
if r == kSecTrustResultUnspecified || r == kSecTrustResultProceed {
|
||||
let r = Int(result.rawValue)
|
||||
if r == Int(SecTrustResultType.unspecified.rawValue) || r == Int(SecTrustResultType.proceed.rawValue) {
|
||||
var trustedCount = 0
|
||||
for serverCert in serverCerts {
|
||||
for cert in certs {
|
||||
if cert == serverCert {
|
||||
if cert as Data == serverCert {
|
||||
trustedCount += 1
|
||||
break
|
||||
}
|
||||
@ -192,8 +192,8 @@ public class SSLSecurity : NSObject {
|
||||
|
||||
- returns: a public key
|
||||
*/
|
||||
func extractPublicKey(data: NSData) -> SecKeyRef? {
|
||||
guard let cert = SecCertificateCreateWithData(nil, data) else { return nil }
|
||||
func extractPublicKey(_ data: Data) -> SecKey? {
|
||||
guard let cert = SecCertificateCreateWithData(nil, data as CFData) else { return nil }
|
||||
|
||||
return extractPublicKeyFromCert(cert, policy: SecPolicyCreateBasicX509())
|
||||
}
|
||||
@ -205,13 +205,13 @@ public class SSLSecurity : NSObject {
|
||||
|
||||
- returns: a public key
|
||||
*/
|
||||
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? {
|
||||
func extractPublicKeyFromCert(_ cert: SecCertificate, policy: SecPolicy) -> SecKey? {
|
||||
var possibleTrust: SecTrust?
|
||||
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
|
||||
|
||||
guard let trust = possibleTrust else { return nil }
|
||||
|
||||
var result: SecTrustResultType = 0
|
||||
var result = SecTrustResultType(rawValue: 0)!
|
||||
SecTrustEvaluate(trust, &result)
|
||||
return SecTrustCopyPublicKey(trust)
|
||||
}
|
||||
@ -223,11 +223,11 @@ public class SSLSecurity : NSObject {
|
||||
|
||||
- returns: the certificate chain for the trust
|
||||
*/
|
||||
func certificateChainForTrust(trust: SecTrustRef) -> [NSData] {
|
||||
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (certificates: [NSData], index: Int) -> [NSData] in
|
||||
func certificateChainForTrust(_ trust: SecTrust) -> [Data] {
|
||||
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([Data]()) { (certificates: [Data], index: Int) -> [Data] in
|
||||
var certificates = certificates
|
||||
let cert = SecTrustGetCertificateAtIndex(trust, index)
|
||||
certificates.append(SecCertificateCopyData(cert!))
|
||||
certificates.append(SecCertificateCopyData(cert!) as Data)
|
||||
return certificates
|
||||
}
|
||||
|
||||
@ -241,9 +241,9 @@ public class SSLSecurity : NSObject {
|
||||
|
||||
- returns: the public keys from the certifcate chain for the trust
|
||||
*/
|
||||
@nonobjc func publicKeyChainForTrust(trust: SecTrustRef) -> [SecKeyRef] {
|
||||
@nonobjc func publicKeyChainForTrust(trust: SecTrust) -> [SecKey] {
|
||||
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
|
||||
let cert = SecTrustGetCertificateAtIndex(trust, index)
|
||||
if let key = extractPublicKeyFromCert(cert!, policy: policy) {
|
||||
@ -257,4 +257,4 @@ public class SSLSecurity : NSObject {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,15 +33,15 @@ public final class SocketAckEmitter : NSObject {
|
||||
self.ackNum = ackNum
|
||||
}
|
||||
|
||||
public func with(items: AnyObject...) {
|
||||
public func with(_ items: SocketData...) {
|
||||
guard ackNum != -1 else { return }
|
||||
|
||||
socket.emitAck(ackNum, withItems: items)
|
||||
socket.emitAck(ackNum, with: items)
|
||||
}
|
||||
|
||||
public func with(items: [AnyObject]) {
|
||||
public func with(_ items: [Any]) {
|
||||
guard ackNum != -1 else { return }
|
||||
|
||||
socket.emitAck(ackNum, withItems: items)
|
||||
socket.emitAck(ackNum, with: items)
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ private struct SocketAck : Hashable {
|
||||
self.ack = ack
|
||||
}
|
||||
|
||||
init(ack: Int, callback: AckCallback) {
|
||||
init(ack: Int, callback: @escaping AckCallback) {
|
||||
self.ack = ack
|
||||
self.callback = callback
|
||||
}
|
||||
@ -52,25 +52,21 @@ private func ==(lhs: SocketAck, rhs: SocketAck) -> Bool {
|
||||
struct SocketAckManager {
|
||||
private var acks = Set<SocketAck>(minimumCapacity: 1)
|
||||
|
||||
mutating func addAck(ack: Int, callback: AckCallback) {
|
||||
mutating func addAck(_ ack: Int, callback: @escaping AckCallback) {
|
||||
acks.insert(SocketAck(ack: ack, callback: callback))
|
||||
}
|
||||
|
||||
/// Should be called on handle queue
|
||||
mutating func executeAck(ack: Int, items: [AnyObject], onQueue: dispatch_queue_t) {
|
||||
let callback = acks.remove(SocketAck(ack: ack))
|
||||
mutating func executeAck(_ ack: Int, with items: [Any], onQueue: DispatchQueue) {
|
||||
let ack = acks.remove(SocketAck(ack: ack))
|
||||
|
||||
dispatch_async(onQueue) {
|
||||
callback?.callback(items)
|
||||
}
|
||||
onQueue.async() { ack?.callback(items) }
|
||||
}
|
||||
|
||||
/// Should be called on handle queue
|
||||
mutating func timeoutAck(ack: Int, onQueue: dispatch_queue_t) {
|
||||
let callback = acks.remove(SocketAck(ack: ack))
|
||||
mutating func timeoutAck(_ ack: Int, onQueue: DispatchQueue) {
|
||||
let ack = acks.remove(SocketAck(ack: ack))
|
||||
|
||||
dispatch_async(onQueue) {
|
||||
callback?.callback(["NO ACK"])
|
||||
}
|
||||
onQueue.async() { ack?.callback?(["NO ACK"]) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,12 +26,12 @@ import Foundation
|
||||
|
||||
public final class SocketAnyEvent : NSObject {
|
||||
public let event: String
|
||||
public let items: NSArray?
|
||||
public let items: [Any]?
|
||||
override public var description: String {
|
||||
return "SocketAnyEvent: Event: \(event) items: \(items ?? nil)"
|
||||
}
|
||||
|
||||
init(event: String, items: NSArray?) {
|
||||
init(event: String, items: [Any]?) {
|
||||
self.event = event
|
||||
self.items = items
|
||||
}
|
||||
|
||||
@ -43,12 +43,12 @@ import Foundation
|
||||
manager["room1"]?.emit("hello")
|
||||
```
|
||||
*/
|
||||
public final class SocketClientManager : NSObject {
|
||||
public static let sharedManager = SocketClientManager()
|
||||
open class SocketClientManager : NSObject {
|
||||
open static let sharedManager = SocketClientManager()
|
||||
|
||||
private var sockets = [String: SocketIOClient]()
|
||||
|
||||
public subscript(string: String) -> SocketIOClient? {
|
||||
open subscript(string: String) -> SocketIOClient? {
|
||||
get {
|
||||
return sockets[string]
|
||||
}
|
||||
@ -58,25 +58,25 @@ public final class SocketClientManager : NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
public func addSocket(socket: SocketIOClient, labeledAs label: String) {
|
||||
open func addSocket(_ socket: SocketIOClient, labeledAs label: String) {
|
||||
sockets[label] = socket
|
||||
}
|
||||
|
||||
public func removeSocket(withLabel label: String) -> SocketIOClient? {
|
||||
return sockets.removeValueForKey(label)
|
||||
open func removeSocket(withLabel label: String) -> SocketIOClient? {
|
||||
return sockets.removeValue(forKey: label)
|
||||
}
|
||||
|
||||
public func removeSocket(socket: SocketIOClient) -> SocketIOClient? {
|
||||
open func removeSocket(_ socket: SocketIOClient) -> SocketIOClient? {
|
||||
var returnSocket: SocketIOClient?
|
||||
|
||||
for (label, dictSocket) in sockets where dictSocket === socket {
|
||||
returnSocket = sockets.removeValueForKey(label)
|
||||
returnSocket = sockets.removeValue(forKey: label)
|
||||
}
|
||||
|
||||
return returnSocket
|
||||
}
|
||||
|
||||
public func removeSockets() {
|
||||
open func removeSockets() {
|
||||
sockets.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,24 +24,24 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class SocketEngine : NSObject, NSURLSessionDelegate, 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)
|
||||
public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket {
|
||||
public let emitQueue = DispatchQueue(label: "com.socketio.engineEmitQueue", attributes: [])
|
||||
public let handleQueue = DispatchQueue(label: "com.socketio.engineHandleQueue", attributes: [])
|
||||
public let parseQueue = DispatchQueue(label: "com.socketio.engineParseQueue", attributes: [])
|
||||
|
||||
public var connectParams: [String: AnyObject]? {
|
||||
public var connectParams: [String: Any]? {
|
||||
didSet {
|
||||
(urlPolling, urlWebSocket) = createURLs()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public var postWait = [String]()
|
||||
public var waitingForPoll = false
|
||||
public var waitingForPost = false
|
||||
|
||||
|
||||
public private(set) var closed = false
|
||||
public private(set) var connected = false
|
||||
public private(set) var cookies: [NSHTTPCookie]?
|
||||
public private(set) var cookies: [HTTPCookie]?
|
||||
public private(set) var doubleEncodeUTF8 = true
|
||||
public private(set) var extraHeaders: [String: String]?
|
||||
public private(set) var fastUpgrade = false
|
||||
@ -50,28 +50,28 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
public private(set) var invalidated = false
|
||||
public private(set) var polling = true
|
||||
public private(set) var probing = false
|
||||
public private(set) var session: NSURLSession?
|
||||
public private(set) var session: URLSession?
|
||||
public private(set) var sid = ""
|
||||
public private(set) var socketPath = "/engine.io/"
|
||||
public private(set) var urlPolling = NSURL()
|
||||
public private(set) var urlWebSocket = NSURL()
|
||||
public private(set) var urlPolling = URL(string: "http://localhost/")!
|
||||
public private(set) var urlWebSocket = URL(string: "http://localhost/")!
|
||||
public private(set) var websocket = false
|
||||
public private(set) var ws: WebSocket?
|
||||
|
||||
public weak var client: SocketEngineClient?
|
||||
|
||||
private weak var sessionDelegate: NSURLSessionDelegate?
|
||||
|
||||
private weak var sessionDelegate: URLSessionDelegate?
|
||||
|
||||
private let logType = "SocketEngine"
|
||||
private let url: NSURL
|
||||
|
||||
private let url: URL
|
||||
|
||||
private var pingInterval: Double?
|
||||
private var pingTimeout = 0.0 {
|
||||
didSet {
|
||||
pongsMissedMax = Int(pingTimeout / (pingInterval ?? 25))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var pongsMissed = 0
|
||||
private var pongsMissedMax = 0
|
||||
private var probeWait = ProbeWaitQueue()
|
||||
@ -80,45 +80,44 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
private var selfSigned = false
|
||||
private var voipEnabled = false
|
||||
|
||||
public init(client: SocketEngineClient, url: NSURL, config: SocketIOClientConfiguration) {
|
||||
public init(client: SocketEngineClient, url: URL, config: SocketIOClientConfiguration) {
|
||||
self.client = client
|
||||
self.url = url
|
||||
|
||||
for option in config {
|
||||
switch option {
|
||||
case let .ConnectParams(params):
|
||||
case let .connectParams(params):
|
||||
connectParams = params
|
||||
case let .Cookies(cookies):
|
||||
case let .cookies(cookies):
|
||||
self.cookies = cookies
|
||||
case let .DoubleEncodeUTF8(encode):
|
||||
case let .doubleEncodeUTF8(encode):
|
||||
doubleEncodeUTF8 = encode
|
||||
case let .ExtraHeaders(headers):
|
||||
case let .extraHeaders(headers):
|
||||
extraHeaders = headers
|
||||
case let .SessionDelegate(delegate):
|
||||
case let .sessionDelegate(delegate):
|
||||
sessionDelegate = delegate
|
||||
case let .ForcePolling(force):
|
||||
case let .forcePolling(force):
|
||||
forcePolling = force
|
||||
case let .ForceWebsockets(force):
|
||||
case let .forceWebsockets(force):
|
||||
forceWebsockets = force
|
||||
case let .Path(path):
|
||||
case let .path(path):
|
||||
socketPath = path
|
||||
|
||||
case let .voipEnabled(enable):
|
||||
if !socketPath.hasSuffix("/") {
|
||||
socketPath += "/"
|
||||
}
|
||||
case let .VoipEnabled(enable):
|
||||
|
||||
voipEnabled = enable
|
||||
case let .Secure(secure):
|
||||
case let .secure(secure):
|
||||
self.secure = secure
|
||||
case let .Security(security):
|
||||
self.security = security
|
||||
case let .SelfSigned(selfSigned):
|
||||
case let .selfSigned(selfSigned):
|
||||
self.selfSigned = selfSigned
|
||||
case let .security(security):
|
||||
self.security = security
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
super.init()
|
||||
|
||||
sessionDelegate = sessionDelegate ?? self
|
||||
@ -126,17 +125,17 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
(urlPolling, urlWebSocket) = createURLs()
|
||||
}
|
||||
|
||||
public convenience init(client: SocketEngineClient, url: NSURL, options: NSDictionary?) {
|
||||
public convenience init(client: SocketEngineClient, url: URL, options: NSDictionary?) {
|
||||
self.init(client: client, url: url, config: options?.toSocketConfiguration() ?? [])
|
||||
}
|
||||
|
||||
|
||||
deinit {
|
||||
DefaultSocketLogger.Logger.log("Engine is being released", type: logType)
|
||||
closed = true
|
||||
stopPolling()
|
||||
}
|
||||
|
||||
private func checkAndHandleEngineError(msg: String) {
|
||||
|
||||
private func checkAndHandleEngineError(_ msg: String) {
|
||||
do {
|
||||
let dict = try msg.toNSDictionary()
|
||||
guard let error = dict["message"] as? String else { return }
|
||||
@ -147,76 +146,76 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
2: Bad handshake request
|
||||
3: Bad request
|
||||
*/
|
||||
didError(error)
|
||||
didError(reason: error)
|
||||
} catch {
|
||||
client?.engineDidError("Got unknown error from server \(msg)")
|
||||
client?.engineDidError(reason: "Got unknown error from server \(msg)")
|
||||
}
|
||||
}
|
||||
|
||||
private func handleBase64(message: String) {
|
||||
// binary in base64 string
|
||||
let noPrefix = message[message.startIndex.advancedBy(2)..<message.endIndex]
|
||||
let noPrefix = message[message.index(message.startIndex, offsetBy: 2)..<message.endIndex]
|
||||
|
||||
if let data = NSData(base64EncodedString: noPrefix, options: .IgnoreUnknownCharacters) {
|
||||
client?.parseEngineBinaryData(data)
|
||||
if let data = NSData(base64Encoded: noPrefix, options: .ignoreUnknownCharacters) {
|
||||
client?.parseEngineBinaryData(data as Data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func closeOutEngine(reason: String) {
|
||||
sid = ""
|
||||
closed = true
|
||||
invalidated = true
|
||||
connected = false
|
||||
|
||||
|
||||
ws?.disconnect()
|
||||
stopPolling()
|
||||
client?.engineDidClose(reason)
|
||||
client?.engineDidClose(reason: 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)
|
||||
disconnect("reconnect")
|
||||
disconnect(reason: "reconnect")
|
||||
}
|
||||
|
||||
|
||||
DefaultSocketLogger.Logger.log("Starting engine. Server: %@", type: logType, args: url)
|
||||
DefaultSocketLogger.Logger.log("Handshaking", type: logType)
|
||||
|
||||
|
||||
resetEngine()
|
||||
|
||||
|
||||
if forceWebsockets {
|
||||
polling = false
|
||||
websocket = true
|
||||
createWebsocketAndConnect()
|
||||
return
|
||||
}
|
||||
|
||||
let reqPolling = NSMutableURLRequest(URL: urlPolling)
|
||||
|
||||
|
||||
let reqPolling = NSMutableURLRequest(url: urlPolling)
|
||||
|
||||
if cookies != nil {
|
||||
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
|
||||
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
|
||||
reqPolling.allHTTPHeaderFields = headers
|
||||
}
|
||||
|
||||
|
||||
if let extraHeaders = extraHeaders {
|
||||
for (headerName, value) in extraHeaders {
|
||||
reqPolling.setValue(value, forHTTPHeaderField: headerName)
|
||||
}
|
||||
}
|
||||
|
||||
doLongPoll(reqPolling)
|
||||
|
||||
doLongPoll(for: reqPolling as URLRequest)
|
||||
}
|
||||
|
||||
private func createURLs() -> (NSURL, NSURL) {
|
||||
private func createURLs() -> (URL, URL) {
|
||||
if client == nil {
|
||||
return (NSURL(), NSURL())
|
||||
return (URL(string: "http://localhost/")!, URL(string: "http://localhost/")!)
|
||||
}
|
||||
|
||||
let urlPolling = NSURLComponents(string: url.absoluteString)!
|
||||
let urlWebSocket = NSURLComponents(string: url.absoluteString)!
|
||||
var urlPolling = URLComponents(string: url.absoluteString)!
|
||||
var urlWebSocket = URLComponents(string: url.absoluteString)!
|
||||
var queryString = ""
|
||||
|
||||
|
||||
urlWebSocket.path = socketPath
|
||||
urlPolling.path = socketPath
|
||||
|
||||
@ -239,15 +238,15 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
|
||||
urlWebSocket.percentEncodedQuery = "transport=websocket" + queryString
|
||||
urlPolling.percentEncodedQuery = "transport=polling&b64=1" + queryString
|
||||
|
||||
return (urlPolling.URL!, urlWebSocket.URL!)
|
||||
|
||||
return (urlPolling.url!, urlWebSocket.url!)
|
||||
}
|
||||
|
||||
private func createWebsocketAndConnect() {
|
||||
ws = WebSocket(url: urlWebSocketWithSid)
|
||||
|
||||
ws = WebSocket(url: urlWebSocketWithSid as NSURL)
|
||||
|
||||
if cookies != nil {
|
||||
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
|
||||
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
|
||||
for (key, value) in headers {
|
||||
ws?.headers[key] = value
|
||||
}
|
||||
@ -267,38 +266,38 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
|
||||
ws?.connect()
|
||||
}
|
||||
|
||||
public func didError(error: String) {
|
||||
DefaultSocketLogger.Logger.error("%@", type: logType, args: error)
|
||||
client?.engineDidError(error)
|
||||
disconnect(error)
|
||||
|
||||
public func didError(reason: String) {
|
||||
DefaultSocketLogger.Logger.error("%@", type: logType, args: reason)
|
||||
client?.engineDidError(reason: reason)
|
||||
disconnect(reason: reason)
|
||||
}
|
||||
|
||||
|
||||
public func disconnect(reason: String) {
|
||||
guard connected else { return closeOutEngine(reason) }
|
||||
|
||||
guard connected else { return closeOutEngine(reason: reason) }
|
||||
|
||||
DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType)
|
||||
|
||||
|
||||
if closed {
|
||||
return closeOutEngine(reason)
|
||||
return closeOutEngine(reason: reason)
|
||||
}
|
||||
|
||||
|
||||
if websocket {
|
||||
sendWebSocketMessage("", withType: .Close, withData: [])
|
||||
closeOutEngine(reason)
|
||||
sendWebSocketMessage("", withType: .close, withData: [])
|
||||
closeOutEngine(reason: reason)
|
||||
} else {
|
||||
disconnectPolling(reason)
|
||||
disconnectPolling(reason: reason)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We need to take special care when we're polling that we send it ASAP
|
||||
// Also make sure we're on the emitQueue since we're touching postWait
|
||||
private func disconnectPolling(reason: String) {
|
||||
dispatch_sync(emitQueue) {
|
||||
self.postWait.append(String(SocketEnginePacketType.Close.rawValue))
|
||||
emitQueue.sync {
|
||||
self.postWait.append(String(SocketEnginePacketType.close.rawValue))
|
||||
let req = self.createRequestForPostWithPostWait()
|
||||
self.doRequest(req) {_, _, _ in }
|
||||
self.closeOutEngine(reason)
|
||||
self.doRequest(for: req) {_, _, _ in }
|
||||
self.closeOutEngine(reason: reason)
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,7 +307,7 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
"we'll probably disconnect soon. You should report this.", type: logType)
|
||||
}
|
||||
|
||||
sendWebSocketMessage("", withType: .Upgrade, withData: [])
|
||||
sendWebSocketMessage("", withType: .upgrade, withData: [])
|
||||
websocket = true
|
||||
polling = false
|
||||
fastUpgrade = false
|
||||
@ -319,105 +318,108 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
private func flushProbeWait() {
|
||||
DefaultSocketLogger.Logger.log("Flushing probe wait", type: logType)
|
||||
|
||||
dispatch_async(emitQueue) {
|
||||
emitQueue.async {
|
||||
for waiter in self.probeWait {
|
||||
self.write(waiter.msg, withType: waiter.type, withData: waiter.data)
|
||||
}
|
||||
|
||||
self.probeWait.removeAll(keepCapacity: false)
|
||||
|
||||
|
||||
self.probeWait.removeAll(keepingCapacity: false)
|
||||
|
||||
if self.postWait.count != 0 {
|
||||
self.flushWaitingForPostToWebSocket()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We had packets waiting for send when we upgraded
|
||||
// Send them raw
|
||||
public func flushWaitingForPostToWebSocket() {
|
||||
guard let ws = self.ws else { return }
|
||||
|
||||
|
||||
for msg in postWait {
|
||||
ws.writeString(msg)
|
||||
}
|
||||
|
||||
postWait.removeAll(keepCapacity: true)
|
||||
|
||||
postWait.removeAll(keepingCapacity: false)
|
||||
}
|
||||
|
||||
private func handleClose(reason: String) {
|
||||
client?.engineDidClose(reason)
|
||||
private func handleClose(_ reason: String) {
|
||||
client?.engineDidClose(reason: reason)
|
||||
}
|
||||
|
||||
private func handleMessage(message: String) {
|
||||
private func handleMessage(_ message: String) {
|
||||
client?.parseEngineMessage(message)
|
||||
}
|
||||
|
||||
private func handleNOOP() {
|
||||
doPoll()
|
||||
}
|
||||
|
||||
|
||||
private func handleOpen(openData: String) {
|
||||
do {
|
||||
let json = try openData.toNSDictionary()
|
||||
guard let sid = json["sid"] as? String else {
|
||||
client?.engineDidError("Open packet contained no sid")
|
||||
return
|
||||
}
|
||||
guard let json = try? openData.toNSDictionary() else {
|
||||
didError(reason: "Error parsing open packet")
|
||||
|
||||
let upgradeWs: Bool
|
||||
|
||||
self.sid = sid
|
||||
connected = true
|
||||
|
||||
if let upgrades = json["upgrades"] as? [String] {
|
||||
upgradeWs = upgrades.contains("websocket")
|
||||
} else {
|
||||
upgradeWs = false
|
||||
}
|
||||
|
||||
if let pingInterval = json["pingInterval"] as? Double, pingTimeout = json["pingTimeout"] as? Double {
|
||||
self.pingInterval = pingInterval / 1000.0
|
||||
self.pingTimeout = pingTimeout / 1000.0
|
||||
}
|
||||
|
||||
if !forcePolling && !forceWebsockets && upgradeWs {
|
||||
createWebsocketAndConnect()
|
||||
}
|
||||
|
||||
sendPing()
|
||||
|
||||
if !forceWebsockets {
|
||||
doPoll()
|
||||
}
|
||||
|
||||
client?.engineDidOpen("Connect")
|
||||
} catch {
|
||||
didError("Error parsing open packet")
|
||||
return
|
||||
}
|
||||
|
||||
guard let sid = json["sid"] as? String else {
|
||||
didError(reason: "Open packet contained no sid")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let upgradeWs: Bool
|
||||
|
||||
self.sid = sid
|
||||
connected = true
|
||||
|
||||
if let upgrades = json["upgrades"] as? [String] {
|
||||
upgradeWs = upgrades.contains("websocket")
|
||||
} else {
|
||||
upgradeWs = false
|
||||
}
|
||||
|
||||
if let pingInterval = json["pingInterval"] as? Double, let pingTimeout = json["pingTimeout"] as? Double {
|
||||
self.pingInterval = pingInterval / 1000.0
|
||||
self.pingTimeout = pingTimeout / 1000.0
|
||||
}
|
||||
|
||||
if !forcePolling && !forceWebsockets && upgradeWs {
|
||||
createWebsocketAndConnect()
|
||||
}
|
||||
|
||||
sendPing()
|
||||
|
||||
if !forceWebsockets {
|
||||
doPoll()
|
||||
}
|
||||
|
||||
client?.engineDidOpen(reason: "Connect")
|
||||
}
|
||||
|
||||
private func handlePong(pongMessage: String) {
|
||||
private func handlePong(with message: String) {
|
||||
pongsMissed = 0
|
||||
|
||||
// We should upgrade
|
||||
if pongMessage == "3probe" {
|
||||
if message == "3probe" {
|
||||
upgradeTransport()
|
||||
}
|
||||
}
|
||||
|
||||
public func parseEngineData(data: NSData) {
|
||||
|
||||
public func parseEngineData(_ data: Data) {
|
||||
DefaultSocketLogger.Logger.log("Got binary data: %@", type: "SocketEngine", args: data)
|
||||
client?.parseEngineBinaryData(data.subdataWithRange(NSMakeRange(1, data.length - 1)))
|
||||
|
||||
client?.parseEngineBinaryData(data.subdata(in: 1..<data.endIndex))
|
||||
}
|
||||
|
||||
public func parseEngineMessage(message: String, fromPolling: Bool) {
|
||||
public func parseEngineMessage(_ message: String, fromPolling: Bool) {
|
||||
DefaultSocketLogger.Logger.log("Got message: %@", type: logType, args: message)
|
||||
|
||||
|
||||
let reader = SocketStringReader(message: message)
|
||||
let fixedString: String
|
||||
|
||||
if message.hasPrefix("b4") {
|
||||
return handleBase64(message)
|
||||
return handleBase64(message: message)
|
||||
}
|
||||
|
||||
guard let type = SocketEnginePacketType(rawValue: Int(reader.currentCharacter) ?? -1) else {
|
||||
@ -426,28 +428,28 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
return
|
||||
}
|
||||
|
||||
if fromPolling && type != .Noop && doubleEncodeUTF8 {
|
||||
if fromPolling && type != .noop && doubleEncodeUTF8 {
|
||||
fixedString = fixDoubleUTF8(message)
|
||||
} else {
|
||||
fixedString = message
|
||||
}
|
||||
|
||||
switch type {
|
||||
case .Message:
|
||||
handleMessage(fixedString[fixedString.startIndex.successor()..<fixedString.endIndex])
|
||||
case .Noop:
|
||||
case .message:
|
||||
handleMessage(String(fixedString.characters.dropFirst()))
|
||||
case .noop:
|
||||
handleNOOP()
|
||||
case .Pong:
|
||||
handlePong(fixedString)
|
||||
case .Open:
|
||||
handleOpen(fixedString[fixedString.startIndex.successor()..<fixedString.endIndex])
|
||||
case .Close:
|
||||
case .pong:
|
||||
handlePong(with: fixedString)
|
||||
case .open:
|
||||
handleOpen(openData: String(fixedString.characters.dropFirst()))
|
||||
case .close:
|
||||
handleClose(fixedString)
|
||||
default:
|
||||
DefaultSocketLogger.Logger.log("Got unknown packet type", type: logType)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Puts the engine back in its default state
|
||||
private func resetEngine() {
|
||||
closed = false
|
||||
@ -456,9 +458,7 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
polling = true
|
||||
probing = false
|
||||
invalidated = false
|
||||
session = NSURLSession(configuration: .defaultSessionConfiguration(),
|
||||
delegate: sessionDelegate,
|
||||
delegateQueue: NSOperationQueue.mainQueue())
|
||||
session = Foundation.URLSession(configuration: .default, delegate: sessionDelegate, delegateQueue: OperationQueue.main)
|
||||
sid = ""
|
||||
waitingForPoll = false
|
||||
waitingForPost = false
|
||||
@ -466,43 +466,40 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
}
|
||||
|
||||
private func sendPing() {
|
||||
if !connected {
|
||||
return
|
||||
}
|
||||
|
||||
//Server is not responding
|
||||
guard connected else { return }
|
||||
|
||||
// Server is not responding
|
||||
if pongsMissed > pongsMissedMax {
|
||||
client?.engineDidClose("Ping timeout")
|
||||
client?.engineDidClose(reason: "Ping timeout")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
guard let pingInterval = pingInterval else { return }
|
||||
|
||||
if let pingInterval = pingInterval {
|
||||
pongsMissed += 1
|
||||
write("", withType: .Ping, withData: [])
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
pongsMissed += 1
|
||||
write("", withType: .ping, withData: [])
|
||||
|
||||
let time = DispatchTime.now() + Double(Int64(pingInterval * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
|
||||
DispatchQueue.main.asyncAfter(deadline: time) {[weak self] in self?.sendPing() }
|
||||
}
|
||||
|
||||
|
||||
// Moves from long-polling to websockets
|
||||
private func upgradeTransport() {
|
||||
if ws?.isConnected ?? false {
|
||||
DefaultSocketLogger.Logger.log("Upgrading transport to WebSockets", type: logType)
|
||||
|
||||
fastUpgrade = true
|
||||
sendPollMessage("", withType: .Noop, withData: [])
|
||||
sendPollMessage("", withType: .noop, withData: [])
|
||||
// After this point, we should not send anymore polling messages
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a message, independent of transport.
|
||||
public func write(msg: String, withType type: SocketEnginePacketType, withData data: [NSData]) {
|
||||
dispatch_async(emitQueue) {
|
||||
public func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data]) {
|
||||
emitQueue.async {
|
||||
guard self.connected else { return }
|
||||
|
||||
|
||||
if self.websocket {
|
||||
DefaultSocketLogger.Logger.log("Writing ws: %@ has data: %@",
|
||||
type: self.logType, args: msg, data.count != 0)
|
||||
@ -516,9 +513,9 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Delegate methods
|
||||
public func websocketDidConnect(socket: WebSocket) {
|
||||
public func websocketDidConnect(_ socket: WebSocket) {
|
||||
if !forceWebsockets {
|
||||
probing = true
|
||||
probeWebSocket()
|
||||
@ -528,23 +525,24 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
polling = false
|
||||
}
|
||||
}
|
||||
|
||||
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
|
||||
|
||||
public func websocketDidDisconnect(_ socket: WebSocket, error: NSError?) {
|
||||
probing = false
|
||||
|
||||
|
||||
if closed {
|
||||
client?.engineDidClose("Disconnect")
|
||||
client?.engineDidClose(reason: "Disconnect")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if websocket {
|
||||
connected = false
|
||||
websocket = false
|
||||
|
||||
|
||||
if let reason = error?.localizedDescription {
|
||||
didError(reason)
|
||||
didError(reason: reason)
|
||||
} else {
|
||||
client?.engineDidClose("Socket Disconnected")
|
||||
client?.engineDidClose(reason: "Socket Disconnected")
|
||||
}
|
||||
} else {
|
||||
flushProbeWait()
|
||||
@ -553,9 +551,9 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
|
||||
}
|
||||
|
||||
extension SocketEngine {
|
||||
public func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
|
||||
public func URLSession(session: URLSession, didBecomeInvalidWithError error: NSError?) {
|
||||
DefaultSocketLogger.Logger.error("Engine URLSession became invalid", type: "SocketEngine")
|
||||
|
||||
didError("Engine URLSession became invalid")
|
||||
didError(reason: "Engine URLSession became invalid")
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,6 @@ import Foundation
|
||||
func engineDidError(reason: String)
|
||||
func engineDidClose(reason: String)
|
||||
func engineDidOpen(reason: String)
|
||||
func parseEngineMessage(msg: String)
|
||||
func parseEngineBinaryData(data: NSData)
|
||||
func parseEngineMessage(_ msg: String)
|
||||
func parseEngineBinaryData(_ data: Data)
|
||||
}
|
||||
|
||||
@ -26,5 +26,5 @@
|
||||
import Foundation
|
||||
|
||||
@objc public enum SocketEnginePacketType : Int {
|
||||
case Open, Close, Ping, Pong, Message, Upgrade, Noop
|
||||
case open, close, ping, pong, message, upgrade, noop
|
||||
}
|
||||
@ -30,7 +30,7 @@ public protocol SocketEnginePollable : SocketEngineSpec {
|
||||
/// Holds strings waiting to be sent over polling.
|
||||
/// You shouldn't need to mess with this.
|
||||
var postWait: [String] { get set }
|
||||
var session: NSURLSession? { get }
|
||||
var session: URLSession? { get }
|
||||
/// Because socket.io doesn't let you send two polling request at the same time
|
||||
/// we have to keep track if there's an outstanding poll
|
||||
var waitingForPoll: Bool { get set }
|
||||
@ -39,15 +39,17 @@ public protocol SocketEnginePollable : SocketEngineSpec {
|
||||
var waitingForPost: Bool { get set }
|
||||
|
||||
func doPoll()
|
||||
func sendPollMessage(message: String, withType type: SocketEnginePacketType, withData datas: [NSData])
|
||||
func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data])
|
||||
func stopPolling()
|
||||
}
|
||||
|
||||
// Default polling methods
|
||||
extension SocketEnginePollable {
|
||||
private func addHeaders(req: NSMutableURLRequest) {
|
||||
private func addHeaders(for req: URLRequest) -> URLRequest {
|
||||
var req = req
|
||||
|
||||
if cookies != nil {
|
||||
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
|
||||
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
|
||||
req.allHTTPHeaderFields = headers
|
||||
}
|
||||
|
||||
@ -56,10 +58,12 @@ extension SocketEnginePollable {
|
||||
req.setValue(value, forHTTPHeaderField: headerName)
|
||||
}
|
||||
}
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
func createRequestForPostWithPostWait() -> NSURLRequest {
|
||||
defer { postWait.removeAll(keepCapacity: true) }
|
||||
func createRequestForPostWithPostWait() -> URLRequest {
|
||||
defer { postWait.removeAll(keepingCapacity: true) }
|
||||
|
||||
var postStr = ""
|
||||
|
||||
@ -71,49 +75,52 @@ extension SocketEnginePollable {
|
||||
|
||||
DefaultSocketLogger.Logger.log("Created POST string: %@", type: "SocketEnginePolling", args: postStr)
|
||||
|
||||
let req = NSMutableURLRequest(URL: urlPollingWithSid)
|
||||
let postData = postStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
|
||||
var req = URLRequest(url: urlPollingWithSid)
|
||||
let postData = postStr.data(using: .utf8, allowLossyConversion: false)!
|
||||
|
||||
addHeaders(req)
|
||||
req = addHeaders(for: req)
|
||||
|
||||
req.HTTPMethod = "POST"
|
||||
req.httpMethod = "POST"
|
||||
req.setValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type")
|
||||
req.HTTPBody = postData
|
||||
req.setValue(String(postData.length), forHTTPHeaderField: "Content-Length")
|
||||
|
||||
req.httpBody = postData
|
||||
req.setValue(String(postData.count), forHTTPHeaderField: "Content-Length")
|
||||
|
||||
return req
|
||||
return req as URLRequest
|
||||
}
|
||||
|
||||
public func doPoll() {
|
||||
if websocket || waitingForPoll || !connected || closed { return }
|
||||
if websocket || waitingForPoll || !connected || closed {
|
||||
return
|
||||
}
|
||||
|
||||
waitingForPoll = true
|
||||
|
||||
let req = NSMutableURLRequest(URL: urlPollingWithSid)
|
||||
var req = URLRequest(url: urlPollingWithSid)
|
||||
|
||||
addHeaders(req)
|
||||
doLongPoll(req)
|
||||
req = addHeaders(for: req)
|
||||
doLongPoll(for: req )
|
||||
}
|
||||
|
||||
func doRequest(req: NSURLRequest, withCallback callback: (NSData?, NSURLResponse?, NSError?) -> Void) {
|
||||
func doRequest(for req: URLRequest, callbackWith callback: @escaping (Data?, URLResponse?, Error?) -> Void) {
|
||||
if !polling || closed || invalidated || fastUpgrade {
|
||||
return
|
||||
}
|
||||
|
||||
DefaultSocketLogger.Logger.log("Doing polling request", type: "SocketEnginePolling")
|
||||
|
||||
session?.dataTaskWithRequest(req, completionHandler: callback).resume()
|
||||
session?.dataTask(with: req, completionHandler: callback).resume()
|
||||
}
|
||||
|
||||
func doLongPoll(req: NSURLRequest) {
|
||||
doRequest(req) {[weak self] data, res, err in
|
||||
guard let this = self where this.polling else { return }
|
||||
func doLongPoll(for req: URLRequest) {
|
||||
doRequest(for: req) {[weak self] data, res, err in
|
||||
guard let this = self, this.polling else { return }
|
||||
|
||||
if err != nil || data == nil {
|
||||
DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: "SocketEnginePolling")
|
||||
|
||||
if this.polling {
|
||||
this.didError(err?.localizedDescription ?? "Error")
|
||||
this.didError(reason: err?.localizedDescription ?? "Error")
|
||||
}
|
||||
|
||||
return
|
||||
@ -121,8 +128,8 @@ extension SocketEnginePollable {
|
||||
|
||||
DefaultSocketLogger.Logger.log("Got polling response", type: "SocketEnginePolling")
|
||||
|
||||
if let str = String(data: data!, encoding: NSUTF8StringEncoding) {
|
||||
dispatch_async(this.parseQueue) {
|
||||
if let str = String(data: data!, encoding: String.Encoding.utf8) {
|
||||
this.parseQueue.async {
|
||||
this.parsePollingMessage(str)
|
||||
}
|
||||
}
|
||||
@ -151,14 +158,14 @@ extension SocketEnginePollable {
|
||||
|
||||
DefaultSocketLogger.Logger.log("POSTing", type: "SocketEnginePolling")
|
||||
|
||||
doRequest(req) {[weak self] data, res, err in
|
||||
doRequest(for: req) {[weak self] data, res, err in
|
||||
guard let this = self else { return }
|
||||
|
||||
if err != nil {
|
||||
DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: "SocketEnginePolling")
|
||||
|
||||
if this.polling {
|
||||
this.didError(err?.localizedDescription ?? "Error")
|
||||
this.didError(reason: err?.localizedDescription ?? "Error")
|
||||
}
|
||||
|
||||
return
|
||||
@ -166,7 +173,7 @@ extension SocketEnginePollable {
|
||||
|
||||
this.waitingForPost = false
|
||||
|
||||
dispatch_async(this.emitQueue) {
|
||||
this.emitQueue.async {
|
||||
if !this.fastUpgrade {
|
||||
this.flushWaitingForPost()
|
||||
this.doPoll()
|
||||
@ -175,22 +182,18 @@ extension SocketEnginePollable {
|
||||
}
|
||||
}
|
||||
|
||||
func parsePollingMessage(str: String) {
|
||||
func parsePollingMessage(_ str: String) {
|
||||
guard str.characters.count != 1 else { return }
|
||||
|
||||
var reader = SocketStringReader(message: str)
|
||||
|
||||
while reader.hasNext {
|
||||
if let n = Int(reader.readUntilStringOccurence(":")) {
|
||||
let str = reader.read(n)
|
||||
if let n = Int(reader.readUntilOccurence(of: ":")) {
|
||||
let str = reader.read(count: n)
|
||||
|
||||
dispatch_async(handleQueue) {
|
||||
self.parseEngineMessage(str, fromPolling: true)
|
||||
}
|
||||
handleQueue.async { self.parseEngineMessage(str, fromPolling: true) }
|
||||
} else {
|
||||
dispatch_async(handleQueue) {
|
||||
self.parseEngineMessage(str, fromPolling: true)
|
||||
}
|
||||
handleQueue.async { self.parseEngineMessage(str, fromPolling: true) }
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -198,7 +201,7 @@ extension SocketEnginePollable {
|
||||
|
||||
/// Send polling message.
|
||||
/// Only call on emitQueue
|
||||
public func sendPollMessage(message: String, withType type: SocketEnginePacketType, withData datas: [NSData]) {
|
||||
public func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data]) {
|
||||
DefaultSocketLogger.Logger.log("Sending poll: %@ as type: %@", type: "SocketEnginePolling", args: message, type.rawValue)
|
||||
let fixedMessage: String
|
||||
|
||||
@ -211,7 +214,7 @@ extension SocketEnginePollable {
|
||||
postWait.append(String(type.rawValue) + fixedMessage)
|
||||
|
||||
for data in datas {
|
||||
if case let .Right(bin) = createBinaryDataForSend(data) {
|
||||
if case let .right(bin) = createBinaryDataForSend(using: data) {
|
||||
postWait.append(bin)
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,79 +29,79 @@ import Foundation
|
||||
weak var client: SocketEngineClient? { get set }
|
||||
var closed: Bool { get }
|
||||
var connected: Bool { get }
|
||||
var connectParams: [String: AnyObject]? { get set }
|
||||
var connectParams: [String: Any]? { get set }
|
||||
var doubleEncodeUTF8: Bool { get }
|
||||
var cookies: [NSHTTPCookie]? { get }
|
||||
var cookies: [HTTPCookie]? { get }
|
||||
var extraHeaders: [String: String]? { get }
|
||||
var fastUpgrade: Bool { get }
|
||||
var forcePolling: Bool { get }
|
||||
var forceWebsockets: Bool { get }
|
||||
var parseQueue: dispatch_queue_t! { get }
|
||||
var parseQueue: DispatchQueue { get }
|
||||
var polling: Bool { get }
|
||||
var probing: Bool { get }
|
||||
var emitQueue: dispatch_queue_t! { get }
|
||||
var handleQueue: dispatch_queue_t! { get }
|
||||
var emitQueue: DispatchQueue { get }
|
||||
var handleQueue: DispatchQueue { get }
|
||||
var sid: String { get }
|
||||
var socketPath: String { get }
|
||||
var urlPolling: NSURL { get }
|
||||
var urlWebSocket: NSURL { get }
|
||||
var urlPolling: URL { get }
|
||||
var urlWebSocket: URL { get }
|
||||
var websocket: Bool { get }
|
||||
var ws: WebSocket? { get }
|
||||
|
||||
init(client: SocketEngineClient, url: NSURL, options: NSDictionary?)
|
||||
init(client: SocketEngineClient, url: URL, options: NSDictionary?)
|
||||
|
||||
func connect()
|
||||
func didError(error: String)
|
||||
func didError(reason: String)
|
||||
func disconnect(reason: String)
|
||||
func doFastUpgrade()
|
||||
func flushWaitingForPostToWebSocket()
|
||||
func parseEngineData(data: NSData)
|
||||
func parseEngineMessage(message: String, fromPolling: Bool)
|
||||
func write(msg: String, withType type: SocketEnginePacketType, withData data: [NSData])
|
||||
func parseEngineData(_ data: Data)
|
||||
func parseEngineMessage(_ message: String, fromPolling: Bool)
|
||||
func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data])
|
||||
}
|
||||
|
||||
extension SocketEngineSpec {
|
||||
var urlPollingWithSid: NSURL {
|
||||
let com = NSURLComponents(URL: urlPolling, resolvingAgainstBaseURL: false)!
|
||||
var urlPollingWithSid: URL {
|
||||
var com = URLComponents(url: urlPolling, resolvingAgainstBaseURL: false)!
|
||||
com.percentEncodedQuery = com.percentEncodedQuery! + "&sid=\(sid.urlEncode()!)"
|
||||
|
||||
return com.URL!
|
||||
return com.url!
|
||||
}
|
||||
|
||||
var urlWebSocketWithSid: NSURL {
|
||||
let com = NSURLComponents(URL: urlWebSocket, resolvingAgainstBaseURL: false)!
|
||||
var urlWebSocketWithSid: URL {
|
||||
var com = URLComponents(url: urlWebSocket, resolvingAgainstBaseURL: false)!
|
||||
com.percentEncodedQuery = com.percentEncodedQuery! + (sid == "" ? "" : "&sid=\(sid.urlEncode()!)")
|
||||
|
||||
return com.URL!
|
||||
return com.url!
|
||||
}
|
||||
|
||||
func createBinaryDataForSend(data: NSData) -> Either<NSData, String> {
|
||||
func createBinaryDataForSend(using data: Data) -> Either<Data, String> {
|
||||
if websocket {
|
||||
var byteArray = [UInt8](count: 1, repeatedValue: 0x4)
|
||||
var byteArray = [UInt8](repeating: 0x4, count: 1)
|
||||
let mutData = NSMutableData(bytes: &byteArray, length: 1)
|
||||
|
||||
mutData.appendData(data)
|
||||
mutData.append(data)
|
||||
|
||||
return .Left(mutData)
|
||||
return .left(mutData as Data)
|
||||
} else {
|
||||
let str = "b4" + data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
|
||||
let str = "b4" + data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
|
||||
|
||||
return .Right(str)
|
||||
return .right(str)
|
||||
}
|
||||
}
|
||||
|
||||
func doubleEncodeUTF8(string: String) -> String {
|
||||
if let latin1 = string.dataUsingEncoding(NSUTF8StringEncoding),
|
||||
utf8 = NSString(data: latin1, encoding: NSISOLatin1StringEncoding) {
|
||||
func doubleEncodeUTF8(_ string: String) -> String {
|
||||
if let latin1 = string.data(using: String.Encoding.utf8),
|
||||
let utf8 = NSString(data: latin1, encoding: String.Encoding.isoLatin1.rawValue) {
|
||||
return utf8 as String
|
||||
} else {
|
||||
return string
|
||||
}
|
||||
}
|
||||
|
||||
func fixDoubleUTF8(string: String) -> String {
|
||||
if let utf8 = string.dataUsingEncoding(NSISOLatin1StringEncoding),
|
||||
latin1 = NSString(data: utf8, encoding: NSUTF8StringEncoding) {
|
||||
func fixDoubleUTF8(_ string: String) -> String {
|
||||
if let utf8 = string.data(using: String.Encoding.isoLatin1),
|
||||
let latin1 = NSString(data: utf8, encoding: String.Encoding.utf8.rawValue) {
|
||||
return latin1 as String
|
||||
} else {
|
||||
return string
|
||||
@ -109,7 +109,7 @@ extension SocketEngineSpec {
|
||||
}
|
||||
|
||||
/// Send an engine message (4)
|
||||
func send(msg: String, withData datas: [NSData]) {
|
||||
write(msg, withType: .Message, withData: datas)
|
||||
func send(_ msg: String, withData datas: [Data]) {
|
||||
write(msg, withType: .message, withData: datas)
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,36 +27,36 @@ import Foundation
|
||||
|
||||
/// Protocol that is used to implement socket.io WebSocket support
|
||||
public protocol SocketEngineWebsocket : SocketEngineSpec, WebSocketDelegate {
|
||||
func sendWebSocketMessage(str: String, withType type: SocketEnginePacketType, withData datas: [NSData])
|
||||
func sendWebSocketMessage(_ str: String, withType type: SocketEnginePacketType, withData datas: [Data])
|
||||
}
|
||||
|
||||
// WebSocket methods
|
||||
extension SocketEngineWebsocket {
|
||||
func probeWebSocket() {
|
||||
if ws?.isConnected ?? false {
|
||||
sendWebSocketMessage("probe", withType: .Ping, withData: [])
|
||||
sendWebSocketMessage("probe", withType: .ping, withData: [])
|
||||
}
|
||||
}
|
||||
|
||||
/// Send message on WebSockets
|
||||
/// Only call on emitQueue
|
||||
public func sendWebSocketMessage(str: String, withType type: SocketEnginePacketType, withData datas: [NSData]) {
|
||||
public func sendWebSocketMessage(_ str: String, withType type: SocketEnginePacketType, withData datas: [Data]) {
|
||||
DefaultSocketLogger.Logger.log("Sending ws: %@ as type: %@", type: "SocketEngine", args: str, type.rawValue)
|
||||
|
||||
ws?.writeString("\(type.rawValue)\(str)")
|
||||
|
||||
for data in datas {
|
||||
if case let .Left(bin) = createBinaryDataForSend(data) {
|
||||
if case let .left(bin) = createBinaryDataForSend(using: data) {
|
||||
ws?.writeData(bin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
|
||||
public func websocketDidReceiveMessage(_ socket: WebSocket, text: String) {
|
||||
parseEngineMessage(text, fromPolling: false)
|
||||
}
|
||||
|
||||
public func websocketDidReceiveData(socket: WebSocket, data: NSData) {
|
||||
public func websocketDidReceiveData(_ socket: WebSocket, data: Data) {
|
||||
parseEngineData(data)
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,10 +26,10 @@ import Foundation
|
||||
|
||||
struct SocketEventHandler {
|
||||
let event: String
|
||||
let id: NSUUID
|
||||
let id: UUID
|
||||
let callback: NormalCallback
|
||||
|
||||
func executeCallback(items: [AnyObject], withAck ack: Int, withSocket socket: SocketIOClient) {
|
||||
func executeCallback(with items: [Any], withAck ack: Int, withSocket socket: SocketIOClient) {
|
||||
callback(items, SocketAckEmitter(socket: socket, ackNum: ack))
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,66 +24,66 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
enum JSONError : ErrorType {
|
||||
enum JSONError : Error {
|
||||
case notArray
|
||||
case notNSDictionary
|
||||
}
|
||||
|
||||
extension Array where Element: AnyObject {
|
||||
func toJSON() throws -> NSData {
|
||||
return try NSJSONSerialization.dataWithJSONObject(self as NSArray, options: NSJSONWritingOptions(rawValue: 0))
|
||||
extension Array {
|
||||
func toJSON() throws -> Data {
|
||||
return try JSONSerialization.data(withJSONObject: self, options: JSONSerialization.WritingOptions(rawValue: 0))
|
||||
}
|
||||
}
|
||||
|
||||
extension NSCharacterSet {
|
||||
static var allowedURLCharacterSet: NSCharacterSet {
|
||||
return NSCharacterSet(charactersInString: "!*'();:@&=+$,/?%#[]\" {}").invertedSet
|
||||
extension CharacterSet {
|
||||
static var allowedURLCharacterSet: CharacterSet {
|
||||
return CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[]\" {}").inverted
|
||||
}
|
||||
}
|
||||
|
||||
extension NSDictionary {
|
||||
private static func keyValueToSocketIOClientOption(key: String, value: AnyObject) -> SocketIOClientOption? {
|
||||
private static func keyValueToSocketIOClientOption(key: String, value: Any) -> SocketIOClientOption? {
|
||||
switch (key, value) {
|
||||
case let ("connectParams", params as [String: AnyObject]):
|
||||
return .ConnectParams(params)
|
||||
case let ("cookies", cookies as [NSHTTPCookie]):
|
||||
return .Cookies(cookies)
|
||||
case let ("connectParams", params as [String: Any]):
|
||||
return .connectParams(params)
|
||||
case let ("cookies", cookies as [HTTPCookie]):
|
||||
return .cookies(cookies)
|
||||
case let ("doubleEncodeUTF8", encode as Bool):
|
||||
return .DoubleEncodeUTF8(encode)
|
||||
return .doubleEncodeUTF8(encode)
|
||||
case let ("extraHeaders", headers as [String: String]):
|
||||
return .ExtraHeaders(headers)
|
||||
return .extraHeaders(headers)
|
||||
case let ("forceNew", force as Bool):
|
||||
return .ForceNew(force)
|
||||
return .forceNew(force)
|
||||
case let ("forcePolling", force as Bool):
|
||||
return .ForcePolling(force)
|
||||
return .forcePolling(force)
|
||||
case let ("forceWebsockets", force as Bool):
|
||||
return .ForceWebsockets(force)
|
||||
case let ("handleQueue", queue as dispatch_queue_t):
|
||||
return .HandleQueue(queue)
|
||||
return .forceWebsockets(force)
|
||||
case let ("handleQueue", queue as DispatchQueue):
|
||||
return .handleQueue(queue)
|
||||
case let ("log", log as Bool):
|
||||
return .Log(log)
|
||||
return .log(log)
|
||||
case let ("logger", logger as SocketLogger):
|
||||
return .Logger(logger)
|
||||
return .logger(logger)
|
||||
case let ("nsp", nsp as String):
|
||||
return .Nsp(nsp)
|
||||
return .nsp(nsp)
|
||||
case let ("path", path as String):
|
||||
return .Path(path)
|
||||
return .path(path)
|
||||
case let ("reconnects", reconnects as Bool):
|
||||
return .Reconnects(reconnects)
|
||||
return .reconnects(reconnects)
|
||||
case let ("reconnectAttempts", attempts as Int):
|
||||
return .ReconnectAttempts(attempts)
|
||||
return .reconnectAttempts(attempts)
|
||||
case let ("reconnectWait", wait as Int):
|
||||
return .ReconnectWait(wait)
|
||||
return .reconnectWait(wait)
|
||||
case let ("secure", secure as Bool):
|
||||
return .Secure(secure)
|
||||
return .secure(secure)
|
||||
case let ("security", security as SSLSecurity):
|
||||
return .Security(security)
|
||||
return .security(security)
|
||||
case let ("selfSigned", selfSigned as Bool):
|
||||
return .SelfSigned(selfSigned)
|
||||
case let ("sessionDelegate", delegate as NSURLSessionDelegate):
|
||||
return .SessionDelegate(delegate)
|
||||
return .selfSigned(selfSigned)
|
||||
case let ("sessionDelegate", delegate as URLSessionDelegate):
|
||||
return .sessionDelegate(delegate)
|
||||
case let ("voipEnabled", enable as Bool):
|
||||
return .VoipEnabled(enable)
|
||||
return .voipEnabled(enable)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@ -93,7 +93,7 @@ extension NSDictionary {
|
||||
var options = [] as SocketIOClientConfiguration
|
||||
|
||||
for (rawKey, value) in self {
|
||||
if let key = rawKey as? String, opt = NSDictionary.keyValueToSocketIOClientOption(key, value: value) {
|
||||
if let key = rawKey as? String, let opt = NSDictionary.keyValueToSocketIOClientOption(key: key, value: value) {
|
||||
options.insert(opt)
|
||||
}
|
||||
}
|
||||
@ -103,9 +103,9 @@ extension NSDictionary {
|
||||
}
|
||||
|
||||
extension String {
|
||||
func toArray() throws -> [AnyObject] {
|
||||
guard let stringData = dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) else { return [] }
|
||||
guard let array = try NSJSONSerialization.JSONObjectWithData(stringData, options: .MutableContainers) as? [AnyObject] else {
|
||||
func toArray() throws -> [Any] {
|
||||
guard let stringData = data(using: .utf8, allowLossyConversion: false) else { return [] }
|
||||
guard let array = try JSONSerialization.jsonObject(with: stringData, options: .mutableContainers) as? [Any] else {
|
||||
throw JSONError.notArray
|
||||
}
|
||||
|
||||
@ -113,8 +113,8 @@ extension String {
|
||||
}
|
||||
|
||||
func toNSDictionary() throws -> NSDictionary {
|
||||
guard let binData = dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) else { return [:] }
|
||||
guard let json = try NSJSONSerialization.JSONObjectWithData(binData, options: .AllowFragments) as? NSDictionary else {
|
||||
guard let binData = data(using: .utf8, allowLossyConversion: false) else { return [:] }
|
||||
guard let json = try JSONSerialization.jsonObject(with: binData, options: .allowFragments) as? NSDictionary else {
|
||||
throw JSONError.notNSDictionary
|
||||
}
|
||||
|
||||
@ -122,6 +122,6 @@ extension String {
|
||||
}
|
||||
|
||||
func urlEncode() -> String? {
|
||||
return stringByAddingPercentEncodingWithAllowedCharacters(.allowedURLCharacterSet)
|
||||
return addingPercentEncoding(withAllowedCharacters: .allowedURLCharacterSet)
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,13 +25,13 @@
|
||||
import Foundation
|
||||
|
||||
public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable {
|
||||
public let socketURL: NSURL
|
||||
public let socketURL: URL
|
||||
|
||||
public private(set) var engine: SocketEngineSpec?
|
||||
public private(set) var status = SocketIOClientStatus.NotConnected {
|
||||
public private(set) var status = SocketIOClientStatus.notConnected {
|
||||
didSet {
|
||||
switch status {
|
||||
case .Connected:
|
||||
case .connected:
|
||||
reconnecting = false
|
||||
currentReconnectAttempt = 0
|
||||
default:
|
||||
@ -46,10 +46,10 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
public var reconnects = true
|
||||
public var reconnectWait = 10
|
||||
|
||||
private let ackQueue = dispatch_queue_create("com.socketio.ackQueue", DISPATCH_QUEUE_SERIAL)
|
||||
private let emitQueue = dispatch_queue_create("com.socketio.emitQueue", DISPATCH_QUEUE_SERIAL)
|
||||
private let ackQueue = DispatchQueue(label: "com.socketio.ackQueue", attributes: [])
|
||||
private let emitQueue = DispatchQueue(label: "com.socketio.emitQueue", attributes: [])
|
||||
private let logType = "SocketIOClient"
|
||||
private let parseQueue = dispatch_queue_create("com.socketio.parseQueue", DISPATCH_QUEUE_SERIAL)
|
||||
private let parseQueue = DispatchQueue(label: "com.socketio.parseQueue", attributes: [])
|
||||
|
||||
private var anyHandler: ((SocketAnyEvent) -> Void)?
|
||||
private var currentReconnectAttempt = 0
|
||||
@ -58,7 +58,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
private var reconnecting = false
|
||||
|
||||
private(set) var currentAck = -1
|
||||
private(set) var handleQueue = dispatch_get_main_queue()
|
||||
private(set) var handleQueue = DispatchQueue.main
|
||||
private(set) var reconnectAttempts = -1
|
||||
|
||||
var waitingPackets = [SocketPacket]()
|
||||
@ -67,39 +67,39 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
return nsp + "#" + (engine?.sid ?? "")
|
||||
}
|
||||
|
||||
/// Type safe way to create a new SocketIOClient. config can be omitted
|
||||
public init(socketURL: NSURL, config: SocketIOClientConfiguration = []) {
|
||||
/// Type safe way to create a new SocketIOClient. opts can be omitted
|
||||
public init(socketURL: URL, config: SocketIOClientConfiguration = []) {
|
||||
self.config = config
|
||||
self.socketURL = socketURL
|
||||
|
||||
if socketURL.absoluteString.hasPrefix("https://") {
|
||||
self.config.insert(.Secure(true))
|
||||
self.config.insert(.secure(true))
|
||||
}
|
||||
|
||||
for option in config {
|
||||
switch option {
|
||||
case let .Reconnects(reconnects):
|
||||
case let .reconnects(reconnects):
|
||||
self.reconnects = reconnects
|
||||
case let .ReconnectAttempts(attempts):
|
||||
case let .reconnectAttempts(attempts):
|
||||
reconnectAttempts = attempts
|
||||
case let .ReconnectWait(wait):
|
||||
case let .reconnectWait(wait):
|
||||
reconnectWait = abs(wait)
|
||||
case let .Nsp(nsp):
|
||||
case let .nsp(nsp):
|
||||
self.nsp = nsp
|
||||
case let .Log(log):
|
||||
case let .log(log):
|
||||
DefaultSocketLogger.Logger.log = log
|
||||
case let .Logger(logger):
|
||||
case let .logger(logger):
|
||||
DefaultSocketLogger.Logger = logger
|
||||
case let .HandleQueue(queue):
|
||||
case let .handleQueue(queue):
|
||||
handleQueue = queue
|
||||
case let .ForceNew(force):
|
||||
case let .forceNew(force):
|
||||
forceNew = force
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
self.config.insert(.Path("/socket.io/"), replacing: false)
|
||||
|
||||
self.config.insert(.path("/socket.io/"), replacing: false)
|
||||
|
||||
super.init()
|
||||
}
|
||||
@ -107,16 +107,16 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
/// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.
|
||||
/// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set<SocketIOClientOption>)`
|
||||
public convenience init(socketURL: NSURL, config: NSDictionary?) {
|
||||
self.init(socketURL: socketURL, config: config?.toSocketConfiguration() ?? [])
|
||||
self.init(socketURL: socketURL as URL, config: config?.toSocketConfiguration() ?? [])
|
||||
}
|
||||
|
||||
deinit {
|
||||
DefaultSocketLogger.Logger.log("Client is being released", type: logType)
|
||||
engine?.disconnect("Client Deinit")
|
||||
engine?.disconnect(reason: "Client Deinit")
|
||||
}
|
||||
|
||||
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, config: config)
|
||||
|
||||
@ -125,19 +125,20 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
|
||||
/// Connect to the server.
|
||||
public func connect() {
|
||||
connect(timeoutAfter: 0, withTimeoutHandler: nil)
|
||||
connect(timeoutAfter: 0, withHandler: nil)
|
||||
}
|
||||
|
||||
/// Connect to the server. If we aren't connected after timeoutAfter, call handler
|
||||
public func connect(timeoutAfter timeoutAfter: Int, withTimeoutHandler handler: (() -> Void)?) {
|
||||
/// Connect to the server. If we aren't connected after timeoutAfter, call withHandler
|
||||
/// 0 Never times out
|
||||
public func connect(timeoutAfter: Int, withHandler handler: (() -> Void)?) {
|
||||
assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)")
|
||||
|
||||
guard status != .Connected else {
|
||||
guard status != .connected else {
|
||||
DefaultSocketLogger.Logger.log("Tried connecting on an already connected socket", type: logType)
|
||||
return
|
||||
}
|
||||
|
||||
status = .Connecting
|
||||
status = .connecting
|
||||
|
||||
if engine == nil || forceNew {
|
||||
addEngine().connect()
|
||||
@ -147,25 +148,25 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
|
||||
guard timeoutAfter != 0 else { return }
|
||||
|
||||
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeoutAfter) * Int64(NSEC_PER_SEC))
|
||||
let time = DispatchTime.now() + Double(Int64(timeoutAfter) * Int64(NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
|
||||
|
||||
dispatch_after(time, handleQueue) {[weak self] in
|
||||
guard let this = self where this.status != .Connected && this.status != .Disconnected else { return }
|
||||
handleQueue.asyncAfter(deadline: time) {[weak self] in
|
||||
guard let this = self, this.status != .connected && this.status != .disconnected else { return }
|
||||
|
||||
this.status = .Disconnected
|
||||
this.engine?.disconnect("Connect timeout")
|
||||
this.status = .disconnected
|
||||
this.engine?.disconnect(reason: "Connect timeout")
|
||||
|
||||
handler?()
|
||||
}
|
||||
}
|
||||
|
||||
private func createOnAck(items: [AnyObject]) -> OnAckCallback {
|
||||
private func createOnAck(_ items: [Any]) -> OnAckCallback {
|
||||
currentAck += 1
|
||||
|
||||
|
||||
return {[weak self, ack = currentAck] timeout, callback in
|
||||
guard let this = self else { return }
|
||||
|
||||
dispatch_sync(this.ackQueue) {
|
||||
|
||||
this.ackQueue.sync() {
|
||||
this.ackHandlers.addAck(ack, callback: callback)
|
||||
}
|
||||
|
||||
@ -173,9 +174,9 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
this._emit(items, ack: ack)
|
||||
|
||||
if timeout != 0 {
|
||||
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * NSEC_PER_SEC))
|
||||
let time = DispatchTime.now() + Double(Int64(timeout * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
|
||||
|
||||
dispatch_after(time, this.ackQueue) {
|
||||
this.handleQueue.asyncAfter(deadline: time) {
|
||||
this.ackHandlers.timeoutAck(ack, onQueue: this.handleQueue)
|
||||
}
|
||||
}
|
||||
@ -184,7 +185,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
|
||||
func didConnect() {
|
||||
DefaultSocketLogger.Logger.log("Socket connected", type: logType)
|
||||
status = .Connected
|
||||
status = .connected
|
||||
|
||||
// Don't handle as internal because something crazy could happen where
|
||||
// we disconnect before it's handled
|
||||
@ -192,15 +193,15 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
}
|
||||
|
||||
func didDisconnect(reason: String) {
|
||||
guard status != .Disconnected else { return }
|
||||
guard status != .disconnected else { return }
|
||||
|
||||
DefaultSocketLogger.Logger.log("Disconnected: %@", type: logType, args: reason)
|
||||
|
||||
reconnecting = false
|
||||
status = .Disconnected
|
||||
status = .disconnected
|
||||
|
||||
// Make sure the engine is actually dead.
|
||||
engine?.disconnect(reason)
|
||||
engine?.disconnect(reason: reason)
|
||||
handleEvent("disconnect", data: [reason], isInternalMessage: true)
|
||||
}
|
||||
|
||||
@ -208,17 +209,17 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
public func disconnect() {
|
||||
DefaultSocketLogger.Logger.log("Closing socket", type: logType)
|
||||
|
||||
didDisconnect("Disconnect")
|
||||
didDisconnect(reason: "Disconnect")
|
||||
}
|
||||
|
||||
/// Send a message to the server
|
||||
public func emit(event: String, _ items: AnyObject...) {
|
||||
emit(event, withItems: items)
|
||||
public func emit(_ event: String, _ items: SocketData...) {
|
||||
emit(event, with: items)
|
||||
}
|
||||
|
||||
/// Same as emit, but meant for Objective-C
|
||||
public func emit(event: String, withItems items: [AnyObject]) {
|
||||
guard status == .Connected else {
|
||||
public func emit(_ event: String, with items: [Any]) {
|
||||
guard status == .connected else {
|
||||
handleEvent("error", data: ["Tried emitting \(event) when not connected"], isInternalMessage: true)
|
||||
return
|
||||
}
|
||||
@ -228,18 +229,18 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
|
||||
/// 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)
|
||||
public func emitWithAck(_ event: String, _ items: SocketData...) -> OnAckCallback {
|
||||
return emitWithAck(event, with: items)
|
||||
}
|
||||
|
||||
/// Same as emitWithAck, but for Objective-C
|
||||
public func emitWithAck(event: String, withItems items: [AnyObject]) -> OnAckCallback {
|
||||
public func emitWithAck(_ event: String, with items: [Any]) -> OnAckCallback {
|
||||
return createOnAck([event] + items)
|
||||
}
|
||||
|
||||
private func _emit(data: [AnyObject], ack: Int? = nil) {
|
||||
dispatch_async(emitQueue) {
|
||||
guard self.status == .Connected else {
|
||||
private func _emit(_ data: [Any], ack: Int? = nil) {
|
||||
emitQueue.async {
|
||||
guard self.status == .connected else {
|
||||
self.handleEvent("error", data: ["Tried emitting when not connected"], isInternalMessage: true)
|
||||
return
|
||||
}
|
||||
@ -254,31 +255,31 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
}
|
||||
|
||||
// If the server wants to know that the client received data
|
||||
func emitAck(ack: Int, withItems items: [AnyObject]) {
|
||||
dispatch_async(emitQueue) {
|
||||
if self.status == .Connected {
|
||||
let packet = SocketPacket.packetFromEmit(items, id: ack ?? -1, nsp: self.nsp, ack: true)
|
||||
let str = packet.packetString
|
||||
|
||||
DefaultSocketLogger.Logger.log("Emitting Ack: %@", type: self.logType, args: str)
|
||||
|
||||
self.engine?.send(str, withData: packet.binary)
|
||||
}
|
||||
func emitAck(_ ack: Int, with items: [Any]) {
|
||||
emitQueue.async {
|
||||
guard self.status == .connected else { return }
|
||||
|
||||
let packet = SocketPacket.packetFromEmit(items, id: ack, nsp: self.nsp, ack: true)
|
||||
let str = packet.packetString
|
||||
|
||||
DefaultSocketLogger.Logger.log("Emitting Ack: %@", type: self.logType, args: str)
|
||||
|
||||
self.engine?.send(str, withData: packet.binary)
|
||||
}
|
||||
}
|
||||
|
||||
public func engineDidClose(reason: String) {
|
||||
waitingPackets.removeAll()
|
||||
|
||||
if status != .Disconnected {
|
||||
status = .NotConnected
|
||||
if status != .disconnected {
|
||||
status = .notConnected
|
||||
}
|
||||
|
||||
if status == .Disconnected || !reconnects {
|
||||
didDisconnect(reason)
|
||||
if status == .disconnected || !reconnects {
|
||||
didDisconnect(reason: reason)
|
||||
} else if !reconnecting {
|
||||
reconnecting = true
|
||||
tryReconnectWithReason(reason)
|
||||
tryReconnect(reason: reason)
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,27 +295,27 @@ 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 }
|
||||
func handleAck(_ ack: Int, data: [Any]) {
|
||||
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)
|
||||
|
||||
dispatch_async(ackQueue) {
|
||||
self.ackHandlers.executeAck(ack, items: data, onQueue: self.handleQueue)
|
||||
handleQueue.async() {
|
||||
self.ackHandlers.executeAck(ack, with: data, onQueue: self.handleQueue)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
guard status == .Connected || isInternalMessage else { return }
|
||||
public func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int = -1) {
|
||||
guard status == .connected || isInternalMessage else { 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)
|
||||
|
||||
dispatch_async(handleQueue) {
|
||||
handleQueue.async {
|
||||
self.anyHandler?(SocketAnyEvent(event: event, items: data))
|
||||
|
||||
for handler in self.handlers where handler.event == event {
|
||||
handler.executeCallback(data, withAck: ack, withSocket: self)
|
||||
handler.executeCallback(with: data, withAck: ack, withSocket: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -328,7 +329,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
}
|
||||
|
||||
/// Joins namespace
|
||||
public func joinNamespace(namespace: String) {
|
||||
public func joinNamespace(_ namespace: String) {
|
||||
nsp = namespace
|
||||
|
||||
if nsp != "/" {
|
||||
@ -338,14 +339,14 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
}
|
||||
|
||||
/// Removes handler(s) based on name
|
||||
public func off(event: String) {
|
||||
public func off(_ event: String) {
|
||||
DefaultSocketLogger.Logger.log("Removing handler for event: %@", type: logType, args: event)
|
||||
|
||||
handlers = handlers.filter({ $0.event != event })
|
||||
}
|
||||
|
||||
/// Removes a handler with the specified UUID gotten from an `on` or `once`
|
||||
public func off(id id: NSUUID) {
|
||||
public func off(id: UUID) {
|
||||
DefaultSocketLogger.Logger.log("Removing handler with id: %@", type: logType, args: id)
|
||||
|
||||
handlers = handlers.filter({ $0.id != id })
|
||||
@ -353,10 +354,11 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
|
||||
/// Adds a handler for an event.
|
||||
/// Returns: A unique id for the handler
|
||||
public func on(event: String, callback: NormalCallback) -> NSUUID {
|
||||
@discardableResult
|
||||
public func on(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
||||
DefaultSocketLogger.Logger.log("Adding handler for event: %@", type: logType, args: event)
|
||||
|
||||
let handler = SocketEventHandler(event: event, id: NSUUID(), callback: callback)
|
||||
let handler = SocketEventHandler(event: event, id: UUID(), callback: callback)
|
||||
handlers.append(handler)
|
||||
|
||||
return handler.id
|
||||
@ -364,10 +366,11 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
|
||||
/// Adds a single-use handler for an event.
|
||||
/// Returns: A unique id for the handler
|
||||
public func once(event: String, callback: NormalCallback) -> NSUUID {
|
||||
@discardableResult
|
||||
public func once(_ event: String, callback: @escaping NormalCallback) -> UUID {
|
||||
DefaultSocketLogger.Logger.log("Adding once handler for event: %@", type: logType, args: event)
|
||||
|
||||
let id = NSUUID()
|
||||
let id = UUID()
|
||||
|
||||
let handler = SocketEventHandler(event: event, id: id) {[weak self] data, ack in
|
||||
guard let this = self else { return }
|
||||
@ -381,38 +384,34 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
}
|
||||
|
||||
/// Adds a handler that will be called on every event.
|
||||
public func onAny(handler: (SocketAnyEvent) -> Void) {
|
||||
public func onAny(_ handler: @escaping (SocketAnyEvent) -> Void) {
|
||||
anyHandler = handler
|
||||
}
|
||||
|
||||
public func parseEngineMessage(msg: String) {
|
||||
public func parseEngineMessage(_ msg: String) {
|
||||
DefaultSocketLogger.Logger.log("Should parse message: %@", type: "SocketIOClient", args: msg)
|
||||
|
||||
dispatch_async(parseQueue) {
|
||||
self.parseSocketMessage(msg)
|
||||
}
|
||||
parseQueue.async { self.parseSocketMessage(msg) }
|
||||
}
|
||||
|
||||
public func parseEngineBinaryData(data: NSData) {
|
||||
dispatch_async(parseQueue) {
|
||||
self.parseBinaryData(data)
|
||||
}
|
||||
public func parseEngineBinaryData(_ data: Data) {
|
||||
parseQueue.async { self.parseBinaryData(data) }
|
||||
}
|
||||
|
||||
/// Tries to reconnect to the server.
|
||||
public func reconnect() {
|
||||
guard !reconnecting else { return }
|
||||
|
||||
engine?.disconnect("manual reconnect")
|
||||
engine?.disconnect(reason: "manual reconnect")
|
||||
}
|
||||
|
||||
/// Removes all handlers.
|
||||
/// Can be used after disconnecting to break any potential remaining retain cycles.
|
||||
public func removeAllHandlers() {
|
||||
handlers.removeAll(keepCapacity: false)
|
||||
handlers.removeAll(keepingCapacity: false)
|
||||
}
|
||||
|
||||
private func tryReconnectWithReason(reason: String) {
|
||||
private func tryReconnect(reason: String) {
|
||||
guard reconnecting else { return }
|
||||
|
||||
DefaultSocketLogger.Logger.log("Starting reconnect", type: logType)
|
||||
@ -425,37 +424,35 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
|
||||
guard reconnecting else { return }
|
||||
|
||||
if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts || !reconnects {
|
||||
return didDisconnect("Reconnect Failed")
|
||||
return didDisconnect(reason: "Reconnect Failed")
|
||||
}
|
||||
|
||||
DefaultSocketLogger.Logger.log("Trying to reconnect", type: logType)
|
||||
handleEvent("reconnectAttempt", data: [reconnectAttempts - currentReconnectAttempt],
|
||||
isInternalMessage: true)
|
||||
handleEvent("reconnectAttempt", data: [(reconnectAttempts - currentReconnectAttempt)], isInternalMessage: true)
|
||||
|
||||
currentReconnectAttempt += 1
|
||||
connect()
|
||||
|
||||
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(UInt64(reconnectWait) * NSEC_PER_SEC))
|
||||
let deadline = DispatchTime.now() + Double(Int64(UInt64(reconnectWait) * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
|
||||
|
||||
dispatch_after(time, dispatch_get_main_queue(), _tryReconnect)
|
||||
DispatchQueue.main.asyncAfter(deadline: deadline, execute: _tryReconnect)
|
||||
}
|
||||
}
|
||||
|
||||
// Test extensions
|
||||
extension SocketIOClient {
|
||||
|
||||
// Test properties
|
||||
|
||||
var testHandlers: [SocketEventHandler] {
|
||||
return handlers
|
||||
}
|
||||
|
||||
|
||||
func setTestable() {
|
||||
status = .Connected
|
||||
status = .connected
|
||||
}
|
||||
|
||||
func setTestEngine(engine: SocketEngineSpec?) {
|
||||
|
||||
func setTestEngine(_ engine: SocketEngineSpec?) {
|
||||
self.engine = engine
|
||||
}
|
||||
|
||||
func emitTest(event: String, _ data: AnyObject...) {
|
||||
|
||||
func emitTest(event: String, _ data: Any...) {
|
||||
_emit([event] + data)
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
public struct SocketIOClientConfiguration : ArrayLiteralConvertible, CollectionType, MutableCollectionType {
|
||||
public struct SocketIOClientConfiguration : ExpressibleByArrayLiteral, Collection, MutableCollection {
|
||||
public typealias Element = SocketIOClientOption
|
||||
public typealias Index = Array<SocketIOClientOption>.Index
|
||||
public typealias Generator = Array<SocketIOClientOption>.Generator
|
||||
@ -42,7 +42,7 @@ public struct SocketIOClientConfiguration : ArrayLiteralConvertible, CollectionT
|
||||
return backingArray.isEmpty
|
||||
}
|
||||
|
||||
public var count: Index.Distance {
|
||||
public var count: Index.Stride {
|
||||
return backingArray.count
|
||||
}
|
||||
|
||||
@ -75,10 +75,14 @@ public struct SocketIOClientConfiguration : ArrayLiteralConvertible, CollectionT
|
||||
}
|
||||
|
||||
public func generate() -> Generator {
|
||||
return backingArray.generate()
|
||||
return backingArray.makeIterator()
|
||||
}
|
||||
|
||||
public mutating func insert(element: Element, replacing replace: Bool = true) {
|
||||
public func index(after i: Index) -> Index {
|
||||
return backingArray.index(after: i)
|
||||
}
|
||||
|
||||
public mutating func insert(_ element: Element, replacing replace: Bool = true) {
|
||||
for i in 0..<backingArray.count where backingArray[i] == element {
|
||||
guard replace else { return }
|
||||
|
||||
@ -90,18 +94,15 @@ public struct SocketIOClientConfiguration : ArrayLiteralConvertible, CollectionT
|
||||
backingArray.append(element)
|
||||
}
|
||||
|
||||
@warn_unused_result
|
||||
public func prefixUpTo(end: Index) -> SubSequence {
|
||||
return backingArray.prefixUpTo(end)
|
||||
public func prefix(upTo end: Index) -> SubSequence {
|
||||
return backingArray.prefix(upTo: end)
|
||||
}
|
||||
|
||||
@warn_unused_result
|
||||
public func prefixThrough(position: Index) -> SubSequence {
|
||||
return backingArray.prefixThrough(position)
|
||||
public func prefix(through position: Index) -> SubSequence {
|
||||
return backingArray.prefix(through: position)
|
||||
}
|
||||
|
||||
@warn_unused_result
|
||||
public func suffixFrom(start: Index) -> SubSequence {
|
||||
return backingArray.suffixFrom(start)
|
||||
public func suffix(from start: Index) -> SubSequence {
|
||||
return backingArray.suffix(from: start)
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,123 +25,123 @@
|
||||
import Foundation
|
||||
|
||||
protocol ClientOption : CustomStringConvertible, Equatable {
|
||||
func getSocketIOOptionValue() -> AnyObject
|
||||
func getSocketIOOptionValue() -> Any
|
||||
}
|
||||
|
||||
public enum SocketIOClientOption : ClientOption {
|
||||
case ConnectParams([String: AnyObject])
|
||||
case Cookies([NSHTTPCookie])
|
||||
case DoubleEncodeUTF8(Bool)
|
||||
case ExtraHeaders([String: String])
|
||||
case ForceNew(Bool)
|
||||
case ForcePolling(Bool)
|
||||
case ForceWebsockets(Bool)
|
||||
case HandleQueue(dispatch_queue_t)
|
||||
case Log(Bool)
|
||||
case Logger(SocketLogger)
|
||||
case Nsp(String)
|
||||
case Path(String)
|
||||
case Reconnects(Bool)
|
||||
case ReconnectAttempts(Int)
|
||||
case ReconnectWait(Int)
|
||||
case Secure(Bool)
|
||||
case Security(SSLSecurity)
|
||||
case SelfSigned(Bool)
|
||||
case SessionDelegate(NSURLSessionDelegate)
|
||||
case VoipEnabled(Bool)
|
||||
case connectParams([String: Any])
|
||||
case cookies([HTTPCookie])
|
||||
case doubleEncodeUTF8(Bool)
|
||||
case extraHeaders([String: String])
|
||||
case forceNew(Bool)
|
||||
case forcePolling(Bool)
|
||||
case forceWebsockets(Bool)
|
||||
case handleQueue(DispatchQueue)
|
||||
case log(Bool)
|
||||
case logger(SocketLogger)
|
||||
case nsp(String)
|
||||
case path(String)
|
||||
case reconnects(Bool)
|
||||
case reconnectAttempts(Int)
|
||||
case reconnectWait(Int)
|
||||
case secure(Bool)
|
||||
case security(SSLSecurity)
|
||||
case selfSigned(Bool)
|
||||
case sessionDelegate(URLSessionDelegate)
|
||||
case voipEnabled(Bool)
|
||||
|
||||
public var description: String {
|
||||
let description: String
|
||||
|
||||
switch self {
|
||||
case .ConnectParams:
|
||||
case .connectParams:
|
||||
description = "connectParams"
|
||||
case .Cookies:
|
||||
case .cookies:
|
||||
description = "cookies"
|
||||
case .DoubleEncodeUTF8:
|
||||
case .doubleEncodeUTF8:
|
||||
description = "doubleEncodeUTF8"
|
||||
case .ExtraHeaders:
|
||||
case .extraHeaders:
|
||||
description = "extraHeaders"
|
||||
case .ForceNew:
|
||||
case .forceNew:
|
||||
description = "forceNew"
|
||||
case .ForcePolling:
|
||||
case .forcePolling:
|
||||
description = "forcePolling"
|
||||
case .ForceWebsockets:
|
||||
case .forceWebsockets:
|
||||
description = "forceWebsockets"
|
||||
case .HandleQueue:
|
||||
case .handleQueue:
|
||||
description = "handleQueue"
|
||||
case .Log:
|
||||
case .log:
|
||||
description = "log"
|
||||
case .Logger:
|
||||
case .logger:
|
||||
description = "logger"
|
||||
case .Nsp:
|
||||
case .nsp:
|
||||
description = "nsp"
|
||||
case .Path:
|
||||
case .path:
|
||||
description = "path"
|
||||
case .Reconnects:
|
||||
case .reconnects:
|
||||
description = "reconnects"
|
||||
case .ReconnectAttempts:
|
||||
case .reconnectAttempts:
|
||||
description = "reconnectAttempts"
|
||||
case .ReconnectWait:
|
||||
case .reconnectWait:
|
||||
description = "reconnectWait"
|
||||
case .Secure:
|
||||
case .secure:
|
||||
description = "secure"
|
||||
case .Security:
|
||||
description = "security"
|
||||
case .SelfSigned:
|
||||
case .selfSigned:
|
||||
description = "selfSigned"
|
||||
case .SessionDelegate:
|
||||
case .security:
|
||||
description = "security"
|
||||
case .sessionDelegate:
|
||||
description = "sessionDelegate"
|
||||
case .VoipEnabled:
|
||||
case .voipEnabled:
|
||||
description = "voipEnabled"
|
||||
}
|
||||
|
||||
return description
|
||||
}
|
||||
|
||||
func getSocketIOOptionValue() -> AnyObject {
|
||||
let value: AnyObject
|
||||
func getSocketIOOptionValue() -> Any {
|
||||
let value: Any
|
||||
|
||||
switch self {
|
||||
case let .ConnectParams(params):
|
||||
case let .connectParams(params):
|
||||
value = params
|
||||
case let .Cookies(cookies):
|
||||
case let .cookies(cookies):
|
||||
value = cookies
|
||||
case let .DoubleEncodeUTF8(encode):
|
||||
case let .doubleEncodeUTF8(encode):
|
||||
value = encode
|
||||
case let .ExtraHeaders(headers):
|
||||
case let .extraHeaders(headers):
|
||||
value = headers
|
||||
case let .ForceNew(force):
|
||||
case let .forceNew(force):
|
||||
value = force
|
||||
case let .ForcePolling(force):
|
||||
case let .forcePolling(force):
|
||||
value = force
|
||||
case let .ForceWebsockets(force):
|
||||
case let .forceWebsockets(force):
|
||||
value = force
|
||||
case let .HandleQueue(queue):
|
||||
case let .handleQueue(queue):
|
||||
value = queue
|
||||
case let .Log(log):
|
||||
case let .log(log):
|
||||
value = log
|
||||
case let .Logger(logger):
|
||||
case let .logger(logger):
|
||||
value = logger
|
||||
case let .Nsp(nsp):
|
||||
case let .nsp(nsp):
|
||||
value = nsp
|
||||
case let .Path(path):
|
||||
case let .path(path):
|
||||
value = path
|
||||
case let .Reconnects(reconnects):
|
||||
case let .reconnects(reconnects):
|
||||
value = reconnects
|
||||
case let .ReconnectAttempts(attempts):
|
||||
case let .reconnectAttempts(attempts):
|
||||
value = attempts
|
||||
case let .ReconnectWait(wait):
|
||||
case let .reconnectWait(wait):
|
||||
value = wait
|
||||
case let .Secure(secure):
|
||||
case let .secure(secure):
|
||||
value = secure
|
||||
case let .Security(security):
|
||||
case let .security(security):
|
||||
value = security
|
||||
case let .SelfSigned(signed):
|
||||
case let .selfSigned(signed):
|
||||
value = signed
|
||||
case let .SessionDelegate(delegate):
|
||||
case let .sessionDelegate(delegate):
|
||||
value = delegate
|
||||
case let .VoipEnabled(enabled):
|
||||
case let .voipEnabled(enabled):
|
||||
value = enabled
|
||||
}
|
||||
|
||||
|
||||
@ -29,9 +29,9 @@ protocol SocketIOClientSpec : class {
|
||||
func didConnect()
|
||||
func didDisconnect(reason: String)
|
||||
func didError(reason: String)
|
||||
func handleAck(ack: Int, data: [AnyObject])
|
||||
func handleEvent(event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int)
|
||||
func joinNamespace(namespace: String)
|
||||
func handleAck(_ ack: Int, data: [Any])
|
||||
func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int)
|
||||
func joinNamespace(_ namespace: String)
|
||||
}
|
||||
|
||||
extension SocketIOClientSpec {
|
||||
|
||||
@ -28,5 +28,5 @@ import Foundation
|
||||
///
|
||||
/// **Disconnected**: connected before
|
||||
@objc public enum SocketIOClientStatus : Int {
|
||||
case NotConnected, Disconnected, Connecting, Connected
|
||||
case notConnected, disconnected, connecting, connected
|
||||
}
|
||||
|
||||
@ -29,28 +29,28 @@ public protocol SocketLogger : class {
|
||||
var log: Bool { get set }
|
||||
|
||||
/// Normal log messages
|
||||
func log(message: String, type: String, args: AnyObject...)
|
||||
func log(_ message: String, type: String, args: Any...)
|
||||
|
||||
/// Error Messages
|
||||
func error(message: String, type: String, args: AnyObject...)
|
||||
func error(_ message: String, type: String, args: Any...)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func error(message: String, type: String, args: AnyObject...) {
|
||||
func error(_ message: String, type: String, args: Any...) {
|
||||
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 }
|
||||
|
||||
let newArgs = args.map({arg -> CVarArgType in String(arg)})
|
||||
let replaced = String(format: message, arguments: newArgs)
|
||||
let newArgs = args.map({arg -> CVarArg in String(describing: arg)})
|
||||
let messageFormat = String(format: message, arguments: newArgs)
|
||||
|
||||
NSLog("%@ %@: %@", logType, type, replaced)
|
||||
NSLog("\(logType) \(type): %@", messageFormat)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ import Foundation
|
||||
|
||||
struct SocketPacket {
|
||||
enum PacketType: Int {
|
||||
case Connect, Disconnect, Event, Ack, Error, BinaryEvent, BinaryAck
|
||||
case connect, disconnect, event, ack, error, binaryEvent, binaryAck
|
||||
}
|
||||
|
||||
private let placeholders: Int
|
||||
@ -38,11 +38,10 @@ struct SocketPacket {
|
||||
let id: Int
|
||||
let type: PacketType
|
||||
|
||||
var binary: [NSData]
|
||||
var data: [AnyObject]
|
||||
|
||||
var args: [AnyObject] {
|
||||
if type == .Event || type == .BinaryEvent && data.count != 0 {
|
||||
var binary: [Data]
|
||||
var data: [Any]
|
||||
var args: [Any] {
|
||||
if type == .event || type == .binaryEvent && data.count != 0 {
|
||||
return Array(data.dropFirst())
|
||||
} else {
|
||||
return data
|
||||
@ -51,19 +50,19 @@ struct SocketPacket {
|
||||
|
||||
var description: String {
|
||||
return "SocketPacket {type: \(String(type.rawValue)); data: " +
|
||||
"\(String(data)); id: \(id); placeholders: \(placeholders); nsp: \(nsp)}"
|
||||
"\(String(describing: data)); id: \(id); placeholders: \(placeholders); nsp: \(nsp)}"
|
||||
}
|
||||
|
||||
var event: String {
|
||||
return String(data[0])
|
||||
return String(describing: data[0])
|
||||
}
|
||||
|
||||
var packetString: String {
|
||||
return createPacketString()
|
||||
}
|
||||
|
||||
init(type: PacketType, data: [AnyObject] = [AnyObject](), id: Int = -1,
|
||||
nsp: String, placeholders: Int = 0, binary: [NSData] = [NSData]()) {
|
||||
init(type: PacketType, data: [Any] = [Any](), id: Int = -1, nsp: String, placeholders: Int = 0,
|
||||
binary: [Data] = [Data]()) {
|
||||
self.data = data
|
||||
self.id = id
|
||||
self.nsp = nsp
|
||||
@ -72,7 +71,7 @@ struct SocketPacket {
|
||||
self.binary = binary
|
||||
}
|
||||
|
||||
mutating func addData(data: NSData) -> Bool {
|
||||
mutating func addData(_ data: Data) -> Bool {
|
||||
if placeholders == binary.count {
|
||||
return true
|
||||
}
|
||||
@ -87,7 +86,7 @@ struct SocketPacket {
|
||||
}
|
||||
}
|
||||
|
||||
private func completeMessage(message: String) -> String {
|
||||
private func completeMessage(_ message: String) -> String {
|
||||
let restOfMessage: String
|
||||
|
||||
if data.count == 0 {
|
||||
@ -96,7 +95,7 @@ struct SocketPacket {
|
||||
|
||||
do {
|
||||
let jsonSend = try data.toJSON()
|
||||
guard let jsonString = String(data: jsonSend, encoding: NSUTF8StringEncoding) else { return message + "[]" }
|
||||
guard let jsonString = String(data: jsonSend, encoding: .utf8) else { return message + "[]" }
|
||||
|
||||
restOfMessage = jsonString
|
||||
} catch {
|
||||
@ -112,9 +111,9 @@ struct SocketPacket {
|
||||
private func createPacketString() -> String {
|
||||
let typeString = String(type.rawValue)
|
||||
// Binary count?
|
||||
let binaryCountString = typeString + (type == .BinaryEvent || type == .BinaryAck ? String(binary.count) + "-" : "")
|
||||
let binaryCountString = typeString + (type == .binaryEvent || type == .binaryAck ? "\(String(binary.count))-" : "")
|
||||
// Namespace?
|
||||
let nspString = binaryCountString + (nsp != "/" ? nsp + "," : "")
|
||||
let nspString = binaryCountString + (nsp != "/" ? "\(nsp)," : "")
|
||||
// Ack number?
|
||||
let idString = nspString + (id != -1 ? String(id) : "")
|
||||
|
||||
@ -132,18 +131,21 @@ struct SocketPacket {
|
||||
// If object is a collection it will recurse
|
||||
// Returns the object if it is not a placeholder or the corresponding
|
||||
// binary data
|
||||
private func _fillInPlaceholders(object: AnyObject) -> AnyObject {
|
||||
private func _fillInPlaceholders(_ object: Any) -> Any {
|
||||
switch object {
|
||||
case let dict as NSDictionary:
|
||||
case let dict as [String: Any]:
|
||||
if dict["_placeholder"] as? Bool ?? false {
|
||||
return binary[dict["num"] as! Int]
|
||||
} else {
|
||||
return dict.reduce(NSMutableDictionary(), combine: {cur, keyValue in
|
||||
cur[keyValue.0 as! NSCopying] = _fillInPlaceholders(keyValue.1)
|
||||
return dict.reduce([String: Any](), {cur, keyValue in
|
||||
var cur = cur
|
||||
|
||||
cur[keyValue.0] = _fillInPlaceholders(keyValue.1)
|
||||
|
||||
return cur
|
||||
})
|
||||
}
|
||||
case let arr as [AnyObject]:
|
||||
case let arr as [Any]:
|
||||
return arr.map(_fillInPlaceholders)
|
||||
default:
|
||||
return object
|
||||
@ -152,22 +154,22 @@ struct SocketPacket {
|
||||
}
|
||||
|
||||
extension SocketPacket {
|
||||
private static func findType(binCount: Int, ack: Bool) -> PacketType {
|
||||
private static func findType(_ binCount: Int, ack: Bool) -> PacketType {
|
||||
switch binCount {
|
||||
case 0 where !ack:
|
||||
return .Event
|
||||
return .event
|
||||
case 0 where ack:
|
||||
return .Ack
|
||||
return .ack
|
||||
case _ where !ack:
|
||||
return .BinaryEvent
|
||||
return .binaryEvent
|
||||
case _ where ack:
|
||||
return .BinaryAck
|
||||
return .binaryAck
|
||||
default:
|
||||
return .Error
|
||||
return .error
|
||||
}
|
||||
}
|
||||
|
||||
static func packetFromEmit(items: [AnyObject], id: Int, nsp: String, ack: Bool) -> SocketPacket {
|
||||
static func packetFromEmit(_ items: [Any], id: Int, nsp: String, ack: Bool) -> SocketPacket {
|
||||
let (parsedData, binary) = deconstructData(items)
|
||||
let packet = SocketPacket(type: findType(binary.count, ack: ack), data: parsedData,
|
||||
id: id, nsp: nsp, binary: binary)
|
||||
@ -178,19 +180,23 @@ extension SocketPacket {
|
||||
|
||||
private extension SocketPacket {
|
||||
// Recursive function that looks for NSData in collections
|
||||
static func shred(data: AnyObject, inout binary: [NSData]) -> AnyObject {
|
||||
let placeholder = ["_placeholder": true, "num": binary.count]
|
||||
static func shred(_ data: Any, binary: inout [Data]) -> Any {
|
||||
let placeholder = ["_placeholder": true, "num": binary.count] as [String : Any]
|
||||
|
||||
switch data {
|
||||
case let bin as NSData:
|
||||
case let bin as Data:
|
||||
binary.append(bin)
|
||||
|
||||
return placeholder
|
||||
case let arr as [AnyObject]:
|
||||
case let arr as [Any]:
|
||||
return arr.map({shred($0, binary: &binary)})
|
||||
case let dict as NSDictionary:
|
||||
return dict.reduce(NSMutableDictionary(), combine: {cur, keyValue in
|
||||
cur[keyValue.0 as! NSCopying] = shred(keyValue.1, binary: &binary)
|
||||
return cur
|
||||
case let dict as [String: Any]:
|
||||
return dict.reduce([String: Any](), {cur, keyValue in
|
||||
var mutCur = cur
|
||||
|
||||
mutCur[keyValue.0] = shred(keyValue.1, binary: &binary)
|
||||
|
||||
return mutCur
|
||||
})
|
||||
default:
|
||||
return data
|
||||
@ -199,8 +205,8 @@ private extension SocketPacket {
|
||||
|
||||
// Removes binary data from emit data
|
||||
// Returns a type containing the de-binaryed data and the binary
|
||||
static func deconstructData(data: [AnyObject]) -> ([AnyObject], [NSData]) {
|
||||
var binary = [NSData]()
|
||||
static func deconstructData(_ data: [Any]) -> ([Any], [Data]) {
|
||||
var binary = [Data]()
|
||||
|
||||
return (data.map({shred($0, binary: &binary)}), binary)
|
||||
}
|
||||
|
||||
@ -23,16 +23,16 @@
|
||||
import Foundation
|
||||
|
||||
protocol SocketParsable : SocketIOClientSpec {
|
||||
func parseBinaryData(data: NSData)
|
||||
func parseSocketMessage(message: String)
|
||||
func parseBinaryData(_ data: Data)
|
||||
func parseSocketMessage(_ message: String)
|
||||
}
|
||||
|
||||
extension SocketParsable {
|
||||
private func isCorrectNamespace(nsp: String) -> Bool {
|
||||
private func isCorrectNamespace(_ nsp: String) -> Bool {
|
||||
return nsp == self.nsp
|
||||
}
|
||||
|
||||
private func handleConnect(packetNamespace: String) {
|
||||
private func handleConnect(_ packetNamespace: String) {
|
||||
if packetNamespace == "/" && nsp != "/" {
|
||||
joinNamespace(nsp)
|
||||
} else {
|
||||
@ -40,21 +40,21 @@ extension SocketParsable {
|
||||
}
|
||||
}
|
||||
|
||||
private func handlePacket(pack: SocketPacket) {
|
||||
private func handlePacket(_ pack: SocketPacket) {
|
||||
switch pack.type {
|
||||
case .Event where isCorrectNamespace(pack.nsp):
|
||||
case .event where isCorrectNamespace(pack.nsp):
|
||||
handleEvent(pack.event, data: pack.args, isInternalMessage: false, withAck: pack.id)
|
||||
case .Ack where isCorrectNamespace(pack.nsp):
|
||||
case .ack where isCorrectNamespace(pack.nsp):
|
||||
handleAck(pack.id, data: pack.data)
|
||||
case .BinaryEvent where isCorrectNamespace(pack.nsp):
|
||||
case .binaryEvent where isCorrectNamespace(pack.nsp):
|
||||
waitingPackets.append(pack)
|
||||
case .BinaryAck where isCorrectNamespace(pack.nsp):
|
||||
case .binaryAck where isCorrectNamespace(pack.nsp):
|
||||
waitingPackets.append(pack)
|
||||
case .Connect:
|
||||
case .connect:
|
||||
handleConnect(pack.nsp)
|
||||
case .Disconnect:
|
||||
didDisconnect("Got Disconnect")
|
||||
case .Error:
|
||||
case .disconnect:
|
||||
didDisconnect(reason: "Got Disconnect")
|
||||
case .error:
|
||||
handleEvent("error", data: pack.data, isInternalMessage: true, withAck: pack.id)
|
||||
default:
|
||||
DefaultSocketLogger.Logger.log("Got invalid packet: %@", type: "SocketParser", args: pack.description)
|
||||
@ -62,93 +62,93 @@ extension SocketParsable {
|
||||
}
|
||||
|
||||
/// Parses a messsage from the engine. Returning either a string error or a complete SocketPacket
|
||||
func parseString(message: String) -> Either<String, SocketPacket> {
|
||||
func parseString(_ message: String) -> Either<String, SocketPacket> {
|
||||
var reader = SocketStringReader(message: message)
|
||||
|
||||
guard let type = SocketPacket.PacketType(rawValue: Int(reader.read(1)) ?? -1) else {
|
||||
return .Left("Invalid packet type")
|
||||
guard let type = Int(reader.read(count: 1)).flatMap({ SocketPacket.PacketType(rawValue: $0) }) else {
|
||||
return .left("Invalid packet type")
|
||||
}
|
||||
|
||||
if !reader.hasNext {
|
||||
return .Right(SocketPacket(type: type, nsp: "/"))
|
||||
return .right(SocketPacket(type: type, nsp: "/"))
|
||||
}
|
||||
|
||||
var namespace = "/"
|
||||
var placeholders = -1
|
||||
|
||||
if type == .BinaryEvent || type == .BinaryAck {
|
||||
if let holders = Int(reader.readUntilStringOccurence("-")) {
|
||||
if type == .binaryEvent || type == .binaryAck {
|
||||
if let holders = Int(reader.readUntilOccurence(of: "-")) {
|
||||
placeholders = holders
|
||||
} else {
|
||||
return .Left("Invalid packet")
|
||||
return .left("Invalid packet")
|
||||
}
|
||||
}
|
||||
|
||||
if reader.currentCharacter == "/" {
|
||||
namespace = reader.readUntilStringOccurence(",") ?? reader.readUntilEnd()
|
||||
namespace = reader.readUntilOccurence(of: ",")
|
||||
}
|
||||
|
||||
if !reader.hasNext {
|
||||
return .Right(SocketPacket(type: type, nsp: namespace, placeholders: placeholders))
|
||||
return .right(SocketPacket(type: type, nsp: namespace, placeholders: placeholders))
|
||||
}
|
||||
|
||||
var idString = ""
|
||||
|
||||
if type == .Error {
|
||||
reader.advanceIndexBy(-1)
|
||||
if type == .error {
|
||||
reader.advance(by: -1)
|
||||
} else {
|
||||
while reader.hasNext {
|
||||
if let int = Int(reader.read(1)) {
|
||||
if let int = Int(reader.read(count: 1)) {
|
||||
idString += String(int)
|
||||
} else {
|
||||
reader.advanceIndexBy(-2)
|
||||
reader.advance(by: -2)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let d = message[reader.currentIndex.advancedBy(1)..<message.endIndex]
|
||||
|
||||
switch parseData(d) {
|
||||
case let .Left(err):
|
||||
// Errors aren't always enclosed in an array
|
||||
if case let .Right(data) = parseData("\([d as AnyObject])") {
|
||||
return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1,
|
||||
nsp: namespace, placeholders: placeholders))
|
||||
} else {
|
||||
return .Left(err)
|
||||
}
|
||||
case let .Right(data):
|
||||
return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1,
|
||||
|
||||
var dataArray = message[message.characters.index(reader.currentIndex, offsetBy: 1)..<message.endIndex]
|
||||
|
||||
if type == .error && !dataArray.hasPrefix("[") && !dataArray.hasSuffix("]") {
|
||||
dataArray = "[" + dataArray + "]"
|
||||
}
|
||||
|
||||
switch parseData(dataArray) {
|
||||
case let .left(err):
|
||||
return .left(err)
|
||||
case let .right(data):
|
||||
return .right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1,
|
||||
nsp: namespace, placeholders: placeholders))
|
||||
}
|
||||
}
|
||||
|
||||
// Parses data for events
|
||||
private func parseData(data: String) -> Either<String, [AnyObject]> {
|
||||
private func parseData(_ data: String) -> Either<String, [Any]> {
|
||||
do {
|
||||
return .Right(try data.toArray())
|
||||
return .right(try data.toArray())
|
||||
} catch {
|
||||
return .Left("Error parsing data for packet")
|
||||
return .left("Error parsing data for packet")
|
||||
}
|
||||
}
|
||||
|
||||
// Parses messages recieved
|
||||
func parseSocketMessage(message: String) {
|
||||
func parseSocketMessage(_ message: String) {
|
||||
guard !message.isEmpty else { return }
|
||||
|
||||
DefaultSocketLogger.Logger.log("Parsing %@", type: "SocketParser", args: message)
|
||||
|
||||
switch parseString(message) {
|
||||
case let .Left(err):
|
||||
case let .left(err):
|
||||
DefaultSocketLogger.Logger.error("\(err): %@", type: "SocketParser", args: message)
|
||||
case let .Right(pack):
|
||||
case let .right(pack):
|
||||
DefaultSocketLogger.Logger.log("Decoded packet as: %@", type: "SocketParser", args: pack.description)
|
||||
handlePacket(pack)
|
||||
}
|
||||
}
|
||||
|
||||
func parseBinaryData(data: NSData) {
|
||||
func parseBinaryData(_ data: Data) {
|
||||
guard !waitingPackets.isEmpty else {
|
||||
DefaultSocketLogger.Logger.error("Got data when not remaking packet", type: "SocketParser")
|
||||
return
|
||||
@ -159,9 +159,8 @@ extension SocketParsable {
|
||||
|
||||
let packet = waitingPackets.removeLast()
|
||||
|
||||
if packet.type != .BinaryAck {
|
||||
handleEvent(packet.event, data: packet.args ?? [],
|
||||
isInternalMessage: false, withAck: packet.id)
|
||||
if packet.type != .binaryAck {
|
||||
handleEvent(packet.event, data: packet.args, isInternalMessage: false, withAck: packet.id)
|
||||
} else {
|
||||
handleAck(packet.id, data: packet.args)
|
||||
}
|
||||
|
||||
@ -38,31 +38,36 @@ struct SocketStringReader {
|
||||
currentIndex = message.startIndex
|
||||
}
|
||||
|
||||
mutating func advanceIndexBy(n: Int) {
|
||||
currentIndex = currentIndex.advancedBy(n)
|
||||
@discardableResult
|
||||
mutating func advance(by: Int) -> String.Index {
|
||||
currentIndex = message.characters.index(currentIndex, offsetBy: by)
|
||||
|
||||
return currentIndex
|
||||
}
|
||||
|
||||
mutating func read(readLength: Int) -> String {
|
||||
let readString = message[currentIndex..<currentIndex.advancedBy(readLength)]
|
||||
advanceIndexBy(readLength)
|
||||
mutating func read(count: Int) -> String {
|
||||
let readString = message[currentIndex..<message.characters.index(currentIndex, offsetBy: count)]
|
||||
|
||||
advance(by: count)
|
||||
|
||||
return readString
|
||||
}
|
||||
|
||||
mutating func readUntilStringOccurence(string: String) -> String {
|
||||
mutating func readUntilOccurence(of string: String) -> String {
|
||||
let substring = message[currentIndex..<message.endIndex]
|
||||
guard let foundRange = substring.rangeOfString(string) else {
|
||||
|
||||
guard let foundRange = substring.range(of: string) else {
|
||||
currentIndex = message.endIndex
|
||||
|
||||
return substring
|
||||
}
|
||||
|
||||
advanceIndexBy(message.startIndex.distanceTo(foundRange.startIndex) + 1)
|
||||
advance(by: message.characters.distance(from: message.characters.startIndex, to: foundRange.lowerBound) + 1)
|
||||
|
||||
return substring.substringToIndex(foundRange.startIndex)
|
||||
return substring.substring(to: foundRange.lowerBound)
|
||||
}
|
||||
|
||||
mutating func readUntilEnd() -> String {
|
||||
return read(currentIndex.distanceTo(message.endIndex))
|
||||
return read(count: message.characters.distance(from: currentIndex, to: message.endIndex))
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,14 +24,29 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public typealias AckCallback = ([AnyObject]) -> Void
|
||||
public typealias NormalCallback = ([AnyObject], SocketAckEmitter) -> Void
|
||||
public typealias OnAckCallback = (timeoutAfter: UInt64, callback: AckCallback) -> Void
|
||||
public protocol SocketData {}
|
||||
|
||||
typealias Probe = (msg: String, type: SocketEnginePacketType, data: [NSData])
|
||||
extension Array : SocketData {}
|
||||
extension Bool : SocketData {}
|
||||
extension Dictionary : SocketData {}
|
||||
extension Double : SocketData {}
|
||||
extension Int : SocketData {}
|
||||
extension NSArray : SocketData {}
|
||||
extension Data : SocketData {}
|
||||
extension NSData : SocketData {}
|
||||
extension NSDictionary : SocketData {}
|
||||
extension NSString : SocketData {}
|
||||
extension NSNull : SocketData {}
|
||||
extension String : SocketData {}
|
||||
|
||||
public typealias AckCallback = ([Any]) -> Void
|
||||
public typealias NormalCallback = ([Any], SocketAckEmitter) -> Void
|
||||
public typealias OnAckCallback = (_ timeoutAfter: UInt64, _ callback: @escaping AckCallback) -> Void
|
||||
|
||||
typealias Probe = (msg: String, type: SocketEnginePacketType, data: [Data])
|
||||
typealias ProbeWaitQueue = [Probe]
|
||||
|
||||
enum Either<E, V> {
|
||||
case Left(E)
|
||||
case Right(V)
|
||||
case left(E)
|
||||
case right(V)
|
||||
}
|
||||
|
||||
@ -28,51 +28,51 @@ public let WebsocketDidDisconnectNotification = "WebsocketDidDisconnectNotificat
|
||||
public let WebsocketDisconnectionErrorKeyName = "WebsocketDisconnectionErrorKeyName"
|
||||
|
||||
public protocol WebSocketDelegate: class {
|
||||
func websocketDidConnect(socket: WebSocket)
|
||||
func websocketDidDisconnect(socket: WebSocket, error: NSError?)
|
||||
func websocketDidReceiveMessage(socket: WebSocket, text: String)
|
||||
func websocketDidReceiveData(socket: WebSocket, data: NSData)
|
||||
func websocketDidConnect(_ socket: WebSocket)
|
||||
func websocketDidDisconnect(_ socket: WebSocket, error: NSError?)
|
||||
func websocketDidReceiveMessage(_ socket: WebSocket, text: String)
|
||||
func websocketDidReceiveData(_ socket: WebSocket, data: Data)
|
||||
}
|
||||
|
||||
public protocol WebSocketPongDelegate: class {
|
||||
func websocketDidReceivePong(socket: WebSocket)
|
||||
func websocketDidReceivePong(_ socket: WebSocket)
|
||||
}
|
||||
|
||||
public class WebSocket: NSObject, NSStreamDelegate {
|
||||
public class WebSocket : NSObject, StreamDelegate {
|
||||
|
||||
enum OpCode: UInt8 {
|
||||
case ContinueFrame = 0x0
|
||||
case TextFrame = 0x1
|
||||
case BinaryFrame = 0x2
|
||||
enum OpCode : UInt8 {
|
||||
case continueFrame = 0x0
|
||||
case textFrame = 0x1
|
||||
case binaryFrame = 0x2
|
||||
// 3-7 are reserved.
|
||||
case ConnectionClose = 0x8
|
||||
case Ping = 0x9
|
||||
case Pong = 0xA
|
||||
case connectionClose = 0x8
|
||||
case ping = 0x9
|
||||
case pong = 0xA
|
||||
// B-F reserved.
|
||||
}
|
||||
|
||||
public enum CloseCode: UInt16 {
|
||||
case Normal = 1000
|
||||
case GoingAway = 1001
|
||||
case ProtocolError = 1002
|
||||
case ProtocolUnhandledType = 1003
|
||||
public enum CloseCode : UInt16 {
|
||||
case normal = 1000
|
||||
case goingAway = 1001
|
||||
case protocolError = 1002
|
||||
case protocolUnhandledType = 1003
|
||||
// 1004 reserved.
|
||||
case NoStatusReceived = 1005
|
||||
case noStatusReceived = 1005
|
||||
// 1006 reserved.
|
||||
case Encoding = 1007
|
||||
case PolicyViolated = 1008
|
||||
case MessageTooBig = 1009
|
||||
case encoding = 1007
|
||||
case policyViolated = 1008
|
||||
case messageTooBig = 1009
|
||||
}
|
||||
|
||||
public static let ErrorDomain = "WebSocket"
|
||||
|
||||
enum InternalErrorCode: UInt16 {
|
||||
enum InternalErrorCode : UInt16 {
|
||||
// 0-999 WebSocket status codes not used
|
||||
case OutputStreamWriteError = 1
|
||||
case outputStreamWriteError = 1
|
||||
}
|
||||
|
||||
/// Where the callback is executed. It defaults to the main UI thread queue.
|
||||
public var callbackQueue = dispatch_get_main_queue()
|
||||
public var callbackQueue = DispatchQueue.main
|
||||
|
||||
var optionalProtocols : [String]?
|
||||
|
||||
@ -101,7 +101,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
|
||||
class WSResponse {
|
||||
var isFin = false
|
||||
var code: OpCode = .ContinueFrame
|
||||
var code: OpCode = .continueFrame
|
||||
var bytesLeft = 0
|
||||
var frameCount = 0
|
||||
var buffer: NSMutableData?
|
||||
@ -122,7 +122,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
public var onConnect: ((Void) -> Void)?
|
||||
public var onDisconnect: ((NSError?) -> Void)?
|
||||
public var onText: ((String) -> Void)?
|
||||
public var onData: ((NSData) -> Void)?
|
||||
public var onData: ((Data) -> Void)?
|
||||
public var onPong: ((Void) -> Void)?
|
||||
|
||||
public var headers = [String: String]()
|
||||
@ -135,24 +135,25 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
public var isConnected :Bool {
|
||||
return connected
|
||||
}
|
||||
|
||||
public var currentURL: NSURL { return url }
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private var url: NSURL
|
||||
private var inputStream: NSInputStream?
|
||||
private var outputStream: NSOutputStream?
|
||||
private var inputStream: InputStream?
|
||||
private var outputStream: OutputStream?
|
||||
private var connected = false
|
||||
private var isConnecting = false
|
||||
private var writeQueue = NSOperationQueue()
|
||||
private var writeQueue = OperationQueue()
|
||||
private var readStack = [WSResponse]()
|
||||
private var inputQueue = [NSData]()
|
||||
private var fragBuffer: NSData?
|
||||
private var inputQueue = [Data]()
|
||||
private var fragBuffer: Data?
|
||||
private var certValidated = false
|
||||
private var didDisconnect = false
|
||||
private var readyToWrite = false
|
||||
private let mutex = NSLock()
|
||||
private let notificationCenter = NSNotificationCenter.defaultCenter()
|
||||
private let notificationCenter = NotificationCenter.default
|
||||
private var canDispatch: Bool {
|
||||
mutex.lock()
|
||||
let canWork = readyToWrite
|
||||
@ -161,7 +162,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
}
|
||||
|
||||
/// The shared processing queue used for all WebSocket.
|
||||
private static let sharedWorkQueue = dispatch_queue_create("com.vluxe.starscream.websocket", DISPATCH_QUEUE_SERIAL)
|
||||
private static let sharedWorkQueue = DispatchQueue(label: "com.vluxe.starscream.websocket")
|
||||
|
||||
/// Used for setting protocols.
|
||||
public init(url: NSURL, protocols: [String]? = nil) {
|
||||
@ -189,18 +190,18 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
|
||||
- Parameter forceTimeout: Maximum time to wait for the server to close the socket.
|
||||
*/
|
||||
public func disconnect(forceTimeout forceTimeout: NSTimeInterval? = nil) {
|
||||
public func disconnect(forceTimeout: TimeInterval? = nil) {
|
||||
switch forceTimeout {
|
||||
case .Some(let seconds) where seconds > 0:
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))), callbackQueue) { [weak self] in
|
||||
self?.disconnectStream(nil)
|
||||
case .some(let seconds) where seconds > 0:
|
||||
callbackQueue.asyncAfter(deadline: DispatchTime.now() + Double(Int64(seconds * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { [weak self] in
|
||||
self?.disconnectStream(error: nil)
|
||||
}
|
||||
fallthrough
|
||||
case .None:
|
||||
writeError(CloseCode.Normal.rawValue)
|
||||
case .none:
|
||||
writeError(code: CloseCode.normal.rawValue)
|
||||
|
||||
default:
|
||||
disconnectStream(nil)
|
||||
disconnectStream(error: nil)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -213,9 +214,9 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
- parameter str: The string to write.
|
||||
- parameter completion: The (optional) completion handler.
|
||||
*/
|
||||
public func writeString(str: String, completion: (() -> ())? = nil) {
|
||||
public func writeString(_ str: String, completion: (() -> ())? = nil) {
|
||||
guard isConnected else { return }
|
||||
dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame, writeCompletion: completion)
|
||||
dequeueWrite(data: str.data(using: String.Encoding.utf8)! as NSData, code: .textFrame, writeCompletion: completion)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,54 +227,54 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
- parameter data: The data to write.
|
||||
- parameter completion: The (optional) completion handler.
|
||||
*/
|
||||
public func writeData(data: NSData, completion: (() -> ())? = nil) {
|
||||
public func writeData(_ data: Data, completion: (() -> ())? = nil) {
|
||||
guard isConnected else { return }
|
||||
dequeueWrite(data, code: .BinaryFrame, writeCompletion: completion)
|
||||
dequeueWrite(data: data as NSData, 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, completion: (() -> ())? = nil) {
|
||||
guard isConnected else { return }
|
||||
dequeueWrite(data, code: .Ping, writeCompletion: completion)
|
||||
dequeueWrite(data: data, code: .ping, writeCompletion: completion)
|
||||
}
|
||||
|
||||
/// Private method that starts the connection.
|
||||
private func createHTTPRequest() {
|
||||
|
||||
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET",
|
||||
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET" as CFString,
|
||||
url, kCFHTTPVersion1_1).takeRetainedValue()
|
||||
|
||||
var port = url.port
|
||||
if port == nil {
|
||||
if supportedSSLSchemes.contains(url.scheme) {
|
||||
if supportedSSLSchemes.contains(url.scheme!) {
|
||||
port = 443
|
||||
} else {
|
||||
port = 80
|
||||
}
|
||||
}
|
||||
addHeader(urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue)
|
||||
addHeader(urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue)
|
||||
addHeader(urlRequest, key: headerWSUpgradeName as NSString, val: headerWSUpgradeValue as NSString)
|
||||
addHeader(urlRequest, key: headerWSConnectionName as NSString, val: headerWSConnectionValue as NSString)
|
||||
if let protocols = optionalProtocols {
|
||||
addHeader(urlRequest, key: headerWSProtocolName, val: protocols.joinWithSeparator(","))
|
||||
addHeader(urlRequest, key: headerWSProtocolName as NSString, val: protocols.joined(separator: ",") as NSString)
|
||||
}
|
||||
addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue)
|
||||
addHeader(urlRequest, key: headerWSKeyName, val: generateWebSocketKey())
|
||||
addHeader(urlRequest, key: headerWSVersionName as NSString, val: headerWSVersionValue as NSString)
|
||||
addHeader(urlRequest, key: headerWSKeyName as NSString, val: generateWebSocketKey() as NSString)
|
||||
if let origin = origin {
|
||||
addHeader(urlRequest, key: headerOriginName, val: origin)
|
||||
addHeader(urlRequest, key: headerOriginName as NSString, val: origin as NSString)
|
||||
}
|
||||
addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)")
|
||||
addHeader(urlRequest, key: headerWSHostName as NSString, val: "\(url.host!):\(port!)" as NSString)
|
||||
for (key,value) in headers {
|
||||
addHeader(urlRequest, key: key, val: value)
|
||||
addHeader(urlRequest, key: key as NSString, val: value as NSString)
|
||||
}
|
||||
if let cfHTTPMessage = CFHTTPMessageCopySerializedMessage(urlRequest) {
|
||||
let serializedRequest = cfHTTPMessage.takeRetainedValue()
|
||||
initStreamsWithData(serializedRequest, Int(port!))
|
||||
initStreamsWithData(data: serializedRequest as NSData, Int(port!))
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a header to the CFHTTPMessage by using the NSString bridges to CFString.
|
||||
private func addHeader(urlRequest: CFHTTPMessage, key: NSString, val: NSString) {
|
||||
private func addHeader(_ urlRequest: CFHTTPMessage, key: NSString, val: NSString) {
|
||||
CFHTTPMessageSetHeaderFieldValue(urlRequest, key, val)
|
||||
}
|
||||
|
||||
@ -283,10 +284,10 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
let seed = 16
|
||||
for _ in 0..<seed {
|
||||
let uni = UnicodeScalar(UInt32(97 + arc4random_uniform(25)))
|
||||
key += "\(Character(uni))"
|
||||
key += "\(Character(uni!))"
|
||||
}
|
||||
let data = key.dataUsingEncoding(NSUTF8StringEncoding)
|
||||
let baseKey = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
|
||||
let data = key.data(using: String.Encoding.utf8)
|
||||
let baseKey = data?.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
|
||||
return baseKey!
|
||||
}
|
||||
|
||||
@ -297,41 +298,41 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
|
||||
var readStream: Unmanaged<CFReadStream>?
|
||||
var writeStream: Unmanaged<CFWriteStream>?
|
||||
let h: NSString = url.host!
|
||||
CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream)
|
||||
let h = url.host!
|
||||
CFStreamCreatePairWithSocketToHost(nil, h as NSString, UInt32(port), &readStream, &writeStream)
|
||||
inputStream = readStream!.takeRetainedValue()
|
||||
outputStream = writeStream!.takeRetainedValue()
|
||||
guard let inStream = inputStream, let outStream = outputStream else { return }
|
||||
inStream.delegate = self
|
||||
outStream.delegate = self
|
||||
if supportedSSLSchemes.contains(url.scheme) {
|
||||
inStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
|
||||
outStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
|
||||
if supportedSSLSchemes.contains(url.scheme!) {
|
||||
inStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
outStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
} else {
|
||||
certValidated = true //not a https session, so no need to check SSL pinning
|
||||
}
|
||||
if voipEnabled {
|
||||
inStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
|
||||
outStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
|
||||
inStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType)
|
||||
outStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType)
|
||||
}
|
||||
if selfSignedSSL {
|
||||
let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(bool: false), kCFStreamSSLPeerName: kCFNull]
|
||||
inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String)
|
||||
outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String)
|
||||
let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(value: false), kCFStreamSSLPeerName: kCFNull]
|
||||
inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)
|
||||
outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)
|
||||
}
|
||||
if let cipherSuites = self.enabledSSLCipherSuites {
|
||||
if let sslContextIn = CFReadStreamCopyProperty(inputStream, kCFStreamPropertySSLContext) as! SSLContextRef?,
|
||||
sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContextRef? {
|
||||
if let sslContextIn = CFReadStreamCopyProperty(inputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext?,
|
||||
let sslContextOut = CFWriteStreamCopyProperty(outputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext? {
|
||||
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)
|
||||
disconnectStream(error: error)
|
||||
return
|
||||
}
|
||||
if resOut != errSecSuccess {
|
||||
let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut))
|
||||
disconnectStream(error)
|
||||
disconnectStream(error: error)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -345,15 +346,15 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
self.readyToWrite = true
|
||||
self.mutex.unlock()
|
||||
|
||||
let bytes = UnsafePointer<UInt8>(data.bytes)
|
||||
let bytes = UnsafeRawPointer(data.bytes).assumingMemoryBound(to: UInt8.self)
|
||||
var out = timeout * 1000000 // wait 5 seconds before giving up
|
||||
writeQueue.addOperationWithBlock { [weak self] in
|
||||
writeQueue.addOperation { [weak self] in
|
||||
while !outStream.hasSpaceAvailable {
|
||||
usleep(100) // wait until the socket is ready
|
||||
out -= 100
|
||||
if out < 0 {
|
||||
self?.cleanupStream()
|
||||
self?.doDisconnect(self?.errorWithDetail("write wait timed out", code: 2))
|
||||
self?.doDisconnect(error: self?.errorWithDetail("write wait timed out", code: 2))
|
||||
return
|
||||
} else if outStream.streamError != nil {
|
||||
return // disconnectStream will be called.
|
||||
@ -364,29 +365,26 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
}
|
||||
|
||||
// Delegate for the stream methods. Processes incoming bytes.
|
||||
public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
|
||||
|
||||
if let sec = security where !certValidated && [.HasBytesAvailable, .HasSpaceAvailable].contains(eventCode) {
|
||||
let possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as String)
|
||||
if let trust: AnyObject = possibleTrust {
|
||||
let domain: AnyObject? = aStream.propertyForKey(kCFStreamSSLPeerName as String)
|
||||
if sec.isValid(trust as! SecTrustRef, domain: domain as! String?) {
|
||||
certValidated = true
|
||||
} else {
|
||||
let error = errorWithDetail("Invalid SSL certificate", code: 1)
|
||||
disconnectStream(error)
|
||||
return
|
||||
}
|
||||
public func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
|
||||
if let sec = security , !certValidated && [.hasBytesAvailable, .hasSpaceAvailable].contains(eventCode) {
|
||||
let possibleTrust = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey)
|
||||
let domain = aStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as? String
|
||||
if sec.isValid(possibleTrust as! SecTrust, domain: domain) {
|
||||
certValidated = true
|
||||
} else {
|
||||
let error = errorWithDetail("Invalid SSL certificate", code: 1)
|
||||
disconnectStream(error: error)
|
||||
return
|
||||
}
|
||||
}
|
||||
if eventCode == .HasBytesAvailable {
|
||||
if eventCode == .hasBytesAvailable {
|
||||
if aStream == inputStream {
|
||||
processInputStream()
|
||||
}
|
||||
} else if eventCode == .ErrorOccurred {
|
||||
disconnectStream(aStream.streamError)
|
||||
} else if eventCode == .EndEncountered {
|
||||
disconnectStream(nil)
|
||||
} else if eventCode == .errorOccurred {
|
||||
disconnectStream(error: aStream.streamError as NSError?)
|
||||
} else if eventCode == .endEncountered {
|
||||
disconnectStream(error: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -398,7 +396,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
writeQueue.cancelAllOperations()
|
||||
}
|
||||
cleanupStream()
|
||||
doDisconnect(error)
|
||||
doDisconnect(error: error)
|
||||
}
|
||||
|
||||
private func cleanupStream() {
|
||||
@ -419,7 +417,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
/// Handles the incoming bytes and sending them to the proper processing method.
|
||||
private func processInputStream() {
|
||||
let buf = NSMutableData(capacity: BUFFER_MAX)
|
||||
let buffer = UnsafeMutablePointer<UInt8>(buf!.bytes)
|
||||
let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self)
|
||||
let length = inputStream!.read(buffer, maxLength: BUFFER_MAX)
|
||||
|
||||
guard length > 0 else { return }
|
||||
@ -427,7 +425,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
if inputQueue.count == 0 {
|
||||
process = true
|
||||
}
|
||||
inputQueue.append(NSData(bytes: buffer, length: length))
|
||||
inputQueue.append(Data(bytes: UnsafeRawPointer(buffer).assumingMemoryBound(to: UInt8.self), count: length))
|
||||
if process {
|
||||
dequeueInput()
|
||||
}
|
||||
@ -440,14 +438,14 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
var work = data
|
||||
if let fragBuffer = fragBuffer {
|
||||
let combine = NSMutableData(data: fragBuffer)
|
||||
combine.appendData(data)
|
||||
work = combine
|
||||
combine.append(data)
|
||||
work = combine as Data
|
||||
self.fragBuffer = nil
|
||||
}
|
||||
let buffer = UnsafePointer<UInt8>(work.bytes)
|
||||
let length = work.length
|
||||
let buffer = UnsafeRawPointer((work as NSData).bytes).assumingMemoryBound(to: UInt8.self)
|
||||
let length = work.count
|
||||
if !connected {
|
||||
processTCPHandshake(buffer, bufferLen: length)
|
||||
processTCPHandshake(buffer: buffer, bufferLen: length)
|
||||
} else {
|
||||
processRawMessagesInBuffer(buffer, bufferLen: length)
|
||||
}
|
||||
@ -457,22 +455,22 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
|
||||
// Handle checking the initial connection status.
|
||||
private func processTCPHandshake(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
|
||||
let code = processHTTP(buffer, bufferLen: bufferLen)
|
||||
let code = processHTTP(buffer: buffer, bufferLen: bufferLen)
|
||||
switch code {
|
||||
case 0:
|
||||
connected = true
|
||||
guard canDispatch else {return}
|
||||
dispatch_async(callbackQueue) { [weak self] in
|
||||
callbackQueue.async { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.onConnect?()
|
||||
s.delegate?.websocketDidConnect(s)
|
||||
s.notificationCenter.postNotificationName(WebsocketDidConnectNotification, object: self)
|
||||
s.notificationCenter.post(name: NSNotification.Name(WebsocketDidConnectNotification), object: self)
|
||||
}
|
||||
case -1:
|
||||
fragBuffer = NSData(bytes: buffer, length: bufferLen)
|
||||
fragBuffer = NSData(bytes: buffer, length: bufferLen) as Data
|
||||
break // do nothing, we are going to collect more data
|
||||
default:
|
||||
doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: UInt16(code)))
|
||||
doDisconnect(error: errorWithDetail("Invalid HTTP upgrade", code: UInt16(code)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -493,7 +491,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
}
|
||||
}
|
||||
if totalSize > 0 {
|
||||
let code = validateResponse(buffer, bufferLen: totalSize)
|
||||
let code = validateResponse(buffer: buffer, bufferLen: totalSize)
|
||||
if code != 0 {
|
||||
return code
|
||||
}
|
||||
@ -517,7 +515,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
}
|
||||
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
|
||||
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 {
|
||||
return 0
|
||||
}
|
||||
@ -554,16 +552,16 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@warn_unused_result
|
||||
|
||||
private func processOneRawMessage(inBuffer buffer: UnsafeBufferPointer<UInt8>) -> UnsafeBufferPointer<UInt8> {
|
||||
let response = readStack.last
|
||||
let baseAddress = buffer.baseAddress
|
||||
guard let baseAddress = buffer.baseAddress else { fatalError("") }
|
||||
let bufferLen = buffer.count
|
||||
if response != nil && bufferLen < 2 {
|
||||
fragBuffer = NSData(buffer: buffer)
|
||||
fragBuffer = Data(buffer: buffer)
|
||||
return emptyBuffer
|
||||
}
|
||||
if let response = response where response.bytesLeft > 0 {
|
||||
if let response = response, response.bytesLeft > 0 {
|
||||
var len = response.bytesLeft
|
||||
var extra = bufferLen - response.bytesLeft
|
||||
if response.bytesLeft > bufferLen {
|
||||
@ -571,8 +569,8 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
extra = 0
|
||||
}
|
||||
response.bytesLeft -= len
|
||||
response.buffer?.appendData(NSData(bytes: baseAddress, length: len))
|
||||
processResponse(response)
|
||||
response.buffer?.append(Data(bytes: baseAddress, count: len))
|
||||
_ = processResponse(response)
|
||||
return buffer.fromOffset(bufferLen - extra)
|
||||
} else {
|
||||
let isFin = (FinMask & baseAddress[0])
|
||||
@ -580,34 +578,34 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
let isMasked = (MaskMask & baseAddress[1])
|
||||
let payloadLen = (PayloadLenMask & baseAddress[1])
|
||||
var offset = 2
|
||||
if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .Pong {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
doDisconnect(errorWithDetail("masked and rsv data is not currently supported", code: errCode))
|
||||
writeError(errCode)
|
||||
if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .pong {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(error: errorWithDetail("masked and rsv data is not currently supported", code: errCode))
|
||||
writeError(code: errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
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)
|
||||
let isControlFrame = (receivedOpcode == .connectionClose || receivedOpcode == .ping)
|
||||
if !isControlFrame && (receivedOpcode != .binaryFrame && receivedOpcode != .continueFrame &&
|
||||
receivedOpcode != .textFrame && receivedOpcode != .pong) {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(error: errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode))
|
||||
writeError(code: errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
if isControlFrame && isFin == 0 {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
doDisconnect(errorWithDetail("control frames can't be fragmented", code: errCode))
|
||||
writeError(errCode)
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(error: errorWithDetail("control frames can't be fragmented", code: errCode))
|
||||
writeError(code: errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
if receivedOpcode == .ConnectionClose {
|
||||
var code = CloseCode.Normal.rawValue
|
||||
if receivedOpcode == .connectionClose {
|
||||
var code = CloseCode.normal.rawValue
|
||||
if payloadLen == 1 {
|
||||
code = CloseCode.ProtocolError.rawValue
|
||||
code = CloseCode.protocolError.rawValue
|
||||
} else if payloadLen > 1 {
|
||||
code = WebSocket.readUint16(baseAddress, offset: offset)
|
||||
code = WebSocket.readUint16(buffer: baseAddress, offset: offset)
|
||||
if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) {
|
||||
code = CloseCode.ProtocolError.rawValue
|
||||
code = CloseCode.protocolError.rawValue
|
||||
}
|
||||
offset += 2
|
||||
}
|
||||
@ -616,47 +614,47 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
let len = Int(payloadLen - 2)
|
||||
if len > 0 {
|
||||
let bytes = baseAddress + offset
|
||||
if let customCloseReason = String(data: NSData(bytes: bytes, length: len), encoding: NSUTF8StringEncoding) {
|
||||
if let customCloseReason = String(data: NSData(bytes: bytes, length: len) as Data, encoding: String.Encoding.utf8) {
|
||||
closeReason = customCloseReason
|
||||
} else {
|
||||
code = CloseCode.ProtocolError.rawValue
|
||||
code = CloseCode.protocolError.rawValue
|
||||
}
|
||||
}
|
||||
}
|
||||
doDisconnect(errorWithDetail(closeReason, code: code))
|
||||
writeError(code)
|
||||
doDisconnect(error: errorWithDetail(closeReason, code: code))
|
||||
writeError(code: code)
|
||||
return emptyBuffer
|
||||
}
|
||||
if isControlFrame && payloadLen > 125 {
|
||||
writeError(CloseCode.ProtocolError.rawValue)
|
||||
writeError(code: CloseCode.protocolError.rawValue)
|
||||
return emptyBuffer
|
||||
}
|
||||
var dataLength = UInt64(payloadLen)
|
||||
if dataLength == 127 {
|
||||
dataLength = WebSocket.readUint64(baseAddress, offset: offset)
|
||||
offset += sizeof(UInt64)
|
||||
dataLength = WebSocket.readUint64(buffer: baseAddress, offset: offset)
|
||||
offset += MemoryLayout<UInt64>.size
|
||||
} else if dataLength == 126 {
|
||||
dataLength = UInt64(WebSocket.readUint16(baseAddress, offset: offset))
|
||||
offset += sizeof(UInt16)
|
||||
dataLength = UInt64(WebSocket.readUint16(buffer: baseAddress, offset: offset))
|
||||
offset += MemoryLayout<UInt16>.size
|
||||
}
|
||||
if bufferLen < offset || UInt64(bufferLen - offset) < dataLength {
|
||||
fragBuffer = NSData(bytes: baseAddress, length: bufferLen)
|
||||
fragBuffer = Data(bytes: baseAddress, count: bufferLen)
|
||||
return emptyBuffer
|
||||
}
|
||||
var len = dataLength
|
||||
if dataLength > UInt64(bufferLen) {
|
||||
len = UInt64(bufferLen-offset)
|
||||
}
|
||||
let data: NSData
|
||||
let data: Data
|
||||
if len < 0 {
|
||||
len = 0
|
||||
data = NSData()
|
||||
data = Data()
|
||||
} else {
|
||||
data = NSData(bytes: baseAddress+offset, length: Int(len))
|
||||
data = Data(bytes: baseAddress+offset, count: Int(len))
|
||||
}
|
||||
if receivedOpcode == .Pong {
|
||||
if receivedOpcode == .pong {
|
||||
if canDispatch {
|
||||
dispatch_async(callbackQueue) { [weak self] in
|
||||
callbackQueue.async { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.onPong?()
|
||||
s.pongDelegate?.websocketDidReceivePong(s)
|
||||
@ -668,19 +666,19 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
if isControlFrame {
|
||||
response = nil // Don't append pings.
|
||||
}
|
||||
if isFin == 0 && receivedOpcode == .ContinueFrame && response == nil {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
doDisconnect(errorWithDetail("continue frame before a binary or text frame", code: errCode))
|
||||
writeError(errCode)
|
||||
if isFin == 0 && receivedOpcode == .continueFrame && response == nil {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(error: errorWithDetail("continue frame before a binary or text frame", code: errCode))
|
||||
writeError(code: errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
var isNew = false
|
||||
if response == nil {
|
||||
if receivedOpcode == .ContinueFrame {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
doDisconnect(errorWithDetail("first frame can't be a continue frame",
|
||||
code: errCode))
|
||||
writeError(errCode)
|
||||
if receivedOpcode == .continueFrame {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(error: errorWithDetail("first frame can't be a continue frame",
|
||||
code: errCode))
|
||||
writeError(code: errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
isNew = true
|
||||
@ -689,16 +687,16 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
response!.bytesLeft = Int(dataLength)
|
||||
response!.buffer = NSMutableData(data: data)
|
||||
} else {
|
||||
if receivedOpcode == .ContinueFrame {
|
||||
if receivedOpcode == .continueFrame {
|
||||
response!.bytesLeft = Int(dataLength)
|
||||
} else {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
doDisconnect(errorWithDetail("second and beyond of fragment message must be a continue frame",
|
||||
code: errCode))
|
||||
writeError(errCode)
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(error: errorWithDetail("second and beyond of fragment message must be a continue frame",
|
||||
code: errCode))
|
||||
writeError(code: errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
response!.buffer!.appendData(data)
|
||||
response!.buffer!.append(data)
|
||||
}
|
||||
if let response = response {
|
||||
response.bytesLeft -= Int(len)
|
||||
@ -707,7 +705,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
if isNew {
|
||||
readStack.append(response)
|
||||
}
|
||||
processResponse(response)
|
||||
_ = processResponse(response)
|
||||
}
|
||||
|
||||
let step = Int(offset + numericCast(len))
|
||||
@ -716,42 +714,42 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
}
|
||||
|
||||
/// Process all messages in the buffer if possible.
|
||||
private func processRawMessagesInBuffer(pointer: UnsafePointer<UInt8>, bufferLen: Int) {
|
||||
private func processRawMessagesInBuffer(_ pointer: UnsafePointer<UInt8>, bufferLen: Int) {
|
||||
var buffer = UnsafeBufferPointer(start: pointer, count: bufferLen)
|
||||
repeat {
|
||||
buffer = processOneRawMessage(inBuffer: buffer)
|
||||
} while buffer.count >= 2
|
||||
if buffer.count > 0 {
|
||||
fragBuffer = NSData(buffer: buffer)
|
||||
fragBuffer = Data(buffer: buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Process the finished response of a buffer.
|
||||
private func processResponse(response: WSResponse) -> Bool {
|
||||
private func processResponse(_ response: WSResponse) -> Bool {
|
||||
if response.isFin && response.bytesLeft <= 0 {
|
||||
if response.code == .Ping {
|
||||
if response.code == .ping {
|
||||
let data = response.buffer! // local copy so it's not perverse for writing
|
||||
dequeueWrite(data, code: OpCode.Pong)
|
||||
} else if response.code == .TextFrame {
|
||||
let str: NSString? = NSString(data: response.buffer!, encoding: NSUTF8StringEncoding)
|
||||
dequeueWrite(data: data, code: OpCode.pong)
|
||||
} else if response.code == .textFrame {
|
||||
let str: NSString? = NSString(data: response.buffer! as Data, encoding: String.Encoding.utf8.rawValue)
|
||||
if str == nil {
|
||||
writeError(CloseCode.Encoding.rawValue)
|
||||
writeError(code: CloseCode.encoding.rawValue)
|
||||
return false
|
||||
}
|
||||
if canDispatch {
|
||||
dispatch_async(callbackQueue) { [weak self] in
|
||||
callbackQueue.async { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.onText?(str! as String)
|
||||
s.delegate?.websocketDidReceiveMessage(s, text: str! as String)
|
||||
}
|
||||
}
|
||||
} else if response.code == .BinaryFrame {
|
||||
} else if response.code == .binaryFrame {
|
||||
if canDispatch {
|
||||
let data = response.buffer! //local copy so it's not perverse for writing
|
||||
dispatch_async(callbackQueue) { [weak self] in
|
||||
callbackQueue.async { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.onData?(data)
|
||||
s.delegate?.websocketDidReceiveData(s, data: data)
|
||||
s.onData?(data as Data)
|
||||
s.delegate?.websocketDidReceiveData(s, data: data as Data)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -762,7 +760,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
}
|
||||
|
||||
/// Create an error.
|
||||
private func errorWithDetail(detail: String, code: UInt16) -> NSError {
|
||||
private func errorWithDetail(_ detail: String, code: UInt16) -> NSError {
|
||||
var details = [String: String]()
|
||||
details[NSLocalizedDescriptionKey] = detail
|
||||
return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details)
|
||||
@ -770,64 +768,64 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
|
||||
/// Write a an error to the socket.
|
||||
private func writeError(code: UInt16) {
|
||||
let buf = NSMutableData(capacity: sizeof(UInt16))
|
||||
let buffer = UnsafeMutablePointer<UInt8>(buf!.bytes)
|
||||
WebSocket.writeUint16(buffer, offset: 0, value: code)
|
||||
dequeueWrite(NSData(bytes: buffer, length: sizeof(UInt16)), code: .ConnectionClose)
|
||||
let buf = NSMutableData(capacity: MemoryLayout<UInt16>.size)
|
||||
let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self)
|
||||
WebSocket.writeUint16(buffer: buffer, offset: 0, value: code)
|
||||
dequeueWrite(data: Data(bytes: buffer, count: MemoryLayout<UInt16>.size) as NSData, code: .connectionClose)
|
||||
}
|
||||
|
||||
/// Used to write things to the stream.
|
||||
private func dequeueWrite(data: NSData, code: OpCode, writeCompletion: (() -> ())? = nil) {
|
||||
writeQueue.addOperationWithBlock { [weak self] in
|
||||
writeQueue.addOperation { [weak self] in
|
||||
//stream isn't ready, let's wait
|
||||
guard let s = self else { return }
|
||||
var offset = 2
|
||||
let bytes = UnsafeMutablePointer<UInt8>(data.bytes)
|
||||
let bytes = UnsafeMutableRawPointer(mutating: (data as NSData).bytes).assumingMemoryBound(to: UInt8.self)
|
||||
let dataLength = data.length
|
||||
let frame = NSMutableData(capacity: dataLength + s.MaxFrameSize)
|
||||
let buffer = UnsafeMutablePointer<UInt8>(frame!.mutableBytes)
|
||||
let buffer = UnsafeMutableRawPointer(frame!.mutableBytes).assumingMemoryBound(to: UInt8.self)
|
||||
buffer[0] = s.FinMask | code.rawValue
|
||||
if dataLength < 126 {
|
||||
buffer[1] = CUnsignedChar(dataLength)
|
||||
} else if dataLength <= Int(UInt16.max) {
|
||||
buffer[1] = 126
|
||||
WebSocket.writeUint16(buffer, offset: offset, value: UInt16(dataLength))
|
||||
offset += sizeof(UInt16)
|
||||
WebSocket.writeUint16(buffer: buffer, offset: offset, value: UInt16(dataLength))
|
||||
offset += MemoryLayout<UInt16>.size
|
||||
} else {
|
||||
buffer[1] = 127
|
||||
WebSocket.writeUint64(buffer, offset: offset, value: UInt64(dataLength))
|
||||
offset += sizeof(UInt64)
|
||||
WebSocket.writeUint64(buffer: buffer, offset: offset, value: UInt64(dataLength))
|
||||
offset += MemoryLayout<UInt64>.size
|
||||
}
|
||||
buffer[1] |= s.MaskMask
|
||||
let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
|
||||
SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey)
|
||||
offset += sizeof(UInt32)
|
||||
_ = SecRandomCopyBytes(kSecRandomDefault, Int(MemoryLayout<UInt32>.size), maskKey)
|
||||
offset += MemoryLayout<UInt32>.size
|
||||
|
||||
for i in 0..<dataLength {
|
||||
buffer[offset] = bytes[i] ^ maskKey[i % sizeof(UInt32)]
|
||||
buffer[offset] = bytes[i] ^ maskKey[i % MemoryLayout<UInt32>.size]
|
||||
offset += 1
|
||||
}
|
||||
var total = 0
|
||||
while true {
|
||||
guard let outStream = s.outputStream else { break }
|
||||
let writeBuffer = UnsafePointer<UInt8>(frame!.bytes+total)
|
||||
let writeBuffer = UnsafeRawPointer(frame!.bytes+total).assumingMemoryBound(to: UInt8.self)
|
||||
let len = outStream.write(writeBuffer, maxLength: offset-total)
|
||||
if len < 0 {
|
||||
var error: NSError?
|
||||
if let streamError = outStream.streamError {
|
||||
error = streamError
|
||||
error = streamError as NSError
|
||||
} else {
|
||||
let errCode = InternalErrorCode.OutputStreamWriteError.rawValue
|
||||
let errCode = InternalErrorCode.outputStreamWriteError.rawValue
|
||||
error = s.errorWithDetail("output stream error during write", code: errCode)
|
||||
}
|
||||
s.doDisconnect(error)
|
||||
s.doDisconnect(error: error)
|
||||
break
|
||||
} else {
|
||||
total += len
|
||||
}
|
||||
if total >= offset {
|
||||
if let callbackQueue = self?.callbackQueue, callback = writeCompletion {
|
||||
dispatch_async(callbackQueue) {
|
||||
if let callbackQueue = self?.callbackQueue, let callback = writeCompletion {
|
||||
callbackQueue.async {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
@ -845,12 +843,12 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
didDisconnect = true
|
||||
connected = false
|
||||
guard canDispatch else {return}
|
||||
dispatch_async(callbackQueue) { [weak self] in
|
||||
callbackQueue.async { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.onDisconnect?(error)
|
||||
s.delegate?.websocketDidDisconnect(s, error: error)
|
||||
let userInfo = error.map{ [WebsocketDisconnectionErrorKeyName: $0] }
|
||||
s.notificationCenter.postNotificationName(WebsocketDidDisconnectNotification, object: self, userInfo: userInfo)
|
||||
s.notificationCenter.post(name: NSNotification.Name(rawValue: WebsocketDidDisconnectNotification), object: self, userInfo: userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@ -865,18 +863,18 @@ public class WebSocket: NSObject, NSStreamDelegate {
|
||||
|
||||
}
|
||||
|
||||
private extension NSData {
|
||||
private extension Data {
|
||||
|
||||
convenience init(buffer: UnsafeBufferPointer<UInt8>) {
|
||||
self.init(bytes: buffer.baseAddress, length: buffer.count)
|
||||
init(buffer: UnsafeBufferPointer<UInt8>) {
|
||||
self.init(bytes: buffer.baseAddress!, count: buffer.count)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension UnsafeBufferPointer {
|
||||
|
||||
func fromOffset(offset: Int) -> UnsafeBufferPointer<Element> {
|
||||
return UnsafeBufferPointer<Element>(start: baseAddress.advancedBy(offset), count: count - offset)
|
||||
func fromOffset(_ offset: Int) -> UnsafeBufferPointer<Element> {
|
||||
return UnsafeBufferPointer<Element>(start: baseAddress!.advanced(by: offset), count: count - offset)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user