Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0ebc467b7 | ||
|
|
fc5529e18f | ||
|
|
b0d7d4504b | ||
|
|
8f3761215b | ||
|
|
cdf8caeb1a | ||
|
|
46db9a75f0 | ||
|
|
5a3adc7756 | ||
|
|
32fb0aa87e | ||
|
|
9c210e006e | ||
|
|
c767cb2a54 | ||
|
|
7c09aa08b4 | ||
|
|
bdab51f4af | ||
|
|
26297ed3c1 | ||
|
|
600815b240 | ||
|
|
5fba5b83b4 | ||
|
|
c39043cc29 | ||
|
|
dd6bfa24b7 | ||
|
|
64661bc6ce | ||
|
|
0f03fab95d | ||
|
|
fc35de78a5 | ||
|
|
d911dc1adf | ||
|
|
f5f777c507 | ||
|
|
cca4af6112 | ||
|
|
b159d03640 | ||
|
|
d5aeefd853 | ||
|
|
f0f2ceee20 | ||
|
|
10cfb16197 | ||
|
|
bf0146db26 | ||
|
|
cf6def9453 | ||
|
|
8db1a45c6c | ||
|
|
592da86d06 | ||
|
|
43ade49abe | ||
|
|
e32674a1ea | ||
|
|
7e49eb2458 | ||
|
|
53c19d2388 | ||
|
|
ab02d55700 | ||
|
|
fa0865fcac | ||
|
|
5d185b41cb | ||
|
|
ba1d7db873 | ||
|
|
e2eea95ea5 | ||
|
|
363e331a84 |
@@ -2,6 +2,19 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
`Starscream` adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
#### [1.1.4](https://github.com/daltoniam/Starscream/tree/1.1.4)
|
||||
|
||||
Swift 2.3 support.
|
||||
|
||||
#### [1.1.3](https://github.com/daltoniam/Starscream/tree/1.1.3)
|
||||
|
||||
Changed:
|
||||
[#170](https://github.com/daltoniam/Starscream/issues/170)
|
||||
[#171](https://github.com/daltoniam/Starscream/issues/171)
|
||||
[#174](https://github.com/daltoniam/Starscream/issues/174)
|
||||
[#177](https://github.com/daltoniam/Starscream/issues/177)
|
||||
[#178](https://github.com/daltoniam/Starscream/issues/178)
|
||||
|
||||
#### [1.1.2](https://github.com/daltoniam/Starscream/tree/1.1.2)
|
||||
|
||||
Fixed:
|
||||
|
||||
@@ -4,6 +4,9 @@ Starscream is a conforming WebSocket ([RFC 6455](http://tools.ietf.org/html/rfc6
|
||||
|
||||
It's Objective-C counter part can be found here: [Jetfire](https://github.com/acmacalister/jetfire)
|
||||
|
||||
###Swift 3/Xcode 8
|
||||
If you are looking for Swift 3 support, see [swift 3 here](https://github.com/daltoniam/Starscream/tree/swift3)
|
||||
|
||||
## Features
|
||||
|
||||
- Conforms to all of the base [Autobahn test suite](http://autobahn.ws/testsuite/).
|
||||
@@ -103,6 +106,8 @@ socket.onData = { (data: NSData) 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`.
|
||||
|
||||
|
||||
## The delegate methods give you a simple way to handle data from the server, but how do you send data?
|
||||
|
||||
@@ -211,11 +216,11 @@ Check out the SimpleTest project in the examples directory to see how to setup a
|
||||
|
||||
## Requirements
|
||||
|
||||
Starscream works with iOS 7/OSX 10.9 or above. It is recommended to use iOS 8/10.10 or above for Cocoapods/framework support. To use Starscream with a project targeting iOS 7, you must include all Swift files directly in your project.
|
||||
Starscream works with iOS 7/OSX 10.9 or above. It is recommended to use iOS 8/10.10 or above for CocoaPods/framework support. To use Starscream with a project targeting iOS 7, you must include all Swift files directly in your project.
|
||||
|
||||
## Installation
|
||||
|
||||
### Cocoapods
|
||||
### CocoaPods
|
||||
|
||||
Check out [Get Started](http://cocoapods.org/) tab on [cocoapods.org](http://cocoapods.org/).
|
||||
|
||||
@@ -225,7 +230,7 @@ To use Starscream in your project add the following 'Podfile' to your project
|
||||
platform :ios, '9.0'
|
||||
use_frameworks!
|
||||
|
||||
pod 'Starscream', '~> 1.1.1'
|
||||
pod 'Starscream', '~> 1.1.3'
|
||||
|
||||
Then run:
|
||||
|
||||
@@ -247,7 +252,7 @@ $ brew install carthage
|
||||
To integrate Starscream into your Xcode project using Carthage, specify it in your `Cartfile`:
|
||||
|
||||
```
|
||||
github "daltoniam/Starscream" >= 1.1.1
|
||||
github "daltoniam/Starscream" >= 1.1.3
|
||||
```
|
||||
|
||||
### Rogue
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.1.2</string>
|
||||
<string>1.1.4</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>1.1.2</string>
|
||||
<string>1.1.4</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
+14
-11
@@ -68,7 +68,8 @@ public class SSLSecurity {
|
||||
public convenience init(usePublicKeys: Bool = false) {
|
||||
let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".")
|
||||
|
||||
let certs = paths.reduce([SSLCert]()) { (var certs: [SSLCert], path: String) -> [SSLCert] in
|
||||
let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in
|
||||
var certs = certs
|
||||
if let data = NSData(contentsOfFile: path) {
|
||||
certs.append(SSLCert(data: data))
|
||||
}
|
||||
@@ -91,7 +92,8 @@ public class SSLSecurity {
|
||||
|
||||
if self.usePublicKeys {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) {
|
||||
let pubKeys = certs.reduce([SecKeyRef]()) { (var pubKeys: [SecKeyRef], cert: SSLCert) -> [SecKeyRef] in
|
||||
let pubKeys = certs.reduce([SecKeyRef]()) { (pubKeys: [SecKeyRef], cert: SSLCert) -> [SecKeyRef] in
|
||||
var pubKeys = pubKeys
|
||||
if let data = cert.certData where cert.key == nil {
|
||||
cert.key = self.extractPublicKey(data)
|
||||
}
|
||||
@@ -105,7 +107,8 @@ public class SSLSecurity {
|
||||
self.isReady = true
|
||||
}
|
||||
} else {
|
||||
let certificates = certs.reduce([NSData]()) { (var certificates: [NSData], cert: SSLCert) -> [NSData] in
|
||||
let certificates = certs.reduce([NSData]()) { (certificates: [NSData], cert: SSLCert) -> [NSData] in
|
||||
var certificates = certificates
|
||||
if let data = cert.certData {
|
||||
certificates.append(data)
|
||||
}
|
||||
@@ -159,15 +162,14 @@ public class SSLSecurity {
|
||||
collect.append(SecCertificateCreateWithData(nil,cert)!)
|
||||
}
|
||||
SecTrustSetAnchorCertificates(trust,collect)
|
||||
var result: SecTrustResultType = 0
|
||||
var result: SecTrustResultType = .Unspecified
|
||||
SecTrustEvaluate(trust,&result)
|
||||
let r = Int(result)
|
||||
if r == kSecTrustResultUnspecified || r == kSecTrustResultProceed {
|
||||
if result == .Unspecified || result == .Proceed {
|
||||
var trustedCount = 0
|
||||
for serverCert in serverCerts {
|
||||
for cert in certs {
|
||||
if cert == serverCert {
|
||||
trustedCount++
|
||||
trustedCount += 1
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -205,8 +207,7 @@ public class SSLSecurity {
|
||||
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
|
||||
|
||||
guard let trust = possibleTrust else { return nil }
|
||||
|
||||
var result: SecTrustResultType = 0
|
||||
var result: SecTrustResultType = .Unspecified
|
||||
SecTrustEvaluate(trust, &result)
|
||||
return SecTrustCopyPublicKey(trust)
|
||||
}
|
||||
@@ -219,7 +220,8 @@ public class SSLSecurity {
|
||||
- returns: the certificate chain for the trust
|
||||
*/
|
||||
func certificateChainForTrust(trust: SecTrustRef) -> [NSData] {
|
||||
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (var certificates: [NSData], index: Int) -> [NSData] in
|
||||
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (certificates: [NSData], index: Int) -> [NSData] in
|
||||
var certificates = certificates
|
||||
let cert = SecTrustGetCertificateAtIndex(trust, index)
|
||||
certificates.append(SecCertificateCopyData(cert!))
|
||||
return certificates
|
||||
@@ -237,7 +239,8 @@ public class SSLSecurity {
|
||||
*/
|
||||
func publicKeyChainForTrust(trust: SecTrustRef) -> [SecKeyRef] {
|
||||
let policy = SecPolicyCreateBasicX509()
|
||||
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKeyRef]()) { (var keys: [SecKeyRef], index: Int) -> [SecKeyRef] in
|
||||
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKeyRef]()) { (keys: [SecKeyRef], index: Int) -> [SecKeyRef] in
|
||||
var keys = keys
|
||||
let cert = SecTrustGetCertificateAtIndex(trust, index)
|
||||
if let key = extractPublicKeyFromCert(cert!, policy: policy) {
|
||||
keys.append(key)
|
||||
|
||||
+278
-205
@@ -23,6 +23,10 @@ import Foundation
|
||||
import CoreFoundation
|
||||
import Security
|
||||
|
||||
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?)
|
||||
@@ -34,44 +38,46 @@ public protocol WebSocketPongDelegate: class {
|
||||
func websocketDidReceivePong(socket: WebSocket)
|
||||
}
|
||||
|
||||
public class WebSocket : NSObject, NSStreamDelegate {
|
||||
|
||||
enum OpCode : UInt8 {
|
||||
public class WebSocket: NSObject, NSStreamDelegate {
|
||||
|
||||
enum OpCode: UInt8 {
|
||||
case ContinueFrame = 0x0
|
||||
case TextFrame = 0x1
|
||||
case BinaryFrame = 0x2
|
||||
//3-7 are reserved.
|
||||
// 3-7 are reserved.
|
||||
case ConnectionClose = 0x8
|
||||
case Ping = 0x9
|
||||
case Pong = 0xA
|
||||
//B-F reserved.
|
||||
// B-F reserved.
|
||||
}
|
||||
|
||||
public enum CloseCode : UInt16 {
|
||||
|
||||
public enum CloseCode: UInt16 {
|
||||
case Normal = 1000
|
||||
case GoingAway = 1001
|
||||
case ProtocolError = 1002
|
||||
case ProtocolUnhandledType = 1003
|
||||
// 1004 reserved.
|
||||
case NoStatusReceived = 1005
|
||||
//1006 reserved.
|
||||
// 1006 reserved.
|
||||
case Encoding = 1007
|
||||
case PolicyViolated = 1008
|
||||
case MessageTooBig = 1009
|
||||
}
|
||||
|
||||
public static let ErrorDomain = "WebSocket"
|
||||
public static let ErrorDomain = "WebSocket"
|
||||
|
||||
enum InternalErrorCode : UInt16 {
|
||||
enum InternalErrorCode: UInt16 {
|
||||
// 0-999 WebSocket status codes not used
|
||||
case OutputStreamWriteError = 1
|
||||
}
|
||||
|
||||
//Where the callback is executed. It defaults to the main UI thread queue.
|
||||
public var queue = dispatch_get_main_queue()
|
||||
/// Where the callback is executed. It defaults to the main UI thread queue.
|
||||
public var callbackQueue = dispatch_get_main_queue()
|
||||
|
||||
var optionalProtocols : [String]?
|
||||
//Constant Values.
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
let headerWSUpgradeName = "Upgrade"
|
||||
let headerWSUpgradeValue = "websocket"
|
||||
let headerWSHostName = "Host"
|
||||
@@ -90,7 +96,9 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
let MaskMask: UInt8 = 0x80
|
||||
let PayloadLenMask: UInt8 = 0x7F
|
||||
let MaxFrameSize: Int = 32
|
||||
|
||||
let httpSwitchProtocolCode = 101
|
||||
let supportedSSLSchemes = ["wss", "https"]
|
||||
|
||||
class WSResponse {
|
||||
var isFin = false
|
||||
var code: OpCode = .ContinueFrame
|
||||
@@ -98,29 +106,44 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
var frameCount = 0
|
||||
var buffer: NSMutableData?
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Delegates
|
||||
|
||||
/// Responds to callback about new messages coming in over the WebSocket
|
||||
/// and also connection/disconnect messages.
|
||||
public weak var delegate: WebSocketDelegate?
|
||||
|
||||
/// Recives a callback for each pong message recived.
|
||||
public weak var pongDelegate: WebSocketPongDelegate?
|
||||
|
||||
|
||||
// MARK: - Block based API.
|
||||
|
||||
public var onConnect: ((Void) -> Void)?
|
||||
public var onDisconnect: ((NSError?) -> Void)?
|
||||
public var onText: ((String) -> Void)?
|
||||
public var onData: ((NSData) -> Void)?
|
||||
public var onPong: ((Void) -> Void)?
|
||||
|
||||
public var headers = [String: String]()
|
||||
public var voipEnabled = false
|
||||
public var selfSignedSSL = false
|
||||
public var security: SSLSecurity?
|
||||
public var enabledSSLCipherSuites: [SSLCipherSuite]?
|
||||
public var origin: String?
|
||||
public var timeout = 5
|
||||
public var isConnected :Bool {
|
||||
return connected
|
||||
}
|
||||
public var currentURL: NSURL {return url}
|
||||
public var currentURL: NSURL { return url }
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private var url: NSURL
|
||||
private var inputStream: NSInputStream?
|
||||
private var outputStream: NSOutputStream?
|
||||
private var connected = false
|
||||
private var isCreated = false
|
||||
private var isConnecting = false
|
||||
private var writeQueue = NSOperationQueue()
|
||||
private var readStack = [WSResponse]()
|
||||
private var inputQueue = [NSData]()
|
||||
@@ -129,16 +152,18 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
private var didDisconnect = false
|
||||
private var readyToWrite = false
|
||||
private let mutex = NSLock()
|
||||
private let notificationCenter = NSNotificationCenter.defaultCenter()
|
||||
private var canDispatch: Bool {
|
||||
mutex.lock()
|
||||
let canWork = readyToWrite
|
||||
mutex.unlock()
|
||||
return canWork
|
||||
}
|
||||
//the shared processing queue used for all websocket
|
||||
|
||||
/// The shared processing queue used for all WebSocket.
|
||||
private static let sharedWorkQueue = dispatch_queue_create("com.vluxe.starscream.websocket", DISPATCH_QUEUE_SERIAL)
|
||||
|
||||
//used for setting protocols.
|
||||
|
||||
/// Used for setting protocols.
|
||||
public init(url: NSURL, protocols: [String]? = nil) {
|
||||
self.url = url
|
||||
self.origin = url.absoluteString
|
||||
@@ -146,68 +171,88 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
optionalProtocols = protocols
|
||||
}
|
||||
|
||||
///Connect to the websocket server on a background thread
|
||||
// Used for specifically setting the QOS for the write queue.
|
||||
public convenience init(url: NSURL, writeQueueQOS: NSQualityOfService, protocols: [String]? = nil) {
|
||||
self.init(url: url, protocols: protocols)
|
||||
writeQueue.qualityOfService = writeQueueQOS
|
||||
}
|
||||
|
||||
/// Connect to the WebSocket server on a background thread.
|
||||
public func connect() {
|
||||
guard !isCreated else { return }
|
||||
guard !isConnecting else { return }
|
||||
didDisconnect = false
|
||||
isCreated = true
|
||||
isConnecting = true
|
||||
createHTTPRequest()
|
||||
isCreated = false
|
||||
isConnecting = false
|
||||
}
|
||||
|
||||
/**
|
||||
Disconnect from the server. I send a Close control frame to the server, then expect the server to respond with a Close control frame and close the socket from its end. I notify my delegate once the socket has been closed.
|
||||
|
||||
|
||||
If you supply a non-nil `forceTimeout`, I wait at most that long (in seconds) for the server to close the socket. After the timeout expires, I close the socket and notify my delegate.
|
||||
|
||||
|
||||
If you supply a zero (or negative) `forceTimeout`, I immediately close the socket (without sending a Close control frame) and notify my delegate.
|
||||
|
||||
|
||||
- Parameter forceTimeout: Maximum time to wait for the server to close the socket.
|
||||
*/
|
||||
*/
|
||||
public func disconnect(forceTimeout forceTimeout: NSTimeInterval? = nil) {
|
||||
switch forceTimeout {
|
||||
case .Some(let seconds) where seconds > 0:
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))), queue) { [weak self] in
|
||||
self?.disconnectStream(nil)
|
||||
}
|
||||
fallthrough
|
||||
case .None:
|
||||
writeError(CloseCode.Normal.rawValue)
|
||||
case .Some(let seconds) where seconds > 0:
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))), callbackQueue) { [weak self] in
|
||||
self?.disconnectStream(nil)
|
||||
}
|
||||
fallthrough
|
||||
case .None:
|
||||
writeError(CloseCode.Normal.rawValue)
|
||||
|
||||
default:
|
||||
self.disconnectStream(nil)
|
||||
break
|
||||
default:
|
||||
disconnectStream(nil)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
///write a string to the websocket. This sends it as a text frame.
|
||||
public func writeString(str: String) {
|
||||
|
||||
/**
|
||||
Write a string to the websocket. This sends it as a text frame.
|
||||
|
||||
If you supply a non-nil completion block, I will perform it when the write completes.
|
||||
|
||||
- parameter str: The string to write.
|
||||
- parameter completion: The (optional) completion handler.
|
||||
*/
|
||||
public func writeString(str: String, completion: (() -> ())? = nil) {
|
||||
guard isConnected else { return }
|
||||
dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame)
|
||||
}
|
||||
|
||||
///write binary data to the websocket. This sends it as a binary frame.
|
||||
public func writeData(data: NSData) {
|
||||
guard isConnected else { return }
|
||||
dequeueWrite(data, code: .BinaryFrame)
|
||||
}
|
||||
|
||||
//write a ping to the websocket. This sends it as a control frame.
|
||||
//yodel a sound to the planet. This sends it as an astroid. http://youtu.be/Eu5ZJELRiJ8?t=42s
|
||||
public func writePing(data: NSData) {
|
||||
guard isConnected else { return }
|
||||
dequeueWrite(data, code: .Ping)
|
||||
dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame, writeCompletion: completion)
|
||||
}
|
||||
|
||||
//private method that starts the connection
|
||||
/**
|
||||
Write binary data to the websocket. This sends it as a binary frame.
|
||||
|
||||
If you supply a non-nil completion block, I will perform it when the write completes.
|
||||
|
||||
- parameter data: The data to write.
|
||||
- parameter completion: The (optional) completion handler.
|
||||
*/
|
||||
public func writeData(data: NSData, completion: (() -> ())? = nil) {
|
||||
guard isConnected else { return }
|
||||
dequeueWrite(data, code: .BinaryFrame, writeCompletion: completion)
|
||||
}
|
||||
|
||||
// Write a ping to the websocket. This sends it as a control frame.
|
||||
// Yodel a sound to the planet. This sends it as an astroid. http://youtu.be/Eu5ZJELRiJ8?t=42s
|
||||
public func writePing(data: NSData, completion: (() -> ())? = nil) {
|
||||
guard isConnected else { return }
|
||||
dequeueWrite(data, code: .Ping, writeCompletion: completion)
|
||||
}
|
||||
|
||||
/// Private method that starts the connection.
|
||||
private func createHTTPRequest() {
|
||||
|
||||
|
||||
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET",
|
||||
url, kCFHTTPVersion1_1).takeRetainedValue()
|
||||
|
||||
url, kCFHTTPVersion1_1).takeRetainedValue()
|
||||
|
||||
var port = url.port
|
||||
if port == nil {
|
||||
if ["wss", "https"].contains(url.scheme) {
|
||||
if supportedSSLSchemes.contains(url.scheme!) {
|
||||
port = 443
|
||||
} else {
|
||||
port = 80
|
||||
@@ -232,13 +277,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
initStreamsWithData(serializedRequest, Int(port!))
|
||||
}
|
||||
}
|
||||
|
||||
//Add a header to the CFHTTPMessage by using the NSString bridges to CFString
|
||||
|
||||
/// Add a header to the CFHTTPMessage by using the NSString bridges to CFString.
|
||||
private func addHeader(urlRequest: CFHTTPMessage, key: NSString, val: NSString) {
|
||||
CFHTTPMessageSetHeaderFieldValue(urlRequest, key, val)
|
||||
}
|
||||
|
||||
//generate a websocket key as needed in rfc
|
||||
|
||||
/// Generate a WebSocket key as needed in RFC.
|
||||
private func generateWebSocketKey() -> String {
|
||||
var key = ""
|
||||
let seed = 16
|
||||
@@ -250,12 +295,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
let baseKey = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
|
||||
return baseKey!
|
||||
}
|
||||
|
||||
//Start the stream connection and write the data to the output stream
|
||||
|
||||
/// Start the stream connection and write the data to the output stream.
|
||||
private func initStreamsWithData(data: NSData, _ 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)
|
||||
|
||||
|
||||
var readStream: Unmanaged<CFReadStream>?
|
||||
var writeStream: Unmanaged<CFWriteStream>?
|
||||
let h: NSString = url.host!
|
||||
@@ -265,7 +310,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
guard let inStream = inputStream, let outStream = outputStream else { return }
|
||||
inStream.delegate = self
|
||||
outStream.delegate = self
|
||||
if ["wss", "https"].contains(url.scheme) {
|
||||
if supportedSSLSchemes.contains(url.scheme!) {
|
||||
inStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
|
||||
outStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
|
||||
} else {
|
||||
@@ -276,13 +321,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
outStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
|
||||
}
|
||||
if selfSignedSSL {
|
||||
let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(bool:false), kCFStreamSSLPeerName: kCFNull]
|
||||
let settings: [NSObject: NSObject] = [kCFStreamSSLValidatesCertificateChain: NSNumber(bool: false), kCFStreamSSLPeerName: kCFNull]
|
||||
inStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String)
|
||||
outStream.setProperty(settings, forKey: kCFStreamPropertySSLSettings as String)
|
||||
}
|
||||
if let cipherSuites = self.enabledSSLCipherSuites {
|
||||
if let sslContextIn = CFReadStreamCopyProperty(inputStream, kCFStreamPropertySSLContext) as! SSLContextRef?,
|
||||
sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContextRef? {
|
||||
sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContextRef? {
|
||||
let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count)
|
||||
let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count)
|
||||
if resIn != errSecSuccess {
|
||||
@@ -301,31 +346,32 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue)
|
||||
inStream.open()
|
||||
outStream.open()
|
||||
|
||||
|
||||
self.mutex.lock()
|
||||
self.readyToWrite = true
|
||||
self.mutex.unlock()
|
||||
|
||||
|
||||
let bytes = UnsafePointer<UInt8>(data.bytes)
|
||||
var timeout = 5000000 //wait 5 seconds before giving up
|
||||
var out = timeout * 1000000 // wait 5 seconds before giving up
|
||||
writeQueue.addOperationWithBlock { [weak self] in
|
||||
while !outStream.hasSpaceAvailable {
|
||||
usleep(100) //wait until the socket is ready
|
||||
timeout -= 100
|
||||
if timeout < 0 {
|
||||
usleep(100) // wait until the socket is ready
|
||||
out -= 100
|
||||
if out < 0 {
|
||||
self?.cleanupStream()
|
||||
self?.doDisconnect(self?.errorWithDetail("write wait timed out", code: 2))
|
||||
return
|
||||
} else if outStream.streamError != nil {
|
||||
return //disconnectStream will be called.
|
||||
return // disconnectStream will be called.
|
||||
}
|
||||
}
|
||||
outStream.write(bytes, maxLength: data.length)
|
||||
}
|
||||
}
|
||||
//delegate for the stream methods. Processes incoming bytes
|
||||
|
||||
// Delegate for the stream methods. Processes incoming bytes.
|
||||
public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
|
||||
|
||||
|
||||
if let sec = security where !certValidated && [.HasBytesAvailable, .HasSpaceAvailable].contains(eventCode) {
|
||||
let possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as String)
|
||||
if let trust: AnyObject = possibleTrust {
|
||||
@@ -349,7 +395,8 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
disconnectStream(nil)
|
||||
}
|
||||
}
|
||||
//disconnect the stream object
|
||||
|
||||
/// Disconnect the stream object and notifies the delegate.
|
||||
private func disconnectStream(error: NSError?) {
|
||||
if error == nil {
|
||||
writeQueue.waitUntilAllOperationsAreFinished()
|
||||
@@ -359,7 +406,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
cleanupStream()
|
||||
doDisconnect(error)
|
||||
}
|
||||
|
||||
|
||||
private func cleanupStream() {
|
||||
outputStream?.delegate = nil
|
||||
inputStream?.delegate = nil
|
||||
@@ -374,13 +421,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
outputStream = nil
|
||||
inputStream = nil
|
||||
}
|
||||
|
||||
///handles the incoming bytes and sending them to the proper processing method
|
||||
|
||||
/// Handles the incoming bytes and sending them to the proper processing method.
|
||||
private func processInputStream() {
|
||||
let buf = NSMutableData(capacity: BUFFER_MAX)
|
||||
let buffer = UnsafeMutablePointer<UInt8>(buf!.bytes)
|
||||
let length = inputStream!.read(buffer, maxLength: BUFFER_MAX)
|
||||
|
||||
|
||||
guard length > 0 else { return }
|
||||
var process = false
|
||||
if inputQueue.count == 0 {
|
||||
@@ -391,56 +438,58 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
dequeueInput()
|
||||
}
|
||||
}
|
||||
///dequeue the incoming input so it is processed in order
|
||||
|
||||
/// Dequeue the incoming input so it is processed in order.
|
||||
private func dequeueInput() {
|
||||
guard !inputQueue.isEmpty else { return }
|
||||
|
||||
let data = inputQueue[0]
|
||||
var work = data
|
||||
if let fragBuffer = fragBuffer {
|
||||
let combine = NSMutableData(data: fragBuffer)
|
||||
combine.appendData(data)
|
||||
work = combine
|
||||
self.fragBuffer = nil
|
||||
while !inputQueue.isEmpty {
|
||||
let data = inputQueue[0]
|
||||
var work = data
|
||||
if let fragBuffer = fragBuffer {
|
||||
let combine = NSMutableData(data: fragBuffer)
|
||||
combine.appendData(data)
|
||||
work = combine
|
||||
self.fragBuffer = nil
|
||||
}
|
||||
let buffer = UnsafePointer<UInt8>(work.bytes)
|
||||
let length = work.length
|
||||
if !connected {
|
||||
processTCPHandshake(buffer, bufferLen: length)
|
||||
} else {
|
||||
processRawMessagesInBuffer(buffer, bufferLen: length)
|
||||
}
|
||||
inputQueue = inputQueue.filter{ $0 != data }
|
||||
}
|
||||
let buffer = UnsafePointer<UInt8>(work.bytes)
|
||||
let length = work.length
|
||||
if !connected {
|
||||
processTCPHandshake(buffer, bufferLen: length)
|
||||
} else {
|
||||
processRawMessage(buffer, bufferLen: length)
|
||||
}
|
||||
inputQueue = inputQueue.filter{$0 != data}
|
||||
dequeueInput()
|
||||
}
|
||||
|
||||
//handle checking the inital connection status
|
||||
|
||||
// Handle checking the initial connection status.
|
||||
private func processTCPHandshake(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
|
||||
let code = processHTTP(buffer, bufferLen: bufferLen)
|
||||
switch code {
|
||||
case 0:
|
||||
connected = true
|
||||
guard canDispatch else {return}
|
||||
dispatch_async(queue) { [weak self] in
|
||||
dispatch_async(callbackQueue) { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.onConnect?()
|
||||
s.delegate?.websocketDidConnect(s)
|
||||
s.notificationCenter.postNotificationName(WebsocketDidConnectNotification, object: self)
|
||||
}
|
||||
case -1:
|
||||
fragBuffer = NSData(bytes: buffer, length: bufferLen)
|
||||
break //do nothing, we are going to collect more data
|
||||
break // do nothing, we are going to collect more data
|
||||
default:
|
||||
doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: UInt16(code)))
|
||||
}
|
||||
}
|
||||
///Finds the HTTP Packet in the TCP stream, by looking for the CRLF.
|
||||
|
||||
/// Finds the HTTP Packet in the TCP stream, by looking for the CRLF.
|
||||
private func processHTTP(buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Int {
|
||||
let CRLFBytes = [UInt8(ascii: "\r"), UInt8(ascii: "\n"), UInt8(ascii: "\r"), UInt8(ascii: "\n")]
|
||||
var k = 0
|
||||
var totalSize = 0
|
||||
for i in 0..<bufferLen {
|
||||
if buffer[i] == CRLFBytes[k] {
|
||||
k++
|
||||
k += 1
|
||||
if k == 3 {
|
||||
totalSize = i + 1
|
||||
break
|
||||
@@ -457,19 +506,19 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
totalSize += 1 //skip the last \n
|
||||
let restSize = bufferLen - totalSize
|
||||
if restSize > 0 {
|
||||
processRawMessage((buffer+totalSize),bufferLen: restSize)
|
||||
processRawMessagesInBuffer(buffer + totalSize, bufferLen: restSize)
|
||||
}
|
||||
return 0 //success
|
||||
}
|
||||
return -1 //was unable to find the full TCP header
|
||||
return -1 // Was unable to find the full TCP header.
|
||||
}
|
||||
|
||||
///validates the HTTP is a 101 as per the RFC spec
|
||||
|
||||
/// 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)
|
||||
if code != 101 {
|
||||
if code != httpSwitchProtocolCode {
|
||||
return code
|
||||
}
|
||||
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
|
||||
@@ -482,13 +531,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
///read a 16 bit big endian value from a buffer
|
||||
|
||||
///read a 16-bit big endian value from a buffer
|
||||
private static func readUint16(buffer: UnsafePointer<UInt8>, offset: Int) -> UInt16 {
|
||||
return (UInt16(buffer[offset + 0]) << 8) | UInt16(buffer[offset + 1])
|
||||
}
|
||||
|
||||
///read a 64 bit big endian value from a buffer
|
||||
|
||||
///read a 64-bit big endian value from a buffer
|
||||
private static func readUint64(buffer: UnsafePointer<UInt8>, offset: Int) -> UInt64 {
|
||||
var value = UInt64(0)
|
||||
for i in 0...7 {
|
||||
@@ -496,26 +545,29 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
///write a 16 bit big endian value to a buffer
|
||||
|
||||
/// Write a 16-bit big endian value to a buffer.
|
||||
private static func writeUint16(buffer: UnsafeMutablePointer<UInt8>, offset: Int, value: UInt16) {
|
||||
buffer[offset + 0] = UInt8(value >> 8)
|
||||
buffer[offset + 1] = UInt8(value & 0xff)
|
||||
}
|
||||
|
||||
///write a 64 bit big endian value to a buffer
|
||||
|
||||
/// Write a 64-bit big endian value to a buffer.
|
||||
private static func writeUint64(buffer: UnsafeMutablePointer<UInt8>, offset: Int, value: UInt64) {
|
||||
for i in 0...7 {
|
||||
buffer[offset + i] = UInt8((value >> (8*UInt64(7 - i))) & 0xff)
|
||||
}
|
||||
}
|
||||
|
||||
///process the websocket data
|
||||
private func processRawMessage(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
|
||||
|
||||
/// Process one message at the start of `buffer`. Return another buffer (sharing storage) that contains the leftover contents of `buffer` that I didn't process.
|
||||
@warn_unused_result
|
||||
private func processOneRawMessage(inBuffer buffer: UnsafeBufferPointer<UInt8>) -> UnsafeBufferPointer<UInt8> {
|
||||
let response = readStack.last
|
||||
let baseAddress = buffer.baseAddress
|
||||
let bufferLen = buffer.count
|
||||
if response != nil && bufferLen < 2 {
|
||||
fragBuffer = NSData(bytes: buffer, length: bufferLen)
|
||||
return
|
||||
fragBuffer = NSData(buffer: buffer)
|
||||
return emptyBuffer
|
||||
}
|
||||
if let response = response where response.bytesLeft > 0 {
|
||||
var len = response.bytesLeft
|
||||
@@ -525,115 +577,108 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
extra = 0
|
||||
}
|
||||
response.bytesLeft -= len
|
||||
response.buffer?.appendData(NSData(bytes: buffer, length: len))
|
||||
response.buffer?.appendData(NSData(bytes: baseAddress, length: len))
|
||||
processResponse(response)
|
||||
let offset = bufferLen - extra
|
||||
if extra > 0 {
|
||||
processExtra((buffer+offset), bufferLen: extra)
|
||||
}
|
||||
return
|
||||
return buffer.fromOffset(bufferLen - extra)
|
||||
} else {
|
||||
let isFin = (FinMask & buffer[0])
|
||||
let receivedOpcode = OpCode(rawValue: (OpCodeMask & buffer[0]))
|
||||
let isMasked = (MaskMask & buffer[1])
|
||||
let payloadLen = (PayloadLenMask & buffer[1])
|
||||
let isFin = (FinMask & baseAddress[0])
|
||||
let receivedOpcode = OpCode(rawValue: (OpCodeMask & baseAddress[0]))
|
||||
let isMasked = (MaskMask & baseAddress[1])
|
||||
let payloadLen = (PayloadLenMask & baseAddress[1])
|
||||
var offset = 2
|
||||
if (isMasked > 0 || (RSVMask & buffer[0]) > 0) && receivedOpcode != .Pong {
|
||||
if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .Pong {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
doDisconnect(errorWithDetail("masked and rsv data is not currently supported", code: errCode))
|
||||
writeError(errCode)
|
||||
return
|
||||
return emptyBuffer
|
||||
}
|
||||
let isControlFrame = (receivedOpcode == .ConnectionClose || receivedOpcode == .Ping)
|
||||
if !isControlFrame && (receivedOpcode != .BinaryFrame && receivedOpcode != .ContinueFrame &&
|
||||
receivedOpcode != .TextFrame && receivedOpcode != .Pong) {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
doDisconnect(errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode))
|
||||
writeError(errCode)
|
||||
return
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
doDisconnect(errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode))
|
||||
writeError(errCode)
|
||||
return emptyBuffer
|
||||
}
|
||||
if isControlFrame && isFin == 0 {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
doDisconnect(errorWithDetail("control frames can't be fragmented", code: errCode))
|
||||
writeError(errCode)
|
||||
return
|
||||
return emptyBuffer
|
||||
}
|
||||
if receivedOpcode == .ConnectionClose {
|
||||
var code = CloseCode.Normal.rawValue
|
||||
if payloadLen == 1 {
|
||||
code = CloseCode.ProtocolError.rawValue
|
||||
} else if payloadLen > 1 {
|
||||
code = WebSocket.readUint16(buffer, offset: offset)
|
||||
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)
|
||||
let len = Int(payloadLen - 2)
|
||||
if len > 0 {
|
||||
let bytes = UnsafePointer<UInt8>((buffer+offset))
|
||||
let str: NSString? = NSString(data: NSData(bytes: bytes, length: len), encoding: NSUTF8StringEncoding)
|
||||
if str == nil {
|
||||
let bytes = baseAddress + offset
|
||||
if let customCloseReason = String(data: NSData(bytes: bytes, length: len), encoding: NSUTF8StringEncoding) {
|
||||
closeReason = customCloseReason
|
||||
} else {
|
||||
code = CloseCode.ProtocolError.rawValue
|
||||
}
|
||||
}
|
||||
}
|
||||
doDisconnect(errorWithDetail("connection closed by server", code: code))
|
||||
doDisconnect(errorWithDetail(closeReason, code: code))
|
||||
writeError(code)
|
||||
return
|
||||
return emptyBuffer
|
||||
}
|
||||
if isControlFrame && payloadLen > 125 {
|
||||
writeError(CloseCode.ProtocolError.rawValue)
|
||||
return
|
||||
return emptyBuffer
|
||||
}
|
||||
var dataLength = UInt64(payloadLen)
|
||||
if dataLength == 127 {
|
||||
dataLength = WebSocket.readUint64(buffer, offset: offset)
|
||||
dataLength = WebSocket.readUint64(baseAddress, offset: offset)
|
||||
offset += sizeof(UInt64)
|
||||
} else if dataLength == 126 {
|
||||
dataLength = UInt64(WebSocket.readUint16(buffer, offset: offset))
|
||||
dataLength = UInt64(WebSocket.readUint16(baseAddress, offset: offset))
|
||||
offset += sizeof(UInt16)
|
||||
}
|
||||
if bufferLen < offset || UInt64(bufferLen - offset) < dataLength {
|
||||
fragBuffer = NSData(bytes: buffer, length: bufferLen)
|
||||
return
|
||||
fragBuffer = NSData(bytes: baseAddress, length: bufferLen)
|
||||
return emptyBuffer
|
||||
}
|
||||
var len = dataLength
|
||||
if dataLength > UInt64(bufferLen) {
|
||||
len = UInt64(bufferLen-offset)
|
||||
}
|
||||
var data: NSData!
|
||||
let data: NSData
|
||||
if len < 0 {
|
||||
len = 0
|
||||
data = NSData()
|
||||
} else {
|
||||
data = NSData(bytes: UnsafePointer<UInt8>((buffer+offset)), length: Int(len))
|
||||
data = NSData(bytes: baseAddress+offset, length: Int(len))
|
||||
}
|
||||
if receivedOpcode == .Pong {
|
||||
if canDispatch {
|
||||
dispatch_async(queue) { [weak self] in
|
||||
dispatch_async(callbackQueue) { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.onPong?()
|
||||
s.pongDelegate?.websocketDidReceivePong(s)
|
||||
}
|
||||
}
|
||||
let step = Int(offset+numericCast(len))
|
||||
let extra = bufferLen-step
|
||||
if extra > 0 {
|
||||
processRawMessage((buffer+step), bufferLen: extra)
|
||||
}
|
||||
return
|
||||
return buffer.fromOffset(offset + Int(len))
|
||||
}
|
||||
var response = readStack.last
|
||||
if isControlFrame {
|
||||
response = nil //don't append pings
|
||||
response = nil // Don't append pings.
|
||||
}
|
||||
if isFin == 0 && receivedOpcode == .ContinueFrame && response == nil {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
doDisconnect(errorWithDetail("continue frame before a binary or text frame", code: errCode))
|
||||
writeError(errCode)
|
||||
return
|
||||
return emptyBuffer
|
||||
}
|
||||
var isNew = false
|
||||
if response == nil {
|
||||
@@ -642,7 +687,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
doDisconnect(errorWithDetail("first frame can't be a continue frame",
|
||||
code: errCode))
|
||||
writeError(errCode)
|
||||
return
|
||||
return emptyBuffer
|
||||
}
|
||||
isNew = true
|
||||
response = WSResponse()
|
||||
@@ -657,43 +702,41 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
doDisconnect(errorWithDetail("second and beyond of fragment message must be a continue frame",
|
||||
code: errCode))
|
||||
writeError(errCode)
|
||||
return
|
||||
return emptyBuffer
|
||||
}
|
||||
response!.buffer!.appendData(data)
|
||||
}
|
||||
if let response = response {
|
||||
response.bytesLeft -= Int(len)
|
||||
response.frameCount++
|
||||
response.frameCount += 1
|
||||
response.isFin = isFin > 0 ? true : false
|
||||
if isNew {
|
||||
readStack.append(response)
|
||||
}
|
||||
processResponse(response)
|
||||
}
|
||||
|
||||
let step = Int(offset+numericCast(len))
|
||||
let extra = bufferLen-step
|
||||
if extra > 0 {
|
||||
processExtra((buffer+step), bufferLen: extra)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///process the extra of a buffer
|
||||
private func processExtra(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
|
||||
if bufferLen < 2 {
|
||||
fragBuffer = NSData(bytes: buffer, length: bufferLen)
|
||||
} else {
|
||||
processRawMessage(buffer, bufferLen: bufferLen)
|
||||
|
||||
let step = Int(offset + numericCast(len))
|
||||
return buffer.fromOffset(step)
|
||||
}
|
||||
}
|
||||
|
||||
///process the finished response of a buffer
|
||||
|
||||
/// Process all messages in the buffer if possible.
|
||||
private func processRawMessagesInBuffer(pointer: UnsafePointer<UInt8>, bufferLen: Int) {
|
||||
var buffer = UnsafeBufferPointer(start: pointer, count: bufferLen)
|
||||
repeat {
|
||||
buffer = processOneRawMessage(inBuffer: buffer)
|
||||
} while buffer.count >= 2
|
||||
if buffer.count > 0 {
|
||||
fragBuffer = NSData(buffer: buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Process the finished response of a buffer.
|
||||
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
|
||||
let data = response.buffer! // local copy so it's not perverse for writing
|
||||
dequeueWrite(data, code: OpCode.Pong)
|
||||
} else if response.code == .TextFrame {
|
||||
let str: NSString? = NSString(data: response.buffer!, encoding: NSUTF8StringEncoding)
|
||||
@@ -702,7 +745,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
return false
|
||||
}
|
||||
if canDispatch {
|
||||
dispatch_async(queue) { [weak self] in
|
||||
dispatch_async(callbackQueue) { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.onText?(str! as String)
|
||||
s.delegate?.websocketDidReceiveMessage(s, text: str! as String)
|
||||
@@ -710,8 +753,8 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
}
|
||||
} else if response.code == .BinaryFrame {
|
||||
if canDispatch {
|
||||
let data = response.buffer! //local copy so it is perverse for writing
|
||||
dispatch_async(queue) { [weak self] in
|
||||
let data = response.buffer! //local copy so it's not perverse for writing
|
||||
dispatch_async(callbackQueue) { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.onData?(data)
|
||||
s.delegate?.websocketDidReceiveData(s, data: data)
|
||||
@@ -723,23 +766,24 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
///Create an error
|
||||
|
||||
/// Create an error.
|
||||
private func errorWithDetail(detail: String, code: UInt16) -> NSError {
|
||||
var details = [String: String]()
|
||||
details[NSLocalizedDescriptionKey] = detail
|
||||
return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details)
|
||||
}
|
||||
|
||||
///write a an error to the socket
|
||||
|
||||
/// Write a an error to the socket.
|
||||
private func writeError(code: UInt16) {
|
||||
let buf = NSMutableData(capacity: sizeof(UInt16))
|
||||
let buffer = UnsafeMutablePointer<UInt8>(buf!.bytes)
|
||||
WebSocket.writeUint16(buffer, offset: 0, value: code)
|
||||
dequeueWrite(NSData(bytes: buffer, length: sizeof(UInt16)), code: .ConnectionClose)
|
||||
}
|
||||
///used to write things to the stream
|
||||
private func dequeueWrite(data: NSData, code: OpCode) {
|
||||
|
||||
/// Used to write things to the stream.
|
||||
private func dequeueWrite(data: NSData, code: OpCode, writeCompletion: (() -> ())? = nil) {
|
||||
writeQueue.addOperationWithBlock { [weak self] in
|
||||
//stream isn't ready, let's wait
|
||||
guard let s = self else { return }
|
||||
@@ -762,9 +806,9 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
}
|
||||
buffer[1] |= s.MaskMask
|
||||
let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
|
||||
SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey)
|
||||
_ = SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey)
|
||||
offset += sizeof(UInt32)
|
||||
|
||||
|
||||
for i in 0..<dataLength {
|
||||
buffer[offset] = bytes[i] ^ maskKey[i % sizeof(UInt32)]
|
||||
offset += 1
|
||||
@@ -788,31 +832,60 @@ public class WebSocket : NSObject, NSStreamDelegate {
|
||||
total += len
|
||||
}
|
||||
if total >= offset {
|
||||
if let callbackQueue = self?.callbackQueue, callback = writeCompletion {
|
||||
dispatch_async(callbackQueue) {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
///used to preform the disconnect delegate
|
||||
|
||||
/// Used to preform the disconnect delegate.
|
||||
private func doDisconnect(error: NSError?) {
|
||||
guard !didDisconnect else { return }
|
||||
didDisconnect = true
|
||||
connected = false
|
||||
guard canDispatch else {return}
|
||||
dispatch_async(queue) { [weak self] in
|
||||
dispatch_async(callbackQueue) { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.onDisconnect?(error)
|
||||
s.delegate?.websocketDidDisconnect(s, error: error)
|
||||
let userInfo = error.map{ [WebsocketDisconnectionErrorKeyName: $0] }
|
||||
s.notificationCenter.postNotificationName(WebsocketDidDisconnectNotification, object: self, userInfo: userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Deinit
|
||||
|
||||
deinit {
|
||||
mutex.lock()
|
||||
readyToWrite = false
|
||||
mutex.unlock()
|
||||
cleanupStream()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension NSData {
|
||||
|
||||
convenience init(buffer: UnsafeBufferPointer<UInt8>) {
|
||||
self.init(bytes: buffer.baseAddress, length: buffer.count)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension UnsafeBufferPointer {
|
||||
|
||||
func fromOffset(offset: Int) -> UnsafeBufferPointer<Element> {
|
||||
return UnsafeBufferPointer<Element>(start: baseAddress.advancedBy(offset), count: count - offset)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private let emptyBuffer = UnsafeBufferPointer<UInt8>(start: nil, count: 0)
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Starscream"
|
||||
s.version = "1.1.2"
|
||||
s.version = "1.1.4"
|
||||
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'
|
||||
@@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
||||
s.source = { :git => 'https://github.com/daltoniam/Starscream.git', :tag => "#{s.version}"}
|
||||
s.social_media_url = 'http://twitter.com/daltoniam'
|
||||
s.ios.deployment_target = '8.0'
|
||||
s.osx.deployment_target = '10.9'
|
||||
s.osx.deployment_target = '10.10'
|
||||
s.tvos.deployment_target = '9.0'
|
||||
s.source_files = 'Source/*.swift'
|
||||
s.requires_arc = 'true'
|
||||
|
||||
@@ -300,7 +300,7 @@
|
||||
attributes = {
|
||||
LastSwiftMigration = 0700;
|
||||
LastSwiftUpdateCheck = 0700;
|
||||
LastUpgradeCheck = 0700;
|
||||
LastUpgradeCheck = 0800;
|
||||
ORGANIZATIONNAME = Vluxe;
|
||||
TargetAttributes = {
|
||||
091277961BD673A70003036D = {
|
||||
@@ -311,6 +311,7 @@
|
||||
};
|
||||
6B3E79E519D48B7F006071F7 = {
|
||||
CreatedOnToolsVersion = 6.0.1;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
6B3E79F019D48B7F006071F7 = {
|
||||
CreatedOnToolsVersion = 6.0.1;
|
||||
@@ -477,6 +478,7 @@
|
||||
PRODUCT_NAME = Starscream;
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 2.3;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
};
|
||||
@@ -502,6 +504,8 @@
|
||||
PRODUCT_NAME = Starscream;
|
||||
SDKROOT = appletvos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 2.3;
|
||||
TARGETED_DEVICE_FAMILY = 3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
};
|
||||
@@ -510,6 +514,7 @@
|
||||
091277AC1BD673A70003036D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
@@ -517,6 +522,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.vluxe.StarscreamTvTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
SWIFT_VERSION = 2.3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -524,6 +530,7 @@
|
||||
091277AD1BD673A70003036D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
@@ -532,6 +539,8 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.vluxe.StarscreamTvTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = appletvos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 2.3;
|
||||
TVOS_DEPLOYMENT_TARGET = 9.0;
|
||||
};
|
||||
name = Release;
|
||||
@@ -540,6 +549,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -549,8 +559,10 @@
|
||||
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_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -560,6 +572,7 @@
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
@@ -587,6 +600,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -596,8 +610,10 @@
|
||||
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_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -606,6 +622,7 @@
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
@@ -643,6 +660,7 @@
|
||||
PROVISIONING_PROFILE = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -666,12 +684,15 @@
|
||||
PRODUCT_NAME = Starscream;
|
||||
PROVISIONING_PROFILE = "";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
6B3E79FD19D48B7F006071F7 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
@@ -684,12 +705,14 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
6B3E79FE19D48B7F006071F7 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
@@ -698,6 +721,8 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -722,6 +747,7 @@
|
||||
PRODUCT_NAME = Starscream;
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 2.3;
|
||||
VALID_ARCHS = x86_64;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -744,6 +770,8 @@
|
||||
PRODUCT_NAME = Starscream;
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 2.3;
|
||||
VALID_ARCHS = x86_64;
|
||||
};
|
||||
name = Release;
|
||||
@@ -751,6 +779,7 @@
|
||||
D9C3E37419E48FF1009FC285 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = NO;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
@@ -766,12 +795,14 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D9C3E37519E48FF1009FC285 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
APPLICATION_EXTENSION_API_ONLY = NO;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@@ -784,6 +815,8 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0710"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -37,6 +37,20 @@
|
||||
remoteGlobalIDString = D9C3E36919E48FF1009FC285;
|
||||
remoteInfo = StarscreamOSXTests;
|
||||
};
|
||||
5C42C3EB1D8F32EF00947AA2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 6B3E7A0819D48D00006071F7 /* Starscream.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 091277971BD673A70003036D;
|
||||
remoteInfo = "Starscream tvOS";
|
||||
};
|
||||
5C42C3ED1D8F32EF00947AA2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 6B3E7A0819D48D00006071F7 /* Starscream.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 091277A01BD673A70003036D;
|
||||
remoteInfo = "Starscream tvOSTests";
|
||||
};
|
||||
6B3E7A0D19D48D00006071F7 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 6B3E7A0819D48D00006071F7 /* Starscream.xcodeproj */;
|
||||
@@ -129,9 +143,11 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6B3E7A0E19D48D00006071F7 /* Starscream.framework */,
|
||||
6B3E7A1019D48D00006071F7 /* StarscreamTests.xctest */,
|
||||
6B3E7A1019D48D00006071F7 /* Starscream iOSTests.xctest */,
|
||||
5C06AE8B1B08044600D41060 /* Starscream.framework */,
|
||||
5C06AE8D1B08044600D41060 /* StarscreamOSXTests.xctest */,
|
||||
5C06AE8D1B08044600D41060 /* Starscream OSXTests.xctest */,
|
||||
5C42C3EC1D8F32EF00947AA2 /* Starscream.framework */,
|
||||
5C42C3EE1D8F32EF00947AA2 /* Starscream tvOSTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -165,11 +181,12 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0700;
|
||||
LastUpgradeCheck = 0600;
|
||||
LastUpgradeCheck = 0800;
|
||||
ORGANIZATIONNAME = vluxe;
|
||||
TargetAttributes = {
|
||||
5C765ADA199A6DAA003D9110 = {
|
||||
CreatedOnToolsVersion = 6.0;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -205,13 +222,27 @@
|
||||
remoteRef = 5C06AE8A1B08044600D41060 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5C06AE8D1B08044600D41060 /* StarscreamOSXTests.xctest */ = {
|
||||
5C06AE8D1B08044600D41060 /* Starscream OSXTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = StarscreamOSXTests.xctest;
|
||||
path = "Starscream OSXTests.xctest";
|
||||
remoteRef = 5C06AE8C1B08044600D41060 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5C42C3EC1D8F32EF00947AA2 /* Starscream.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
path = Starscream.framework;
|
||||
remoteRef = 5C42C3EB1D8F32EF00947AA2 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5C42C3EE1D8F32EF00947AA2 /* Starscream tvOSTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = "Starscream tvOSTests.xctest";
|
||||
remoteRef = 5C42C3ED1D8F32EF00947AA2 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
6B3E7A0E19D48D00006071F7 /* Starscream.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
@@ -219,10 +250,10 @@
|
||||
remoteRef = 6B3E7A0D19D48D00006071F7 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
6B3E7A1019D48D00006071F7 /* StarscreamTests.xctest */ = {
|
||||
6B3E7A1019D48D00006071F7 /* Starscream iOSTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = StarscreamTests.xctest;
|
||||
path = "Starscream iOSTests.xctest";
|
||||
remoteRef = 6B3E7A0F19D48D00006071F7 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
@@ -285,15 +316,19 @@
|
||||
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_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
@@ -328,8 +363,10 @@
|
||||
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_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -337,6 +374,7 @@
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
@@ -346,6 +384,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
@@ -358,7 +397,9 @@
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
INFOPLIST_FILE = SimpleTest/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.io.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -369,7 +410,9 @@
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
INFOPLIST_FILE = SimpleTest/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.io.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
BIN
Binary file not shown.
+12
-7
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0600"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -37,10 +37,10 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@@ -62,17 +62,21 @@
|
||||
ReferencedContainer = "container:SimpleTest.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5C765ADA199A6DAA003D9110"
|
||||
@@ -85,12 +89,13 @@
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5C765ADA199A6DAA003D9110"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.vluxe.io.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
||||
Reference in New Issue
Block a user