Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b64f78542 | |||
| 549500a503 | |||
| 22d57dca07 | |||
| dd5119cfce | |||
| a6b2e4329b | |||
| 931e3ba8ae | |||
| 7476f5196e | |||
| 20285cce97 | |||
| 8e1de1d475 | |||
| acf64adc24 | |||
| b0fa08cde4 | |||
| 9d32864ab4 | |||
| 7ef2bc41e0 | |||
| 24e32a78e3 |
@@ -2,6 +2,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
`Starscream` adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
#### [2.0.1](https://github.com/daltoniam/Starscream/tree/2.0.1)
|
||||
|
||||
Bug fixes.
|
||||
|
||||
Fixed:
|
||||
[#261](https://github.com/daltoniam/Starscream/issues/261)
|
||||
[#276](https://github.com/daltoniam/Starscream/issues/276)
|
||||
[#267](https://github.com/daltoniam/Starscream/issues/267)
|
||||
[#266](https://github.com/daltoniam/Starscream/issues/266)
|
||||
[#259](https://github.com/daltoniam/Starscream/issues/259)
|
||||
|
||||
#### [2.0.0](https://github.com/daltoniam/Starscream/tree/2.0.0)
|
||||
|
||||
Added Swift 3 support.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.0.0</string>
|
||||
<string>2.0.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.0.0</string>
|
||||
<string>2.0.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -23,7 +23,11 @@
|
||||
import Foundation
|
||||
import Security
|
||||
|
||||
public class SSLCert {
|
||||
public protocol SSLTrustValidator {
|
||||
func isValid(_ trust: SecTrust, domain: String?) -> Bool
|
||||
}
|
||||
|
||||
open class SSLCert {
|
||||
var certData: Data?
|
||||
var key: SecKey?
|
||||
|
||||
@@ -50,7 +54,7 @@ public class SSLCert {
|
||||
}
|
||||
}
|
||||
|
||||
public class SSLSecurity {
|
||||
open class SSLSecurity : SSLTrustValidator {
|
||||
public var validatedDN = true //should the domain name be validated?
|
||||
|
||||
var isReady = false //is the key processing done?
|
||||
@@ -82,7 +86,7 @@ public class SSLSecurity {
|
||||
/**
|
||||
Designated init
|
||||
|
||||
- parameter keys: is the certificates or public keys to use
|
||||
- parameter certs: is the certificates or public keys to use
|
||||
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
|
||||
|
||||
- returns: a representation security object to be used with
|
||||
|
||||
+57
-53
@@ -38,7 +38,7 @@ public protocol WebSocketPongDelegate: class {
|
||||
func websocketDidReceivePong(socket: WebSocket, data: Data?)
|
||||
}
|
||||
|
||||
public class WebSocket : NSObject, StreamDelegate {
|
||||
open class WebSocket : NSObject, StreamDelegate {
|
||||
|
||||
enum OpCode : UInt8 {
|
||||
case continueFrame = 0x0
|
||||
@@ -128,7 +128,7 @@ public class WebSocket : NSObject, StreamDelegate {
|
||||
public var headers = [String: String]()
|
||||
public var voipEnabled = false
|
||||
public var disableSSLCertValidation = false
|
||||
public var security: SSLSecurity?
|
||||
public var security: SSLTrustValidator?
|
||||
public var enabledSSLCipherSuites: [SSLCipherSuite]?
|
||||
public var origin: String?
|
||||
public var timeout = 5
|
||||
@@ -201,7 +201,8 @@ public class WebSocket : NSObject, StreamDelegate {
|
||||
public func disconnect(forceTimeout: TimeInterval? = nil, closeCode: UInt16 = CloseCode.normal.rawValue) {
|
||||
switch forceTimeout {
|
||||
case .some(let seconds) where seconds > 0:
|
||||
callbackQueue.asyncAfter(deadline: DispatchTime.now() + Double(Int64(seconds * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { [weak self] in
|
||||
let milliseconds = Int(seconds * 1_000)
|
||||
callbackQueue.asyncAfter(deadline: .now() + .milliseconds(milliseconds)) { [weak self] in
|
||||
self?.disconnectStream(nil)
|
||||
}
|
||||
fallthrough
|
||||
@@ -218,7 +219,7 @@ public class WebSocket : NSObject, StreamDelegate {
|
||||
|
||||
If you supply a non-nil completion block, I will perform it when the write completes.
|
||||
|
||||
- parameter str: The string to write.
|
||||
- parameter string: The string to write.
|
||||
- parameter completion: The (optional) completion handler.
|
||||
*/
|
||||
public func write(string: String, completion: (() -> ())? = nil) {
|
||||
@@ -325,6 +326,28 @@ public class WebSocket : NSObject, StreamDelegate {
|
||||
if supportedSSLSchemes.contains(url.scheme!) {
|
||||
inStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
outStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn))
|
||||
disconnectStream(error)
|
||||
return
|
||||
}
|
||||
if resOut != errSecSuccess {
|
||||
let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut))
|
||||
disconnectStream(error)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
certValidated = true //not a https session, so no need to check SSL pinning
|
||||
}
|
||||
@@ -332,28 +355,6 @@ public class WebSocket : NSObject, StreamDelegate {
|
||||
inStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType)
|
||||
outStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn))
|
||||
disconnectStream(error)
|
||||
return
|
||||
}
|
||||
if resOut != errSecSuccess {
|
||||
let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut))
|
||||
disconnectStream(error)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue)
|
||||
CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue)
|
||||
@@ -365,7 +366,7 @@ public class WebSocket : NSObject, StreamDelegate {
|
||||
self.mutex.unlock()
|
||||
|
||||
let bytes = UnsafeRawPointer((data as NSData).bytes).assumingMemoryBound(to: UInt8.self)
|
||||
var out = timeout * 1000000 // wait 5 seconds before giving up
|
||||
var out = timeout * 1_000_000 // wait 5 seconds before giving up
|
||||
writeQueue.addOperation { [weak self] in
|
||||
while !outStream.hasSpaceAvailable {
|
||||
usleep(100) // wait until the socket is ready
|
||||
@@ -387,9 +388,9 @@ public class WebSocket : NSObject, StreamDelegate {
|
||||
*/
|
||||
public func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
|
||||
if let sec = security, !certValidated && [.hasBytesAvailable, .hasSpaceAvailable].contains(eventCode) {
|
||||
let trust = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as AnyObject
|
||||
let trust = aStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust
|
||||
let domain = aStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as? String
|
||||
if sec.isValid(trust as! SecTrust, domain: domain) {
|
||||
if sec.isValid(trust, domain: domain) {
|
||||
certValidated = true
|
||||
} else {
|
||||
let error = errorWithDetail("Invalid SSL certificate", code: 1)
|
||||
@@ -446,7 +447,6 @@ public class WebSocket : NSObject, StreamDelegate {
|
||||
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 }
|
||||
var process = false
|
||||
if inputQueue.count == 0 {
|
||||
@@ -642,34 +642,22 @@ public class WebSocket : NSObject, StreamDelegate {
|
||||
writeError(errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
var closeCode = CloseCode.normal.rawValue
|
||||
if receivedOpcode == .connectionClose {
|
||||
var code = CloseCode.normal.rawValue
|
||||
if payloadLen == 1 {
|
||||
code = CloseCode.protocolError.rawValue
|
||||
closeCode = CloseCode.protocolError.rawValue
|
||||
} else if payloadLen > 1 {
|
||||
code = WebSocket.readUint16(baseAddress, offset: offset)
|
||||
if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) {
|
||||
code = CloseCode.protocolError.rawValue
|
||||
}
|
||||
offset += 2
|
||||
}
|
||||
var closeReason = "connection closed by server"
|
||||
if payloadLen > 2 {
|
||||
let len = Int(payloadLen - 2)
|
||||
if len > 0 {
|
||||
let bytes = baseAddress + offset
|
||||
if let customCloseReason = String(data: Data(bytes: bytes, count: len), encoding: .utf8) {
|
||||
closeReason = customCloseReason
|
||||
} else {
|
||||
code = CloseCode.protocolError.rawValue
|
||||
}
|
||||
closeCode = WebSocket.readUint16(baseAddress, offset: offset)
|
||||
if closeCode < 1000 || (closeCode > 1003 && closeCode < 1007) || (closeCode > 1011 && closeCode < 3000) {
|
||||
closeCode = CloseCode.protocolError.rawValue
|
||||
}
|
||||
}
|
||||
doDisconnect(errorWithDetail(closeReason, code: code))
|
||||
writeError(code)
|
||||
return emptyBuffer
|
||||
}
|
||||
if isControlFrame && payloadLen > 125 {
|
||||
if payloadLen < 2 {
|
||||
doDisconnect(errorWithDetail("connection closed by server", code: closeCode))
|
||||
writeError(closeCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
} else if isControlFrame && payloadLen > 125 {
|
||||
writeError(CloseCode.protocolError.rawValue)
|
||||
return emptyBuffer
|
||||
}
|
||||
@@ -694,8 +682,24 @@ public class WebSocket : NSObject, StreamDelegate {
|
||||
len = 0
|
||||
data = Data()
|
||||
} else {
|
||||
if receivedOpcode == .connectionClose && len > 0 {
|
||||
let size = MemoryLayout<UInt16>.size
|
||||
offset += size
|
||||
len -= UInt64(size)
|
||||
}
|
||||
data = Data(bytes: baseAddress+offset, count: Int(len))
|
||||
}
|
||||
if receivedOpcode == .connectionClose {
|
||||
var closeReason = "connection closed by server"
|
||||
if let customCloseReason = String(data: data, encoding: .utf8) {
|
||||
closeReason = customCloseReason
|
||||
} else {
|
||||
closeCode = CloseCode.protocolError.rawValue
|
||||
}
|
||||
doDisconnect(errorWithDetail(closeReason, code: closeCode))
|
||||
writeError(closeCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
if receivedOpcode == .pong {
|
||||
if canDispatch {
|
||||
callbackQueue.async { [weak self] in
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Starscream"
|
||||
s.version = "2.0.0"
|
||||
s.version = "2.0.1"
|
||||
s.summary = "A conforming WebSocket RFC 6455 client library in Swift for iOS and OSX."
|
||||
s.homepage = "https://github.com/daltoniam/Starscream"
|
||||
s.license = 'Apache License, Version 2.0'
|
||||
|
||||
BIN
Binary file not shown.
+7
@@ -85,6 +85,13 @@
|
||||
ReferencedContainer = "container:SimpleTest.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "OS_ACTIVITY_MODE"
|
||||
value = "disable"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
|
||||
Reference in New Issue
Block a user