Compare commits

...

33 Commits

Author SHA1 Message Date
daltoniam ad91b8f1f3 shared queue and bug fixes 2016-01-17 22:39:41 -08:00
daltoniam c59125f34a fixes #139 2016-01-13 18:20:41 -08:00
daltoniam ce9062ab81 fixes #149 2016-01-13 18:16:37 -08:00
Dalton ff64f0b349 Merge pull request #152 from steven851007/master
Fix #151
2016-01-11 15:27:09 -08:00
Istvan Balogh 65dbee5c94 Add default value to origin to keep backward compatibility 2016-01-09 10:53:14 +01:00
Istvan Balogh 7927195b05 Fix #151 2015-12-25 11:19:09 +01:00
Dalton d32dcb2aaa Merge pull request #144 from JamesPrudente/master
call websocketDidConnect delegate after connected is set to prevent race condition
2015-12-23 15:03:49 -08:00
Dalton aeae316209 Merge pull request #146 from mstorsjo/avoid-unaligned
Avoid doing unaligned reads/writes via UnsafePointer
2015-12-12 20:22:57 +02:00
Martin Storsjö 4f2201d6b7 Avoid doing unaligned reads/writes via UnsafePointer
Instead read/write individual bytes via UnsafePointer<UInt8>.

This fixes crashes that only happen when optimization is turned on,
on 32 bit arm builds.

