Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 114e5df9b6 | |||
| 430bfc32c3 | |||
| 31f522155a | |||
| 327a7d8e9e | |||
| 6e10c04c83 | |||
| 97401e04bc | |||
| 6ba3563148 | |||
| 146368c71a | |||
| 4ea6f4be3a | |||
| 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 | |||
| 789264eeff | |||
| 644e8f92c0 |
+1
-1
@@ -1 +1 @@
|
||||
4.0
|
||||
4.1
|
||||
|
||||
+80
-2
@@ -2,6 +2,84 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
`Starscream` adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
#### [3.0.5](https://github.com/daltoniam/Starscream/tree/3.0.5)
|
||||
|
||||
Swift 4.1 support and bug fixes.
|
||||
|
||||
Pull Requests:
|
||||
[#492](https://github.com/daltoniam/Starscream/pull/492)
|
||||
[#461](https://github.com/daltoniam/Starscream/pull/461)
|
||||
[#476](https://github.com/daltoniam/Starscream/pull/476)
|
||||
|
||||
Issues:
|
||||
[#494](https://github.com/daltoniam/Starscream/issues/494)
|
||||
[#491](https://github.com/daltoniam/Starscream/issues/491)
|
||||
[#474](https://github.com/daltoniam/Starscream/issues/474)
|
||||
[#471](https://github.com/daltoniam/Starscream/issues/471)
|
||||
[#437](https://github.com/daltoniam/Starscream/issues/437)
|
||||
[#445](https://github.com/daltoniam/Starscream/issues/445)
|
||||
[#466](https://github.com/daltoniam/Starscream/issues/466)
|
||||
|
||||
|
||||
#### [3.0.4](https://github.com/daltoniam/Starscream/tree/3.0.4)
|
||||
|
||||
Improved error handling. Timeout fix. Small assorted fixes.
|
||||
|
||||
Pull Requests:
|
||||
[#452](https://github.com/daltoniam/Starscream/pull/452)
|
||||
[#448](https://github.com/daltoniam/Starscream/pull/448)
|
||||
[#444](https://github.com/daltoniam/Starscream/pull/444)
|
||||
[#443](https://github.com/daltoniam/Starscream/pull/443)
|
||||
|
||||
Issues:
|
||||
[#415](https://github.com/daltoniam/Starscream/issues/415)
|
||||
[#422](https://github.com/daltoniam/Starscream/issues/422)
|
||||
[#429](https://github.com/daltoniam/Starscream/issues/429)
|
||||
[#433](https://github.com/daltoniam/Starscream/issues/433)
|
||||
[#439](https://github.com/daltoniam/Starscream/issues/439)
|
||||
|
||||
#### [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:
|
||||
@@ -9,8 +87,8 @@ Major refactor and Swift 4 support. Additions include:
|
||||
- Watchos support.
|
||||
- Linux support.
|
||||
- New Stream class to allow custom socket implementations if desired.
|
||||
- Protocol add for mocking (dependency injection)
|
||||
- Single framework (no more platform suffixes! e.g. StarscreamOSX, StarscreamTVOS, etc)
|
||||
- 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)
|
||||
|
||||
@@ -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.
|
||||
@@ -261,7 +280,7 @@ To use Starscream in your project add the following 'Podfile' to your project
|
||||
platform :ios, '9.0'
|
||||
use_frameworks!
|
||||
|
||||
pod 'Starscream', '~> 3.0.0'
|
||||
pod 'Starscream', '~> 3.0.2'
|
||||
|
||||
Then run:
|
||||
|
||||
@@ -283,7 +302,7 @@ $ brew install carthage
|
||||
To integrate Starscream into your Xcode project using Carthage, specify it in your `Cartfile`:
|
||||
|
||||
```
|
||||
github "daltoniam/Starscream" >= 3.0.0
|
||||
github "daltoniam/Starscream" >= 3.0.2
|
||||
```
|
||||
|
||||
### Rogue
|
||||
@@ -332,7 +351,7 @@ In most cases you do not need the extra info and should use the normal delegate.
|
||||
|
||||
#### websocketDidReceiveMessage
|
||||
```swift
|
||||
func websocketDidReceiveMessage(socket: WebSocketClient, 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)")
|
||||
}
|
||||
@@ -360,6 +379,11 @@ func websocketHttpUpgrade(socket: WebSocketClient, response: CFHTTPMessage) {
|
||||
}
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
- [ ] Add Unit Tests - Local WebSocket server that runs against Autobahn
|
||||
|
||||
@@ -52,7 +52,7 @@ class Decompressor {
|
||||
|
||||
func reset() throws {
|
||||
teardownInflate()
|
||||
guard initInflate() else { throw NSError() }
|
||||
guard initInflate() else { throw WSError(type: .compressionError, message: "Error for decompressor on reset", code: 0) }
|
||||
}
|
||||
|
||||
func decompress(_ data: Data, finish: Bool) throws -> Data {
|
||||
@@ -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(InternalErrorCode.compressionError.rawValue), userInfo: nil)
|
||||
throw WSError(type: .compressionError, message: "Error on decompressing", code: 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ class Compressor {
|
||||
|
||||
func reset() throws {
|
||||
teardownDeflate()
|
||||
guard initDeflate() else { throw NSError() }
|
||||
guard initDeflate() else { throw WSError(type: .compressionError, message: "Error for compressor on reset", code: 0) }
|
||||
}
|
||||
|
||||
func compress(_ data: Data) throws -> Data {
|
||||
@@ -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(InternalErrorCode.compressionError.rawValue), userInfo: nil)
|
||||
throw WSError(type: .compressionError, message: "Error on compressing", code: 0)
|
||||
}
|
||||
|
||||
compressed.removeLast(4)
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.0</string>
|
||||
<string>3.0.5</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -57,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
|
||||
@@ -170,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 {
|
||||
|
||||
+176
-105
@@ -41,33 +41,41 @@ public enum CloseCode : UInt16 {
|
||||
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
|
||||
public enum ErrorType: Error {
|
||||
case outputStreamWriteError //output stream error during write
|
||||
case compressionError
|
||||
case invalidSSLError //Invalid SSL certificate
|
||||
case writeTimeoutError //The socket timed out waiting to be ready to write
|
||||
case protocolError //There was an error parsing the WebSocket frames
|
||||
case upgradeError //There was an error during the HTTP upgrade
|
||||
case closeError //There was an error during the close (socket probably has been dereferenced)
|
||||
}
|
||||
|
||||
public struct WSError: Error {
|
||||
public let type: ErrorType
|
||||
public let message: String
|
||||
public let code: Int
|
||||
}
|
||||
|
||||
//WebSocketClient is setup to be dependency injection for testing
|
||||
public protocol WebSocketClient: class {
|
||||
var delegate: WebSocketDelegate? {get set }
|
||||
|
||||
var disableSSLCertValidation: Bool { get set }
|
||||
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 }
|
||||
var security: SSLTrustValidator? {get set}
|
||||
var enabledSSLCipherSuites: [SSLCipherSuite]? {get set}
|
||||
#endif
|
||||
var isConnected: Bool { get }
|
||||
|
||||
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
|
||||
@@ -83,6 +91,10 @@ extension WebSocketClient {
|
||||
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)
|
||||
@@ -91,11 +103,13 @@ extension WebSocketClient {
|
||||
|
||||
//SSL settings for the stream
|
||||
public struct SSLSettings {
|
||||
let useSSL: Bool
|
||||
let disableCertValidation: Bool
|
||||
public let useSSL: Bool
|
||||
public let disableCertValidation: Bool
|
||||
public var overrideTrustHostname: Bool
|
||||
public var desiredTrustHostname: String?
|
||||
#if os(Linux)
|
||||
#else
|
||||
let cipherSuites: [SSLCipherSuite]?
|
||||
public let cipherSuites: [SSLCipherSuite]?
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -106,7 +120,7 @@ public protocol WSStreamDelegate: class {
|
||||
|
||||
//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}
|
||||
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?
|
||||
@@ -123,6 +137,8 @@ open class FoundationStream : NSObject, WSStream, StreamDelegate {
|
||||
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>?
|
||||
@@ -131,20 +147,41 @@ open class FoundationStream : NSObject, WSStream, StreamDelegate {
|
||||
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 ssl.disableCertValidation {
|
||||
#if os(watchOS) //watchOS us unfortunately is missing the kCFStream properties to make this work
|
||||
#else
|
||||
let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(value: false), kCFStreamSSLPeerName: kCFNull]
|
||||
#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
|
||||
}
|
||||
#endif
|
||||
|
||||
#if os(Linux)
|
||||
#else
|
||||
if let cipherSuites = ssl.cipherSuites {
|
||||
@@ -155,10 +192,10 @@ open class FoundationStream : NSObject, WSStream, StreamDelegate {
|
||||
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)))
|
||||
completion(WSError(type: .invalidSSLError, message: "Error setting ingoing cypher suites", code: Int(resIn)))
|
||||
}
|
||||
if resOut != errSecSuccess {
|
||||
completion(errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut)))
|
||||
completion(WSError(type: .invalidSSLError, message: "Error setting outgoing cypher suites", code: Int(resOut)))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -177,13 +214,14 @@ open class FoundationStream : NSObject, WSStream, StreamDelegate {
|
||||
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))
|
||||
completion(WSError(type: .writeTimeoutError, message: "Timed out waiting for the socket to be ready for a write", code: 0))
|
||||
return
|
||||
} else if let error = outStream.streamError {
|
||||
completion(error)
|
||||
return // disconnectStream will be called.
|
||||
} else if self == nil {
|
||||
completion(WSError(type: .closeError, message: "socket object has been dereferenced", code: 0))
|
||||
return
|
||||
}
|
||||
}
|
||||
completion(nil) //success!
|
||||
@@ -191,15 +229,16 @@ open class FoundationStream : NSObject, WSStream, StreamDelegate {
|
||||
}
|
||||
|
||||
public func write(data: Data) -> Int {
|
||||
guard let outStream = outputStream else {return 0}
|
||||
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 = inputStream!.read(buffer, maxLength: BUFFER_MAX)
|
||||
let length = stream.read(buffer, maxLength: BUFFER_MAX)
|
||||
if length < 1 {
|
||||
return nil
|
||||
}
|
||||
@@ -207,13 +246,13 @@ open class FoundationStream : NSObject, WSStream, StreamDelegate {
|
||||
}
|
||||
|
||||
public func cleanup() {
|
||||
outputStream?.delegate = nil
|
||||
inputStream?.delegate = nil
|
||||
if let stream = inputStream {
|
||||
stream.delegate = nil
|
||||
CFReadStreamSetDispatchQueue(stream, nil)
|
||||
stream.close()
|
||||
}
|
||||
if let stream = outputStream {
|
||||
stream.delegate = nil
|
||||
CFWriteStreamSetDispatchQueue(stream, nil)
|
||||
stream.close()
|
||||
}
|
||||
@@ -225,7 +264,20 @@ open class FoundationStream : NSObject, WSStream, StreamDelegate {
|
||||
#else
|
||||
public func sslTrust() -> (trust: SecTrust?, domain: String?) {
|
||||
let trust = outputStream!.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust?
|
||||
let domain = outputStream!.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as? String
|
||||
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
|
||||
@@ -244,12 +296,6 @@ open class FoundationStream : NSObject, WSStream, StreamDelegate {
|
||||
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
|
||||
@@ -351,6 +397,9 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
public var onPong: ((Data?) -> Void)?
|
||||
|
||||
public var disableSSLCertValidation = false
|
||||
public var overrideTrustHostname = false
|
||||
public var desiredTrustHostname: String? = nil
|
||||
|
||||
public var enableCompression = true
|
||||
#if os(Linux)
|
||||
#else
|
||||
@@ -359,14 +408,16 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
#endif
|
||||
|
||||
public var isConnected: Bool {
|
||||
connectedMutex.lock()
|
||||
mutex.lock()
|
||||
let isConnected = connected
|
||||
connectedMutex.unlock()
|
||||
mutex.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 respondToPingWithPong: Bool = true
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private struct CompressionState {
|
||||
@@ -379,12 +430,11 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
var decompressor:Decompressor? = nil
|
||||
var compressor:Compressor? = nil
|
||||
}
|
||||
|
||||
private var request: URLRequest
|
||||
|
||||
private var stream: WSStream
|
||||
private var connected = false
|
||||
private var isConnecting = false
|
||||
private let connectedMutex = NSLock()
|
||||
private let mutex = NSLock()
|
||||
private var compressionState = CompressionState()
|
||||
private var writeQueue = OperationQueue()
|
||||
private var readStack = [WSResponse]()
|
||||
@@ -394,11 +444,10 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
private var didDisconnect = false
|
||||
private var readyToWrite = false
|
||||
private var headerSecKey = ""
|
||||
private let readyToWriteMutex = NSLock()
|
||||
private var canDispatch: Bool {
|
||||
readyToWriteMutex.lock()
|
||||
mutex.lock()
|
||||
let canWork = readyToWrite
|
||||
readyToWriteMutex.unlock()
|
||||
mutex.unlock()
|
||||
return canWork
|
||||
}
|
||||
|
||||
@@ -505,6 +554,15 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
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.
|
||||
*/
|
||||
@@ -528,14 +586,21 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
let val = "permessage-deflate; client_max_window_bits; server_max_window_bits=15"
|
||||
request.setValue(val, forHTTPHeaderField: headerWSExtensionName)
|
||||
}
|
||||
request.setValue("\(url.host!):\(port!)", forHTTPHeaderField: headerWSHostName)
|
||||
var path = url.path
|
||||
if path.isEmpty {
|
||||
let hostValue = request.allHTTPHeaderFields?[headerWSHostName] ?? "\(url.host!):\(port!)"
|
||||
request.setValue(hostValue, forHTTPHeaderField: headerWSHostName)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
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 {
|
||||
@@ -543,6 +608,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
}
|
||||
}
|
||||
httpBody += "\r\n"
|
||||
|
||||
initStreamsWithData(httpBody.data(using: .utf8)!, Int(port!))
|
||||
advancedDelegate?.websocketHttpUpgrade(socket: self, request: httpBody)
|
||||
}
|
||||
@@ -578,10 +644,15 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
let useSSL = supportedSSLSchemes.contains(url.scheme!)
|
||||
#if os(Linux)
|
||||
let settings = SSLSettings(useSSL: useSSL,
|
||||
disableCertValidation: disableSSLCertValidation)
|
||||
disableCertValidation: disableSSLCertValidation,
|
||||
overrideTrustHostname: overrideTrustHostname,
|
||||
desiredTrustHostname: desiredTrustHostname)
|
||||
#else
|
||||
let settings = SSLSettings(useSSL: useSSL,
|
||||
disableCertValidation: disableSSLCertValidation, cipherSuites: self.enabledSSLCipherSuites)
|
||||
disableCertValidation: disableSSLCertValidation,
|
||||
overrideTrustHostname: overrideTrustHostname,
|
||||
desiredTrustHostname: desiredTrustHostname,
|
||||
cipherSuites: self.enabledSSLCipherSuites)
|
||||
#endif
|
||||
certValidated = !useSSL
|
||||
let timeout = request.timeoutInterval * 1_000_000
|
||||
@@ -589,7 +660,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
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
|
||||
s.disconnectStream(error)
|
||||
return
|
||||
}
|
||||
let operation = BlockOperation()
|
||||
@@ -608,9 +679,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
s.certValidated = false
|
||||
}
|
||||
if !s.certValidated {
|
||||
let errCode = InternalErrorCode.invalidSSLError.rawValue
|
||||
let error = s.errorWithDetail("Invalid SSL certificate", code: errCode)
|
||||
s.disconnectStream(error)
|
||||
s.disconnectStream(WSError(type: .invalidSSLError, message: "Invalid SSL certificate", code: 0))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -620,9 +689,9 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
s.writeQueue.addOperation(operation)
|
||||
})
|
||||
|
||||
self.readyToWriteMutex.lock()
|
||||
self.mutex.lock()
|
||||
self.readyToWrite = true
|
||||
self.readyToWriteMutex.unlock()
|
||||
self.mutex.unlock()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -646,10 +715,11 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
} else {
|
||||
writeQueue.cancelAllOperations()
|
||||
}
|
||||
|
||||
mutex.lock()
|
||||
cleanupStream()
|
||||
connectedMutex.lock()
|
||||
connected = false
|
||||
connectedMutex.unlock()
|
||||
mutex.unlock()
|
||||
if runDelegate {
|
||||
doDisconnect(error)
|
||||
}
|
||||
@@ -717,7 +787,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
fragBuffer = Data(bytes: buffer, count: bufferLen)
|
||||
break // do nothing, we are going to collect more data
|
||||
default:
|
||||
doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: UInt16(code)))
|
||||
doDisconnect(WSError(type: .upgradeError, message: "Invalid HTTP upgrade", code: code))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -745,9 +815,9 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
return code
|
||||
}
|
||||
isConnecting = false
|
||||
connectedMutex.lock()
|
||||
mutex.lock()
|
||||
connected = true
|
||||
connectedMutex.unlock()
|
||||
mutex.unlock()
|
||||
didDisconnect = false
|
||||
if canDispatch {
|
||||
callbackQueue.async { [weak self] in
|
||||
@@ -789,7 +859,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
guard responseSplit.count > 1 else { break }
|
||||
let key = responseSplit[0].trimmingCharacters(in: .whitespaces)
|
||||
let val = responseSplit[1].trimmingCharacters(in: .whitespaces)
|
||||
headers[key] = val
|
||||
headers[key.lowercased()] = val
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
@@ -798,13 +868,13 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
return code
|
||||
}
|
||||
|
||||
if let extensionHeader = headers[headerWSExtensionName] {
|
||||
if let extensionHeader = headers[headerWSExtensionName.lowercased()] {
|
||||
processExtensionHeader(extensionHeader)
|
||||
}
|
||||
|
||||
if let acceptKey = headers[headerWSAcceptName] {
|
||||
if acceptKey.characters.count > 0 {
|
||||
if headerSecKey.characters.count > 0 {
|
||||
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
|
||||
@@ -916,7 +986,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
}
|
||||
if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .pong && !compressionState.messageNeedsDecompression {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(errorWithDetail("masked and rsv data is not currently supported", code: errCode))
|
||||
doDisconnect(WSError(type: .protocolError, message: "masked and rsv data is not currently supported", code: Int(errCode)))
|
||||
writeError(errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
@@ -924,13 +994,13 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
if !isControlFrame && (receivedOpcode != .binaryFrame && receivedOpcode != .continueFrame &&
|
||||
receivedOpcode != .textFrame && receivedOpcode != .pong) {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(errorWithDetail("unknown opcode: \(receivedOpcodeRawValue)", code: errCode))
|
||||
doDisconnect(WSError(type: .protocolError, message: "unknown opcode: \(receivedOpcodeRawValue)", code: Int(errCode)))
|
||||
writeError(errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
if isControlFrame && isFin == 0 {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(errorWithDetail("control frames can't be fragmented", code: errCode))
|
||||
doDisconnect(WSError(type: .protocolError, message: "control frames can't be fragmented", code: Int(errCode)))
|
||||
writeError(errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
@@ -945,7 +1015,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
}
|
||||
}
|
||||
if payloadLen < 2 {
|
||||
doDisconnect(errorWithDetail("connection closed by server", code: closeCode))
|
||||
doDisconnect(WSError(type: .protocolError, message: "connection closed by server", code: Int(closeCode)))
|
||||
writeError(closeCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
@@ -978,13 +1048,13 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
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 {
|
||||
let closeReason = "Decompression failed: \(error)"
|
||||
let closeCode = CloseCode.encoding.rawValue
|
||||
doDisconnect(errorWithDetail(closeReason, code: closeCode))
|
||||
doDisconnect(WSError(type: .protocolError, message: closeReason, code: Int(closeCode)))
|
||||
writeError(closeCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
@@ -999,7 +1069,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
} else {
|
||||
closeCode = CloseCode.protocolError.rawValue
|
||||
}
|
||||
doDisconnect(errorWithDetail(closeReason, code: closeCode))
|
||||
doDisconnect(WSError(type: .protocolError, message: closeReason, code: Int(closeCode)))
|
||||
writeError(closeCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
@@ -1020,7 +1090,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
}
|
||||
if isFin == 0 && receivedOpcode == .continueFrame && response == nil {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(errorWithDetail("continue frame before a binary or text frame", code: errCode))
|
||||
doDisconnect(WSError(type: .protocolError, message: "continue frame before a binary or text frame", code: Int(errCode)))
|
||||
writeError(errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
@@ -1028,8 +1098,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
if response == nil {
|
||||
if receivedOpcode == .continueFrame {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(errorWithDetail("first frame can't be a continue frame",
|
||||
code: errCode))
|
||||
doDisconnect(WSError(type: .protocolError, message: "first frame can't be a continue frame", code: Int(errCode)))
|
||||
writeError(errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
@@ -1043,8 +1112,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
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))
|
||||
doDisconnect(WSError(type: .protocolError, message: "second and beyond of fragment message must be a continue frame", code: Int(errCode)))
|
||||
writeError(errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
@@ -1084,8 +1152,10 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
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 {
|
||||
guard let str = String(data: response.buffer! as Data, encoding: .utf8) else {
|
||||
writeError(CloseCode.encoding.rawValue)
|
||||
@@ -1116,15 +1186,6 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
Create an error
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
Write an error to the socket
|
||||
*/
|
||||
@@ -1184,14 +1245,15 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
}
|
||||
var total = 0
|
||||
while !sOperation.isCancelled {
|
||||
if !s.readyToWrite {
|
||||
s.doDisconnect(WSError(type: .outputStreamWriteError, message: "output stream had an error during write", code: 0))
|
||||
break
|
||||
}
|
||||
let stream = s.stream
|
||||
let writeBuffer = UnsafeRawPointer(frame!.bytes+total).assumingMemoryBound(to: UInt8.self)
|
||||
let len = stream.write(data: Data(bytes: writeBuffer, count: offset-total))
|
||||
if len < 0 {
|
||||
var error: Error?
|
||||
let errCode = InternalErrorCode.outputStreamWriteError.rawValue
|
||||
error = s.errorWithDetail("output stream error during write", code: errCode)
|
||||
s.doDisconnect(error)
|
||||
if len <= 0 {
|
||||
s.doDisconnect(WSError(type: .outputStreamWriteError, message: "output stream had an error during write", code: 0))
|
||||
break
|
||||
} else {
|
||||
total += len
|
||||
@@ -1217,9 +1279,9 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
guard !didDisconnect else { return }
|
||||
didDisconnect = true
|
||||
isConnecting = false
|
||||
connectedMutex.lock()
|
||||
mutex.lock()
|
||||
connected = false
|
||||
connectedMutex.unlock()
|
||||
mutex.unlock()
|
||||
guard canDispatch else {return}
|
||||
callbackQueue.async { [weak self] in
|
||||
guard let s = self else { return }
|
||||
@@ -1234,10 +1296,10 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
// MARK: - Deinit
|
||||
|
||||
deinit {
|
||||
readyToWriteMutex.lock()
|
||||
mutex.lock()
|
||||
readyToWrite = false
|
||||
readyToWriteMutex.unlock()
|
||||
cleanupStream()
|
||||
mutex.unlock()
|
||||
writeQueue.cancelAllOperations()
|
||||
}
|
||||
|
||||
@@ -1269,3 +1331,12 @@ private extension UnsafeBufferPointer {
|
||||
}
|
||||
|
||||
private let emptyBuffer = UnsafeBufferPointer<UInt8>(start: nil, count: 0)
|
||||
|
||||
#if swift(>=4)
|
||||
#else
|
||||
fileprivate extension String {
|
||||
var count: Int {
|
||||
return self.characters.count
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Starscream"
|
||||
s.version = "3.0.0"
|
||||
s.version = "3.0.5"
|
||||
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'
|
||||
@@ -11,9 +11,10 @@ Pod::Spec.new do |s|
|
||||
s.osx.deployment_target = '10.10'
|
||||
s.tvos.deployment_target = '9.0'
|
||||
s.watchos.deployment_target = '2.0'
|
||||
s.source_files = 'Source/*.swift'
|
||||
s.source_files = 'Sources/*.swift'
|
||||
s.libraries = 'z'
|
||||
s.pod_target_xcconfig = {
|
||||
'SWIFT_VERSION' => '4.1',
|
||||
'SWIFT_INCLUDE_PATHS' => '$(PODS_ROOT)/Starscream/zlib'
|
||||
}
|
||||
s.preserve_paths = 'zlib/*'
|
||||
|
||||
@@ -323,6 +323,7 @@
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -333,8 +334,8 @@
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TVOS_DEPLOYMENT_TARGET = 10.0;
|
||||
VALID_ARCHS = "i386 x86_64 armv7s";
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
VALID_ARCHS = "x86_64 i386 arm64 armv7s armv7 armv7k";
|
||||
WATCHOS_DEPLOYMENT_TARGET = 2.0;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -355,6 +356,7 @@
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -365,8 +367,8 @@
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TVOS_DEPLOYMENT_TARGET = 10.0;
|
||||
VALID_ARCHS = "i386 x86_64 armv7s";
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
VALID_ARCHS = "x86_64 i386 arm64 armv7s armv7 armv7k";
|
||||
WATCHOS_DEPLOYMENT_TARGET = 2.0;
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -86,10 +86,11 @@ class ViewController: UIViewController {
|
||||
if !once {
|
||||
once = true
|
||||
print("case:\(caseNum) finished")
|
||||
//self?.verifyTest(caseNum) disabled since it slows down the tests
|
||||
//self?.verifyTest(caseNum) //disabled since it slows down the tests
|
||||
let nextCase = caseNum+1
|
||||
if nextCase <= (self?.caseCount)! {
|
||||
self?.getTestInfo(nextCase)
|
||||
self?.runTest(nextCase)
|
||||
//self?.getTestInfo(nextCase) //disabled since it slows down the tests
|
||||
} else {
|
||||
self?.finishReports()
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
@@ -10,10 +10,13 @@ import UIKit
|
||||
import Starscream
|
||||
|
||||
class ViewController: UIViewController, WebSocketDelegate {
|
||||
var socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat", "superchat"])
|
||||
var socket: WebSocket!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
var request = URLRequest(url: URL(string: "http://localhost:8080")!)
|
||||
request.timeoutInterval = 5
|
||||
socket = WebSocket(request: request)
|
||||
socket.delegate = self
|
||||
socket.connect()
|
||||
}
|
||||
@@ -25,7 +28,9 @@ class ViewController: UIViewController, WebSocketDelegate {
|
||||
}
|
||||
|
||||
func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
|
||||
if let e = error {
|
||||
if let e = error as? WSError {
|
||||
print("websocket is disconnected: \(e.message)")
|
||||
} else if let e = error {
|
||||
print("websocket is disconnected: \(e.localizedDescription)")
|
||||
} else {
|
||||
print("websocket disconnected")
|
||||
|
||||
Reference in New Issue
Block a user