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:
Erik 2016-09-14 07:16:37 -04:00
commit f051962e81
No known key found for this signature in database
GPG Key ID: 4930B7C5FBC1A69D
40 changed files with 1143 additions and 1051 deletions

View File

@ -1,9 +1,13 @@
language: objective-c
xcode_project: Socket.IO-Client-Swift.xcodeproj # path to your xcodeproj folder
xcode_scheme: SocketIO-iOS
osx_image: xcode7.3
osx_image: xcode8
branches:
only:
- master
- development
script: xctool -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO-Mac build test -parallelize
before_install:
- brew update
- brew outdated xctool || brew upgrade xctool
# script: xctool -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO-Mac build test -parallelize
script: xcodebuild -project Socket.IO-Client-Swift.xcodeproj -scheme SocketIO-Mac build test

View File

@ -1,5 +1,5 @@
import PackageDescription
let package = Package(
name: "SocketIOClientSwift"
name: "SocketIO"
)

View File

@ -6,8 +6,7 @@ Socket.IO-client for iOS/OS X.
##Example
```swift
import SocketIO
let socket = SocketIOClient(socketURL: NSURL(string: "http://localhost:8080")!, config: [.Log(true), .ForcePolling(true)])
let socket = SocketIOClient(socketURL: NSURL(string: "http://localhost:8080")!, config: [.log(true), .forcePolling(true)])
socket.on("connect") {data, ack in
print("socket connected")
@ -58,13 +57,15 @@ SocketIOClient* socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{
- Can be used from Objective-C
##Installation
Requires Swift 2.2/Xcode 7.3
Requires Swift 3/Xcode 8.x
If you need Swift 2.1/Xcode 7.2 use v5.5.0 (Pre-Swift 2.2 support is no longer maintained)
If you need swift 2.2 use 7.x (Pre-Swift 3 support is no longer maintained)
If you need Swift 1.2/Xcode 6.3/4 use v2.4.5 (Pre-Swift 2 support is no longer maintained)
If you need Swift 2.1 use v5.5.0 (Pre-Swift 2.2 support is no longer maintained)
If you need Swift 1.1/Xcode 6.2 use v1.5.2. (Pre-Swift 1.2 support is no longer maintained)
If you need Swift 1.2 use v2.4.5 (Pre-Swift 2 support is no longer maintained)
If you need Swift 1.1 use v1.5.2. (Pre-Swift 1.2 support is no longer maintained)
Manually (iOS 7+)
-----------------
@ -151,43 +152,43 @@ Options
All options are a case of SocketIOClientOption. To get the Objective-C Option, convert the name to lowerCamelCase.
```swift
case ConnectParams([String: AnyObject]) // Dictionary whose contents will be passed with the connection.
case Cookies([NSHTTPCookie]) // An array of NSHTTPCookies. Passed during the handshake. Default is nil.
case DoubleEncodeUTF8(Bool) // Whether or not to double encode utf8. If using the node based server this should be true. Default is true.
case ExtraHeaders([String: String]) // Adds custom headers to the initial request. Default is nil.
case ForcePolling(Bool) // `true` forces the client to use xhr-polling. Default is `false`
case ForceNew(Bool) // Will a create a new engine for each connect. Useful if you find a bug in the engine related to reconnects
case ForceWebsockets(Bool) // `true` forces the client to use WebSockets. Default is `false`
case HandleQueue(dispatch_queue_t) // The dispatch queue that handlers are run on. Default is the main queue.
case Log(Bool) // If `true` socket will log debug messages. Default is false.
case Logger(SocketLogger) // Custom logger that conforms to SocketLogger. Will use the default logging otherwise.
case Nsp(String) // The namespace to connect to. Must begin with /. Default is `/`
case Path(String) // If the server uses a custom path. ex: `"/swift/"`. Default is `""`
case Reconnects(Bool) // Whether to reconnect on server lose. Default is `true`
case ReconnectAttempts(Int) // How many times to reconnect. Default is `-1` (infinite tries)
case ReconnectWait(Int) // Amount of time to wait between reconnects. Default is `10`
case SessionDelegate(NSURLSessionDelegate) // Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs. Default is nil.
case Secure(Bool) // If the connection should use TLS. Default is false.
case Security(SSLSecurity) // Allows you to set which certs are valid. Useful for SSL pinning.
case SelfSigned(Bool) // Sets WebSocket.selfSignedSSL. Use this if you're using self-signed certs.
case VoipEnabled(Bool) // Only use this option if you're using the client with VoIP services. Changes the way the WebSocket is created. Default is false
case connectParams([String: AnyObject]) // Dictionary whose contents will be passed with the connection.
case cookies([NSHTTPCookie]) // An array of NSHTTPCookies. Passed during the handshake. Default is nil.
case doubleEncodeUTF8(Bool) // Whether or not to double encode utf8. If using the node based server this should be true. Default is true.
case extraHeaders([String: String]) // Adds custom headers to the initial request. Default is nil.
case forcePolling(Bool) // `true` forces the client to use xhr-polling. Default is `false`
case forceNew(Bool) // Will a create a new engine for each connect. Useful if you find a bug in the engine related to reconnects
case forceWebsockets(Bool) // `true` forces the client to use WebSockets. Default is `false`
case handleQueue(dispatch_queue_t) // The dispatch queue that handlers are run on. Default is the main queue.
case log(Bool) // If `true` socket will log debug messages. Default is false.
case logger(SocketLogger) // Custom logger that conforms to SocketLogger. Will use the default logging otherwise.
case nsp(String) // The namespace to connect to. Must begin with /. Default is `/`
case path(String) // If the server uses a custom path. ex: `"/swift/"`. Default is `""`
case reconnects(Bool) // Whether to reconnect on server lose. Default is `true`
case reconnectAttempts(Int) // How many times to reconnect. Default is `-1` (infinite tries)
case reconnectWait(Int) // Amount of time to wait between reconnects. Default is `10`
case sessionDelegate(NSURLSessionDelegate) // Sets an NSURLSessionDelegate for the underlying engine. Useful if you need to handle self-signed certs. Default is nil.
case secure(Bool) // If the connection should use TLS. Default is false.
case security(SSLSecurity) // Allows you to set which certs are valid. Useful for SSL pinning.
case selfSigned(Bool) // Sets WebSocket.selfSignedSSL. Use this if you're using self-signed certs.
case voipEnabled(Bool) // Only use this option if you're using the client with VoIP services. Changes the way the WebSocket is created. Default is false
```
Methods
-------
1. `on(event: String, callback: NormalCallback) -> NSUUID` - Adds a handler for an event. Items are passed by an array. `ack` can be used to send an ack when one is requested. See example. Returns a unique id for the handler.
2. `once(event: String, callback: NormalCallback) -> NSUUID` - Adds a handler that will only be executed once. Returns a unique id for the handler.
1. `on(_ event: String, callback: NormalCallback) -> NSUUID` - Adds a handler for an event. Items are passed by an array. `ack` can be used to send an ack when one is requested. See example. Returns a unique id for the handler.
2. `once(_ event: String, callback: NormalCallback) -> NSUUID` - Adds a handler that will only be executed once. Returns a unique id for the handler.
3. `onAny(callback:((event: String, items: AnyObject?)) -> Void)` - Adds a handler for all events. It will be called on any received event.
4. `emit(event: String, _ items: AnyObject...)` - Sends a message. Can send multiple items.
5. `emit(event: String, withItems items: [AnyObject])` - `emit` for Objective-C
6. `emitWithAck(event: String, _ items: AnyObject...) -> (timeoutAfter: UInt64, callback: (NSArray?) -> Void) -> Void` - Sends a message that requests an acknowledgement from the server. Returns a function which you can use to add a handler. See example. Note: The message is not sent until you call the returned function.
7. `emitWithAck(event: String, withItems items: [AnyObject]) -> (UInt64, (NSArray?) -> Void) -> Void` - `emitWithAck` for Objective-C. Note: The message is not sent until you call the returned function.
4. `emit(_ event: String, _ items: AnyObject...)` - Sends a message. Can send multiple items.
5. `emit(_ event: String, withItems items: [AnyObject])` - `emit` for Objective-C
6. `emitWithAck(_ event: String, _ items: AnyObject...) -> (timeoutAfter: UInt64, callback: (NSArray?) -> Void) -> Void` - Sends a message that requests an acknowledgement from the server. Returns a function which you can use to add a handler. See example. Note: The message is not sent until you call the returned function.
7. `emitWithAck(_ event: String, withItems items: [AnyObject]) -> (UInt64, (NSArray?) -> Void) -> Void` - `emitWithAck` for Objective-C. Note: The message is not sent until you call the returned function.
8. `connect()` - Establishes a connection to the server. A "connect" event is fired upon successful connection.
9. `connect(timeoutAfter timeoutAfter: Int, withTimeoutHandler handler: (() -> Void)?)` - Connect to the server. If it isn't connected after timeoutAfter seconds, the handler is called.
10. `disconnect()` - Closes the socket. Reopening a disconnected socket is not fully tested.
11. `reconnect()` - Causes the client to reconnect to the server.
12. `joinNamespace(namespace: String)` - Causes the client to join namespace. Shouldn't need to be called unless you change namespaces manually.
12. `joinNamespace(_ namespace: String)` - Causes the client to join namespace. Shouldn't need to be called unless you change namespaces manually.
13. `leaveNamespace()` - Causes the client to leave the nsp and go back to /
14. `off(event: String)` - Removes all event handlers for event.
14. `off(_ event: String)` - Removes all event handlers for event.
15. `off(id id: NSUUID)` - Removes the event that corresponds to id.
16. `removeAllHandlers()` - Removes all handlers.

View File

@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.license = { :type => 'MIT' }
s.author = { "Erik" => "nuclear.ace@gmail.com" }
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.10'
s.osx.deployment_target = '10.9'
s.tvos.deployment_target = '9.0'
s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v7.0.3' }
s.source_files = "Source/**/*.swift"

View File

@ -531,8 +531,9 @@
572EF20E1B51F12F00EEBB58 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftMigration = 0730;
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0720;
LastUpgradeCheck = 0800;
TargetAttributes = {
572EF2181B51F16C00EEBB58 = {
CreatedOnToolsVersion = 6.4;
@ -542,9 +543,11 @@
};
572EF2371B51F18A00EEBB58 = {
CreatedOnToolsVersion = 6.4;
LastSwiftMigration = 0800;
};
572EF2411B51F18A00EEBB58 = {
CreatedOnToolsVersion = 6.4;
LastSwiftMigration = 0800;
};
};
};
@ -771,15 +774,31 @@
isa = XCBuildConfiguration;
buildSettings = {
BITCODE_GENERATION_MODE = bitcode;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application";
ENABLE_BITCODE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.10;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = SocketIO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
TVOS_DEPLOYMENT_TARGET = 9.0;
WATCHOS_DEPLOYMENT_TARGET = 2.0;
};
@ -789,12 +808,28 @@
isa = XCBuildConfiguration;
buildSettings = {
BITCODE_GENERATION_MODE = bitcode;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application";
ENABLE_BITCODE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_NAME = SocketIO;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
TVOS_DEPLOYMENT_TARGET = 9.0;
WATCHOS_DEPLOYMENT_TARGET = 2.0;
};
@ -851,6 +886,7 @@
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@ -901,6 +937,7 @@
PRODUCT_BUNDLE_IDENTIFIER = io.socket.SocketIOClientSwift;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
@ -1057,6 +1094,7 @@
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
@ -1108,6 +1146,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "io.socket.$(PRODUCT_NAME:rfc1034identifier)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
@ -1164,6 +1203,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
@ -1210,6 +1250,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "io.socket.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SWIFT_VERSION = 3.0;
};
name = Release;
};

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -13,14 +13,14 @@ class SocketAckManagerTest: XCTestCase {
var ackManager = SocketAckManager()
func testAddAcks() {
let callbackExpection = self.expectationWithDescription("callbackExpection")
let callbackExpection = self.expectation(description: "callbackExpection")
let itemsArray = ["Hi", "ho"]
func callback(items: [AnyObject]) {
func callback(_ items: [Any]) {
callbackExpection.fulfill()
}
ackManager.addAck(1, callback: callback)
ackManager.executeAck(1, items: itemsArray, onQueue: dispatch_get_main_queue())
waitForExpectationsWithTimeout(3.0, handler: nil)
ackManager.executeAck(1, with: itemsArray, onQueue: DispatchQueue.main)
waitForExpectations(timeout: 3.0, handler: nil)
}
}

View File

@ -10,8 +10,8 @@ import XCTest
@testable import SocketIO
class SocketBasicPacketTest: XCTestCase {
let data = "test".dataUsingEncoding(NSUTF8StringEncoding)!
let data2 = "test2".dataUsingEncoding(NSUTF8StringEncoding)!
let data = "test".data(using: String.Encoding.utf8)!
let data2 = "test2".data(using: String.Encoding.utf8)!
func testEmpyEmit() {
let expectedSendString = "2[\"test\"]"
@ -23,7 +23,7 @@ class SocketBasicPacketTest: XCTestCase {
func testNullEmit() {
let expectedSendString = "2[\"test\",null]"
let sendData = ["test", NSNull()]
let sendData: [Any] = ["test", NSNull()]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
@ -46,8 +46,8 @@ class SocketBasicPacketTest: XCTestCase {
}
func testJSONEmit() {
let expectedSendString = "2[\"test\",{\"test\":\"hello\",\"hello\":1,\"foobar\":true,\"null\":null}]"
let sendData = ["test", ["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]]
let expectedSendString = "2[\"test\",{\"null\":null,\"hello\":1,\"test\":\"hello\",\"foobar\":true}]"
let sendData: [Any] = ["test", ["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
@ -55,15 +55,15 @@ class SocketBasicPacketTest: XCTestCase {
func testArrayEmit() {
let expectedSendString = "2[\"test\",[\"hello\",1,{\"test\":\"test\"}]]"
let sendData = ["test", ["hello", 1, ["test": "test"]]]
let sendData: [Any] = ["test", ["hello", 1, ["test": "test"]]]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
}
func testBinaryEmit() {
let expectedSendString = "51-[\"test\",{\"num\":0,\"_placeholder\":true}]"
let sendData = ["test", data]
let expectedSendString = "51-[\"test\",{\"_placeholder\":true,\"num\":0}]"
let sendData: [Any] = ["test", data]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
@ -71,12 +71,12 @@ class SocketBasicPacketTest: XCTestCase {
}
func testMultipleBinaryEmit() {
let expectedSendString = "52-[\"test\",{\"data1\":{\"num\":0,\"_placeholder\":true},\"data2\":{\"num\":1,\"_placeholder\":true}}]"
let sendData = ["test", ["data1": data, "data2": data2]]
let expectedSendString = "52-[\"test\",{\"data2\":{\"_placeholder\":true,\"num\":0},\"data1\":{\"_placeholder\":true,\"num\":1}}]"
let sendData: [Any] = ["test", ["data1": data, "data2": data2] as NSDictionary]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
XCTAssertEqual(packet.binary, [data, data2])
XCTAssertEqual(packet.binary, [data2, data])
}
func testEmitWithAck() {
@ -84,12 +84,14 @@ class SocketBasicPacketTest: XCTestCase {
let sendData = ["test"]
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
XCTAssertEqual(packet.packetString,
expectedSendString)
}
func testEmitDataWithAck() {
let expectedSendString = "51-0[\"test\",{\"num\":0,\"_placeholder\":true}]"
let sendData = ["test", data]
let expectedSendString = "51-0[\"test\",{\"_placeholder\":true,\"num\":0}]"
let sendData: [Any] = ["test", data]
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
@ -121,7 +123,7 @@ class SocketBasicPacketTest: XCTestCase {
}
func testJSONAck() {
let expectedSendString = "30[{\"test\":\"hello\",\"hello\":1,\"foobar\":true,\"null\":null}]"
let expectedSendString = "30[{\"null\":null,\"hello\":1,\"test\":\"hello\",\"foobar\":true}]"
let sendData = [["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]]
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: true)
@ -129,7 +131,7 @@ class SocketBasicPacketTest: XCTestCase {
}
func testBinaryAck() {
let expectedSendString = "61-0[{\"num\":0,\"_placeholder\":true}]"
let expectedSendString = "61-0[{\"_placeholder\":true,\"num\":0}]"
let sendData = [data]
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: true)
@ -138,7 +140,7 @@ class SocketBasicPacketTest: XCTestCase {
}
func testMultipleBinaryAck() {
let expectedSendString = "62-0[{\"data2\":{\"num\":0,\"_placeholder\":true},\"data1\":{\"num\":1,\"_placeholder\":true}}]"
let expectedSendString = "62-0[{\"data2\":{\"_placeholder\":true,\"num\":0},\"data1\":{\"_placeholder\":true,\"num\":1}}]"
let sendData = [["data1": data, "data2": data2]]
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/", ack: true)
@ -147,15 +149,15 @@ class SocketBasicPacketTest: XCTestCase {
}
func testBinaryStringPlaceholderInMessage() {
let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"num\":1,\"_placeholder\":true}]"
let socket = SocketIOClient(socketURL: NSURL())
let engineString = "52-[\"test\",\"~~0\",{\"num\":0,\"_placeholder\":true},{\"_placeholder\":true,\"num\":1}]"
let socket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
socket.setTestable()
if case let .Right(packet) = socket.parseString(engineString) {
if case let .right(packet) = socket.parseString(engineString) {
var packet = packet
XCTAssertEqual(packet.event, "test")
packet.addData(data)
packet.addData(data2)
_ = packet.addData(data)
_ = packet.addData(data2)
XCTAssertEqual(packet.args[0] as? String, "~~0")
} else {
XCTFail()

View File

@ -15,24 +15,24 @@ class SocketEngineTest: XCTestCase {
override func setUp() {
super.setUp()
client = SocketIOClient(socketURL: NSURL(string: "http://localhost")!)
engine = SocketEngine(client: client, url: NSURL(string: "http://localhost")!, options: nil)
client = SocketIOClient(socketURL: URL(string: "http://localhost")!)
engine = SocketEngine(client: client, url: URL(string: "http://localhost")!, options: nil)
client.setTestable()
}
func testBasicPollingMessage() {
let expectation = expectationWithDescription("Basic polling test")
let expect = expectation(description: "Basic polling test")
client.on("blankTest") {data, ack in
expectation.fulfill()
expect.fulfill()
}
engine.parsePollingMessage("15:42[\"blankTest\"]")
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testTwoPacketsInOnePollTest() {
let finalExpectation = expectationWithDescription("Final packet in poll test")
let finalExpectation = expectation(description: "Final packet in poll test")
var gotBlank = false
client.on("blankTest") {data, ack in
@ -40,7 +40,7 @@ class SocketEngineTest: XCTestCase {
}
client.on("stringTest") {data, ack in
if let str = data[0] as? String where gotBlank {
if let str = data[0] as? String, gotBlank {
if str == "hello" {
finalExpectation.fulfill()
}
@ -48,43 +48,43 @@ class SocketEngineTest: XCTestCase {
}
engine.parsePollingMessage("15:42[\"blankTest\"]24:42[\"stringTest\",\"hello\"]")
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testEngineDoesErrorOnUnknownTransport() {
let finalExpectation = expectationWithDescription("Unknown Transport")
let finalExpectation = expectation(description: "Unknown Transport")
client.on("error") {data, ack in
if let error = data[0] as? String where error == "Unknown transport" {
if let error = data[0] as? String, error == "Unknown transport" {
finalExpectation.fulfill()
}
}
engine.parseEngineMessage("{\"code\": 0, \"message\": \"Unknown transport\"}", fromPolling: false)
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testEngineDoesErrorOnUnknownMessage() {
let finalExpectation = expectationWithDescription("Engine Errors")
let finalExpectation = expectation(description: "Engine Errors")
client.on("error") {data, ack in
finalExpectation.fulfill()
}
engine.parseEngineMessage("afafafda", fromPolling: false)
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testEngineDecodesUTF8Properly() {
let expectation = expectationWithDescription("Engine Decodes utf8")
let expect = expectation(description: "Engine Decodes utf8")
client.on("stringTest") {data, ack in
XCTAssertEqual(data[0] as? String, "lïne one\nlīne \rtwo", "Failed string test")
expectation.fulfill()
expect.fulfill()
}
engine.parsePollingMessage("41:42[\"stringTest\",\"lïne one\\nlīne \\rtwo\"]")
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testEncodeURLProperly() {
@ -102,4 +102,23 @@ class SocketEngineTest: XCTestCase {
XCTAssertEqual(engine.urlPolling.query, "transport=polling&b64=1&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D")
XCTAssertEqual(engine.urlWebSocket.query, "transport=websocket&forbidden=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%22%20%7B%7D")
}
func testBase64Data() {
let expect = expectation(description: "Engine Decodes base64 data")
let b64String = "b4aGVsbG8NCg=="
let packetString = "451-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]"
client.on("test") {data, ack in
if let data = data[0] as? Data, let string = String(data: data, encoding: .utf8) {
XCTAssertEqual(string, "hello")
}
expect.fulfill()
}
engine.parseEngineMessage(packetString, fromPolling: false)
engine.parseEngineMessage(b64String, fromPolling: false)
waitForExpectations(timeout: 3, handler: nil)
}
}

View File

@ -15,16 +15,16 @@ class TestSocketIOClientConfiguration: XCTestCase {
override func setUp() {
super.setUp()
config = [.Log(false), .ForceNew(true)]
config = [.log(false), .forceNew(true)]
}
func testReplaceSameOption() {
config.insert(.Log(true))
config.insert(.log(true))
XCTAssertEqual(config.count, 2)
switch config[0] {
case let .Log(log):
case let .log(log):
XCTAssertTrue(log)
default:
XCTFail()
@ -32,12 +32,12 @@ class TestSocketIOClientConfiguration: XCTestCase {
}
func testIgnoreIfExisting() {
config.insert(.ForceNew(false), replacing: false)
config.insert(.forceNew(false), replacing: false)
XCTAssertEqual(config.count, 2)
switch config[1] {
case let .ForceNew(new):
case let .forceNew(new):
XCTAssertTrue(new)
default:
XCTFail()

View File

@ -10,12 +10,12 @@ import XCTest
@testable import SocketIO
class SocketNamespacePacketTest: XCTestCase {
let data = "test".dataUsingEncoding(NSUTF8StringEncoding)!
let data2 = "test2".dataUsingEncoding(NSUTF8StringEncoding)!
let data = "test".data(using: String.Encoding.utf8)!
let data2 = "test2".data(using: String.Encoding.utf8)!
func testEmpyEmit() {
let expectedSendString = "2/swift,[\"test\"]"
let sendData = ["test"]
let sendData: [Any] = ["test"]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
@ -23,7 +23,7 @@ class SocketNamespacePacketTest: XCTestCase {
func testNullEmit() {
let expectedSendString = "2/swift,[\"test\",null]"
let sendData = ["test", NSNull()]
let sendData: [Any] = ["test", NSNull()]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
@ -31,31 +31,32 @@ class SocketNamespacePacketTest: XCTestCase {
func testStringEmit() {
let expectedSendString = "2/swift,[\"test\",\"foo bar\"]"
let sendData = ["test", "foo bar"]
let sendData: [Any] = ["test", "foo bar"]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
}
func testJSONEmit() {
let expectedSendString = "2/swift,[\"test\",{\"test\":\"hello\",\"hello\":1,\"foobar\":true,\"null\":null}]"
let sendData = ["test", ["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]]
let expectedSendString = "2/swift,[\"test\",{\"null\":null,\"test\":\"hello\",\"hello\":1,\"foobar\":true}]"
let sendData: [Any] = ["test", ["foobar": true, "hello": 1, "test": "hello", "null": NSNull()] as NSDictionary]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
}
func testArrayEmit() {
let expectedSendString = "2/swift,[\"test\",[\"hello\",1,{\"test\":\"test\"}]]"
let sendData = ["test", ["hello", 1, ["test": "test"]]]
let expectedSendString = "2/swift,[\"test\",[\"hello\",1,{\"test\":\"test\"},true]]"
let sendData: [Any] = ["test", ["hello", 1, ["test": "test"], true]]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
}
func testBinaryEmit() {
let expectedSendString = "51-/swift,[\"test\",{\"num\":0,\"_placeholder\":true}]"
let sendData = ["test", data]
let expectedSendString = "51-/swift,[\"test\",{\"_placeholder\":true,\"num\":0}]"
let sendData: [Any] = ["test", data]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
@ -63,12 +64,12 @@ class SocketNamespacePacketTest: XCTestCase {
}
func testMultipleBinaryEmit() {
let expectedSendString = "52-/swift,[\"test\",{\"data1\":{\"num\":0,\"_placeholder\":true},\"data2\":{\"num\":1,\"_placeholder\":true}}]"
let sendData = ["test", ["data1": data, "data2": data2]]
let expectedSendString = "52-/swift,[\"test\",{\"data2\":{\"_placeholder\":true,\"num\":0},\"data1\":{\"_placeholder\":true,\"num\":1}}]"
let sendData: [Any] = ["test", ["data1": data, "data2": data2] as NSDictionary]
let packet = SocketPacket.packetFromEmit(sendData, id: -1, nsp: "/swift", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
XCTAssertEqual(packet.binary, [data, data2])
XCTAssertEqual(packet.binary, [data2, data])
}
func testEmitWithAck() {
@ -80,8 +81,8 @@ class SocketNamespacePacketTest: XCTestCase {
}
func testEmitDataWithAck() {
let expectedSendString = "51-/swift,0[\"test\",{\"num\":0,\"_placeholder\":true}]"
let sendData = ["test", data]
let expectedSendString = "51-/swift,0[\"test\",{\"_placeholder\":true,\"num\":0}]"
let sendData: [Any] = ["test", data]
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: false)
XCTAssertEqual(packet.packetString, expectedSendString)
@ -113,7 +114,7 @@ class SocketNamespacePacketTest: XCTestCase {
}
func testJSONAck() {
let expectedSendString = "3/swift,0[{\"test\":\"hello\",\"hello\":1,\"foobar\":true,\"null\":null}]"
let expectedSendString = "3/swift,0[{\"null\":null,\"hello\":1,\"test\":\"hello\",\"foobar\":true}]"
let sendData = [["foobar": true, "hello": 1, "test": "hello", "null": NSNull()]]
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: true)
@ -121,7 +122,7 @@ class SocketNamespacePacketTest: XCTestCase {
}
func testBinaryAck() {
let expectedSendString = "61-/swift,0[{\"num\":0,\"_placeholder\":true}]"
let expectedSendString = "61-/swift,0[{\"_placeholder\":true,\"num\":0}]"
let sendData = [data]
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: true)
@ -130,7 +131,7 @@ class SocketNamespacePacketTest: XCTestCase {
}
func testMultipleBinaryAck() {
let expectedSendString = "62-/swift,0[{\"data2\":{\"num\":0,\"_placeholder\":true},\"data1\":{\"num\":1,\"_placeholder\":true}}]"
let expectedSendString = "62-/swift,0[{\"data2\":{\"_placeholder\":true,\"num\":0},\"data1\":{\"_placeholder\":true,\"num\":1}}]"
let sendData = [["data1": data, "data2": data2]]
let packet = SocketPacket.packetFromEmit(sendData, id: 0, nsp: "/swift", ack: true)

View File

@ -31,11 +31,11 @@
}
- (void)testEmitSyntax {
[self.socket emit:@"testEmit" withItems:@[@YES]];
[self.socket emit:@"testEmit" with:@[@YES]];
}
- (void)testEmitWithAckSyntax {
[self.socket emitWithAck:@"testAckEmit" withItems:@[@YES]](0, ^(NSArray* data) {
[self.socket emitWithAck:@"testAckEmit" with:@[@YES]](0, ^(NSArray* data) {
});
}
@ -47,7 +47,7 @@
- (void)testSocketManager {
SocketClientManager* manager = [SocketClientManager sharedManager];
[manager addSocket:self.socket labeledAs:@"test"];
[manager removeSocketWithLabel:@"test"];
[manager removeSocketWithLabel:@"test"];
}
@end

View File

@ -10,24 +10,25 @@ import XCTest
@testable import SocketIO
class SocketParserTest: XCTestCase {
let testSocket = SocketIOClient(socketURL: NSURL())
let testSocket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
//Format key: message; namespace-data-binary-id
static let packetTypes: [String: (String, [AnyObject], [NSData], Int)] = [
static let packetTypes: [String: (String, [Any], [Data], Int)] = [
"0": ("/", [], [], -1), "1": ("/", [], [], -1),
"25[\"test\"]": ("/", ["test"], [], 5),
"2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1),
"2/swift,[\"testArrayEmitReturn\",[\"test3\",\"test4\"]]": ("/swift", ["testArrayEmitReturn", ["test3", "test4"]], [], -1),
"51-/swift,[\"testMultipleItemsWithBufferEmitReturn\",[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", ["testMultipleItemsWithBufferEmitReturn", [1, 2], ["test": "bob"], 25, "polo", ["_placeholder": true, "num": 0]], [], -1),
"3/swift,0[[\"test3\",\"test4\"]]": ("/swift", [["test3", "test4"]], [], 0),
"2/swift,[\"testArrayEmitReturn\",[\"test3\",\"test4\"]]": ("/swift", ["testArrayEmitReturn", ["test3", "test4"] as NSArray], [], -1),
"51-/swift,[\"testMultipleItemsWithBufferEmitReturn\",[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", ["testMultipleItemsWithBufferEmitReturn", [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], -1),
"3/swift,0[[\"test3\",\"test4\"]]": ("/swift", [["test3", "test4"] as NSArray], [], 0),
"61-/swift,19[[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]":
("/swift", [ [1, 2], ["test": "bob"], 25, "polo", ["_placeholder": true, "num": 0]], [], 19),
("/swift", [ [1, 2] as NSArray, ["test": "bob"] as NSDictionary, 25, "polo", ["_placeholder": true, "num": 0] as NSDictionary], [], 19),
"4/swift,": ("/swift", [], [], -1),
"0/swift": ("/swift", [], [], -1),
"1/swift": ("/swift", [], [], -1),
"4\"ERROR\"": ("/", ["ERROR"], [], -1),
"4{\"test\":2}": ("/", [["test": 2]], [], -1),
"41": ("/", [1], [], -1)]
"41": ("/", [1], [], -1),
"4[1, \"hello\"]": ("/", [1, "hello"], [], -1)]
func testDisconnect() {
let message = "1"
@ -99,33 +100,38 @@ class SocketParserTest: XCTestCase {
validateParseResult(message)
}
func testErrorTypeArray() {
let message = "4[1, \"hello\"]"
validateParseResult(message)
}
func testInvalidInput() {
let message = "8"
switch testSocket.parseString(message) {
case .Left(_):
case .left(_):
return
case .Right(_):
case .right(_):
XCTFail("Created packet when shouldn't have")
}
}
func testGenericParser() {
var parser = SocketStringReader(message: "61-/swift,")
XCTAssertEqual(parser.read(1), "6")
XCTAssertEqual(parser.read(count: 1), "6")
XCTAssertEqual(parser.currentCharacter, "1")
XCTAssertEqual(parser.readUntilStringOccurence("-"), "1")
XCTAssertEqual(parser.readUntilOccurence(of: "-"), "1")
XCTAssertEqual(parser.currentCharacter, "/")
}
func validateParseResult(message: String) {
func validateParseResult(_ message: String) {
let validValues = SocketParserTest.packetTypes[message]!
let packet = testSocket.parseString(message)
let type = message.substringWithRange(Range<String.Index>(message.startIndex..<message.startIndex.advancedBy(1)))
if case let .Right(packet) = packet {
let type = String(message.characters.prefix(1))
if case let .right(packet) = packet {
XCTAssertEqual(packet.type, SocketPacket.PacketType(rawValue: Int(type) ?? -1)!)
XCTAssertEqual(packet.nsp, validValues.0)
XCTAssertTrue((packet.data as NSArray).isEqualToArray(validValues.1), "\(packet.data)")
XCTAssertTrue((packet.binary as NSArray).isEqualToArray(validValues.2), "\(packet.binary)")
XCTAssertTrue((packet.data as NSArray).isEqual(to: validValues.1), "\(packet.data)")
XCTAssertTrue((packet.binary as NSArray).isEqual(to: validValues.2), "\(packet.binary)")
XCTAssertEqual(packet.id, validValues.3)
} else {
XCTFail()
@ -134,10 +140,10 @@ class SocketParserTest: XCTestCase {
func testParsePerformance() {
let keys = Array(SocketParserTest.packetTypes.keys)
measureBlock({
for item in keys.enumerate() {
self.testSocket.parseString(item.element)
measure {
for item in keys.enumerated() {
_ = self.testSocket.parseString(item.element)
}
})
}
}
}

View File

@ -10,13 +10,13 @@ import XCTest
@testable import SocketIO
class SocketSideEffectTest: XCTestCase {
let data = "test".dataUsingEncoding(NSUTF8StringEncoding)!
let data2 = "test2".dataUsingEncoding(NSUTF8StringEncoding)!
let data = "test".data(using: String.Encoding.utf8)!
let data2 = "test2".data(using: String.Encoding.utf8)!
private var socket: SocketIOClient!
override func setUp() {
super.setUp()
socket = SocketIOClient(socketURL: NSURL())
socket = SocketIOClient(socketURL: URL(string: "http://localhost/")!)
socket.setTestable()
}
@ -25,72 +25,72 @@ class SocketSideEffectTest: XCTestCase {
}
func testFirstAck() {
socket.emitWithAck("test")(timeoutAfter: 0) {data in}
socket.emitWithAck("test")(0) {data in}
XCTAssertEqual(socket.currentAck, 0)
}
func testSecondAck() {
socket.emitWithAck("test")(timeoutAfter: 0) {data in}
socket.emitWithAck("test")(timeoutAfter: 0) {data in}
socket.emitWithAck("test")(0) {data in}
socket.emitWithAck("test")(0) {data in}
XCTAssertEqual(socket.currentAck, 1)
}
func testHandleAck() {
let expectation = expectationWithDescription("handled ack")
socket.emitWithAck("test")(timeoutAfter: 0) {data in
let expect = expectation(description: "handled ack")
socket.emitWithAck("test")(0) {data in
XCTAssertEqual(data[0] as? String, "hello world")
expectation.fulfill()
expect.fulfill()
}
socket.parseSocketMessage("30[\"hello world\"]")
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testHandleAck2() {
let expectation = expectationWithDescription("handled ack2")
socket.emitWithAck("test")(timeoutAfter: 0) {data in
let expect = expectation(description: "handled ack2")
socket.emitWithAck("test")(0) {data in
XCTAssertTrue(data.count == 2, "Wrong number of ack items")
expectation.fulfill()
expect.fulfill()
}
socket.parseSocketMessage("61-0[{\"_placeholder\":true,\"num\":0},{\"test\":true}]")
socket.parseBinaryData(NSData())
waitForExpectationsWithTimeout(3, handler: nil)
socket.parseBinaryData(Data())
waitForExpectations(timeout: 3, handler: nil)
}
func testHandleEvent() {
let expectation = expectationWithDescription("handled event")
let expect = expectation(description: "handled event")
socket.on("test") {data, ack in
XCTAssertEqual(data[0] as? String, "hello world")
expectation.fulfill()
expect.fulfill()
}
socket.parseSocketMessage("2[\"test\",\"hello world\"]")
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testHandleStringEventWithQuotes() {
let expectation = expectationWithDescription("handled event")
let expect = expectation(description: "handled event")
socket.on("test") {data, ack in
XCTAssertEqual(data[0] as? String, "\"hello world\"")
expectation.fulfill()
expect.fulfill()
}
socket.parseSocketMessage("2[\"test\",\"\\\"hello world\\\"\"]")
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testHandleOnceEvent() {
let expectation = expectationWithDescription("handled event")
let expect = expectation(description: "handled event")
socket.once("test") {data, ack in
XCTAssertEqual(data[0] as? String, "hello world")
XCTAssertEqual(self.socket.testHandlers.count, 0)
expectation.fulfill()
expect.fulfill()
}
socket.parseSocketMessage("2[\"test\",\"hello world\"]")
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testOffWithEvent() {
@ -112,46 +112,46 @@ class SocketSideEffectTest: XCTestCase {
}
func testHandlesErrorPacket() {
let expectation = expectationWithDescription("Handled error")
let expect = expectation(description: "Handled error")
socket.on("error") {data, ack in
if let error = data[0] as? String where error == "test error" {
expectation.fulfill()
if let error = data[0] as? String, error == "test error" {
expect.fulfill()
}
}
socket.parseSocketMessage("4\"test error\"")
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testHandleBinaryEvent() {
let expectation = expectationWithDescription("handled binary event")
let expect = expectation(description: "handled binary event")
socket.on("test") {data, ack in
if let dict = data[0] as? NSDictionary, data = dict["test"] as? NSData {
XCTAssertEqual(data, self.data)
expectation.fulfill()
if let dict = data[0] as? NSDictionary, let data = dict["test"] as? NSData {
XCTAssertEqual(data as Data, self.data)
expect.fulfill()
}
}
socket.parseSocketMessage("51-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0}}]")
socket.parseBinaryData(data)
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testHandleMultipleBinaryEvent() {
let expectation = expectationWithDescription("handled multiple binary event")
let expect = expectation(description: "handled multiple binary event")
socket.on("test") {data, ack in
if let dict = data[0] as? NSDictionary, data = dict["test"] as? NSData,
data2 = dict["test2"] as? NSData {
XCTAssertEqual(data, self.data)
XCTAssertEqual(data2, self.data2)
expectation.fulfill()
if let dict = data[0] as? NSDictionary, let data = dict["test"] as? NSData,
let data2 = dict["test2"] as? NSData {
XCTAssertEqual(data as Data, self.data)
XCTAssertEqual(data2 as Data, self.data2)
expect.fulfill()
}
}
socket.parseSocketMessage("52-[\"test\",{\"test\":{\"_placeholder\":true,\"num\":0},\"test2\":{\"_placeholder\":true,\"num\":1}}]")
socket.parseBinaryData(data)
socket.parseBinaryData(data2)
waitForExpectationsWithTimeout(3, handler: nil)
waitForExpectations(timeout: 3, handler: nil)
}
func testSocketManager() {

View File

@ -24,8 +24,8 @@ import Foundation
import Security
public class SSLCert : NSObject {
var certData: NSData?
var key: SecKeyRef?
var certData: Data?
var key: SecKey?
/**
Designated init for certificates
@ -34,7 +34,7 @@ public class SSLCert : NSObject {
- returns: a representation security object to be used with
*/
public init(data: NSData) {
public init(data: Data) {
self.certData = data
}
@ -45,7 +45,7 @@ public class SSLCert : NSObject {
- returns: a representation security object to be used with
*/
public init(key: SecKeyRef) {
public init(key: SecKey) {
self.key = key
}
}
@ -55,7 +55,7 @@ public class SSLSecurity : NSObject {
var isReady = false //is the key processing done?
var certificates: [NSData]? //the certificates
@nonobjc var pubKeys: [SecKeyRef]? //the public keys
@nonobjc var pubKeys: [SecKey]? //the public keys
var usePublicKeys = false //use public keys or certificate validation?
/**
@ -66,12 +66,12 @@ public class SSLSecurity : NSObject {
- returns: a representation security object to be used with
*/
public convenience init(usePublicKeys: Bool = false) {
let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".")
let paths = Bundle.main.paths(forResourcesOfType: "cer", inDirectory: ".")
let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in
var certs = certs
if let data = NSData(contentsOfFile: path) {
certs.append(SSLCert(data: data))
certs.append(SSLCert(data: data as Data))
}
return certs
}
@ -93,10 +93,10 @@ public class SSLSecurity : NSObject {
super.init()
if self.usePublicKeys {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) {
let pubKeys = certs.reduce([SecKeyRef]()) { (pubKeys: [SecKeyRef], cert: SSLCert) -> [SecKeyRef] in
DispatchQueue.global(qos: .default).async {
let pubKeys = certs.reduce([SecKey]()) { (pubKeys: [SecKey], cert: SSLCert) -> [SecKey] in
var pubKeys = pubKeys
if let data = cert.certData where cert.key == nil {
if let data = cert.certData, cert.key == nil {
cert.key = self.extractPublicKey(data)
}
if let key = cert.key {
@ -109,14 +109,14 @@ public class SSLSecurity : NSObject {
self.isReady = true
}
} else {
let certificates = certs.reduce([NSData]()) { (certificates: [NSData], cert: SSLCert) -> [NSData] in
let certificates = certs.reduce([Data]()) { (certificates: [Data], cert: SSLCert) -> [Data] in
var certificates = certificates
if let data = cert.certData {
certificates.append(data)
}
return certificates
}
self.certificates = certificates
self.certificates = certificates as [NSData]
self.isReady = true
}
}
@ -129,7 +129,7 @@ public class SSLSecurity : NSObject {
- returns: if the key was successfully validated
*/
public func isValid(trust: SecTrustRef, domain: String?) -> Bool {
public func isValid(_ trust: SecTrust, domain: String?) -> Bool {
var tries = 0
while(!self.isReady) {
@ -139,19 +139,19 @@ public class SSLSecurity : NSObject {
return false //doesn't appear it is going to ever be ready...
}
}
var policy: SecPolicyRef
var policy: SecPolicy
if self.validatedDN {
policy = SecPolicyCreateSSL(true, domain)
policy = SecPolicyCreateSSL(true, domain as CFString?)
} else {
policy = SecPolicyCreateBasicX509()
}
SecTrustSetPolicies(trust,policy)
if self.usePublicKeys {
if let keys = self.pubKeys {
let serverPubKeys = publicKeyChainForTrust(trust)
for serverKey in serverPubKeys as [AnyObject] {
for key in keys as [AnyObject] {
if serverKey.isEqual(key) {
let serverPubKeys = publicKeyChainForTrust(trust: trust)
for serverKey in serverPubKeys {
for key in keys {
if CFEqual(serverKey, key) {
return true
}
}
@ -163,15 +163,15 @@ public class SSLSecurity : NSObject {
for cert in certs {
collect.append(SecCertificateCreateWithData(nil,cert)!)
}
SecTrustSetAnchorCertificates(trust,collect)
var result: SecTrustResultType = 0
SecTrustSetAnchorCertificates(trust,collect as CFArray)
var result = SecTrustResultType(rawValue: 0)!
SecTrustEvaluate(trust,&result)
let r = Int(result)
if r == kSecTrustResultUnspecified || r == kSecTrustResultProceed {
let r = Int(result.rawValue)
if r == Int(SecTrustResultType.unspecified.rawValue) || r == Int(SecTrustResultType.proceed.rawValue) {
var trustedCount = 0
for serverCert in serverCerts {
for cert in certs {
if cert == serverCert {
if cert as Data == serverCert {
trustedCount += 1
break
}
@ -192,8 +192,8 @@ public class SSLSecurity : NSObject {
- returns: a public key
*/
func extractPublicKey(data: NSData) -> SecKeyRef? {
guard let cert = SecCertificateCreateWithData(nil, data) else { return nil }
func extractPublicKey(_ data: Data) -> SecKey? {
guard let cert = SecCertificateCreateWithData(nil, data as CFData) else { return nil }
return extractPublicKeyFromCert(cert, policy: SecPolicyCreateBasicX509())
}
@ -205,13 +205,13 @@ public class SSLSecurity : NSObject {
- returns: a public key
*/
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? {
func extractPublicKeyFromCert(_ cert: SecCertificate, policy: SecPolicy) -> SecKey? {
var possibleTrust: SecTrust?
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
guard let trust = possibleTrust else { return nil }
var result: SecTrustResultType = 0
var result = SecTrustResultType(rawValue: 0)!
SecTrustEvaluate(trust, &result)
return SecTrustCopyPublicKey(trust)
}
@ -223,11 +223,11 @@ public class SSLSecurity : NSObject {
- returns: the certificate chain for the trust
*/
func certificateChainForTrust(trust: SecTrustRef) -> [NSData] {
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (certificates: [NSData], index: Int) -> [NSData] in
func certificateChainForTrust(_ trust: SecTrust) -> [Data] {
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([Data]()) { (certificates: [Data], index: Int) -> [Data] in
var certificates = certificates
let cert = SecTrustGetCertificateAtIndex(trust, index)
certificates.append(SecCertificateCopyData(cert!))
certificates.append(SecCertificateCopyData(cert!) as Data)
return certificates
}
@ -241,9 +241,9 @@ public class SSLSecurity : NSObject {
- returns: the public keys from the certifcate chain for the trust
*/
@nonobjc func publicKeyChainForTrust(trust: SecTrustRef) -> [SecKeyRef] {
@nonobjc func publicKeyChainForTrust(trust: SecTrust) -> [SecKey] {
let policy = SecPolicyCreateBasicX509()
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKeyRef]()) { (keys: [SecKeyRef], index: Int) -> [SecKeyRef] in
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKey]()) { (keys: [SecKey], index: Int) -> [SecKey] in
var keys = keys
let cert = SecTrustGetCertificateAtIndex(trust, index)
if let key = extractPublicKeyFromCert(cert!, policy: policy) {
@ -257,4 +257,4 @@ public class SSLSecurity : NSObject {
}
}
}

View File

@ -33,15 +33,15 @@ public final class SocketAckEmitter : NSObject {
self.ackNum = ackNum
}
public func with(items: AnyObject...) {
public func with(_ items: SocketData...) {
guard ackNum != -1 else { return }
socket.emitAck(ackNum, withItems: items)
socket.emitAck(ackNum, with: items)
}
public func with(items: [AnyObject]) {
public func with(_ items: [Any]) {
guard ackNum != -1 else { return }
socket.emitAck(ackNum, withItems: items)
socket.emitAck(ackNum, with: items)
}
}

View File

@ -35,7 +35,7 @@ private struct SocketAck : Hashable {
self.ack = ack
}
init(ack: Int, callback: AckCallback) {
init(ack: Int, callback: @escaping AckCallback) {
self.ack = ack
self.callback = callback
}
@ -52,25 +52,21 @@ private func ==(lhs: SocketAck, rhs: SocketAck) -> Bool {
struct SocketAckManager {
private var acks = Set<SocketAck>(minimumCapacity: 1)
mutating func addAck(ack: Int, callback: AckCallback) {
mutating func addAck(_ ack: Int, callback: @escaping AckCallback) {
acks.insert(SocketAck(ack: ack, callback: callback))
}
/// Should be called on handle queue
mutating func executeAck(ack: Int, items: [AnyObject], onQueue: dispatch_queue_t) {
let callback = acks.remove(SocketAck(ack: ack))
mutating func executeAck(_ ack: Int, with items: [Any], onQueue: DispatchQueue) {
let ack = acks.remove(SocketAck(ack: ack))
dispatch_async(onQueue) {
callback?.callback(items)
}
onQueue.async() { ack?.callback(items) }
}
/// Should be called on handle queue
mutating func timeoutAck(ack: Int, onQueue: dispatch_queue_t) {
let callback = acks.remove(SocketAck(ack: ack))
mutating func timeoutAck(_ ack: Int, onQueue: DispatchQueue) {
let ack = acks.remove(SocketAck(ack: ack))
dispatch_async(onQueue) {
callback?.callback(["NO ACK"])
}
onQueue.async() { ack?.callback?(["NO ACK"]) }
}
}

View File

@ -26,12 +26,12 @@ import Foundation
public final class SocketAnyEvent : NSObject {
public let event: String
public let items: NSArray?
public let items: [Any]?
override public var description: String {
return "SocketAnyEvent: Event: \(event) items: \(items ?? nil)"
}
init(event: String, items: NSArray?) {
init(event: String, items: [Any]?) {
self.event = event
self.items = items
}

View File

@ -43,12 +43,12 @@ import Foundation
manager["room1"]?.emit("hello")
```
*/
public final class SocketClientManager : NSObject {
public static let sharedManager = SocketClientManager()
open class SocketClientManager : NSObject {
open static let sharedManager = SocketClientManager()
private var sockets = [String: SocketIOClient]()
public subscript(string: String) -> SocketIOClient? {
open subscript(string: String) -> SocketIOClient? {
get {
return sockets[string]
}
@ -58,25 +58,25 @@ public final class SocketClientManager : NSObject {
}
}
public func addSocket(socket: SocketIOClient, labeledAs label: String) {
open func addSocket(_ socket: SocketIOClient, labeledAs label: String) {
sockets[label] = socket
}
public func removeSocket(withLabel label: String) -> SocketIOClient? {
return sockets.removeValueForKey(label)
open func removeSocket(withLabel label: String) -> SocketIOClient? {
return sockets.removeValue(forKey: label)
}
public func removeSocket(socket: SocketIOClient) -> SocketIOClient? {
open func removeSocket(_ socket: SocketIOClient) -> SocketIOClient? {
var returnSocket: SocketIOClient?
for (label, dictSocket) in sockets where dictSocket === socket {
returnSocket = sockets.removeValueForKey(label)
returnSocket = sockets.removeValue(forKey: label)
}
return returnSocket
}
public func removeSockets() {
open func removeSockets() {
sockets.removeAll()
}
}

View File

@ -24,24 +24,24 @@
import Foundation
public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket {
public let emitQueue = dispatch_queue_create("com.socketio.engineEmitQueue", DISPATCH_QUEUE_SERIAL)
public let handleQueue = dispatch_queue_create("com.socketio.engineHandleQueue", DISPATCH_QUEUE_SERIAL)
public let parseQueue = dispatch_queue_create("com.socketio.engineParseQueue", DISPATCH_QUEUE_SERIAL)
public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePollable, SocketEngineWebsocket {
public let emitQueue = DispatchQueue(label: "com.socketio.engineEmitQueue", attributes: [])
public let handleQueue = DispatchQueue(label: "com.socketio.engineHandleQueue", attributes: [])
public let parseQueue = DispatchQueue(label: "com.socketio.engineParseQueue", attributes: [])
public var connectParams: [String: AnyObject]? {
public var connectParams: [String: Any]? {
didSet {
(urlPolling, urlWebSocket) = createURLs()
}
}
public var postWait = [String]()
public var waitingForPoll = false
public var waitingForPost = false
public private(set) var closed = false
public private(set) var connected = false
public private(set) var cookies: [NSHTTPCookie]?
public private(set) var cookies: [HTTPCookie]?
public private(set) var doubleEncodeUTF8 = true
public private(set) var extraHeaders: [String: String]?
public private(set) var fastUpgrade = false
@ -50,28 +50,28 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
public private(set) var invalidated = false
public private(set) var polling = true
public private(set) var probing = false
public private(set) var session: NSURLSession?
public private(set) var session: URLSession?
public private(set) var sid = ""
public private(set) var socketPath = "/engine.io/"
public private(set) var urlPolling = NSURL()
public private(set) var urlWebSocket = NSURL()
public private(set) var urlPolling = URL(string: "http://localhost/")!
public private(set) var urlWebSocket = URL(string: "http://localhost/")!
public private(set) var websocket = false
public private(set) var ws: WebSocket?
public weak var client: SocketEngineClient?
private weak var sessionDelegate: NSURLSessionDelegate?
private weak var sessionDelegate: URLSessionDelegate?
private let logType = "SocketEngine"
private let url: NSURL
private let url: URL
private var pingInterval: Double?
private var pingTimeout = 0.0 {
didSet {
pongsMissedMax = Int(pingTimeout / (pingInterval ?? 25))
}
}
private var pongsMissed = 0
private var pongsMissedMax = 0
private var probeWait = ProbeWaitQueue()
@ -80,45 +80,44 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
private var selfSigned = false
private var voipEnabled = false
public init(client: SocketEngineClient, url: NSURL, config: SocketIOClientConfiguration) {
public init(client: SocketEngineClient, url: URL, config: SocketIOClientConfiguration) {
self.client = client
self.url = url
for option in config {
switch option {
case let .ConnectParams(params):
case let .connectParams(params):
connectParams = params
case let .Cookies(cookies):
case let .cookies(cookies):
self.cookies = cookies
case let .DoubleEncodeUTF8(encode):
case let .doubleEncodeUTF8(encode):
doubleEncodeUTF8 = encode
case let .ExtraHeaders(headers):
case let .extraHeaders(headers):
extraHeaders = headers
case let .SessionDelegate(delegate):
case let .sessionDelegate(delegate):
sessionDelegate = delegate
case let .ForcePolling(force):
case let .forcePolling(force):
forcePolling = force
case let .ForceWebsockets(force):
case let .forceWebsockets(force):
forceWebsockets = force
case let .Path(path):
case let .path(path):
socketPath = path
case let .voipEnabled(enable):
if !socketPath.hasSuffix("/") {
socketPath += "/"
}
case let .VoipEnabled(enable):
voipEnabled = enable
case let .Secure(secure):
case let .secure(secure):
self.secure = secure
case let .Security(security):
self.security = security
case let .SelfSigned(selfSigned):
case let .selfSigned(selfSigned):
self.selfSigned = selfSigned
case let .security(security):
self.security = security
default:
continue
}
}
super.init()
sessionDelegate = sessionDelegate ?? self
@ -126,17 +125,17 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
(urlPolling, urlWebSocket) = createURLs()
}
public convenience init(client: SocketEngineClient, url: NSURL, options: NSDictionary?) {
public convenience init(client: SocketEngineClient, url: URL, options: NSDictionary?) {
self.init(client: client, url: url, config: options?.toSocketConfiguration() ?? [])
}
deinit {
DefaultSocketLogger.Logger.log("Engine is being released", type: logType)
closed = true
stopPolling()
}
private func checkAndHandleEngineError(msg: String) {
private func checkAndHandleEngineError(_ msg: String) {
do {
let dict = try msg.toNSDictionary()
guard let error = dict["message"] as? String else { return }
@ -147,76 +146,76 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
2: Bad handshake request
3: Bad request
*/
didError(error)
didError(reason: error)
} catch {
client?.engineDidError("Got unknown error from server \(msg)")
client?.engineDidError(reason: "Got unknown error from server \(msg)")
}
}
private func handleBase64(message: String) {
// binary in base64 string
let noPrefix = message[message.startIndex.advancedBy(2)..<message.endIndex]
let noPrefix = message[message.index(message.startIndex, offsetBy: 2)..<message.endIndex]
if let data = NSData(base64EncodedString: noPrefix, options: .IgnoreUnknownCharacters) {
client?.parseEngineBinaryData(data)
if let data = NSData(base64Encoded: noPrefix, options: .ignoreUnknownCharacters) {
client?.parseEngineBinaryData(data as Data)
}
}
private func closeOutEngine(reason: String) {
sid = ""
closed = true
invalidated = true
connected = false
ws?.disconnect()
stopPolling()
client?.engineDidClose(reason)
client?.engineDidClose(reason: reason)
}
/// Starts the connection to the server
public func connect() {
if connected {
DefaultSocketLogger.Logger.error("Engine tried opening while connected. Assuming this was a reconnect", type: logType)
disconnect("reconnect")
disconnect(reason: "reconnect")
}
DefaultSocketLogger.Logger.log("Starting engine. Server: %@", type: logType, args: url)
DefaultSocketLogger.Logger.log("Handshaking", type: logType)
resetEngine()
if forceWebsockets {
polling = false
websocket = true
createWebsocketAndConnect()
return
}
let reqPolling = NSMutableURLRequest(URL: urlPolling)
let reqPolling = NSMutableURLRequest(url: urlPolling)
if cookies != nil {
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
reqPolling.allHTTPHeaderFields = headers
}
if let extraHeaders = extraHeaders {
for (headerName, value) in extraHeaders {
reqPolling.setValue(value, forHTTPHeaderField: headerName)
}
}
doLongPoll(reqPolling)
doLongPoll(for: reqPolling as URLRequest)
}
private func createURLs() -> (NSURL, NSURL) {
private func createURLs() -> (URL, URL) {
if client == nil {
return (NSURL(), NSURL())
return (URL(string: "http://localhost/")!, URL(string: "http://localhost/")!)
}
let urlPolling = NSURLComponents(string: url.absoluteString)!
let urlWebSocket = NSURLComponents(string: url.absoluteString)!
var urlPolling = URLComponents(string: url.absoluteString)!
var urlWebSocket = URLComponents(string: url.absoluteString)!
var queryString = ""
urlWebSocket.path = socketPath
urlPolling.path = socketPath
@ -239,15 +238,15 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
urlWebSocket.percentEncodedQuery = "transport=websocket" + queryString
urlPolling.percentEncodedQuery = "transport=polling&b64=1" + queryString
return (urlPolling.URL!, urlWebSocket.URL!)
return (urlPolling.url!, urlWebSocket.url!)
}
private func createWebsocketAndConnect() {
ws = WebSocket(url: urlWebSocketWithSid)
ws = WebSocket(url: urlWebSocketWithSid as NSURL)
if cookies != nil {
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
for (key, value) in headers {
ws?.headers[key] = value
}
@ -267,38 +266,38 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
ws?.connect()
}
public func didError(error: String) {
DefaultSocketLogger.Logger.error("%@", type: logType, args: error)
client?.engineDidError(error)
disconnect(error)
public func didError(reason: String) {
DefaultSocketLogger.Logger.error("%@", type: logType, args: reason)
client?.engineDidError(reason: reason)
disconnect(reason: reason)
}
public func disconnect(reason: String) {
guard connected else { return closeOutEngine(reason) }
guard connected else { return closeOutEngine(reason: reason) }
DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType)
if closed {
return closeOutEngine(reason)
return closeOutEngine(reason: reason)
}
if websocket {
sendWebSocketMessage("", withType: .Close, withData: [])
closeOutEngine(reason)
sendWebSocketMessage("", withType: .close, withData: [])
closeOutEngine(reason: reason)
} else {
disconnectPolling(reason)
disconnectPolling(reason: reason)
}
}
// We need to take special care when we're polling that we send it ASAP
// Also make sure we're on the emitQueue since we're touching postWait
private func disconnectPolling(reason: String) {
dispatch_sync(emitQueue) {
self.postWait.append(String(SocketEnginePacketType.Close.rawValue))
emitQueue.sync {
self.postWait.append(String(SocketEnginePacketType.close.rawValue))
let req = self.createRequestForPostWithPostWait()
self.doRequest(req) {_, _, _ in }
self.closeOutEngine(reason)
self.doRequest(for: req) {_, _, _ in }
self.closeOutEngine(reason: reason)
}
}
@ -308,7 +307,7 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
"we'll probably disconnect soon. You should report this.", type: logType)
}
sendWebSocketMessage("", withType: .Upgrade, withData: [])
sendWebSocketMessage("", withType: .upgrade, withData: [])
websocket = true
polling = false
fastUpgrade = false
@ -319,105 +318,108 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
private func flushProbeWait() {
DefaultSocketLogger.Logger.log("Flushing probe wait", type: logType)
dispatch_async(emitQueue) {
emitQueue.async {
for waiter in self.probeWait {
self.write(waiter.msg, withType: waiter.type, withData: waiter.data)
}
self.probeWait.removeAll(keepCapacity: false)
self.probeWait.removeAll(keepingCapacity: false)
if self.postWait.count != 0 {
self.flushWaitingForPostToWebSocket()
}
}
}
// We had packets waiting for send when we upgraded
// Send them raw
public func flushWaitingForPostToWebSocket() {
guard let ws = self.ws else { return }
for msg in postWait {
ws.writeString(msg)
}
postWait.removeAll(keepCapacity: true)
postWait.removeAll(keepingCapacity: false)
}
private func handleClose(reason: String) {
client?.engineDidClose(reason)
private func handleClose(_ reason: String) {
client?.engineDidClose(reason: reason)
}
private func handleMessage(message: String) {
private func handleMessage(_ message: String) {
client?.parseEngineMessage(message)
}
private func handleNOOP() {
doPoll()
}
private func handleOpen(openData: String) {
do {
let json = try openData.toNSDictionary()
guard let sid = json["sid"] as? String else {
client?.engineDidError("Open packet contained no sid")
return
}
guard let json = try? openData.toNSDictionary() else {
didError(reason: "Error parsing open packet")
let upgradeWs: Bool
self.sid = sid
connected = true
if let upgrades = json["upgrades"] as? [String] {
upgradeWs = upgrades.contains("websocket")
} else {
upgradeWs = false
}
if let pingInterval = json["pingInterval"] as? Double, pingTimeout = json["pingTimeout"] as? Double {
self.pingInterval = pingInterval / 1000.0
self.pingTimeout = pingTimeout / 1000.0
}
if !forcePolling && !forceWebsockets && upgradeWs {
createWebsocketAndConnect()
}
sendPing()
if !forceWebsockets {
doPoll()
}
client?.engineDidOpen("Connect")
} catch {
didError("Error parsing open packet")
return
}
guard let sid = json["sid"] as? String else {
didError(reason: "Open packet contained no sid")
return
}
let upgradeWs: Bool
self.sid = sid
connected = true
if let upgrades = json["upgrades"] as? [String] {
upgradeWs = upgrades.contains("websocket")
} else {
upgradeWs = false
}
if let pingInterval = json["pingInterval"] as? Double, let pingTimeout = json["pingTimeout"] as? Double {
self.pingInterval = pingInterval / 1000.0
self.pingTimeout = pingTimeout / 1000.0
}
if !forcePolling && !forceWebsockets && upgradeWs {
createWebsocketAndConnect()
}
sendPing()
if !forceWebsockets {
doPoll()
}
client?.engineDidOpen(reason: "Connect")
}
private func handlePong(pongMessage: String) {
private func handlePong(with message: String) {
pongsMissed = 0
// We should upgrade
if pongMessage == "3probe" {
if message == "3probe" {
upgradeTransport()
}
}
public func parseEngineData(data: NSData) {
public func parseEngineData(_ data: Data) {
DefaultSocketLogger.Logger.log("Got binary data: %@", type: "SocketEngine", args: data)
client?.parseEngineBinaryData(data.subdataWithRange(NSMakeRange(1, data.length - 1)))
client?.parseEngineBinaryData(data.subdata(in: 1..<data.endIndex))
}
public func parseEngineMessage(message: String, fromPolling: Bool) {
public func parseEngineMessage(_ message: String, fromPolling: Bool) {
DefaultSocketLogger.Logger.log("Got message: %@", type: logType, args: message)
let reader = SocketStringReader(message: message)
let fixedString: String
if message.hasPrefix("b4") {
return handleBase64(message)
return handleBase64(message: message)
}
guard let type = SocketEnginePacketType(rawValue: Int(reader.currentCharacter) ?? -1) else {
@ -426,28 +428,28 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
return
}
if fromPolling && type != .Noop && doubleEncodeUTF8 {
if fromPolling && type != .noop && doubleEncodeUTF8 {
fixedString = fixDoubleUTF8(message)
} else {
fixedString = message
}
switch type {
case .Message:
handleMessage(fixedString[fixedString.startIndex.successor()..<fixedString.endIndex])
case .Noop:
case .message:
handleMessage(String(fixedString.characters.dropFirst()))
case .noop:
handleNOOP()
case .Pong:
handlePong(fixedString)
case .Open:
handleOpen(fixedString[fixedString.startIndex.successor()..<fixedString.endIndex])
case .Close:
case .pong:
handlePong(with: fixedString)
case .open:
handleOpen(openData: String(fixedString.characters.dropFirst()))
case .close:
handleClose(fixedString)
default:
DefaultSocketLogger.Logger.log("Got unknown packet type", type: logType)
}
}
// Puts the engine back in its default state
private func resetEngine() {
closed = false
@ -456,9 +458,7 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
polling = true
probing = false
invalidated = false
session = NSURLSession(configuration: .defaultSessionConfiguration(),
delegate: sessionDelegate,
delegateQueue: NSOperationQueue.mainQueue())
session = Foundation.URLSession(configuration: .default, delegate: sessionDelegate, delegateQueue: OperationQueue.main)
sid = ""
waitingForPoll = false
waitingForPost = false
@ -466,43 +466,40 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
}
private func sendPing() {
if !connected {
return
}
//Server is not responding
guard connected else { return }
// Server is not responding
if pongsMissed > pongsMissedMax {
client?.engineDidClose("Ping timeout")
client?.engineDidClose(reason: "Ping timeout")
return
}
guard let pingInterval = pingInterval else { return }
if let pingInterval = pingInterval {
pongsMissed += 1
write("", withType: .Ping, withData: [])
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(pingInterval * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {[weak self] in
self?.sendPing()
}
}
pongsMissed += 1
write("", withType: .ping, withData: [])
let time = DispatchTime.now() + Double(Int64(pingInterval * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: time) {[weak self] in self?.sendPing() }
}
// Moves from long-polling to websockets
private func upgradeTransport() {
if ws?.isConnected ?? false {
DefaultSocketLogger.Logger.log("Upgrading transport to WebSockets", type: logType)
fastUpgrade = true
sendPollMessage("", withType: .Noop, withData: [])
sendPollMessage("", withType: .noop, withData: [])
// After this point, we should not send anymore polling messages
}
}
/// Write a message, independent of transport.
public func write(msg: String, withType type: SocketEnginePacketType, withData data: [NSData]) {
dispatch_async(emitQueue) {
public func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data]) {
emitQueue.async {
guard self.connected else { return }
if self.websocket {
DefaultSocketLogger.Logger.log("Writing ws: %@ has data: %@",
type: self.logType, args: msg, data.count != 0)
@ -516,9 +513,9 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
}
}
}
// Delegate methods
public func websocketDidConnect(socket: WebSocket) {
public func websocketDidConnect(_ socket: WebSocket) {
if !forceWebsockets {
probing = true
probeWebSocket()
@ -528,23 +525,24 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
polling = false
}
}
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
public func websocketDidDisconnect(_ socket: WebSocket, error: NSError?) {
probing = false
if closed {
client?.engineDidClose("Disconnect")
client?.engineDidClose(reason: "Disconnect")
return
}
if websocket {
connected = false
websocket = false
if let reason = error?.localizedDescription {
didError(reason)
didError(reason: reason)
} else {
client?.engineDidClose("Socket Disconnected")
client?.engineDidClose(reason: "Socket Disconnected")
}
} else {
flushProbeWait()
@ -553,9 +551,9 @@ public final class SocketEngine : NSObject, NSURLSessionDelegate, SocketEnginePo
}
extension SocketEngine {
public func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
public func URLSession(session: URLSession, didBecomeInvalidWithError error: NSError?) {
DefaultSocketLogger.Logger.error("Engine URLSession became invalid", type: "SocketEngine")
didError("Engine URLSession became invalid")
didError(reason: "Engine URLSession became invalid")
}
}

View File

@ -29,6 +29,6 @@ import Foundation
func engineDidError(reason: String)
func engineDidClose(reason: String)
func engineDidOpen(reason: String)
func parseEngineMessage(msg: String)
func parseEngineBinaryData(data: NSData)
func parseEngineMessage(_ msg: String)
func parseEngineBinaryData(_ data: Data)
}

View File

@ -26,5 +26,5 @@
import Foundation
@objc public enum SocketEnginePacketType : Int {
case Open, Close, Ping, Pong, Message, Upgrade, Noop
case open, close, ping, pong, message, upgrade, noop
}

View File

@ -30,7 +30,7 @@ public protocol SocketEnginePollable : SocketEngineSpec {
/// Holds strings waiting to be sent over polling.
/// You shouldn't need to mess with this.
var postWait: [String] { get set }
var session: NSURLSession? { get }
var session: URLSession? { get }
/// Because socket.io doesn't let you send two polling request at the same time
/// we have to keep track if there's an outstanding poll
var waitingForPoll: Bool { get set }
@ -39,15 +39,17 @@ public protocol SocketEnginePollable : SocketEngineSpec {
var waitingForPost: Bool { get set }
func doPoll()
func sendPollMessage(message: String, withType type: SocketEnginePacketType, withData datas: [NSData])
func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data])
func stopPolling()
}
// Default polling methods
extension SocketEnginePollable {
private func addHeaders(req: NSMutableURLRequest) {
private func addHeaders(for req: URLRequest) -> URLRequest {
var req = req
if cookies != nil {
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
req.allHTTPHeaderFields = headers
}
@ -56,10 +58,12 @@ extension SocketEnginePollable {
req.setValue(value, forHTTPHeaderField: headerName)
}
}
return req
}
func createRequestForPostWithPostWait() -> NSURLRequest {
defer { postWait.removeAll(keepCapacity: true) }
func createRequestForPostWithPostWait() -> URLRequest {
defer { postWait.removeAll(keepingCapacity: true) }
var postStr = ""
@ -71,49 +75,52 @@ extension SocketEnginePollable {
DefaultSocketLogger.Logger.log("Created POST string: %@", type: "SocketEnginePolling", args: postStr)
let req = NSMutableURLRequest(URL: urlPollingWithSid)
let postData = postStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
var req = URLRequest(url: urlPollingWithSid)
let postData = postStr.data(using: .utf8, allowLossyConversion: false)!
addHeaders(req)
req = addHeaders(for: req)
req.HTTPMethod = "POST"
req.httpMethod = "POST"
req.setValue("text/plain; charset=UTF-8", forHTTPHeaderField: "Content-Type")
req.HTTPBody = postData
req.setValue(String(postData.length), forHTTPHeaderField: "Content-Length")
req.httpBody = postData
req.setValue(String(postData.count), forHTTPHeaderField: "Content-Length")
return req
return req as URLRequest
}
public func doPoll() {
if websocket || waitingForPoll || !connected || closed { return }
if websocket || waitingForPoll || !connected || closed {
return
}
waitingForPoll = true
let req = NSMutableURLRequest(URL: urlPollingWithSid)
var req = URLRequest(url: urlPollingWithSid)
addHeaders(req)
doLongPoll(req)
req = addHeaders(for: req)
doLongPoll(for: req )
}
func doRequest(req: NSURLRequest, withCallback callback: (NSData?, NSURLResponse?, NSError?) -> Void) {
func doRequest(for req: URLRequest, callbackWith callback: @escaping (Data?, URLResponse?, Error?) -> Void) {
if !polling || closed || invalidated || fastUpgrade {
return
}
DefaultSocketLogger.Logger.log("Doing polling request", type: "SocketEnginePolling")
session?.dataTaskWithRequest(req, completionHandler: callback).resume()
session?.dataTask(with: req, completionHandler: callback).resume()
}
func doLongPoll(req: NSURLRequest) {
doRequest(req) {[weak self] data, res, err in
guard let this = self where this.polling else { return }
func doLongPoll(for req: URLRequest) {
doRequest(for: req) {[weak self] data, res, err in
guard let this = self, this.polling else { return }
if err != nil || data == nil {
DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: "SocketEnginePolling")
if this.polling {
this.didError(err?.localizedDescription ?? "Error")
this.didError(reason: err?.localizedDescription ?? "Error")
}
return
@ -121,8 +128,8 @@ extension SocketEnginePollable {
DefaultSocketLogger.Logger.log("Got polling response", type: "SocketEnginePolling")
if let str = String(data: data!, encoding: NSUTF8StringEncoding) {
dispatch_async(this.parseQueue) {
if let str = String(data: data!, encoding: String.Encoding.utf8) {
this.parseQueue.async {
this.parsePollingMessage(str)
}
}
@ -151,14 +158,14 @@ extension SocketEnginePollable {
DefaultSocketLogger.Logger.log("POSTing", type: "SocketEnginePolling")
doRequest(req) {[weak self] data, res, err in
doRequest(for: req) {[weak self] data, res, err in
guard let this = self else { return }
if err != nil {
DefaultSocketLogger.Logger.error(err?.localizedDescription ?? "Error", type: "SocketEnginePolling")
if this.polling {
this.didError(err?.localizedDescription ?? "Error")
this.didError(reason: err?.localizedDescription ?? "Error")
}
return
@ -166,7 +173,7 @@ extension SocketEnginePollable {
this.waitingForPost = false
dispatch_async(this.emitQueue) {
this.emitQueue.async {
if !this.fastUpgrade {
this.flushWaitingForPost()
this.doPoll()
@ -175,22 +182,18 @@ extension SocketEnginePollable {
}
}
func parsePollingMessage(str: String) {
func parsePollingMessage(_ str: String) {
guard str.characters.count != 1 else { return }
var reader = SocketStringReader(message: str)
while reader.hasNext {
if let n = Int(reader.readUntilStringOccurence(":")) {
let str = reader.read(n)
if let n = Int(reader.readUntilOccurence(of: ":")) {
let str = reader.read(count: n)
dispatch_async(handleQueue) {
self.parseEngineMessage(str, fromPolling: true)
}
handleQueue.async { self.parseEngineMessage(str, fromPolling: true) }
} else {
dispatch_async(handleQueue) {
self.parseEngineMessage(str, fromPolling: true)
}
handleQueue.async { self.parseEngineMessage(str, fromPolling: true) }
break
}
}
@ -198,7 +201,7 @@ extension SocketEnginePollable {
/// Send polling message.
/// Only call on emitQueue
public func sendPollMessage(message: String, withType type: SocketEnginePacketType, withData datas: [NSData]) {
public func sendPollMessage(_ message: String, withType type: SocketEnginePacketType, withData datas: [Data]) {
DefaultSocketLogger.Logger.log("Sending poll: %@ as type: %@", type: "SocketEnginePolling", args: message, type.rawValue)
let fixedMessage: String
@ -211,7 +214,7 @@ extension SocketEnginePollable {
postWait.append(String(type.rawValue) + fixedMessage)
for data in datas {
if case let .Right(bin) = createBinaryDataForSend(data) {
if case let .right(bin) = createBinaryDataForSend(using: data) {
postWait.append(bin)
}
}

View File

@ -29,79 +29,79 @@ import Foundation
weak var client: SocketEngineClient? { get set }
var closed: Bool { get }
var connected: Bool { get }
var connectParams: [String: AnyObject]? { get set }
var connectParams: [String: Any]? { get set }
var doubleEncodeUTF8: Bool { get }
var cookies: [NSHTTPCookie]? { get }
var cookies: [HTTPCookie]? { get }
var extraHeaders: [String: String]? { get }
var fastUpgrade: Bool { get }
var forcePolling: Bool { get }
var forceWebsockets: Bool { get }
var parseQueue: dispatch_queue_t! { get }
var parseQueue: DispatchQueue { get }
var polling: Bool { get }
var probing: Bool { get }
var emitQueue: dispatch_queue_t! { get }
var handleQueue: dispatch_queue_t! { get }
var emitQueue: DispatchQueue { get }
var handleQueue: DispatchQueue { get }
var sid: String { get }
var socketPath: String { get }
var urlPolling: NSURL { get }
var urlWebSocket: NSURL { get }
var urlPolling: URL { get }
var urlWebSocket: URL { get }
var websocket: Bool { get }
var ws: WebSocket? { get }
init(client: SocketEngineClient, url: NSURL, options: NSDictionary?)
init(client: SocketEngineClient, url: URL, options: NSDictionary?)
func connect()
func didError(error: String)
func didError(reason: String)
func disconnect(reason: String)
func doFastUpgrade()
func flushWaitingForPostToWebSocket()
func parseEngineData(data: NSData)
func parseEngineMessage(message: String, fromPolling: Bool)
func write(msg: String, withType type: SocketEnginePacketType, withData data: [NSData])
func parseEngineData(_ data: Data)
func parseEngineMessage(_ message: String, fromPolling: Bool)
func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data])
}
extension SocketEngineSpec {
var urlPollingWithSid: NSURL {
let com = NSURLComponents(URL: urlPolling, resolvingAgainstBaseURL: false)!
var urlPollingWithSid: URL {
var com = URLComponents(url: urlPolling, resolvingAgainstBaseURL: false)!
com.percentEncodedQuery = com.percentEncodedQuery! + "&sid=\(sid.urlEncode()!)"
return com.URL!
return com.url!
}
var urlWebSocketWithSid: NSURL {
let com = NSURLComponents(URL: urlWebSocket, resolvingAgainstBaseURL: false)!
var urlWebSocketWithSid: URL {
var com = URLComponents(url: urlWebSocket, resolvingAgainstBaseURL: false)!
com.percentEncodedQuery = com.percentEncodedQuery! + (sid == "" ? "" : "&sid=\(sid.urlEncode()!)")
return com.URL!
return com.url!
}
func createBinaryDataForSend(data: NSData) -> Either<NSData, String> {
func createBinaryDataForSend(using data: Data) -> Either<Data, String> {
if websocket {
var byteArray = [UInt8](count: 1, repeatedValue: 0x4)
var byteArray = [UInt8](repeating: 0x4, count: 1)
let mutData = NSMutableData(bytes: &byteArray, length: 1)
mutData.appendData(data)
mutData.append(data)
return .Left(mutData)
return .left(mutData as Data)
} else {
let str = "b4" + data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
let str = "b4" + data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
return .Right(str)
return .right(str)
}
}
func doubleEncodeUTF8(string: String) -> String {
if let latin1 = string.dataUsingEncoding(NSUTF8StringEncoding),
utf8 = NSString(data: latin1, encoding: NSISOLatin1StringEncoding) {
func doubleEncodeUTF8(_ string: String) -> String {
if let latin1 = string.data(using: String.Encoding.utf8),
let utf8 = NSString(data: latin1, encoding: String.Encoding.isoLatin1.rawValue) {
return utf8 as String
} else {
return string
}
}
func fixDoubleUTF8(string: String) -> String {
if let utf8 = string.dataUsingEncoding(NSISOLatin1StringEncoding),
latin1 = NSString(data: utf8, encoding: NSUTF8StringEncoding) {
func fixDoubleUTF8(_ string: String) -> String {
if let utf8 = string.data(using: String.Encoding.isoLatin1),
let latin1 = NSString(data: utf8, encoding: String.Encoding.utf8.rawValue) {
return latin1 as String
} else {
return string
@ -109,7 +109,7 @@ extension SocketEngineSpec {
}
/// Send an engine message (4)
func send(msg: String, withData datas: [NSData]) {
write(msg, withType: .Message, withData: datas)
func send(_ msg: String, withData datas: [Data]) {
write(msg, withType: .message, withData: datas)
}
}

View File

@ -27,36 +27,36 @@ import Foundation
/// Protocol that is used to implement socket.io WebSocket support
public protocol SocketEngineWebsocket : SocketEngineSpec, WebSocketDelegate {
func sendWebSocketMessage(str: String, withType type: SocketEnginePacketType, withData datas: [NSData])
func sendWebSocketMessage(_ str: String, withType type: SocketEnginePacketType, withData datas: [Data])
}
// WebSocket methods
extension SocketEngineWebsocket {
func probeWebSocket() {
if ws?.isConnected ?? false {
sendWebSocketMessage("probe", withType: .Ping, withData: [])
sendWebSocketMessage("probe", withType: .ping, withData: [])
}
}
/// Send message on WebSockets
/// Only call on emitQueue
public func sendWebSocketMessage(str: String, withType type: SocketEnginePacketType, withData datas: [NSData]) {
public func sendWebSocketMessage(_ str: String, withType type: SocketEnginePacketType, withData datas: [Data]) {
DefaultSocketLogger.Logger.log("Sending ws: %@ as type: %@", type: "SocketEngine", args: str, type.rawValue)
ws?.writeString("\(type.rawValue)\(str)")
for data in datas {
if case let .Left(bin) = createBinaryDataForSend(data) {
if case let .left(bin) = createBinaryDataForSend(using: data) {
ws?.writeData(bin)
}
}
}
public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
public func websocketDidReceiveMessage(_ socket: WebSocket, text: String) {
parseEngineMessage(text, fromPolling: false)
}
public func websocketDidReceiveData(socket: WebSocket, data: NSData) {
public func websocketDidReceiveData(_ socket: WebSocket, data: Data) {
parseEngineData(data)
}
}

View File

@ -26,10 +26,10 @@ import Foundation
struct SocketEventHandler {
let event: String
let id: NSUUID
let id: UUID
let callback: NormalCallback
func executeCallback(items: [AnyObject], withAck ack: Int, withSocket socket: SocketIOClient) {
func executeCallback(with items: [Any], withAck ack: Int, withSocket socket: SocketIOClient) {
callback(items, SocketAckEmitter(socket: socket, ackNum: ack))
}
}

View File

@ -24,66 +24,66 @@
import Foundation
enum JSONError : ErrorType {
enum JSONError : Error {
case notArray
case notNSDictionary
}
extension Array where Element: AnyObject {
func toJSON() throws -> NSData {
return try NSJSONSerialization.dataWithJSONObject(self as NSArray, options: NSJSONWritingOptions(rawValue: 0))
extension Array {
func toJSON() throws -> Data {
return try JSONSerialization.data(withJSONObject: self, options: JSONSerialization.WritingOptions(rawValue: 0))
}
}
extension NSCharacterSet {
static var allowedURLCharacterSet: NSCharacterSet {
return NSCharacterSet(charactersInString: "!*'();:@&=+$,/?%#[]\" {}").invertedSet
extension CharacterSet {
static var allowedURLCharacterSet: CharacterSet {
return CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[]\" {}").inverted
}
}
extension NSDictionary {
private static func keyValueToSocketIOClientOption(key: String, value: AnyObject) -> SocketIOClientOption? {
private static func keyValueToSocketIOClientOption(key: String, value: Any) -> SocketIOClientOption? {
switch (key, value) {
case let ("connectParams", params as [String: AnyObject]):
return .ConnectParams(params)
case let ("cookies", cookies as [NSHTTPCookie]):
return .Cookies(cookies)
case let ("connectParams", params as [String: Any]):
return .connectParams(params)
case let ("cookies", cookies as [HTTPCookie]):
return .cookies(cookies)
case let ("doubleEncodeUTF8", encode as Bool):
return .DoubleEncodeUTF8(encode)
return .doubleEncodeUTF8(encode)
case let ("extraHeaders", headers as [String: String]):
return .ExtraHeaders(headers)
return .extraHeaders(headers)
case let ("forceNew", force as Bool):
return .ForceNew(force)
return .forceNew(force)
case let ("forcePolling", force as Bool):
return .ForcePolling(force)
return .forcePolling(force)
case let ("forceWebsockets", force as Bool):
return .ForceWebsockets(force)
case let ("handleQueue", queue as dispatch_queue_t):
return .HandleQueue(queue)
return .forceWebsockets(force)
case let ("handleQueue", queue as DispatchQueue):
return .handleQueue(queue)
case let ("log", log as Bool):
return .Log(log)
return .log(log)
case let ("logger", logger as SocketLogger):
return .Logger(logger)
return .logger(logger)
case let ("nsp", nsp as String):
return .Nsp(nsp)
return .nsp(nsp)
case let ("path", path as String):
return .Path(path)
return .path(path)
case let ("reconnects", reconnects as Bool):
return .Reconnects(reconnects)
return .reconnects(reconnects)
case let ("reconnectAttempts", attempts as Int):
return .ReconnectAttempts(attempts)
return .reconnectAttempts(attempts)
case let ("reconnectWait", wait as Int):
return .ReconnectWait(wait)
return .reconnectWait(wait)
case let ("secure", secure as Bool):
return .Secure(secure)
return .secure(secure)
case let ("security", security as SSLSecurity):
return .Security(security)
return .security(security)
case let ("selfSigned", selfSigned as Bool):
return .SelfSigned(selfSigned)
case let ("sessionDelegate", delegate as NSURLSessionDelegate):
return .SessionDelegate(delegate)
return .selfSigned(selfSigned)
case let ("sessionDelegate", delegate as URLSessionDelegate):
return .sessionDelegate(delegate)
case let ("voipEnabled", enable as Bool):
return .VoipEnabled(enable)
return .voipEnabled(enable)
default:
return nil
}
@ -93,7 +93,7 @@ extension NSDictionary {
var options = [] as SocketIOClientConfiguration
for (rawKey, value) in self {
if let key = rawKey as? String, opt = NSDictionary.keyValueToSocketIOClientOption(key, value: value) {
if let key = rawKey as? String, let opt = NSDictionary.keyValueToSocketIOClientOption(key: key, value: value) {
options.insert(opt)
}
}
@ -103,9 +103,9 @@ extension NSDictionary {
}
extension String {
func toArray() throws -> [AnyObject] {
guard let stringData = dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) else { return [] }
guard let array = try NSJSONSerialization.JSONObjectWithData(stringData, options: .MutableContainers) as? [AnyObject] else {
func toArray() throws -> [Any] {
guard let stringData = data(using: .utf8, allowLossyConversion: false) else { return [] }
guard let array = try JSONSerialization.jsonObject(with: stringData, options: .mutableContainers) as? [Any] else {
throw JSONError.notArray
}
@ -113,8 +113,8 @@ extension String {
}
func toNSDictionary() throws -> NSDictionary {
guard let binData = dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) else { return [:] }
guard let json = try NSJSONSerialization.JSONObjectWithData(binData, options: .AllowFragments) as? NSDictionary else {
guard let binData = data(using: .utf8, allowLossyConversion: false) else { return [:] }
guard let json = try JSONSerialization.jsonObject(with: binData, options: .allowFragments) as? NSDictionary else {
throw JSONError.notNSDictionary
}
@ -122,6 +122,6 @@ extension String {
}
func urlEncode() -> String? {
return stringByAddingPercentEncodingWithAllowedCharacters(.allowedURLCharacterSet)
return addingPercentEncoding(withAllowedCharacters: .allowedURLCharacterSet)
}
}

View File

@ -25,13 +25,13 @@
import Foundation
public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable {
public let socketURL: NSURL
public let socketURL: URL
public private(set) var engine: SocketEngineSpec?
public private(set) var status = SocketIOClientStatus.NotConnected {
public private(set) var status = SocketIOClientStatus.notConnected {
didSet {
switch status {
case .Connected:
case .connected:
reconnecting = false
currentReconnectAttempt = 0
default:
@ -46,10 +46,10 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
public var reconnects = true
public var reconnectWait = 10
private let ackQueue = dispatch_queue_create("com.socketio.ackQueue", DISPATCH_QUEUE_SERIAL)
private let emitQueue = dispatch_queue_create("com.socketio.emitQueue", DISPATCH_QUEUE_SERIAL)
private let ackQueue = DispatchQueue(label: "com.socketio.ackQueue", attributes: [])
private let emitQueue = DispatchQueue(label: "com.socketio.emitQueue", attributes: [])
private let logType = "SocketIOClient"
private let parseQueue = dispatch_queue_create("com.socketio.parseQueue", DISPATCH_QUEUE_SERIAL)
private let parseQueue = DispatchQueue(label: "com.socketio.parseQueue", attributes: [])
private var anyHandler: ((SocketAnyEvent) -> Void)?
private var currentReconnectAttempt = 0
@ -58,7 +58,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
private var reconnecting = false
private(set) var currentAck = -1
private(set) var handleQueue = dispatch_get_main_queue()
private(set) var handleQueue = DispatchQueue.main
private(set) var reconnectAttempts = -1
var waitingPackets = [SocketPacket]()
@ -67,39 +67,39 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
return nsp + "#" + (engine?.sid ?? "")
}
/// Type safe way to create a new SocketIOClient. config can be omitted
public init(socketURL: NSURL, config: SocketIOClientConfiguration = []) {
/// Type safe way to create a new SocketIOClient. opts can be omitted
public init(socketURL: URL, config: SocketIOClientConfiguration = []) {
self.config = config
self.socketURL = socketURL
if socketURL.absoluteString.hasPrefix("https://") {
self.config.insert(.Secure(true))
self.config.insert(.secure(true))
}
for option in config {
switch option {
case let .Reconnects(reconnects):
case let .reconnects(reconnects):
self.reconnects = reconnects
case let .ReconnectAttempts(attempts):
case let .reconnectAttempts(attempts):
reconnectAttempts = attempts
case let .ReconnectWait(wait):
case let .reconnectWait(wait):
reconnectWait = abs(wait)
case let .Nsp(nsp):
case let .nsp(nsp):
self.nsp = nsp
case let .Log(log):
case let .log(log):
DefaultSocketLogger.Logger.log = log
case let .Logger(logger):
case let .logger(logger):
DefaultSocketLogger.Logger = logger
case let .HandleQueue(queue):
case let .handleQueue(queue):
handleQueue = queue
case let .ForceNew(force):
case let .forceNew(force):
forceNew = force
default:
continue
}
}
self.config.insert(.Path("/socket.io/"), replacing: false)
self.config.insert(.path("/socket.io/"), replacing: false)
super.init()
}
@ -107,16 +107,16 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
/// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.
/// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set<SocketIOClientOption>)`
public convenience init(socketURL: NSURL, config: NSDictionary?) {
self.init(socketURL: socketURL, config: config?.toSocketConfiguration() ?? [])
self.init(socketURL: socketURL as URL, config: config?.toSocketConfiguration() ?? [])
}
deinit {
DefaultSocketLogger.Logger.log("Client is being released", type: logType)
engine?.disconnect("Client Deinit")
engine?.disconnect(reason: "Client Deinit")
}
private func addEngine() -> SocketEngineSpec {
DefaultSocketLogger.Logger.log("Adding engine", type: logType)
DefaultSocketLogger.Logger.log("Adding engine", type: logType, args: "")
engine = SocketEngine(client: self, url: socketURL, config: config)
@ -125,19 +125,20 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
/// Connect to the server.
public func connect() {
connect(timeoutAfter: 0, withTimeoutHandler: nil)
connect(timeoutAfter: 0, withHandler: nil)
}
/// Connect to the server. If we aren't connected after timeoutAfter, call handler
public func connect(timeoutAfter timeoutAfter: Int, withTimeoutHandler handler: (() -> Void)?) {
/// Connect to the server. If we aren't connected after timeoutAfter, call withHandler
/// 0 Never times out
public func connect(timeoutAfter: Int, withHandler handler: (() -> Void)?) {
assert(timeoutAfter >= 0, "Invalid timeout: \(timeoutAfter)")
guard status != .Connected else {
guard status != .connected else {
DefaultSocketLogger.Logger.log("Tried connecting on an already connected socket", type: logType)
return
}
status = .Connecting
status = .connecting
if engine == nil || forceNew {
addEngine().connect()
@ -147,25 +148,25 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
guard timeoutAfter != 0 else { return }
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeoutAfter) * Int64(NSEC_PER_SEC))
let time = DispatchTime.now() + Double(Int64(timeoutAfter) * Int64(NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
dispatch_after(time, handleQueue) {[weak self] in
guard let this = self where this.status != .Connected && this.status != .Disconnected else { return }
handleQueue.asyncAfter(deadline: time) {[weak self] in
guard let this = self, this.status != .connected && this.status != .disconnected else { return }
this.status = .Disconnected
this.engine?.disconnect("Connect timeout")
this.status = .disconnected
this.engine?.disconnect(reason: "Connect timeout")
handler?()
}
}
private func createOnAck(items: [AnyObject]) -> OnAckCallback {
private func createOnAck(_ items: [Any]) -> OnAckCallback {
currentAck += 1
return {[weak self, ack = currentAck] timeout, callback in
guard let this = self else { return }
dispatch_sync(this.ackQueue) {
this.ackQueue.sync() {
this.ackHandlers.addAck(ack, callback: callback)
}
@ -173,9 +174,9 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
this._emit(items, ack: ack)
if timeout != 0 {
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * NSEC_PER_SEC))
let time = DispatchTime.now() + Double(Int64(timeout * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
dispatch_after(time, this.ackQueue) {
this.handleQueue.asyncAfter(deadline: time) {
this.ackHandlers.timeoutAck(ack, onQueue: this.handleQueue)
}
}
@ -184,7 +185,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
func didConnect() {
DefaultSocketLogger.Logger.log("Socket connected", type: logType)
status = .Connected
status = .connected
// Don't handle as internal because something crazy could happen where
// we disconnect before it's handled
@ -192,15 +193,15 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
}
func didDisconnect(reason: String) {
guard status != .Disconnected else { return }
guard status != .disconnected else { return }
DefaultSocketLogger.Logger.log("Disconnected: %@", type: logType, args: reason)
reconnecting = false
status = .Disconnected
status = .disconnected
// Make sure the engine is actually dead.
engine?.disconnect(reason)
engine?.disconnect(reason: reason)
handleEvent("disconnect", data: [reason], isInternalMessage: true)
}
@ -208,17 +209,17 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
public func disconnect() {
DefaultSocketLogger.Logger.log("Closing socket", type: logType)
didDisconnect("Disconnect")
didDisconnect(reason: "Disconnect")
}
/// Send a message to the server
public func emit(event: String, _ items: AnyObject...) {
emit(event, withItems: items)
public func emit(_ event: String, _ items: SocketData...) {
emit(event, with: items)
}
/// Same as emit, but meant for Objective-C
public func emit(event: String, withItems items: [AnyObject]) {
guard status == .Connected else {
public func emit(_ event: String, with items: [Any]) {
guard status == .connected else {
handleEvent("error", data: ["Tried emitting \(event) when not connected"], isInternalMessage: true)
return
}
@ -228,18 +229,18 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
/// Sends a message to the server, requesting an ack. Use the onAck method of SocketAckHandler to add
/// an ack.
public func emitWithAck(event: String, _ items: AnyObject...) -> OnAckCallback {
return emitWithAck(event, withItems: items)
public func emitWithAck(_ event: String, _ items: SocketData...) -> OnAckCallback {
return emitWithAck(event, with: items)
}
/// Same as emitWithAck, but for Objective-C
public func emitWithAck(event: String, withItems items: [AnyObject]) -> OnAckCallback {
public func emitWithAck(_ event: String, with items: [Any]) -> OnAckCallback {
return createOnAck([event] + items)
}
private func _emit(data: [AnyObject], ack: Int? = nil) {
dispatch_async(emitQueue) {
guard self.status == .Connected else {
private func _emit(_ data: [Any], ack: Int? = nil) {
emitQueue.async {
guard self.status == .connected else {
self.handleEvent("error", data: ["Tried emitting when not connected"], isInternalMessage: true)
return
}
@ -254,31 +255,31 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
}
// If the server wants to know that the client received data
func emitAck(ack: Int, withItems items: [AnyObject]) {
dispatch_async(emitQueue) {
if self.status == .Connected {
let packet = SocketPacket.packetFromEmit(items, id: ack ?? -1, nsp: self.nsp, ack: true)
let str = packet.packetString
DefaultSocketLogger.Logger.log("Emitting Ack: %@", type: self.logType, args: str)
self.engine?.send(str, withData: packet.binary)
}
func emitAck(_ ack: Int, with items: [Any]) {
emitQueue.async {
guard self.status == .connected else { return }
let packet = SocketPacket.packetFromEmit(items, id: ack, nsp: self.nsp, ack: true)
let str = packet.packetString
DefaultSocketLogger.Logger.log("Emitting Ack: %@", type: self.logType, args: str)
self.engine?.send(str, withData: packet.binary)
}
}
public func engineDidClose(reason: String) {
waitingPackets.removeAll()
if status != .Disconnected {
status = .NotConnected
if status != .disconnected {
status = .notConnected
}
if status == .Disconnected || !reconnects {
didDisconnect(reason)
if status == .disconnected || !reconnects {
didDisconnect(reason: reason)
} else if !reconnecting {
reconnecting = true
tryReconnectWithReason(reason)
tryReconnect(reason: reason)
}
}
@ -294,27 +295,27 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
}
// Called when the socket gets an ack for something it sent
func handleAck(ack: Int, data: [AnyObject]) {
guard status == .Connected else { return }
func handleAck(_ ack: Int, data: [Any]) {
guard status == .connected else { return }
DefaultSocketLogger.Logger.log("Handling ack: %@ with data: %@", type: logType, args: ack, data ?? "")
DefaultSocketLogger.Logger.log("Handling ack: %@ with data: %@", type: logType, args: ack, data)
dispatch_async(ackQueue) {
self.ackHandlers.executeAck(ack, items: data, onQueue: self.handleQueue)
handleQueue.async() {
self.ackHandlers.executeAck(ack, with: data, onQueue: self.handleQueue)
}
}
/// Causes an event to be handled. Only use if you know what you're doing.
public func handleEvent(event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int = -1) {
guard status == .Connected || isInternalMessage else { return }
public func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int = -1) {
guard status == .connected || isInternalMessage else { return }
DefaultSocketLogger.Logger.log("Handling event: %@ with data: %@", type: logType, args: event, data ?? "")
DefaultSocketLogger.Logger.log("Handling event: %@ with data: %@", type: logType, args: event, data)
dispatch_async(handleQueue) {
handleQueue.async {
self.anyHandler?(SocketAnyEvent(event: event, items: data))
for handler in self.handlers where handler.event == event {
handler.executeCallback(data, withAck: ack, withSocket: self)
handler.executeCallback(with: data, withAck: ack, withSocket: self)
}
}
}
@ -328,7 +329,7 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
}
/// Joins namespace
public func joinNamespace(namespace: String) {
public func joinNamespace(_ namespace: String) {
nsp = namespace
if nsp != "/" {
@ -338,14 +339,14 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
}
/// Removes handler(s) based on name
public func off(event: String) {
public func off(_ event: String) {
DefaultSocketLogger.Logger.log("Removing handler for event: %@", type: logType, args: event)
handlers = handlers.filter({ $0.event != event })
}
/// Removes a handler with the specified UUID gotten from an `on` or `once`
public func off(id id: NSUUID) {
public func off(id: UUID) {
DefaultSocketLogger.Logger.log("Removing handler with id: %@", type: logType, args: id)
handlers = handlers.filter({ $0.id != id })
@ -353,10 +354,11 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
/// Adds a handler for an event.
/// Returns: A unique id for the handler
public func on(event: String, callback: NormalCallback) -> NSUUID {
@discardableResult
public func on(_ event: String, callback: @escaping NormalCallback) -> UUID {
DefaultSocketLogger.Logger.log("Adding handler for event: %@", type: logType, args: event)
let handler = SocketEventHandler(event: event, id: NSUUID(), callback: callback)
let handler = SocketEventHandler(event: event, id: UUID(), callback: callback)
handlers.append(handler)
return handler.id
@ -364,10 +366,11 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
/// Adds a single-use handler for an event.
/// Returns: A unique id for the handler
public func once(event: String, callback: NormalCallback) -> NSUUID {
@discardableResult
public func once(_ event: String, callback: @escaping NormalCallback) -> UUID {
DefaultSocketLogger.Logger.log("Adding once handler for event: %@", type: logType, args: event)
let id = NSUUID()
let id = UUID()
let handler = SocketEventHandler(event: event, id: id) {[weak self] data, ack in
guard let this = self else { return }
@ -381,38 +384,34 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
}
/// Adds a handler that will be called on every event.
public func onAny(handler: (SocketAnyEvent) -> Void) {
public func onAny(_ handler: @escaping (SocketAnyEvent) -> Void) {
anyHandler = handler
}
public func parseEngineMessage(msg: String) {
public func parseEngineMessage(_ msg: String) {
DefaultSocketLogger.Logger.log("Should parse message: %@", type: "SocketIOClient", args: msg)
dispatch_async(parseQueue) {
self.parseSocketMessage(msg)
}
parseQueue.async { self.parseSocketMessage(msg) }
}
public func parseEngineBinaryData(data: NSData) {
dispatch_async(parseQueue) {
self.parseBinaryData(data)
}
public func parseEngineBinaryData(_ data: Data) {
parseQueue.async { self.parseBinaryData(data) }
}
/// Tries to reconnect to the server.
public func reconnect() {
guard !reconnecting else { return }
engine?.disconnect("manual reconnect")
engine?.disconnect(reason: "manual reconnect")
}
/// Removes all handlers.
/// Can be used after disconnecting to break any potential remaining retain cycles.
public func removeAllHandlers() {
handlers.removeAll(keepCapacity: false)
handlers.removeAll(keepingCapacity: false)
}
private func tryReconnectWithReason(reason: String) {
private func tryReconnect(reason: String) {
guard reconnecting else { return }
DefaultSocketLogger.Logger.log("Starting reconnect", type: logType)
@ -425,37 +424,35 @@ public final class SocketIOClient : NSObject, SocketEngineClient, SocketParsable
guard reconnecting else { return }
if reconnectAttempts != -1 && currentReconnectAttempt + 1 > reconnectAttempts || !reconnects {
return didDisconnect("Reconnect Failed")
return didDisconnect(reason: "Reconnect Failed")
}
DefaultSocketLogger.Logger.log("Trying to reconnect", type: logType)
handleEvent("reconnectAttempt", data: [reconnectAttempts - currentReconnectAttempt],
isInternalMessage: true)
handleEvent("reconnectAttempt", data: [(reconnectAttempts - currentReconnectAttempt)], isInternalMessage: true)
currentReconnectAttempt += 1
connect()
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(UInt64(reconnectWait) * NSEC_PER_SEC))
let deadline = DispatchTime.now() + Double(Int64(UInt64(reconnectWait) * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
dispatch_after(time, dispatch_get_main_queue(), _tryReconnect)
DispatchQueue.main.asyncAfter(deadline: deadline, execute: _tryReconnect)
}
}
// Test extensions
extension SocketIOClient {
// Test properties
var testHandlers: [SocketEventHandler] {
return handlers
}
func setTestable() {
status = .Connected
status = .connected
}
func setTestEngine(engine: SocketEngineSpec?) {
func setTestEngine(_ engine: SocketEngineSpec?) {
self.engine = engine
}
func emitTest(event: String, _ data: AnyObject...) {
func emitTest(event: String, _ data: Any...) {
_emit([event] + data)
}
}

View File

@ -22,7 +22,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct SocketIOClientConfiguration : ArrayLiteralConvertible, CollectionType, MutableCollectionType {
public struct SocketIOClientConfiguration : ExpressibleByArrayLiteral, Collection, MutableCollection {
public typealias Element = SocketIOClientOption
public typealias Index = Array<SocketIOClientOption>.Index
public typealias Generator = Array<SocketIOClientOption>.Generator
@ -42,7 +42,7 @@ public struct SocketIOClientConfiguration : ArrayLiteralConvertible, CollectionT
return backingArray.isEmpty
}
public var count: Index.Distance {
public var count: Index.Stride {
return backingArray.count
}
@ -75,10 +75,14 @@ public struct SocketIOClientConfiguration : ArrayLiteralConvertible, CollectionT
}
public func generate() -> Generator {
return backingArray.generate()
return backingArray.makeIterator()
}
public mutating func insert(element: Element, replacing replace: Bool = true) {
public func index(after i: Index) -> Index {
return backingArray.index(after: i)
}
public mutating func insert(_ element: Element, replacing replace: Bool = true) {
for i in 0..<backingArray.count where backingArray[i] == element {
guard replace else { return }
@ -90,18 +94,15 @@ public struct SocketIOClientConfiguration : ArrayLiteralConvertible, CollectionT
backingArray.append(element)
}
@warn_unused_result
public func prefixUpTo(end: Index) -> SubSequence {
return backingArray.prefixUpTo(end)
public func prefix(upTo end: Index) -> SubSequence {
return backingArray.prefix(upTo: end)
}
@warn_unused_result
public func prefixThrough(position: Index) -> SubSequence {
return backingArray.prefixThrough(position)
public func prefix(through position: Index) -> SubSequence {
return backingArray.prefix(through: position)
}
@warn_unused_result
public func suffixFrom(start: Index) -> SubSequence {
return backingArray.suffixFrom(start)
public func suffix(from start: Index) -> SubSequence {
return backingArray.suffix(from: start)
}
}

View File

@ -25,123 +25,123 @@
import Foundation
protocol ClientOption : CustomStringConvertible, Equatable {
func getSocketIOOptionValue() -> AnyObject
func getSocketIOOptionValue() -> Any
}
public enum SocketIOClientOption : ClientOption {
case ConnectParams([String: AnyObject])
case Cookies([NSHTTPCookie])
case DoubleEncodeUTF8(Bool)
case ExtraHeaders([String: String])
case ForceNew(Bool)
case ForcePolling(Bool)
case ForceWebsockets(Bool)
case HandleQueue(dispatch_queue_t)
case Log(Bool)
case Logger(SocketLogger)
case Nsp(String)
case Path(String)
case Reconnects(Bool)
case ReconnectAttempts(Int)
case ReconnectWait(Int)
case Secure(Bool)
case Security(SSLSecurity)
case SelfSigned(Bool)
case SessionDelegate(NSURLSessionDelegate)
case VoipEnabled(Bool)
case connectParams([String: Any])
case cookies([HTTPCookie])
case doubleEncodeUTF8(Bool)
case extraHeaders([String: String])
case forceNew(Bool)
case forcePolling(Bool)
case forceWebsockets(Bool)
case handleQueue(DispatchQueue)
case log(Bool)
case logger(SocketLogger)
case nsp(String)
case path(String)
case reconnects(Bool)
case reconnectAttempts(Int)
case reconnectWait(Int)
case secure(Bool)
case security(SSLSecurity)
case selfSigned(Bool)
case sessionDelegate(URLSessionDelegate)
case voipEnabled(Bool)
public var description: String {
let description: String
switch self {
case .ConnectParams:
case .connectParams:
description = "connectParams"
case .Cookies:
case .cookies:
description = "cookies"
case .DoubleEncodeUTF8:
case .doubleEncodeUTF8:
description = "doubleEncodeUTF8"
case .ExtraHeaders:
case .extraHeaders:
description = "extraHeaders"
case .ForceNew:
case .forceNew:
description = "forceNew"
case .ForcePolling:
case .forcePolling:
description = "forcePolling"
case .ForceWebsockets:
case .forceWebsockets:
description = "forceWebsockets"
case .HandleQueue:
case .handleQueue:
description = "handleQueue"
case .Log:
case .log:
description = "log"
case .Logger:
case .logger:
description = "logger"
case .Nsp:
case .nsp:
description = "nsp"
case .Path:
case .path:
description = "path"
case .Reconnects:
case .reconnects:
description = "reconnects"
case .ReconnectAttempts:
case .reconnectAttempts:
description = "reconnectAttempts"
case .ReconnectWait:
case .reconnectWait:
description = "reconnectWait"
case .Secure:
case .secure:
description = "secure"
case .Security:
description = "security"
case .SelfSigned:
case .selfSigned:
description = "selfSigned"
case .SessionDelegate:
case .security:
description = "security"
case .sessionDelegate:
description = "sessionDelegate"
case .VoipEnabled:
case .voipEnabled:
description = "voipEnabled"
}
return description
}
func getSocketIOOptionValue() -> AnyObject {
let value: AnyObject
func getSocketIOOptionValue() -> Any {
let value: Any
switch self {
case let .ConnectParams(params):
case let .connectParams(params):
value = params
case let .Cookies(cookies):
case let .cookies(cookies):
value = cookies
case let .DoubleEncodeUTF8(encode):
case let .doubleEncodeUTF8(encode):
value = encode
case let .ExtraHeaders(headers):
case let .extraHeaders(headers):
value = headers
case let .ForceNew(force):
case let .forceNew(force):
value = force
case let .ForcePolling(force):
case let .forcePolling(force):
value = force
case let .ForceWebsockets(force):
case let .forceWebsockets(force):
value = force
case let .HandleQueue(queue):
case let .handleQueue(queue):
value = queue
case let .Log(log):
case let .log(log):
value = log
case let .Logger(logger):
case let .logger(logger):
value = logger
case let .Nsp(nsp):
case let .nsp(nsp):
value = nsp
case let .Path(path):
case let .path(path):
value = path
case let .Reconnects(reconnects):
case let .reconnects(reconnects):
value = reconnects
case let .ReconnectAttempts(attempts):
case let .reconnectAttempts(attempts):
value = attempts
case let .ReconnectWait(wait):
case let .reconnectWait(wait):
value = wait
case let .Secure(secure):
case let .secure(secure):
value = secure
case let .Security(security):
case let .security(security):
value = security
case let .SelfSigned(signed):
case let .selfSigned(signed):
value = signed
case let .SessionDelegate(delegate):
case let .sessionDelegate(delegate):
value = delegate
case let .VoipEnabled(enabled):
case let .voipEnabled(enabled):
value = enabled
}

View File

@ -29,9 +29,9 @@ protocol SocketIOClientSpec : class {
func didConnect()
func didDisconnect(reason: String)
func didError(reason: String)
func handleAck(ack: Int, data: [AnyObject])
func handleEvent(event: String, data: [AnyObject], isInternalMessage: Bool, withAck ack: Int)
func joinNamespace(namespace: String)
func handleAck(_ ack: Int, data: [Any])
func handleEvent(_ event: String, data: [Any], isInternalMessage: Bool, withAck ack: Int)
func joinNamespace(_ namespace: String)
}
extension SocketIOClientSpec {

View File

@ -28,5 +28,5 @@ import Foundation
///
/// **Disconnected**: connected before
@objc public enum SocketIOClientStatus : Int {
case NotConnected, Disconnected, Connecting, Connected
case notConnected, disconnected, connecting, connected
}

View File

@ -29,28 +29,28 @@ public protocol SocketLogger : class {
var log: Bool { get set }
/// Normal log messages
func log(message: String, type: String, args: AnyObject...)
func log(_ message: String, type: String, args: Any...)
/// Error Messages
func error(message: String, type: String, args: AnyObject...)
func error(_ message: String, type: String, args: Any...)
}
public extension SocketLogger {
func log(message: String, type: String, args: AnyObject...) {
func log(_ message: String, type: String, args: Any...) {
abstractLog("LOG", message: message, type: type, args: args)
}
func error(message: String, type: String, args: AnyObject...) {
func error(_ message: String, type: String, args: Any...) {
abstractLog("ERROR", message: message, type: type, args: args)
}
private func abstractLog(logType: String, message: String, type: String, args: [AnyObject]) {
private func abstractLog(_ logType: String, message: String, type: String, args: [Any]) {
guard log else { return }
let newArgs = args.map({arg -> CVarArgType in String(arg)})
let replaced = String(format: message, arguments: newArgs)
let newArgs = args.map({arg -> CVarArg in String(describing: arg)})
let messageFormat = String(format: message, arguments: newArgs)
NSLog("%@ %@: %@", logType, type, replaced)
NSLog("\(logType) \(type): %@", messageFormat)
}
}

View File

@ -27,7 +27,7 @@ import Foundation
struct SocketPacket {
enum PacketType: Int {
case Connect, Disconnect, Event, Ack, Error, BinaryEvent, BinaryAck
case connect, disconnect, event, ack, error, binaryEvent, binaryAck
}
private let placeholders: Int
@ -38,11 +38,10 @@ struct SocketPacket {
let id: Int
let type: PacketType
var binary: [NSData]
var data: [AnyObject]
var args: [AnyObject] {
if type == .Event || type == .BinaryEvent && data.count != 0 {
var binary: [Data]
var data: [Any]
var args: [Any] {
if type == .event || type == .binaryEvent && data.count != 0 {
return Array(data.dropFirst())
} else {
return data
@ -51,19 +50,19 @@ struct SocketPacket {
var description: String {
return "SocketPacket {type: \(String(type.rawValue)); data: " +
"\(String(data)); id: \(id); placeholders: \(placeholders); nsp: \(nsp)}"
"\(String(describing: data)); id: \(id); placeholders: \(placeholders); nsp: \(nsp)}"
}
var event: String {
return String(data[0])
return String(describing: data[0])
}
var packetString: String {
return createPacketString()
}
init(type: PacketType, data: [AnyObject] = [AnyObject](), id: Int = -1,
nsp: String, placeholders: Int = 0, binary: [NSData] = [NSData]()) {
init(type: PacketType, data: [Any] = [Any](), id: Int = -1, nsp: String, placeholders: Int = 0,
binary: [Data] = [Data]()) {
self.data = data
self.id = id
self.nsp = nsp
@ -72,7 +71,7 @@ struct SocketPacket {
self.binary = binary
}
mutating func addData(data: NSData) -> Bool {
mutating func addData(_ data: Data) -> Bool {
if placeholders == binary.count {
return true
}
@ -87,7 +86,7 @@ struct SocketPacket {
}
}
private func completeMessage(message: String) -> String {
private func completeMessage(_ message: String) -> String {
let restOfMessage: String
if data.count == 0 {
@ -96,7 +95,7 @@ struct SocketPacket {
do {
let jsonSend = try data.toJSON()
guard let jsonString = String(data: jsonSend, encoding: NSUTF8StringEncoding) else { return message + "[]" }
guard let jsonString = String(data: jsonSend, encoding: .utf8) else { return message + "[]" }
restOfMessage = jsonString
} catch {
@ -112,9 +111,9 @@ struct SocketPacket {
private func createPacketString() -> String {
let typeString = String(type.rawValue)
// Binary count?
let binaryCountString = typeString + (type == .BinaryEvent || type == .BinaryAck ? String(binary.count) + "-" : "")
let binaryCountString = typeString + (type == .binaryEvent || type == .binaryAck ? "\(String(binary.count))-" : "")
// Namespace?
let nspString = binaryCountString + (nsp != "/" ? nsp + "," : "")
let nspString = binaryCountString + (nsp != "/" ? "\(nsp)," : "")
// Ack number?
let idString = nspString + (id != -1 ? String(id) : "")
@ -132,18 +131,21 @@ struct SocketPacket {
// If object is a collection it will recurse
// Returns the object if it is not a placeholder or the corresponding
// binary data
private func _fillInPlaceholders(object: AnyObject) -> AnyObject {
private func _fillInPlaceholders(_ object: Any) -> Any {
switch object {
case let dict as NSDictionary:
case let dict as [String: Any]:
if dict["_placeholder"] as? Bool ?? false {
return binary[dict["num"] as! Int]
} else {
return dict.reduce(NSMutableDictionary(), combine: {cur, keyValue in
cur[keyValue.0 as! NSCopying] = _fillInPlaceholders(keyValue.1)
return dict.reduce([String: Any](), {cur, keyValue in
var cur = cur
cur[keyValue.0] = _fillInPlaceholders(keyValue.1)
return cur
})
}
case let arr as [AnyObject]:
case let arr as [Any]:
return arr.map(_fillInPlaceholders)
default:
return object
@ -152,22 +154,22 @@ struct SocketPacket {
}
extension SocketPacket {
private static func findType(binCount: Int, ack: Bool) -> PacketType {
private static func findType(_ binCount: Int, ack: Bool) -> PacketType {
switch binCount {
case 0 where !ack:
return .Event
return .event
case 0 where ack:
return .Ack
return .ack
case _ where !ack:
return .BinaryEvent
return .binaryEvent
case _ where ack:
return .BinaryAck
return .binaryAck
default:
return .Error
return .error
}
}
static func packetFromEmit(items: [AnyObject], id: Int, nsp: String, ack: Bool) -> SocketPacket {
static func packetFromEmit(_ items: [Any], id: Int, nsp: String, ack: Bool) -> SocketPacket {
let (parsedData, binary) = deconstructData(items)
let packet = SocketPacket(type: findType(binary.count, ack: ack), data: parsedData,
id: id, nsp: nsp, binary: binary)
@ -178,19 +180,23 @@ extension SocketPacket {
private extension SocketPacket {
// Recursive function that looks for NSData in collections
static func shred(data: AnyObject, inout binary: [NSData]) -> AnyObject {
let placeholder = ["_placeholder": true, "num": binary.count]
static func shred(_ data: Any, binary: inout [Data]) -> Any {
let placeholder = ["_placeholder": true, "num": binary.count] as [String : Any]
switch data {
case let bin as NSData:
case let bin as Data:
binary.append(bin)
return placeholder
case let arr as [AnyObject]:
case let arr as [Any]:
return arr.map({shred($0, binary: &binary)})
case let dict as NSDictionary:
return dict.reduce(NSMutableDictionary(), combine: {cur, keyValue in
cur[keyValue.0 as! NSCopying] = shred(keyValue.1, binary: &binary)
return cur
case let dict as [String: Any]:
return dict.reduce([String: Any](), {cur, keyValue in
var mutCur = cur
mutCur[keyValue.0] = shred(keyValue.1, binary: &binary)
return mutCur
})
default:
return data
@ -199,8 +205,8 @@ private extension SocketPacket {
// Removes binary data from emit data
// Returns a type containing the de-binaryed data and the binary
static func deconstructData(data: [AnyObject]) -> ([AnyObject], [NSData]) {
var binary = [NSData]()
static func deconstructData(_ data: [Any]) -> ([Any], [Data]) {
var binary = [Data]()
return (data.map({shred($0, binary: &binary)}), binary)
}

View File

@ -23,16 +23,16 @@
import Foundation
protocol SocketParsable : SocketIOClientSpec {
func parseBinaryData(data: NSData)
func parseSocketMessage(message: String)
func parseBinaryData(_ data: Data)
func parseSocketMessage(_ message: String)
}
extension SocketParsable {
private func isCorrectNamespace(nsp: String) -> Bool {
private func isCorrectNamespace(_ nsp: String) -> Bool {
return nsp == self.nsp
}
private func handleConnect(packetNamespace: String) {
private func handleConnect(_ packetNamespace: String) {
if packetNamespace == "/" && nsp != "/" {
joinNamespace(nsp)
} else {
@ -40,21 +40,21 @@ extension SocketParsable {
}
}
private func handlePacket(pack: SocketPacket) {
private func handlePacket(_ pack: SocketPacket) {
switch pack.type {
case .Event where isCorrectNamespace(pack.nsp):
case .event where isCorrectNamespace(pack.nsp):
handleEvent(pack.event, data: pack.args, isInternalMessage: false, withAck: pack.id)
case .Ack where isCorrectNamespace(pack.nsp):
case .ack where isCorrectNamespace(pack.nsp):
handleAck(pack.id, data: pack.data)
case .BinaryEvent where isCorrectNamespace(pack.nsp):
case .binaryEvent where isCorrectNamespace(pack.nsp):
waitingPackets.append(pack)
case .BinaryAck where isCorrectNamespace(pack.nsp):
case .binaryAck where isCorrectNamespace(pack.nsp):
waitingPackets.append(pack)
case .Connect:
case .connect:
handleConnect(pack.nsp)
case .Disconnect:
didDisconnect("Got Disconnect")
case .Error:
case .disconnect:
didDisconnect(reason: "Got Disconnect")
case .error:
handleEvent("error", data: pack.data, isInternalMessage: true, withAck: pack.id)
default:
DefaultSocketLogger.Logger.log("Got invalid packet: %@", type: "SocketParser", args: pack.description)
@ -62,93 +62,93 @@ extension SocketParsable {
}
/// Parses a messsage from the engine. Returning either a string error or a complete SocketPacket
func parseString(message: String) -> Either<String, SocketPacket> {
func parseString(_ message: String) -> Either<String, SocketPacket> {
var reader = SocketStringReader(message: message)
guard let type = SocketPacket.PacketType(rawValue: Int(reader.read(1)) ?? -1) else {
return .Left("Invalid packet type")
guard let type = Int(reader.read(count: 1)).flatMap({ SocketPacket.PacketType(rawValue: $0) }) else {
return .left("Invalid packet type")
}
if !reader.hasNext {
return .Right(SocketPacket(type: type, nsp: "/"))
return .right(SocketPacket(type: type, nsp: "/"))
}
var namespace = "/"
var placeholders = -1
if type == .BinaryEvent || type == .BinaryAck {
if let holders = Int(reader.readUntilStringOccurence("-")) {
if type == .binaryEvent || type == .binaryAck {
if let holders = Int(reader.readUntilOccurence(of: "-")) {
placeholders = holders
} else {
return .Left("Invalid packet")
return .left("Invalid packet")
}
}
if reader.currentCharacter == "/" {
namespace = reader.readUntilStringOccurence(",") ?? reader.readUntilEnd()
namespace = reader.readUntilOccurence(of: ",")
}
if !reader.hasNext {
return .Right(SocketPacket(type: type, nsp: namespace, placeholders: placeholders))
return .right(SocketPacket(type: type, nsp: namespace, placeholders: placeholders))
}
var idString = ""
if type == .Error {
reader.advanceIndexBy(-1)
if type == .error {
reader.advance(by: -1)
} else {
while reader.hasNext {
if let int = Int(reader.read(1)) {
if let int = Int(reader.read(count: 1)) {
idString += String(int)
} else {
reader.advanceIndexBy(-2)
reader.advance(by: -2)
break
}
}
}
let d = message[reader.currentIndex.advancedBy(1)..<message.endIndex]
switch parseData(d) {
case let .Left(err):
// Errors aren't always enclosed in an array
if case let .Right(data) = parseData("\([d as AnyObject])") {
return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1,
nsp: namespace, placeholders: placeholders))
} else {
return .Left(err)
}
case let .Right(data):
return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1,
var dataArray = message[message.characters.index(reader.currentIndex, offsetBy: 1)..<message.endIndex]
if type == .error && !dataArray.hasPrefix("[") && !dataArray.hasSuffix("]") {
dataArray = "[" + dataArray + "]"
}
switch parseData(dataArray) {
case let .left(err):
return .left(err)
case let .right(data):
return .right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1,
nsp: namespace, placeholders: placeholders))
}
}
// Parses data for events
private func parseData(data: String) -> Either<String, [AnyObject]> {
private func parseData(_ data: String) -> Either<String, [Any]> {
do {
return .Right(try data.toArray())
return .right(try data.toArray())
} catch {
return .Left("Error parsing data for packet")
return .left("Error parsing data for packet")
}
}
// Parses messages recieved
func parseSocketMessage(message: String) {
func parseSocketMessage(_ message: String) {
guard !message.isEmpty else { return }
DefaultSocketLogger.Logger.log("Parsing %@", type: "SocketParser", args: message)
switch parseString(message) {
case let .Left(err):
case let .left(err):
DefaultSocketLogger.Logger.error("\(err): %@", type: "SocketParser", args: message)
case let .Right(pack):
case let .right(pack):
DefaultSocketLogger.Logger.log("Decoded packet as: %@", type: "SocketParser", args: pack.description)
handlePacket(pack)
}
}
func parseBinaryData(data: NSData) {
func parseBinaryData(_ data: Data) {
guard !waitingPackets.isEmpty else {
DefaultSocketLogger.Logger.error("Got data when not remaking packet", type: "SocketParser")
return
@ -159,9 +159,8 @@ extension SocketParsable {
let packet = waitingPackets.removeLast()
if packet.type != .BinaryAck {
handleEvent(packet.event, data: packet.args ?? [],
isInternalMessage: false, withAck: packet.id)
if packet.type != .binaryAck {
handleEvent(packet.event, data: packet.args, isInternalMessage: false, withAck: packet.id)
} else {
handleAck(packet.id, data: packet.args)
}

View File

@ -38,31 +38,36 @@ struct SocketStringReader {
currentIndex = message.startIndex
}
mutating func advanceIndexBy(n: Int) {
currentIndex = currentIndex.advancedBy(n)
@discardableResult
mutating func advance(by: Int) -> String.Index {
currentIndex = message.characters.index(currentIndex, offsetBy: by)
return currentIndex
}
mutating func read(readLength: Int) -> String {
let readString = message[currentIndex..<currentIndex.advancedBy(readLength)]
advanceIndexBy(readLength)
mutating func read(count: Int) -> String {
let readString = message[currentIndex..<message.characters.index(currentIndex, offsetBy: count)]
advance(by: count)
return readString
}
mutating func readUntilStringOccurence(string: String) -> String {
mutating func readUntilOccurence(of string: String) -> String {
let substring = message[currentIndex..<message.endIndex]
guard let foundRange = substring.rangeOfString(string) else {
guard let foundRange = substring.range(of: string) else {
currentIndex = message.endIndex
return substring
}
advanceIndexBy(message.startIndex.distanceTo(foundRange.startIndex) + 1)
advance(by: message.characters.distance(from: message.characters.startIndex, to: foundRange.lowerBound) + 1)
return substring.substringToIndex(foundRange.startIndex)
return substring.substring(to: foundRange.lowerBound)
}
mutating func readUntilEnd() -> String {
return read(currentIndex.distanceTo(message.endIndex))
return read(count: message.characters.distance(from: currentIndex, to: message.endIndex))
}
}

View File

@ -24,14 +24,29 @@
import Foundation
public typealias AckCallback = ([AnyObject]) -> Void
public typealias NormalCallback = ([AnyObject], SocketAckEmitter) -> Void
public typealias OnAckCallback = (timeoutAfter: UInt64, callback: AckCallback) -> Void
public protocol SocketData {}
typealias Probe = (msg: String, type: SocketEnginePacketType, data: [NSData])
extension Array : SocketData {}
extension Bool : SocketData {}
extension Dictionary : SocketData {}
extension Double : SocketData {}
extension Int : SocketData {}
extension NSArray : SocketData {}
extension Data : SocketData {}
extension NSData : SocketData {}
extension NSDictionary : SocketData {}
extension NSString : SocketData {}
extension NSNull : SocketData {}
extension String : SocketData {}
public typealias AckCallback = ([Any]) -> Void
public typealias NormalCallback = ([Any], SocketAckEmitter) -> Void
public typealias OnAckCallback = (_ timeoutAfter: UInt64, _ callback: @escaping AckCallback) -> Void
typealias Probe = (msg: String, type: SocketEnginePacketType, data: [Data])
typealias ProbeWaitQueue = [Probe]
enum Either<E, V> {
case Left(E)
case Right(V)
case left(E)
case right(V)
}

View File

@ -28,51 +28,51 @@ public let WebsocketDidDisconnectNotification = "WebsocketDidDisconnectNotificat
public let WebsocketDisconnectionErrorKeyName = "WebsocketDisconnectionErrorKeyName"
public protocol WebSocketDelegate: class {
func websocketDidConnect(socket: WebSocket)
func websocketDidDisconnect(socket: WebSocket, error: NSError?)
func websocketDidReceiveMessage(socket: WebSocket, text: String)
func websocketDidReceiveData(socket: WebSocket, data: NSData)
func websocketDidConnect(_ socket: WebSocket)
func websocketDidDisconnect(_ socket: WebSocket, error: NSError?)
func websocketDidReceiveMessage(_ socket: WebSocket, text: String)
func websocketDidReceiveData(_ socket: WebSocket, data: Data)
}
public protocol WebSocketPongDelegate: class {
func websocketDidReceivePong(socket: WebSocket)
func websocketDidReceivePong(_ socket: WebSocket)
}
public class WebSocket: NSObject, NSStreamDelegate {
public class WebSocket : NSObject, StreamDelegate {
enum OpCode: UInt8 {
case ContinueFrame = 0x0
case TextFrame = 0x1
case BinaryFrame = 0x2
enum OpCode : UInt8 {
case continueFrame = 0x0
case textFrame = 0x1
case binaryFrame = 0x2
// 3-7 are reserved.
case ConnectionClose = 0x8
case Ping = 0x9
case Pong = 0xA
case connectionClose = 0x8
case ping = 0x9
case pong = 0xA
// B-F reserved.
}
public enum CloseCode: UInt16 {
case Normal = 1000
case GoingAway = 1001
case ProtocolError = 1002
case ProtocolUnhandledType = 1003
public enum CloseCode : UInt16 {
case normal = 1000
case goingAway = 1001
case protocolError = 1002
case protocolUnhandledType = 1003
// 1004 reserved.
case NoStatusReceived = 1005
case noStatusReceived = 1005
// 1006 reserved.
case Encoding = 1007
case PolicyViolated = 1008
case MessageTooBig = 1009
case encoding = 1007
case policyViolated = 1008
case messageTooBig = 1009
}
public static let ErrorDomain = "WebSocket"
enum InternalErrorCode: UInt16 {
enum InternalErrorCode : UInt16 {
// 0-999 WebSocket status codes not used
case OutputStreamWriteError = 1
case outputStreamWriteError = 1
}
/// Where the callback is executed. It defaults to the main UI thread queue.
public var callbackQueue = dispatch_get_main_queue()
public var callbackQueue = DispatchQueue.main
var optionalProtocols : [String]?
@ -101,7 +101,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
class WSResponse {
var isFin = false
var code: OpCode = .ContinueFrame
var code: OpCode = .continueFrame
var bytesLeft = 0
var frameCount = 0
var buffer: NSMutableData?
@ -122,7 +122,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
public var onConnect: ((Void) -> Void)?
public var onDisconnect: ((NSError?) -> Void)?
public var onText: ((String) -> Void)?
public var onData: ((NSData) -> Void)?
public var onData: ((Data) -> Void)?
public var onPong: ((Void) -> Void)?
public var headers = [String: String]()
@ -135,24 +135,25 @@ public class WebSocket: NSObject, NSStreamDelegate {
public var isConnected :Bool {
return connected
}
public var currentURL: NSURL { return url }
// MARK: - Private
private var url: NSURL
private var inputStream: NSInputStream?
private var outputStream: NSOutputStream?
private var inputStream: InputStream?
private var outputStream: OutputStream?
private var connected = false
private var isConnecting = false
private var writeQueue = NSOperationQueue()
private var writeQueue = OperationQueue()
private var readStack = [WSResponse]()
private var inputQueue = [NSData]()
private var fragBuffer: NSData?
private var inputQueue = [Data]()
private var fragBuffer: Data?
private var certValidated = false
private var didDisconnect = false
private var readyToWrite = false
private let mutex = NSLock()
private let notificationCenter = NSNotificationCenter.defaultCenter()
private let notificationCenter = NotificationCenter.default
private var canDispatch: Bool {
mutex.lock()
let canWork = readyToWrite
@ -161,7 +162,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
}
/// The shared processing queue used for all WebSocket.
private static let sharedWorkQueue = dispatch_queue_create("com.vluxe.starscream.websocket", DISPATCH_QUEUE_SERIAL)
private static let sharedWorkQueue = DispatchQueue(label: "com.vluxe.starscream.websocket")
/// Used for setting protocols.
public init(url: NSURL, protocols: [String]? = nil) {
@ -189,18 +190,18 @@ public class WebSocket: NSObject, NSStreamDelegate {
- Parameter forceTimeout: Maximum time to wait for the server to close the socket.
*/
public func disconnect(forceTimeout forceTimeout: NSTimeInterval? = nil) {
public func disconnect(forceTimeout: TimeInterval? = nil) {
switch forceTimeout {
case .Some(let seconds) where seconds > 0:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))), callbackQueue) { [weak self] in
self?.disconnectStream(nil)
case .some(let seconds) where seconds > 0:
callbackQueue.asyncAfter(deadline: DispatchTime.now() + Double(Int64(seconds * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { [weak self] in
self?.disconnectStream(error: nil)
}
fallthrough
case .None:
writeError(CloseCode.Normal.rawValue)
case .none:
writeError(code: CloseCode.normal.rawValue)
default:
disconnectStream(nil)
disconnectStream(error: nil)
break
}
}
@ -213,9 +214,9 @@ public class WebSocket: NSObject, NSStreamDelegate {
- parameter str: The string to write.
- parameter completion: The (optional) completion handler.
*/
public func writeString(str: String, completion: (() -> ())? = nil) {
public func writeString(_ str: String, completion: (() -> ())? = nil) {
guard isConnected else { return }
dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame, writeCompletion: completion)
dequeueWrite(data: str.data(using: String.Encoding.utf8)! as NSData, code: .textFrame, writeCompletion: completion)
}
/**
@ -226,54 +227,54 @@ public class WebSocket: NSObject, NSStreamDelegate {
- parameter data: The data to write.
- parameter completion: The (optional) completion handler.
*/
public func writeData(data: NSData, completion: (() -> ())? = nil) {
public func writeData(_ data: Data, completion: (() -> ())? = nil) {
guard isConnected else { return }
dequeueWrite(data, code: .BinaryFrame, writeCompletion: completion)
dequeueWrite(data: data as NSData, code: .binaryFrame, writeCompletion: completion)
}
// Write a ping to the websocket. This sends it as a control frame.
// Yodel a sound to the planet. This sends it as an astroid. http://youtu.be/Eu5ZJELRiJ8?t=42s
public func writePing(data: NSData, completion: (() -> ())? = nil) {
guard isConnected else { return }
dequeueWrite(data, code: .Ping, writeCompletion: completion)
dequeueWrite(data: data, code: .ping, writeCompletion: completion)
}
/// Private method that starts the connection.
private func createHTTPRequest() {
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET",
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET" as CFString,
url, kCFHTTPVersion1_1).takeRetainedValue()
var port = url.port
if port == nil {
if supportedSSLSchemes.contains(url.scheme) {
if supportedSSLSchemes.contains(url.scheme!) {
port = 443
} else {
port = 80
}
}
addHeader(urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue)
addHeader(urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue)
addHeader(urlRequest, key: headerWSUpgradeName as NSString, val: headerWSUpgradeValue as NSString)
addHeader(urlRequest, key: headerWSConnectionName as NSString, val: headerWSConnectionValue as NSString)
if let protocols = optionalProtocols {
addHeader(urlRequest, key: headerWSProtocolName, val: protocols.joinWithSeparator(","))
addHeader(urlRequest, key: headerWSProtocolName as NSString, val: protocols.joined(separator: ",") as NSString)
}
addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue)
addHeader(urlRequest, key: headerWSKeyName, val: generateWebSocketKey())
addHeader(urlRequest, key: headerWSVersionName as NSString, val: headerWSVersionValue as NSString)
addHeader(urlRequest, key: headerWSKeyName as NSString, val: generateWebSocketKey() as NSString)
if let origin = origin {
addHeader(urlRequest, key: headerOriginName, val: origin)
addHeader(urlRequest, key: headerOriginName as NSString, val: origin as NSString)
}
addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)")
addHeader(urlRequest, key: headerWSHostName as NSString, val: "\(url.host!):\(port!)" as NSString)
for (key,value) in headers {
addHeader(urlRequest, key: key, val: value)
addHeader(urlRequest, key: key as NSString, val: value as NSString)
}
if let cfHTTPMessage = CFHTTPMessageCopySerializedMessage(urlRequest) {
let serializedRequest = cfHTTPMessage.takeRetainedValue()
initStreamsWithData(serializedRequest, Int(port!))
initStreamsWithData(data: serializedRequest as NSData, Int(port!))
}
}
/// Add a header to the CFHTTPMessage by using the NSString bridges to CFString.
private func addHeader(urlRequest: CFHTTPMessage, key: NSString, val: NSString) {
private func addHeader(_ urlRequest: CFHTTPMessage, key: NSString, val: NSString) {
CFHTTPMessageSetHeaderFieldValue(urlRequest, key, val)
}
@ -283,10 +284,10 @@ public class WebSocket: NSObject, NSStreamDelegate {
let seed = 16
for _ in 0..<seed {
let uni = UnicodeScalar(UInt32(97 + arc4random_uniform(25)))
key += "\(Character(uni))"
key += "\(Character(uni!))"
}
let data = key.dataUsingEncoding(NSUTF8StringEncoding)
let baseKey = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
let data = key.data(using: String.Encoding.utf8)
let baseKey = data?.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
return baseKey!
}
@ -297,41 +298,41 @@ public class WebSocket: NSObject, NSStreamDelegate {
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
let h: NSString = url.host!
CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream)
let h = url.host!
CFStreamCreatePairWithSocketToHost(nil, h as NSString, UInt32(port), &readStream, &writeStream)
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()
guard let inStream = inputStream, let outStream = outputStream else { return }
inStream.delegate = self
outStream.delegate = self
if supportedSSLSchemes.contains(url.scheme) {
inStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
outStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
if supportedSSLSchemes.contains(url.scheme!) {
inStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
outStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
} else {
certValidated = true //not a https session, so no need to check SSL pinning
}
if voipEnabled {
inStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
outStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
inStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType)
outStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType)
}
if selfSignedSSL {
let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(bool: false), kCFStreamSSLPeerName: kCFNull]
inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String)
outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String)
let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(value: false), kCFStreamSSLPeerName: kCFNull]
inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)
outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)
}
if let cipherSuites = self.enabledSSLCipherSuites {
if let sslContextIn = CFReadStreamCopyProperty(inputStream, kCFStreamPropertySSLContext) as! SSLContextRef?,
sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContextRef? {
if let sslContextIn = CFReadStreamCopyProperty(inputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext?,
let sslContextOut = CFWriteStreamCopyProperty(outputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext? {
let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count)
let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count)
if resIn != errSecSuccess {
let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn))
disconnectStream(error)
disconnectStream(error: error)
return
}
if resOut != errSecSuccess {
let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut))
disconnectStream(error)
disconnectStream(error: error)
return
}
}
@ -345,15 +346,15 @@ public class WebSocket: NSObject, NSStreamDelegate {
self.readyToWrite = true
self.mutex.unlock()
let bytes = UnsafePointer<UInt8>(data.bytes)
let bytes = UnsafeRawPointer(data.bytes).assumingMemoryBound(to: UInt8.self)
var out = timeout * 1000000 // wait 5 seconds before giving up
writeQueue.addOperationWithBlock { [weak self] in
writeQueue.addOperation { [weak self] in
while !outStream.hasSpaceAvailable {
usleep(100) // wait until the socket is ready
out -= 100
if out < 0 {
self?.cleanupStream()
self?.doDisconnect(self?.errorWithDetail("write wait timed out", code: 2))
self?.doDisconnect(error: self?.errorWithDetail("write wait timed out", code: 2))
return
} else if outStream.streamError != nil {
return // disconnectStream will be called.
@ -364,29 +365,26 @@ public class WebSocket: NSObject, NSStreamDelegate {
}
// Delegate for the stream methods. Processes incoming bytes.
public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
if let sec = security where !certValidated && [.HasBytesAvailable, .HasSpaceAvailable].contains(eventCode) {
let possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as String)
if let trust: AnyObject = possibleTrust {
let domain: AnyObject? = aStream.propertyForKey(kCFStreamSSLPeerName as String)
if sec.isValid(trust as! SecTrustRef, domain: domain as! String?) {
certValidated = true
} else {
let error = errorWithDetail("Invalid SSL certificate", code: 1)
disconnectStream(error)
return
}
public func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
if let sec = security , !certValidated && [.hasBytesAvailable, .hasSpaceAvailable].contains(eventCode) {
let possibleTrust = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey)
let domain = aStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as? String
if sec.isValid(possibleTrust as! SecTrust, domain: domain) {
certValidated = true
} else {
let error = errorWithDetail("Invalid SSL certificate", code: 1)
disconnectStream(error: error)
return
}
}
if eventCode == .HasBytesAvailable {
if eventCode == .hasBytesAvailable {
if aStream == inputStream {
processInputStream()
}
} else if eventCode == .ErrorOccurred {
disconnectStream(aStream.streamError)
} else if eventCode == .EndEncountered {
disconnectStream(nil)
} else if eventCode == .errorOccurred {
disconnectStream(error: aStream.streamError as NSError?)
} else if eventCode == .endEncountered {
disconnectStream(error: nil)
}
}
@ -398,7 +396,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
writeQueue.cancelAllOperations()
}
cleanupStream()
doDisconnect(error)
doDisconnect(error: error)
}
private func cleanupStream() {
@ -419,7 +417,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
/// Handles the incoming bytes and sending them to the proper processing method.
private func processInputStream() {
let buf = NSMutableData(capacity: BUFFER_MAX)
let buffer = UnsafeMutablePointer<UInt8>(buf!.bytes)
let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self)
let length = inputStream!.read(buffer, maxLength: BUFFER_MAX)
guard length > 0 else { return }
@ -427,7 +425,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
if inputQueue.count == 0 {
process = true
}
inputQueue.append(NSData(bytes: buffer, length: length))
inputQueue.append(Data(bytes: UnsafeRawPointer(buffer).assumingMemoryBound(to: UInt8.self), count: length))
if process {
dequeueInput()
}
@ -440,14 +438,14 @@ public class WebSocket: NSObject, NSStreamDelegate {
var work = data
if let fragBuffer = fragBuffer {
let combine = NSMutableData(data: fragBuffer)
combine.appendData(data)
work = combine
combine.append(data)
work = combine as Data
self.fragBuffer = nil
}
let buffer = UnsafePointer<UInt8>(work.bytes)
let length = work.length
let buffer = UnsafeRawPointer((work as NSData).bytes).assumingMemoryBound(to: UInt8.self)
let length = work.count
if !connected {
processTCPHandshake(buffer, bufferLen: length)
processTCPHandshake(buffer: buffer, bufferLen: length)
} else {
processRawMessagesInBuffer(buffer, bufferLen: length)
}
@ -457,22 +455,22 @@ public class WebSocket: NSObject, NSStreamDelegate {
// Handle checking the initial connection status.
private func processTCPHandshake(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
let code = processHTTP(buffer, bufferLen: bufferLen)
let code = processHTTP(buffer: buffer, bufferLen: bufferLen)
switch code {
case 0:
connected = true
guard canDispatch else {return}
dispatch_async(callbackQueue) { [weak self] in
callbackQueue.async { [weak self] in
guard let s = self else { return }
s.onConnect?()
s.delegate?.websocketDidConnect(s)
s.notificationCenter.postNotificationName(WebsocketDidConnectNotification, object: self)
s.notificationCenter.post(name: NSNotification.Name(WebsocketDidConnectNotification), object: self)
}
case -1:
fragBuffer = NSData(bytes: buffer, length: bufferLen)
fragBuffer = NSData(bytes: buffer, length: bufferLen) as Data
break // do nothing, we are going to collect more data
default:
doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: UInt16(code)))
doDisconnect(error: errorWithDetail("Invalid HTTP upgrade", code: UInt16(code)))
}
}
@ -493,7 +491,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
}
}
if totalSize > 0 {
let code = validateResponse(buffer, bufferLen: totalSize)
let code = validateResponse(buffer: buffer, bufferLen: totalSize)
if code != 0 {
return code
}
@ -517,7 +515,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
}
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
let headers = cfHeaders.takeRetainedValue() as NSDictionary
if let acceptKey = headers[headerWSAcceptName] as? NSString {
if let acceptKey = headers[headerWSAcceptName as NSString] as? NSString {
if acceptKey.length > 0 {
return 0
}
@ -554,16 +552,16 @@ public class WebSocket: NSObject, NSStreamDelegate {
}
/// Process one message at the start of `buffer`. Return another buffer (sharing storage) that contains the leftover contents of `buffer` that I didn't process.
@warn_unused_result
private func processOneRawMessage(inBuffer buffer: UnsafeBufferPointer<UInt8>) -> UnsafeBufferPointer<UInt8> {
let response = readStack.last
let baseAddress = buffer.baseAddress
guard let baseAddress = buffer.baseAddress else { fatalError("") }
let bufferLen = buffer.count
if response != nil && bufferLen < 2 {
fragBuffer = NSData(buffer: buffer)
fragBuffer = Data(buffer: buffer)
return emptyBuffer
}
if let response = response where response.bytesLeft > 0 {
if let response = response, response.bytesLeft > 0 {
var len = response.bytesLeft
var extra = bufferLen - response.bytesLeft
if response.bytesLeft > bufferLen {
@ -571,8 +569,8 @@ public class WebSocket: NSObject, NSStreamDelegate {
extra = 0
}
response.bytesLeft -= len
response.buffer?.appendData(NSData(bytes: baseAddress, length: len))
processResponse(response)
response.buffer?.append(Data(bytes: baseAddress, count: len))
_ = processResponse(response)
return buffer.fromOffset(bufferLen - extra)
} else {
let isFin = (FinMask & baseAddress[0])
@ -580,34 +578,34 @@ public class WebSocket: NSObject, NSStreamDelegate {
let isMasked = (MaskMask & baseAddress[1])
let payloadLen = (PayloadLenMask & baseAddress[1])
var offset = 2
if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .Pong {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("masked and rsv data is not currently supported", code: errCode))
writeError(errCode)
if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .pong {
let errCode = CloseCode.protocolError.rawValue
doDisconnect(error: errorWithDetail("masked and rsv data is not currently supported", code: errCode))
writeError(code: errCode)
return emptyBuffer
}
let isControlFrame = (receivedOpcode == .ConnectionClose || receivedOpcode == .Ping)
if !isControlFrame && (receivedOpcode != .BinaryFrame && receivedOpcode != .ContinueFrame &&
receivedOpcode != .TextFrame && receivedOpcode != .Pong) {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode))
writeError(errCode)
let isControlFrame = (receivedOpcode == .connectionClose || receivedOpcode == .ping)
if !isControlFrame && (receivedOpcode != .binaryFrame && receivedOpcode != .continueFrame &&
receivedOpcode != .textFrame && receivedOpcode != .pong) {
let errCode = CloseCode.protocolError.rawValue
doDisconnect(error: errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode))
writeError(code: errCode)
return emptyBuffer
}
if isControlFrame && isFin == 0 {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("control frames can't be fragmented", code: errCode))
writeError(errCode)
let errCode = CloseCode.protocolError.rawValue
doDisconnect(error: errorWithDetail("control frames can't be fragmented", code: errCode))
writeError(code: errCode)
return emptyBuffer
}
if receivedOpcode == .ConnectionClose {
var code = CloseCode.Normal.rawValue
if receivedOpcode == .connectionClose {
var code = CloseCode.normal.rawValue
if payloadLen == 1 {
code = CloseCode.ProtocolError.rawValue
code = CloseCode.protocolError.rawValue
} else if payloadLen > 1 {
code = WebSocket.readUint16(baseAddress, offset: offset)
code = WebSocket.readUint16(buffer: baseAddress, offset: offset)
if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) {
code = CloseCode.ProtocolError.rawValue
code = CloseCode.protocolError.rawValue
}
offset += 2
}
@ -616,47 +614,47 @@ public class WebSocket: NSObject, NSStreamDelegate {
let len = Int(payloadLen - 2)
if len > 0 {
let bytes = baseAddress + offset
if let customCloseReason = String(data: NSData(bytes: bytes, length: len), encoding: NSUTF8StringEncoding) {
if let customCloseReason = String(data: NSData(bytes: bytes, length: len) as Data, encoding: String.Encoding.utf8) {
closeReason = customCloseReason
} else {
code = CloseCode.ProtocolError.rawValue
code = CloseCode.protocolError.rawValue
}
}
}
doDisconnect(errorWithDetail(closeReason, code: code))
writeError(code)
doDisconnect(error: errorWithDetail(closeReason, code: code))
writeError(code: code)
return emptyBuffer
}
if isControlFrame && payloadLen > 125 {
writeError(CloseCode.ProtocolError.rawValue)
writeError(code: CloseCode.protocolError.rawValue)
return emptyBuffer
}
var dataLength = UInt64(payloadLen)
if dataLength == 127 {
dataLength = WebSocket.readUint64(baseAddress, offset: offset)
offset += sizeof(UInt64)
dataLength = WebSocket.readUint64(buffer: baseAddress, offset: offset)
offset += MemoryLayout<UInt64>.size
} else if dataLength == 126 {
dataLength = UInt64(WebSocket.readUint16(baseAddress, offset: offset))
offset += sizeof(UInt16)
dataLength = UInt64(WebSocket.readUint16(buffer: baseAddress, offset: offset))
offset += MemoryLayout<UInt16>.size
}
if bufferLen < offset || UInt64(bufferLen - offset) < dataLength {
fragBuffer = NSData(bytes: baseAddress, length: bufferLen)
fragBuffer = Data(bytes: baseAddress, count: bufferLen)
return emptyBuffer
}
var len = dataLength
if dataLength > UInt64(bufferLen) {
len = UInt64(bufferLen-offset)
}
let data: NSData
let data: Data
if len < 0 {
len = 0
data = NSData()
data = Data()
} else {
data = NSData(bytes: baseAddress+offset, length: Int(len))
data = Data(bytes: baseAddress+offset, count: Int(len))
}
if receivedOpcode == .Pong {
if receivedOpcode == .pong {
if canDispatch {
dispatch_async(callbackQueue) { [weak self] in
callbackQueue.async { [weak self] in
guard let s = self else { return }
s.onPong?()
s.pongDelegate?.websocketDidReceivePong(s)
@ -668,19 +666,19 @@ public class WebSocket: NSObject, NSStreamDelegate {
if isControlFrame {
response = nil // Don't append pings.
}
if isFin == 0 && receivedOpcode == .ContinueFrame && response == nil {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("continue frame before a binary or text frame", code: errCode))
writeError(errCode)
if isFin == 0 && receivedOpcode == .continueFrame && response == nil {
let errCode = CloseCode.protocolError.rawValue
doDisconnect(error: errorWithDetail("continue frame before a binary or text frame", code: errCode))
writeError(code: errCode)
return emptyBuffer
}
var isNew = false
if response == nil {
if receivedOpcode == .ContinueFrame {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("first frame can't be a continue frame",
code: errCode))
writeError(errCode)
if receivedOpcode == .continueFrame {
let errCode = CloseCode.protocolError.rawValue
doDisconnect(error: errorWithDetail("first frame can't be a continue frame",
code: errCode))
writeError(code: errCode)
return emptyBuffer
}
isNew = true
@ -689,16 +687,16 @@ public class WebSocket: NSObject, NSStreamDelegate {
response!.bytesLeft = Int(dataLength)
response!.buffer = NSMutableData(data: data)
} else {
if receivedOpcode == .ContinueFrame {
if receivedOpcode == .continueFrame {
response!.bytesLeft = Int(dataLength)
} else {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("second and beyond of fragment message must be a continue frame",
code: errCode))
writeError(errCode)
let errCode = CloseCode.protocolError.rawValue
doDisconnect(error: errorWithDetail("second and beyond of fragment message must be a continue frame",
code: errCode))
writeError(code: errCode)
return emptyBuffer
}
response!.buffer!.appendData(data)
response!.buffer!.append(data)
}
if let response = response {
response.bytesLeft -= Int(len)
@ -707,7 +705,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
if isNew {
readStack.append(response)
}
processResponse(response)
_ = processResponse(response)
}
let step = Int(offset + numericCast(len))
@ -716,42 +714,42 @@ public class WebSocket: NSObject, NSStreamDelegate {
}
/// Process all messages in the buffer if possible.
private func processRawMessagesInBuffer(pointer: UnsafePointer<UInt8>, bufferLen: Int) {
private func processRawMessagesInBuffer(_ pointer: UnsafePointer<UInt8>, bufferLen: Int) {
var buffer = UnsafeBufferPointer(start: pointer, count: bufferLen)
repeat {
buffer = processOneRawMessage(inBuffer: buffer)
} while buffer.count >= 2
if buffer.count > 0 {
fragBuffer = NSData(buffer: buffer)
fragBuffer = Data(buffer: buffer)
}
}
/// Process the finished response of a buffer.
private func processResponse(response: WSResponse) -> Bool {
private func processResponse(_ response: WSResponse) -> Bool {
if response.isFin && response.bytesLeft <= 0 {
if response.code == .Ping {
if response.code == .ping {
let data = response.buffer! // local copy so it's not perverse for writing
dequeueWrite(data, code: OpCode.Pong)
} else if response.code == .TextFrame {
let str: NSString? = NSString(data: response.buffer!, encoding: NSUTF8StringEncoding)
dequeueWrite(data: data, code: OpCode.pong)
} else if response.code == .textFrame {
let str: NSString? = NSString(data: response.buffer! as Data, encoding: String.Encoding.utf8.rawValue)
if str == nil {
writeError(CloseCode.Encoding.rawValue)
writeError(code: CloseCode.encoding.rawValue)
return false
}
if canDispatch {
dispatch_async(callbackQueue) { [weak self] in
callbackQueue.async { [weak self] in
guard let s = self else { return }
s.onText?(str! as String)
s.delegate?.websocketDidReceiveMessage(s, text: str! as String)
}
}
} else if response.code == .BinaryFrame {
} else if response.code == .binaryFrame {
if canDispatch {
let data = response.buffer! //local copy so it's not perverse for writing
dispatch_async(callbackQueue) { [weak self] in
callbackQueue.async { [weak self] in
guard let s = self else { return }
s.onData?(data)
s.delegate?.websocketDidReceiveData(s, data: data)
s.onData?(data as Data)
s.delegate?.websocketDidReceiveData(s, data: data as Data)
}
}
}
@ -762,7 +760,7 @@ public class WebSocket: NSObject, NSStreamDelegate {
}
/// Create an error.
private func errorWithDetail(detail: String, code: UInt16) -> NSError {
private func errorWithDetail(_ detail: String, code: UInt16) -> NSError {
var details = [String: String]()
details[NSLocalizedDescriptionKey] = detail
return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details)
@ -770,64 +768,64 @@ public class WebSocket: NSObject, NSStreamDelegate {
/// Write a an error to the socket.
private func writeError(code: UInt16) {
let buf = NSMutableData(capacity: sizeof(UInt16))
let buffer = UnsafeMutablePointer<UInt8>(buf!.bytes)
WebSocket.writeUint16(buffer, offset: 0, value: code)
dequeueWrite(NSData(bytes: buffer, length: sizeof(UInt16)), code: .ConnectionClose)
let buf = NSMutableData(capacity: MemoryLayout<UInt16>.size)
let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self)
WebSocket.writeUint16(buffer: buffer, offset: 0, value: code)
dequeueWrite(data: Data(bytes: buffer, count: MemoryLayout<UInt16>.size) as NSData, code: .connectionClose)
}
/// Used to write things to the stream.
private func dequeueWrite(data: NSData, code: OpCode, writeCompletion: (() -> ())? = nil) {
writeQueue.addOperationWithBlock { [weak self] in
writeQueue.addOperation { [weak self] in
//stream isn't ready, let's wait
guard let s = self else { return }
var offset = 2
let bytes = UnsafeMutablePointer<UInt8>(data.bytes)
let bytes = UnsafeMutableRawPointer(mutating: (data as NSData).bytes).assumingMemoryBound(to: UInt8.self)
let dataLength = data.length
let frame = NSMutableData(capacity: dataLength + s.MaxFrameSize)
let buffer = UnsafeMutablePointer<UInt8>(frame!.mutableBytes)
let buffer = UnsafeMutableRawPointer(frame!.mutableBytes).assumingMemoryBound(to: UInt8.self)
buffer[0] = s.FinMask | code.rawValue
if dataLength < 126 {
buffer[1] = CUnsignedChar(dataLength)
} else if dataLength <= Int(UInt16.max) {
buffer[1] = 126
WebSocket.writeUint16(buffer, offset: offset, value: UInt16(dataLength))
offset += sizeof(UInt16)
WebSocket.writeUint16(buffer: buffer, offset: offset, value: UInt16(dataLength))
offset += MemoryLayout<UInt16>.size
} else {
buffer[1] = 127
WebSocket.writeUint64(buffer, offset: offset, value: UInt64(dataLength))
offset += sizeof(UInt64)
WebSocket.writeUint64(buffer: buffer, offset: offset, value: UInt64(dataLength))
offset += MemoryLayout<UInt64>.size
}
buffer[1] |= s.MaskMask
let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey)
offset += sizeof(UInt32)
_ = SecRandomCopyBytes(kSecRandomDefault, Int(MemoryLayout<UInt32>.size), maskKey)
offset += MemoryLayout<UInt32>.size
for i in 0..<dataLength {
buffer[offset] = bytes[i] ^ maskKey[i % sizeof(UInt32)]
buffer[offset] = bytes[i] ^ maskKey[i % MemoryLayout<UInt32>.size]
offset += 1
}
var total = 0
while true {
guard let outStream = s.outputStream else { break }
let writeBuffer = UnsafePointer<UInt8>(frame!.bytes+total)
let writeBuffer = UnsafeRawPointer(frame!.bytes+total).assumingMemoryBound(to: UInt8.self)
let len = outStream.write(writeBuffer, maxLength: offset-total)
if len < 0 {
var error: NSError?
if let streamError = outStream.streamError {
error = streamError
error = streamError as NSError
} else {
let errCode = InternalErrorCode.OutputStreamWriteError.rawValue
let errCode = InternalErrorCode.outputStreamWriteError.rawValue
error = s.errorWithDetail("output stream error during write", code: errCode)
}
s.doDisconnect(error)
s.doDisconnect(error: error)
break
} else {
total += len
}
if total >= offset {
if let callbackQueue = self?.callbackQueue, callback = writeCompletion {
dispatch_async(callbackQueue) {
if let callbackQueue = self?.callbackQueue, let callback = writeCompletion {
callbackQueue.async {
callback()
}
}
@ -845,12 +843,12 @@ public class WebSocket: NSObject, NSStreamDelegate {
didDisconnect = true
connected = false
guard canDispatch else {return}
dispatch_async(callbackQueue) { [weak self] in
callbackQueue.async { [weak self] in
guard let s = self else { return }
s.onDisconnect?(error)
s.delegate?.websocketDidDisconnect(s, error: error)
let userInfo = error.map{ [WebsocketDisconnectionErrorKeyName: $0] }
s.notificationCenter.postNotificationName(WebsocketDidDisconnectNotification, object: self, userInfo: userInfo)
s.notificationCenter.post(name: NSNotification.Name(rawValue: WebsocketDidDisconnectNotification), object: self, userInfo: userInfo)
}
}
@ -865,18 +863,18 @@ public class WebSocket: NSObject, NSStreamDelegate {
}
private extension NSData {
private extension Data {
convenience init(buffer: UnsafeBufferPointer<UInt8>) {
self.init(bytes: buffer.baseAddress, length: buffer.count)
init(buffer: UnsafeBufferPointer<UInt8>) {
self.init(bytes: buffer.baseAddress!, count: buffer.count)
}
}
private extension UnsafeBufferPointer {
func fromOffset(offset: Int) -> UnsafeBufferPointer<Element> {
return UnsafeBufferPointer<Element>(start: baseAddress.advancedBy(offset), count: count - offset)
func fromOffset(_ offset: Int) -> UnsafeBufferPointer<Element> {
return UnsafeBufferPointer<Element>(start: baseAddress!.advanced(by: offset), count: count - offset)
}
}