Not all processors support unaligned reads/writes of 64 bit values.
Without optimizations, the reads/writes probably end up handling one
byte at a time (like this commit makes it do), while higher
optimization levels convert it to single reads/writes of the full
size, which may require that the pointer is properly aligned.
2015-12-11 20:39:03 +02:00
James Prudente ec313a376e call websocketDidConnect delegate after connected is set to prevent race condition 2015-12-10 16:05:07 -08:00
Dalton d5f690123f Merge pull request #141 from delba/syntax
Syntax
2015-12-07 11:37:30 -08:00
delba b00a84716c Use implicit casting 2015-12-07 18:41:19 +01:00
delba aa11fd3bbe Use guard 2015-12-07 18:39:58 +01:00
delba b6dddff56c Use if-let optional unwrapping 2015-12-07 18:29:22 +01:00
delba ac4f5ced1e Use contains 2015-12-07 18:24:32 +01:00
delba 947601b673 Initialize OpCode from rawValue 2015-12-07 18:15:11 +01:00
delba cea6ce13c8 Remove unneccessary parentheses around conditions 2015-12-07 18:15:11 +01:00
delba 09c032a138 Use reduce 2015-12-07 18:15:11 +01:00
delba 3fe7363ee9 Use an optional parameter 2015-12-07 18:15:11 +01:00
delba 32925e3847 Use the optional method call syntax 2015-12-07 18:15:11 +01:00
delba 6ad98fb458 Use the for-i-in-range syntax 2015-12-07 18:15:11 +01:00
delba f02840355d Use the trailing closure syntax 2015-12-07 18:15:11 +01:00
delba 4bece17d89 Use the shorthand notation for Array and Dictionary 2015-12-07 18:15:11 +01:00
Dalton 3582bab0b8 Merge pull request #140 from markus-k/master
Unwrap header safely when validating response.
2015-11-24 09:58:22 -08:00
Dalton 737fca6e73 Merge pull request #138 from Abizern/error-codes
Make CloseCode enum public
2015-11-24 09:52:38 -08:00
Markus Kasten e9446a57cd Unwrap header safely when validating response.
App will crash otherwise, when the header is missing.
2015-11-24 11:53:02 +01:00
Abizer Nasir b6054567ee Create a WebSocket error domain
Saves using strings, and also the domain of the error can be switched against.
2015-11-22 00:14:52 +00:00
Abizer Nasir 2b8d6214e2 Make CloseCode enum public
Since they get passed to the websocketDidDisconnect delegate method,
making them public means that the codes can be switched against without
needing magic numbers.
2015-11-21 23:59:45 +00:00
Dalton c56e0104c3 Merge pull request #135 from simonbs/master
Sets UIRequiredDeviceCapabilities in tvOS target
2015-11-16 11:10:31 -08:00
Dalton 2bbc0895d6 Merge pull request #133 from mayoff/maintenance-1.0.2
clean up WebSocket.disconnect parameter
2015-11-16 11:10:05 -08:00
Simon Støvring f1acef9a5f Sets UIRequiredDeviceCapabilities in tvOS target 2015-11-16 18:51:41 +01:00
Rob Mayoff b29059a937 clean up WebSocket.disconnect parameter
Now the parameter is properly an Optional<NSTimeInterval>, nil means no timeout, and zero means close immediately and forcibly.
2015-11-12 21:02:02 -06:00
Dalton e1bbc7612e added license to source files 2015-11-12 11:40:48 -08:00
13 changed files with 444 additions and 325 deletions
+13
View File
@@ -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.0](https://github.com/daltoniam/Starscream/tree/1.1.0)
Changed:
Moved over to Runloop/default GCD queues to shared queue.
Fixed:
[#153](https://github.com/daltoniam/Starscream/issues/153)
[#151](https://github.com/daltoniam/Starscream/issues/151)
[#150](https://github.com/daltoniam/Starscream/issues/150)
[#149](https://github.com/daltoniam/Starscream/issues/149)
[#147](https://github.com/daltoniam/Starscream/issues/147)
[#139](https://github.com/daltoniam/Starscream/issues/139)
[#77](https://github.com/daltoniam/Starscream/issues/77)
#### [1.0.2](https://github.com/daltoniam/Starscream/tree/1.0.2)
+4 -29
View File
@@ -1,7 +1,9 @@
Apache License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2014-2015 Dalton Cherry.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
@@ -171,31 +173,4 @@ Apache License
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
of your accepting any such warranty or additional liability.
+25
View File
@@ -0,0 +1,25 @@
//
// Package.Swift
// Starscream
//
// Created by Dalton Cherry on 5/16/15.
// Copyright (c) 2014-2015 Dalton Cherry.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import PackageDescription
let package = Package(
name: "Starscream"
)
+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
</dict>
</plist>
View File
+61 -45
View File
@@ -4,7 +4,19 @@
// Starscream
//
// Created by Dalton Cherry on 5/16/15.
// Copyright (c) 2015 Vluxe. All rights reserved.
// Copyright (c) 2014-2015 Dalton Cherry.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//////////////////////////////////////////////////////////////////////////////////////////////////
@@ -55,13 +67,15 @@ public class SSLSecurity {
*/
public convenience init(usePublicKeys: Bool = false) {
let paths = NSBundle.mainBundle().pathsForResourcesOfType("cer", inDirectory: ".")
var collect = Array<SSLCert>()
for path in paths {
if let d = NSData(contentsOfFile: path as String) {
collect.append(SSLCert(data: d))
let certs = paths.reduce([SSLCert]()) { (var certs: [SSLCert], path: String) -> [SSLCert] in
if let data = NSData(contentsOfFile: path) {
certs.append(SSLCert(data: data))
}
return certs
}
self.init(certs:collect, usePublicKeys: usePublicKeys)
self.init(certs: certs, usePublicKeys: usePublicKeys)
}
/**
@@ -76,27 +90,28 @@ public class SSLSecurity {
self.usePublicKeys = usePublicKeys
if self.usePublicKeys {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {
var collect = Array<SecKeyRef>()
for cert in certs {
if let data = cert.certData where cert.key == nil {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) {
let pubKeys = certs.reduce([SecKeyRef]()) { (var pubKeys: [SecKeyRef], cert: SSLCert) -> [SecKeyRef] in
if let data = cert.certData where cert.key == nil {
cert.key = self.extractPublicKey(data)
}
if let k = cert.key {
collect.append(k)
if let key = cert.key {
pubKeys.append(key)
}
return pubKeys
}
self.pubKeys = collect
self.pubKeys = pubKeys
self.isReady = true
})
} else {
var collect = Array<NSData>()
for cert in certs {
if let d = cert.certData {
collect.append(d)
}
}
self.certificates = collect
} else {
let certificates = certs.reduce([NSData]()) { (var certificates: [NSData], cert: SSLCert) -> [NSData] in
if let data = cert.certData {
certificates.append(data)
}
return certificates
}
self.certificates = certificates
self.isReady = true
}
}
@@ -139,7 +154,7 @@ public class SSLSecurity {
}
} else if let certs = self.certificates {
let serverCerts = certificateChainForTrust(trust)
var collect = Array<SecCertificate>()
var collect = [SecCertificate]()
for cert in certs {
collect.append(SecCertificateCreateWithData(nil,cert)!)
}
@@ -173,11 +188,9 @@ public class SSLSecurity {
- returns: a public key
*/
func extractPublicKey(data: NSData) -> SecKeyRef? {
let possibleCert = SecCertificateCreateWithData(nil,data)
if let cert = possibleCert {
return extractPublicKeyFromCert(cert, policy: SecPolicyCreateBasicX509())
}
return nil
guard let cert = SecCertificateCreateWithData(nil, data) else { return nil }
return extractPublicKeyFromCert(cert, policy: SecPolicyCreateBasicX509())
}
/**
@@ -190,12 +203,12 @@ public class SSLSecurity {
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? {
var possibleTrust: SecTrust?
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
if let trust = possibleTrust {
var result: SecTrustResultType = 0
SecTrustEvaluate(trust, &result)
return SecTrustCopyPublicKey(trust)
}
return nil
guard let trust = possibleTrust else { return nil }
var result: SecTrustResultType = 0
SecTrustEvaluate(trust, &result)
return SecTrustCopyPublicKey(trust)
}
/**
@@ -205,13 +218,14 @@ public class SSLSecurity {
- returns: the certificate chain for the trust
*/
func certificateChainForTrust(trust: SecTrustRef) -> Array<NSData> {
var collect = Array<NSData>()
for var i = 0; i < SecTrustGetCertificateCount(trust); i++ {
let cert = SecTrustGetCertificateAtIndex(trust,i)
collect.append(SecCertificateCopyData(cert!))
func certificateChainForTrust(trust: SecTrustRef) -> [NSData] {
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (var certificates: [NSData], index: Int) -> [NSData] in
let cert = SecTrustGetCertificateAtIndex(trust, index)
certificates.append(SecCertificateCopyData(cert!))
return certificates
}
return collect
return certificates
}
/**
@@ -221,16 +235,18 @@ public class SSLSecurity {
- returns: the public keys from the certifcate chain for the trust
*/
func publicKeyChainForTrust(trust: SecTrustRef) -> Array<SecKeyRef> {
var collect = Array<SecKeyRef>()
func publicKeyChainForTrust(trust: SecTrustRef) -> [SecKeyRef] {
let policy = SecPolicyCreateBasicX509()
for var i = 0; i < SecTrustGetCertificateCount(trust); i++ {
let cert = SecTrustGetCertificateAtIndex(trust,i)
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKeyRef]()) { (var keys: [SecKeyRef], index: Int) -> [SecKeyRef] in
let cert = SecTrustGetCertificateAtIndex(trust, index)
if let key = extractPublicKeyFromCert(cert!, policy: policy) {
collect.append(key)
keys.append(key)
}
return keys
}
return collect
return keys
}
+228 -186
View File
@@ -3,6 +3,19 @@
// Websocket.swift
//
// Created by Dalton Cherry on 7/16/14.
// Copyright (c) 2014-2015 Dalton Cherry.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//////////////////////////////////////////////////////////////////////////////////////////////////
@@ -34,7 +47,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
//B-F reserved.
}
enum CloseCode : UInt16 {
public enum CloseCode : UInt16 {
case Normal = 1000
case GoingAway = 1001
case ProtocolError = 1002
@@ -47,6 +60,8 @@ public class WebSocket : NSObject, NSStreamDelegate {
case MessageTooBig = 1009
}
public static let ErrorDomain = "WebSocket"
enum InternalErrorCode : UInt16 {
// 0-999 WebSocket status codes not used
case OutputStreamWriteError = 1
@@ -55,7 +70,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
//Where the callback is executed. It defaults to the main UI thread queue.
public var queue = dispatch_get_main_queue()
var optionalProtocols : Array<String>?
var optionalProtocols : [String]?
//Constant Values.
let headerWSUpgradeName = "Upgrade"
let headerWSUpgradeValue = "websocket"
@@ -91,80 +106,91 @@ public class WebSocket : NSObject, NSStreamDelegate {
public var onText: ((String) -> Void)?
public var onData: ((NSData) -> Void)?
public var onPong: ((Void) -> Void)?
public var headers = Dictionary<String,String>()
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 isConnected :Bool {
return connected
}
public var currentURL: NSURL {return url}
private var url: NSURL
private var inputStream: NSInputStream?
private var outputStream: NSOutputStream?
private var isRunLoop = false
private var connected = false
private var isCreated = false
private var writeQueue = NSOperationQueue()
private var readStack = Array<WSResponse>()
private var inputQueue = Array<NSData>()
private var readStack = [WSResponse]()
private var inputQueue = [NSData]()
private var fragBuffer: NSData?
private var certValidated = false
private var didDisconnect = false
//the shared processing queue used for all websocket
private static let sharedWorkQueue = dispatch_queue_create("com.vluxe.starscream.websocket", DISPATCH_QUEUE_SERIAL)
//init the websocket with a url
public init(url: NSURL) {
self.url = url
writeQueue.maxConcurrentOperationCount = 1
}
//used for setting protocols.
public convenience init(url: NSURL, protocols: Array<String>) {
self.init(url: url)
public init(url: NSURL, protocols: [String]? = nil) {
self.url = url
self.origin = url.absoluteString
writeQueue.maxConcurrentOperationCount = 1
optionalProtocols = protocols
}
///Connect to the websocket server on a background thread
public func connect() {
if isCreated {
return
}
dispatch_async(queue,{ [weak self] in
self?.didDisconnect = false
})
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), { [weak self] in
self?.isCreated = true
self?.createHTTPRequest()
self?.isCreated = false
})
guard !isCreated else { return }
didDisconnect = false
isCreated = true
createHTTPRequest()
isCreated = false
}
///disconnect from the websocket server
public func disconnect(forceTimeout: Int = 0) {
writeError(CloseCode.Normal.rawValue)
if forceTimeout > 0 { //not needed most of the time, for an edge case
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(forceTimeout) * Int64(NSEC_PER_SEC)), queue, { [unowned self] in
/**
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) { [unowned self] in
self.disconnectStream(nil)
}
fallthrough
case .None:
writeError(CloseCode.Normal.rawValue)
default:
self.disconnectStream(nil)
})
break
}
}
///write a string to the websocket. This sends it as a text frame.
public func writeString(str: String) {
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)
}
//private methods below!
//private method that starts the connection
private func createHTTPRequest() {
@@ -173,7 +199,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
var port = url.port
if port == nil {
if url.scheme == "wss" || url.scheme == "https" {
if ["wss", "https"].contains(url.scheme) {
port = 443
} else {
port = 80
@@ -186,7 +212,9 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue)
addHeader(urlRequest, key: headerWSKeyName, val: generateWebSocketKey())
addHeader(urlRequest, key: headerOriginName, val: url.absoluteString)
if let origin = origin {
addHeader(urlRequest, key: headerOriginName, val: origin)
}
addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)")
for (key,value) in headers {
addHeader(urlRequest, key: key, val: value)
@@ -196,19 +224,17 @@ public class WebSocket : NSObject, NSStreamDelegate {
initStreamsWithData(serializedRequest, Int(port!))
}
}
//Add a header to the CFHTTPMessage by using the NSString bridges to CFString
private func addHeader(urlRequest: CFHTTPMessage,key: String, val: String) {
let nsKey: NSString = key
let nsVal: NSString = val
CFHTTPMessageSetHeaderFieldValue(urlRequest,
nsKey,
nsVal)
private func addHeader(urlRequest: CFHTTPMessage, key: NSString, val: NSString) {
CFHTTPMessageSetHeaderFieldValue(urlRequest, key, val)
}
//generate a websocket key as needed in rfc
private func generateWebSocketKey() -> String {
var key = ""
let seed = 16
for (var i = 0; i < seed; i++) {
for _ in 0..<seed {
let uni = UnicodeScalar(UInt32(97 + arc4random_uniform(25)))
key += "\(Character(uni))"
}
@@ -216,6 +242,7 @@ 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
private func initStreamsWithData(data: NSData, _ port: Int) {
//higher level API we will cut over to at some point
@@ -230,7 +257,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
guard let inStream = inputStream, let outStream = outputStream else { return }
inStream.delegate = self
outStream.delegate = self
if url.scheme == "wss" || url.scheme == "https" {
if ["wss", "https"].contains(url.scheme) {
inStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
outStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
} else {
@@ -241,7 +268,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
outStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
}
if selfSignedSSL {
let settings: Dictionary<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)
}
@@ -250,33 +277,34 @@ public class WebSocket : NSObject, NSStreamDelegate {
sslContextOut = CFWriteStreamCopyProperty(outputStream, kCFStreamPropertySSLContext) as! SSLContextRef? {
let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count)
let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count)
if (resIn != errSecSuccess) {
if resIn != errSecSuccess {
let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn))
disconnectStream(error)
return
}
if (resOut != errSecSuccess) {
if resOut != errSecSuccess {
let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut))
disconnectStream(error)
return
}
}
}
isRunLoop = true
inStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue)
CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue)
inStream.open()
outStream.open()
let bytes = UnsafePointer<UInt8>(data.bytes)
outStream.write(bytes, maxLength: data.length)
while(isRunLoop) {
NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture() as NSDate)
writeQueue.addOperationWithBlock {
while !outStream.hasSpaceAvailable {
usleep(100) //wait until the socket is ready
}
outStream.write(bytes, maxLength: data.length)
}
}
//delegate for the stream methods. Processes incoming bytes
public func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
if let sec = security where !certValidated && (eventCode == .HasBytesAvailable || eventCode == .HasSpaceAvailable) {
if let sec = security where !certValidated && [.HasBytesAvailable, .HasSpaceAvailable].contains(eventCode) {
let possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as String)
if let trust: AnyObject = possibleTrust {
let domain: AnyObject? = aStream.propertyForKey(kCFStreamSSLPeerName as String)
@@ -290,7 +318,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
}
if eventCode == .HasBytesAvailable {
if(aStream == inputStream) {
if aStream == inputStream {
processInputStream()
}
} else if eventCode == .ErrorOccurred {
@@ -303,18 +331,16 @@ public class WebSocket : NSObject, NSStreamDelegate {
private func disconnectStream(error: NSError?) {
writeQueue.waitUntilAllOperationsAreFinished()
if let stream = inputStream {
stream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
CFReadStreamSetDispatchQueue(stream, nil)
stream.close()
}
if let stream = outputStream {
stream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
CFWriteStreamSetDispatchQueue(stream, nil)
stream.close()
}
outputStream = nil
isRunLoop = false
certValidated = false
doDisconnect(error)
connected = false
}
///handles the incoming bytes and sending them to the proper processing method
@@ -322,50 +348,62 @@ public class WebSocket : NSObject, NSStreamDelegate {
let buf = NSMutableData(capacity: BUFFER_MAX)
let buffer = UnsafeMutablePointer<UInt8>(buf!.bytes)
let length = inputStream!.read(buffer, maxLength: BUFFER_MAX)
if length > 0 {
if !connected {
connected = processHTTP(buffer, bufferLen: length)
if !connected {
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue()
CFHTTPMessageAppendBytes(response, buffer, length)
let code = CFHTTPMessageGetResponseStatusCode(response)
doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: UInt16(code)))
}
} else {
var process = false
if inputQueue.count == 0 {
process = true
}
inputQueue.append(NSData(bytes: buffer, length: length))
if process {
dequeueInput()
}
}
guard length > 0 else { return }
var process = false
if inputQueue.count == 0 {
process = true
}
inputQueue.append(NSData(bytes: buffer, length: length))
if process {
dequeueInput()
}
}
///dequeue the incoming input so it is processed in order
private func dequeueInput() {
if inputQueue.count > 0 {
let data = inputQueue[0]
var work = data
if fragBuffer != nil {
let combine = NSMutableData(data: fragBuffer!)
combine.appendData(data)
work = combine
fragBuffer = nil
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
}
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
private func processTCPHandshake(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
let code = processHTTP(buffer, bufferLen: bufferLen)
switch code {
case 0:
connected = true
dispatch_async(queue) { [weak self] in
guard let s = self else { return }
s.onConnect?()
s.delegate?.websocketDidConnect(s)
}
let buffer = UnsafePointer<UInt8>(work.bytes)
processRawMessage(buffer, bufferLen: work.length)
inputQueue = inputQueue.filter{$0 != data}
dequeueInput()
case -1: 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.
private func processHTTP(buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Bool {
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 var i = 0; i < bufferLen; i++ {
for i in 0..<bufferLen {
if buffer[i] == CRLFBytes[k] {
k++
if k == 3 {
@@ -377,40 +415,64 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
}
if totalSize > 0 {
if validateResponse(buffer, bufferLen: totalSize) {
dispatch_async(queue,{ [weak self] in
guard let s = self else { return }
if let connectBlock = s.onConnect {
connectBlock()
}
s.delegate?.websocketDidConnect(s)
})
totalSize += 1 //skip the last \n
let restSize = bufferLen - totalSize
if restSize > 0 {
processRawMessage((buffer+totalSize),bufferLen: restSize)
}
return true
let code = validateResponse(buffer, bufferLen: totalSize)
if code != 0 {
return code
}
totalSize += 1 //skip the last \n
let restSize = bufferLen - totalSize
if restSize > 0 {
processRawMessage((buffer+totalSize),bufferLen: restSize)
}
return 0 //success
}
return false
return -1 //was unable to find the full TCP header
}
///validates the HTTP is a 101 as per the RFC spec
private func validateResponse(buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Bool {
private func validateResponse(buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Int {
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue()
CFHTTPMessageAppendBytes(response, buffer, bufferLen)
if CFHTTPMessageGetResponseStatusCode(response) != 101 {
return false
let code = CFHTTPMessageGetResponseStatusCode(response)
if code != 101 {
return code
}
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
let headers = cfHeaders.takeRetainedValue() as NSDictionary
let acceptKey = headers[headerWSAcceptName] as! NSString
if acceptKey.length > 0 {
return true
if let acceptKey = headers[headerWSAcceptName] as? NSString {
if acceptKey.length > 0 {
return 0
}
}
}
return false
return -1
}
///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
private static func readUint64(buffer: UnsafePointer<UInt8>, offset: Int) -> UInt64 {
var value = UInt64(0)
for i in 0...7 {
value = (value << 8) | UInt64(buffer[offset + i])
}
return value
}
///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
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
@@ -420,17 +482,16 @@ public class WebSocket : NSObject, NSStreamDelegate {
fragBuffer = NSData(bytes: buffer, length: bufferLen)
return
}
if response != nil && response!.bytesLeft > 0 {
let resp = response!
var len = resp.bytesLeft
var extra = bufferLen - resp.bytesLeft
if resp.bytesLeft > bufferLen {
if let response = response where response.bytesLeft > 0 {
var len = response.bytesLeft
var extra = bufferLen - response.bytesLeft
if response.bytesLeft > bufferLen {
len = bufferLen
extra = 0
}
resp.bytesLeft -= len
resp.buffer?.appendData(NSData(bytes: buffer, length: len))
processResponse(resp)
response.bytesLeft -= len
response.buffer?.appendData(NSData(bytes: buffer, length: len))
processResponse(response)
let offset = bufferLen - extra
if extra > 0 {
processExtra((buffer+offset), bufferLen: extra)
@@ -438,19 +499,19 @@ public class WebSocket : NSObject, NSStreamDelegate {
return
} else {
let isFin = (FinMask & buffer[0])
let receivedOpcode = (OpCodeMask & buffer[0])
let receivedOpcode = OpCode(rawValue: (OpCodeMask & buffer[0]))
let isMasked = (MaskMask & buffer[1])
let payloadLen = (PayloadLenMask & buffer[1])
var offset = 2
if((isMasked > 0 || (RSVMask & buffer[0]) > 0) && receivedOpcode != OpCode.Pong.rawValue) {
if (isMasked > 0 || (RSVMask & buffer[0]) > 0) && receivedOpcode != .Pong {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("masked and rsv data is not currently supported", code: errCode))
writeError(errCode)
return
}
let isControlFrame = (receivedOpcode == OpCode.ConnectionClose.rawValue || receivedOpcode == OpCode.Ping.rawValue)
if !isControlFrame && (receivedOpcode != OpCode.BinaryFrame.rawValue && receivedOpcode != OpCode.ContinueFrame.rawValue &&
receivedOpcode != OpCode.TextFrame.rawValue && receivedOpcode != OpCode.Pong.rawValue) {
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)
@@ -462,13 +523,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
writeError(errCode)
return
}
if receivedOpcode == OpCode.ConnectionClose.rawValue {
if receivedOpcode == .ConnectionClose {
var code = CloseCode.Normal.rawValue
if payloadLen == 1 {
code = CloseCode.ProtocolError.rawValue
} else if payloadLen > 1 {
let codeBuffer = UnsafePointer<UInt16>((buffer+offset))
code = codeBuffer[0].bigEndian
code = WebSocket.readUint16(buffer, offset: offset)
if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) {
code = CloseCode.ProtocolError.rawValue
}
@@ -494,12 +554,10 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
var dataLength = UInt64(payloadLen)
if dataLength == 127 {
let bytes = UnsafePointer<UInt64>((buffer+offset))
dataLength = bytes[0].bigEndian
dataLength = WebSocket.readUint64(buffer, offset: offset)
offset += sizeof(UInt64)
} else if dataLength == 126 {
let bytes = UnsafePointer<UInt16>((buffer+offset))
dataLength = UInt64(bytes[0].bigEndian)
dataLength = UInt64(WebSocket.readUint16(buffer, offset: offset))
offset += sizeof(UInt16)
}
if bufferLen < offset || UInt64(bufferLen - offset) < dataLength {
@@ -517,14 +575,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
} else {
data = NSData(bytes: UnsafePointer<UInt8>((buffer+offset)), length: Int(len))
}
if receivedOpcode == OpCode.Pong.rawValue {
dispatch_async(queue,{ [weak self] in
if receivedOpcode == .Pong {
dispatch_async(queue) { [weak self] in
guard let s = self else { return }
if let pongBlock = s.onPong {
pongBlock()
}
s.onPong?()
s.pongDelegate?.websocketDidReceivePong(s)
})
}
let step = Int(offset+numericCast(len))
let extra = bufferLen-step
@@ -537,15 +593,15 @@ public class WebSocket : NSObject, NSStreamDelegate {
if isControlFrame {
response = nil //don't append pings
}
if isFin == 0 && receivedOpcode == OpCode.ContinueFrame.rawValue && response == nil {
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
}
var isNew = false
if(response == nil) {
if receivedOpcode == OpCode.ContinueFrame.rawValue {
if response == nil {
if receivedOpcode == .ContinueFrame {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("first frame can't be a continue frame",
code: errCode))
@@ -554,11 +610,11 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
isNew = true
response = WSResponse()
response!.code = OpCode(rawValue: receivedOpcode)!
response!.code = receivedOpcode!
response!.bytesLeft = Int(dataLength)
response!.buffer = NSMutableData(data: data)
} else {
if receivedOpcode == OpCode.ContinueFrame.rawValue {
if receivedOpcode == .ContinueFrame {
response!.bytesLeft = Int(dataLength)
} else {
let errCode = CloseCode.ProtocolError.rawValue
@@ -569,19 +625,19 @@ public class WebSocket : NSObject, NSStreamDelegate {
}
response!.buffer!.appendData(data)
}
if response != nil {
response!.bytesLeft -= Int(len)
response!.frameCount++
response!.isFin = isFin > 0 ? true : false
if(isNew) {
readStack.append(response!)
if let response = response {
response.bytesLeft -= Int(len)
response.frameCount++
response.isFin = isFin > 0 ? true : false
if isNew {
readStack.append(response)
}
processResponse(response!)
processResponse(response)
}
let step = Int(offset+numericCast(len))
let extra = bufferLen-step
if(extra > 0) {
if extra > 0 {
processExtra((buffer+step), bufferLen: extra)
}
}
@@ -610,22 +666,18 @@ public class WebSocket : NSObject, NSStreamDelegate {
return false
}
dispatch_async(queue,{ [weak self] in
dispatch_async(queue) { [weak self] in
guard let s = self else { return }
if let textBlock = s.onText {
textBlock(str! as String)
}
s.onText?(str! as String)
s.delegate?.websocketDidReceiveMessage(s, text: str! as String)
})
}
} else if response.code == .BinaryFrame {
let data = response.buffer! //local copy so it is perverse for writing
dispatch_async(queue,{ [weak self] in
dispatch_async(queue) { [weak self] in
guard let s = self else { return }
if let dataBlock = s.onData {
dataBlock(data)
}
s.onData?(data)
s.delegate?.websocketDidReceiveData(s, data: data)
})
}
}
readStack.removeLast()
return true
@@ -635,23 +687,20 @@ public class WebSocket : NSObject, NSStreamDelegate {
///Create an error
private func errorWithDetail(detail: String, code: UInt16) -> NSError {
var details = Dictionary<String,String>()
var details = [String: String]()
details[NSLocalizedDescriptionKey] = detail
return NSError(domain: "Websocket", code: Int(code), userInfo: details)
return NSError(domain: WebSocket.ErrorDomain, code: Int(code), userInfo: details)
}
///write a an error to the socket
private func writeError(code: UInt16) {
let buf = NSMutableData(capacity: sizeof(UInt16))
let buffer = UnsafeMutablePointer<UInt16>(buf!.bytes)
buffer[0] = code.bigEndian
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) {
if !isConnected {
return
}
writeQueue.addOperationWithBlock { [weak self] in
//stream isn't ready, let's wait
guard let s = self else { return }
@@ -665,13 +714,11 @@ public class WebSocket : NSObject, NSStreamDelegate {
buffer[1] = CUnsignedChar(dataLength)
} else if dataLength <= Int(UInt16.max) {
buffer[1] = 126
let sizeBuffer = UnsafeMutablePointer<UInt16>((buffer+offset))
sizeBuffer[0] = UInt16(dataLength).bigEndian
WebSocket.writeUint16(buffer, offset: offset, value: UInt16(dataLength))
offset += sizeof(UInt16)
} else {
buffer[1] = 127
let sizeBuffer = UnsafeMutablePointer<UInt64>((buffer+offset))
sizeBuffer[0] = UInt64(dataLength).bigEndian
WebSocket.writeUint64(buffer, offset: offset, value: UInt64(dataLength))
offset += sizeof(UInt64)
}
buffer[1] |= s.MaskMask
@@ -679,15 +726,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey)
offset += sizeof(UInt32)
for (var i = 0; i < dataLength; i++) {
for i in 0..<dataLength {
buffer[offset] = bytes[i] ^ maskKey[i % sizeof(UInt32)]
offset += 1
}
var total = 0
while true {
if !s.isConnected {
break
}
guard let outStream = s.outputStream else { break }
let writeBuffer = UnsafePointer<UInt8>(frame!.bytes+total)
let len = outStream.write(writeBuffer, maxLength: offset-total)
@@ -714,15 +758,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
///used to preform the disconnect delegate
private func doDisconnect(error: NSError?) {
if !didDisconnect {
dispatch_async(queue,{ [weak self] in
guard let s = self else { return }
s.didDisconnect = true
if let disconnect = s.onDisconnect {
disconnect(error)
}
s.delegate?.websocketDidDisconnect(s, error: error)
})
guard !didDisconnect else { return }
didDisconnect = true
connected = false
dispatch_async(queue) { [weak self] in
guard let s = self else { return }
s.onDisconnect?(error)
s.delegate?.websocketDidDisconnect(s, error: error)
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Starscream"
s.version = "1.0.2"
s.version = "1.1.0"
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'
@@ -10,6 +10,6 @@ Pod::Spec.new do |s|
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.9'
s.tvos.deployment_target = '9.0'
s.source_files = '*.swift'
s.source_files = 'Source/*.swift'
s.requires_arc = 'true'
end
+36 -34
View File
@@ -8,19 +8,19 @@
/* Begin PBXBuildFile section */
091277A11BD673A70003036D /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 091277971BD673A70003036D /* Starscream.framework */; };
5C06AE8F1B08050D00D41060 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C06AE8E1B08050D00D41060 /* SSLSecurity.swift */; };
5C06AE901B08050D00D41060 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C06AE8E1B08050D00D41060 /* SSLSecurity.swift */; };
5CADAB2A1BEAC5CF005DE2F0 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B3E7A0419D48C41006071F7 /* WebSocket.swift */; };
5CADAB2B1BEAC5CF005DE2F0 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C06AE8E1B08050D00D41060 /* SSLSecurity.swift */; };
5CADAB2C1BEAC5DA005DE2F0 /* Starscream.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B3E7A0619D48C5F006071F7 /* Starscream.h */; settings = {ATTRIBUTES = (Public, ); }; };
5C1360021C473BEF00AA3A01 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C135FFF1C473BEF00AA3A01 /* SSLSecurity.swift */; };
5C1360031C473BEF00AA3A01 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C135FFF1C473BEF00AA3A01 /* SSLSecurity.swift */; };
5C1360041C473BEF00AA3A01 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C135FFF1C473BEF00AA3A01 /* SSLSecurity.swift */; };
5C1360051C473BEF00AA3A01 /* Starscream.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C1360001C473BEF00AA3A01 /* Starscream.h */; settings = {ATTRIBUTES = (Public, ); }; };
5C1360061C473BEF00AA3A01 /* Starscream.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C1360001C473BEF00AA3A01 /* Starscream.h */; settings = {ATTRIBUTES = (Public, ); }; };
5C1360071C473BEF00AA3A01 /* Starscream.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C1360001C473BEF00AA3A01 /* Starscream.h */; settings = {ATTRIBUTES = (Public, ); }; };
5C1360081C473BEF00AA3A01 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1360011C473BEF00AA3A01 /* WebSocket.swift */; };
5C1360091C473BEF00AA3A01 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1360011C473BEF00AA3A01 /* WebSocket.swift */; };
5C13600A1C473BEF00AA3A01 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1360011C473BEF00AA3A01 /* WebSocket.swift */; };
5CADAB511BEBD068005DE2F0 /* StarscreamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B3E7A0119D48C2F006071F7 /* StarscreamTests.swift */; };
6B3E7A0319D48C2F006071F7 /* StarscreamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B3E7A0119D48C2F006071F7 /* StarscreamTests.swift */; };
6B3E7A0519D48C41006071F7 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B3E7A0419D48C41006071F7 /* WebSocket.swift */; };
6B3E7A0719D48C5F006071F7 /* Starscream.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B3E7A0619D48C5F006071F7 /* Starscream.h */; settings = {ATTRIBUTES = (Public, ); }; };
D9C3E36A19E48FF1009FC285 /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9C3E35F19E48FF1009FC285 /* Starscream.framework */; };
D9C3E37819E4903F009FC285 /* StarscreamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B3E7A0119D48C2F006071F7 /* StarscreamTests.swift */; };
D9C3E37919E49051009FC285 /* Starscream.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B3E7A0619D48C5F006071F7 /* Starscream.h */; settings = {ATTRIBUTES = (Public, ); }; };
D9C3E37A19E49058009FC285 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B3E7A0419D48C41006071F7 /* WebSocket.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -43,14 +43,15 @@
/* Begin PBXFileReference section */
091277971BD673A70003036D /* Starscream.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Starscream.framework; sourceTree = BUILT_PRODUCTS_DIR; };
091277A01BD673A70003036D /* StarscreamTvTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StarscreamTvTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5C06AE8E1B08050D00D41060 /* SSLSecurity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSLSecurity.swift; sourceTree = SOURCE_ROOT; };
5C135FFF1C473BEF00AA3A01 /* SSLSecurity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SSLSecurity.swift; path = Source/SSLSecurity.swift; sourceTree = SOURCE_ROOT; };
5C1360001C473BEF00AA3A01 /* Starscream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Starscream.h; path = Source/Starscream.h; sourceTree = SOURCE_ROOT; };
5C1360011C473BEF00AA3A01 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket.swift; sourceTree = SOURCE_ROOT; };
5C13600B1C473BFE00AA3A01 /* Info-tvOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Info-tvOS.plist"; path = "Source/Info-tvOS.plist"; sourceTree = SOURCE_ROOT; };
5C13600C1C473BFE00AA3A01 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Source/Info.plist; sourceTree = SOURCE_ROOT; };
6B3E79E619D48B7F006071F7 /* Starscream.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Starscream.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6B3E79EA19D48B7F006071F7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6B3E79F119D48B7F006071F7 /* StarscreamTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StarscreamTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
6B3E7A0019D48C2F006071F7 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6B3E7A0119D48C2F006071F7 /* StarscreamTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StarscreamTests.swift; sourceTree = "<group>"; };
6B3E7A0419D48C41006071F7 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = SOURCE_ROOT; };
6B3E7A0619D48C5F006071F7 /* Starscream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Starscream.h; sourceTree = SOURCE_ROOT; };
D9C3E35F19E48FF1009FC285 /* Starscream.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Starscream.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D9C3E36919E48FF1009FC285 /* StarscreamOSXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StarscreamOSXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -128,9 +129,9 @@
6B3E79E819D48B7F006071F7 /* Starscream */ = {
isa = PBXGroup;
children = (
6B3E7A0419D48C41006071F7 /* WebSocket.swift */,
5C06AE8E1B08050D00D41060 /* SSLSecurity.swift */,
6B3E7A0619D48C5F006071F7 /* Starscream.h */,
5C1360001C473BEF00AA3A01 /* Starscream.h */,
5C135FFF1C473BEF00AA3A01 /* SSLSecurity.swift */,
5C1360011C473BEF00AA3A01 /* WebSocket.swift */,
6B3E79E919D48B7F006071F7 /* Supporting Files */,
);
path = Starscream;
@@ -139,7 +140,8 @@
6B3E79E919D48B7F006071F7 /* Supporting Files */ = {
isa = PBXGroup;
children = (
6B3E79EA19D48B7F006071F7 /* Info.plist */,
5C13600B1C473BFE00AA3A01 /* Info-tvOS.plist */,
5C13600C1C473BFE00AA3A01 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
@@ -160,7 +162,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
5CADAB2C1BEAC5DA005DE2F0 /* Starscream.h in Headers */,
5C1360071C473BEF00AA3A01 /* Starscream.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -168,7 +170,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
6B3E7A0719D48C5F006071F7 /* Starscream.h in Headers */,
5C1360051C473BEF00AA3A01 /* Starscream.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -176,7 +178,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
D9C3E37919E49051009FC285 /* Starscream.h in Headers */,
5C1360061C473BEF00AA3A01 /* Starscream.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -393,8 +395,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5CADAB2A1BEAC5CF005DE2F0 /* WebSocket.swift in Sources */,
5CADAB2B1BEAC5CF005DE2F0 /* SSLSecurity.swift in Sources */,
5C13600A1C473BEF00AA3A01 /* WebSocket.swift in Sources */,
5C1360041C473BEF00AA3A01 /* SSLSecurity.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -410,8 +412,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6B3E7A0519D48C41006071F7 /* WebSocket.swift in Sources */,
5C06AE8F1B08050D00D41060 /* SSLSecurity.swift in Sources */,
5C1360081C473BEF00AA3A01 /* WebSocket.swift in Sources */,
5C1360021C473BEF00AA3A01 /* SSLSecurity.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -427,8 +429,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D9C3E37A19E49058009FC285 /* WebSocket.swift in Sources */,
5C06AE901B08050D00D41060 /* SSLSecurity.swift in Sources */,
5C1360091C473BEF00AA3A01 /* WebSocket.swift in Sources */,
5C1360031C473BEF00AA3A01 /* SSLSecurity.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -468,7 +470,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = Info.plist;
INFOPLIST_FILE = "$(SRCROOT)/Source/Info-tvOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.vluxe.Starscream;
@@ -493,7 +495,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = Info.plist;
INFOPLIST_FILE = "$(SRCROOT)/Source/Info-tvOS.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.vluxe.Starscream;
@@ -510,7 +512,7 @@
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = Info.plist;
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.vluxe.StarscreamTvTests;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -525,7 +527,7 @@
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = Info.plist;
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.vluxe.StarscreamTvTests;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -631,7 +633,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Info.plist;
INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@@ -655,7 +657,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Info.plist;
INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@@ -712,7 +714,7 @@
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = Info.plist;
INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
@@ -734,7 +736,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = Info.plist;
INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="Autobahn" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
@@ -11,9 +11,9 @@ import Starscream
class ViewController: UIViewController {
static let host = "localhost:9001"
static let scheme = "ws"
var socket = WebSocket(url: NSURL(scheme: scheme, host: host, path: "/getCaseCount")!, protocols: [])
let host = "localhost:9001"
let scheme = "ws"
var socketArray = [WebSocket]()
var caseCount = 300 //starting cases
override func viewDidLoad() {
super.viewDidLoad()
@@ -21,22 +21,30 @@ class ViewController: UIViewController {
//getTestInfo(1)
}
func removeSocket(s: WebSocket) {
self.socketArray = self.socketArray.filter{$0 != s}
}
func getCaseCount() {
socket.onText = {(text: String) in
let s = WebSocket(url: NSURL(scheme: scheme, host: host, path: "/getCaseCount")!, protocols: [])
socketArray.append(s)
s.onText = {[unowned self] (text: String) in
if let c = Int(text) {
print("number of cases is: \(c)")
self.caseCount = c
}
}
socket.onDisconnect = {(error: NSError?) in
s.onDisconnect = {[unowned self] (error: NSError?) in
self.getTestInfo(1)
self.removeSocket(s)
}
socket.connect()
s.connect()
}
func getTestInfo(caseNum: Int) {
socket = createSocket("getCaseInfo",caseNum)
socket.onText = {(text: String) in
let s = createSocket("getCaseInfo",caseNum)
socketArray.append(s)
s.onText = {(text: String) in
// let data = text.dataUsingEncoding(NSUTF8StringEncoding)
// do {
// let resp: AnyObject? = try NSJSONSerialization.JSONObjectWithData(data!,
@@ -54,37 +62,41 @@ class ViewController: UIViewController {
}
var once = false
socket.onDisconnect = {(error: NSError?) in
s.onDisconnect = {[unowned self] (error: NSError?) in
if !once {
once = true
self.runTest(caseNum)
}
self.removeSocket(s)
}
socket.connect()
s.connect()
}
func runTest(caseNum: Int) {
socket = createSocket("runCase",caseNum)
socket.onText = {(text: String) in
self.socket.writeString(text)
let s = createSocket("runCase",caseNum)
self.socketArray.append(s)
s.onText = {(text: String) in
s.writeString(text)
}
socket.onData = {(data: NSData) in
self.socket.writeData(data)
s.onData = {(data: NSData) in
s.writeData(data)
}
var once = false
socket.onDisconnect = {(error: NSError?) in
s.onDisconnect = {[unowned self] (error: NSError?) in
if !once {
once = true
print("case:\(caseNum) finished")
self.verifyTest(caseNum)
self.removeSocket(s)
}
}
socket.connect()
s.connect()
}
func verifyTest(caseNum: Int) {
socket = createSocket("getCaseStatus",caseNum)
socket.onText = {(text: String) in
let s = createSocket("getCaseStatus",caseNum)
self.socketArray.append(s)
s.onText = {(text: String) in
let data = text.dataUsingEncoding(NSUTF8StringEncoding)
do {
let resp: AnyObject? = try NSJSONSerialization.JSONObjectWithData(data!,
@@ -103,7 +115,7 @@ class ViewController: UIViewController {
}
}
var once = false
socket.onDisconnect = {(error: NSError?) in
s.onDisconnect = {[unowned self] (error: NSError?) in
if !once {
once = true
let nextCase = caseNum+1
@@ -113,21 +125,24 @@ class ViewController: UIViewController {
self.finishReports()
}
}
self.removeSocket(s)
}
socket.connect()
s.connect()
}
func finishReports() {
socket = createSocket("updateReports",0)
socket.onDisconnect = {(error: NSError?) in
let s = createSocket("updateReports",0)
self.socketArray.append(s)
s.onDisconnect = {[unowned self] (error: NSError?) in
print("finished all the tests!")
self.removeSocket(s)
}
socket.connect()
s.connect()
}
func createSocket(cmd: String, _ caseNum: Int) -> WebSocket {
return WebSocket(url: NSURL(scheme: ViewController.scheme,
host: ViewController.host, path: buildPath(cmd,caseNum))!, protocols: [])
return WebSocket(url: NSURL(scheme: scheme,
host: host, path: buildPath(cmd,caseNum))!, protocols: [])
}
func buildPath(cmd: String, _ caseNum: Int) -> String {