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