Compare commits

...

34 Commits

Author SHA1 Message Date
daltoniam 6e10c04c83 bumped spec. Updated CHANGELOG 2018-01-13 19:14:22 -06:00
daltoniam 97401e04bc Merge branch 'master' of https://github.com/daltoniam/Starscream 2018-01-13 18:56:36 -06:00
Dalton 6ba3563148 Merge pull request #452 from victorg1991/master
Enable editing Host header value
2018-01-13 18:56:27 -06:00
daltoniam 146368c71a new error handling. Timeout fix. Assorted fixes 2018-01-13 18:53:59 -06:00
Victor Galan 4ea6f4be3a Enable editing Host header value 2018-01-11 17:20:04 +01:00
daltoniam 6cb1c474e0 watchos, forgot about that one 2017-12-08 13:56:15 -06:00
daltoniam 7102d31afb watchos, forgot about that one 2017-12-08 13:53:31 -06:00
daltoniam 0f94bb3d95 spec 2017-12-08 13:46:43 -06:00
daltoniam 28070bf2a4 bumped spec 2017-12-08 13:40:21 -06:00
daltoniam ee0c7f9213 Merge branch 'master' of https://github.com/daltoniam/Starscream 2017-12-08 13:06:52 -06:00
daltoniam a49c7e2119 bug fixes 2017-12-08 13:06:50 -06:00
Dalton 7fad6b3a82 Merge pull request #438 from BlakeBarrett/patch-1
Update README.md
2017-12-08 13:04:32 -06:00
Dalton 06baac0fe7 Merge pull request #423 from neoneye/master
enableSOCKSProxy - See websocket traffic in Charles proxy
2017-12-08 13:04:16 -06:00
Blake Barrett 5866676617 Update README.md
Fix a typo in one of the code samples.
2017-12-07 16:44:04 -08:00
Simon Strandgaard b074c6c833 I was unable to see any websocket traffic in Charles proxy. In order to enable websocket inspection I applied the snippet by https://github.com/kevinmlong that is mentioned here: https://github.com/daltoniam/Starscream/issues/240 2017-11-15 15:58:36 +01:00
Dalton 8f2af31bd3 Merge pull request #418 from frederik-jacques/master
Fix warning for Swift 4 on the use of `character` property
2017-11-08 12:51:17 -06:00
Dalton d3fb769f93 Merge pull request #420 from fjcaetano/fix/deployment_target
Fix: tvOS framework deployment target
2017-11-08 12:47:36 -06:00
Flávio Caetano f58eb4ccad Fix: tvOS framework deployment target
fix #419
2017-11-07 17:35:35 -02:00
Frederik Jacques 880b5b4c9e Fix warning for Swift 4 on the use of character property 2017-11-07 09:37:00 +01:00
Dalton 97e378d94a Merge pull request #405 from hishnash/sendPong
Expose Pong Control frames
2017-11-06 12:58:32 -06:00
Dalton ca20b02ca2 Merge pull request #410 from qiuncheng/master
Fixed #409 When url contains query, server cannot get query parameters.
2017-11-06 12:57:18 -06:00
Dalton a1f133d737 Merge pull request #412 from eliburke/master
improved handling for self-signed certificate validation
2017-11-06 12:56:56 -06:00
Eli Burke 13f685f464 add flag to SSLSecurity implementation to turn off full cert chain checks. The
existing code would not work in cases where a CA cert was trusted, and the web
site used a signed leaf certificate. default behavior is unchanged
2017-10-31 11:41:41 -04:00
Eli Burke fae7ef2817 preserve original behavior when desiredTrustHostname is set to nil or "" 2017-10-23 09:45:19 -04:00
Eli Burke b24eaba135 separate self-signed from cert name override
add a 2nd method of getting the remote host domain name
2017-10-20 16:29:25 -04:00
qiuncheng e8182190b7 Fixed #409 When url contains query, server cannot get query parameters. 2017-10-17 14:22:22 +08:00
Dalton bf24825167 Merge pull request #400 from fassko/ISSUE_399
Fixes #399 String.characters.count deprecation warning in Xcode 9.1
2017-10-16 12:50:57 -05:00
Dalton 9ee57e089d Merge pull request #404 from rastersize/patch-1
Fix version number in changelog
2017-10-16 12:49:39 -05:00
Matthaus Woolard f057a8b33d Expose Pong Control frames
* Let users send Pong frames
* let users turn off automatic Ping responds with Pong. (only needed are edge cases)
2017-10-16 08:43:15 +03:00
Aron Cedercrantz b8fe0f64e3 Fix version number in changelog 2017-10-13 15:02:06 -04:00
Kristaps Grinbergs 94e54af61c Fixes #399
String.characters.count deprecation warnings
2017-10-09 16:40:15 +03:00
daltoniam 44ce58956f handle trailing slash better. bumped version 2017-10-04 21:54:22 -05:00
daltoniam c7cc1db0e3 fix for servers disliking not having a trailing slash. #394, #392 2017-10-02 13:08:10 -05:00
daltoniam a438ea1977 added armv7k 2017-09-27 15:12:41 -05:00
11 changed files with 245 additions and 93 deletions
+49
View File
@@ -2,6 +2,55 @@
All notable changes to this project will be documented in this file.
`Starscream` adheres to [Semantic Versioning](http://semver.org/).
#### [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.
+22 -3
View File
@@ -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)")
}
+4 -4
View File
@@ -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
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.0.1</string>
<string>3.0.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+5 -1
View File
@@ -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 {
+148 -75
View File
@@ -41,13 +41,20 @@ 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
@@ -55,6 +62,8 @@ 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 }
@@ -68,6 +77,7 @@ public protocol WebSocketClient: class {
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 +93,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)
@@ -93,6 +107,8 @@ extension WebSocketClient {
public struct SSLSettings {
let useSSL: Bool
let disableCertValidation: Bool
var overrideTrustHostname: Bool
var desiredTrustHostname: String?
#if os(Linux)
#else
let cipherSuites: [SSLCipherSuite]?
@@ -123,6 +139,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 +149,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 +194,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 +216,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 +231,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 +248,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 +266,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 +298,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 +399,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
@@ -364,9 +415,11 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
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 respondToPingWithPong: Bool = true
// MARK: - Private
private struct CompressionState {
@@ -379,8 +432,7 @@ 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
@@ -505,6 +557,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 +589,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 +611,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 +647,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 +663,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 +682,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
}
}
@@ -717,7 +789,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))
}
}
@@ -803,8 +875,8 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
}
if let acceptKey = headers[headerWSAcceptName.lowercased()] {
if acceptKey.characters.count > 0 {
if headerSecKey.characters.count > 0 {
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 +988,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 +996,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 +1017,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 +1050,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 +1071,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 +1092,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 +1100,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 +1114,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 +1154,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 +1188,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 +1247,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
@@ -1269,3 +1333,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
+2 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Starscream"
s.version = "3.0.1"
s.version = "3.0.4"
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'
@@ -14,6 +14,7 @@ Pod::Spec.new do |s|
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/*'
+4 -4
View File
@@ -334,8 +334,8 @@
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
TVOS_DEPLOYMENT_TARGET = 10.0;
VALID_ARCHS = "x86_64 i386 arm64 armv7s armv7";
TVOS_DEPLOYMENT_TARGET = 9.0;
VALID_ARCHS = "x86_64 i386 arm64 armv7s armv7 armv7k";
WATCHOS_DEPLOYMENT_TARGET = 2.0;
};
name = Debug;
@@ -367,8 +367,8 @@
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
TVOS_DEPLOYMENT_TARGET = 10.0;
VALID_ARCHS = "x86_64 i386 arm64 armv7s armv7";
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()
}
@@ -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")