Compare commits
210 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 44ce58956f | |||
| c7cc1db0e3 | |||
| a438ea1977 | |||
| b5991eda48 | |||
| 0a6f6d2a79 | |||
| 3fdf33a078 | |||
| 9f5f0f44f9 | |||
| da7949d2da | |||
| dc48916804 | |||
| e0e8271725 | |||
| 411820d836 | |||
| d326d448b0 | |||
| 7dd90900dc | |||
| f506be54ae | |||
| 143ab16c1a | |||
| 4d281416c2 | |||
| e9d10000ec | |||
| 789264eeff | |||
| 644e8f92c0 | |||
| 68b64ede4e | |||
| 31277a418c | |||
| 21678c9426 | |||
| acbf665b3d | |||
| e99deb9af8 | |||
| 6dc9cdffbc | |||
| ef4ed1b2f9 | |||
| 3401fb25f4 | |||
| daa97005e6 | |||
| 008302326b | |||
| d7c41e513f | |||
| 92816513c3 | |||
| 6c9f76e7ea | |||
| 4d6dce3d64 | |||
| b0ed1decce | |||
| 86310e7bbc | |||
| 7edfb2ad85 | |||
| 1efe3ce798 | |||
| 624a5cc6c3 | |||
| 09b9136ef0 | |||
| 5434be279c | |||
| 08edd54a40 | |||
| 8623fe8b9e | |||
| fa85b9c61c | |||
| 6e9a33a854 | |||
| b5e68fceae | |||
| 601ef74ca4 | |||
| 7e517c246b | |||
| bb11123729 | |||
| 7bf478ce94 | |||
| bce64dabfa | |||
| 3104c289e9 | |||
| f35266534a | |||
| 44fdfa3791 | |||
| 35fc161193 | |||
| 45546818fb | |||
| 19793796bb | |||
| 4ba077ee4b | |||
| ca85642c21 | |||
| ccc9c4ea43 | |||
| 02718e7a45 | |||
| 961fd34f3d | |||
| 87dbce07ea | |||
| ee993322c1 | |||
| 85070aab91 | |||
| 07351dcb77 | |||
| dbeb1190b8 | |||
| 13859364e3 | |||
| 41c0c5e08b | |||
| 60b27a4388 | |||
| a68bf7c35b | |||
| 33a4551f3b | |||
| b22fae407e | |||
| e9160df255 | |||
| c63e173021 | |||
| ddcc30d200 | |||
| 6bacb6f972 | |||
| aca2ad5332 | |||
| 3ba1963c65 | |||
| 3cc4a60e05 | |||
| 14313df360 | |||
| 162c1d53b8 | |||
| b404f98244 | |||
| 8d0c0c1afa | |||
| 816fe413a4 | |||
| 5cc0d860ba | |||
| 4d2f8dbfee | |||
| 126c021e61 | |||
| c13584b99c | |||
| 178c3e8a49 | |||
| ccc1b703dd | |||
| 1b64f78542 | |||
| 549500a503 | |||
| 22d57dca07 | |||
| dd5119cfce | |||
| a6b2e4329b | |||
| 931e3ba8ae | |||
| 7476f5196e | |||
| 20285cce97 | |||
| 8e1de1d475 | |||
| acf64adc24 | |||
| b0fa08cde4 | |||
| 9d32864ab4 | |||
| 7ef2bc41e0 | |||
| 24e32a78e3 | |||
| d831901ff4 | |||
| 26b03ddaef | |||
| d94c3ff3b4 | |||
| 60d853742c | |||
| 8563f6b85e | |||
| c889a6c977 | |||
| 8f3761215b | |||
| cdf8caeb1a | |||
| 00d8fae6cd | |||
| 46db9a75f0 | |||
| 5a3adc7756 | |||
| b1472f2a99 | |||
| 34ed785564 | |||
| 08d2da1221 | |||
| 5c607a80ce | |||
| 3aac891e83 | |||
| 32fb0aa87e | |||
| 473a1382ac | |||
| 9c210e006e | |||
| c767cb2a54 | |||
| cdd1224a71 | |||
| 7c09aa08b4 | |||
| c2782b5452 | |||
| 14bc9ad90f | |||
| bdab51f4af | |||
| 26297ed3c1 | |||
| 600815b240 | |||
| 5fba5b83b4 | |||
| c39043cc29 | |||
| dd6bfa24b7 | |||
| 64661bc6ce | |||
| 0f03fab95d | |||
| c7b52b4485 | |||
| fc35de78a5 | |||
| d911dc1adf | |||
| f5f777c507 | |||
| cca4af6112 | |||
| afad19ca84 | |||
| b159d03640 | |||
| d5aeefd853 | |||
| f0f2ceee20 | |||
| 10cfb16197 | |||
| bf0146db26 | |||
| cf6def9453 | |||
| 8db1a45c6c | |||
| 592da86d06 | |||
| 43ade49abe | |||
| e32674a1ea | |||
| 7e49eb2458 | |||
| 53c19d2388 | |||
| ab02d55700 | |||
| fa0865fcac | |||
| 5d185b41cb | |||
| ba1d7db873 | |||
| e2eea95ea5 | |||
| 363e331a84 | |||
| 75721192c9 | |||
| 0c4a99588f | |||
| 4387f497c9 | |||
| d87b422950 | |||
| c5569dff12 | |||
| 6b303eafec | |||
| 0f8dd95f70 | |||
| ede7c9aa89 | |||
| afefdff061 | |||
| 3a59333bac | |||
| 1951d74516 | |||
| 6c34076224 | |||
| f189e7c51f | |||
| 95c91f9dbf | |||
| 398d9c15bf | |||
| 5a85e4e524 | |||
| d6f2c2c409 | |||
| ad91b8f1f3 | |||
| c59125f34a | |||
| ce9062ab81 | |||
| ff64f0b349 | |||
| 65dbee5c94 | |||
| 7927195b05 | |||
| d32dcb2aaa | |||
| aeae316209 | |||
| 4f2201d6b7 | |||
| ec313a376e | |||
| d5f690123f | |||
| b00a84716c | |||
| aa11fd3bbe | |||
| b6dddff56c | |||
| ac4f5ced1e | |||
| 947601b673 | |||
| cea6ce13c8 | |||
| 09c032a138 | |||
| 3fe7363ee9 | |||
| 32925e3847 | |||
| 6ad98fb458 | |||
| f02840355d | |||
| 4bece17d89 | |||
| 3582bab0b8 | |||
| 737fca6e73 | |||
| e9446a57cd | |||
| b6054567ee | |||
| 2b8d6214e2 | |||
| c56e0104c3 | |||
| 2bbc0895d6 | |||
| f1acef9a5f | |||
| b29059a937 | |||
| e1bbc7612e |
@@ -0,0 +1 @@
|
||||
4.0
|
||||
+141
-1
@@ -2,6 +2,146 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
`Starscream` adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
#### [3.0.1](https://github.com/daltoniam/Starscream/tree/3.0.2)
|
||||
|
||||
Small fixes for 3.0.1.
|
||||
|
||||
[#394](https://github.com/daltoniam/Starscream/issues/394)
|
||||
[#392](https://github.com/daltoniam/Starscream/issues/392)
|
||||
[#391](https://github.com/daltoniam/Starscream/issues/391)
|
||||
|
||||
#### [3.0.1](https://github.com/daltoniam/Starscream/tree/3.0.1)
|
||||
|
||||
Small fixes for 3.0.0.
|
||||
|
||||
[#389](https://github.com/daltoniam/Starscream/issues/389)
|
||||
[#354](https://github.com/daltoniam/Starscream/issues/354)
|
||||
[#386](https://github.com/daltoniam/Starscream/pull/386)
|
||||
[#388](https://github.com/daltoniam/Starscream/pull/388)
|
||||
[#390](https://github.com/daltoniam/Starscream/pull/390)
|
||||
|
||||
#### [3.0.0](https://github.com/daltoniam/Starscream/tree/3.0.0)
|
||||
|
||||
Major refactor and Swift 4 support. Additions include:
|
||||
|
||||
- Watchos support.
|
||||
- Linux support.
|
||||
- New Stream class to allow custom socket implementations if desired.
|
||||
- Protocol added for mocking (dependency injection).
|
||||
- Single framework (no more platform suffixes! e.g. StarscreamOSX, StarscreamTVOS, etc).
|
||||
|
||||
[#384](https://github.com/daltoniam/Starscream/issues/384)
|
||||
[#377](https://github.com/daltoniam/Starscream/pull/377)
|
||||
[#374](https://github.com/daltoniam/Starscream/issues/374)
|
||||
[#346](https://github.com/daltoniam/Starscream/issues/346)
|
||||
[#335](https://github.com/daltoniam/Starscream/issues/335)
|
||||
[#311](https://github.com/daltoniam/Starscream/pull/311)
|
||||
[#269](https://github.com/daltoniam/Starscream/issues/269)
|
||||
|
||||
#### [2.1.1](https://github.com/daltoniam/Starscream/tree/2.1.1)
|
||||
|
||||
Fixes race condition. Updated to avoid SPM dependencies.
|
||||
|
||||
[#370](https://github.com/daltoniam/Starscream/issues/370)
|
||||
[#367](https://github.com/daltoniam/Starscream/issues/367)
|
||||
[#364](https://github.com/daltoniam/Starscream/pull/364)
|
||||
[#357](https://github.com/daltoniam/Starscream/pull/357)
|
||||
[#355](https://github.com/daltoniam/Starscream/pull/355)
|
||||
|
||||
#### [2.1.0](https://github.com/daltoniam/Starscream/tree/2.1.0)
|
||||
|
||||
Adds WebSocket compression. Also adds advance WebSocket delegate for extra control. Bug Fixes.
|
||||
|
||||
[#349](https://github.com/daltoniam/Starscream/pull/349)
|
||||
[#344](https://github.com/daltoniam/Starscream/pull/344)
|
||||
[#339](https://github.com/daltoniam/Starscream/pull/339)
|
||||
[#337](https://github.com/daltoniam/Starscream/pull/337)
|
||||
[#334](https://github.com/daltoniam/Starscream/issues/334)
|
||||
[#333](https://github.com/daltoniam/Starscream/pull/333)
|
||||
[#319](https://github.com/daltoniam/Starscream/issues/319)
|
||||
[#309](https://github.com/daltoniam/Starscream/issues/309)
|
||||
[#329](https://github.com/daltoniam/Starscream/issues/329)
|
||||
|
||||
#### [2.0.4](https://github.com/daltoniam/Starscream/tree/2.0.4)
|
||||
|
||||
SSL Pinning fix by Giuliano Galea as reported by Lukas Futera of [Centralway](https://www.centralway.com/de/).
|
||||
Warning fixes for Swift 3.1
|
||||
|
||||
#### [2.0.3](https://github.com/daltoniam/Starscream/tree/2.0.3)
|
||||
|
||||
[#302](https://github.com/daltoniam/Starscream/issues/302)
|
||||
[#301](https://github.com/daltoniam/Starscream/issues/301)
|
||||
[#300](https://github.com/daltoniam/Starscream/issues/300)
|
||||
[#296](https://github.com/daltoniam/Starscream/issues/296)
|
||||
[#294](https://github.com/daltoniam/Starscream/issues/294)
|
||||
[#292](https://github.com/daltoniam/Starscream/issues/292)
|
||||
[#289](https://github.com/daltoniam/Starscream/issues/289)
|
||||
[#288](https://github.com/daltoniam/Starscream/issues/288)
|
||||
|
||||
#### [2.0.2](https://github.com/daltoniam/Starscream/tree/2.0.2)
|
||||
|
||||
Fix for the Swift Package Manager.
|
||||
|
||||
Fixed:
|
||||
[#277](https://github.com/daltoniam/Starscream/issues/277)
|
||||
|
||||
#### [2.0.1](https://github.com/daltoniam/Starscream/tree/2.0.1)
|
||||
|
||||
Bug fixes.
|
||||
|
||||
Fixed:
|
||||
[#261](https://github.com/daltoniam/Starscream/issues/261)
|
||||
[#276](https://github.com/daltoniam/Starscream/issues/276)
|
||||
[#267](https://github.com/daltoniam/Starscream/issues/267)
|
||||
[#266](https://github.com/daltoniam/Starscream/issues/266)
|
||||
[#259](https://github.com/daltoniam/Starscream/issues/259)
|
||||
|
||||
#### [2.0.0](https://github.com/daltoniam/Starscream/tree/2.0.0)
|
||||
|
||||
Added Swift 3 support.
|
||||
|
||||
Fixed:
|
||||
[#229](https://github.com/daltoniam/Starscream/issues/229)
|
||||
[#232](https://github.com/daltoniam/Starscream/issues/232)
|
||||
|
||||
#### [1.1.4](https://github.com/daltoniam/Starscream/tree/1.1.4)
|
||||
|
||||
Swift 2.3 support.
|
||||
|
||||
#### [1.1.3](https://github.com/daltoniam/Starscream/tree/1.1.3)
|
||||
|
||||
Changed:
|
||||
[#170](https://github.com/daltoniam/Starscream/issues/170)
|
||||
[#171](https://github.com/daltoniam/Starscream/issues/171)
|
||||
[#174](https://github.com/daltoniam/Starscream/issues/174)
|
||||
[#177](https://github.com/daltoniam/Starscream/issues/177)
|
||||
[#178](https://github.com/daltoniam/Starscream/issues/178)
|
||||
|
||||
#### [1.1.2](https://github.com/daltoniam/Starscream/tree/1.1.2)
|
||||
|
||||
Fixed:
|
||||
[#158](https://github.com/daltoniam/Starscream/issues/158)
|
||||
[#161](https://github.com/daltoniam/Starscream/issues/161)
|
||||
[#164](https://github.com/daltoniam/Starscream/issues/164)
|
||||
|
||||
#### [1.1.1](https://github.com/daltoniam/Starscream/tree/1.1.1)
|
||||
|
||||
Fixed:
|
||||
[#157](https://github.com/daltoniam/Starscream/issues/157)
|
||||
|
||||
#### [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)
|
||||
|
||||
@@ -13,4 +153,4 @@ Fixes for #121, #123
|
||||
|
||||
#### [1.0.0](https://github.com/daltoniam/Starscream/tree/1.0.0)
|
||||
|
||||
first release of Swift 2 support.
|
||||
first release of Swift 2 support.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
github "daltoniam/zlib-spm" ~> 1.1
|
||||
github "daltoniam/common-crypto-spm" ~> 1.1
|
||||
@@ -0,0 +1,2 @@
|
||||
github "daltoniam/zlib-spm" "1.1"
|
||||
github "daltoniam/common-crypto-spm" "1.1"
|
||||
@@ -1,7 +1,9 @@
|
||||
Apache License
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright (c) 2014-2016 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.
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Package.Swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 5/16/15.
|
||||
// Copyright (c) 2014-2016 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",
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/daltoniam/zlib-spm.git",
|
||||
majorVersion: 1, minor: 1),
|
||||
.Package(url: "https://github.com/daltoniam/common-crypto-spm",
|
||||
majorVersion: 1, minor: 1),
|
||||
],
|
||||
exclude: ["Tests", "examples"]
|
||||
)
|
||||
@@ -1,17 +1,15 @@
|
||||

|
||||
|
||||
Starscream is a conforming WebSocket ([RFC 6455](http://tools.ietf.org/html/rfc6455)) client library in Swift for iOS and OSX.
|
||||
Starscream is a conforming WebSocket ([RFC 6455](http://tools.ietf.org/html/rfc6455)) client library in Swift.
|
||||
|
||||
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).
|
||||
Its Objective-C counterpart can be found here: [Jetfire](https://github.com/acmacalister/jetfire)
|
||||
|
||||
## Features
|
||||
|
||||
- Conforms to all of the base [Autobahn test suite](http://autobahn.ws/testsuite/).
|
||||
- Nonblocking. Everything happens in the background, thanks to GCD.
|
||||
- Simple delegate pattern design.
|
||||
- TLS/WSS support.
|
||||
- Compression Extensions support ([RFC 7692](https://tools.ietf.org/html/rfc7692))
|
||||
- Simple concise codebase at just a few hundred LOC.
|
||||
|
||||
## Example
|
||||
@@ -22,10 +20,10 @@ First thing is to import the framework. See the Installation instructions on how
|
||||
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.
|
||||
Once imported, you can open a connection to your WebSocket server. Note that `socket` is probably best as a property, so it doesn't get deallocated right after being setup.
|
||||
|
||||
```swift
|
||||
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!)
|
||||
socket = WebSocket(url: URL(string: "ws://localhost:8080/")!)
|
||||
socket.delegate = self
|
||||
socket.connect()
|
||||
```
|
||||
@@ -37,8 +35,8 @@ After you are connected, there are some delegate methods that we need to impleme
|
||||
websocketDidConnect is called as soon as the client connects to the server.
|
||||
|
||||
```swift
|
||||
func websocketDidConnect(socket: WebSocket) {
|
||||
println("websocket is connected")
|
||||
func websocketDidConnect(socket: WebSocketClient) {
|
||||
print("websocket is connected")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -47,8 +45,8 @@ func websocketDidConnect(socket: WebSocket) {
|
||||
websocketDidDisconnect is called as soon as the client is disconnected from the server.
|
||||
|
||||
```swift
|
||||
func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
|
||||
println("websocket is disconnected: \(error?.localizedDescription)")
|
||||
func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
|
||||
print("websocket is disconnected: \(error?.localizedDescription)")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -57,8 +55,8 @@ func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
|
||||
websocketDidReceiveMessage is called when the client gets a text frame from the connection.
|
||||
|
||||
```swift
|
||||
func websocketDidReceiveMessage(socket: WebSocket, text: String) {
|
||||
println("got some text: \(text)")
|
||||
func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
|
||||
print("got some text: \(text)")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -67,8 +65,8 @@ func websocketDidReceiveMessage(socket: WebSocket, text: String) {
|
||||
websocketDidReceiveData is called when the client gets a binary frame from the connection.
|
||||
|
||||
```swift
|
||||
func websocketDidReceiveData(socket: WebSocket, data: NSData) {
|
||||
println("got some data: \(data.length)")
|
||||
func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
|
||||
print("got some data: \(data.count)")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -77,60 +75,62 @@ func websocketDidReceiveData(socket: WebSocket, data: NSData) {
|
||||
websocketDidReceivePong is called when the client gets a pong response from the connection. You need to implement the WebSocketPongDelegate protocol and set an additional delegate, eg: ` socket.pongDelegate = self`
|
||||
|
||||
```swift
|
||||
func websocketDidReceivePong(socket: WebSocket) {
|
||||
println("Got pong!")
|
||||
func websocketDidReceivePong(socket: WebSocketClient, data: Data?) {
|
||||
print("Got pong! Maybe some data: \(data?.count)")
|
||||
}
|
||||
```
|
||||
|
||||
Or you can use closures.
|
||||
|
||||
```swift
|
||||
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!)
|
||||
socket = WebSocket(url: URL(string: "ws://localhost:8080/")!)
|
||||
//websocketDidConnect
|
||||
socket.onConnect = {
|
||||
println("websocket is connected")
|
||||
print("websocket is connected")
|
||||
}
|
||||
//websocketDidDisconnect
|
||||
socket.onDisconnect = { (error: NSError?) in
|
||||
println("websocket is disconnected: \(error?.localizedDescription)")
|
||||
socket.onDisconnect = { (error: Error?) in
|
||||
print("websocket is disconnected: \(error?.localizedDescription)")
|
||||
}
|
||||
//websocketDidReceiveMessage
|
||||
socket.onText = { (text: String) in
|
||||
println("got some text: \(text)")
|
||||
print("got some text: \(text)")
|
||||
}
|
||||
//websocketDidReceiveData
|
||||
socket.onData = { (data: NSData) in
|
||||
println("got some data: \(data.length)")
|
||||
socket.onData = { (data: Data) in
|
||||
print("got some data: \(data.count)")
|
||||
}
|
||||
//you could do onPong as well.
|
||||
socket.connect()
|
||||
```
|
||||
|
||||
One more: you can listen to socket connection and disconnection via notifications. Starscream posts `WebsocketDidConnectNotification` and `WebsocketDidDisconnectNotification`. You can find an `Error` that caused the disconection by accessing `WebsocketDisconnectionErrorKeyName` on notification `userInfo`.
|
||||
|
||||
|
||||
## The delegate methods give you a simple way to handle data from the server, but how do you send data?
|
||||
|
||||
### writeData
|
||||
### write a binary frame
|
||||
|
||||
The writeData method gives you a simple way to send `NSData` (binary) data to the server.
|
||||
The writeData method gives you a simple way to send `Data` (binary) data to the server.
|
||||
|
||||
```swift
|
||||
self.socket.writeData(data) //write some NSData over the socket!
|
||||
socket.write(data: data) //write some Data over the socket!
|
||||
```
|
||||
|
||||
### writeString
|
||||
### write a string frame
|
||||
|
||||
The writeString method is the same as writeData, but sends text/string.
|
||||
|
||||
```swift
|
||||
self.socket.writeString("Hi Server!") //example on how to write text over the socket!
|
||||
socket.write(string: "Hi Server!") //example on how to write text over the socket!
|
||||
```
|
||||
|
||||
### writePing
|
||||
### write a ping frame
|
||||
|
||||
The writePing method is the same as writeData, but sends a ping control frame.
|
||||
The writePing method is the same as write, but sends a ping control frame.
|
||||
|
||||
```swift
|
||||
self.socket.writePing(NSData()) //example on how to write a ping control frame over the socket!
|
||||
socket.write(ping: Data()) //example on how to write a ping control frame over the socket!
|
||||
```
|
||||
|
||||
### disconnect
|
||||
@@ -138,7 +138,7 @@ self.socket.writePing(NSData()) //example on how to write a ping control frame o
|
||||
The disconnect method does what you would expect and closes the socket.
|
||||
|
||||
```swift
|
||||
self.socket.disconnect()
|
||||
socket.disconnect()
|
||||
```
|
||||
|
||||
### isConnected
|
||||
@@ -146,7 +146,7 @@ self.socket.disconnect()
|
||||
Returns if the socket is connected or not.
|
||||
|
||||
```swift
|
||||
if self.socket.isConnected {
|
||||
if socket.isConnected {
|
||||
// do cool stuff.
|
||||
}
|
||||
```
|
||||
@@ -156,9 +156,23 @@ if self.socket.isConnected {
|
||||
You can also override the default websocket headers with your own custom ones like so:
|
||||
|
||||
```swift
|
||||
socket.headers["Sec-WebSocket-Protocol"] = "someother protocols"
|
||||
socket.headers["Sec-WebSocket-Version"] = "14"
|
||||
socket.headers["My-Awesome-Header"] = "Everything is Awesome!"
|
||||
var request = URLRequest(url: URL(string: "ws://localhost:8080/")!)
|
||||
request.timeoutInterval = 5
|
||||
request.setValue("someother protocols", forHTTPHeaderField: "Sec-WebSocket-Protocol")
|
||||
request.setValue("14", forHTTPHeaderField: "Sec-WebSocket-Version")
|
||||
request.setValue("Everything is Awesome!", forHTTPHeaderField: "My-Awesome-Header")
|
||||
let socket = WebSocket(request: request)
|
||||
```
|
||||
|
||||
### Custom HTTP Method
|
||||
|
||||
Your server may use a different HTTP method when connecting to the websocket:
|
||||
|
||||
```swift
|
||||
var request = URLRequest(url: URL(string: "ws://localhost:8080/")!)
|
||||
request.httpMethod = "POST"
|
||||
request.timeoutInterval = 5
|
||||
let socket = WebSocket(request: request)
|
||||
```
|
||||
|
||||
### Protocols
|
||||
@@ -167,45 +181,64 @@ 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(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
|
||||
socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
|
||||
socket.delegate = self
|
||||
socket.connect()
|
||||
```
|
||||
|
||||
### Self Signed SSL and VOIP
|
||||
|
||||
There are a couple of other properties that modify the stream:
|
||||
### Self Signed SSL
|
||||
|
||||
```swift
|
||||
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
|
||||
socket = WebSocket(url: URL(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
|
||||
|
||||
//set this you want to ignore SSL cert validation, so a self signed SSL certificate can be used.
|
||||
socket.selfSignedSSL = true
|
||||
//set this if you want to ignore SSL cert validation, so a self signed SSL certificate can be used.
|
||||
socket.disableSSLCertValidation = true
|
||||
```
|
||||
|
||||
### SSL Pinning
|
||||
|
||||
SSL Pinning is also supported in Starscream.
|
||||
SSL Pinning is also supported in Starscream.
|
||||
|
||||
```swift
|
||||
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
|
||||
socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
|
||||
let data = ... //load your certificate from disk
|
||||
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.
|
||||
You load either a `Data` 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.
|
||||
|
||||
### SSL Cipher Suites
|
||||
|
||||
To use an SSL encrypted connection, you need to tell Starscream about the cipher suites your server supports.
|
||||
|
||||
```swift
|
||||
socket = WebSocket(url: URL(string: "wss://localhost:8080/")!, protocols: ["chat","superchat"])
|
||||
|
||||
// Set enabled cipher suites to AES 256 and AES 128
|
||||
socket.enabledSSLCipherSuites = [TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
|
||||
```
|
||||
|
||||
If you don't know which cipher suites are supported by your server, you can try pointing [SSL Labs](https://www.ssllabs.com/ssltest/) at it and checking the results.
|
||||
|
||||
### Compression Extensions
|
||||
|
||||
Compression Extensions ([RFC 7692](https://tools.ietf.org/html/rfc7692)) is supported in Starscream. Compression is enabled by default, however compression will only be used if it is supported by the server as well. You may enable or disable compression via the `.enableCompression` property:
|
||||
|
||||
```swift
|
||||
socket = WebSocket(url: URL(string: "ws://localhost:8080/")!)
|
||||
socket.enableCompression = false
|
||||
```
|
||||
|
||||
Compression should be disabled if your application is transmitting already-compressed, random, or other uncompressable data.
|
||||
|
||||
### Custom Queue
|
||||
|
||||
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.
|
||||
A custom queue can be specified when delegate methods are called. By default `DispatchQueue.main` 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(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
|
||||
socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
|
||||
//create a custom queue
|
||||
socket.queue = dispatch_queue_create("com.vluxe.starscream.myapp", nil)
|
||||
socket.callbackQueue = DispatchQueue(label: "com.vluxe.starscream.myapp")
|
||||
```
|
||||
|
||||
## Example Project
|
||||
@@ -214,21 +247,21 @@ Check out the SimpleTest project in the examples directory to see how to setup a
|
||||
|
||||
## Requirements
|
||||
|
||||
Starscream works with iOS 7/OSX 10.9 or above. It is recommended to use iOS 8/10.10 or above for Cocoapods/framework support. To use Starscream with a project targeting iOS 7, you must include all Swift files directly in your project.
|
||||
Starscream works with iOS 7/OSX 10.9 or above. It is recommended to use iOS 8/10.10 or above for CocoaPods/framework support. To use Starscream with a project targeting iOS 7, you must include all Swift files directly in your project.
|
||||
|
||||
## Installation
|
||||
|
||||
### Cocoapods
|
||||
### CocoaPods
|
||||
|
||||
Check out [Get Started](http://cocoapods.org/) tab on [cocoapods.org](http://cocoapods.org/).
|
||||
|
||||
To use Starscream in your project add the following 'Podfile' to your project
|
||||
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
platform :ios, '8.0'
|
||||
platform :ios, '9.0'
|
||||
use_frameworks!
|
||||
|
||||
pod 'Starscream', '~> 1.0.0'
|
||||
pod 'Starscream', '~> 3.0.2'
|
||||
|
||||
Then run:
|
||||
|
||||
@@ -250,7 +283,7 @@ $ brew install carthage
|
||||
To integrate Starscream into your Xcode project using Carthage, specify it in your `Cartfile`:
|
||||
|
||||
```
|
||||
github "daltoniam/Starscream" >= 1.0.0
|
||||
github "daltoniam/Starscream" >= 3.0.2
|
||||
```
|
||||
|
||||
### Rogue
|
||||
@@ -260,11 +293,23 @@ First see the [installation docs](https://github.com/acmacalister/Rogue) for how
|
||||
To install Starscream run the command below in the directory you created the rogue file.
|
||||
|
||||
```
|
||||
rogue add https://github.com/daltoniam/starscream
|
||||
rogue add https://github.com/daltoniam/Starscream
|
||||
```
|
||||
|
||||
Next open the `libs` folder and add the `Starscream.xcodeproj` to your Xcode project. Once that is complete, in your "Build Phases" add the `Starscream.framework` to your "Link Binary with Libraries" phase. Make sure to add the `libs` folder to your `.gitignore` file.
|
||||
|
||||
### Swift Package Manager
|
||||
|
||||
The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler.
|
||||
|
||||
Once you have your Swift package set up, adding Starscream as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/daltoniam/Starscream.git", majorVersion: 3)
|
||||
]
|
||||
```
|
||||
|
||||
### Other
|
||||
|
||||
Simply grab the framework (either via git submodule or another package manager).
|
||||
@@ -275,10 +320,54 @@ Add the `Starscream.xcodeproj` to your Xcode project. Once that is complete, in
|
||||
|
||||
If you are running this in an OSX app or on a physical iOS device you will need to make sure you add the `Starscream.framework` to be included in your app bundle. To do this, in Xcode, navigate to the target configuration window by clicking on the blue project icon, and selecting the application target under the "Targets" heading in the sidebar. In the tab bar at the top of that window, open the "Build Phases" panel. Expand the "Link Binary with Libraries" group, and add `Starscream.framework`. Click on the + button at the top left of the panel and select "New Copy Files Phase". Rename this new phase to "Copy Frameworks", set the "Destination" to "Frameworks", and add `Starscream.framework` respectively.
|
||||
|
||||
|
||||
## WebSocketAdvancedDelegate
|
||||
The advanced delegate acts just like the simpler delegate but provides some additional information on the connection and incoming frames.
|
||||
|
||||
```swift
|
||||
socket.advancedDelegate = self
|
||||
```
|
||||
|
||||
In most cases you do not need the extra info and should use the normal delegate.
|
||||
|
||||
#### websocketDidReceiveMessage
|
||||
```swift
|
||||
func websocketDidReceiveMessage(socket: WebSocketClient, text: String, response: WebSocket.WSResponse {
|
||||
print("got some text: \(text)")
|
||||
print("First frame for this message arrived on \(response.firstFrame)")
|
||||
}
|
||||
```
|
||||
|
||||
#### websocketDidReceiveData
|
||||
```swift
|
||||
func websocketDidReceiveData(socket: WebSocketClient, data: Date, response: WebSocket.WSResponse) {
|
||||
print("got some data it long: \(data.count)")
|
||||
print("A total of \(response.frameCount) frames were used to send this data")
|
||||
}
|
||||
```
|
||||
|
||||
#### websocketHttpUpgrade
|
||||
These methods are called when the HTTP upgrade request is sent and when the response returns.
|
||||
```swift
|
||||
func websocketHttpUpgrade(socket: WebSocketClient, request: CFHTTPMessage) {
|
||||
print("the http request was sent we can check the raw http if we need to")
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
func websocketHttpUpgrade(socket: WebSocketClient, response: CFHTTPMessage) {
|
||||
print("the http response has returned.")
|
||||
}
|
||||
```
|
||||
|
||||
## KNOWN ISSUES
|
||||
- WatchOS does not have the the CFNetwork String constants to modify the stream's SSL behavior. It will be the default Foundation SSL behavior. This means watchOS CANNOT use `SSLCiphers`, `disableSSLCertValidation`, or SSL pinning. All these values set on watchOS will do nothing.
|
||||
- Linux does not have the security framework, so it CANNOT use SSL pinning or `SSLCiphers` either.
|
||||
|
||||
|
||||
## TODOs
|
||||
|
||||
- [ ] WatchOS
|
||||
- [ ] Add Unit Tests
|
||||
- [ ] Add Unit Tests - Local WebSocket server that runs against Autobahn
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Compression.swift
|
||||
//
|
||||
// Created by Joseph Ross on 7/16/14.
|
||||
// Copyright © 2017 Joseph Ross.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Compression implementation is implemented in conformance with RFC 7692 Compression Extensions
|
||||
// for WebSocket: https://tools.ietf.org/html/rfc7692
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
import SSCZLib
|
||||
|
||||
class Decompressor {
|
||||
private var strm = z_stream()
|
||||
private var buffer = [UInt8](repeating: 0, count: 0x2000)
|
||||
private var inflateInitialized = false
|
||||
private let windowBits:Int
|
||||
|
||||
init?(windowBits:Int) {
|
||||
self.windowBits = windowBits
|
||||
guard initInflate() else { return nil }
|
||||
}
|
||||
|
||||
private func initInflate() -> Bool {
|
||||
if Z_OK == inflateInit2_(&strm, -CInt(windowBits),
|
||||
ZLIB_VERSION, CInt(MemoryLayout<z_stream>.size))
|
||||
{
|
||||
inflateInitialized = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func reset() throws {
|
||||
teardownInflate()
|
||||
guard initInflate() else { throw NSError() }
|
||||
}
|
||||
|
||||
func decompress(_ data: Data, finish: Bool) throws -> Data {
|
||||
return try data.withUnsafeBytes { (bytes:UnsafePointer<UInt8>) -> Data in
|
||||
return try decompress(bytes: bytes, count: data.count, finish: finish)
|
||||
}
|
||||
}
|
||||
|
||||
func decompress(bytes: UnsafePointer<UInt8>, count: Int, finish: Bool) throws -> Data {
|
||||
var decompressed = Data()
|
||||
try decompress(bytes: bytes, count: count, out: &decompressed)
|
||||
|
||||
if finish {
|
||||
let tail:[UInt8] = [0x00, 0x00, 0xFF, 0xFF]
|
||||
try decompress(bytes: tail, count: tail.count, out: &decompressed)
|
||||
}
|
||||
|
||||
return decompressed
|
||||
|
||||
}
|
||||
|
||||
private func decompress(bytes: UnsafePointer<UInt8>, count: Int, out:inout Data) throws {
|
||||
var res:CInt = 0
|
||||
strm.next_in = UnsafeMutablePointer<UInt8>(mutating: bytes)
|
||||
strm.avail_in = CUnsignedInt(count)
|
||||
|
||||
repeat {
|
||||
strm.next_out = UnsafeMutablePointer<UInt8>(&buffer)
|
||||
strm.avail_out = CUnsignedInt(buffer.count)
|
||||
|
||||
res = inflate(&strm, 0)
|
||||
|
||||
let byteCount = buffer.count - Int(strm.avail_out)
|
||||
out.append(buffer, count: byteCount)
|
||||
} while res == Z_OK && strm.avail_out == 0
|
||||
|
||||
guard (res == Z_OK && strm.avail_out > 0)
|
||||
|| (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count)
|
||||
else {
|
||||
throw NSError(domain: WebSocket.ErrorDomain, code: Int(InternalErrorCode.compressionError.rawValue), userInfo: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func teardownInflate() {
|
||||
if inflateInitialized, Z_OK == inflateEnd(&strm) {
|
||||
inflateInitialized = false
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
teardownInflate()
|
||||
}
|
||||
}
|
||||
|
||||
class Compressor {
|
||||
private var strm = z_stream()
|
||||
private var buffer = [UInt8](repeating: 0, count: 0x2000)
|
||||
private var deflateInitialized = false
|
||||
private let windowBits:Int
|
||||
|
||||
init?(windowBits: Int) {
|
||||
self.windowBits = windowBits
|
||||
guard initDeflate() else { return nil }
|
||||
}
|
||||
|
||||
private func initDeflate() -> Bool {
|
||||
if Z_OK == deflateInit2_(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
||||
-CInt(windowBits), 8, Z_DEFAULT_STRATEGY,
|
||||
ZLIB_VERSION, CInt(MemoryLayout<z_stream>.size))
|
||||
{
|
||||
deflateInitialized = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func reset() throws {
|
||||
teardownDeflate()
|
||||
guard initDeflate() else { throw NSError() }
|
||||
}
|
||||
|
||||
func compress(_ data: Data) throws -> Data {
|
||||
var compressed = Data()
|
||||
var res:CInt = 0
|
||||
data.withUnsafeBytes { (ptr:UnsafePointer<UInt8>) -> Void in
|
||||
strm.next_in = UnsafeMutablePointer<UInt8>(mutating: ptr)
|
||||
strm.avail_in = CUnsignedInt(data.count)
|
||||
|
||||
repeat {
|
||||
strm.next_out = UnsafeMutablePointer<UInt8>(&buffer)
|
||||
strm.avail_out = CUnsignedInt(buffer.count)
|
||||
|
||||
res = deflate(&strm, Z_SYNC_FLUSH)
|
||||
|
||||
let byteCount = buffer.count - Int(strm.avail_out)
|
||||
compressed.append(buffer, count: byteCount)
|
||||
}
|
||||
while res == Z_OK && strm.avail_out == 0
|
||||
|
||||
}
|
||||
|
||||
guard res == Z_OK && strm.avail_out > 0
|
||||
|| (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count)
|
||||
else {
|
||||
throw NSError(domain: WebSocket.ErrorDomain, code: Int(InternalErrorCode.compressionError.rawValue), userInfo: nil)
|
||||
}
|
||||
|
||||
compressed.removeLast(4)
|
||||
return compressed
|
||||
}
|
||||
|
||||
private func teardownDeflate() {
|
||||
if deflateInitialized, Z_OK == deflateEnd(&strm) {
|
||||
deflateInitialized = false
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
teardownDeflate()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>3.0.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
@@ -4,16 +4,33 @@
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 5/16/15.
|
||||
// Copyright (c) 2015 Vluxe. All rights reserved.
|
||||
// Copyright (c) 2014-2016 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.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if os(Linux)
|
||||
#else
|
||||
import Foundation
|
||||
import Security
|
||||
|
||||
public class SSLCert {
|
||||
var certData: NSData?
|
||||
var key: SecKeyRef?
|
||||
public protocol SSLTrustValidator {
|
||||
func isValid(_ trust: SecTrust, domain: String?) -> Bool
|
||||
}
|
||||
|
||||
open class SSLCert {
|
||||
var certData: Data?
|
||||
var key: SecKey?
|
||||
|
||||
/**
|
||||
Designated init for certificates
|
||||
@@ -22,7 +39,7 @@ public class SSLCert {
|
||||
|
||||
- returns: a representation security object to be used with
|
||||
*/
|
||||
public init(data: NSData) {
|
||||
public init(data: Data) {
|
||||
self.certData = data
|
||||
}
|
||||
|
||||
@@ -33,17 +50,17 @@ public class SSLCert {
|
||||
|
||||
- returns: a representation security object to be used with
|
||||
*/
|
||||
public init(key: SecKeyRef) {
|
||||
public init(key: SecKey) {
|
||||
self.key = key
|
||||
}
|
||||
}
|
||||
|
||||
public class SSLSecurity {
|
||||
open class SSLSecurity : SSLTrustValidator {
|
||||
public var validatedDN = true //should the domain name be validated?
|
||||
|
||||
var isReady = false //is the key processing done?
|
||||
var certificates: [NSData]? //the certificates
|
||||
var pubKeys: [SecKeyRef]? //the public keys
|
||||
var certificates: [Data]? //the certificates
|
||||
var pubKeys: [SecKey]? //the public keys
|
||||
var usePublicKeys = false //use public keys or certificate validation?
|
||||
|
||||
/**
|
||||
@@ -54,20 +71,23 @@ public class SSLSecurity {
|
||||
- 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))
|
||||
let paths = Bundle.main.paths(forResourcesOfType: "cer", inDirectory: ".")
|
||||
|
||||
let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in
|
||||
var certs = certs
|
||||
if let data = NSData(contentsOfFile: path) {
|
||||
certs.append(SSLCert(data: data as Data))
|
||||
}
|
||||
return certs
|
||||
}
|
||||
self.init(certs:collect, usePublicKeys: usePublicKeys)
|
||||
|
||||
self.init(certs: certs, usePublicKeys: usePublicKeys)
|
||||
}
|
||||
|
||||
/**
|
||||
Designated init
|
||||
|
||||
- parameter keys: is the certificates or public keys to use
|
||||
- parameter certs: is the certificates or public keys to use
|
||||
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
|
||||
|
||||
- returns: a representation security object to be used with
|
||||
@@ -76,27 +96,30 @@ public class SSLSecurity {
|
||||
self.usePublicKeys = usePublicKeys
|
||||
|
||||
if self.usePublicKeys {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {
|
||||
var collect = Array<SecKeyRef>()
|
||||
for cert in certs {
|
||||
if let data = cert.certData where cert.key == nil {
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
let pubKeys = certs.reduce([SecKey]()) { (pubKeys: [SecKey], cert: SSLCert) -> [SecKey] in
|
||||
var pubKeys = pubKeys
|
||||
if let data = cert.certData, cert.key == nil {
|
||||
cert.key = self.extractPublicKey(data)
|
||||
}
|
||||
if let k = cert.key {
|
||||
collect.append(k)
|
||||
if let key = cert.key {
|
||||
pubKeys.append(key)
|
||||
}
|
||||
return pubKeys
|
||||
}
|
||||
self.pubKeys = collect
|
||||
|
||||
self.pubKeys = pubKeys
|
||||
self.isReady = true
|
||||
})
|
||||
} else {
|
||||
var collect = Array<NSData>()
|
||||
for cert in certs {
|
||||
if let d = cert.certData {
|
||||
collect.append(d)
|
||||
}
|
||||
}
|
||||
self.certificates = collect
|
||||
} else {
|
||||
let certificates = certs.reduce([Data]()) { (certificates: [Data], cert: SSLCert) -> [Data] in
|
||||
var certificates = certificates
|
||||
if let data = cert.certData {
|
||||
certificates.append(data)
|
||||
}
|
||||
return certificates
|
||||
}
|
||||
self.certificates = certificates
|
||||
self.isReady = true
|
||||
}
|
||||
}
|
||||
@@ -109,26 +132,26 @@ public class SSLSecurity {
|
||||
|
||||
- returns: if the key was successfully validated
|
||||
*/
|
||||
public func isValid(trust: SecTrustRef, domain: String?) -> Bool {
|
||||
public func isValid(_ trust: SecTrust, domain: String?) -> Bool {
|
||||
|
||||
var tries = 0
|
||||
while(!self.isReady) {
|
||||
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
|
||||
var policy: SecPolicy
|
||||
if self.validatedDN {
|
||||
policy = SecPolicyCreateSSL(true, domain)
|
||||
policy = SecPolicyCreateSSL(true, domain as NSString?)
|
||||
} else {
|
||||
policy = SecPolicyCreateBasicX509()
|
||||
}
|
||||
SecTrustSetPolicies(trust,policy)
|
||||
if self.usePublicKeys {
|
||||
if let keys = self.pubKeys {
|
||||
let serverPubKeys = publicKeyChainForTrust(trust)
|
||||
let serverPubKeys = publicKeyChain(trust)
|
||||
for serverKey in serverPubKeys as [AnyObject] {
|
||||
for key in keys as [AnyObject] {
|
||||
if serverKey.isEqual(key) {
|
||||
@@ -138,21 +161,20 @@ public class SSLSecurity {
|
||||
}
|
||||
}
|
||||
} else if let certs = self.certificates {
|
||||
let serverCerts = certificateChainForTrust(trust)
|
||||
var collect = Array<SecCertificate>()
|
||||
let serverCerts = certificateChain(trust)
|
||||
var collect = [SecCertificate]()
|
||||
for cert in certs {
|
||||
collect.append(SecCertificateCreateWithData(nil,cert)!)
|
||||
collect.append(SecCertificateCreateWithData(nil,cert as CFData)!)
|
||||
}
|
||||
SecTrustSetAnchorCertificates(trust,collect)
|
||||
var result: SecTrustResultType = 0
|
||||
SecTrustSetAnchorCertificates(trust,collect as NSArray)
|
||||
var result: SecTrustResultType = .unspecified
|
||||
SecTrustEvaluate(trust,&result)
|
||||
let r = Int(result)
|
||||
if r == kSecTrustResultUnspecified || r == kSecTrustResultProceed {
|
||||
if result == .unspecified || result == .proceed {
|
||||
var trustedCount = 0
|
||||
for serverCert in serverCerts {
|
||||
for cert in certs {
|
||||
if cert == serverCert {
|
||||
trustedCount++
|
||||
trustedCount += 1
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -172,12 +194,10 @@ public class SSLSecurity {
|
||||
|
||||
- returns: a public key
|
||||
*/
|
||||
func extractPublicKey(data: NSData) -> SecKeyRef? {
|
||||
let possibleCert = SecCertificateCreateWithData(nil,data)
|
||||
if let cert = possibleCert {
|
||||
return extractPublicKeyFromCert(cert, policy: SecPolicyCreateBasicX509())
|
||||
}
|
||||
return nil
|
||||
func extractPublicKey(_ data: Data) -> SecKey? {
|
||||
guard let cert = SecCertificateCreateWithData(nil, data as CFData) else { return nil }
|
||||
|
||||
return extractPublicKey(cert, policy: SecPolicyCreateBasicX509())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,15 +207,14 @@ public class SSLSecurity {
|
||||
|
||||
- returns: a public key
|
||||
*/
|
||||
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKeyRef? {
|
||||
func extractPublicKey(_ cert: SecCertificate, policy: SecPolicy) -> SecKey? {
|
||||
var possibleTrust: SecTrust?
|
||||
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
|
||||
if let trust = possibleTrust {
|
||||
var result: SecTrustResultType = 0
|
||||
SecTrustEvaluate(trust, &result)
|
||||
return SecTrustCopyPublicKey(trust)
|
||||
}
|
||||
return nil
|
||||
|
||||
guard let trust = possibleTrust else { return nil }
|
||||
var result: SecTrustResultType = .unspecified
|
||||
SecTrustEvaluate(trust, &result)
|
||||
return SecTrustCopyPublicKey(trust)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,13 +224,15 @@ public class SSLSecurity {
|
||||
|
||||
- returns: the certificate chain for the trust
|
||||
*/
|
||||
func certificateChainForTrust(trust: SecTrustRef) -> Array<NSData> {
|
||||
var collect = Array<NSData>()
|
||||
for var i = 0; i < SecTrustGetCertificateCount(trust); i++ {
|
||||
let cert = SecTrustGetCertificateAtIndex(trust,i)
|
||||
collect.append(SecCertificateCopyData(cert!))
|
||||
func certificateChain(_ trust: SecTrust) -> [Data] {
|
||||
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([Data]()) { (certificates: [Data], index: Int) -> [Data] in
|
||||
var certificates = certificates
|
||||
let cert = SecTrustGetCertificateAtIndex(trust, index)
|
||||
certificates.append(SecCertificateCopyData(cert!) as Data)
|
||||
return certificates
|
||||
}
|
||||
return collect
|
||||
|
||||
return certificates
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,17 +242,21 @@ public class SSLSecurity {
|
||||
|
||||
- returns: the public keys from the certifcate chain for the trust
|
||||
*/
|
||||
func publicKeyChainForTrust(trust: SecTrustRef) -> Array<SecKeyRef> {
|
||||
var collect = Array<SecKeyRef>()
|
||||
func publicKeyChain(_ trust: SecTrust) -> [SecKey] {
|
||||
let policy = SecPolicyCreateBasicX509()
|
||||
for var i = 0; i < SecTrustGetCertificateCount(trust); i++ {
|
||||
let cert = SecTrustGetCertificateAtIndex(trust,i)
|
||||
if let key = extractPublicKeyFromCert(cert!, policy: policy) {
|
||||
collect.append(key)
|
||||
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKey]()) { (keys: [SecKey], index: Int) -> [SecKey] in
|
||||
var keys = keys
|
||||
let cert = SecTrustGetCertificateAtIndex(trust, index)
|
||||
if let key = extractPublicKey(cert!, policy: policy) {
|
||||
keys.append(key)
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
return collect
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
+10
-5
@@ -1,15 +1,20 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Starscream"
|
||||
s.version = "1.0.2"
|
||||
s.summary = "A conforming WebSocket RFC 6455 client library in Swift for iOS and OSX."
|
||||
s.version = "3.0.2"
|
||||
s.summary = "A conforming WebSocket RFC 6455 client library in Swift."
|
||||
s.homepage = "https://github.com/daltoniam/Starscream"
|
||||
s.license = 'Apache License, Version 2.0'
|
||||
s.author = {'Dalton Cherry' => 'http://daltoniam.com', 'Austin Cherry' => 'http://austincherry.me'}
|
||||
s.source = { :git => 'https://github.com/daltoniam/Starscream.git', :tag => "#{s.version}"}
|
||||
s.social_media_url = 'http://twitter.com/daltoniam'
|
||||
s.ios.deployment_target = '8.0'
|
||||
s.osx.deployment_target = '10.9'
|
||||
s.osx.deployment_target = '10.10'
|
||||
s.tvos.deployment_target = '9.0'
|
||||
s.source_files = '*.swift'
|
||||
s.requires_arc = 'true'
|
||||
s.watchos.deployment_target = '2.0'
|
||||
s.source_files = 'Sources/*.swift'
|
||||
s.libraries = 'z'
|
||||
s.pod_target_xcconfig = {
|
||||
'SWIFT_INCLUDE_PATHS' => '$(PODS_ROOT)/Starscream/zlib'
|
||||
}
|
||||
s.preserve_paths = 'zlib/*'
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.3">
|
||||
LastUpgradeVersion = "0900"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@@ -14,7 +14,7 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79E519D48B7F006071F7"
|
||||
BlueprintIdentifier = "33CCF0841F5DDC030099B092"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
@@ -26,9 +26,34 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "335FA1F41F5DF71D00F6D2EC"
|
||||
BuildableName = "Starscream Tests.xctest"
|
||||
BlueprintName = "Starscream Tests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
<LocationScenarioReference
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
</LocationScenarioReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CCF0841F5DDC030099B092"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
@@ -36,6 +61,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
@@ -45,7 +71,7 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79E519D48B7F006071F7"
|
||||
BlueprintIdentifier = "33CCF0841F5DDC030099B092"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
@@ -63,7 +89,7 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79E519D48B7F006071F7"
|
||||
BlueprintIdentifier = "33CCF0841F5DDC030099B092"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "Starscream"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D9C3E35E19E48FF1009FC285"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "StarscreamOSX"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D9C3E36819E48FF1009FC285"
|
||||
BuildableName = "StarscreamOSXTests.xctest"
|
||||
BlueprintName = "StarscreamOSXTests"
|
||||
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 = "D9C3E36819E48FF1009FC285"
|
||||
BuildableName = "StarscreamOSXTests.xctest"
|
||||
BlueprintName = "StarscreamOSXTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D9C3E35E19E48FF1009FC285"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "StarscreamOSX"
|
||||
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 = "D9C3E35E19E48FF1009FC285"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "StarscreamOSX"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D9C3E35E19E48FF1009FC285"
|
||||
BuildableName = "Starscream.framework"
|
||||
BlueprintName = "StarscreamOSX"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,99 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79F019D48B7F006071F7"
|
||||
BuildableName = "StarscreamTests.xctest"
|
||||
BlueprintName = "StarscreamTests"
|
||||
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 = "6B3E79F019D48B7F006071F7"
|
||||
BuildableName = "StarscreamTests.xctest"
|
||||
BlueprintName = "StarscreamTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79F019D48B7F006071F7"
|
||||
BuildableName = "StarscreamTests.xctest"
|
||||
BlueprintName = "StarscreamTests"
|
||||
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 = "6B3E79F019D48B7F006071F7"
|
||||
BuildableName = "StarscreamTests.xctest"
|
||||
BlueprintName = "StarscreamTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6B3E79F019D48B7F006071F7"
|
||||
BuildableName = "StarscreamTests.xctest"
|
||||
BlueprintName = "StarscreamTests"
|
||||
ReferencedContainer = "container:Starscream.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,113 +0,0 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,65 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CompressionTests.swift
|
||||
//
|
||||
// Created by Joseph Ross on 7/16/14.
|
||||
// Copyright © 2017 Joseph Ross.
|
||||
//
|
||||
// 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 XCTest
|
||||
|
||||
class CompressionTests: 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 testBasic() {
|
||||
let compressor = Compressor(windowBits: 15)!
|
||||
let decompressor = Decompressor(windowBits: 15)!
|
||||
|
||||
let rawData = "Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!".data(using: .utf8)!
|
||||
|
||||
let compressed = try! compressor.compress(rawData)
|
||||
let uncompressed = try! decompressor.decompress(compressed, finish: true)
|
||||
|
||||
XCTAssert(rawData == uncompressed)
|
||||
}
|
||||
|
||||
func testHugeData() {
|
||||
let compressor = Compressor(windowBits: 15)!
|
||||
let decompressor = Decompressor(windowBits: 15)!
|
||||
|
||||
// 2 Gigs!
|
||||
// var rawData = Data(repeating: 0, count: 0x80000000)
|
||||
var rawData = Data(repeating: 0, count: 0x80000)
|
||||
rawData.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer<UInt8>) -> Void in
|
||||
arc4random_buf(ptr, rawData.count)
|
||||
}
|
||||
|
||||
let compressed = try! compressor.compress(rawData)
|
||||
let uncompressed = try! decompressor.decompress(compressed, finish: true)
|
||||
|
||||
XCTAssert(rawData == uncompressed)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class StarscreamTests: XCTestCase {
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measureBlock() {
|
||||
self.measure() {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
-729
@@ -1,729 +0,0 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Websocket.swift
|
||||
//
|
||||
// Created by Dalton Cherry on 7/16/14.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
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 = 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 = Dictionary<String,String>()
|
||||
public var voipEnabled = false
|
||||
public var selfSignedSSL = false
|
||||
public var security: SSLSecurity?
|
||||
public var enabledSSLCipherSuites: [SSLCipherSuite]?
|
||||
public var isConnected :Bool {
|
||||
return connected
|
||||
}
|
||||
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?
|
||||
private var certValidated = false
|
||||
private var didDisconnect = false
|
||||
|
||||
//init the websocket with a url
|
||||
public init(url: NSURL) {
|
||||
self.url = url
|
||||
writeQueue.maxConcurrentOperationCount = 1
|
||||
}
|
||||
//used for setting protocols.
|
||||
public convenience init(url: NSURL, protocols: Array<String>) {
|
||||
self.init(url: url)
|
||||
optionalProtocols = protocols
|
||||
}
|
||||
|
||||
///Connect to the websocket server on a background thread
|
||||
public func connect() {
|
||||
if isCreated {
|
||||
return
|
||||
}
|
||||
dispatch_async(queue,{ [weak self] in
|
||||
self?.didDisconnect = false
|
||||
})
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), { [weak self] in
|
||||
self?.isCreated = true
|
||||
self?.createHTTPRequest()
|
||||
self?.isCreated = false
|
||||
})
|
||||
}
|
||||
|
||||
///disconnect from the websocket server
|
||||
public func disconnect(forceTimeout: Int = 0) {
|
||||
writeError(CloseCode.Normal.rawValue)
|
||||
if forceTimeout > 0 { //not needed most of the time, for an edge case
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(forceTimeout) * Int64(NSEC_PER_SEC)), queue, { [unowned self] in
|
||||
self.disconnectStream(nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
///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 urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET",
|
||||
url, kCFHTTPVersion1_1).takeRetainedValue()
|
||||
|
||||
var port = url.port
|
||||
if port == nil {
|
||||
if url.scheme == "wss" || url.scheme == "https" {
|
||||
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())
|
||||
addHeader(urlRequest, key: headerOriginName, val: url.absoluteString)
|
||||
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: String, val: String) {
|
||||
let nsKey: NSString = key
|
||||
let nsVal: NSString = val
|
||||
CFHTTPMessageSetHeaderFieldValue(urlRequest,
|
||||
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))"
|
||||
}
|
||||
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 url.scheme == "wss" || url.scheme == "https" {
|
||||
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: Dictionary<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
|
||||
}
|
||||
}
|
||||
}
|
||||
isRunLoop = true
|
||||
inStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
outStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
|
||||
inStream.open()
|
||||
outStream.open()
|
||||
let bytes = UnsafePointer<UInt8>(data.bytes)
|
||||
outStream.write(bytes, maxLength: data.length)
|
||||
while(isRunLoop) {
|
||||
NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture() as NSDate)
|
||||
}
|
||||
}
|
||||
//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) {
|
||||
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 {
|
||||
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
|
||||
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)
|
||||
let buffer = UnsafeMutablePointer<UInt8>(buf!.bytes)
|
||||
let length = inputStream!.read(buffer, maxLength: BUFFER_MAX)
|
||||
if length > 0 {
|
||||
if !connected {
|
||||
connected = processHTTP(buffer, bufferLen: length)
|
||||
if !connected {
|
||||
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue()
|
||||
CFHTTPMessageAppendBytes(response, buffer, length)
|
||||
let code = CFHTTPMessageGetResponseStatusCode(response)
|
||||
doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: UInt16(code)))
|
||||
}
|
||||
} else {
|
||||
var process = false
|
||||
if inputQueue.count == 0 {
|
||||
process = true
|
||||
}
|
||||
inputQueue.append(NSData(bytes: buffer, length: length))
|
||||
if process {
|
||||
dequeueInput()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
///dequeue the incoming input so it is processed in order
|
||||
private func dequeueInput() {
|
||||
if inputQueue.count > 0 {
|
||||
let data = inputQueue[0]
|
||||
var work = data
|
||||
if fragBuffer != nil {
|
||||
let combine = NSMutableData(data: fragBuffer!)
|
||||
combine.appendData(data)
|
||||
work = combine
|
||||
fragBuffer = nil
|
||||
}
|
||||
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,{ [weak self] in
|
||||
guard let s = self else { return }
|
||||
if let connectBlock = s.onConnect {
|
||||
connectBlock()
|
||||
}
|
||||
s.delegate?.websocketDidConnect(s)
|
||||
})
|
||||
totalSize += 1 //skip the last \n
|
||||
let restSize = bufferLen - totalSize
|
||||
if restSize > 0 {
|
||||
processRawMessage((buffer+totalSize),bufferLen: restSize)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
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, false).takeRetainedValue()
|
||||
CFHTTPMessageAppendBytes(response, buffer, bufferLen)
|
||||
if CFHTTPMessageGetResponseStatusCode(response) != 101 {
|
||||
return false
|
||||
}
|
||||
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
|
||||
let headers = cfHeaders.takeRetainedValue() as NSDictionary
|
||||
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) {
|
||||
let 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)
|
||||
let 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
|
||||
doDisconnect(errorWithDetail("masked and rsv data is not currently supported", code: errCode))
|
||||
writeError(errCode)
|
||||
return
|
||||
}
|
||||
let isControlFrame = (receivedOpcode == OpCode.ConnectionClose.rawValue || receivedOpcode == OpCode.Ping.rawValue)
|
||||
if !isControlFrame && (receivedOpcode != OpCode.BinaryFrame.rawValue && receivedOpcode != OpCode.ContinueFrame.rawValue &&
|
||||
receivedOpcode != OpCode.TextFrame.rawValue && receivedOpcode != OpCode.Pong.rawValue) {
|
||||
let 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 == OpCode.ConnectionClose.rawValue {
|
||||
var code = CloseCode.Normal.rawValue
|
||||
if payloadLen == 1 {
|
||||
code = CloseCode.ProtocolError.rawValue
|
||||
} else if payloadLen > 1 {
|
||||
let codeBuffer = UnsafePointer<UInt16>((buffer+offset))
|
||||
code = codeBuffer[0].bigEndian
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
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 == OpCode.Pong.rawValue {
|
||||
dispatch_async(queue,{ [weak self] in
|
||||
guard let s = self else { return }
|
||||
if let pongBlock = s.onPong {
|
||||
pongBlock()
|
||||
}
|
||||
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 == OpCode.ContinueFrame.rawValue && response == nil {
|
||||
let errCode = CloseCode.ProtocolError.rawValue
|
||||
doDisconnect(errorWithDetail("continue frame before a binary or text frame", code: errCode))
|
||||
writeError(errCode)
|
||||
return
|
||||
}
|
||||
var isNew = false
|
||||
if(response == nil) {
|
||||
if receivedOpcode == OpCode.ContinueFrame.rawValue {
|
||||
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 = 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
|
||||
doDisconnect(errorWithDetail("second and beyond of fragment message must be a continue frame",
|
||||
code: errCode))
|
||||
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 {
|
||||
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 }
|
||||
if let textBlock = s.onText {
|
||||
textBlock(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 }
|
||||
if let dataBlock = s.onData {
|
||||
dataBlock(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 = 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))
|
||||
let 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 !isConnected {
|
||||
return
|
||||
}
|
||||
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
|
||||
let sizeBuffer = UnsafeMutablePointer<UInt16>((buffer+offset))
|
||||
sizeBuffer[0] = UInt16(dataLength).bigEndian
|
||||
offset += sizeof(UInt16)
|
||||
} else {
|
||||
buffer[1] = 127
|
||||
let sizeBuffer = UnsafeMutablePointer<UInt64>((buffer+offset))
|
||||
sizeBuffer[0] = UInt64(dataLength).bigEndian
|
||||
offset += sizeof(UInt64)
|
||||
}
|
||||
buffer[1] |= s.MaskMask
|
||||
let 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 !s.isConnected {
|
||||
break
|
||||
}
|
||||
guard let outStream = s.outputStream else { break }
|
||||
let writeBuffer = UnsafePointer<UInt8>(frame!.bytes+total)
|
||||
let len = outStream.write(writeBuffer, maxLength: offset-total)
|
||||
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?) {
|
||||
if !didDisconnect {
|
||||
dispatch_async(queue,{ [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.didDisconnect = true
|
||||
if let disconnect = s.onDisconnect {
|
||||
disconnect(error)
|
||||
}
|
||||
s.delegate?.websocketDidDisconnect(s, error: error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,6 +59,20 @@
|
||||
remoteGlobalIDString = 6B3E79E519D48B7F006071F7;
|
||||
remoteInfo = Starscream;
|
||||
};
|
||||
5C42C3D51D8DF51C00947AA2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 091277971BD673A70003036D;
|
||||
remoteInfo = "Starscream tvOS";
|
||||
};
|
||||
5C42C3D71D8DF51C00947AA2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5C178E411B62D0EF00A97204 /* Starscream.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 091277A01BD673A70003036D;
|
||||
remoteInfo = "Starscream tvOSTests";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -158,6 +172,8 @@
|
||||
5C178E4B1B62D0EF00A97204 /* StarscreamTests.xctest */,
|
||||
5C178E4D1B62D0EF00A97204 /* Starscream.framework */,
|
||||
5C178E4F1B62D0EF00A97204 /* StarscreamOSXTests.xctest */,
|
||||
5C42C3D61D8DF51C00947AA2 /* Starscream.framework */,
|
||||
5C42C3D81D8DF51C00947AA2 /* Starscream tvOSTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -214,9 +230,11 @@
|
||||
TargetAttributes = {
|
||||
5C178E1B1B62D0B900A97204 = {
|
||||
CreatedOnToolsVersion = 6.4;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
5C178E301B62D0B900A97204 = {
|
||||
CreatedOnToolsVersion = 6.4;
|
||||
LastSwiftMigration = 0800;
|
||||
TestTargetID = 5C178E1B1B62D0B900A97204;
|
||||
};
|
||||
};
|
||||
@@ -257,7 +275,7 @@
|
||||
5C178E4B1B62D0EF00A97204 /* StarscreamTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = StarscreamTests.xctest;
|
||||
path = "Starscream iOSTests.xctest";
|
||||
remoteRef = 5C178E4A1B62D0EF00A97204 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
@@ -271,10 +289,24 @@
|
||||
5C178E4F1B62D0EF00A97204 /* StarscreamOSXTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = StarscreamOSXTests.xctest;
|
||||
path = "Starscream OSXTests.xctest";
|
||||
remoteRef = 5C178E4E1B62D0EF00A97204 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5C42C3D61D8DF51C00947AA2 /* Starscream.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
path = Starscream.framework;
|
||||
remoteRef = 5C42C3D51D8DF51C00947AA2 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5C42C3D81D8DF51C00947AA2 /* Starscream tvOSTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = "Starscream tvOSTests.xctest";
|
||||
remoteRef = 5C42C3D71D8DF51C00947AA2 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
/* End PBXReferenceProxy section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
@@ -440,6 +472,7 @@
|
||||
INFOPLIST_FILE = Autobahn/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -450,6 +483,7 @@
|
||||
INFOPLIST_FILE = Autobahn/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -468,6 +502,7 @@
|
||||
INFOPLIST_FILE = AutobahnTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Autobahn.app/Autobahn";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -483,6 +518,7 @@
|
||||
INFOPLIST_FILE = AutobahnTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Autobahn.app/Autobahn";
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -14,30 +14,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationWillResignActive(application: UIApplication) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="Autobahn" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
|
||||
@@ -11,9 +11,8 @@ import Starscream
|
||||
|
||||
class ViewController: UIViewController {
|
||||
|
||||
static let host = "localhost:9001"
|
||||
static let scheme = "ws"
|
||||
var socket = WebSocket(url: NSURL(scheme: scheme, host: host, path: "/getCaseCount")!, protocols: [])
|
||||
let host = "localhost:9001"
|
||||
var socketArray = [WebSocket]()
|
||||
var caseCount = 300 //starting cases
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
@@ -21,22 +20,31 @@ class ViewController: UIViewController {
|
||||
//getTestInfo(1)
|
||||
}
|
||||
|
||||
func getCaseCount() {
|
||||
socket.onText = {(text: String) in
|
||||
if let c = Int(text) {
|
||||
print("number of cases is: \(c)")
|
||||
self.caseCount = c
|
||||
}
|
||||
}
|
||||
socket.onDisconnect = {(error: NSError?) in
|
||||
self.getTestInfo(1)
|
||||
}
|
||||
socket.connect()
|
||||
func removeSocket(_ s: WebSocket?) {
|
||||
socketArray = socketArray.filter{$0 != s}
|
||||
}
|
||||
|
||||
func getTestInfo(caseNum: Int) {
|
||||
socket = createSocket("getCaseInfo",caseNum)
|
||||
socket.onText = {(text: String) in
|
||||
func getCaseCount() {
|
||||
|
||||
let s = WebSocket(url: URL(string: "ws://\(host)/getCaseCount")!, protocols: [])
|
||||
socketArray.append(s)
|
||||
s.onText = { [weak self] (text: String) in
|
||||
if let c = Int(text) {
|
||||
print("number of cases is: \(c)")
|
||||
self?.caseCount = c
|
||||
}
|
||||
}
|
||||
s.onDisconnect = { [weak self, weak s] (error: Error?) 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!,
|
||||
@@ -54,41 +62,52 @@ class ViewController: UIViewController {
|
||||
|
||||
}
|
||||
var once = false
|
||||
socket.onDisconnect = {(error: NSError?) in
|
||||
s.onDisconnect = { [weak self, weak s] (error: Error?) in
|
||||
if !once {
|
||||
once = true
|
||||
self.runTest(caseNum)
|
||||
self?.runTest(caseNum)
|
||||
}
|
||||
self?.removeSocket(s)
|
||||
}
|
||||
socket.connect()
|
||||
s.connect()
|
||||
}
|
||||
|
||||
func runTest(caseNum: Int) {
|
||||
socket = createSocket("runCase",caseNum)
|
||||
socket.onText = {(text: String) in
|
||||
self.socket.writeString(text)
|
||||
func runTest(_ caseNum: Int) {
|
||||
let s = createSocket("runCase",caseNum)
|
||||
self.socketArray.append(s)
|
||||
s.onText = { [weak s] (text: String) in
|
||||
s?.write(string: text)
|
||||
}
|
||||
socket.onData = {(data: NSData) in
|
||||
self.socket.writeData(data)
|
||||
s.onData = { [weak s] (data: Data) in
|
||||
s?.write(data: data)
|
||||
}
|
||||
var once = false
|
||||
socket.onDisconnect = {(error: NSError?) in
|
||||
s.onDisconnect = {[weak self, weak s] (error: Error?) in
|
||||
if !once {
|
||||
once = true
|
||||
print("case:\(caseNum) finished")
|
||||
self.verifyTest(caseNum)
|
||||
//self?.verifyTest(caseNum) //disabled since it slows down the tests
|
||||
let nextCase = caseNum+1
|
||||
if nextCase <= (self?.caseCount)! {
|
||||
self?.runTest(nextCase)
|
||||
//self?.getTestInfo(nextCase) //disabled since it slows down the tests
|
||||
} else {
|
||||
self?.finishReports()
|
||||
}
|
||||
self?.removeSocket(s)
|
||||
}
|
||||
}
|
||||
socket.connect()
|
||||
s.connect()
|
||||
}
|
||||
|
||||
func verifyTest(caseNum: Int) {
|
||||
socket = createSocket("getCaseStatus",caseNum)
|
||||
socket.onText = {(text: String) in
|
||||
let data = text.dataUsingEncoding(NSUTF8StringEncoding)
|
||||
func verifyTest(_ caseNum: Int) {
|
||||
let s = createSocket("getCaseStatus",caseNum)
|
||||
self.socketArray.append(s)
|
||||
s.onText = { (text: String) in
|
||||
let data = text.data(using: String.Encoding.utf8)
|
||||
do {
|
||||
let resp: AnyObject? = try NSJSONSerialization.JSONObjectWithData(data!,
|
||||
options: NSJSONReadingOptions())
|
||||
let resp: Any? = try JSONSerialization.jsonObject(with: data!,
|
||||
options: JSONSerialization.ReadingOptions())
|
||||
if let dict = resp as? Dictionary<String,String> {
|
||||
if let status = dict["behavior"] {
|
||||
if status == "OK" {
|
||||
@@ -103,34 +122,37 @@ class ViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
var once = false
|
||||
socket.onDisconnect = {(error: NSError?) in
|
||||
s.onDisconnect = { [weak self, weak s] (error: Error?) in
|
||||
if !once {
|
||||
once = true
|
||||
let nextCase = caseNum+1
|
||||
if nextCase <= self.caseCount {
|
||||
self.getTestInfo(nextCase)
|
||||
print("next test is: \(nextCase)")
|
||||
if nextCase <= (self?.caseCount)! {
|
||||
self?.getTestInfo(nextCase)
|
||||
} else {
|
||||
self.finishReports()
|
||||
self?.finishReports()
|
||||
}
|
||||
}
|
||||
self?.removeSocket(s)
|
||||
}
|
||||
socket.connect()
|
||||
s.connect()
|
||||
}
|
||||
|
||||
func finishReports() {
|
||||
socket = createSocket("updateReports",0)
|
||||
socket.onDisconnect = {(error: NSError?) in
|
||||
let s = createSocket("updateReports",0)
|
||||
self.socketArray.append(s)
|
||||
s.onDisconnect = { [weak self, weak s] (error: Error?) in
|
||||
print("finished all the tests!")
|
||||
self?.removeSocket(s)
|
||||
}
|
||||
socket.connect()
|
||||
s.connect()
|
||||
}
|
||||
|
||||
func createSocket(cmd: String, _ caseNum: Int) -> WebSocket {
|
||||
return WebSocket(url: NSURL(scheme: ViewController.scheme,
|
||||
host: ViewController.host, path: buildPath(cmd,caseNum))!, protocols: [])
|
||||
func createSocket(_ cmd: String, _ caseNum: Int) -> WebSocket {
|
||||
return WebSocket(url: URL(string: "ws://\(host)\(buildPath(cmd,caseNum))")!, protocols: [])
|
||||
}
|
||||
|
||||
func buildPath(cmd: String, _ caseNum: Int) -> String {
|
||||
func buildPath(_ cmd: String, _ caseNum: Int) -> String {
|
||||
return "/\(cmd)?case=\(caseNum)&agent=Starscream"
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class AutobahnTests: XCTestCase {
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measureBlock() {
|
||||
self.measure() {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,20 @@
|
||||
remoteGlobalIDString = D9C3E36919E48FF1009FC285;
|
||||
remoteInfo = StarscreamOSXTests;
|
||||
};
|
||||
5C42C3E01D8F31DC00947AA2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 6B3E7A0819D48D00006071F7 /* Starscream.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 091277971BD673A70003036D;
|
||||
remoteInfo = "Starscream tvOS";
|
||||
};
|
||||
5C42C3E21D8F31DC00947AA2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 6B3E7A0819D48D00006071F7 /* Starscream.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 091277A01BD673A70003036D;
|
||||
remoteInfo = "Starscream tvOSTests";
|
||||
};
|
||||
6B3E7A0D19D48D00006071F7 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 6B3E7A0819D48D00006071F7 /* Starscream.xcodeproj */;
|
||||
@@ -129,9 +143,11 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6B3E7A0E19D48D00006071F7 /* Starscream.framework */,
|
||||
6B3E7A1019D48D00006071F7 /* StarscreamTests.xctest */,
|
||||
6B3E7A1019D48D00006071F7 /* Starscream iOSTests.xctest */,
|
||||
5C06AE8B1B08044600D41060 /* Starscream.framework */,
|
||||
5C06AE8D1B08044600D41060 /* StarscreamOSXTests.xctest */,
|
||||
5C06AE8D1B08044600D41060 /* Starscream OSXTests.xctest */,
|
||||
5C42C3E11D8F31DC00947AA2 /* Starscream.framework */,
|
||||
5C42C3E31D8F31DC00947AA2 /* Starscream tvOSTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -165,11 +181,12 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0700;
|
||||
LastUpgradeCheck = 0600;
|
||||
LastUpgradeCheck = 0900;
|
||||
ORGANIZATIONNAME = vluxe;
|
||||
TargetAttributes = {
|
||||
5C765ADA199A6DAA003D9110 = {
|
||||
CreatedOnToolsVersion = 6.0;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -205,13 +222,27 @@
|
||||
remoteRef = 5C06AE8A1B08044600D41060 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5C06AE8D1B08044600D41060 /* StarscreamOSXTests.xctest */ = {
|
||||
5C06AE8D1B08044600D41060 /* Starscream OSXTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = StarscreamOSXTests.xctest;
|
||||
path = "Starscream OSXTests.xctest";
|
||||
remoteRef = 5C06AE8C1B08044600D41060 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5C42C3E11D8F31DC00947AA2 /* Starscream.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
path = Starscream.framework;
|
||||
remoteRef = 5C42C3E01D8F31DC00947AA2 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5C42C3E31D8F31DC00947AA2 /* Starscream tvOSTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = "Starscream tvOSTests.xctest";
|
||||
remoteRef = 5C42C3E21D8F31DC00947AA2 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
6B3E7A0E19D48D00006071F7 /* Starscream.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
@@ -219,10 +250,10 @@
|
||||
remoteRef = 6B3E7A0D19D48D00006071F7 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
6B3E7A1019D48D00006071F7 /* StarscreamTests.xctest */ = {
|
||||
6B3E7A1019D48D00006071F7 /* Starscream iOSTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = StarscreamTests.xctest;
|
||||
path = "Starscream iOSTests.xctest";
|
||||
remoteRef = 6B3E7A0F19D48D00006071F7 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
@@ -280,20 +311,30 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = 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_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
@@ -323,13 +364,21 @@
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = 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_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -337,6 +386,7 @@
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
@@ -346,6 +396,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
@@ -358,7 +409,9 @@
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
INFOPLIST_FILE = SimpleTest/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.io.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -369,7 +422,9 @@
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
INFOPLIST_FILE = SimpleTest/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.vluxe.io.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
BIN
Binary file not shown.
+21
-7
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0600"
|
||||
LastUpgradeVersion = "0900"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -37,10 +37,11 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@@ -62,17 +63,22 @@
|
||||
ReferencedContainer = "container:SimpleTest.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5C765ADA199A6DAA003D9110"
|
||||
@@ -81,16 +87,24 @@
|
||||
ReferencedContainer = "container:SimpleTest.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "OS_ACTIVITY_MODE"
|
||||
value = "disable"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5C765ADA199A6DAA003D9110"
|
||||
|
||||
@@ -14,30 +14,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationWillResignActive(application: UIApplication) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.vluxe.io.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
||||
@@ -10,7 +10,7 @@ import UIKit
|
||||
import Starscream
|
||||
|
||||
class ViewController: UIViewController, WebSocketDelegate {
|
||||
var socket = WebSocket(url: NSURL(string: "ws://localhost:8080/")!, protocols: ["chat", "superchat"])
|
||||
var socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat", "superchat"])
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
@@ -20,11 +20,11 @@ class ViewController: UIViewController, WebSocketDelegate {
|
||||
|
||||
// MARK: Websocket Delegate Methods.
|
||||
|
||||
func websocketDidConnect(ws: WebSocket) {
|
||||
func websocketDidConnect(socket: WebSocketClient) {
|
||||
print("websocket is connected")
|
||||
}
|
||||
|
||||
func websocketDidDisconnect(ws: WebSocket, error: NSError?) {
|
||||
func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
|
||||
if let e = error {
|
||||
print("websocket is disconnected: \(e.localizedDescription)")
|
||||
} else {
|
||||
@@ -32,23 +32,23 @@ class ViewController: UIViewController, WebSocketDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func websocketDidReceiveMessage(ws: WebSocket, text: String) {
|
||||
func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
|
||||
print("Received text: \(text)")
|
||||
}
|
||||
|
||||
func websocketDidReceiveData(ws: WebSocket, data: NSData) {
|
||||
print("Received data: \(data.length)")
|
||||
func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
|
||||
print("Received data: \(data.count)")
|
||||
}
|
||||
|
||||
// MARK: Write Text Action
|
||||
|
||||
@IBAction func writeText(sender: UIBarButtonItem) {
|
||||
socket.writeString("hello there!")
|
||||
@IBAction func writeText(_ sender: UIBarButtonItem) {
|
||||
socket.write(string: "hello there!")
|
||||
}
|
||||
|
||||
// MARK: Disconnect Action
|
||||
|
||||
@IBAction func disconnect(sender: UIBarButtonItem) {
|
||||
@IBAction func disconnect(_ sender: UIBarButtonItem) {
|
||||
if socket.isConnected {
|
||||
sender.title = "Connect"
|
||||
socket.disconnect()
|
||||
|
||||
@@ -22,4 +22,4 @@ EM.run {
|
||||
ws.send Faker::Hacker.say_something_smart
|
||||
}
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#include <zlib.h>
|
||||
#include <CommonCrypto/CommonCrypto.h>
|
||||
@@ -0,0 +1,9 @@
|
||||
module SSCZLib [system] {
|
||||
header "include.h"
|
||||
link "z"
|
||||
export *
|
||||
}
|
||||
module SSCommonCrypto [system] {
|
||||
header "include.h"
|
||||
export *
|
||||
}
|
||||
Reference in New Issue
Block a user