Compare commits

...

94 Commits

Author SHA1 Message Date
daltoniam 398d9c15bf bumped spec 2016-01-18 12:26:32 -08:00
Dalton 5a85e4e524 Merge pull request #157 from dsato80/fragBuffer-fix
set fragBuffer data if processHTTP returns -1
2016-01-18 12:15:57 -08:00
Daisuke Sato d6f2c2c409 set fragBuffer data if processHTTP returns -1 2016-01-18 18:10:30 +09:00
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
Dalton e10942bc23 bumped spec 2015-11-05 10:09:45 -08:00
Dalton 9cf2c1f787 framework and package managers 2015-11-05 10:03:19 -08:00
Dalton bc48e654ca fixed tvOS build settings 2015-11-04 15:02:51 -08:00
Dalton 5e2768c048 possible fix for #132 2015-11-04 13:18:00 -08:00
Dalton 756a42756a Merge branch 'master' of https://github.com/daltoniam/Starscream 2015-10-20 18:51:17 -07:00
Dalton 4442aed401 added force timeout option for #127 2015-10-20 18:51:10 -07:00
Dalton 5f4db02f99 Merge pull request #129 from OlivierBoucher/master
Added tvOS target and podspec modifications
2015-10-20 18:46:09 -07:00
Olivier Boucher 679d858bbb Added tvOS target and podspec modifications 2015-10-20 09:16:21 -04:00
Dalton ec32edfb4e README update 2015-10-13 19:20:01 -07:00
Dalton df98a6e363 changelog and README update 2015-10-12 12:06:54 -07:00
Dalton 47bcdfbe57 bumped spec 2015-09-29 12:59:38 -07:00
Austin Cherry d69b169970 Merge pull request #125 from victorluft/master
When using cert pinning with public keys, only require one match.
2015-09-29 11:57:43 -07:00
Victor Luft aa2cb1b8fb When using cert pinning with public keys, only require one match. 2015-09-29 10:34:26 -07:00
Dalton fe2c26b760 fix for #121. Possible fix for upgrade issue 2015-09-28 10:29:36 -07:00
Dalton 357cd532dc example updates and fix #119 2015-09-28 10:14:36 -07:00
Dalton e0e4b379e7 Merge pull request #110 from TeletronicsDotAe/master
Property for enabling only certain cipher suites for the WSS connection
2015-09-22 10:28:03 -07:00
Pelle Stenild Coltau 4c66b22b43 Changes made regarding CipherSuites with respect to pull request review comments: Removed NSLog and doDisconnect() and added return statements in failure scenarios. 2015-09-21 11:27:14 +04:00
Pelle Stenild Coltau e848f186a5 Merge remote-tracking branch 'upstream/master' 2015-09-20 12:32:39 +04:00
Dalton c8201ec71c README update 2015-09-18 10:40:21 -07:00
Dalton f990c68c53 fixed conflicts 2015-09-14 09:56:30 -07:00
Dalton 9d37723c4d ready to become the master 2015-09-14 09:49:33 -07:00
Dalton b32048b229 ready to become the master 2015-09-14 09:47:31 -07:00
Dalton a40f448a16 quick README update 2015-09-14 09:32:49 -07:00
Dalton dcb046a46c bumped podspec 2015-09-14 09:26:01 -07:00
Dalton 357a178a64 last quick fix for #113 2015-09-14 09:24:45 -07:00
Dalton 7a15d7d7e6 fix for #113 2015-09-11 10:52:07 -07:00
Dalton 13f7502163 cleanup for Swift 2 2015-09-10 17:17:42 -07:00
Dalton 2e5cb03ac3 fix for #115 2015-09-10 15:59:29 -07:00
Dalton b26ffa28cd fix for #115 2015-09-10 15:57:34 -07:00
Dalton fd18936144 write queue change 2015-09-04 15:14:43 -07:00
Dalton e30b9a6f78 updated podspec 2015-09-04 13:33:40 -07:00
Dalton b962e3e532 fixed conflicts 2015-09-04 13:30:52 -07:00
Dalton 889d2880eb switch from unowned to weak 2015-09-04 13:21:28 -07:00
Dalton f509d9d485 updated podspec 2015-09-03 20:09:45 -07:00
Dalton 7b46baf12b better 2015-09-03 20:06:41 -07:00
Dalton 5dfe1fc117 added master fixes 2015-09-03 20:05:33 -07:00
Dalton f46e64b179 fix for #109, #105 and socket.io-swift 153 2015-09-03 19:59:26 -07:00
Pelle Stenild Coltau 4d072de18d Added optional unwrapping to cipher suites in case there is no ssl context 2015-08-30 11:22:39 +04:00
Leif Bredgaard Honore f0b188606f Revert "Better error handling of missing SSLContext"
This reverts commit fac69339ba.
2015-08-30 09:10:40 +04:00
Leif Bredgaard Honore fac69339ba Better error handling of missing SSLContext 2015-08-27 14:38:25 +04:00
Leif Bredgaard Honore e2c3e38a23 Bug fixed 2015-08-27 11:12:56 +04:00
Leif Bredgaard Honore 5f46a9097e Improved cipher suite error handling
We check the error code from SSLSetEnabledCiphers() and fail if it does
not succeed.
2015-08-26 14:06:43 +04:00
Leif Bredgaard Honore 8376a9c70b Cypher suite property renamed 2015-08-26 11:14:08 +04:00
Dalton 02f8eac43b Merge pull request #108 from FanVox/xcode7_b6
Make compatible with Xcode7 b5
2015-08-25 10:49:25 -07:00
Abizer Nasir 6a318b29d2 Make compatible with Xcode7 b5 2015-08-25 17:30:35 +01:00
Leif Bredgaard Honore df88d97511 It is now possible to set which SSL Cipher Suites are enabled for a connection 2015-08-24 14:21:05 +04:00
Austin ca478d26b2 fixed conflicts 2015-08-12 19:12:29 -07:00
Austin d1f3deee70 fix for #103 2015-08-12 10:33:32 -07:00
Dalton 4b65324a8d Merge pull request #102 from Ewg777/Swift-2.0
xcode 7 beta 5 compatibility
2015-08-07 13:31:16 -07:00
Ewg 9663215089 xcode 7 beta 5 compatibility 2015-08-07 13:13:35 +03:00
Ewg 5d272a80ef xcode 7 beta 5 compatibility 2015-08-07 13:12:48 +03:00
Dalton 56fb313bc5 added test autobahn project 2015-08-04 10:34:45 -07:00
Dalton e39ddeecf5 Merge pull request #101 from col/Swift-2.0
Xcode 7 / iOS 9 beta 4 compatibility
2015-07-22 10:38:22 -07:00
Colin Harris 36d4426888 Modified the Security class to make it compatible with Xcode 7 / iOS9 beta 4 2015-07-23 01:15:24 +08:00
Dalton 7b19d84984 fixes for #98 and #99. Closures are way more awesome. Possible fix for #87, #77 and fix for #72 2015-07-09 18:41:45 -07:00
Dalton b3db9d413a fix for #92 2015-06-14 19:47:09 -07:00
Dalton a69929fbc1 Merge pull request #95 from ricardopereira/Swift-2.0
Swift 2.0 update
2015-06-10 13:36:29 -07:00
Ricardo Pereira 3178fb1705 #93 Converted to latest swift syntax 2015-06-10 11:58:10 +01:00
34 changed files with 2606 additions and 1059 deletions
+29
View File
@@ -0,0 +1,29 @@
# Change Log
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)
Added TVOS support.
#### [1.0.1](https://github.com/daltoniam/Starscream/tree/1.0.1)
Fixes for #121, #123
#### [1.0.0](https://github.com/daltoniam/Starscream/tree/1.0.0)
first release of Swift 2 support.
+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"
)
+50 -11
View File
@@ -1,9 +1,10 @@
![starscream](http://limitedtoy.com/wp-content/uploads/2014/09/transformers-starscream-wallpaperstarscream-transformers-2-wallpaper---332913-pnx7lnff.jpg)
![starscream](https://raw.githubusercontent.com/daltoniam/starscream/assets/starscream.jpg)
Starscream is a conforming WebSocket ([RFC 6455](http://tools.ietf.org/html/rfc6455)) client library in Swift for iOS and OSX.
It's Objective-C counter part can be found here: [Jetfire](https://github.com/acmacalister/jetfire)
This is written Swift 2. (the latest). If you need older legecy support checkout the Swift-1.2 branch [here](https://github.com/daltoniam/Starscream/tree/swift-1.2).
## Features
@@ -24,7 +25,7 @@ import Starscream
Once imported, you can open a connection to your WebSocket server. Note that `socket` is probably best as a property, so your delegate can stick around.
```swift
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/"))
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!)
socket.delegate = self
socket.connect()
```
@@ -81,6 +82,31 @@ func websocketDidReceivePong(socket: WebSocket) {
}
```
Or you can use closures.
```swift
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!)
//websocketDidConnect
socket.onConnect = {
println("websocket is connected")
}
//websocketDidDisconnect
socket.onDisconnect = { (error: NSError?) in
println("websocket is disconnected: \(error?.localizedDescription)")
}
//websocketDidReceiveMessage
socket.onText = { (text: String) in
println("got some text: \(text)")
}
//websocketDidReceiveData
socket.onData = { (data: NSData) in
println("got some data: \(data.length)")
}
//you could do onPong as well.
socket.connect()
```
## The delegate methods give you a simple way to handle data from the server, but how do you send data?
### writeData
@@ -141,7 +167,7 @@ If you need to specify a protocol, simple add it to the init:
```swift
//chat and superchat are the example protocols here
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/"), protocols: ["chat","superchat"])
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
socket.delegate = self
socket.connect()
```
@@ -151,7 +177,7 @@ socket.connect()
There are a couple of other properties that modify the stream:
```swift
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/"), protocols: ["chat","superchat"])
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
//set this if you are planning on using the socket in a VOIP background setting (using the background VOIP service).
socket.voipEnabled = true
@@ -165,10 +191,10 @@ socket.selfSignedSSL = true
SSL Pinning is also supported in Starscream.
```swift
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/"), protocols: ["chat","superchat"])
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
let data = ... //load your certificate from disk
socket.security = Security(certs: [SSLCert(data: data)], usePublicKeys: true)
//socket.security = Security() //uses the .cer files in your app's bundle
socket.security = SSLSecurity(certs: [SSLCert(data: data)], usePublicKeys: true)
//socket.security = SSLSecurity() //uses the .cer files in your app's bundle
```
You load either a `NSData` blob of your certificate or you can use a `SecKeyRef` if you have a public key you want to use. The `usePublicKeys` bool is whether to use the certificates for validation or the public keys. The public keys will be extracted from the certificates automatically if `usePublicKeys` is choosen.
@@ -177,7 +203,7 @@ You load either a `NSData` blob of your certificate or you can use a `SecKeyRef`
A custom queue can be specified when delegate methods are called. By default `dispatch_get_main_queue` is used, thus making all delegate methods calls run on the main thread. It is important to note that all WebSocket processing is done on a background thread, only the delegate method calls are changed when modifying the queue. The actual processing is always on a background thread and will not pause your app.
```swift
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/"), protocols: ["chat","superchat"])
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
//create a custom queue
socket.queue = dispatch_queue_create("com.vluxe.starscream.myapp", nil)
```
@@ -188,7 +214,7 @@ 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.
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
@@ -202,7 +228,7 @@ To use Starscream in your project add the following 'Podfile' to your project
platform :ios, '8.0'
use_frameworks!
pod 'Starscream', '~> 0.9.2'
pod 'Starscream', '~> 1.0.0'
Then run:
@@ -214,6 +240,19 @@ Check out the [Carthage](https://github.com/Carthage/Carthage) docs on how to ad
[Carthage Install](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application)
You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
```bash
$ brew update
$ brew install carthage
```
To integrate Starscream into your Xcode project using Carthage, specify it in your `Cartfile`:
```
github "daltoniam/Starscream" >= 1.0.0
```
### Rogue
First see the [installation docs](https://github.com/acmacalister/Rogue) for how to install Rogue.
@@ -238,7 +277,7 @@ If you are running this in an OSX app or on a physical iOS device you will need
## TODOs
- [ ] Complete Docs
- [ ] WatchOS
- [ ] Add Unit Tests
## License
-244
View File
@@ -1,244 +0,0 @@
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// Security.swift
// Starscream
//
// Created by Dalton Cherry on 5/16/15.
// Copyright (c) 2015 Vluxe. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////////////////////////
import Foundation
import Security
public class SSLCert {
var certData: NSData?
var key: SecKeyRef?
/**
Designated init for certificates
:param: data is the binary data of the certificate
:returns: a representation security object to be used with
*/
public init(data: NSData) {
self.certData = data
}
/**
Designated init for public keys
:param: key is the public key to be used
:returns: a representation security object to be used with
*/
public init(key: SecKeyRef) {
self.key = key
}
}
public class Security {
public var validatedDN = true //should the domain name be validated?
var isReady = false //is the key processing done?
var certificates: [NSData]? //the certificates
var pubKeys: [SecKeyRef]? //the public keys
var usePublicKeys = false //use public keys or certificate validation?
/**
Use certs from main app bundle
:param: usePublicKeys is to specific if the publicKeys or certificates should be used for SSL pinning validation
:returns: a representation security object to be used with
*/
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))
}
}
self.init(certs:collect, usePublicKeys: usePublicKeys)
}
/**
Designated init
:param: keys is the certificates or public keys to use
:param: usePublicKeys is to specific if the publicKeys or certificates should be used for SSL pinning validation
:returns: a representation security object to be used with
*/
public init(certs: [SSLCert], usePublicKeys: Bool) {
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 {
cert.key = self.extractPublicKey(data)
}
if let k = cert.key {
collect.append(k)
}
}
self.pubKeys = collect
self.isReady = true
})
} else {
var collect = Array<NSData>()
for cert in certs {
if let d = cert.certData {
collect.append(d)
}
}
self.certificates = collect
self.isReady = true
}
}
/**
Valid the trust and domain name.
:param: trust is the serverTrust to validate
:param: domain is the CN domain to validate
:returns: if the key was successfully validated
*/
public func isValid(trust: SecTrustRef, domain: String?) -> Bool {
var tries = 0
while(!self.isReady) {
usleep(1000)
tries += 1
if tries > 5 {
return false //doesn't appear it is going to ever be ready...
}
}
var policy: SecPolicyRef
if self.validatedDN {
policy = SecPolicyCreateSSL(1, domain).takeRetainedValue()
} else {
policy = SecPolicyCreateBasicX509().takeRetainedValue()
}
SecTrustSetPolicies(trust,policy)
if self.usePublicKeys {
if let keys = self.pubKeys {
var trustedCount = 0
let serverPubKeys = publicKeyChainForTrust(trust)
for serverKey in serverPubKeys as [AnyObject] {
for key in keys as [AnyObject] {
if serverKey.isEqual(key) {
trustedCount++
break
}
}
}
if trustedCount == serverPubKeys.count {
return true
}
}
} else if let certs = self.certificates {
let serverCerts = certificateChainForTrust(trust)
var collect = Array<SecCertificate>()
for cert in certs {
collect.append(SecCertificateCreateWithData(nil,cert).takeRetainedValue())
}
SecTrustSetAnchorCertificates(trust,collect)
var result: SecTrustResultType = 0
SecTrustEvaluate(trust,&result)
let r = Int(result)
if r == kSecTrustResultUnspecified || r == kSecTrustResultProceed {
var trustedCount = 0
for serverCert in serverCerts {
for cert in certs {
if cert == serverCert {
trustedCount++
break
}
}
}
if trustedCount == serverCerts.count {
return true
}
}
}
return false
}
/**
Get the public key from a certificate data
:param: data is the certificate to pull the public key from
:returns: a public key
*/
func extractPublicKey(data: NSData) -> SecKeyRef? {
var publicKey: NSData?
let possibleCert = SecCertificateCreateWithData(nil,data)
if let cert = possibleCert {
return extractPublicKeyFromCert(cert.takeRetainedValue(),policy: SecPolicyCreateBasicX509().takeRetainedValue())
}
return nil
}
/**
Get the public key from a certificate
:param: data is the certificate to pull the public key from
:returns: a public key
*/
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? {
var possibleTrust: Unmanaged<SecTrust>?
SecTrustCreateWithCertificates(cert,policy, &possibleTrust)
if let trust = possibleTrust {
let t = trust.takeRetainedValue()
var result: SecTrustResultType = 0
SecTrustEvaluate(t,&result)
return SecTrustCopyPublicKey(t).takeRetainedValue()
}
return nil
}
/**
Get the certificate chain for the trust
:param: trust is the trust to lookup the certificate chain for
: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.takeRetainedValue()).takeRetainedValue())
}
return collect
}
/**
Get the public key chain for the trust
:param: trust is the trust to lookup the certificate chain and extract the public keys
:returns: the public keys from the certifcate chain for the trust
*/
func publicKeyChainForTrust(trust: SecTrustRef) -> Array<SecKeyRef> {
var collect = Array<SecKeyRef>()
let policy = SecPolicyCreateBasicX509().takeRetainedValue()
for var i = 0; i < SecTrustGetCertificateCount(trust); i++ {
let cert = SecTrustGetCertificateAtIndex(trust,i)
if let key = extractPublicKeyFromCert(cert.takeRetainedValue(), policy: policy) {
collect.append(key)
}
}
return collect
}
}
+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.1.1</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>
+2 -2
View File
@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.vluxe.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>1.1.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+253
View File
@@ -0,0 +1,253 @@
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// SSLSecurity.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 Foundation
import Security
public class SSLCert {
var certData: NSData?
var key: SecKeyRef?
/**
Designated init for certificates
- parameter data: is the binary data of the certificate
- returns: a representation security object to be used with
*/
public init(data: NSData) {
self.certData = data
}
/**
Designated init for public keys
- parameter key: is the public key to be used
- returns: a representation security object to be used with
*/
public init(key: SecKeyRef) {
self.key = key
}
}
public class SSLSecurity {
public var validatedDN = true //should the domain name be validated?
var isReady = false //is the key processing done?
var certificates: [NSData]? //the certificates
var pubKeys: [SecKeyRef]? //the public keys
var usePublicKeys = false //use public keys or certificate validation?
/**
Use certs from main app bundle
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
- returns: a representation security object to be used with
*/
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
if let data = NSData(contentsOfFile: path) {
certs.append(SSLCert(data: data))
}
return certs
}
self.init(certs: certs, usePublicKeys: usePublicKeys)
}
/**
Designated init
- parameter keys: is the certificates or public keys to use
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
- returns: a representation security object to be used with
*/
public init(certs: [SSLCert], usePublicKeys: Bool) {
self.usePublicKeys = usePublicKeys
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
if let data = cert.certData where cert.key == nil {
cert.key = self.extractPublicKey(data)
}
if let key = cert.key {
pubKeys.append(key)
}
return pubKeys
}
self.pubKeys = pubKeys
self.isReady = true
}
} 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
}
}
/**
Valid the trust and domain name.
- parameter trust: is the serverTrust to validate
- parameter domain: is the CN domain to validate
- returns: if the key was successfully validated
*/
public func isValid(trust: SecTrustRef, domain: String?) -> Bool {
var tries = 0
while(!self.isReady) {
usleep(1000)
tries += 1
if tries > 5 {
return false //doesn't appear it is going to ever be ready...
}
}
var policy: SecPolicyRef
if self.validatedDN {
policy = SecPolicyCreateSSL(true, domain)
} else {
policy = SecPolicyCreateBasicX509()
}
SecTrustSetPolicies(trust,policy)
if self.usePublicKeys {
if let keys = self.pubKeys {
let serverPubKeys = publicKeyChainForTrust(trust)
for serverKey in serverPubKeys as [AnyObject] {
for key in keys as [AnyObject] {
if serverKey.isEqual(key) {
return true
}
}
}
}
} else if let certs = self.certificates {
let serverCerts = certificateChainForTrust(trust)
var collect = [SecCertificate]()
for cert in certs {
collect.append(SecCertificateCreateWithData(nil,cert)!)
}
SecTrustSetAnchorCertificates(trust,collect)
var result: SecTrustResultType = 0
SecTrustEvaluate(trust,&result)
let r = Int(result)
if r == kSecTrustResultUnspecified || r == kSecTrustResultProceed {
var trustedCount = 0
for serverCert in serverCerts {
for cert in certs {
if cert == serverCert {
trustedCount++
break
}
}
}
if trustedCount == serverCerts.count {
return true
}
}
}
return false
}
/**
Get the public key from a certificate data
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKey(data: NSData) -> SecKeyRef? {
guard let cert = SecCertificateCreateWithData(nil, data) else { return nil }
return extractPublicKeyFromCert(cert, policy: SecPolicyCreateBasicX509())
}
/**
Get the public key from a certificate
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? {
var possibleTrust: SecTrust?
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
guard let trust = possibleTrust else { return nil }
var result: SecTrustResultType = 0
SecTrustEvaluate(trust, &result)
return SecTrustCopyPublicKey(trust)
}
/**
Get the certificate chain for the trust
- parameter trust: is the trust to lookup the certificate chain for
- 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 cert = SecTrustGetCertificateAtIndex(trust, index)
certificates.append(SecCertificateCopyData(cert!))
return certificates
}
return certificates
}
/**
Get the public key chain for the trust
- parameter trust: is the trust to lookup the certificate chain and extract the public keys
- returns: the public keys from the certifcate chain for the trust
*/
func publicKeyChainForTrust(trust: SecTrustRef) -> [SecKeyRef] {
let policy = SecPolicyCreateBasicX509()
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) {
keys.append(key)
}
return keys
}
return keys
}
}
+773
View File
@@ -0,0 +1,773 @@
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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.
//
//////////////////////////////////////////////////////////////////////////////////////////////////
import Foundation
import CoreFoundation
import Security
public protocol WebSocketDelegate: class {
func websocketDidConnect(socket: WebSocket)
func websocketDidDisconnect(socket: WebSocket, error: NSError?)
func websocketDidReceiveMessage(socket: WebSocket, text: String)
func websocketDidReceiveData(socket: WebSocket, data: NSData)
}
public protocol WebSocketPongDelegate: class {
func websocketDidReceivePong(socket: WebSocket)
}
public class WebSocket : NSObject, NSStreamDelegate {
enum OpCode : UInt8 {
case ContinueFrame = 0x0
case TextFrame = 0x1
case BinaryFrame = 0x2
//3-7 are reserved.
case ConnectionClose = 0x8
case Ping = 0x9
case Pong = 0xA
//B-F reserved.
}
public enum CloseCode : UInt16 {
case Normal = 1000
case GoingAway = 1001
case ProtocolError = 1002
case ProtocolUnhandledType = 1003
// 1004 reserved.
case NoStatusReceived = 1005
//1006 reserved.
case Encoding = 1007
case PolicyViolated = 1008
case MessageTooBig = 1009
}
public static let ErrorDomain = "WebSocket"
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()
var optionalProtocols : [String]?
//Constant Values.
let headerWSUpgradeName = "Upgrade"
let headerWSUpgradeValue = "websocket"
let headerWSHostName = "Host"
let headerWSConnectionName = "Connection"
let headerWSConnectionValue = "Upgrade"
let headerWSProtocolName = "Sec-WebSocket-Protocol"
let headerWSVersionName = "Sec-WebSocket-Version"
let headerWSVersionValue = "13"
let headerWSKeyName = "Sec-WebSocket-Key"
let headerOriginName = "Origin"
let headerWSAcceptName = "Sec-WebSocket-Accept"
let BUFFER_MAX = 4096
let FinMask: UInt8 = 0x80
let OpCodeMask: UInt8 = 0x0F
let RSVMask: UInt8 = 0x70
let MaskMask: UInt8 = 0x80
let PayloadLenMask: UInt8 = 0x7F
let MaxFrameSize: Int = 32
class WSResponse {
var isFin = false
var code: OpCode = .ContinueFrame
var bytesLeft = 0
var frameCount = 0
var buffer: NSMutableData?
}
public weak var delegate: WebSocketDelegate?
public weak var pongDelegate: WebSocketPongDelegate?
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 isConnected :Bool {
return connected
}
public var currentURL: NSURL {return url}
private var url: NSURL
private var inputStream: NSInputStream?
private var outputStream: NSOutputStream?
private var connected = false
private var isCreated = false
private var writeQueue = NSOperationQueue()
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)
//used for setting protocols.
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() {
guard !isCreated else { return }
didDisconnect = false
isCreated = true
createHTTPRequest()
isCreated = 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) { [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 method that starts the connection
private func createHTTPRequest() {
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET",
url, kCFHTTPVersion1_1).takeRetainedValue()
var port = url.port
if port == nil {
if ["wss", "https"].contains(url.scheme) {
port = 443
} else {
port = 80
}
}
addHeader(urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue)
addHeader(urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue)
if let protocols = optionalProtocols {
addHeader(urlRequest, key: headerWSProtocolName, val: protocols.joinWithSeparator(","))
}
addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue)
addHeader(urlRequest, key: headerWSKeyName, val: generateWebSocketKey())
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)
}
if let cfHTTPMessage = CFHTTPMessageCopySerializedMessage(urlRequest) {
let serializedRequest = cfHTTPMessage.takeRetainedValue()
initStreamsWithData(serializedRequest, Int(port!))
}
}
//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
private func generateWebSocketKey() -> String {
var key = ""
let seed = 16
for _ in 0..<seed {
let uni = UnicodeScalar(UInt32(97 + arc4random_uniform(25)))
key += "\(Character(uni))"
}
let data = key.dataUsingEncoding(NSUTF8StringEncoding)
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
//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!
CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream)
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()
guard let inStream = inputStream, let outStream = outputStream else { return }
inStream.delegate = self
outStream.delegate = self
if ["wss", "https"].contains(url.scheme) {
inStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
outStream.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
} else {
certValidated = true //not a https session, so no need to check SSL pinning
}
if voipEnabled {
inStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
outStream.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
}
if selfSignedSSL {
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? {
let resIn = SSLSetEnabledCiphers(sslContextIn, cipherSuites, cipherSuites.count)
let resOut = SSLSetEnabledCiphers(sslContextOut, cipherSuites, cipherSuites.count)
if resIn != errSecSuccess {
let error = self.errorWithDetail("Error setting ingoing cypher suites", code: UInt16(resIn))
disconnectStream(error)
return
}
if resOut != errSecSuccess {
let error = self.errorWithDetail("Error setting outgoing cypher suites", code: UInt16(resOut))
disconnectStream(error)
return
}
}
}
CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue)
CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue)
inStream.open()
outStream.open()
let bytes = UnsafePointer<UInt8>(data.bytes)
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 && [.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)
if sec.isValid(trust as! SecTrustRef, domain: domain as! String?) {
certValidated = true
} else {
let error = errorWithDetail("Invalid SSL certificate", code: 1)
disconnectStream(error)
return
}
}
}
if eventCode == .HasBytesAvailable {
if aStream == inputStream {
processInputStream()
}
} else if eventCode == .ErrorOccurred {
disconnectStream(aStream.streamError)
} else if eventCode == .EndEncountered {
disconnectStream(nil)
}
}
//disconnect the stream object
private func disconnectStream(error: NSError?) {
writeQueue.waitUntilAllOperationsAreFinished()
if let stream = inputStream {
CFReadStreamSetDispatchQueue(stream, nil)
stream.close()
}
if let stream = outputStream {
CFWriteStreamSetDispatchQueue(stream, nil)
stream.close()
}
outputStream = nil
certValidated = false
doDisconnect(error)
}
///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 {
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() {
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)
}
case -1:
fragBuffer = NSData(bytes: buffer, length: bufferLen)
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) -> 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++
if k == 3 {
totalSize = i + 1
break
}
} else {
k = 0
}
}
if totalSize > 0 {
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 -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) -> Int {
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue()
CFHTTPMessageAppendBytes(response, buffer, bufferLen)
let code = CFHTTPMessageGetResponseStatusCode(response)
if code != 101 {
return code
}
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
let headers = cfHeaders.takeRetainedValue() as NSDictionary
if let acceptKey = headers[headerWSAcceptName] as? NSString {
if acceptKey.length > 0 {
return 0
}
}
}
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
private func processRawMessage(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
let response = readStack.last
if response != nil && bufferLen < 2 {
fragBuffer = NSData(bytes: buffer, length: bufferLen)
return
}
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
}
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)
}
return
} else {
let isFin = (FinMask & 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 != .Pong {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("masked and rsv data is not currently supported", code: errCode))
writeError(errCode)
return
}
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
}
if isControlFrame && isFin == 0 {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("control frames can't be fragmented", code: errCode))
writeError(errCode)
return
}
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)
if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) {
code = CloseCode.ProtocolError.rawValue
}
offset += 2
}
if 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 {
code = CloseCode.ProtocolError.rawValue
}
}
}
doDisconnect(errorWithDetail("connection closed by server", code: code))
writeError(code)
return
}
if isControlFrame && payloadLen > 125 {
writeError(CloseCode.ProtocolError.rawValue)
return
}
var dataLength = UInt64(payloadLen)
if dataLength == 127 {
dataLength = WebSocket.readUint64(buffer, offset: offset)
offset += sizeof(UInt64)
} else if dataLength == 126 {
dataLength = UInt64(WebSocket.readUint16(buffer, offset: offset))
offset += sizeof(UInt16)
}
if bufferLen < offset || UInt64(bufferLen - offset) < dataLength {
fragBuffer = NSData(bytes: buffer, length: bufferLen)
return
}
var len = dataLength
if dataLength > UInt64(bufferLen) {
len = UInt64(bufferLen-offset)
}
var data: NSData!
if len < 0 {
len = 0
data = NSData()
} else {
data = NSData(bytes: UnsafePointer<UInt8>((buffer+offset)), length: Int(len))
}
if receivedOpcode == .Pong {
dispatch_async(queue) { [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
}
var response = readStack.last
if isControlFrame {
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
}
var isNew = false
if response == nil {
if receivedOpcode == .ContinueFrame {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("first frame can't be a continue frame",
code: errCode))
writeError(errCode)
return
}
isNew = true
response = WSResponse()
response!.code = receivedOpcode!
response!.bytesLeft = Int(dataLength)
response!.buffer = NSMutableData(data: data)
} else {
if receivedOpcode == .ContinueFrame {
response!.bytesLeft = Int(dataLength)
} else {
let errCode = CloseCode.ProtocolError.rawValue
doDisconnect(errorWithDetail("second and beyond of fragment message must be a continue frame",
code: errCode))
writeError(errCode)
return
}
response!.buffer!.appendData(data)
}
if let response = response {
response.bytesLeft -= Int(len)
response.frameCount++
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)
}
}
///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
dequeueWrite(data, code: OpCode.Pong)
} else if response.code == .TextFrame {
let str: NSString? = NSString(data: response.buffer!, encoding: NSUTF8StringEncoding)
if str == nil {
writeError(CloseCode.Encoding.rawValue)
return false
}
dispatch_async(queue) { [weak self] in
guard let s = self else { return }
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
guard let s = self else { return }
s.onData?(data)
s.delegate?.websocketDidReceiveData(s, data: data)
}
}
readStack.removeLast()
return true
}
return false
}
///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
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) {
writeQueue.addOperationWithBlock { [weak self] in
//stream isn't ready, let's wait
guard let s = self else { return }
var offset = 2
let bytes = UnsafeMutablePointer<UInt8>(data.bytes)
let dataLength = data.length
let frame = NSMutableData(capacity: dataLength + s.MaxFrameSize)
let buffer = UnsafeMutablePointer<UInt8>(frame!.mutableBytes)
buffer[0] = s.FinMask | code.rawValue
if dataLength < 126 {
buffer[1] = CUnsignedChar(dataLength)
} else if dataLength <= Int(UInt16.max) {
buffer[1] = 126
WebSocket.writeUint16(buffer, offset: offset, value: UInt16(dataLength))
offset += sizeof(UInt16)
} else {
buffer[1] = 127
WebSocket.writeUint64(buffer, offset: offset, value: UInt64(dataLength))
offset += sizeof(UInt64)
}
buffer[1] |= s.MaskMask
let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey)
offset += sizeof(UInt32)
for i in 0..<dataLength {
buffer[offset] = bytes[i] ^ maskKey[i % sizeof(UInt32)]
offset += 1
}
var total = 0
while true {
guard let outStream = s.outputStream else { break }
let writeBuffer = UnsafePointer<UInt8>(frame!.bytes+total)
let len = outStream.write(writeBuffer, maxLength: offset-total)
if len < 0 {
var error: NSError?
if let streamError = outStream.streamError {
error = streamError
} else {
let errCode = InternalErrorCode.OutputStreamWriteError.rawValue
error = s.errorWithDetail("output stream error during write", code: errCode)
}
s.doDisconnect(error)
break
} else {
total += len
}
if total >= offset {
break
}
}
}
}
///used to preform the disconnect delegate
private func doDisconnect(error: NSError?) {
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)
}
}
}
+3 -2
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Starscream"
s.version = "0.9.4"
s.version = "1.1.1"
s.summary = "A conforming WebSocket RFC 6455 client library in Swift for iOS and OSX."
s.homepage = "https://github.com/daltoniam/Starscream"
s.license = 'Apache License, Version 2.0'
@@ -9,6 +9,7 @@ Pod::Spec.new do |s|
s.social_media_url = 'http://twitter.com/daltoniam'
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.9'
s.source_files = '*.swift'
s.tvos.deployment_target = '9.0'
s.source_files = 'Source/*.swift'
s.requires_arc = 'true'
end
+257 -25
View File
@@ -7,18 +7,30 @@
objects = {
/* Begin PBXBuildFile section */
5C06AE8F1B08050D00D41060 /* Security.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C06AE8E1B08050D00D41060 /* Security.swift */; };
5C06AE901B08050D00D41060 /* Security.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C06AE8E1B08050D00D41060 /* Security.swift */; };
091277A11BD673A70003036D /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 091277971BD673A70003036D /* Starscream.framework */; };
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 */
091277A21BD673A70003036D /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 6B3E79DD19D48B7F006071F7 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 091277961BD673A70003036D;
remoteInfo = StarscreamTv;
};
D9C3E36B19E48FF1009FC285 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 6B3E79DD19D48B7F006071F7 /* Project object */;
@@ -29,19 +41,37 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
5C06AE8E1B08050D00D41060 /* Security.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Security.swift; sourceTree = SOURCE_ROOT; };
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; };
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 */
/* Begin PBXFrameworksBuildPhase section */
091277931BD673A70003036D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0912779D1BD673A70003036D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
091277A11BD673A70003036D /* Starscream.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
6B3E79E219D48B7F006071F7 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -90,6 +120,8 @@
6B3E79F119D48B7F006071F7 /* StarscreamTests.xctest */,
D9C3E35F19E48FF1009FC285 /* Starscream.framework */,
D9C3E36919E48FF1009FC285 /* StarscreamOSXTests.xctest */,
091277971BD673A70003036D /* Starscream.framework */,
091277A01BD673A70003036D /* StarscreamTvTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -97,9 +129,9 @@
6B3E79E819D48B7F006071F7 /* Starscream */ = {
isa = PBXGroup;
children = (
6B3E7A0419D48C41006071F7 /* WebSocket.swift */,
5C06AE8E1B08050D00D41060 /* Security.swift */,
6B3E7A0619D48C5F006071F7 /* Starscream.h */,
5C1360001C473BEF00AA3A01 /* Starscream.h */,
5C135FFF1C473BEF00AA3A01 /* SSLSecurity.swift */,
5C1360011C473BEF00AA3A01 /* WebSocket.swift */,
6B3E79E919D48B7F006071F7 /* Supporting Files */,
);
path = Starscream;
@@ -108,7 +140,8 @@
6B3E79E919D48B7F006071F7 /* Supporting Files */ = {
isa = PBXGroup;
children = (
6B3E79EA19D48B7F006071F7 /* Info.plist */,
5C13600B1C473BFE00AA3A01 /* Info-tvOS.plist */,
5C13600C1C473BFE00AA3A01 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
@@ -125,11 +158,19 @@
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
091277941BD673A70003036D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
5C1360071C473BEF00AA3A01 /* Starscream.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
6B3E79E319D48B7F006071F7 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
6B3E7A0719D48C5F006071F7 /* Starscream.h in Headers */,
5C1360051C473BEF00AA3A01 /* Starscream.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -137,13 +178,49 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
D9C3E37919E49051009FC285 /* Starscream.h in Headers */,
5C1360061C473BEF00AA3A01 /* Starscream.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
091277961BD673A70003036D /* StarscreamTv */ = {
isa = PBXNativeTarget;
buildConfigurationList = 091277A81BD673A70003036D /* Build configuration list for PBXNativeTarget "StarscreamTv" */;
buildPhases = (
091277921BD673A70003036D /* Sources */,
091277931BD673A70003036D /* Frameworks */,
091277941BD673A70003036D /* Headers */,
091277951BD673A70003036D /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = StarscreamTv;
productName = StarscreamTv;
productReference = 091277971BD673A70003036D /* Starscream.framework */;
productType = "com.apple.product-type.framework";
};
0912779F1BD673A70003036D /* StarscreamTvTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 091277AB1BD673A70003036D /* Build configuration list for PBXNativeTarget "StarscreamTvTests" */;
buildPhases = (
0912779C1BD673A70003036D /* Sources */,
0912779D1BD673A70003036D /* Frameworks */,
0912779E1BD673A70003036D /* Resources */,
);
buildRules = (
);
dependencies = (
091277A31BD673A70003036D /* PBXTargetDependency */,
);
name = StarscreamTvTests;
productName = StarscreamTvTests;
productReference = 091277A01BD673A70003036D /* StarscreamTvTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
6B3E79E519D48B7F006071F7 /* Starscream */ = {
isa = PBXNativeTarget;
buildConfigurationList = 6B3E79F919D48B7F006071F7 /* Build configuration list for PBXNativeTarget "Starscream" */;
@@ -221,9 +298,17 @@
6B3E79DD19D48B7F006071F7 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0600;
LastSwiftMigration = 0700;
LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = Vluxe;
TargetAttributes = {
091277961BD673A70003036D = {
CreatedOnToolsVersion = 7.1;
};
0912779F1BD673A70003036D = {
CreatedOnToolsVersion = 7.1;
};
6B3E79E519D48B7F006071F7 = {
CreatedOnToolsVersion = 6.0.1;
};
@@ -254,11 +339,27 @@
6B3E79F019D48B7F006071F7 /* StarscreamTests */,
D9C3E35E19E48FF1009FC285 /* StarscreamOSX */,
D9C3E36819E48FF1009FC285 /* StarscreamOSXTests */,
091277961BD673A70003036D /* StarscreamTv */,
0912779F1BD673A70003036D /* StarscreamTvTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
091277951BD673A70003036D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0912779E1BD673A70003036D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
6B3E79E419D48B7F006071F7 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -290,12 +391,29 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
091277921BD673A70003036D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5C13600A1C473BEF00AA3A01 /* WebSocket.swift in Sources */,
5C1360041C473BEF00AA3A01 /* SSLSecurity.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0912779C1BD673A70003036D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5CADAB511BEBD068005DE2F0 /* StarscreamTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
6B3E79E119D48B7F006071F7 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6B3E7A0519D48C41006071F7 /* WebSocket.swift in Sources */,
5C06AE8F1B08050D00D41060 /* Security.swift in Sources */,
5C1360081C473BEF00AA3A01 /* WebSocket.swift in Sources */,
5C1360021C473BEF00AA3A01 /* SSLSecurity.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -311,8 +429,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D9C3E37A19E49058009FC285 /* WebSocket.swift in Sources */,
5C06AE901B08050D00D41060 /* Security.swift in Sources */,
5C1360091C473BEF00AA3A01 /* WebSocket.swift in Sources */,
5C1360031C473BEF00AA3A01 /* SSLSecurity.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -327,6 +445,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
091277A31BD673A70003036D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 091277961BD673A70003036D /* StarscreamTv */;
targetProxy = 091277A21BD673A70003036D /* PBXContainerItemProxy */;
};
D9C3E36C19E48FF1009FC285 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D9C3E35E19E48FF1009FC285 /* StarscreamOSX */;
@@ -335,6 +458,84 @@
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
091277A91BD673A70003036D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
BITCODE_GENERATION_MODE = marker;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEBUG_INFORMATION_FORMAT = dwarf;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
GCC_NO_COMMON_BLOCKS = YES;
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;
PRODUCT_NAME = Starscream;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
};
name = Debug;
};
091277AA1BD673A70003036D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
BITCODE_GENERATION_MODE = bitcode;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
GCC_NO_COMMON_BLOCKS = YES;
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;
PRODUCT_NAME = Starscream;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
};
name = Release;
};
091277AC1BD673A70003036D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_NO_COMMON_BLOCKS = YES;
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)";
SDKROOT = appletvos;
TVOS_DEPLOYMENT_TARGET = 9.0;
};
name = Debug;
};
091277AD1BD673A70003036D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_NO_COMMON_BLOCKS = YES;
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)";
SDKROOT = appletvos;
TVOS_DEPLOYMENT_TARGET = 9.0;
};
name = Release;
};
6B3E79F719D48B7F006071F7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -356,6 +557,7 @@
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -423,6 +625,7 @@
6B3E79FA19D48B7F006071F7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BITCODE_GENERATION_MODE = marker;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@@ -430,11 +633,12 @@
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";
OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
SKIP_INSTALL = YES;
@@ -445,6 +649,7 @@
6B3E79FB19D48B7F006071F7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BITCODE_GENERATION_MODE = bitcode;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@@ -452,11 +657,12 @@
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";
OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
SKIP_INSTALL = YES;
@@ -476,6 +682,7 @@
);
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
@@ -489,6 +696,7 @@
);
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
@@ -506,13 +714,15 @@
"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;
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = Starscream;
SDKROOT = macosx;
SKIP_INSTALL = YES;
VALID_ARCHS = x86_64;
};
name = Debug;
};
@@ -526,13 +736,15 @@
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;
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = Starscream;
SDKROOT = macosx;
SKIP_INSTALL = YES;
VALID_ARCHS = x86_64;
};
name = Release;
};
@@ -551,6 +763,7 @@
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
};
@@ -568,6 +781,7 @@
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
};
@@ -576,6 +790,24 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
091277A81BD673A70003036D /* Build configuration list for PBXNativeTarget "StarscreamTv" */ = {
isa = XCConfigurationList;
buildConfigurations = (
091277A91BD673A70003036D /* Debug */,
091277AA1BD673A70003036D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
091277AB1BD673A70003036D /* Build configuration list for PBXNativeTarget "StarscreamTvTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
091277AC1BD673A70003036D /* Debug */,
091277AD1BD673A70003036D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
6B3E79E019D48B7F006071F7 /* Build configuration list for PBXProject "Starscream" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -23,21 +23,24 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<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">
<MacroExpansion>
<BuildableReference
@@ -52,10 +55,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
LastUpgradeVersion = "0700"
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,15 +62,18 @@
ReferencedContainer = "container:Starscream.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">
<MacroExpansion>
<BuildableReference
@@ -85,10 +88,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -23,10 +23,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">
@@ -48,15 +48,18 @@
ReferencedContainer = "container:Starscream.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">
<MacroExpansion>
<BuildableReference
@@ -71,10 +74,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "091277961BD673A70003036D"
BuildableName = "Starscream.framework"
BlueprintName = "StarscreamTv"
ReferencedContainer = "container:Starscream.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0912779F1BD673A70003036D"
BuildableName = "StarscreamTvTests.xctest"
BlueprintName = "StarscreamTvTests"
ReferencedContainer = "container:Starscream.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0912779F1BD673A70003036D"
BuildableName = "StarscreamTvTests.xctest"
BlueprintName = "StarscreamTvTests"
ReferencedContainer = "container:Starscream.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "091277961BD673A70003036D"
BuildableName = "Starscream.framework"
BlueprintName = "StarscreamTv"
ReferencedContainer = "container:Starscream.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "091277961BD673A70003036D"
BuildableName = "Starscream.framework"
BlueprintName = "StarscreamTv"
ReferencedContainer = "container:Starscream.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "091277961BD673A70003036D"
BuildableName = "Starscream.framework"
BlueprintName = "StarscreamTv"
ReferencedContainer = "container:Starscream.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+1 -1
View File
@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.vluxe.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
-722
View File
@@ -1,722 +0,0 @@
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// Websocket.swift
//
// Created by Dalton Cherry on 7/16/14.
//
//////////////////////////////////////////////////////////////////////////////////////////////////
import Foundation
import CoreFoundation
public protocol WebSocketDelegate: class {
func websocketDidConnect(socket: WebSocket)
func websocketDidDisconnect(socket: WebSocket, error: NSError?)
func websocketDidReceiveMessage(socket: WebSocket, text: String)
func websocketDidReceiveData(socket: WebSocket, data: NSData)
}
public protocol WebSocketPongDelegate: class {
func websocketDidReceivePong(socket: WebSocket)
}
public class WebSocket : NSObject, NSStreamDelegate {
enum OpCode : UInt8 {
case ContinueFrame = 0x0
case TextFrame = 0x1
case BinaryFrame = 0x2
//3-7 are reserved.
case ConnectionClose = 0x8
case Ping = 0x9
case Pong = 0xA
//B-F reserved.
}
enum CloseCode : UInt16 {
case Normal = 1000
case GoingAway = 1001
case ProtocolError = 1002
case ProtocolUnhandledType = 1003
// 1004 reserved.
case NoStatusReceived = 1005
//1006 reserved.
case Encoding = 1007
case PolicyViolated = 1008
case MessageTooBig = 1009
}
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()
var optionalProtocols : Array<String>?
//Constant Values.
let headerWSUpgradeName = "Upgrade"
let headerWSUpgradeValue = "websocket"
let headerWSHostName = "Host"
let headerWSConnectionName = "Connection"
let headerWSConnectionValue = "Upgrade"
let headerWSProtocolName = "Sec-WebSocket-Protocol"
let headerWSVersionName = "Sec-WebSocket-Version"
let headerWSVersionValue = "13"
let headerWSKeyName = "Sec-WebSocket-Key"
let headerOriginName = "Origin"
let headerWSAcceptName = "Sec-WebSocket-Accept"
let BUFFER_MAX = 2048
let FinMask: UInt8 = 0x80
let OpCodeMask: UInt8 = 0x0F
let RSVMask: UInt8 = 0x70
let MaskMask: UInt8 = 0x80
let PayloadLenMask: UInt8 = 0x7F
let MaxFrameSize: Int = 32
class WSResponse {
var isFin = false
var code: OpCode = .ContinueFrame
var bytesLeft = 0
var frameCount = 0
var buffer: NSMutableData?
}
public weak var delegate: WebSocketDelegate?
public weak var pongDelegate: WebSocketPongDelegate?
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 fragBuffer: NSData?
public var headers = Dictionary<String,String>()
public var voipEnabled = false
public var selfSignedSSL = false
public var security: Security?
private var certValidated = false
private var connectedBlock: ((Void) -> Void)? = nil
private var disconnectedBlock: ((NSError?) -> Void)? = nil
private var receivedTextBlock: ((String) -> Void)? = nil
private var receivedDataBlock: ((NSData) -> Void)? = nil
public var isConnected :Bool {
return connected
}
//init the websocket with a url
public init(url: NSURL) {
self.url = url
}
//used for setting protocols.
public convenience init(url: NSURL, protocols: Array<String>) {
self.init(url: url)
optionalProtocols = protocols
}
//closure based instead of the delegate
public convenience init(url: NSURL, protocols: Array<String>, connect:((Void) -> Void), disconnect:((NSError?) -> Void), text:((String) -> Void), data:(NSData) -> Void) {
self.init(url: url, protocols: protocols)
connectedBlock = connect
disconnectedBlock = disconnect
receivedTextBlock = text
receivedDataBlock = data
}
//same as above, just shorter
public convenience init(url: NSURL, connect:((Void) -> Void), disconnect:((NSError?) -> Void), text:((String) -> Void)) {
self.init(url: url)
connectedBlock = connect
disconnectedBlock = disconnect
receivedTextBlock = text
}
//same as above, just shorter
public convenience init(url: NSURL, connect:((Void) -> Void), disconnect:((NSError?) -> Void), data:((NSData) -> Void)) {
self.init(url: url)
connectedBlock = connect
disconnectedBlock = disconnect
receivedDataBlock = data
}
///Connect to the websocket server on a background thread
public func connect() {
if isCreated {
return
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {
self.isCreated = true
self.createHTTPRequest()
self.isCreated = false
})
}
///disconnect from the websocket server
public func disconnect() {
writeError(CloseCode.Normal.rawValue)
}
///write a string to the websocket. This sends it as a text frame.
public func writeString(str: String) {
dequeueWrite(str.dataUsingEncoding(NSUTF8StringEncoding)!, code: .TextFrame)
}
///write binary data to the websocket. This sends it as a binary frame.
public func writeData(data: NSData) {
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) {
dequeueWrite(data, code: .Ping)
}
//private methods below!
//private method that starts the connection
private func createHTTPRequest() {
let str: NSString = url.absoluteString!
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET",
url, kCFHTTPVersion1_1)
var port = url.port
if port == nil {
if url.scheme == "wss" || url.scheme == "https" {
port = 443
} else {
port = 80
}
}
self.addHeader(urlRequest, key: headerWSUpgradeName, val: headerWSUpgradeValue)
self.addHeader(urlRequest, key: headerWSConnectionName, val: headerWSConnectionValue)
if let protocols = optionalProtocols {
self.addHeader(urlRequest, key: headerWSProtocolName, val: ",".join(protocols))
}
self.addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue)
self.addHeader(urlRequest, key: headerWSKeyName, val: self.generateWebSocketKey())
self.addHeader(urlRequest, key: headerOriginName, val: url.absoluteString!)
self.addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)")
for (key,value) in headers {
self.addHeader(urlRequest, key: key, val: value)
}
let serializedRequest: NSData = CFHTTPMessageCopySerializedMessage(urlRequest.takeUnretainedValue()).takeUnretainedValue()
self.initStreamsWithData(serializedRequest, Int(port!))
}
//Add a header to the CFHTTPMessage by using the NSString bridges to CFString
private func addHeader(urlRequest: Unmanaged<CFHTTPMessage>,key: String, val: String) {
let nsKey: NSString = key
let nsVal: NSString = val
CFHTTPMessageSetHeaderFieldValue(urlRequest.takeUnretainedValue(),
nsKey,
nsVal)
}
//generate a websocket key as needed in rfc
private func generateWebSocketKey() -> String {
var key = ""
let seed = 16
for (var i = 0; i < seed; i++) {
let uni = UnicodeScalar(UInt32(97 + arc4random_uniform(25)))
key += "\(Character(uni))"
}
var data = key.dataUsingEncoding(NSUTF8StringEncoding)
var baseKey = data?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(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
//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!
CFStreamCreatePairWithSocketToHost(nil, h, UInt32(port), &readStream, &writeStream)
inputStream = readStream!.takeUnretainedValue()
outputStream = writeStream!.takeUnretainedValue()
inputStream!.delegate = self
outputStream!.delegate = self
if url.scheme == "wss" || url.scheme == "https" {
inputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
outputStream!.setProperty(NSStreamSocketSecurityLevelNegotiatedSSL, forKey: NSStreamSocketSecurityLevelKey)
} else {
certValidated = true //not a https session, so no need to check SSL pinning
}
if self.voipEnabled {
inputStream!.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
outputStream!.setProperty(NSStreamNetworkServiceTypeVoIP, forKey: NSStreamNetworkServiceType)
}
if self.selfSignedSSL {
let settings: Dictionary<NSObject, NSObject> = [kCFStreamSSLValidatesCertificateChain: NSNumber(bool:false), kCFStreamSSLPeerName: kCFNull]
inputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as! String)
outputStream!.setProperty(settings, forKey: kCFStreamPropertySSLSettings as! String)
}
isRunLoop = true
inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
inputStream!.open()
outputStream!.open()
let bytes = UnsafePointer<UInt8>(data.bytes)
outputStream!.write(bytes, maxLength: data.length)
while(isRunLoop) {
NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture() as! NSDate)
}
}
//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) {
var possibleTrust: AnyObject? = aStream.propertyForKey(kCFStreamPropertySSLPeerTrust as! String)
if let trust: AnyObject = possibleTrust {
var domain: AnyObject? = aStream.propertyForKey(kCFStreamSSLPeerName as! String)
if sec.isValid(trust as! SecTrustRef, domain: domain as! String?) {
certValidated = true
} else {
let error = self.errorWithDetail("Invalid SSL certificate", code: 1)
doDisconnect(error)
disconnectStream(error)
return
}
}
}
if eventCode == .HasBytesAvailable {
if(aStream == inputStream) {
processInputStream()
}
} else if eventCode == .ErrorOccurred {
disconnectStream(aStream.streamError)
} else if eventCode == .EndEncountered {
disconnectStream(nil)
}
}
//disconnect the stream object
private func disconnectStream(error: NSError?) {
if writeQueue != nil {
writeQueue!.waitUntilAllOperationsAreFinished()
}
if let stream = inputStream {
stream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
stream.close()
}
if let stream = outputStream {
stream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
stream.close()
}
outputStream = nil
isRunLoop = false
certValidated = false
self.doDisconnect(error)
connected = false
}
///handles the incoming bytes and sending them to the proper processing method
private func processInputStream() {
let buf = NSMutableData(capacity: BUFFER_MAX)
var 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 {
self.doDisconnect(self.errorWithDetail("Invalid HTTP upgrade", code: 1))
}
} else {
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) {
var combine = NSMutableData(data: fragBuffer!)
combine.appendData(data)
work = combine
fragBuffer = nil
}
let buffer = UnsafePointer<UInt8>(work.bytes)
processRawMessage(buffer, bufferLen: work.length)
inputQueue = inputQueue.filter{$0 != data}
dequeueInput()
}
}
///Finds the HTTP Packet in the TCP stream, by looking for the CRLF.
private func processHTTP(buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Bool {
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++ {
if buffer[i] == CRLFBytes[k] {
k++
if k == 3 {
totalSize = i + 1
break
}
} else {
k = 0
}
}
if totalSize > 0 {
if validateResponse(buffer, bufferLen: totalSize) {
dispatch_async(queue,{
if let connectBlock = self.connectedBlock {
connectBlock()
}
self.delegate?.websocketDidConnect(self)
})
totalSize += 1 //skip the last \n
let restSize = bufferLen - totalSize
if restSize > 0 {
processRawMessage((buffer+totalSize),bufferLen: restSize)
}
return true
}
}
return false
}
///validates the HTTP is a 101 as per the RFC spec
private func validateResponse(buffer: UnsafePointer<UInt8>, bufferLen: Int) -> Bool {
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, 0).takeRetainedValue()
CFHTTPMessageAppendBytes(response, buffer, bufferLen)
if CFHTTPMessageGetResponseStatusCode(response) != 101 {
return false
}
let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response)
let headers: NSDictionary = cfHeaders.takeRetainedValue()
let acceptKey = headers[headerWSAcceptName] as! NSString
if acceptKey.length > 0 {
return true
}
return false
}
///process the websocket data
private func processRawMessage(buffer: UnsafePointer<UInt8>, bufferLen: Int) {
var response = readStack.last
if response != nil && bufferLen < 2 {
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 {
len = bufferLen
extra = 0
}
resp.bytesLeft -= len
resp.buffer?.appendData(NSData(bytes: buffer, length: len))
processResponse(resp)
var offset = bufferLen - extra
if extra > 0 {
processExtra((buffer+offset), bufferLen: extra)
}
return
} else {
let isFin = (FinMask & buffer[0])
let receivedOpcode = (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) {
let errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("masked and rsv data is not currently supported", code: errCode)
self.doDisconnect(error)
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 errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("unknown opcode: \(receivedOpcode)", code: errCode)
self.doDisconnect(error)
writeError(errCode)
return
}
if isControlFrame && isFin == 0 {
let errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("control frames can't be fragmented", code: errCode)
self.doDisconnect(error)
writeError(errCode)
return
}
if receivedOpcode == OpCode.ConnectionClose.rawValue {
var code = CloseCode.Normal.rawValue
if payloadLen == 1 {
code = CloseCode.ProtocolError.rawValue
} else if payloadLen > 1 {
var codeBuffer = UnsafePointer<UInt16>((buffer+offset))
code = codeBuffer[0].bigEndian
if code < 1000 || (code > 1003 && code < 1007) || (code > 1011 && code < 3000) {
code = CloseCode.ProtocolError.rawValue
}
offset += 2
}
if payloadLen > 2 {
let len = Int(payloadLen-2)
if len > 0 {
let bytes = UnsafePointer<UInt8>((buffer+offset))
var str: NSString? = NSString(data: NSData(bytes: bytes, length: len), encoding: NSUTF8StringEncoding)
if str == nil {
code = CloseCode.ProtocolError.rawValue
}
}
}
let error = self.errorWithDetail("connection closed by server", code: code)
self.doDisconnect(error)
writeError(code)
return
}
if isControlFrame && payloadLen > 125 {
writeError(CloseCode.ProtocolError.rawValue)
return
}
var dataLength = UInt64(payloadLen)
if dataLength == 127 {
let bytes = UnsafePointer<UInt64>((buffer+offset))
dataLength = bytes[0].bigEndian
offset += sizeof(UInt64)
} else if dataLength == 126 {
let bytes = UnsafePointer<UInt16>((buffer+offset))
dataLength = UInt64(bytes[0].bigEndian)
offset += sizeof(UInt16)
}
var len = dataLength
if dataLength > UInt64(bufferLen) {
len = UInt64(bufferLen-offset)
}
var data: NSData!
if len < 0 {
len = 0
data = NSData()
} else {
data = NSData(bytes: UnsafePointer<UInt8>((buffer+offset)), length: Int(len))
}
if receivedOpcode == OpCode.Pong.rawValue {
self.pongDelegate?.websocketDidReceivePong(self)
let step = Int(offset+numericCast(len))
let extra = bufferLen-step
if extra > 0 {
processRawMessage((buffer+step), bufferLen: extra)
}
return
}
var response = readStack.last
if isControlFrame {
response = nil //don't append pings
}
if isFin == 0 && receivedOpcode == OpCode.ContinueFrame.rawValue && response == nil {
let errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("continue frame before a binary or text frame", code: errCode)
self.doDisconnect(error)
writeError(errCode)
return
}
var isNew = false
if(response == nil) {
if receivedOpcode == OpCode.ContinueFrame.rawValue {
let errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("first frame can't be a continue frame",
code: errCode)
self.doDisconnect(error)
writeError(errCode)
return
}
isNew = true
response = WSResponse()
response!.code = OpCode(rawValue: receivedOpcode)!
response!.bytesLeft = Int(dataLength)
response!.buffer = NSMutableData(data: data)
} else {
if receivedOpcode == OpCode.ContinueFrame.rawValue {
response!.bytesLeft = Int(dataLength)
} else {
let errCode = CloseCode.ProtocolError.rawValue
let error = self.errorWithDetail("second and beyond of fragment message must be a continue frame",
code: errCode)
self.doDisconnect(error)
writeError(errCode)
return
}
response!.buffer!.appendData(data)
}
if response != nil {
response!.bytesLeft -= Int(len)
response!.frameCount++
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)
}
}
///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
dequeueWrite(data, code: OpCode.Pong)
} else if response.code == .TextFrame {
var str: NSString? = NSString(data: response.buffer!, encoding: NSUTF8StringEncoding)
if str == nil {
writeError(CloseCode.Encoding.rawValue)
return false
}
dispatch_async(queue,{
if let textBlock = self.receivedTextBlock{
textBlock(str! as! String)
}
self.delegate?.websocketDidReceiveMessage(self, text: str! as! String)
})
} else if response.code == .BinaryFrame {
let data = response.buffer! //local copy so it is perverse for writing
dispatch_async(queue,{
if let dataBlock = self.receivedDataBlock{
dataBlock(data)
}
self.delegate?.websocketDidReceiveData(self, data: data)
})
}
readStack.removeLast()
return true
}
return false
}
///Create an error
private func errorWithDetail(detail: String, code: UInt16) -> NSError {
var details = Dictionary<String,String>()
details[NSLocalizedDescriptionKey] = detail
return NSError(domain: "Websocket", code: Int(code), userInfo: details)
}
///write a an error to the socket
private func writeError(code: UInt16) {
let buf = NSMutableData(capacity: sizeof(UInt16))
var buffer = UnsafeMutablePointer<UInt16>(buf!.bytes)
buffer[0] = code.bigEndian
dequeueWrite(NSData(bytes: buffer, length: sizeof(UInt16)), code: .ConnectionClose)
}
///used to write things to the stream
private func dequeueWrite(data: NSData, code: OpCode) {
if writeQueue == nil {
writeQueue = NSOperationQueue()
writeQueue!.maxConcurrentOperationCount = 1
}
writeQueue!.addOperationWithBlock {
//stream isn't ready, let's wait
var tries = 0;
while self.outputStream == nil || !self.connected {
if(tries < 5) {
sleep(1);
} else {
break;
}
tries++;
}
if !self.connected {
return
}
var offset = 2
UINT16_MAX
let bytes = UnsafeMutablePointer<UInt8>(data.bytes)
let dataLength = data.length
let frame = NSMutableData(capacity: dataLength + self.MaxFrameSize)
let buffer = UnsafeMutablePointer<UInt8>(frame!.mutableBytes)
buffer[0] = self.FinMask | code.rawValue
if dataLength < 126 {
buffer[1] = CUnsignedChar(dataLength)
} else if dataLength <= Int(UInt16.max) {
buffer[1] = 126
var sizeBuffer = UnsafeMutablePointer<UInt16>((buffer+offset))
sizeBuffer[0] = UInt16(dataLength).bigEndian
offset += sizeof(UInt16)
} else {
buffer[1] = 127
var sizeBuffer = UnsafeMutablePointer<UInt64>((buffer+offset))
sizeBuffer[0] = UInt64(dataLength).bigEndian
offset += sizeof(UInt64)
}
buffer[1] |= self.MaskMask
var maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
SecRandomCopyBytes(kSecRandomDefault, Int(sizeof(UInt32)), maskKey)
offset += sizeof(UInt32)
for (var i = 0; i < dataLength; i++) {
buffer[offset] = bytes[i] ^ maskKey[i % sizeof(UInt32)]
offset += 1
}
var total = 0
while true {
if self.outputStream == nil {
break
}
let writeBuffer = UnsafePointer<UInt8>(frame!.bytes+total)
var len = self.outputStream?.write(writeBuffer, maxLength: offset-total)
if len == nil || len! < 0 {
var error: NSError?
if let streamError = self.outputStream?.streamError {
error = streamError
} else {
let errCode = InternalErrorCode.OutputStreamWriteError.rawValue
error = self.errorWithDetail("output stream error during write", code: errCode)
}
self.doDisconnect(error)
break
} else {
total += len!
}
if total >= offset {
break
}
}
}
}
///used to preform the disconnect delegate
private func doDisconnect(error: NSError?) {
if self.isConnected {
dispatch_async(queue,{
if let disconnect = self.disconnectedBlock {
disconnect(error)
}
self.delegate?.websocketDidDisconnect(self, error: error)
})
}
}
}
+27
View File
@@ -0,0 +1,27 @@
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control?
#
# Pods/
# Xcode
.DS_Store
build
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
profile
*.moved-aside
DerivedData
.idea/
*.hmap
*.xccheckout
*.xcodeproj/*.xcworkspace
@@ -0,0 +1,523 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
5C178E221B62D0B900A97204 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C178E211B62D0B900A97204 /* AppDelegate.swift */; };
5C178E241B62D0B900A97204 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C178E231B62D0B900A97204 /* ViewController.swift */; };
5C178E271B62D0B900A97204 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5C178E251B62D0B900A97204 /* Main.storyboard */; };
5C178E291B62D0B900A97204 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5C178E281B62D0B900A97204 /* Images.xcassets */; };
5C178E2C1B62D0B900A97204 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5C178E2A1B62D0B900A97204 /* LaunchScreen.xib */; };
5C178E381B62D0B900A97204 /* AutobahnTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C178E371B62D0B900A97204 /* AutobahnTests.swift */; };
5C178E521B62D11200A97204 /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C178E491B62D0EF00A97204 /* Starscream.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
5C178E321B62D0B900A97204 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5C178E141B62D0B900A97204 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 5C178E1B1B62D0B900A97204;
remoteInfo = Autobahn;
};
5C178E481B62D0EF00A97204 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 6B3E79E619D48B7F006071F7;
remoteInfo = Starscream;
};
5C178E4A1B62D0EF00A97204 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 6B3E79F119D48B7F006071F7;
remoteInfo = StarscreamTests;
};
5C178E4C1B62D0EF00A97204 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = D9C3E35F19E48FF1009FC285;
remoteInfo = StarscreamOSX;
};
5C178E4E1B62D0EF00A97204 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = D9C3E36919E48FF1009FC285;
remoteInfo = StarscreamOSXTests;
};
5C178E501B62D10A00A97204 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 6B3E79E519D48B7F006071F7;
remoteInfo = Starscream;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
5C178E1C1B62D0B900A97204 /* Autobahn.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Autobahn.app; sourceTree = BUILT_PRODUCTS_DIR; };
5C178E201B62D0B900A97204 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5C178E211B62D0B900A97204 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
5C178E231B62D0B900A97204 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
5C178E261B62D0B900A97204 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
5C178E281B62D0B900A97204 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
5C178E2B1B62D0B900A97204 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
5C178E311B62D0B900A97204 /* AutobahnTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AutobahnTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5C178E361B62D0B900A97204 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5C178E371B62D0B900A97204 /* AutobahnTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutobahnTests.swift; sourceTree = "<group>"; };
5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Starscream.xcodeproj; path = ../../Starscream.xcodeproj; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
5C178E191B62D0B900A97204 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5C178E521B62D11200A97204 /* Starscream.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5C178E2E1B62D0B900A97204 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
5C178E131B62D0B900A97204 = {
isa = PBXGroup;
children = (
5C178E1E1B62D0B900A97204 /* Autobahn */,
5C178E341B62D0B900A97204 /* AutobahnTests */,
5C178E1D1B62D0B900A97204 /* Products */,
5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */,
);
sourceTree = "<group>";
};
5C178E1D1B62D0B900A97204 /* Products */ = {
isa = PBXGroup;
children = (
5C178E1C1B62D0B900A97204 /* Autobahn.app */,
5C178E311B62D0B900A97204 /* AutobahnTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
5C178E1E1B62D0B900A97204 /* Autobahn */ = {
isa = PBXGroup;
children = (
5C178E211B62D0B900A97204 /* AppDelegate.swift */,
5C178E231B62D0B900A97204 /* ViewController.swift */,
5C178E251B62D0B900A97204 /* Main.storyboard */,
5C178E281B62D0B900A97204 /* Images.xcassets */,
5C178E2A1B62D0B900A97204 /* LaunchScreen.xib */,
5C178E1F1B62D0B900A97204 /* Supporting Files */,
);
path = Autobahn;
sourceTree = "<group>";
};
5C178E1F1B62D0B900A97204 /* Supporting Files */ = {
isa = PBXGroup;
children = (
5C178E201B62D0B900A97204 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
5C178E341B62D0B900A97204 /* AutobahnTests */ = {
isa = PBXGroup;
children = (
5C178E371B62D0B900A97204 /* AutobahnTests.swift */,
5C178E351B62D0B900A97204 /* Supporting Files */,
);
path = AutobahnTests;
sourceTree = "<group>";
};
5C178E351B62D0B900A97204 /* Supporting Files */ = {
isa = PBXGroup;
children = (
5C178E361B62D0B900A97204 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
5C178E421B62D0EF00A97204 /* Products */ = {
isa = PBXGroup;
children = (
5C178E491B62D0EF00A97204 /* Starscream.framework */,
5C178E4B1B62D0EF00A97204 /* StarscreamTests.xctest */,
5C178E4D1B62D0EF00A97204 /* Starscream.framework */,
5C178E4F1B62D0EF00A97204 /* StarscreamOSXTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
5C178E1B1B62D0B900A97204 /* Autobahn */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5C178E3B1B62D0B900A97204 /* Build configuration list for PBXNativeTarget "Autobahn" */;
buildPhases = (
5C178E181B62D0B900A97204 /* Sources */,
5C178E191B62D0B900A97204 /* Frameworks */,
5C178E1A1B62D0B900A97204 /* Resources */,
);
buildRules = (
);
dependencies = (
5C178E511B62D10A00A97204 /* PBXTargetDependency */,
);
name = Autobahn;
productName = Autobahn;
productReference = 5C178E1C1B62D0B900A97204 /* Autobahn.app */;
productType = "com.apple.product-type.application";
};
5C178E301B62D0B900A97204 /* AutobahnTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5C178E3E1B62D0B900A97204 /* Build configuration list for PBXNativeTarget "AutobahnTests" */;
buildPhases = (
5C178E2D1B62D0B900A97204 /* Sources */,
5C178E2E1B62D0B900A97204 /* Frameworks */,
5C178E2F1B62D0B900A97204 /* Resources */,
);
buildRules = (
);
dependencies = (
5C178E331B62D0B900A97204 /* PBXTargetDependency */,
);
name = AutobahnTests;
productName = AutobahnTests;
productReference = 5C178E311B62D0B900A97204 /* AutobahnTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
5C178E141B62D0B900A97204 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftMigration = 0700;
LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 0640;
ORGANIZATIONNAME = vluxe;
TargetAttributes = {
5C178E1B1B62D0B900A97204 = {
CreatedOnToolsVersion = 6.4;
};
5C178E301B62D0B900A97204 = {
CreatedOnToolsVersion = 6.4;
TestTargetID = 5C178E1B1B62D0B900A97204;
};
};
};
buildConfigurationList = 5C178E171B62D0B900A97204 /* Build configuration list for PBXProject "Autobahn" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 5C178E131B62D0B900A97204;
productRefGroup = 5C178E1D1B62D0B900A97204 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = 5C178E421B62D0EF00A97204 /* Products */;
ProjectRef = 5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */;
},
);
projectRoot = "";
targets = (
5C178E1B1B62D0B900A97204 /* Autobahn */,
5C178E301B62D0B900A97204 /* AutobahnTests */,
);
};
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
5C178E491B62D0EF00A97204 /* Starscream.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = Starscream.framework;
remoteRef = 5C178E481B62D0EF00A97204 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
5C178E4B1B62D0EF00A97204 /* StarscreamTests.xctest */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = StarscreamTests.xctest;
remoteRef = 5C178E4A1B62D0EF00A97204 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
5C178E4D1B62D0EF00A97204 /* Starscream.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = Starscream.framework;
remoteRef = 5C178E4C1B62D0EF00A97204 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
5C178E4F1B62D0EF00A97204 /* StarscreamOSXTests.xctest */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = StarscreamOSXTests.xctest;
remoteRef = 5C178E4E1B62D0EF00A97204 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
5C178E1A1B62D0B900A97204 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5C178E271B62D0B900A97204 /* Main.storyboard in Resources */,
5C178E2C1B62D0B900A97204 /* LaunchScreen.xib in Resources */,
5C178E291B62D0B900A97204 /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5C178E2F1B62D0B900A97204 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
5C178E181B62D0B900A97204 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5C178E241B62D0B900A97204 /* ViewController.swift in Sources */,
5C178E221B62D0B900A97204 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5C178E2D1B62D0B900A97204 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5C178E381B62D0B900A97204 /* AutobahnTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
5C178E331B62D0B900A97204 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5C178E1B1B62D0B900A97204 /* Autobahn */;
targetProxy = 5C178E321B62D0B900A97204 /* PBXContainerItemProxy */;
};
5C178E511B62D10A00A97204 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = Starscream;
targetProxy = 5C178E501B62D10A00A97204 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
5C178E251B62D0B900A97204 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
5C178E261B62D0B900A97204 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
5C178E2A1B62D0B900A97204 /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
5C178E2B1B62D0B900A97204 /* Base */,
);
name = LaunchScreen.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
5C178E391B62D0B900A97204 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = 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",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.4;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
5C178E3A1B62D0B900A97204 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
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;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.4;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
5C178E3C1B62D0B900A97204 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = Autobahn/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
5C178E3D1B62D0B900A97204 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = Autobahn/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
5C178E3F1B62D0B900A97204 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = AutobahnTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Autobahn.app/Autobahn";
};
name = Debug;
};
5C178E401B62D0B900A97204 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = AutobahnTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Autobahn.app/Autobahn";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
5C178E171B62D0B900A97204 /* Build configuration list for PBXProject "Autobahn" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5C178E391B62D0B900A97204 /* Debug */,
5C178E3A1B62D0B900A97204 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5C178E3B1B62D0B900A97204 /* Build configuration list for PBXNativeTarget "Autobahn" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5C178E3C1B62D0B900A97204 /* Debug */,
5C178E3D1B62D0B900A97204 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5C178E3E1B62D0B900A97204 /* Build configuration list for PBXNativeTarget "AutobahnTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5C178E3F1B62D0B900A97204 /* Debug */,
5C178E401B62D0B900A97204 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 5C178E141B62D0B900A97204 /* Project object */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Autobahn.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,46 @@
//
// AppDelegate.swift
// Autobahn
//
// Created by Dalton Cherry on 7/24/15.
// Copyright (c) 2015 vluxe. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 vluxe. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Autobahn" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
</document>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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>
<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" customModule="Autobahn" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
@@ -0,0 +1,68 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
+47
View File
@@ -0,0 +1,47 @@
<?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>com.vluxe.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
@@ -0,0 +1,159 @@
//
// ViewController.swift
// Autobahn
//
// Created by Dalton Cherry on 7/24/15.
// Copyright (c) 2015 vluxe. All rights reserved.
//
import UIKit
import Starscream
class ViewController: UIViewController {
let host = "localhost:9001"
let scheme = "ws"
var socketArray = [WebSocket]()
var caseCount = 300 //starting cases
override func viewDidLoad() {
super.viewDidLoad()
getCaseCount()
//getTestInfo(1)
}
func removeSocket(s: WebSocket) {
self.socketArray = self.socketArray.filter{$0 != s}
}
func getCaseCount() {
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
}
}
s.onDisconnect = {[unowned self] (error: NSError?) in
self.getTestInfo(1)
self.removeSocket(s)
}
s.connect()
}
func getTestInfo(caseNum: Int) {
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!,
// options: NSJSONReadingOptions())
// if let dict = resp as? Dictionary<String,String> {
// let num = dict["id"]
// let summary = dict["description"]
// if let n = num, let sum = summary {
// print("running case:\(caseNum) id:\(n) summary: \(sum)")
// }
// }
// } catch {
// print("error parsing the json")
// }
}
var once = false
s.onDisconnect = {[unowned self] (error: NSError?) in
if !once {
once = true
self.runTest(caseNum)
}
self.removeSocket(s)
}
s.connect()
}
func runTest(caseNum: Int) {
let s = createSocket("runCase",caseNum)
self.socketArray.append(s)
s.onText = {(text: String) in
s.writeString(text)
}
s.onData = {(data: NSData) in
s.writeData(data)
}
var once = false
s.onDisconnect = {[unowned self] (error: NSError?) in
if !once {
once = true
print("case:\(caseNum) finished")
self.verifyTest(caseNum)
self.removeSocket(s)
}
}
s.connect()
}
func verifyTest(caseNum: Int) {
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!,
options: NSJSONReadingOptions())
if let dict = resp as? Dictionary<String,String> {
if let status = dict["behavior"] {
if status == "OK" {
print("SUCCESS: \(caseNum)")
return
}
}
print("FAILURE: \(caseNum)")
}
} catch {
print("error parsing the json")
}
}
var once = false
s.onDisconnect = {[unowned self] (error: NSError?) in
if !once {
once = true
let nextCase = caseNum+1
if nextCase <= self.caseCount {
self.getTestInfo(nextCase)
} else {
self.finishReports()
}
}
self.removeSocket(s)
}
s.connect()
}
func finishReports() {
let s = createSocket("updateReports",0)
self.socketArray.append(s)
s.onDisconnect = {[unowned self] (error: NSError?) in
print("finished all the tests!")
self.removeSocket(s)
}
s.connect()
}
func createSocket(cmd: String, _ caseNum: Int) -> WebSocket {
return WebSocket(url: NSURL(scheme: scheme,
host: host, path: buildPath(cmd,caseNum))!, protocols: [])
}
func buildPath(cmd: String, _ caseNum: Int) -> String {
return "/\(cmd)?case=\(caseNum)&agent=Starscream"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
@@ -0,0 +1,36 @@
//
// AutobahnTests.swift
// AutobahnTests
//
// Created by Dalton Cherry on 7/24/15.
// Copyright (c) 2015 vluxe. All rights reserved.
//
import UIKit
import XCTest
class AutobahnTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
XCTAssert(true, "Pass")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock() {
// Put the code you want to measure the time of here.
}
}
}
@@ -0,0 +1,24 @@
<?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>com.vluxe.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
+27
View File
@@ -0,0 +1,27 @@
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control?
#
# Pods/
# Xcode
.DS_Store
build
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
profile
*.moved-aside
DerivedData
.idea/
*.hmap
*.xccheckout
*.xcodeproj/*.xcworkspace
@@ -164,6 +164,7 @@
5C765AD3199A6DAA003D9110 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 0600;
ORGANIZATIONNAME = vluxe;
TargetAttributes = {
@@ -11,7 +11,7 @@
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>C86D95FCAEB1FEA0694B5D4AC7241D7E5F42F31D</key>
<string>https://github.com/daltoniam/starscream.git</string>
<string>https://github.com/daltoniam/Starscream</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>examples/SimpleTest/SimpleTest.xcodeproj</string>
@@ -21,7 +21,7 @@
<string>../../../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://github.com/daltoniam/starscream.git</string>
<string>https://github.com/daltoniam/Starscream</string>
<key>IDESourceControlProjectVersion</key>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
@@ -34,7 +34,7 @@
<key>IDESourceControlWCCIdentifierKey</key>
<string>C86D95FCAEB1FEA0694B5D4AC7241D7E5F42F31D</string>
<key>IDESourceControlWCCName</key>
<string>starscream</string>
<string>Starscream</string>
</dict>
</array>
</dict>
@@ -10,7 +10,7 @@ import UIKit
import Starscream
class ViewController: UIViewController, WebSocketDelegate {
var socket = WebSocket(url: NSURL(scheme: "ws", host: "localhost:8080", path: "/")!, protocols: ["chat", "superchat"])
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat", "superchat"])
override func viewDidLoad() {
super.viewDidLoad()
@@ -21,21 +21,23 @@ class ViewController: UIViewController, WebSocketDelegate {
// MARK: Websocket Delegate Methods.
func websocketDidConnect(ws: WebSocket) {
println("websocket is connected")
print("websocket is connected")
}
func websocketDidDisconnect(ws: WebSocket, error: NSError?) {
if let e = error {
println("websocket is disconnected: \(e.localizedDescription)")
print("websocket is disconnected: \(e.localizedDescription)")
} else {
print("websocket disconnected")
}
}
func websocketDidReceiveMessage(ws: WebSocket, text: String) {
println("Received text: \(text)")
print("Received text: \(text)")
}
func websocketDidReceiveData(ws: WebSocket, data: NSData) {
println("Received data: \(data.length)")
print("Received data: \(data.length)")
}
// MARK: Write Text Action