Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6cb1c474e0 | |||
| 7102d31afb | |||
| 0f94bb3d95 | |||
| 28070bf2a4 | |||
| ee0c7f9213 | |||
| a49c7e2119 | |||
| 7fad6b3a82 | |||
| 06baac0fe7 | |||
| 5866676617 | |||
| b074c6c833 | |||
| 8f2af31bd3 | |||
| d3fb769f93 | |||
| f58eb4ccad | |||
| 880b5b4c9e | |||
| 97e378d94a | |||
| ca20b02ca2 | |||
| a1f133d737 | |||
| 13f685f464 | |||
| fae7ef2817 | |||
| b24eaba135 | |||
| e8182190b7 | |||
| bf24825167 | |||
| 9ee57e089d | |||
| f057a8b33d | |||
| b8fe0f64e3 | |||
| 94e54af61c | |||
| 44ce58956f | |||
| c7cc1db0e3 | |||
| a438ea1977 | |||
| b5991eda48 | |||
| 0a6f6d2a79 | |||
| 3fdf33a078 | |||
| 9f5f0f44f9 | |||
| da7949d2da | |||
| dc48916804 | |||
| e0e8271725 | |||
| 411820d836 | |||
| d326d448b0 | |||
| 7dd90900dc | |||
| f506be54ae | |||
| 143ab16c1a | |||
| 4d281416c2 | |||
| e9d10000ec | |||
| 789264eeff | |||
| 644e8f92c0 | |||
| 68b64ede4e | |||
| 31277a418c |
+1
-1
@@ -1 +1 @@
|
||||
3.0
|
||||
4.0
|
||||
|
||||
@@ -2,6 +2,66 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
`Starscream` adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
#### [3.0.3](https://github.com/daltoniam/Starscream/tree/3.0.3)
|
||||
|
||||
Assorted fixes.
|
||||
|
||||
Pull Requests:
|
||||
[#438](https://github.com/daltoniam/Starscream/pull/438)
|
||||
[#423](https://github.com/daltoniam/Starscream/pull/423)
|
||||
[#420](https://github.com/daltoniam/Starscream/pull/420)
|
||||
[#418](https://github.com/daltoniam/Starscream/pull/418)
|
||||
[#410](https://github.com/daltoniam/Starscream/pull/410)
|
||||
[#405](https://github.com/daltoniam/Starscream/pull/405)
|
||||
[#404](https://github.com/daltoniam/Starscream/pull/404)
|
||||
[#400](https://github.com/daltoniam/Starscream/pull/400)
|
||||
|
||||
Issues:
|
||||
[#435](https://github.com/daltoniam/Starscream/issues/435)
|
||||
[#431](https://github.com/daltoniam/Starscream/issues/431)
|
||||
[#426](https://github.com/daltoniam/Starscream/issues/426)
|
||||
[#409](https://github.com/daltoniam/Starscream/issues/409)
|
||||
[#408](https://github.com/daltoniam/Starscream/issues/408)
|
||||
[#401](https://github.com/daltoniam/Starscream/issues/401)
|
||||
[#399](https://github.com/daltoniam/Starscream/issues/399)
|
||||
[#378](https://github.com/daltoniam/Starscream/issues/378)
|
||||
|
||||
#### [3.0.2](https://github.com/daltoniam/Starscream/tree/3.0.2)
|
||||
|
||||
Small fixes for 3.0.1.
|
||||
|
||||
[#394](https://github.com/daltoniam/Starscream/issues/394)
|
||||
[#392](https://github.com/daltoniam/Starscream/issues/392)
|
||||
[#391](https://github.com/daltoniam/Starscream/issues/391)
|
||||
|
||||
#### [3.0.1](https://github.com/daltoniam/Starscream/tree/3.0.1)
|
||||
|
||||
Small fixes for 3.0.0.
|
||||
|
||||
[#389](https://github.com/daltoniam/Starscream/issues/389)
|
||||
[#354](https://github.com/daltoniam/Starscream/issues/354)
|
||||
[#386](https://github.com/daltoniam/Starscream/pull/386)
|
||||
[#388](https://github.com/daltoniam/Starscream/pull/388)
|
||||
[#390](https://github.com/daltoniam/Starscream/pull/390)
|
||||
|
||||
#### [3.0.0](https://github.com/daltoniam/Starscream/tree/3.0.0)
|
||||
|
||||
Major refactor and Swift 4 support. Additions include:
|
||||
|
||||
- Watchos support.
|
||||
- Linux support.
|
||||
- New Stream class to allow custom socket implementations if desired.
|
||||
- Protocol added for mocking (dependency injection).
|
||||
- Single framework (no more platform suffixes! e.g. StarscreamOSX, StarscreamTVOS, etc).
|
||||
|
||||
[#384](https://github.com/daltoniam/Starscream/issues/384)
|
||||
[#377](https://github.com/daltoniam/Starscream/pull/377)
|
||||
[#374](https://github.com/daltoniam/Starscream/issues/374)
|
||||
[#346](https://github.com/daltoniam/Starscream/issues/346)
|
||||
[#335](https://github.com/daltoniam/Starscream/issues/335)
|
||||
[#311](https://github.com/daltoniam/Starscream/pull/311)
|
||||
[#269](https://github.com/daltoniam/Starscream/issues/269)
|
||||
|
||||
#### [2.1.1](https://github.com/daltoniam/Starscream/tree/2.1.1)
|
||||
|
||||
Fixes race condition. Updated to avoid SPM dependencies.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
github "daltoniam/zlib-spm" ~> 1.1
|
||||
github "daltoniam/common-crypto-spm" ~> 1.1
|
||||
@@ -0,0 +1,2 @@
|
||||
github "daltoniam/zlib-spm" "1.1"
|
||||
github "daltoniam/common-crypto-spm" "1.1"
|
||||
@@ -1,6 +1,6 @@
|
||||

|
||||
|
||||
Starscream is a conforming WebSocket ([RFC 6455](http://tools.ietf.org/html/rfc6455)) client library in Swift for iOS and OSX.
|
||||
Starscream is a conforming WebSocket ([RFC 6455](http://tools.ietf.org/html/rfc6455)) client library in Swift.
|
||||
|
||||
Its Objective-C counterpart can be found here: [Jetfire](https://github.com/acmacalister/jetfire)
|
||||
|
||||
@@ -35,7 +35,7 @@ After you are connected, there are some delegate methods that we need to impleme
|
||||
websocketDidConnect is called as soon as the client connects to the server.
|
||||
|
||||
```swift
|
||||
func websocketDidConnect(socket: WebSocket) {
|
||||
func websocketDidConnect(socket: WebSocketClient) {
|
||||
print("websocket is connected")
|
||||
}
|
||||
```
|
||||
@@ -45,7 +45,7 @@ func websocketDidConnect(socket: WebSocket) {
|
||||
websocketDidDisconnect is called as soon as the client is disconnected from the server.
|
||||
|
||||
```swift
|
||||
func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
|
||||
func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
|
||||
print("websocket is disconnected: \(error?.localizedDescription)")
|
||||
}
|
||||
```
|
||||
@@ -55,7 +55,7 @@ func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
|
||||
websocketDidReceiveMessage is called when the client gets a text frame from the connection.
|
||||
|
||||
```swift
|
||||
func websocketDidReceiveMessage(socket: WebSocket, text: String) {
|
||||
func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
|
||||
print("got some text: \(text)")
|
||||
}
|
||||
```
|
||||
@@ -65,7 +65,7 @@ func websocketDidReceiveMessage(socket: WebSocket, text: String) {
|
||||
websocketDidReceiveData is called when the client gets a binary frame from the connection.
|
||||
|
||||
```swift
|
||||
func websocketDidReceiveData(socket: WebSocket, data: Data) {
|
||||
func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
|
||||
print("got some data: \(data.count)")
|
||||
}
|
||||
```
|
||||
@@ -75,7 +75,7 @@ func websocketDidReceiveData(socket: WebSocket, data: Data) {
|
||||
websocketDidReceivePong is called when the client gets a pong response from the connection. You need to implement the WebSocketPongDelegate protocol and set an additional delegate, eg: ` socket.pongDelegate = self`
|
||||
|
||||
```swift
|
||||
func websocketDidReceivePong(socket: WebSocket, data: Data?) {
|
||||
func websocketDidReceivePong(socket: WebSocketClient, data: Data?) {
|
||||
print("Got pong! Maybe some data: \(data?.count)")
|
||||
}
|
||||
```
|
||||
@@ -89,7 +89,7 @@ socket.onConnect = {
|
||||
print("websocket is connected")
|
||||
}
|
||||
//websocketDidDisconnect
|
||||
socket.onDisconnect = { (error: NSError?) in
|
||||
socket.onDisconnect = { (error: Error?) in
|
||||
print("websocket is disconnected: \(error?.localizedDescription)")
|
||||
}
|
||||
//websocketDidReceiveMessage
|
||||
@@ -104,7 +104,7 @@ socket.onData = { (data: Data) in
|
||||
socket.connect()
|
||||
```
|
||||
|
||||
One more: you can listen to socket connection and disconnection via notifications. Starscream posts `WebsocketDidConnectNotification` and `WebsocketDidDisconnectNotification`. You can find an `NSError` that caused the disconection by accessing `WebsocketDisconnectionErrorKeyName` on notification `userInfo`.
|
||||
One more: you can listen to socket connection and disconnection via notifications. Starscream posts `WebsocketDidConnectNotification` and `WebsocketDidDisconnectNotification`. You can find an `Error` that caused the disconection by accessing `WebsocketDisconnectionErrorKeyName` on notification `userInfo`.
|
||||
|
||||
|
||||
## The delegate methods give you a simple way to handle data from the server, but how do you send data?
|
||||
@@ -133,6 +133,25 @@ The writePing method is the same as write, but sends a ping control frame.
|
||||
socket.write(ping: Data()) //example on how to write a ping control frame over the socket!
|
||||
```
|
||||
|
||||
### write a pong frame
|
||||
|
||||
|
||||
the writePong method is the same as writePing, but sends a pong control frame.
|
||||
|
||||
```swift
|
||||
socket.write(pong: Data()) //example on how to write a pong control frame over the socket!
|
||||
```
|
||||
|
||||
Starscream will automatically respond to incoming `ping` control frames so you do not need to manually send `pong`s.
|
||||
|
||||
However if for some reason you need to control this prosses you can turn off the automatic `ping` response by disabling `respondToPingWithPong`.
|
||||
|
||||
```swift
|
||||
socket.respondToPingWithPong = false //Do not automaticaly respond to incoming pings with pongs.
|
||||
```
|
||||
|
||||
In most cases you will not need to do this.
|
||||
|
||||
### disconnect
|
||||
|
||||
The disconnect method does what you would expect and closes the socket.
|
||||
@@ -156,9 +175,12 @@ if socket.isConnected {
|
||||
You can also override the default websocket headers with your own custom ones like so:
|
||||
|
||||
```swift
|
||||
socket.headers["Sec-WebSocket-Protocol"] = "someother protocols"
|
||||
socket.headers["Sec-WebSocket-Version"] = "14"
|
||||
socket.headers["My-Awesome-Header"] = "Everything is Awesome!"
|
||||
var request = URLRequest(url: URL(string: "ws://localhost:8080/")!)
|
||||
request.timeoutInterval = 5
|
||||
request.setValue("someother protocols", forHTTPHeaderField: "Sec-WebSocket-Protocol")
|
||||
request.setValue("14", forHTTPHeaderField: "Sec-WebSocket-Version")
|
||||
request.setValue("Everything is Awesome!", forHTTPHeaderField: "My-Awesome-Header")
|
||||
let socket = WebSocket(request: request)
|
||||
```
|
||||
|
||||
### Custom HTTP Method
|
||||
@@ -166,15 +188,11 @@ socket.headers["My-Awesome-Header"] = "Everything is Awesome!"
|
||||
Your server may use a different HTTP method when connecting to the websocket:
|
||||
|
||||
```swift
|
||||
socket.httpMethod = .post
|
||||
var request = URLRequest(url: URL(string: "ws://localhost:8080/")!)
|
||||
request.httpMethod = "POST"
|
||||
request.timeoutInterval = 5
|
||||
let socket = WebSocket(request: request)
|
||||
```
|
||||
you can use a custom string:
|
||||
|
||||
```swift
|
||||
socket.httpMethod = .custom(value: "mycustomhttpmethod")
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Protocols
|
||||
|
||||
@@ -262,7 +280,7 @@ To use Starscream in your project add the following 'Podfile' to your project
|
||||
platform :ios, '9.0'
|
||||
use_frameworks!
|
||||
|
||||
pod 'Starscream', '~> 2.0.3'
|
||||
pod 'Starscream', '~> 3.0.2'
|
||||
|
||||
Then run:
|
||||
|
||||
@@ -284,7 +302,7 @@ $ brew install carthage
|
||||
To integrate Starscream into your Xcode project using Carthage, specify it in your `Cartfile`:
|
||||
|
||||
```
|
||||
github "daltoniam/Starscream" >= 2.0.3
|
||||
github "daltoniam/Starscream" >= 3.0.2
|
||||
```
|
||||
|
||||
### Rogue
|
||||
@@ -294,7 +312,7 @@ First see the [installation docs](https://github.com/acmacalister/Rogue) for how
|
||||
To install Starscream run the command below in the directory you created the rogue file.
|
||||
|
||||
```
|
||||
rogue add https://github.com/daltoniam/starscream
|
||||
rogue add https://github.com/daltoniam/Starscream
|
||||
```
|
||||
|
||||
Next open the `libs` folder and add the `Starscream.xcodeproj` to your Xcode project. Once that is complete, in your "Build Phases" add the `Starscream.framework` to your "Link Binary with Libraries" phase. Make sure to add the `libs` folder to your `.gitignore` file.
|
||||
@@ -307,7 +325,7 @@ Once you have your Swift package set up, adding Starscream as a dependency is as
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/daltoniam/Starscream.git", majorVersion: 2)
|
||||
.Package(url: "https://github.com/daltoniam/Starscream.git", majorVersion: 3)
|
||||
]
|
||||
```
|
||||
|
||||
@@ -333,7 +351,7 @@ In most cases you do not need the extra info and should use the normal delegate.
|
||||
|
||||
#### websocketDidReceiveMessage
|
||||
```swift
|
||||
func websocketDidReceiveMessage(socket: WebSocket, text: String, response: WebSocket.WSResponse {
|
||||
func websocketDidReceiveMessage(socket: WebSocketClient, text: String, response: WebSocket.WSResponse) {
|
||||
print("got some text: \(text)")
|
||||
print("First frame for this message arrived on \(response.firstFrame)")
|
||||
}
|
||||
@@ -341,7 +359,7 @@ func websocketDidReceiveMessage(socket: WebSocket, text: String, response: WebSo
|
||||
|
||||
#### websocketDidReceiveData
|
||||
```swift
|
||||
func websocketDidReceiveData(socket: WebSocket, data: Date, response: WebSocket.WSResponse) {
|
||||
func websocketDidReceiveData(socket: WebSocketClient, data: Date, response: WebSocket.WSResponse) {
|
||||
print("got some data it long: \(data.count)")
|
||||
print("A total of \(response.frameCount) frames were used to send this data")
|
||||
}
|
||||
@@ -350,22 +368,25 @@ func websocketDidReceiveData(socket: WebSocket, data: Date, response: WebSocket.
|
||||
#### websocketHttpUpgrade
|
||||
These methods are called when the HTTP upgrade request is sent and when the response returns.
|
||||
```swift
|
||||
func websocketHttpUpgrade(socket: WebSocket, request: CFHTTPMessage) {
|
||||
func websocketHttpUpgrade(socket: WebSocketClient, request: CFHTTPMessage) {
|
||||
print("the http request was sent we can check the raw http if we need to")
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
func websocketHttpUpgrade(socket: WebSocket, response: CFHTTPMessage) {
|
||||
func websocketHttpUpgrade(socket: WebSocketClient, response: CFHTTPMessage) {
|
||||
print("the http response has returned.")
|
||||
}
|
||||
```
|
||||
|
||||
## KNOWN ISSUES
|
||||
- WatchOS does not have the the CFNetwork String constants to modify the stream's SSL behavior. It will be the default Foundation SSL behavior. This means watchOS CANNOT use `SSLCiphers`, `disableSSLCertValidation`, or SSL pinning. All these values set on watchOS will do nothing.
|
||||
- Linux does not have the security framework, so it CANNOT use SSL pinning or `SSLCiphers` either.
|
||||
|
||||
|
||||
## TODOs
|
||||
|
||||
- [ ] WatchOS?
|
||||
- [ ] Linux Support?
|
||||
- [ ] Add Unit Tests - Local Swift websocket server
|
||||
- [ ] Add Unit Tests - Local WebSocket server that runs against Autobahn
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.1.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -92,7 +92,7 @@ class Decompressor {
|
||||
guard (res == Z_OK && strm.avail_out > 0)
|
||||
|| (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count)
|
||||
else {
|
||||
throw NSError(domain: WebSocket.ErrorDomain, code: Int(WebSocket.InternalErrorCode.compressionError.rawValue), userInfo: nil)
|
||||
throw NSError(domain: WebSocket.ErrorDomain, code: Int(InternalErrorCode.compressionError.rawValue), userInfo: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ class Compressor {
|
||||
guard res == Z_OK && strm.avail_out > 0
|
||||
|| (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count)
|
||||
else {
|
||||
throw NSError(domain: WebSocket.ErrorDomain, code: Int(WebSocket.InternalErrorCode.compressionError.rawValue), userInfo: nil)
|
||||
throw NSError(domain: WebSocket.ErrorDomain, code: Int(InternalErrorCode.compressionError.rawValue), userInfo: nil)
|
||||
}
|
||||
|
||||
compressed.removeLast(4)
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.1.1</string>
|
||||
<string>3.0.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
@@ -19,7 +19,8 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if os(Linux)
|
||||
#else
|
||||
import Foundation
|
||||
import Security
|
||||
|
||||
@@ -56,7 +57,8 @@ open class SSLCert {
|
||||
|
||||
open class SSLSecurity : SSLTrustValidator {
|
||||
public var validatedDN = true //should the domain name be validated?
|
||||
|
||||
public var validateEntireChain = true //should the entire cert chain be validated
|
||||
|
||||
var isReady = false //is the key processing done?
|
||||
var certificates: [Data]? //the certificates
|
||||
var pubKeys: [SecKey]? //the public keys
|
||||
@@ -169,6 +171,9 @@ open class SSLSecurity : SSLTrustValidator {
|
||||
var result: SecTrustResultType = .unspecified
|
||||
SecTrustEvaluate(trust,&result)
|
||||
if result == .unspecified || result == .proceed {
|
||||
if !validateEntireChain {
|
||||
return true
|
||||
}
|
||||
var trustedCount = 0
|
||||
for serverCert in serverCerts {
|
||||
for cert in certs {
|
||||
@@ -258,3 +263,4 @@ open class SSLSecurity : SSLTrustValidator {
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -3,7 +3,7 @@
|
||||
// Websocket.swift
|
||||
//
|
||||
// Created by Dalton Cherry on 7/16/14.
|
||||
// Copyright (c) 2014-2016 Dalton Cherry.
|
||||
// Copyright (c) 2014-2017 Dalton Cherry.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -27,28 +27,304 @@ public let WebsocketDidConnectNotification = "WebsocketDidConnectNotification"
|
||||
public let WebsocketDidDisconnectNotification = "WebsocketDidDisconnectNotification"
|
||||
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: Data)
|
||||
//Standard WebSocket close codes
|
||||
public enum CloseCode : UInt16 {
|
||||
case normal = 1000
|
||||
case goingAway = 1001
|
||||
case protocolError = 1002
|
||||
case protocolUnhandledType = 1003
|
||||
// 1004 reserved.
|
||||
case noStatusReceived = 1005
|
||||
//1006 reserved.
|
||||
case encoding = 1007
|
||||
case policyViolated = 1008
|
||||
case messageTooBig = 1009
|
||||
}
|
||||
|
||||
//Error codes
|
||||
enum InternalErrorCode: UInt16 {
|
||||
// 0-999 WebSocket status codes not used
|
||||
case outputStreamWriteError = 1
|
||||
case compressionError = 2
|
||||
case invalidSSLError = 3
|
||||
case writeTimeoutError = 4
|
||||
}
|
||||
|
||||
//WebSocketClient is setup to be dependency injection for testing
|
||||
public protocol WebSocketClient: class {
|
||||
var delegate: WebSocketDelegate? {get set }
|
||||
|
||||
var disableSSLCertValidation: Bool { get set }
|
||||
var overrideTrustHostname: Bool { get set }
|
||||
var desiredTrustHostname: String? { get set }
|
||||
#if os(Linux)
|
||||
#else
|
||||
var security: SSLTrustValidator? { get set }
|
||||
var enabledSSLCipherSuites: [SSLCipherSuite]? { get set }
|
||||
#endif
|
||||
var isConnected: Bool { get }
|
||||
|
||||
|
||||
func connect()
|
||||
func disconnect(forceTimeout: TimeInterval?, closeCode: UInt16)
|
||||
func write(string: String, completion: (() -> ())?)
|
||||
func write(data: Data, completion: (() -> ())?)
|
||||
func write(ping: Data, completion: (() -> ())?)
|
||||
func write(pong: Data, completion: (() -> ())?)
|
||||
}
|
||||
|
||||
//implements some of the base behaviors
|
||||
extension WebSocketClient {
|
||||
public func write(string: String) {
|
||||
write(string: string, completion: nil)
|
||||
}
|
||||
|
||||
public func write(data: Data) {
|
||||
write(data: data, completion: nil)
|
||||
}
|
||||
|
||||
public func write(ping: Data) {
|
||||
write(ping: ping, completion: nil)
|
||||
}
|
||||
|
||||
public func write(pong: Data) {
|
||||
write(pong: pong, completion: nil)
|
||||
}
|
||||
|
||||
public func disconnect() {
|
||||
disconnect(forceTimeout: nil, closeCode: CloseCode.normal.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
//SSL settings for the stream
|
||||
public struct SSLSettings {
|
||||
let useSSL: Bool
|
||||
let disableCertValidation: Bool
|
||||
var overrideTrustHostname: Bool
|
||||
var desiredTrustHostname: String?
|
||||
#if os(Linux)
|
||||
#else
|
||||
let cipherSuites: [SSLCipherSuite]?
|
||||
#endif
|
||||
}
|
||||
|
||||
public protocol WSStreamDelegate: class {
|
||||
func newBytesInStream()
|
||||
func streamDidError(error: Error?)
|
||||
}
|
||||
|
||||
//This protocol is to allow custom implemention of the underlining stream. This way custom socket libraries (e.g. linux) can be used
|
||||
public protocol WSStream {
|
||||
weak var delegate: WSStreamDelegate? {get set}
|
||||
func connect(url: URL, port: Int, timeout: TimeInterval, ssl: SSLSettings, completion: @escaping ((Error?) -> Void))
|
||||
func write(data: Data) -> Int
|
||||
func read() -> Data?
|
||||
func cleanup()
|
||||
#if os(Linux) || os(watchOS)
|
||||
#else
|
||||
func sslTrust() -> (trust: SecTrust?, domain: String?)
|
||||
#endif
|
||||
}
|
||||
|
||||
open class FoundationStream : NSObject, WSStream, StreamDelegate {
|
||||
private static let sharedWorkQueue = DispatchQueue(label: "com.vluxe.starscream.websocket", attributes: [])
|
||||
private var inputStream: InputStream?
|
||||
private var outputStream: OutputStream?
|
||||
public weak var delegate: WSStreamDelegate?
|
||||
let BUFFER_MAX = 4096
|
||||
|
||||
public var enableSOCKSProxy = false
|
||||
|
||||
public func connect(url: URL, port: Int, timeout: TimeInterval, ssl: SSLSettings, completion: @escaping ((Error?) -> Void)) {
|
||||
var readStream: Unmanaged<CFReadStream>?
|
||||
var writeStream: Unmanaged<CFWriteStream>?
|
||||
let h = url.host! as NSString
|
||||
CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream)
|
||||
inputStream = readStream!.takeRetainedValue()
|
||||
outputStream = writeStream!.takeRetainedValue()
|
||||
|
||||
#if os(watchOS) //watchOS us unfortunately is missing the kCFStream properties to make this work
|
||||
#else
|
||||
if enableSOCKSProxy {
|
||||
let proxyDict = CFNetworkCopySystemProxySettings()
|
||||
let socksConfig = CFDictionaryCreateMutableCopy(nil, 0, proxyDict!.takeRetainedValue())
|
||||
let propertyKey = CFStreamPropertyKey(rawValue: kCFStreamPropertySOCKSProxy)
|
||||
CFWriteStreamSetProperty(outputStream, propertyKey, socksConfig)
|
||||
CFReadStreamSetProperty(inputStream, propertyKey, socksConfig)
|
||||
}
|
||||
#endif
|
||||
|
||||
guard let inStream = inputStream, let outStream = outputStream else { return }
|
||||
inStream.delegate = self
|
||||
outStream.delegate = self
|
||||
if ssl.useSSL {
|
||||
inStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
outStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
#if os(watchOS) //watchOS us unfortunately is missing the kCFStream properties to make this work
|
||||
#else
|
||||
var settings = [NSObject: NSObject]()
|
||||
if ssl.disableCertValidation {
|
||||
settings[kCFStreamSSLValidatesCertificateChain] = NSNumber(value: false)
|
||||
}
|
||||
if ssl.overrideTrustHostname {
|
||||
if let hostname = ssl.desiredTrustHostname {
|
||||
settings[kCFStreamSSLPeerName] = hostname as NSString
|
||||
} else {
|
||||
settings[kCFStreamSSLPeerName] = kCFNull
|
||||
}
|
||||
}
|
||||
inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)
|
||||
outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as Stream.PropertyKey)
|
||||
#endif
|
||||
|
||||
#if os(Linux)
|
||||
#else
|
||||
if let cipherSuites = ssl.cipherSuites {
|
||||
#if os(watchOS) //watchOS us unfortunately is missing the kCFStream properties to make this work
|
||||
#else
|
||||
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 {
|
||||
completion(errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn)))
|
||||
}
|
||||
if resOut != errSecSuccess {
|
||||
completion(errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut)))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
CFReadStreamSetDispatchQueue(inStream, FoundationStream.sharedWorkQueue)
|
||||
CFWriteStreamSetDispatchQueue(outStream, FoundationStream.sharedWorkQueue)
|
||||
inStream.open()
|
||||
outStream.open()
|
||||
|
||||
var out = timeout// wait X seconds before giving up
|
||||
FoundationStream.sharedWorkQueue.async { [weak self] in
|
||||
while !outStream.hasSpaceAvailable {
|
||||
usleep(100) // wait until the socket is ready
|
||||
out -= 100
|
||||
if out < 0 {
|
||||
guard let s = self else {return}
|
||||
let errCode = InternalErrorCode.writeTimeoutError.rawValue
|
||||
completion(s.errorWithDetail("write wait timed out", code: errCode))
|
||||
return
|
||||
} else if let error = outStream.streamError {
|
||||
completion(error)
|
||||
return // disconnectStream will be called.
|
||||
}
|
||||
}
|
||||
completion(nil) //success!
|
||||
}
|
||||
}
|
||||
|
||||
public func write(data: Data) -> Int {
|
||||
guard let outStream = outputStream else {return -1}
|
||||
let buffer = UnsafeRawPointer((data as NSData).bytes).assumingMemoryBound(to: UInt8.self)
|
||||
return outStream.write(buffer, maxLength: data.count)
|
||||
}
|
||||
|
||||
public func read() -> Data? {
|
||||
guard let stream = inputStream else {return nil}
|
||||
let buf = NSMutableData(capacity: BUFFER_MAX)
|
||||
let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self)
|
||||
let length = stream.read(buffer, maxLength: BUFFER_MAX)
|
||||
if length < 1 {
|
||||
return nil
|
||||
}
|
||||
return Data(bytes: buffer, count: length)
|
||||
}
|
||||
|
||||
public func cleanup() {
|
||||
if let stream = inputStream {
|
||||
stream.delegate = nil
|
||||
CFReadStreamSetDispatchQueue(stream, nil)
|
||||
stream.close()
|
||||
}
|
||||
if let stream = outputStream {
|
||||
stream.delegate = nil
|
||||
CFWriteStreamSetDispatchQueue(stream, nil)
|
||||
stream.close()
|
||||
}
|
||||
outputStream = nil
|
||||
inputStream = nil
|
||||
}
|
||||
|
||||
#if os(Linux) || os(watchOS)
|
||||
#else
|
||||
public func sslTrust() -> (trust: SecTrust?, domain: String?) {
|
||||
let trust = outputStream!.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust?
|
||||
var domain = outputStream!.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as? String
|
||||
if domain == nil,
|
||||
let sslContextOut = CFWriteStreamCopyProperty(outputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext? {
|
||||
var peerNameLen: Int = 0
|
||||
SSLGetPeerDomainNameLength(sslContextOut, &peerNameLen)
|
||||
var peerName = Data(count: peerNameLen)
|
||||
let _ = peerName.withUnsafeMutableBytes { (peerNamePtr: UnsafeMutablePointer<Int8>) in
|
||||
SSLGetPeerDomainName(sslContextOut, peerNamePtr, &peerNameLen)
|
||||
}
|
||||
if let peerDomain = String(bytes: peerName, encoding: .utf8), peerDomain.count > 0 {
|
||||
domain = peerDomain
|
||||
}
|
||||
}
|
||||
|
||||
return (trust, domain)
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
Delegate for the stream methods. Processes incoming bytes
|
||||
*/
|
||||
open func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
|
||||
if eventCode == .hasBytesAvailable {
|
||||
if aStream == inputStream {
|
||||
delegate?.newBytesInStream()
|
||||
}
|
||||
} else if eventCode == .errorOccurred {
|
||||
delegate?.streamDidError(error: aStream.streamError)
|
||||
} else if eventCode == .endEncountered {
|
||||
delegate?.streamDidError(error: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func errorWithDetail(_ detail: String, code: UInt16) -> Error {
|
||||
var details = [String: String]()
|
||||
details[NSLocalizedDescriptionKey] = detail
|
||||
return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details) as Error
|
||||
}
|
||||
}
|
||||
|
||||
//WebSocket implementation
|
||||
|
||||
//standard delegate you should use
|
||||
public protocol WebSocketDelegate: class {
|
||||
func websocketDidConnect(socket: WebSocketClient)
|
||||
func websocketDidDisconnect(socket: WebSocketClient, error: Error?)
|
||||
func websocketDidReceiveMessage(socket: WebSocketClient, text: String)
|
||||
func websocketDidReceiveData(socket: WebSocketClient, data: Data)
|
||||
}
|
||||
|
||||
//got pongs
|
||||
public protocol WebSocketPongDelegate: class {
|
||||
func websocketDidReceivePong(socket: WebSocket, data: Data?)
|
||||
func websocketDidReceivePong(socket: WebSocketClient, data: Data?)
|
||||
}
|
||||
|
||||
// A Delegate with more advanced info on messages and connection etc.
|
||||
public protocol WebSocketAdvancedDelegate: class {
|
||||
func websocketDidConnect(socket: WebSocket)
|
||||
func websocketDidDisconnect(socket: WebSocket, error: NSError?)
|
||||
func websocketDidDisconnect(socket: WebSocket, error: Error?)
|
||||
func websocketDidReceiveMessage(socket: WebSocket, text: String, response: WebSocket.WSResponse)
|
||||
func websocketDidReceiveData(socket: WebSocket, data: Data, response: WebSocket.WSResponse)
|
||||
func websocketHttpUpgrade(socket: WebSocket, request: CFHTTPMessage)
|
||||
func websocketHttpUpgrade(socket: WebSocket, response: CFHTTPMessage)
|
||||
func websocketHttpUpgrade(socket: WebSocket, request: String)
|
||||
func websocketHttpUpgrade(socket: WebSocket, response: String)
|
||||
}
|
||||
|
||||
open class WebSocket : NSObject, StreamDelegate {
|
||||
|
||||
open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelegate {
|
||||
|
||||
public enum OpCode : UInt8 {
|
||||
case continueFrame = 0x0
|
||||
@@ -61,34 +337,11 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
// B-F reserved.
|
||||
}
|
||||
|
||||
public enum CloseCode : UInt16 {
|
||||
case normal = 1000
|
||||
case goingAway = 1001
|
||||
case protocolError = 1002
|
||||
case protocolUnhandledType = 1003
|
||||
// 1004 reserved.
|
||||
case noStatusReceived = 1005
|
||||
//1006 reserved.
|
||||
case encoding = 1007
|
||||
case policyViolated = 1008
|
||||
case messageTooBig = 1009
|
||||
}
|
||||
|
||||
public static let ErrorDomain = "WebSocket"
|
||||
|
||||
enum InternalErrorCode: UInt16 {
|
||||
// 0-999 WebSocket status codes not used
|
||||
case outputStreamWriteError = 1
|
||||
case compressionError = 2
|
||||
case invalidSSLError = 3
|
||||
case writeTimeoutError = 4
|
||||
}
|
||||
|
||||
// Where the callback is executed. It defaults to the main UI thread queue.
|
||||
public var callbackQueue = DispatchQueue.main
|
||||
|
||||
var optionalProtocols: [String]?
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
let headerWSUpgradeName = "Upgrade"
|
||||
@@ -130,60 +383,40 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
/// Responds to callback about new messages coming in over the WebSocket
|
||||
/// and also connection/disconnect messages.
|
||||
public weak var delegate: WebSocketDelegate?
|
||||
|
||||
/// The optional advanced delegate can be used insteadof of the delegate
|
||||
|
||||
/// The optional advanced delegate can be used instead of of the delegate
|
||||
public weak var advancedDelegate: WebSocketAdvancedDelegate?
|
||||
|
||||
/// Receives a callback for each pong message recived.
|
||||
public weak var pongDelegate: WebSocketPongDelegate?
|
||||
|
||||
|
||||
// MARK: - Block based API.
|
||||
|
||||
public enum HTTPMethod {
|
||||
case get
|
||||
case post
|
||||
case put
|
||||
case connect
|
||||
case custom(value: String)
|
||||
var representation: String {
|
||||
switch self {
|
||||
case .get:
|
||||
return "GET"
|
||||
case .post:
|
||||
return "POST"
|
||||
case .put:
|
||||
return "PUT"
|
||||
case .connect:
|
||||
return "CONNECT"
|
||||
case .custom(let value):
|
||||
return value.capitalized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public var onConnect: (() -> Void)?
|
||||
public var onDisconnect: ((NSError?) -> Void)?
|
||||
public var onDisconnect: ((Error?) -> Void)?
|
||||
public var onText: ((String) -> Void)?
|
||||
public var onData: ((Data) -> Void)?
|
||||
public var onPong: ((Data?) -> Void)?
|
||||
|
||||
public var httpMethod: HTTPMethod = .get
|
||||
public var headers = [String: String]()
|
||||
public var disableSSLCertValidation = false
|
||||
public var overrideTrustHostname = false
|
||||
public var desiredTrustHostname: String? = nil
|
||||
|
||||
public var enableCompression = true
|
||||
#if os(Linux)
|
||||
#else
|
||||
public var security: SSLTrustValidator?
|
||||
public var enabledSSLCipherSuites: [SSLCipherSuite]?
|
||||
public var origin: String?
|
||||
public var timeout = 5
|
||||
#endif
|
||||
|
||||
public var isConnected: Bool {
|
||||
connectedMutex.lock()
|
||||
let isConnected = connected
|
||||
connectedMutex.unlock()
|
||||
return isConnected
|
||||
}
|
||||
public var request: URLRequest //this is only public to allow headers, timeout, etc to be modified on reconnect
|
||||
public var currentURL: URL { return request.url! }
|
||||
|
||||
public var currentURL: URL { return url }
|
||||
public var respondToPingWithPong: Bool = true
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
@@ -197,10 +430,8 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
var decompressor:Decompressor? = nil
|
||||
var compressor:Compressor? = nil
|
||||
}
|
||||
|
||||
private var url: URL
|
||||
private var inputStream: InputStream?
|
||||
private var outputStream: OutputStream?
|
||||
|
||||
private var stream: WSStream
|
||||
private var connected = false
|
||||
private var isConnecting = false
|
||||
private let connectedMutex = NSLock()
|
||||
@@ -214,27 +445,36 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
private var readyToWrite = false
|
||||
private var headerSecKey = ""
|
||||
private let readyToWriteMutex = NSLock()
|
||||
private let notificationCenter = NotificationCenter.default
|
||||
private var canDispatch: Bool {
|
||||
readyToWriteMutex.lock()
|
||||
let canWork = readyToWrite
|
||||
readyToWriteMutex.unlock()
|
||||
return canWork
|
||||
}
|
||||
/// The shared processing queue used for all WebSocket.
|
||||
private static let sharedWorkQueue = DispatchQueue(label: "com.vluxe.starscream.websocket", attributes: [])
|
||||
|
||||
|
||||
/// Used for setting protocols.
|
||||
public init(url: URL, protocols: [String]? = nil) {
|
||||
self.url = url
|
||||
self.origin = url.absoluteString
|
||||
if let hostUrl = URL (string: "/", relativeTo: url) {
|
||||
var origin = hostUrl.absoluteString
|
||||
origin.remove(at: origin.index(before: origin.endIndex))
|
||||
self.origin = origin
|
||||
public init(request: URLRequest, protocols: [String]? = nil, stream: WSStream = FoundationStream()) {
|
||||
self.request = request
|
||||
self.stream = stream
|
||||
if request.value(forHTTPHeaderField: headerOriginName) == nil {
|
||||
guard let url = request.url else {return}
|
||||
var origin = url.absoluteString
|
||||
if let hostUrl = URL (string: "/", relativeTo: url) {
|
||||
origin = hostUrl.absoluteString
|
||||
origin.remove(at: origin.index(before: origin.endIndex))
|
||||
}
|
||||
self.request.setValue(origin, forHTTPHeaderField: headerOriginName)
|
||||
}
|
||||
if let protocols = protocols {
|
||||
self.request.setValue(protocols.joined(separator: ","), forHTTPHeaderField: headerWSProtocolName)
|
||||
}
|
||||
writeQueue.maxConcurrentOperationCount = 1
|
||||
optionalProtocols = protocols
|
||||
}
|
||||
|
||||
public convenience init(url: URL, protocols: [String]? = nil) {
|
||||
var request = URLRequest(url: url)
|
||||
request.timeoutInterval = 5
|
||||
self.init(request: request, protocols: protocols)
|
||||
}
|
||||
|
||||
// Used for specifically setting the QOS for the write queue.
|
||||
@@ -269,17 +509,13 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
case .some(let seconds) where seconds > 0:
|
||||
let milliseconds = Int(seconds * 1_000)
|
||||
callbackQueue.asyncAfter(deadline: .now() + .milliseconds(milliseconds)) { [weak self] in
|
||||
WebSocket.sharedWorkQueue.async {
|
||||
self?.disconnectStream(nil)
|
||||
}
|
||||
self?.disconnectStream(nil)
|
||||
}
|
||||
fallthrough
|
||||
case .none:
|
||||
writeError(closeCode)
|
||||
default:
|
||||
WebSocket.sharedWorkQueue.async { [weak self] in
|
||||
self?.disconnectStream(nil)
|
||||
}
|
||||
disconnectStream(nil)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -319,13 +555,20 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
dequeueWrite(ping, code: .ping, writeCompletion: completion)
|
||||
}
|
||||
|
||||
/**
|
||||
Write a pong to the websocket. This sends it as a control frame.
|
||||
Respond to a Yodel.
|
||||
*/
|
||||
open func write(pong: Data, completion: (() -> ())? = nil) {
|
||||
guard isConnected else { return }
|
||||
dequeueWrite(pong, code: .pong, writeCompletion: completion)
|
||||
}
|
||||
|
||||
/**
|
||||
Private method that starts the connection.
|
||||
*/
|
||||
private func createHTTPRequest() {
|
||||
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, httpMethod.representation as CFString,
|
||||
url as CFURL, kCFHTTPVersion1_1).takeRetainedValue()
|
||||
|
||||
guard let url = request.url else {return}
|
||||
var port = url.port
|
||||
if port == nil {
|
||||
if supportedSSLSchemes.contains(url.scheme!) {
|
||||
@@ -334,37 +577,40 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
port = 80
|
||||
}
|
||||
}
|
||||
addHeader(urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue)
|
||||
addHeader(urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue)
|
||||
if let protocols = optionalProtocols {
|
||||
addHeader(urlRequest, key: headerWSProtocolName, val: protocols.joined(separator: ","))
|
||||
}
|
||||
request.setValue(headerWSUpgradeValue, forHTTPHeaderField: headerWSUpgradeName)
|
||||
request.setValue(headerWSConnectionValue, forHTTPHeaderField: headerWSConnectionName)
|
||||
headerSecKey = generateWebSocketKey()
|
||||
addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue)
|
||||
addHeader(urlRequest, key: headerWSKeyName, val: headerSecKey)
|
||||
if let origin = origin {
|
||||
addHeader(urlRequest, key: headerOriginName, val: origin)
|
||||
}
|
||||
request.setValue(headerWSVersionValue, forHTTPHeaderField: headerWSVersionName)
|
||||
request.setValue(headerSecKey, forHTTPHeaderField: headerWSKeyName)
|
||||
|
||||
if enableCompression {
|
||||
let val = "permessage-deflate; client_max_window_bits; server_max_window_bits=15"
|
||||
addHeader(urlRequest, key: headerWSExtensionName, val: val)
|
||||
request.setValue(val, forHTTPHeaderField: headerWSExtensionName)
|
||||
}
|
||||
addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)")
|
||||
for (key, value) in headers {
|
||||
addHeader(urlRequest, key: key, val: value)
|
||||
}
|
||||
if let cfHTTPMessage = CFHTTPMessageCopySerializedMessage(urlRequest) {
|
||||
let serializedRequest = cfHTTPMessage.takeRetainedValue()
|
||||
initStreamsWithData(serializedRequest as Data, Int(port!))
|
||||
self.advancedDelegate?.websocketHttpUpgrade(socket: self, request: urlRequest)
|
||||
}
|
||||
}
|
||||
request.setValue("\(url.host!):\(port!)", forHTTPHeaderField: headerWSHostName)
|
||||
|
||||
/**
|
||||
Add a header to the CFHTTPMessage by using the NSString bridges to CFString
|
||||
*/
|
||||
private func addHeader(_ urlRequest: CFHTTPMessage, key: String, val: String) {
|
||||
CFHTTPMessageSetHeaderFieldValue(urlRequest, key as CFString, val as CFString)
|
||||
var path = url.absoluteString
|
||||
let offset = (url.scheme?.count ?? 2) + 3
|
||||
path = String(path[path.index(path.startIndex, offsetBy: offset)..<path.endIndex])
|
||||
if let range = path.range(of: "/") {
|
||||
path = String(path[range.lowerBound..<path.endIndex])
|
||||
} else {
|
||||
path = "/"
|
||||
if let query = url.query {
|
||||
path += "?" + query
|
||||
}
|
||||
}
|
||||
|
||||
var httpBody = "\(request.httpMethod ?? "GET") \(path) HTTP/1.1\r\n"
|
||||
if let headers = request.allHTTPHeaderFields {
|
||||
for (key, val) in headers {
|
||||
httpBody += "\(key): \(val)\r\n"
|
||||
}
|
||||
}
|
||||
httpBody += "\r\n"
|
||||
|
||||
initStreamsWithData(httpBody.data(using: .utf8)!, Int(port!))
|
||||
advancedDelegate?.websocketHttpUpgrade(socket: self, request: httpBody)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,126 +632,86 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
Start the stream connection and write the data to the output stream.
|
||||
*/
|
||||
private func initStreamsWithData(_ data: Data, _ port: Int) {
|
||||
//higher level API we will cut over to at some point
|
||||
//NSStream.getStreamsToHostWithName(url.host, port: url.port.integerValue, inputStream: &inputStream, outputStream: &outputStream)
|
||||
|
||||
guard let url = request.url else {
|
||||
disconnectStream(nil, runDelegate: true)
|
||||
return
|
||||
|
||||
}
|
||||
// Disconnect and clean up any existing streams before setting up a new pair
|
||||
disconnectStream(nil, runDelegate: false)
|
||||
|
||||
var readStream: Unmanaged<CFReadStream>?
|
||||
var writeStream: Unmanaged<CFWriteStream>?
|
||||
let h = url.host! as NSString
|
||||
CFStreamCreatePairWithSocketToHost(nil, h, 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!) {
|
||||
certValidated = false
|
||||
inStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
outStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
if disableSSLCertValidation {
|
||||
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)
|
||||
let useSSL = supportedSSLSchemes.contains(url.scheme!)
|
||||
#if os(Linux)
|
||||
let settings = SSLSettings(useSSL: useSSL,
|
||||
disableCertValidation: disableSSLCertValidation,
|
||||
overrideTrustHostname: overrideTrustHostname,
|
||||
desiredTrustHostname: desiredTrustHostname)
|
||||
#else
|
||||
let settings = SSLSettings(useSSL: useSSL,
|
||||
disableCertValidation: disableSSLCertValidation,
|
||||
overrideTrustHostname: overrideTrustHostname,
|
||||
desiredTrustHostname: desiredTrustHostname,
|
||||
cipherSuites: self.enabledSSLCipherSuites)
|
||||
#endif
|
||||
certValidated = !useSSL
|
||||
let timeout = request.timeoutInterval * 1_000_000
|
||||
stream.delegate = self
|
||||
stream.connect(url: url, port: port, timeout: timeout, ssl: settings, completion: { [weak self] (error) in
|
||||
guard let s = self else {return}
|
||||
if error != nil {
|
||||
//do disconnect
|
||||
return
|
||||
}
|
||||
if let cipherSuites = self.enabledSSLCipherSuites {
|
||||
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 {
|
||||
WebSocket.sharedWorkQueue.async { [weak self] in
|
||||
let error = self?.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn))
|
||||
self?.disconnectStream(error)
|
||||
let operation = BlockOperation()
|
||||
operation.addExecutionBlock { [weak self, weak operation] in
|
||||
guard let sOperation = operation, let s = self else { return }
|
||||
guard !sOperation.isCancelled else { return }
|
||||
// Do the pinning now if needed
|
||||
#if os(Linux) || os(watchOS)
|
||||
s.certValidated = false
|
||||
#else
|
||||
if let sec = s.security, !s.certValidated {
|
||||
let trustObj = s.stream.sslTrust()
|
||||
if let possibleTrust = trustObj.trust {
|
||||
s.certValidated = sec.isValid(possibleTrust, domain: trustObj.domain)
|
||||
} else {
|
||||
s.certValidated = false
|
||||
}
|
||||
return
|
||||
}
|
||||
if resOut != errSecSuccess {
|
||||
WebSocket.sharedWorkQueue.async { [weak self] in
|
||||
let error = self?.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut))
|
||||
self?.disconnectStream(error)
|
||||
if !s.certValidated {
|
||||
let errCode = InternalErrorCode.invalidSSLError.rawValue
|
||||
let error = s.errorWithDetail("Invalid SSL certificate", code: errCode)
|
||||
s.disconnectStream(error)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
#endif
|
||||
let _ = s.stream.write(data: data)
|
||||
}
|
||||
} else {
|
||||
certValidated = true //not a https session, so no need to check SSL pinning
|
||||
}
|
||||
|
||||
CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue)
|
||||
CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue)
|
||||
inStream.open()
|
||||
outStream.open()
|
||||
s.writeQueue.addOperation(operation)
|
||||
})
|
||||
|
||||
self.readyToWriteMutex.lock()
|
||||
self.readyToWrite = true
|
||||
self.readyToWriteMutex.unlock()
|
||||
|
||||
let bytes = UnsafeRawPointer((data as NSData).bytes).assumingMemoryBound(to: UInt8.self)
|
||||
var out = timeout * 1_000_000 // wait 5 seconds before giving up
|
||||
let operation = BlockOperation()
|
||||
operation.addExecutionBlock { [weak self, weak operation] in
|
||||
guard let sOperation = operation else { return }
|
||||
while !outStream.hasSpaceAvailable && !sOperation.isCancelled {
|
||||
usleep(100) // wait until the socket is ready
|
||||
guard !sOperation.isCancelled else { return }
|
||||
out -= 100
|
||||
if out < 0 {
|
||||
WebSocket.sharedWorkQueue.async {
|
||||
self?.cleanupStream()
|
||||
}
|
||||
let errCode = InternalErrorCode.writeTimeoutError.rawValue
|
||||
self?.doDisconnect(self?.errorWithDetail("write wait timed out", code: errCode))
|
||||
return
|
||||
} else if outStream.streamError != nil {
|
||||
return // disconnectStream will be called.
|
||||
}
|
||||
}
|
||||
guard !sOperation.isCancelled, let s = self else { return }
|
||||
// Do the pinning now if needed
|
||||
if let sec = s.security, !s.certValidated {
|
||||
if let possibleTrust = outStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) {
|
||||
let domain = outStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as? String
|
||||
s.certValidated = sec.isValid(possibleTrust as! SecTrust, domain: domain)
|
||||
} else {
|
||||
s.certValidated = false
|
||||
}
|
||||
if !s.certValidated {
|
||||
WebSocket.sharedWorkQueue.async {
|
||||
let errCode = InternalErrorCode.invalidSSLError.rawValue
|
||||
let error = s.errorWithDetail("Invalid SSL certificate", code: errCode)
|
||||
s.disconnectStream(error)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
outStream.write(bytes, maxLength: data.count)
|
||||
}
|
||||
writeQueue.addOperation(operation)
|
||||
}
|
||||
|
||||
/**
|
||||
Delegate for the stream methods. Processes incoming bytes
|
||||
*/
|
||||
open func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
|
||||
if eventCode == .hasBytesAvailable {
|
||||
if aStream == inputStream {
|
||||
processInputStream()
|
||||
}
|
||||
} else if eventCode == .errorOccurred {
|
||||
disconnectStream(aStream.streamError as NSError?)
|
||||
} else if eventCode == .endEncountered {
|
||||
disconnectStream(nil)
|
||||
}
|
||||
|
||||
public func newBytesInStream() {
|
||||
processInputStream()
|
||||
}
|
||||
|
||||
public func streamDidError(error: Error?) {
|
||||
disconnectStream(error)
|
||||
}
|
||||
|
||||
/**
|
||||
Disconnect the stream object and notifies the delegate.
|
||||
*/
|
||||
private func disconnectStream(_ error: NSError?, runDelegate: Bool = true) {
|
||||
private func disconnectStream(_ error: Error?, runDelegate: Bool = true) {
|
||||
if error == nil {
|
||||
writeQueue.waitUntilAllOperationsAreFinished()
|
||||
} else {
|
||||
@@ -524,18 +730,7 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
cleanup the streams.
|
||||
*/
|
||||
private func cleanupStream() {
|
||||
outputStream?.delegate = nil
|
||||
inputStream?.delegate = nil
|
||||
if let stream = inputStream {
|
||||
CFReadStreamSetDispatchQueue(stream, nil)
|
||||
stream.close()
|
||||
}
|
||||
if let stream = outputStream {
|
||||
CFWriteStreamSetDispatchQueue(stream, nil)
|
||||
stream.close()
|
||||
}
|
||||
outputStream = nil
|
||||
inputStream = nil
|
||||
stream.cleanup()
|
||||
fragBuffer = nil
|
||||
}
|
||||
|
||||
@@ -543,15 +738,13 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
Handles the incoming bytes and sending them to the proper processing method.
|
||||
*/
|
||||
private func processInputStream() {
|
||||
let buf = NSMutableData(capacity: BUFFER_MAX)
|
||||
let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self)
|
||||
let length = inputStream!.read(buffer, maxLength: BUFFER_MAX)
|
||||
guard length > 0 else { return }
|
||||
let data = stream.read()
|
||||
guard let d = data else { return }
|
||||
var process = false
|
||||
if inputQueue.count == 0 {
|
||||
process = true
|
||||
}
|
||||
inputQueue.append(Data(bytes: buffer, count: length))
|
||||
inputQueue.append(d)
|
||||
if process {
|
||||
dequeueInput()
|
||||
}
|
||||
@@ -609,7 +802,7 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
for i in 0..<bufferLen {
|
||||
if buffer[i] == CRLFBytes[k] {
|
||||
k += 1
|
||||
if k == 3 {
|
||||
if k == 4 {
|
||||
totalSize = i + 1
|
||||
break
|
||||
}
|
||||
@@ -633,10 +826,10 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
s.onConnect?()
|
||||
s.delegate?.websocketDidConnect(socket: s)
|
||||
s.advancedDelegate?.websocketDidConnect(socket: s)
|
||||
s.notificationCenter.post(name: NSNotification.Name(WebsocketDidConnectNotification), object: self)
|
||||
NotificationCenter.default.post(name: NSNotification.Name(WebsocketDidConnectNotification), object: self)
|
||||
}
|
||||
}
|
||||
totalSize += 1 //skip the last \n
|
||||
//totalSize += 1 //skip the last \n
|
||||
let restSize = bufferLen - totalSize
|
||||
if restSize > 0 {
|
||||
processRawMessagesInBuffer(buffer + totalSize, bufferLen: restSize)
|
||||
@@ -650,29 +843,45 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
Validates the HTTP is a 101 as per the RFC spec.
|
||||
*/
|
||||
private func validateResponse(_ buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Int {
|
||||
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue()
|
||||
CFHTTPMessageAppendBytes(response, buffer, bufferLen)
|
||||
let code = CFHTTPMessageGetResponseStatusCode(response)
|
||||
self.advancedDelegate?.websocketHttpUpgrade(socket: self, response: response)
|
||||
guard let str = String(data: Data(bytes: buffer, count: bufferLen), encoding: .utf8) else { return -1 }
|
||||
let splitArr = str.components(separatedBy: "\r\n")
|
||||
var code = -1
|
||||
var i = 0
|
||||
var headers = [String: String]()
|
||||
for str in splitArr {
|
||||
if i == 0 {
|
||||
let responseSplit = str.components(separatedBy: .whitespaces)
|
||||
guard responseSplit.count > 1 else { return -1 }
|
||||
if let c = Int(responseSplit[1]) {
|
||||
code = c
|
||||
}
|
||||
} else {
|
||||
let responseSplit = str.components(separatedBy: ":")
|
||||
guard responseSplit.count > 1 else { break }
|
||||
let key = responseSplit[0].trimmingCharacters(in: .whitespaces)
|
||||
let val = responseSplit[1].trimmingCharacters(in: .whitespaces)
|
||||
headers[key.lowercased()] = val
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
advancedDelegate?.websocketHttpUpgrade(socket: self, response: str)
|
||||
if code != httpSwitchProtocolCode {
|
||||
return code
|
||||
}
|
||||
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
|
||||
let headers = cfHeaders.takeRetainedValue() as NSDictionary
|
||||
if let extensionHeader = headers[headerWSExtensionName as NSString] as? String {
|
||||
processExtensionHeader(extensionHeader)
|
||||
}
|
||||
|
||||
if let acceptKey = headers[headerWSAcceptName as NSString] as? NSString {
|
||||
if acceptKey.length > 0 {
|
||||
if headerSecKey.characters.count > 0 {
|
||||
let sha = "\(headerSecKey)258EAFA5-E914-47DA-95CA-C5AB0DC85B11".sha1Base64()
|
||||
if sha != acceptKey as String {
|
||||
return -1
|
||||
}
|
||||
|
||||
if let extensionHeader = headers[headerWSExtensionName.lowercased()] {
|
||||
processExtensionHeader(extensionHeader)
|
||||
}
|
||||
|
||||
if let acceptKey = headers[headerWSAcceptName.lowercased()] {
|
||||
if acceptKey.count > 0 {
|
||||
if headerSecKey.count > 0 {
|
||||
let sha = "\(headerSecKey)258EAFA5-E914-47DA-95CA-C5AB0DC85B11".sha1Base64()
|
||||
if sha != acceptKey as String {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return -1
|
||||
@@ -802,7 +1011,7 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
closeCode = CloseCode.protocolError.rawValue
|
||||
} else if payloadLen > 1 {
|
||||
closeCode = WebSocket.readUint16(baseAddress, offset: offset)
|
||||
if closeCode < 1000 || (closeCode > 1003 && closeCode < 1007) || (closeCode > 1011 && closeCode < 3000) {
|
||||
if closeCode < 1000 || (closeCode > 1003 && closeCode < 1007) || (closeCode > 1013 && closeCode < 3000) {
|
||||
closeCode = CloseCode.protocolError.rawValue
|
||||
}
|
||||
}
|
||||
@@ -840,7 +1049,7 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
if compressionState.messageNeedsDecompression, let decompressor = compressionState.decompressor {
|
||||
do {
|
||||
data = try decompressor.decompress(bytes: baseAddress+offset, count: Int(len), finish: isFin > 0)
|
||||
if isFin > 0 && compressionState.serverNoContextTakeover{
|
||||
if isFin > 0 && compressionState.serverNoContextTakeover {
|
||||
try decompressor.reset()
|
||||
}
|
||||
} catch {
|
||||
@@ -946,20 +1155,21 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
private func processResponse(_ response: WSResponse) -> Bool {
|
||||
if response.isFin && response.bytesLeft <= 0 {
|
||||
if response.code == .ping {
|
||||
let data = response.buffer! // local copy so it is perverse for writing
|
||||
dequeueWrite(data as Data, code: .pong)
|
||||
if respondToPingWithPong {
|
||||
let data = response.buffer! // local copy so it is perverse for writing
|
||||
dequeueWrite(data as Data, code: .pong)
|
||||
}
|
||||
} else if response.code == .textFrame {
|
||||
let str: NSString? = NSString(data: response.buffer! as Data, encoding: String.Encoding.utf8.rawValue)
|
||||
if str == nil {
|
||||
guard let str = String(data: response.buffer! as Data, encoding: .utf8) else {
|
||||
writeError(CloseCode.encoding.rawValue)
|
||||
return false
|
||||
}
|
||||
if canDispatch {
|
||||
callbackQueue.async { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.onText?(str! as String)
|
||||
s.delegate?.websocketDidReceiveMessage(socket: s, text: str! as String)
|
||||
s.advancedDelegate?.websocketDidReceiveMessage(socket: s, text: str! as String, response: response)
|
||||
s.onText?(str)
|
||||
s.delegate?.websocketDidReceiveMessage(socket: s, text: str)
|
||||
s.advancedDelegate?.websocketDidReceiveMessage(socket: s, text: str, response: response)
|
||||
}
|
||||
}
|
||||
} else if response.code == .binaryFrame {
|
||||
@@ -982,10 +1192,10 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
/**
|
||||
Create an error
|
||||
*/
|
||||
private func errorWithDetail(_ detail: String, code: UInt16) -> NSError {
|
||||
private func errorWithDetail(_ detail: String, code: UInt16) -> Error {
|
||||
var details = [String: String]()
|
||||
details[NSLocalizedDescriptionKey] = detail
|
||||
return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details)
|
||||
return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details) as Error
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1047,18 +1257,14 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
}
|
||||
var total = 0
|
||||
while !sOperation.isCancelled {
|
||||
guard let outStream = s.outputStream else { break }
|
||||
let stream = s.stream
|
||||
let writeBuffer = UnsafeRawPointer(frame!.bytes+total).assumingMemoryBound(to: UInt8.self)
|
||||
let len = outStream.write(writeBuffer, maxLength: offset-total)
|
||||
if len < 0 {
|
||||
let len = stream.write(data: Data(bytes: writeBuffer, count: offset-total))
|
||||
if len <= 0 {
|
||||
var error: Error?
|
||||
if let streamError = outStream.streamError {
|
||||
error = streamError
|
||||
} else {
|
||||
let errCode = InternalErrorCode.outputStreamWriteError.rawValue
|
||||
error = s.errorWithDetail("output stream error during write", code: errCode)
|
||||
}
|
||||
s.doDisconnect(error as NSError?)
|
||||
s.doDisconnect(error)
|
||||
break
|
||||
} else {
|
||||
total += len
|
||||
@@ -1080,7 +1286,7 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
/**
|
||||
Used to preform the disconnect delegate
|
||||
*/
|
||||
private func doDisconnect(_ error: NSError?) {
|
||||
private func doDisconnect(_ error: Error?) {
|
||||
guard !didDisconnect else { return }
|
||||
didDisconnect = true
|
||||
isConnecting = false
|
||||
@@ -1094,7 +1300,7 @@ open class WebSocket : NSObject, StreamDelegate {
|
||||
s.delegate?.websocketDidDisconnect(socket: s, error: error)
|
||||
s.advancedDelegate?.websocketDidDisconnect(socket: s, error: error)
|
||||
let userInfo = error.map{ [WebsocketDisconnectionErrorKeyName: $0] }
|
||||
s.notificationCenter.post(name: NSNotification.Name(WebsocketDidDisconnectNotification), object: self, userInfo: userInfo)
|
||||
NotificationCenter.default.post(name: NSNotification.Name(WebsocketDidDisconnectNotification), object: self, userInfo: userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -1,7 +1,7 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Starscream"
|
||||
s.version = "2.1.1"
|
||||
s.summary = "A conforming WebSocket RFC 6455 client library in Swift for iOS and OSX."
|
||||
s.version = "3.0.3"
|
||||
s.summary = "A conforming WebSocket RFC 6455 client library in Swift."
|
||||
s.homepage = "https://github.com/daltoniam/Starscream"
|
||||
s.license = 'Apache License, Version 2.0'
|
||||
s.author = {'Dalton Cherry' => 'http://daltoniam.com', 'Austin Cherry' => 'http://austincherry.me'}
|
||||
@@ -10,10 +10,11 @@ Pod::Spec.new do |s|
|
||||
s.ios.deployment_target = '8.0'
|
||||
s.osx.deployment_target = '10.10'
|
||||
s.tvos.deployment_target = '9.0'
|
||||
s.source_files = 'Source/*.swift'
|
||||
s.requires_arc = 'true'
|
||||
s.watchos.deployment_target = '2.0'
|
||||
s.source_files = 'Sources/*.swift'
|
||||
s.libraries = 'z'
|
||||
s.pod_target_xcconfig = {
|
||||
'SWIFT_VERSION' => '4.0',
|
||||
'SWIFT_INCLUDE_PATHS' => '$(PODS_ROOT)/Starscream/zlib'
|
||||
}
|
||||
s.preserve_paths = 'zlib/*'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0810"
|
||||
version = "1.3">
|
||||
LastUpgradeVersion = "0900"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@@ -14,9 +14,9 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79E519D48B7F006071F7"
|
||||
BlueprintIdentifier = "33CCF0841F5DDC030099B092"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream iOS"
|
||||
BlueprintName = "Starscream"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
@@ -26,9 +26,34 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "335FA1F41F5DF71D00F6D2EC"
|
||||
BuildableName = "Starscream Tests.xctest"
|
||||
BlueprintName = "Starscream Tests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
<LocationScenarioReference
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
</LocationScenarioReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CCF0841F5DDC030099B092"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
@@ -36,6 +61,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
@@ -45,9 +71,9 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79E519D48B7F006071F7"
|
||||
BlueprintIdentifier = "33CCF0841F5DDC030099B092"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream iOS"
|
||||
BlueprintName = "Starscream"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
@@ -63,9 +89,9 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79E519D48B7F006071F7"
|
||||
BlueprintIdentifier = "33CCF0841F5DDC030099B092"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream iOS"
|
||||
BlueprintName = "Starscream"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0810"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D9C3E35E19E48FF1009FC285"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream OSX"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D9C3E36819E48FF1009FC285"
|
||||
BuildableName = "Starscream OSXTests.xctest"
|
||||
BlueprintName = "Starscream OSXTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D9C3E36819E48FF1009FC285"
|
||||
BuildableName = "Starscream OSXTests.xctest"
|
||||
BlueprintName = "Starscream OSXTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D9C3E35E19E48FF1009FC285"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream OSX"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D9C3E35E19E48FF1009FC285"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream OSX"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D9C3E35E19E48FF1009FC285"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream OSX"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,99 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0810"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79F019D48B7F006071F7"
|
||||
BuildableName = "Starscream iOSTests.xctest"
|
||||
BlueprintName = "Starscream iOSTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79F019D48B7F006071F7"
|
||||
BuildableName = "Starscream iOSTests.xctest"
|
||||
BlueprintName = "Starscream iOSTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79F019D48B7F006071F7"
|
||||
BuildableName = "Starscream iOSTests.xctest"
|
||||
BlueprintName = "Starscream iOSTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79F019D48B7F006071F7"
|
||||
BuildableName = "Starscream iOSTests.xctest"
|
||||
BlueprintName = "Starscream iOSTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79F019D48B7F006071F7"
|
||||
BuildableName = "Starscream iOSTests.xctest"
|
||||
BlueprintName = "Starscream iOSTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,113 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0810"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "091277961BD673A70003036D"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream tvOS"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0912779F1BD673A70003036D"
|
||||
BuildableName = "Starscream tvOSTests.xctest"
|
||||
BlueprintName = "Starscream tvOSTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0912779F1BD673A70003036D"
|
||||
BuildableName = "Starscream tvOSTests.xctest"
|
||||
BlueprintName = "Starscream tvOSTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "091277961BD673A70003036D"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream tvOS"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "091277961BD673A70003036D"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream tvOS"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "091277961BD673A70003036D"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream tvOS"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -34,7 +34,7 @@ class ViewController: UIViewController {
|
||||
self?.caseCount = c
|
||||
}
|
||||
}
|
||||
s.onDisconnect = { [weak self, weak s] (error: NSError?) in
|
||||
s.onDisconnect = { [weak self, weak s] (error: Error?) in
|
||||
self?.getTestInfo(1)
|
||||
self?.removeSocket(s)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class ViewController: UIViewController {
|
||||
|
||||
}
|
||||
var once = false
|
||||
s.onDisconnect = { [weak self, weak s] (error: NSError?) in
|
||||
s.onDisconnect = { [weak self, weak s] (error: Error?) in
|
||||
if !once {
|
||||
once = true
|
||||
self?.runTest(caseNum)
|
||||
@@ -82,11 +82,18 @@ class ViewController: UIViewController {
|
||||
s?.write(data: data)
|
||||
}
|
||||
var once = false
|
||||
s.onDisconnect = {[weak self, weak s] (error: NSError?) in
|
||||
s.onDisconnect = {[weak self, weak s] (error: Error?) in
|
||||
if !once {
|
||||
once = true
|
||||
print("case:\(caseNum) finished")
|
||||
self?.verifyTest(caseNum)
|
||||
//self?.verifyTest(caseNum) //disabled since it slows down the tests
|
||||
let nextCase = caseNum+1
|
||||
if nextCase <= (self?.caseCount)! {
|
||||
self?.runTest(nextCase)
|
||||
//self?.getTestInfo(nextCase) //disabled since it slows down the tests
|
||||
} else {
|
||||
self?.finishReports()
|
||||
}
|
||||
self?.removeSocket(s)
|
||||
}
|
||||
}
|
||||
@@ -115,10 +122,11 @@ class ViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
var once = false
|
||||
s.onDisconnect = { [weak self, weak s] (error: NSError?) in
|
||||
s.onDisconnect = { [weak self, weak s] (error: Error?) in
|
||||
if !once {
|
||||
once = true
|
||||
let nextCase = caseNum+1
|
||||
print("next test is: \(nextCase)")
|
||||
if nextCase <= (self?.caseCount)! {
|
||||
self?.getTestInfo(nextCase)
|
||||
} else {
|
||||
@@ -133,7 +141,7 @@ class ViewController: UIViewController {
|
||||
func finishReports() {
|
||||
let s = createSocket("updateReports",0)
|
||||
self.socketArray.append(s)
|
||||
s.onDisconnect = { [weak self, weak s] (error: NSError?) in
|
||||
s.onDisconnect = { [weak self, weak s] (error: Error?) in
|
||||
print("finished all the tests!")
|
||||
self?.removeSocket(s)
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
6B3E7A0E19D48D00006071F7 /* Starscream.framework */,
|
||||
6B3E7A1019D48D00006071F7 /* Starscream iOSTests.xctest */,
|
||||
5C06AE8B1B08044600D41060 /* Starscream.framework */,
|
||||
5C06AE8D1B08044600D41060 /* StarscreamOSXTests.xctest */,
|
||||
5C06AE8D1B08044600D41060 /* Starscream OSXTests.xctest */,
|
||||
5C42C3E11D8F31DC00947AA2 /* Starscream.framework */,
|
||||
5C42C3E31D8F31DC00947AA2 /* Starscream tvOSTests.xctest */,
|
||||
);
|
||||
@@ -181,7 +181,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0700;
|
||||
LastUpgradeCheck = 0800;
|
||||
LastUpgradeCheck = 0900;
|
||||
ORGANIZATIONNAME = vluxe;
|
||||
TargetAttributes = {
|
||||
5C765ADA199A6DAA003D9110 = {
|
||||
@@ -311,14 +311,20 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
@@ -358,14 +364,20 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
@@ -399,7 +411,7 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.io.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -412,7 +424,7 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.io.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
BIN
Binary file not shown.
+3
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0800"
|
||||
LastUpgradeVersion = "0900"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -40,6 +40,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
@@ -69,6 +70,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
@@ -10,7 +10,7 @@ import UIKit
|
||||
import Starscream
|
||||
|
||||
class ViewController: UIViewController, WebSocketDelegate {
|
||||
var socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat", "superchat"])
|
||||
var socket = WebSocket(url: URL(string: "http://echo.websocket.org")!, protocols: ["chat", "superchat"])
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
@@ -20,11 +20,11 @@ class ViewController: UIViewController, WebSocketDelegate {
|
||||
|
||||
// MARK: Websocket Delegate Methods.
|
||||
|
||||
func websocketDidConnect(socket: WebSocket) {
|
||||
func websocketDidConnect(socket: WebSocketClient) {
|
||||
print("websocket is connected")
|
||||
}
|
||||
|
||||
func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
|
||||
func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
|
||||
if let e = error {
|
||||
print("websocket is disconnected: \(e.localizedDescription)")
|
||||
} else {
|
||||
@@ -32,11 +32,11 @@ class ViewController: UIViewController, WebSocketDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func websocketDidReceiveMessage(socket: WebSocket, text: String) {
|
||||
func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
|
||||
print("Received text: \(text)")
|
||||
}
|
||||
|
||||
func websocketDidReceiveData(socket: WebSocket, data: Data) {
|
||||
func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
|
||||
print("Received data: \(data.count)")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user