├── .swift-version ├── Test ├── Autobahn │ ├── .gitignore │ ├── fuzzingserver.json │ ├── run.sh │ └── autobahn.swift ├── Test.swift └── Info.plist ├── tools ├── server │ ├── .gitignore │ ├── run-server.sh │ ├── ssl │ │ └── mkcert.sh │ └── server.go ├── res │ ├── badge.psd │ ├── docs.png │ ├── logo.png │ ├── swift.png │ ├── swift.psd │ ├── failing.png │ └── passing.png ├── gh-pages.sh ├── echo.html ├── docs.sh └── run-objc-test.sh ├── Package.swift ├── SwiftWebSocket.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ ├── Test-OSX.xcscheme │ │ ├── Test-iOS.xcscheme │ │ ├── SwiftWebSocket-OSX.xcscheme │ │ ├── SwiftWebSocket-tvOS.xcscheme │ │ └── SwiftWebSocket-iOS.xcscheme └── project.pbxproj ├── Test-ObjectiveC ├── Connection.h ├── Test_ObjectiveC.m ├── Info.plist └── Connection.m ├── Source ├── SwiftWebSocket.h ├── Info.plist ├── Info-tvOS.plist └── WebSocket.swift ├── .gitignore ├── CHANGELOG.md ├── SwiftWebSocket.podspec ├── LICENSE └── README.md /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /Test/Autobahn/.gitignore: -------------------------------------------------------------------------------- 1 | reports/ -------------------------------------------------------------------------------- /tools/server/.gitignore: -------------------------------------------------------------------------------- 1 | ssl/server.* 2 | pkg/ 3 | bin/ 4 | src/ 5 | -------------------------------------------------------------------------------- /tools/res/badge.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidwall/SwiftWebSocket/HEAD/tools/res/badge.psd -------------------------------------------------------------------------------- /tools/res/docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidwall/SwiftWebSocket/HEAD/tools/res/docs.png -------------------------------------------------------------------------------- /tools/res/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidwall/SwiftWebSocket/HEAD/tools/res/logo.png -------------------------------------------------------------------------------- /tools/res/swift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidwall/SwiftWebSocket/HEAD/tools/res/swift.png -------------------------------------------------------------------------------- /tools/res/swift.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidwall/SwiftWebSocket/HEAD/tools/res/swift.psd -------------------------------------------------------------------------------- /tools/res/failing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidwall/SwiftWebSocket/HEAD/tools/res/failing.png -------------------------------------------------------------------------------- /tools/res/passing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidwall/SwiftWebSocket/HEAD/tools/res/passing.png -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "SwiftWebSocket" 5 | ) 6 | -------------------------------------------------------------------------------- /tools/server/run-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export GOPATH=$(cd $(dirname "${BASH_SOURCE[0]}"); pwd) 4 | cd $GOPATH 5 | 6 | go get "github.com/gorilla/websocket" 7 | go run server.go $@ 8 | -------------------------------------------------------------------------------- /Test/Autobahn/fuzzingserver.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "url": "ws://127.0.0.1:9001", 4 | "outdir": "./reports/clients", 5 | "cases": ["*"], 6 | "exclude-cases": [], 7 | "exclude-agent-cases": {} 8 | } 9 | -------------------------------------------------------------------------------- /SwiftWebSocket.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Test-ObjectiveC/Connection.h: -------------------------------------------------------------------------------- 1 | // 2 | // Connection.h 3 | // SwiftWebSocket 4 | // 5 | // Created by Ricardo Pereira on 17/12/15. 6 | // Copyright © 2015 ONcast, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Connection : NSObject 12 | 13 | - (void)open; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SwiftWebSocket.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tools/gh-pages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd $(dirname "${BASH_SOURCE[0]}") 6 | 7 | git checkout gh-pages 8 | cleanup() { 9 | git checkout master 10 | } 11 | trap cleanup EXIT 12 | 13 | if [ -f "reports/build.png" ]; then 14 | cp -rf reports/build.png ../build.png 15 | git add ../build.png 16 | fi 17 | if [ -d "reports/clients/" ]; then 18 | cp -rf reports/clients/ ../results/ 19 | git add ../results/ 20 | fi 21 | git commit -m 'updated result' 22 | git push 23 | -------------------------------------------------------------------------------- /Source/SwiftWebSocket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SwiftWebSocket (swiftwebsocket.h) 3 | * 4 | * Copyright (C) Josh Baker. All Rights Reserved. 5 | * Contact: @tidwall, joshbaker77@gmail.com 6 | * 7 | * This software may be modified and distributed under the terms 8 | * of the MIT license. See the LICENSE file for details. 9 | * 10 | */ 11 | 12 | #import 13 | 14 | FOUNDATION_EXPORT double SwiftWebSocketVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char SwiftWebSocketVersionString[]; -------------------------------------------------------------------------------- /Test/Test.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Test.swift 3 | // Test 4 | // 5 | // Created by Josh Baker on 10/23/15. 6 | // Copyright © 2015 ONcast, LLC. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class Test: XCTestCase { 12 | override func setUp() { 13 | super.setUp() 14 | } 15 | 16 | override func tearDown() { 17 | super.tearDown() 18 | } 19 | 20 | func test() { 21 | print("Run Autobahn test ./Test/Autobahn/run.sh") 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Xcode 4 | # 5 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 6 | 7 | ## Build generated 8 | build/ 9 | DerivedData 10 | 11 | ## Various settings 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata 21 | 22 | ## Other 23 | *.xccheckout 24 | *.moved-aside 25 | *.xcuserstate 26 | *.xcscmblueprint 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | 32 | docsb/ 33 | .build/ 34 | 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | `SwiftWebSocket` adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | #### [2.6.1](https://github.com/tidwall/SwiftWebSocket/tree/2.6.1) 6 | 7 | - Fix: Sub-protocol now separate with a comma. #40 8 | - Fix: Reusing websocket doesn't work. #39 9 | 10 | #### [2.6.0](https://github.com/tidwall/SwiftWebSocket/tree/2.6.0) 11 | 12 | - Added: Swift 2.2/Xcode 7.3. #36 13 | - Fix: Receive buffer grows in size. #35 14 | 15 | #### [2.5.0](https://github.com/tidwall/SwiftWebSocket/tree/2.5.0) 16 | 17 | - Added: tvOS support. #29 18 | -------------------------------------------------------------------------------- /tools/server/ssl/mkcert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | cd $(dirname "${BASH_SOURCE[0]}") 5 | 6 | subj="$@" 7 | if [ "$opts" == "" ]; then 8 | subj="/C=US/ST=Arizona/L=Tempe/O=Tidwall/OU=IT/CN=mytestdomain.com" 9 | fi 10 | rm -rf server.* 11 | #openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -keyout server.key -out server.cer -subj "$subj" 12 | openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 13 | openssl rsa -passin pass:x -in server.pass.key -out server.key 14 | rm server.pass.key 15 | openssl req -new -key server.key -out server.csr -subj "/C=US/ST=Arizona/L=Tempe/O=Tidwall/CN=mytestdomain.com" 16 | openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.cer -------------------------------------------------------------------------------- /tools/echo.html: -------------------------------------------------------------------------------- 1 |

 2 | 


--------------------------------------------------------------------------------
/Test-ObjectiveC/Test_ObjectiveC.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  Test_ObjectiveC.m
 3 | //  Test-ObjectiveC
 4 | //
 5 | //  Created by Ricardo Pereira on 17/12/15.
 6 | //  Copyright © 2015 ONcast, LLC. All rights reserved.
 7 | //
 8 | 
 9 | #import 
10 | #import "Connection.h"
11 | 
12 | @interface Test_ObjectiveC : XCTestCase
13 | 
14 | @end
15 | 
16 | @implementation Test_ObjectiveC
17 | 
18 | - (void)setUp {
19 |     [super setUp];
20 |     // Put setup code here. This method is called before the invocation of each test method in the class.
21 | }
22 | 
23 | - (void)tearDown {
24 |     // Put teardown code here. This method is called after the invocation of each test method in the class.
25 |     [super tearDown];
26 | }
27 | 
28 | - (void)testObjectiveC {
29 |     [[[Connection alloc] init] open];
30 | }
31 | 
32 | @end
33 | 


--------------------------------------------------------------------------------
/Test/Info.plist:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 | 
 5 | 	CFBundleDevelopmentRegion
 6 | 	en
 7 | 	CFBundleExecutable
 8 | 	$(EXECUTABLE_NAME)
 9 | 	CFBundleIdentifier
10 | 	$(PRODUCT_BUNDLE_IDENTIFIER)
11 | 	CFBundleInfoDictionaryVersion
12 | 	6.0
13 | 	CFBundleName
14 | 	$(PRODUCT_NAME)
15 | 	CFBundlePackageType
16 | 	BNDL
17 | 	CFBundleShortVersionString
18 | 	1.0
19 | 	CFBundleSignature
20 | 	????
21 | 	CFBundleVersion
22 | 	1
23 | 
24 | 
25 | 


--------------------------------------------------------------------------------
/Test-ObjectiveC/Info.plist:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 | 
 5 | 	CFBundleDevelopmentRegion
 6 | 	en
 7 | 	CFBundleExecutable
 8 | 	$(EXECUTABLE_NAME)
 9 | 	CFBundleIdentifier
10 | 	$(PRODUCT_BUNDLE_IDENTIFIER)
11 | 	CFBundleInfoDictionaryVersion
12 | 	6.0
13 | 	CFBundleName
14 | 	$(PRODUCT_NAME)
15 | 	CFBundlePackageType
16 | 	BNDL
17 | 	CFBundleShortVersionString
18 | 	1.0
19 | 	CFBundleSignature
20 | 	????
21 | 	CFBundleVersion
22 | 	1
23 | 
24 | 
25 | 


--------------------------------------------------------------------------------
/SwiftWebSocket.podspec:
--------------------------------------------------------------------------------
 1 | Pod::Spec.new do |s|
 2 |   s.name                   = "SwiftWebSocket"
 3 |   s.version                = "2.7.0"
 4 |   s.summary                = "A high performance WebSocket client library for Swift."
 5 |   s.homepage               = "https://github.com/tidwall/SwiftWebSocket"
 6 |   s.license                = { :type => "Attribution License", :file => "LICENSE" }
 7 |   s.source                 = { :git => "https://github.com/tidwall/SwiftWebSocket.git", :tag => "v2.7.0" }
 8 |   s.authors                = { 'Josh Baker' => 'joshbaker77@gmail.com' }
 9 |   s.social_media_url       = "https://twitter.com/tidwall"
10 |   s.ios.deployment_target  = "8.0"
11 |   s.osx.deployment_target  = "10.9"
12 |   s.tvos.deployment_target = "9.0"
13 |   s.source_files           = "Source/*.swift"
14 |   s.requires_arc           = true
15 |   s.libraries              = 'z'
16 | end
17 | 


--------------------------------------------------------------------------------
/Source/Info.plist:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 | 
 5 | 	CFBundleDevelopmentRegion
 6 | 	en
 7 | 	CFBundleExecutable
 8 | 	$(EXECUTABLE_NAME)
 9 | 	CFBundleIdentifier
10 | 	$(PRODUCT_BUNDLE_IDENTIFIER)
11 | 	CFBundleInfoDictionaryVersion
12 | 	6.0
13 | 	CFBundleName
14 | 	$(PRODUCT_NAME)
15 | 	CFBundlePackageType
16 | 	FMWK
17 | 	CFBundleShortVersionString
18 | 	1.0
19 | 	CFBundleSignature
20 | 	????
21 | 	CFBundleVersion
22 | 	$(CURRENT_PROJECT_VERSION)
23 | 	NSPrincipalClass
24 | 	
25 | 
26 | 
27 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | Permission is hereby granted, free of charge, to any person obtaining a copy
 2 | of this software and associated documentation files (the "Software"), to deal
 3 | in the Software without restriction, including without limitation the rights
 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 5 | copies of the Software, and to permit persons to whom the Software is
 6 | furnished to do so, subject to the following conditions:
 7 | 
 8 | The above copyright notice and this permission notice shall be included in
 9 | all copies or substantial portions of the Software.
10 | 
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
17 | THE SOFTWARE.
18 | 


--------------------------------------------------------------------------------
/Source/Info-tvOS.plist:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 |     
 5 |         CFBundleDevelopmentRegion
 6 |         en
 7 |         CFBundleExecutable
 8 |         $(EXECUTABLE_NAME)
 9 |         CFBundleIdentifier
10 |         $(PRODUCT_BUNDLE_IDENTIFIER)
11 |         CFBundleInfoDictionaryVersion
12 |         6.0
13 |         CFBundleName
14 |         $(PRODUCT_NAME)
15 |         CFBundlePackageType
16 |         FMWK
17 |         CFBundleShortVersionString
18 |         3.1.5
19 |         CFBundleSignature
20 |         ????
21 |         CFBundleVersion
22 |         $(CURRENT_PROJECT_VERSION)
23 |         NSPrincipalClass
24 |         
25 |         UIRequiredDeviceCapabilities
26 |         
27 |             arm64
28 |         
29 |     
30 | 


--------------------------------------------------------------------------------
/tools/docs.sh:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | set -e 
 3 | 
 4 | cd $(dirname "${BASH_SOURCE[0]}")
 5 | cd ..
 6 | jazzy -g https://github.com/tidwall/SwiftWebSocket -o docsb --skip-undocumented -a "Josh Baker" -m "SwiftWebSocket" -u "http://github.com/tidwall"
 7 | 
 8 | echo ".nav-group-name a[href=\"Extensions.html\"] { display: none; }" >> docsb/css/jazzy.css
 9 | echo ".nav-group-name a[href=\"Extensions.html\"] ~ ul { display: none; }" >> docsb/css/jazzy.css
10 | printf "%s(\".nav-group-name a[href='Extensions.html']\").parent().hide()\n" "$" >> docsb/js/jazzy.js
11 | printf "%s(\".nav-group-name a[href='../Extensions.html']\").parent().hide()\n" "$" >> docsb/js/jazzy.js
12 | printf "%s(\"header .content-wrapper a[href='index.html']\").parent().html(\"SwiftWebSocket Docs\")\n" "$" >> docsb/js/jazzy.js
13 | printf "%s(\"header .content-wrapper a[href='../index.html']\").parent().html(\"SwiftWebSocket Docs\")\n" "$" >> docsb/js/jazzy.js
14 | 
15 | git checkout gh-pages
16 | function cleanup {
17 | 	git reset
18 | 	git checkout master
19 | }
20 | trap cleanup EXIT
21 | rm -rf docs
22 | mv docsb docs
23 | git add docs/
24 | git commit -m "updated docs"
25 | echo "Make sure to push the gh-pages branch"


--------------------------------------------------------------------------------
/Test/Autobahn/run.sh:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | 
 3 | #export PATH=/Library/Developer/Toolchains/swift-latest.xctoolchain/usr/bin:"${PATH}"
 4 | 
 5 | cd $(dirname "${BASH_SOURCE[0]}")
 6 | WSTEST=$(ls $HOME/Library/Python/2.*/bin/wstest 2>/dev/null)
 7 | set -e
 8 | if [ ! -f "$WSTEST" ] || [ "$UPGRADE" == "1" ]; then
 9 | 	pip install --user --upgrade unittest2
10 | 	pip install --user --upgrade autobahntestsuite
11 | 	WSTEST=$(ls $HOME/Library/Python/2.*/bin/wstest)
12 | fi
13 | if [ "$SERVER" == "1" ]; then
14 | 	$WSTEST -m fuzzingserver
15 | 	exit
16 | fi 
17 | if [ "$CLIENT" != "1" ]; then
18 | 	$WSTEST -m fuzzingserver &
19 | 	WSTEST_PID=$!
20 | 	cleanup() {
21 | 		kill $WSTEST_PID
22 | 		if [ "$SUCCESS" == "1" ]; then
23 | 			cp -f res/passing.png reports/build.png
24 | 			printf "\033[0;32m[SUCCESS]\033[0m\n"
25 | 		else
26 | 			if [ -d "reports/clients/" ]; then
27 | 				cp -f res/failing.png reports/build.png
28 | 				printf "\033[0;31m[FAILURE]\033[0m\n"
29 | 			else
30 | 				printf "\033[0;31m[FAILURE]\033[0m Cancelled Early\n"
31 | 				exit
32 | 			fi
33 | 		fi
34 | 		printf "\033[0;33mDon't forget to run 'test/gh-pages.sh' to process the results.\033[0m\n"
35 | 	}
36 | 	trap cleanup EXIT
37 | 	sleep 1
38 | fi
39 | printf "\033[0;33m[BUILDING]\033[0m\n"
40 | rm -fr reports
41 | mkdir -p reports
42 | mkdir -p /tmp/SwiftWebSocket/tests
43 | 
44 | cat ../../Source/WebSocket.swift > /tmp/SwiftWebSocket/tests/main.swift
45 | echo "" >> /tmp/SwiftWebSocket/tests/main.swift
46 | cat autobahn.swift >> /tmp/SwiftWebSocket/tests/main.swift
47 | #swift -Ounchecked /tmp/SwiftWebSocket/tests/main.swift
48 | swift /tmp/SwiftWebSocket/tests/main.swift
49 | 
50 | SUCCESS=1
51 | 


--------------------------------------------------------------------------------
/tools/run-objc-test.sh:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | 
 3 | set -o pipefail
 4 | 
 5 | : ${BUILDTOOL:=xcodebuild} #Default
 6 | 
 7 | # Xcode Build Command Line
 8 | # https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/xcodebuild.1.html
 9 | : ${PROJECT:="SwiftWebSocket.xcodeproj"}
10 | : ${SCHEME:="SwiftWebSocket-iOS"}
11 | : ${TARGET:="Test-ObjectiveC"}
12 | : ${SDK:="iphonesimulator"}
13 | 
14 | echo "Started: $(date)"
15 | 
16 | init() {
17 |   # Launch the simulator before running the tests
18 |   # Avoid "iPhoneSimulator: Timed out waiting"
19 |   open -b com.apple.iphonesimulator
20 | }
21 | 
22 | COMMAND="-project \"${PROJECT}\" -scheme \"${SCHEME}\" -sdk \"${SDK}\""
23 | 
24 | case "${BUILDTOOL}" in
25 |   xctool) echo "Selected build tool: xctool"
26 |   init
27 |     # Tests (Swift & Objective-C)
28 |   	case "${CLASS}" in
29 |   	  "") echo "Testing all classes"
30 |       COMMAND="xctool clean test "${COMMAND}
31 |       ;;
32 |       *) echo "Testing ${CLASS}"
33 |       COMMAND="xctool clean test -only ${CLASS} "${COMMAND}
34 |       ;;
35 |   	esac
36 |   ;;
37 |   xcodebuild-travis) echo "Selected tool: xcodebuild + xcpretty (format: travisci)"
38 |   init
39 |     # Use xcpretty together with tee to store the raw log in a file, and get the pretty output in the terminal
40 |     COMMAND="xcodebuild clean test "${COMMAND}" | tee xcodebuild.log | xcpretty -f `xcpretty-travis-formatter`"
41 |   ;;
42 |   xcodebuild-pretty) echo "Selected tool: xcodebuild + xcpretty"
43 |   init
44 |     COMMAND="xcodebuild clean test "${COMMAND}" | xcpretty --test"
45 |   ;;
46 |   xcodebuild) echo "Selected tool: xcodebuild"
47 |   init
48 |     COMMAND="xcodebuild clean test "${COMMAND}
49 |   ;;
50 |   *) echo "No build tool especified" && exit 2
51 | esac
52 | 
53 | set -x
54 | eval "${COMMAND}"
55 | 


--------------------------------------------------------------------------------
/Test-ObjectiveC/Connection.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  Connection.m
 3 | //  SwiftWebSocket
 4 | //
 5 | //  Created by Ricardo Pereira on 17/12/15.
 6 | //  Copyright © 2015 ONcast, LLC. All rights reserved.
 7 | //
 8 | 
 9 | #import "Connection.h"
10 | #import 
11 | 
12 | @interface Connection () 
13 | 
14 | @end
15 | 
16 | @implementation Connection {
17 |     WebSocket *_webSocket;
18 | }
19 | 
20 | - (instancetype)init {
21 |     if (self = [super init]) {
22 |         _webSocket = nil;
23 |     }
24 |     return self;
25 | }
26 | 
27 | - (void)open {
28 |     _webSocket = [[WebSocket alloc] init:@"ws://localhost:9000"];
29 |     _webSocket.delegate = self;
30 |     [_webSocket open];
31 |     NSAssert(_webSocket.readyState == WebSocketReadyStateConnecting, @"WebSocket is not connecting");
32 | }
33 | 
34 | - (void)webSocketOpen {
35 |     NSLog(@"Open");
36 |     [_webSocket sendWithText:@"test"];
37 |     [_webSocket sendWithData:[@"test" dataUsingEncoding:NSUTF8StringEncoding]];
38 |     NSAssert(_webSocket.readyState == WebSocketReadyStateOpen, @"WebSocket is not ready to communicate");
39 |     [_webSocket close:0 reason:@""];
40 | }
41 | 
42 | - (void)webSocketClose:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
43 |     NSLog(@"Close: %@", reason);
44 | }
45 | 
46 | - (void)webSocketMessageText:(NSString *)text {
47 |     NSLog(@"Message: %@", text);
48 | }
49 | 
50 | - (void)webSocketMessageData:(NSData *)data {
51 |     NSLog(@"Message: %@", data);
52 | }
53 | 
54 | - (void)webSocketPong {
55 |     NSLog(@"Pong");
56 | }
57 | 
58 | - (void)webSocketError:(NSError *)error {
59 |     NSLog(@"Error: %@", error);
60 | }
61 | 
62 | - (void)webSocketEnd:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean error:(NSError *)error {
63 |     NSLog(@"End: %@", error);
64 | }
65 | 
66 | @end
67 | 


--------------------------------------------------------------------------------
/SwiftWebSocket.xcodeproj/xcshareddata/xcschemes/Test-OSX.xcscheme:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 5 |    
 8 |    
 9 |    
14 |       
15 |          
17 |             
23 |             
24 |          
25 |       
26 |       
27 |       
28 |    
29 |    
39 |       
40 |       
41 |    
42 |    
48 |    
49 |    
51 |    
52 |    
55 |    
56 | 
57 | 


--------------------------------------------------------------------------------
/SwiftWebSocket.xcodeproj/xcshareddata/xcschemes/Test-iOS.xcscheme:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 5 |    
 8 |    
 9 |    
14 |       
15 |          
17 |             
23 |             
24 |          
25 |       
26 |       
27 |       
28 |    
29 |    
39 |       
40 |       
41 |    
42 |    
48 |    
49 |    
51 |    
52 |    
55 |    
56 | 
57 | 


--------------------------------------------------------------------------------
/SwiftWebSocket.xcodeproj/xcshareddata/xcschemes/SwiftWebSocket-OSX.xcscheme:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 5 |    
 8 |       
 9 |          
15 |             
21 |             
22 |          
23 |       
24 |    
25 |    
30 |       
31 |       
32 |       
33 |       
34 |    
35 |    
45 |       
46 |          
52 |          
53 |       
54 |       
55 |       
56 |    
57 |    
63 |       
64 |          
70 |          
71 |       
72 |    
73 |    
75 |    
76 |    
79 |    
80 | 
81 | 


--------------------------------------------------------------------------------
/SwiftWebSocket.xcodeproj/xcshareddata/xcschemes/SwiftWebSocket-tvOS.xcscheme:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 5 |    
 8 |       
 9 |          
15 |             
21 |             
22 |          
23 |       
24 |    
25 |    
30 |       
31 |       
32 |       
33 |       
34 |    
35 |    
45 |       
46 |          
52 |          
53 |       
54 |       
55 |       
56 |    
57 |    
63 |       
64 |          
70 |          
71 |       
72 |    
73 |    
75 |    
76 |    
79 |    
80 | 
81 | 


--------------------------------------------------------------------------------
/SwiftWebSocket.xcodeproj/xcshareddata/xcschemes/SwiftWebSocket-iOS.xcscheme:
--------------------------------------------------------------------------------
  1 | 
  2 | 
  5 |    
  8 |       
  9 |          
 15 |             
 21 |             
 22 |          
 23 |       
 24 |    
 25 |    
 30 |       
 31 |          
 33 |             
 39 |             
 40 |          
 41 |       
 42 |       
 43 |          
 49 |          
 50 |       
 51 |       
 52 |       
 53 |    
 54 |    
 64 |       
 65 |          
 71 |          
 72 |       
 73 |       
 74 |       
 75 |    
 76 |    
 82 |       
 83 |          
 89 |          
 90 |       
 91 |    
 92 |    
 94 |    
 95 |    
 98 |    
 99 | 
100 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | #  SwiftWebSocket
  2 | 
  3 | [![API Docs](https://img.shields.io/badge/api-docs-blue.svg?style=flat-square)](http://tidwall.com/SwiftWebSocket/docs/)
  4 | [![Swift/5.0](https://img.shields.io/badge/swift-5.0-brightgreen.svg?style=flat-square)](https://developer.apple.com/swift/)
  5 | [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg?style=flat-square)](http://tidwall.com/SwiftWebSocket/results/)
  6 | 
  7 | Conforming WebSocket ([RFC 6455](https://tools.ietf.org/html/rfc6455)) client library for iOS and Mac OSX.
  8 | 
  9 | SwiftWebSocket passes all 521 of the Autobahn's fuzzing tests, including strict UTF-8, and message compression.
 10 | 
 11 | ### `Project Status`
 12 | 
 13 | I'm looking for someone to help with or take over maintenance of this project.
 14 | 
 15 | ## Features
 16 | 
 17 | - High performance.
 18 | - 100% conforms to [Autobahn Tests](http://autobahn.ws/testsuite/#test-suite-coverage). Including base, limits, compression, etc. [Test results](https://tidwall.github.io/SwiftWebSocket/results/).
 19 | - TLS / WSS support. Self-signed certificate option.
 20 | - The API is modeled after the [Javascript API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket).
 21 | - Reads compressed messages (`permessage-deflate`). [RFC 7692](https://tools.ietf.org/html/rfc7692)
 22 | - Send pings and receive pong events.
 23 | - Strict UTF-8 processing. 
 24 | - `binaryType` property to choose between `[UInt8]` or `NSData` messages.
 25 | - Zero asserts. All networking, stream, and protocol errors are routed through the `error` event.
 26 | - iOS / Objective-C support.
 27 | 
 28 | ## Example
 29 | 
 30 | ```swift
 31 | func echoTest(){
 32 |     var messageNum = 0
 33 |     let ws = WebSocket("wss://echo.websocket.org")
 34 |     let send : ()->() = {
 35 |         messageNum += 1
 36 |         let msg = "\(messageNum): \(NSDate().description)"
 37 |         print("send: \(msg)")
 38 |         ws.send(msg)
 39 |     }
 40 |     ws.event.open = {
 41 |         print("opened")
 42 |         send()
 43 |     }
 44 |     ws.event.close = { code, reason, clean in
 45 |         print("close")
 46 |     }
 47 |     ws.event.error = { error in
 48 |         print("error \(error)")
 49 |     }
 50 |     ws.event.message = { message in
 51 |         if let text = message as? String {
 52 |             print("recv: \(text)")
 53 |             if messageNum == 10 {
 54 |                 ws.close()
 55 |             } else {
 56 |                 send()
 57 |             }
 58 |         }
 59 |     }
 60 | }
 61 | ```
 62 | 
 63 | ## Custom Headers
 64 | ```swift
 65 | var request = URLRequest(url: URL(string:"ws://url")!)
 66 | request.addValue("AUTH_TOKEN", forHTTPHeaderField: "Authorization")
 67 | request.addValue("Value", forHTTPHeaderField: "X-Another-Header")
 68 | let ws = WebSocket(request: request)
 69 | ```
 70 | 
 71 | ## Reuse and Delaying WebSocket Connections
 72 | v2.3.0+ makes available an optional `open` method. This will allow for a `WebSocket` object to be instantiated without an immediate connection to the server. It can also be used to reconnect to a server following the `close` event.
 73 | 
 74 | For example,
 75 | 
 76 | ```swift
 77 | let ws = WebSocket()
 78 | ws.event.close = { _,_,_ in
 79 |     ws.open()                 // reopen the socket to the previous url
 80 |     ws.open("ws://otherurl")  // or, reopen the socket to a new url
 81 | }
 82 | ws.open("ws://url") // call with url
 83 | ```
 84 | 
 85 | ## Compression
 86 | 
 87 | The `compression` flag may be used to request compressed messages from the server. If the server does not support or accept the request, then connection will continue as normal, but with uncompressed messages.
 88 | 
 89 | ```swift
 90 | let ws = WebSocket("ws://url")
 91 | ws.compression.on = true
 92 | ```
 93 | 
 94 | ## Self-signed SSL Certificate
 95 | 
 96 | ```swift
 97 | let ws = WebSocket("ws://url")
 98 | ws.allowSelfSignedSSL = true
 99 | ```
100 | 
101 | ## Network Services (VoIP, Video, Background, Voice)
102 | 
103 | ```swift
104 | // Allow socket to handle VoIP in the background.
105 | ws.services = [.VoIP, .Background] 
106 | ```
107 | ## Installation (iOS and OS X)
108 | 
109 | ### [Carthage]
110 | 
111 | [Carthage]: https://github.com/Carthage/Carthage
112 | 
113 | Add the following to your Cartfile:
114 | 
115 | ```
116 | github "tidwall/SwiftWebSocket"
117 | ```
118 | 
119 | Then run `carthage update`.
120 | 
121 | Follow the current instructions in [Carthage's README][carthage-installation]
122 | for up to date installation instructions.
123 | 
124 | [carthage-installation]: https://github.com/Carthage/Carthage#adding-frameworks-to-an-application
125 | 
126 | The `import SwiftWebSocket` directive is required in order to access SwiftWebSocket features.
127 | 
128 | ### [CocoaPods]
129 | 
130 | [CocoaPods]: http://cocoapods.org
131 | 
132 | Add the following to your [Podfile](http://guides.cocoapods.org/using/the-podfile.html):
133 | 
134 | ```ruby
135 | use_frameworks!
136 | pod 'SwiftWebSocket'
137 | ```
138 | 
139 | Then run `pod install` with CocoaPods 0.36 or newer.
140 | 
141 | The `import SwiftWebSocket` directive is required in order to access SwiftWebSocket features.
142 | 
143 | ### Manually
144 | 
145 | Copy the `SwiftWebSocket/WebSocket.swift` file into your project.  
146 | You must also add the `libz.dylib` library. `Project -> Target -> Build Phases -> Link Binary With Libraries`
147 | 
148 | There is no need for `import SwiftWebSocket` when manually installing.
149 | 
150 | 
151 | 
152 | 
153 | ## Contact
154 | Josh Baker [@tidwall](http://twitter.com/tidwall)
155 | 
156 | ## License
157 | 
158 | SwiftWebSocket source code is available under the MIT License.
159 | 


--------------------------------------------------------------------------------
/tools/server/server.go:
--------------------------------------------------------------------------------
  1 | package main
  2 | 
  3 | import (
  4 | 	"crypto/rand"
  5 | 	"flag"
  6 | 	"fmt"
  7 | 	"io"
  8 | 	"log"
  9 | 	"net/http"
 10 | 	"sync"
 11 | 	"time"
 12 | 
 13 | 	"github.com/gorilla/websocket"
 14 | )
 15 | 
 16 | const rapidSize = 250 * 1024
 17 | const rapidFPS = 25
 18 | 
 19 | var port int
 20 | var crt, key string
 21 | var host string
 22 | var s string
 23 | var ports string
 24 | var _case string
 25 | 
 26 | func main() {
 27 | 
 28 | 	flag.StringVar(&crt, "crt", "", "ssl cert file")
 29 | 	flag.StringVar(&key, "key", "", "ssl key file")
 30 | 	flag.StringVar(&host, "host", "localhost", "listening server host")
 31 | 	flag.StringVar(&_case, "case", "", "choose a specialized case, (hang,rapid,t44)")
 32 | 	flag.IntVar(&port, "port", 6789, "listening server port")
 33 | 	flag.Parse()
 34 | 
 35 | 	if crt != "" || key != "" {
 36 | 		s = "s"
 37 | 		if port != 443 {
 38 | 			ports = fmt.Sprintf(":%d", port)
 39 | 		}
 40 | 	} else if port != 80 {
 41 | 		ports = fmt.Sprintf(":%d", port)
 42 | 	}
 43 | 	http.HandleFunc("/client", client)
 44 | 	http.HandleFunc("/echo", socket)
 45 | 	http.HandleFunc("/t44", socket)
 46 | 	log.Printf("Running server on %s:%d\n", host, port)
 47 | 	switch _case {
 48 | 	default:
 49 | 		log.Fatalf("case: %s is unknown", _case)
 50 | 	case "":
 51 | 	case "hang":
 52 | 		log.Printf("case: %s (long connection hanging)\n", _case)
 53 | 	case "rapid":
 54 | 		log.Printf("case: %s (rapid (250 fps) large (2048 bytes) random text messages)\n", _case)
 55 | 	case "t44":
 56 | 		log.Printf("case: %s (send 5 messages per seconds forever. gh issue #44)\n", _case)
 57 | 	}
 58 | 	log.Printf("http%s://%s%s/client (javascript client)\n", s, host, ports)
 59 | 	log.Printf("ws%s://%s%s/echo     (echo socket)\n", s, host, ports)
 60 | 	log.Printf("ws%s://%s%s/t44      (test issue 44 socket)\n", s, host, ports)
 61 | 	var err error
 62 | 	if crt != "" || key != "" {
 63 | 		err = http.ListenAndServeTLS(fmt.Sprintf(":%d", port), crt, key, nil)
 64 | 	} else {
 65 | 		err = http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
 66 | 	}
 67 | 	if err != nil {
 68 | 		log.Fatal("ListenAndServe: ", err)
 69 | 	}
 70 | }
 71 | 
 72 | func socket(w http.ResponseWriter, r *http.Request) {
 73 | 	log.Print("connection established")
 74 | 	t44 := _case == "t44"
 75 | 	rapid := _case == "rapid"
 76 | 	if _case == "hang" {
 77 | 		hang := time.Minute
 78 | 		log.Printf("hanging for %s\n", hang.String())
 79 | 		time.Sleep(hang)
 80 | 	}
 81 | 	ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
 82 | 	if err != nil {
 83 | 		log.Print(err)
 84 | 		return
 85 | 	}
 86 | 	defer func() {
 87 | 		ws.Close()
 88 | 		log.Print("connection closed")
 89 | 	}()
 90 | 	var mu sync.Mutex
 91 | 	go func() {
 92 | 		if t44 {
 93 | 			defer ws.Close()
 94 | 			var i int
 95 | 			t := time.NewTicker(time.Second / 5)
 96 | 			defer t.Stop()
 97 | 			for range t.C {
 98 | 				mu.Lock()
 99 | 				if err := ws.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("msg #%-5d %v", i, time.Now()))); err != nil {
100 | 					mu.Unlock()
101 | 					return
102 | 				}
103 | 				mu.Unlock()
104 | 				i++
105 | 			}
106 | 		} else if rapid {
107 | 			defer ws.Close()
108 | 			msg := make([]byte, rapidSize)
109 | 			b := make([]byte, 2048)
110 | 			for {
111 | 				time.Sleep(time.Second / rapidFPS)
112 | 				i := 0
113 | 			outer:
114 | 				for {
115 | 					rand.Read(b)
116 | 					for _, c := range b {
117 | 						if i == len(msg) {
118 | 							break outer
119 | 						}
120 | 						msg[i] = (c % (126 - 32)) + 32 // ascii #32-126
121 | 						i++
122 | 					}
123 | 				}
124 | 				copy(msg, []byte(time.Now().String()+"\n"))
125 | 				mu.Lock()
126 | 				if err := ws.WriteMessage(websocket.TextMessage, msg); err != nil {
127 | 					mu.Unlock()
128 | 					return
129 | 				}
130 | 				mu.Unlock()
131 | 			}
132 | 		}
133 | 	}()
134 | 	for {
135 | 		msgt, msg, err := ws.ReadMessage()
136 | 		if err != nil {
137 | 			log.Print(err)
138 | 			return
139 | 		}
140 | 		log.Print("rcvd: '" + string(msg) + "'")
141 | 		if !t44 {
142 | 			mu.Lock()
143 | 			ws.WriteMessage(msgt, msg)
144 | 			mu.Unlock()
145 | 		}
146 | 	}
147 | }
148 | 
149 | func client(w http.ResponseWriter, r *http.Request) {
150 | 	log.Print("client request")
151 | 	w.Header().Set("Content-Type", "text/html")
152 | 	if _case != "" {
153 | 		fmt.Fprintf(w, "
["+_case+"]
") 154 | } 155 | epoint := "echo" 156 | if _case == "t44" { 157 | epoint = "t44" 158 | } 159 | url := fmt.Sprintf("ws%s://%s%s/%s", s, host, ports, epoint) 160 | 161 | io.WriteString(w, ` 162 |

163 | 		`)
224 | 
225 | }
226 | 


--------------------------------------------------------------------------------
/Test/Autobahn/autobahn.swift:
--------------------------------------------------------------------------------
  1 | import Foundation
  2 | 
  3 | let baseURL = "ws://localhost:9001"
  4 | let agent = "SwiftWebSocket"
  5 | let debug = false
  6 | let keepStatsUpdated = false
  7 | let stopOnFailure = false
  8 | let stopOnInfo = false
  9 | let stopAfterOne = false
 10 | let showDuration = false
 11 | 
 12 | let startCase = 1
 13 | let stopAtCase = 999
 14 | 
 15 | private enum ErrCode : Int, CustomStringConvertible {
 16 |     case Protocoll = 1002, Payload = 1007, Undefined = -100, Codepoint = -101, Library = -102, Socket = -103
 17 |     var description : String {
 18 |         switch self {
 19 |         case .Protocoll: return "Protocol error"
 20 |         case .Payload: return "Invalid payload data"
 21 |         case .Codepoint: return "Invalid codepoint"
 22 |         case .Library: return "Library error"
 23 |         case .Undefined: return "Undefined error"
 24 |         case .Socket: return "Broken socket"
 25 |         }
 26 |     }
 27 | }
 28 | 
 29 | private func makeError(error : String, code: ErrCode) -> Error {
 30 |     return NSError(domain: "com.github.tidwall.WebSocketConn", code: code.rawValue, userInfo: [NSLocalizedDescriptionKey:"\(error)"])
 31 | }
 32 | private func makeError(error : Error, code: ErrCode) -> Error {
 33 |     let err = error as NSError
 34 |     return NSError(domain: err.domain, code: code.rawValue, userInfo: [NSLocalizedDescriptionKey:"\(err.localizedDescription)"])
 35 | }
 36 | private func makeError(error : String) -> Error {
 37 |     return makeError(error: error, code: ErrCode.Library)
 38 | }
 39 | 
 40 | private func jsonObject(text : String) throws -> [String: AnyObject] {
 41 |     if let data = text.data(using: String.Encoding.utf8, allowLossyConversion: false),
 42 |         let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String : AnyObject] {
 43 |             return json
 44 |     }
 45 |     throw makeError(error: "not json")
 46 | }
 47 | 
 48 | // autobahn api
 49 | func getCaseCount(block: @escaping(_ : Int, _ : Error?)->()){
 50 |     let ws = WebSocket(baseURL + "/getCaseCount")
 51 |     ws.event.message = { (msg) in
 52 |         if let text = msg as? String {
 53 |             ws.close()
 54 |             if let i = Int(text) {
 55 |                 block(i, nil)
 56 |             } else {
 57 |                 block(0, makeError(error: "invalid response"))
 58 |             }
 59 |         }
 60 |     }
 61 |     ws.event.error = { error in
 62 |         block(0, error)
 63 |     }
 64 | }
 65 | 
 66 | func getCaseInfo(caseIdx : Int, block : @escaping(_ : String, _ : String, _ : Error?)->()){
 67 |     let ws = WebSocket(baseURL + "/getCaseInfo?case=\(caseIdx+1)")
 68 |     ws.event.message = { (msg) in
 69 |         if let text = msg as? String {
 70 |             ws.close()
 71 |             do {
 72 |                 let json = try jsonObject(text: text)
 73 |                 if json["id"] == nil || json["description"] == nil {
 74 |                     block("", "", makeError(error: "invalid response"))
 75 |                 }
 76 |                 block(json["id"] as! String, json["description"] as! String, nil)
 77 |             } catch {
 78 |                 block("", "", error)
 79 |             }
 80 |         }
 81 |     }
 82 |     ws.event.error = { error in
 83 |         block("", "", error)
 84 |     }
 85 | }
 86 | 
 87 | func getCaseStatus(caseIdx : Int, block : @escaping(_ : Error?)->()){
 88 |     var responseText = ""
 89 |     let ws = WebSocket(baseURL + "/getCaseStatus?case=\(caseIdx+1)&agent=\(agent)")
 90 |     ws.event.error = { error in
 91 |         block(error)
 92 |     }
 93 |     ws.event.message = { (msg) in
 94 |         if let text = msg as? String {
 95 |             responseText = text
 96 |             ws.close()
 97 |         }
 98 |     }
 99 |     ws.event.close = { (code, reason, clean) in
100 |         do {
101 |             let json = try jsonObject(text: responseText)
102 |             if let behavior = json["behavior"] as? String {
103 |                 if behavior == "OK" {
104 |                     block(nil)
105 |                 } else if behavior == "FAILED"{
106 |                     block(makeError(error: ""))
107 |                 } else {
108 |                     block(makeError(error: behavior))
109 |                 }
110 |                 return
111 |             }
112 |         } catch {
113 |             block(error)
114 |         }
115 |     }
116 | }
117 | 
118 | func updateReports(echo: Bool = false, block : @escaping()->()){
119 |     var success = false
120 |     let ws = WebSocket(baseURL + "/updateReports?agent=\(agent)")
121 |     ws.event.close = { (code, reason, clean) in
122 |         if echo {
123 |             if !success{
124 |                 print("[ERR] reports failed to update")
125 |                 exit(1)
126 |             }
127 |         }
128 |         block()
129 |     }
130 |     ws.event.open = {
131 |         ws.close()
132 |         success = true
133 |     }
134 | }
135 | 
136 | func runCase(caseIdx : Int, caseCount : Int, block : @escaping(_ : Error?)->()) {
137 | //    var start = NSDate().timeIntervalSince1970
138 | //    var evstart = NSTimeInterval(0)
139 |     getCaseInfo(caseIdx: caseIdx, block: { (id, description, error) in
140 |         if error != nil{
141 |             print("[ERR] getCaseInfo failed: \(error!)\n")
142 |             exit(1)
143 |         }
144 | 
145 |         var next = { ()->() in }
146 | 
147 | 
148 |         print("[CASE] #\(caseIdx+1)/\(caseCount): \(id): \(description)")
149 |         let failed : (_ : String)->() = { (message) in
150 |             let error = makeError(error: message)
151 |             printFailure(error: error)
152 |             if stopOnFailure {
153 |                 block(error)    
154 |             } else {
155 |                 next()
156 |             }
157 |         }
158 |         let warn : (_ : String)->() = { (message) in
159 |             printFailure(error: makeError(error: message))
160 |         }
161 |         next = { ()->() in
162 | //            if showDuration {
163 | //                let now = NSDate().timeIntervalSince1970
164 | //                let recv = evstart == 0 ? 0 : (evstart - start) * 1000
165 | //                let total = (now - start) * 1000
166 | //                let send = total - recv
167 | //                println("[DONE] %.0f ms (recv: %.0f ms, send: %.0f ms)", total, recv, send)
168 | //            }
169 |             getCaseStatus(caseIdx: caseIdx){ error in
170 |                 let f : ()->() = {
171 |                     if let error = error as? NSError {
172 |                         if error.localizedDescription == "INFORMATIONAL" {
173 |                             if stopOnInfo {
174 |                                 failed(error.localizedDescription)
175 |                                 return
176 |                             }
177 |                         } else if stopOnFailure {
178 |                             failed(error.localizedDescription)
179 |                             return
180 |                         }
181 |                         warn(error.localizedDescription)
182 |                     }
183 |                     if caseIdx+1 == caseCount || stopAfterOne || (caseIdx+1 == stopAtCase){
184 |                         block(nil)
185 |                     } else {
186 |                         runCase(caseIdx: caseIdx+1, caseCount: caseCount, block: block)
187 |                     }
188 |                 }
189 |                 if keepStatsUpdated || caseIdx % 10 == 0 {
190 |                     updateReports(echo: false, block: f)
191 |                 } else {
192 |                     f()
193 |                 }
194 |             }
195 |         }
196 |         var responseError : Error?
197 |         //print(baseURL + "/runCase?case=\(caseIdx+1)&agent=\(agent)")
198 |         let ws = WebSocket(baseURL + "/runCase?case=\(caseIdx+1)&agent=\(agent)")
199 |         ws.eventQueue = nil
200 |         ws.binaryType = .uInt8UnsafeBufferPointer
201 | 
202 |         if id.hasPrefix("13.") || id.hasPrefix("12.") {
203 |             ws.compression.on = true
204 |             if id.hasPrefix("13.1"){
205 |                 ws.compression.noContextTakeover = false
206 |                 ws.compression.maxWindowBits =  0
207 |             }
208 |             if id.hasPrefix("13.2"){
209 |                 ws.compression.noContextTakeover = true
210 |                 ws.compression.maxWindowBits = 0
211 |             }
212 |             if id.hasPrefix("13.3"){
213 |                 ws.compression.noContextTakeover = false
214 |                 ws.compression.maxWindowBits = 8
215 |             }
216 |             if id.hasPrefix("13.4"){
217 |                 ws.compression.noContextTakeover = false
218 |                 ws.compression.maxWindowBits = 15
219 |             }
220 |             if id.hasPrefix("13.5"){
221 |                 ws.compression.noContextTakeover = true
222 |                 ws.compression.maxWindowBits = 8
223 |             }
224 |             if id.hasPrefix("13.6"){
225 |                 ws.compression.noContextTakeover = true
226 |                 ws.compression.maxWindowBits = 15
227 |             }
228 |             if id.hasPrefix("13.7"){
229 |                 ws.compression.noContextTakeover = true
230 |                 ws.compression.maxWindowBits = 8
231 |             }
232 |         }
233 |         ws.event.end = { (code, reason, clean, error) in
234 |             responseError = error
235 |             if responseError == nil {
236 |                 next()
237 |             } else {
238 |                 var message = ""
239 |                 if let error = responseError as? NSError {
240 |                     message += error.localizedDescription
241 |                 }
242 |                 if code != 0 {
243 |                     message += " with code '\(code)' and reason '\(reason)'"
244 |                 }
245 |                 failed(message)
246 |             }
247 |         }
248 |         ws.event.message = { (msg) in
249 | //            evstart = NSDate().timeIntervalSince1970
250 |             ws.send(msg)
251 |         }
252 |     })
253 | }
254 | func printFailure(error : Error?){
255 |     let error = error as? NSError
256 |     if error == nil || error!.localizedDescription == "" {
257 |         print("[ERR] FAILED")
258 |         exit(1)
259 |     } else {
260 |         if error!.localizedDescription == "INFORMATIONAL" {
261 |             //printinfo("INFORMATIONAL")
262 |         } else {
263 |             print("[ERR] FAILED: \(error!.localizedDescription)")
264 |         }
265 |     }
266 | }
267 | 
268 | getCaseCount { (count, error) in
269 |     if error != nil{
270 |         print("[ERR] getCaseCount failed: \(error!)")
271 |         exit(1)
272 |     }
273 |     runCase(caseIdx: startCase-1, caseCount: count){ (error) in
274 |         if error == nil{
275 |             updateReports(echo: true){
276 |                exit(0)
277 |             }
278 |         } else {
279 |             updateReports(echo: true){
280 |                exit(1)
281 |             }
282 |         }
283 |     }
284 | }
285 | 
286 | RunLoop.main.run()
287 | 


--------------------------------------------------------------------------------
/SwiftWebSocket.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
  1 | // !$*UTF8*$!
  2 | {
  3 | 	archiveVersion = 1;
  4 | 	classes = {
  5 | 	};
  6 | 	objectVersion = 46;
  7 | 	objects = {
  8 | 
  9 | /* Begin PBXBuildFile section */
 10 | 		031967101B4D96C40033860E /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03285D2A1B4A9F1A0078A1AA /* WebSocket.swift */; };
 11 | 		03285D2B1B4A9F1A0078A1AA /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03285D2A1B4A9F1A0078A1AA /* WebSocket.swift */; };
 12 | 		03957A461C5A734C005CC1DB /* SwiftWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = D71948F11B35E5670015C529 /* SwiftWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
 13 | 		03957A471C5A7392005CC1DB /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03285D2A1B4A9F1A0078A1AA /* WebSocket.swift */; };
 14 | 		03957A4C1C5A7532005CC1DB /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 03957A4B1C5A7532005CC1DB /* libz.dylib */; };
 15 | 		03D1E7221B208A5C00AC49AC /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 03D1E7211B208A5C00AC49AC /* libz.dylib */; };
 16 | 		03D70CD11BDAC5EC00D245C3 /* SwiftWebSocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D71949011B35E6130015C529 /* SwiftWebSocket.framework */; };
 17 | 		03D70CE01BDAC63600D245C3 /* SwiftWebSocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03D1E7031B20897100AC49AC /* SwiftWebSocket.framework */; };
 18 | 		03D70CEC1BDAC70B00D245C3 /* Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D70CE91BDAC70100D245C3 /* Test.swift */; };
 19 | 		03D70CED1BDAC70C00D245C3 /* Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D70CE91BDAC70100D245C3 /* Test.swift */; };
 20 | 		D71948F51B35E5670015C529 /* SwiftWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = D71948F11B35E5670015C529 /* SwiftWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
 21 | 		D719491A1B35E6640015C529 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D71948FA1B35E59D0015C529 /* libz.dylib */; };
 22 | 		D719491B1B35E7510015C529 /* SwiftWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = D71948F11B35E5670015C529 /* SwiftWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
 23 | 		D734D9EC1C232E6A00437555 /* Connection.m in Sources */ = {isa = PBXBuildFile; fileRef = D734D9EB1C232E6A00437555 /* Connection.m */; };
 24 | 		D78C40331C232C3800EB72AA /* Test_ObjectiveC.m in Sources */ = {isa = PBXBuildFile; fileRef = D78C40321C232C3800EB72AA /* Test_ObjectiveC.m */; };
 25 | 		D78C40351C232C3800EB72AA /* SwiftWebSocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03D1E7031B20897100AC49AC /* SwiftWebSocket.framework */; };
 26 | /* End PBXBuildFile section */
 27 | 
 28 | /* Begin PBXContainerItemProxy section */
 29 | 		03D70CD21BDAC5EC00D245C3 /* PBXContainerItemProxy */ = {
 30 | 			isa = PBXContainerItemProxy;
 31 | 			containerPortal = 03D1E6FA1B20897100AC49AC /* Project object */;
 32 | 			proxyType = 1;
 33 | 			remoteGlobalIDString = D71949001B35E6130015C529;
 34 | 			remoteInfo = "SwiftWebSocket-OSX";
 35 | 		};
 36 | 		03D70CE11BDAC63600D245C3 /* PBXContainerItemProxy */ = {
 37 | 			isa = PBXContainerItemProxy;
 38 | 			containerPortal = 03D1E6FA1B20897100AC49AC /* Project object */;
 39 | 			proxyType = 1;
 40 | 			remoteGlobalIDString = 03D1E7021B20897100AC49AC;
 41 | 			remoteInfo = "SwiftWebSocket-iOS";
 42 | 		};
 43 | 		D78C40361C232C3800EB72AA /* PBXContainerItemProxy */ = {
 44 | 			isa = PBXContainerItemProxy;
 45 | 			containerPortal = 03D1E6FA1B20897100AC49AC /* Project object */;
 46 | 			proxyType = 1;
 47 | 			remoteGlobalIDString = 03D1E7021B20897100AC49AC;
 48 | 			remoteInfo = "SwiftWebSocket-iOS";
 49 | 		};
 50 | /* End PBXContainerItemProxy section */
 51 | 
 52 | /* Begin PBXFileReference section */
 53 | 		0319670F1B4D96B80033860E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
 54 | 		03285D2A1B4A9F1A0078A1AA /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = ""; };
 55 | 		03957A3C1C5A71DB005CC1DB /* SwiftWebSocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftWebSocket.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 56 | 		03957A481C5A74D9005CC1DB /* Info-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-tvOS.plist"; sourceTree = ""; };
 57 | 		03957A4B1C5A7532005CC1DB /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
 58 | 		03D1E7031B20897100AC49AC /* SwiftWebSocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftWebSocket.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 59 | 		03D1E7211B208A5C00AC49AC /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
 60 | 		03D70CCC1BDAC5EC00D245C3 /* Test-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Test-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 61 | 		03D70CDB1BDAC63600D245C3 /* Test-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Test-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 62 | 		03D70CE81BDAC70100D245C3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
 63 | 		03D70CE91BDAC70100D245C3 /* Test.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Test.swift; sourceTree = ""; };
 64 | 		D71948F11B35E5670015C529 /* SwiftWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftWebSocket.h; sourceTree = ""; };
 65 | 		D71948FA1B35E59D0015C529 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
 66 | 		D71949011B35E6130015C529 /* SwiftWebSocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftWebSocket.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 67 | 		D734D9EA1C232E6A00437555 /* Connection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Connection.h; sourceTree = ""; };
 68 | 		D734D9EB1C232E6A00437555 /* Connection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Connection.m; sourceTree = ""; };
 69 | 		D78C40301C232C3800EB72AA /* Test-ObjectiveC.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Test-ObjectiveC.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 70 | 		D78C40321C232C3800EB72AA /* Test_ObjectiveC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Test_ObjectiveC.m; sourceTree = ""; };
 71 | 		D78C40341C232C3800EB72AA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
 72 | /* End PBXFileReference section */
 73 | 
 74 | /* Begin PBXFrameworksBuildPhase section */
 75 | 		03957A381C5A71DB005CC1DB /* Frameworks */ = {
 76 | 			isa = PBXFrameworksBuildPhase;
 77 | 			buildActionMask = 2147483647;
 78 | 			files = (
 79 | 				03957A4C1C5A7532005CC1DB /* libz.dylib in Frameworks */,
 80 | 			);
 81 | 			runOnlyForDeploymentPostprocessing = 0;
 82 | 		};
 83 | 		03D1E6FF1B20897100AC49AC /* Frameworks */ = {
 84 | 			isa = PBXFrameworksBuildPhase;
 85 | 			buildActionMask = 2147483647;
 86 | 			files = (
 87 | 				03D1E7221B208A5C00AC49AC /* libz.dylib in Frameworks */,
 88 | 			);
 89 | 			runOnlyForDeploymentPostprocessing = 0;
 90 | 		};
 91 | 		03D70CC91BDAC5EC00D245C3 /* Frameworks */ = {
 92 | 			isa = PBXFrameworksBuildPhase;
 93 | 			buildActionMask = 2147483647;
 94 | 			files = (
 95 | 				03D70CD11BDAC5EC00D245C3 /* SwiftWebSocket.framework in Frameworks */,
 96 | 			);
 97 | 			runOnlyForDeploymentPostprocessing = 0;
 98 | 		};
 99 | 		03D70CD81BDAC63600D245C3 /* Frameworks */ = {
100 | 			isa = PBXFrameworksBuildPhase;
101 | 			buildActionMask = 2147483647;
102 | 			files = (
103 | 				03D70CE01BDAC63600D245C3 /* SwiftWebSocket.framework in Frameworks */,
104 | 			);
105 | 			runOnlyForDeploymentPostprocessing = 0;
106 | 		};
107 | 		D71948FD1B35E6130015C529 /* Frameworks */ = {
108 | 			isa = PBXFrameworksBuildPhase;
109 | 			buildActionMask = 2147483647;
110 | 			files = (
111 | 				D719491A1B35E6640015C529 /* libz.dylib in Frameworks */,
112 | 			);
113 | 			runOnlyForDeploymentPostprocessing = 0;
114 | 		};
115 | 		D78C402D1C232C3800EB72AA /* Frameworks */ = {
116 | 			isa = PBXFrameworksBuildPhase;
117 | 			buildActionMask = 2147483647;
118 | 			files = (
119 | 				D78C40351C232C3800EB72AA /* SwiftWebSocket.framework in Frameworks */,
120 | 			);
121 | 			runOnlyForDeploymentPostprocessing = 0;
122 | 		};
123 | /* End PBXFrameworksBuildPhase section */
124 | 
125 | /* Begin PBXGroup section */
126 | 		03D1E6F91B20897100AC49AC = {
127 | 			isa = PBXGroup;
128 | 			children = (
129 | 				03957A4B1C5A7532005CC1DB /* libz.dylib */,
130 | 				D71948FA1B35E59D0015C529 /* libz.dylib */,
131 | 				03D1E7211B208A5C00AC49AC /* libz.dylib */,
132 | 				D71948F01B35E5670015C529 /* Source */,
133 | 				03D70CE71BDAC70100D245C3 /* Test */,
134 | 				D78C40311C232C3800EB72AA /* Test-ObjectiveC */,
135 | 				03D1E7041B20897100AC49AC /* Products */,
136 | 			);
137 | 			sourceTree = "";
138 | 		};
139 | 		03D1E7041B20897100AC49AC /* Products */ = {
140 | 			isa = PBXGroup;
141 | 			children = (
142 | 				03D1E7031B20897100AC49AC /* SwiftWebSocket.framework */,
143 | 				D71949011B35E6130015C529 /* SwiftWebSocket.framework */,
144 | 				03D70CCC1BDAC5EC00D245C3 /* Test-OSX.xctest */,
145 | 				03D70CDB1BDAC63600D245C3 /* Test-iOS.xctest */,
146 | 				D78C40301C232C3800EB72AA /* Test-ObjectiveC.xctest */,
147 | 				03957A3C1C5A71DB005CC1DB /* SwiftWebSocket.framework */,
148 | 			);
149 | 			name = Products;
150 | 			sourceTree = "";
151 | 		};
152 | 		03D70CE71BDAC70100D245C3 /* Test */ = {
153 | 			isa = PBXGroup;
154 | 			children = (
155 | 				03D70CE81BDAC70100D245C3 /* Info.plist */,
156 | 				03D70CE91BDAC70100D245C3 /* Test.swift */,
157 | 			);
158 | 			path = Test;
159 | 			sourceTree = "";
160 | 		};
161 | 		D71948F01B35E5670015C529 /* Source */ = {
162 | 			isa = PBXGroup;
163 | 			children = (
164 | 				03957A481C5A74D9005CC1DB /* Info-tvOS.plist */,
165 | 				0319670F1B4D96B80033860E /* Info.plist */,
166 | 				D71948F11B35E5670015C529 /* SwiftWebSocket.h */,
167 | 				03285D2A1B4A9F1A0078A1AA /* WebSocket.swift */,
168 | 			);
169 | 			path = Source;
170 | 			sourceTree = "";
171 | 		};
172 | 		D78C40311C232C3800EB72AA /* Test-ObjectiveC */ = {
173 | 			isa = PBXGroup;
174 | 			children = (
175 | 				D78C40341C232C3800EB72AA /* Info.plist */,
176 | 				D734D9EA1C232E6A00437555 /* Connection.h */,
177 | 				D734D9EB1C232E6A00437555 /* Connection.m */,
178 | 				D78C40321C232C3800EB72AA /* Test_ObjectiveC.m */,
179 | 			);
180 | 			path = "Test-ObjectiveC";
181 | 			sourceTree = "";
182 | 		};
183 | /* End PBXGroup section */
184 | 
185 | /* Begin PBXHeadersBuildPhase section */
186 | 		03957A391C5A71DB005CC1DB /* Headers */ = {
187 | 			isa = PBXHeadersBuildPhase;
188 | 			buildActionMask = 2147483647;
189 | 			files = (
190 | 				03957A461C5A734C005CC1DB /* SwiftWebSocket.h in Headers */,
191 | 			);
192 | 			runOnlyForDeploymentPostprocessing = 0;
193 | 		};
194 | 		03D1E7001B20897100AC49AC /* Headers */ = {
195 | 			isa = PBXHeadersBuildPhase;
196 | 			buildActionMask = 2147483647;
197 | 			files = (
198 | 				D71948F51B35E5670015C529 /* SwiftWebSocket.h in Headers */,
199 | 			);
200 | 			runOnlyForDeploymentPostprocessing = 0;
201 | 		};
202 | 		D71948FE1B35E6130015C529 /* Headers */ = {
203 | 			isa = PBXHeadersBuildPhase;
204 | 			buildActionMask = 2147483647;
205 | 			files = (
206 | 				D719491B1B35E7510015C529 /* SwiftWebSocket.h in Headers */,
207 | 			);
208 | 			runOnlyForDeploymentPostprocessing = 0;
209 | 		};
210 | /* End PBXHeadersBuildPhase section */
211 | 
212 | /* Begin PBXNativeTarget section */
213 | 		03957A3B1C5A71DB005CC1DB /* SwiftWebSocket-tvOS */ = {
214 | 			isa = PBXNativeTarget;
215 | 			buildConfigurationList = 03957A411C5A71DC005CC1DB /* Build configuration list for PBXNativeTarget "SwiftWebSocket-tvOS" */;
216 | 			buildPhases = (
217 | 				03957A371C5A71DB005CC1DB /* Sources */,
218 | 				03957A381C5A71DB005CC1DB /* Frameworks */,
219 | 				03957A391C5A71DB005CC1DB /* Headers */,
220 | 				03957A3A1C5A71DB005CC1DB /* Resources */,
221 | 			);
222 | 			buildRules = (
223 | 			);
224 | 			dependencies = (
225 | 			);
226 | 			name = "SwiftWebSocket-tvOS";
227 | 			productName = "SwiftWebSocket-tvOS";
228 | 			productReference = 03957A3C1C5A71DB005CC1DB /* SwiftWebSocket.framework */;
229 | 			productType = "com.apple.product-type.framework";
230 | 		};
231 | 		03D1E7021B20897100AC49AC /* SwiftWebSocket-iOS */ = {
232 | 			isa = PBXNativeTarget;
233 | 			buildConfigurationList = 03D1E7191B20897100AC49AC /* Build configuration list for PBXNativeTarget "SwiftWebSocket-iOS" */;
234 | 			buildPhases = (
235 | 				03D1E6FE1B20897100AC49AC /* Sources */,
236 | 				03D1E6FF1B20897100AC49AC /* Frameworks */,
237 | 				03D1E7001B20897100AC49AC /* Headers */,
238 | 				03D1E7011B20897100AC49AC /* Resources */,
239 | 			);
240 | 			buildRules = (
241 | 			);
242 | 			dependencies = (
243 | 			);
244 | 			name = "SwiftWebSocket-iOS";
245 | 			productName = SwiftWebSocket;
246 | 			productReference = 03D1E7031B20897100AC49AC /* SwiftWebSocket.framework */;
247 | 			productType = "com.apple.product-type.framework";
248 | 		};
249 | 		03D70CCB1BDAC5EC00D245C3 /* Test-OSX */ = {
250 | 			isa = PBXNativeTarget;
251 | 			buildConfigurationList = 03D70CD61BDAC5EC00D245C3 /* Build configuration list for PBXNativeTarget "Test-OSX" */;
252 | 			buildPhases = (
253 | 				03D70CC81BDAC5EC00D245C3 /* Sources */,
254 | 				03D70CC91BDAC5EC00D245C3 /* Frameworks */,
255 | 				03D70CCA1BDAC5EC00D245C3 /* Resources */,
256 | 			);
257 | 			buildRules = (
258 | 			);
259 | 			dependencies = (
260 | 				03D70CD31BDAC5EC00D245C3 /* PBXTargetDependency */,
261 | 			);
262 | 			name = "Test-OSX";
263 | 			productName = "Test-OSX";
264 | 			productReference = 03D70CCC1BDAC5EC00D245C3 /* Test-OSX.xctest */;
265 | 			productType = "com.apple.product-type.bundle.unit-test";
266 | 		};
267 | 		03D70CDA1BDAC63600D245C3 /* Test-iOS */ = {
268 | 			isa = PBXNativeTarget;
269 | 			buildConfigurationList = 03D70CE31BDAC63600D245C3 /* Build configuration list for PBXNativeTarget "Test-iOS" */;
270 | 			buildPhases = (
271 | 				03D70CD71BDAC63600D245C3 /* Sources */,
272 | 				03D70CD81BDAC63600D245C3 /* Frameworks */,
273 | 				03D70CD91BDAC63600D245C3 /* Resources */,
274 | 			);
275 | 			buildRules = (
276 | 			);
277 | 			dependencies = (
278 | 				03D70CE21BDAC63600D245C3 /* PBXTargetDependency */,
279 | 			);
280 | 			name = "Test-iOS";
281 | 			productName = "Test-iOS";
282 | 			productReference = 03D70CDB1BDAC63600D245C3 /* Test-iOS.xctest */;
283 | 			productType = "com.apple.product-type.bundle.unit-test";
284 | 		};
285 | 		D71949001B35E6130015C529 /* SwiftWebSocket-OSX */ = {
286 | 			isa = PBXNativeTarget;
287 | 			buildConfigurationList = D71949141B35E6160015C529 /* Build configuration list for PBXNativeTarget "SwiftWebSocket-OSX" */;
288 | 			buildPhases = (
289 | 				D71948FC1B35E6130015C529 /* Sources */,
290 | 				D71948FD1B35E6130015C529 /* Frameworks */,
291 | 				D71948FE1B35E6130015C529 /* Headers */,
292 | 				D71948FF1B35E6130015C529 /* Resources */,
293 | 			);
294 | 			buildRules = (
295 | 			);
296 | 			dependencies = (
297 | 			);
298 | 			name = "SwiftWebSocket-OSX";
299 | 			productName = "SwiftWebSocket-OSX";
300 | 			productReference = D71949011B35E6130015C529 /* SwiftWebSocket.framework */;
301 | 			productType = "com.apple.product-type.framework";
302 | 		};
303 | 		D78C402F1C232C3800EB72AA /* Test-ObjectiveC */ = {
304 | 			isa = PBXNativeTarget;
305 | 			buildConfigurationList = D78C403A1C232C3800EB72AA /* Build configuration list for PBXNativeTarget "Test-ObjectiveC" */;
306 | 			buildPhases = (
307 | 				D78C402C1C232C3800EB72AA /* Sources */,
308 | 				D78C402D1C232C3800EB72AA /* Frameworks */,
309 | 				D78C402E1C232C3800EB72AA /* Resources */,
310 | 			);
311 | 			buildRules = (
312 | 			);
313 | 			dependencies = (
314 | 				D78C40371C232C3800EB72AA /* PBXTargetDependency */,
315 | 			);
316 | 			name = "Test-ObjectiveC";
317 | 			productName = "Test-ObjectiveC";
318 | 			productReference = D78C40301C232C3800EB72AA /* Test-ObjectiveC.xctest */;
319 | 			productType = "com.apple.product-type.bundle.unit-test";
320 | 		};
321 | /* End PBXNativeTarget section */
322 | 
323 | /* Begin PBXProject section */
324 | 		03D1E6FA1B20897100AC49AC /* Project object */ = {
325 | 			isa = PBXProject;
326 | 			attributes = {
327 | 				LastSwiftUpdateCheck = 0730;
328 | 				LastUpgradeCheck = 1020;
329 | 				ORGANIZATIONNAME = "ONcast, LLC";
330 | 				TargetAttributes = {
331 | 					03957A3B1C5A71DB005CC1DB = {
332 | 						CreatedOnToolsVersion = 7.2;
333 | 					};
334 | 					03D1E7021B20897100AC49AC = {
335 | 						CreatedOnToolsVersion = 6.3.2;
336 | 						LastSwiftMigration = 1020;
337 | 					};
338 | 					03D70CCB1BDAC5EC00D245C3 = {
339 | 						CreatedOnToolsVersion = 7.1;
340 | 					};
341 | 					03D70CDA1BDAC63600D245C3 = {
342 | 						CreatedOnToolsVersion = 7.1;
343 | 					};
344 | 					D71949001B35E6130015C529 = {
345 | 						CreatedOnToolsVersion = 6.3.2;
346 | 					};
347 | 					D78C402F1C232C3800EB72AA = {
348 | 						CreatedOnToolsVersion = 7.1.1;
349 | 						LastSwiftMigration = 0900;
350 | 					};
351 | 				};
352 | 			};
353 | 			buildConfigurationList = 03D1E6FD1B20897100AC49AC /* Build configuration list for PBXProject "SwiftWebSocket" */;
354 | 			compatibilityVersion = "Xcode 3.2";
355 | 			developmentRegion = en;
356 | 			hasScannedForEncodings = 0;
357 | 			knownRegions = (
358 | 				en,
359 | 				Base,
360 | 			);
361 | 			mainGroup = 03D1E6F91B20897100AC49AC;
362 | 			productRefGroup = 03D1E7041B20897100AC49AC /* Products */;
363 | 			projectDirPath = "";
364 | 			projectRoot = "";
365 | 			targets = (
366 | 				03D1E7021B20897100AC49AC /* SwiftWebSocket-iOS */,
367 | 				D71949001B35E6130015C529 /* SwiftWebSocket-OSX */,
368 | 				03957A3B1C5A71DB005CC1DB /* SwiftWebSocket-tvOS */,
369 | 				03D70CCB1BDAC5EC00D245C3 /* Test-OSX */,
370 | 				03D70CDA1BDAC63600D245C3 /* Test-iOS */,
371 | 				D78C402F1C232C3800EB72AA /* Test-ObjectiveC */,
372 | 			);
373 | 		};
374 | /* End PBXProject section */
375 | 
376 | /* Begin PBXResourcesBuildPhase section */
377 | 		03957A3A1C5A71DB005CC1DB /* Resources */ = {
378 | 			isa = PBXResourcesBuildPhase;
379 | 			buildActionMask = 2147483647;
380 | 			files = (
381 | 			);
382 | 			runOnlyForDeploymentPostprocessing = 0;
383 | 		};
384 | 		03D1E7011B20897100AC49AC /* Resources */ = {
385 | 			isa = PBXResourcesBuildPhase;
386 | 			buildActionMask = 2147483647;
387 | 			files = (
388 | 			);
389 | 			runOnlyForDeploymentPostprocessing = 0;
390 | 		};
391 | 		03D70CCA1BDAC5EC00D245C3 /* Resources */ = {
392 | 			isa = PBXResourcesBuildPhase;
393 | 			buildActionMask = 2147483647;
394 | 			files = (
395 | 			);
396 | 			runOnlyForDeploymentPostprocessing = 0;
397 | 		};
398 | 		03D70CD91BDAC63600D245C3 /* Resources */ = {
399 | 			isa = PBXResourcesBuildPhase;
400 | 			buildActionMask = 2147483647;
401 | 			files = (
402 | 			);
403 | 			runOnlyForDeploymentPostprocessing = 0;
404 | 		};
405 | 		D71948FF1B35E6130015C529 /* Resources */ = {
406 | 			isa = PBXResourcesBuildPhase;
407 | 			buildActionMask = 2147483647;
408 | 			files = (
409 | 			);
410 | 			runOnlyForDeploymentPostprocessing = 0;
411 | 		};
412 | 		D78C402E1C232C3800EB72AA /* Resources */ = {
413 | 			isa = PBXResourcesBuildPhase;
414 | 			buildActionMask = 2147483647;
415 | 			files = (
416 | 			);
417 | 			runOnlyForDeploymentPostprocessing = 0;
418 | 		};
419 | /* End PBXResourcesBuildPhase section */
420 | 
421 | /* Begin PBXSourcesBuildPhase section */
422 | 		03957A371C5A71DB005CC1DB /* Sources */ = {
423 | 			isa = PBXSourcesBuildPhase;
424 | 			buildActionMask = 2147483647;
425 | 			files = (
426 | 				03957A471C5A7392005CC1DB /* WebSocket.swift in Sources */,
427 | 			);
428 | 			runOnlyForDeploymentPostprocessing = 0;
429 | 		};
430 | 		03D1E6FE1B20897100AC49AC /* Sources */ = {
431 | 			isa = PBXSourcesBuildPhase;
432 | 			buildActionMask = 2147483647;
433 | 			files = (
434 | 				03285D2B1B4A9F1A0078A1AA /* WebSocket.swift in Sources */,
435 | 			);
436 | 			runOnlyForDeploymentPostprocessing = 0;
437 | 		};
438 | 		03D70CC81BDAC5EC00D245C3 /* Sources */ = {
439 | 			isa = PBXSourcesBuildPhase;
440 | 			buildActionMask = 2147483647;
441 | 			files = (
442 | 				03D70CEC1BDAC70B00D245C3 /* Test.swift in Sources */,
443 | 			);
444 | 			runOnlyForDeploymentPostprocessing = 0;
445 | 		};
446 | 		03D70CD71BDAC63600D245C3 /* Sources */ = {
447 | 			isa = PBXSourcesBuildPhase;
448 | 			buildActionMask = 2147483647;
449 | 			files = (
450 | 				03D70CED1BDAC70C00D245C3 /* Test.swift in Sources */,
451 | 			);
452 | 			runOnlyForDeploymentPostprocessing = 0;
453 | 		};
454 | 		D71948FC1B35E6130015C529 /* Sources */ = {
455 | 			isa = PBXSourcesBuildPhase;
456 | 			buildActionMask = 2147483647;
457 | 			files = (
458 | 				031967101B4D96C40033860E /* WebSocket.swift in Sources */,
459 | 			);
460 | 			runOnlyForDeploymentPostprocessing = 0;
461 | 		};
462 | 		D78C402C1C232C3800EB72AA /* Sources */ = {
463 | 			isa = PBXSourcesBuildPhase;
464 | 			buildActionMask = 2147483647;
465 | 			files = (
466 | 				D78C40331C232C3800EB72AA /* Test_ObjectiveC.m in Sources */,
467 | 				D734D9EC1C232E6A00437555 /* Connection.m in Sources */,
468 | 			);
469 | 			runOnlyForDeploymentPostprocessing = 0;
470 | 		};
471 | /* End PBXSourcesBuildPhase section */
472 | 
473 | /* Begin PBXTargetDependency section */
474 | 		03D70CD31BDAC5EC00D245C3 /* PBXTargetDependency */ = {
475 | 			isa = PBXTargetDependency;
476 | 			target = D71949001B35E6130015C529 /* SwiftWebSocket-OSX */;
477 | 			targetProxy = 03D70CD21BDAC5EC00D245C3 /* PBXContainerItemProxy */;
478 | 		};
479 | 		03D70CE21BDAC63600D245C3 /* PBXTargetDependency */ = {
480 | 			isa = PBXTargetDependency;
481 | 			target = 03D1E7021B20897100AC49AC /* SwiftWebSocket-iOS */;
482 | 			targetProxy = 03D70CE11BDAC63600D245C3 /* PBXContainerItemProxy */;
483 | 		};
484 | 		D78C40371C232C3800EB72AA /* PBXTargetDependency */ = {
485 | 			isa = PBXTargetDependency;
486 | 			target = 03D1E7021B20897100AC49AC /* SwiftWebSocket-iOS */;
487 | 			targetProxy = D78C40361C232C3800EB72AA /* PBXContainerItemProxy */;
488 | 		};
489 | /* End PBXTargetDependency section */
490 | 
491 | /* Begin XCBuildConfiguration section */
492 | 		03957A421C5A71DC005CC1DB /* Debug */ = {
493 | 			isa = XCBuildConfiguration;
494 | 			buildSettings = {
495 | 				BITCODE_GENERATION_MODE = bitcode;
496 | 				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
497 | 				DEBUG_INFORMATION_FORMAT = dwarf;
498 | 				DEFINES_MODULE = YES;
499 | 				DYLIB_COMPATIBILITY_VERSION = 1;
500 | 				DYLIB_CURRENT_VERSION = 1;
501 | 				DYLIB_INSTALL_NAME_BASE = "@rpath";
502 | 				ENABLE_BITCODE = YES;
503 | 				INFOPLIST_FILE = "Source/Info-tvOS.plist";
504 | 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
505 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
506 | 				PRODUCT_BUNDLE_IDENTIFIER = com.oncast.SwiftWebSocket;
507 | 				PRODUCT_NAME = SwiftWebSocket;
508 | 				SDKROOT = appletvos;
509 | 				SKIP_INSTALL = YES;
510 | 				TARGETED_DEVICE_FAMILY = 3;
511 | 				TVOS_DEPLOYMENT_TARGET = 9.1;
512 | 			};
513 | 			name = Debug;
514 | 		};
515 | 		03957A431C5A71DC005CC1DB /* Release */ = {
516 | 			isa = XCBuildConfiguration;
517 | 			buildSettings = {
518 | 				BITCODE_GENERATION_MODE = bitcode;
519 | 				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
520 | 				DEFINES_MODULE = YES;
521 | 				DYLIB_COMPATIBILITY_VERSION = 1;
522 | 				DYLIB_CURRENT_VERSION = 1;
523 | 				DYLIB_INSTALL_NAME_BASE = "@rpath";
524 | 				ENABLE_BITCODE = YES;
525 | 				INFOPLIST_FILE = "Source/Info-tvOS.plist";
526 | 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
527 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
528 | 				PRODUCT_BUNDLE_IDENTIFIER = com.oncast.SwiftWebSocket;
529 | 				PRODUCT_NAME = SwiftWebSocket;
530 | 				SDKROOT = appletvos;
531 | 				SKIP_INSTALL = YES;
532 | 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
533 | 				TARGETED_DEVICE_FAMILY = 3;
534 | 				TVOS_DEPLOYMENT_TARGET = 9.1;
535 | 			};
536 | 			name = Release;
537 | 		};
538 | 		03D1E7171B20897100AC49AC /* Debug */ = {
539 | 			isa = XCBuildConfiguration;
540 | 			buildSettings = {
541 | 				ALWAYS_SEARCH_USER_PATHS = NO;
542 | 				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
543 | 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
544 | 				CLANG_CXX_LIBRARY = "libc++";
545 | 				CLANG_ENABLE_MODULES = YES;
546 | 				CLANG_ENABLE_OBJC_ARC = YES;
547 | 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
548 | 				CLANG_WARN_BOOL_CONVERSION = YES;
549 | 				CLANG_WARN_COMMA = YES;
550 | 				CLANG_WARN_CONSTANT_CONVERSION = YES;
551 | 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
552 | 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
553 | 				CLANG_WARN_EMPTY_BODY = YES;
554 | 				CLANG_WARN_ENUM_CONVERSION = YES;
555 | 				CLANG_WARN_INFINITE_RECURSION = YES;
556 | 				CLANG_WARN_INT_CONVERSION = YES;
557 | 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
558 | 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
559 | 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
560 | 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
561 | 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
562 | 				CLANG_WARN_STRICT_PROTOTYPES = YES;
563 | 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
564 | 				CLANG_WARN_UNREACHABLE_CODE = YES;
565 | 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
566 | 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
567 | 				COPY_PHASE_STRIP = NO;
568 | 				CURRENT_PROJECT_VERSION = 1;
569 | 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
570 | 				ENABLE_STRICT_OBJC_MSGSEND = YES;
571 | 				ENABLE_TESTABILITY = YES;
572 | 				GCC_C_LANGUAGE_STANDARD = gnu99;
573 | 				GCC_DYNAMIC_NO_PIC = NO;
574 | 				GCC_NO_COMMON_BLOCKS = YES;
575 | 				GCC_OPTIMIZATION_LEVEL = 0;
576 | 				GCC_PREPROCESSOR_DEFINITIONS = (
577 | 					"DEBUG=1",
578 | 					"$(inherited)",
579 | 				);
580 | 				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
581 | 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
582 | 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
583 | 				GCC_WARN_UNDECLARED_SELECTOR = YES;
584 | 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
585 | 				GCC_WARN_UNUSED_FUNCTION = YES;
586 | 				GCC_WARN_UNUSED_VARIABLE = YES;
587 | 				IPHONEOS_DEPLOYMENT_TARGET = 8.3;
588 | 				MTL_ENABLE_DEBUG_INFO = YES;
589 | 				ONLY_ACTIVE_ARCH = YES;
590 | 				SDKROOT = iphoneos;
591 | 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
592 | 				SWIFT_VERSION = 3.0;
593 | 				TARGETED_DEVICE_FAMILY = "1,2";
594 | 				VERSIONING_SYSTEM = "apple-generic";
595 | 				VERSION_INFO_PREFIX = "";
596 | 			};
597 | 			name = Debug;
598 | 		};
599 | 		03D1E7181B20897100AC49AC /* Release */ = {
600 | 			isa = XCBuildConfiguration;
601 | 			buildSettings = {
602 | 				ALWAYS_SEARCH_USER_PATHS = NO;
603 | 				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
604 | 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
605 | 				CLANG_CXX_LIBRARY = "libc++";
606 | 				CLANG_ENABLE_MODULES = YES;
607 | 				CLANG_ENABLE_OBJC_ARC = YES;
608 | 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
609 | 				CLANG_WARN_BOOL_CONVERSION = YES;
610 | 				CLANG_WARN_COMMA = YES;
611 | 				CLANG_WARN_CONSTANT_CONVERSION = YES;
612 | 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
613 | 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
614 | 				CLANG_WARN_EMPTY_BODY = YES;
615 | 				CLANG_WARN_ENUM_CONVERSION = YES;
616 | 				CLANG_WARN_INFINITE_RECURSION = YES;
617 | 				CLANG_WARN_INT_CONVERSION = YES;
618 | 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
619 | 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
620 | 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
621 | 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
622 | 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
623 | 				CLANG_WARN_STRICT_PROTOTYPES = YES;
624 | 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
625 | 				CLANG_WARN_UNREACHABLE_CODE = YES;
626 | 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
627 | 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
628 | 				COPY_PHASE_STRIP = NO;
629 | 				CURRENT_PROJECT_VERSION = 1;
630 | 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
631 | 				ENABLE_NS_ASSERTIONS = NO;
632 | 				ENABLE_STRICT_OBJC_MSGSEND = YES;
633 | 				GCC_C_LANGUAGE_STANDARD = gnu99;
634 | 				GCC_NO_COMMON_BLOCKS = YES;
635 | 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
636 | 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
637 | 				GCC_WARN_UNDECLARED_SELECTOR = YES;
638 | 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
639 | 				GCC_WARN_UNUSED_FUNCTION = YES;
640 | 				GCC_WARN_UNUSED_VARIABLE = YES;
641 | 				IPHONEOS_DEPLOYMENT_TARGET = 8.3;
642 | 				MTL_ENABLE_DEBUG_INFO = NO;
643 | 				SDKROOT = iphoneos;
644 | 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
645 | 				SWIFT_VERSION = 3.0;
646 | 				TARGETED_DEVICE_FAMILY = "1,2";
647 | 				VALIDATE_PRODUCT = YES;
648 | 				VERSIONING_SYSTEM = "apple-generic";
649 | 				VERSION_INFO_PREFIX = "";
650 | 			};
651 | 			name = Release;
652 | 		};
653 | 		03D1E71A1B20897100AC49AC /* Debug */ = {
654 | 			isa = XCBuildConfiguration;
655 | 			buildSettings = {
656 | 				APPLICATION_EXTENSION_API_ONLY = YES;
657 | 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
658 | 				DEFINES_MODULE = YES;
659 | 				DYLIB_COMPATIBILITY_VERSION = 1;
660 | 				DYLIB_CURRENT_VERSION = 1;
661 | 				DYLIB_INSTALL_NAME_BASE = "@rpath";
662 | 				INFOPLIST_FILE = Source/Info.plist;
663 | 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
664 | 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
665 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
666 | 				PRODUCT_BUNDLE_IDENTIFIER = "com.oncast.$(PRODUCT_NAME:rfc1034identifier)";
667 | 				PRODUCT_NAME = SwiftWebSocket;
668 | 				SKIP_INSTALL = YES;
669 | 				SWIFT_VERSION = 5.0;
670 | 			};
671 | 			name = Debug;
672 | 		};
673 | 		03D1E71B1B20897100AC49AC /* Release */ = {
674 | 			isa = XCBuildConfiguration;
675 | 			buildSettings = {
676 | 				APPLICATION_EXTENSION_API_ONLY = YES;
677 | 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
678 | 				DEFINES_MODULE = YES;
679 | 				DYLIB_COMPATIBILITY_VERSION = 1;
680 | 				DYLIB_CURRENT_VERSION = 1;
681 | 				DYLIB_INSTALL_NAME_BASE = "@rpath";
682 | 				INFOPLIST_FILE = Source/Info.plist;
683 | 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
684 | 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
685 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
686 | 				PRODUCT_BUNDLE_IDENTIFIER = "com.oncast.$(PRODUCT_NAME:rfc1034identifier)";
687 | 				PRODUCT_NAME = SwiftWebSocket;
688 | 				SKIP_INSTALL = YES;
689 | 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
690 | 				SWIFT_VERSION = 5.0;
691 | 			};
692 | 			name = Release;
693 | 		};
694 | 		03D70CD41BDAC5EC00D245C3 /* Debug */ = {
695 | 			isa = XCBuildConfiguration;
696 | 			buildSettings = {
697 | 				CODE_SIGN_IDENTITY = "-";
698 | 				COMBINE_HIDPI_IMAGES = YES;
699 | 				DEBUG_INFORMATION_FORMAT = dwarf;
700 | 				INFOPLIST_FILE = Test/Info.plist;
701 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
702 | 				MACOSX_DEPLOYMENT_TARGET = 10.11;
703 | 				PRODUCT_BUNDLE_IDENTIFIER = com.oncast.SwiftWebSocketTest;
704 | 				PRODUCT_NAME = "$(TARGET_NAME)";
705 | 				SDKROOT = macosx;
706 | 			};
707 | 			name = Debug;
708 | 		};
709 | 		03D70CD51BDAC5EC00D245C3 /* Release */ = {
710 | 			isa = XCBuildConfiguration;
711 | 			buildSettings = {
712 | 				CODE_SIGN_IDENTITY = "-";
713 | 				COMBINE_HIDPI_IMAGES = YES;
714 | 				INFOPLIST_FILE = Test/Info.plist;
715 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
716 | 				MACOSX_DEPLOYMENT_TARGET = 10.11;
717 | 				PRODUCT_BUNDLE_IDENTIFIER = com.oncast.SwiftWebSocketTest;
718 | 				PRODUCT_NAME = "$(TARGET_NAME)";
719 | 				SDKROOT = macosx;
720 | 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
721 | 			};
722 | 			name = Release;
723 | 		};
724 | 		03D70CE41BDAC63600D245C3 /* Debug */ = {
725 | 			isa = XCBuildConfiguration;
726 | 			buildSettings = {
727 | 				DEBUG_INFORMATION_FORMAT = dwarf;
728 | 				INFOPLIST_FILE = Test/Info.plist;
729 | 				IPHONEOS_DEPLOYMENT_TARGET = 9.1;
730 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
731 | 				PRODUCT_BUNDLE_IDENTIFIER = com.oncast.SwiftWebSocketTest;
732 | 				PRODUCT_NAME = "$(TARGET_NAME)";
733 | 			};
734 | 			name = Debug;
735 | 		};
736 | 		03D70CE51BDAC63600D245C3 /* Release */ = {
737 | 			isa = XCBuildConfiguration;
738 | 			buildSettings = {
739 | 				INFOPLIST_FILE = Test/Info.plist;
740 | 				IPHONEOS_DEPLOYMENT_TARGET = 9.1;
741 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
742 | 				PRODUCT_BUNDLE_IDENTIFIER = com.oncast.SwiftWebSocketTest;
743 | 				PRODUCT_NAME = "$(TARGET_NAME)";
744 | 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
745 | 			};
746 | 			name = Release;
747 | 		};
748 | 		D71949151B35E6160015C529 /* Debug */ = {
749 | 			isa = XCBuildConfiguration;
750 | 			buildSettings = {
751 | 				APPLICATION_EXTENSION_API_ONLY = YES;
752 | 				COMBINE_HIDPI_IMAGES = YES;
753 | 				DEBUG_INFORMATION_FORMAT = dwarf;
754 | 				DEFINES_MODULE = YES;
755 | 				DYLIB_COMPATIBILITY_VERSION = 1;
756 | 				DYLIB_CURRENT_VERSION = 1;
757 | 				DYLIB_INSTALL_NAME_BASE = "@rpath";
758 | 				FRAMEWORK_VERSION = A;
759 | 				GCC_PREPROCESSOR_DEFINITIONS = (
760 | 					"DEBUG=1",
761 | 					"$(inherited)",
762 | 				);
763 | 				INFOPLIST_FILE = Source/Info.plist;
764 | 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
765 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
766 | 				MACOSX_DEPLOYMENT_TARGET = 10.10;
767 | 				PRODUCT_BUNDLE_IDENTIFIER = "com.oncast.$(PRODUCT_NAME:rfc1034identifier)";
768 | 				PRODUCT_NAME = SwiftWebSocket;
769 | 				SDKROOT = macosx;
770 | 				SKIP_INSTALL = YES;
771 | 				SWIFT_VERSION = 3.0;
772 | 				VALID_ARCHS = x86_64;
773 | 			};
774 | 			name = Debug;
775 | 		};
776 | 		D71949161B35E6160015C529 /* Release */ = {
777 | 			isa = XCBuildConfiguration;
778 | 			buildSettings = {
779 | 				APPLICATION_EXTENSION_API_ONLY = YES;
780 | 				COMBINE_HIDPI_IMAGES = YES;
781 | 				DEFINES_MODULE = YES;
782 | 				DYLIB_COMPATIBILITY_VERSION = 1;
783 | 				DYLIB_CURRENT_VERSION = 1;
784 | 				DYLIB_INSTALL_NAME_BASE = "@rpath";
785 | 				FRAMEWORK_VERSION = A;
786 | 				INFOPLIST_FILE = Source/Info.plist;
787 | 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
788 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
789 | 				MACOSX_DEPLOYMENT_TARGET = 10.10;
790 | 				PRODUCT_BUNDLE_IDENTIFIER = "com.oncast.$(PRODUCT_NAME:rfc1034identifier)";
791 | 				PRODUCT_NAME = SwiftWebSocket;
792 | 				SDKROOT = macosx;
793 | 				SKIP_INSTALL = YES;
794 | 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
795 | 				SWIFT_VERSION = 3.0;
796 | 				VALID_ARCHS = x86_64;
797 | 			};
798 | 			name = Release;
799 | 		};
800 | 		D78C40381C232C3800EB72AA /* Debug */ = {
801 | 			isa = XCBuildConfiguration;
802 | 			buildSettings = {
803 | 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
804 | 				DEBUG_INFORMATION_FORMAT = dwarf;
805 | 				INFOPLIST_FILE = "Test-ObjectiveC/Info.plist";
806 | 				IPHONEOS_DEPLOYMENT_TARGET = 9.1;
807 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
808 | 				PRODUCT_BUNDLE_IDENTIFIER = "com.oncast.Test-ObjectiveC";
809 | 				PRODUCT_NAME = "$(TARGET_NAME)";
810 | 				SWIFT_SWIFT3_OBJC_INFERENCE = On;
811 | 				SWIFT_VERSION = 4.0;
812 | 			};
813 | 			name = Debug;
814 | 		};
815 | 		D78C40391C232C3800EB72AA /* Release */ = {
816 | 			isa = XCBuildConfiguration;
817 | 			buildSettings = {
818 | 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
819 | 				INFOPLIST_FILE = "Test-ObjectiveC/Info.plist";
820 | 				IPHONEOS_DEPLOYMENT_TARGET = 9.1;
821 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
822 | 				PRODUCT_BUNDLE_IDENTIFIER = "com.oncast.Test-ObjectiveC";
823 | 				PRODUCT_NAME = "$(TARGET_NAME)";
824 | 				SWIFT_SWIFT3_OBJC_INFERENCE = On;
825 | 				SWIFT_VERSION = 4.0;
826 | 			};
827 | 			name = Release;
828 | 		};
829 | /* End XCBuildConfiguration section */
830 | 
831 | /* Begin XCConfigurationList section */
832 | 		03957A411C5A71DC005CC1DB /* Build configuration list for PBXNativeTarget "SwiftWebSocket-tvOS" */ = {
833 | 			isa = XCConfigurationList;
834 | 			buildConfigurations = (
835 | 				03957A421C5A71DC005CC1DB /* Debug */,
836 | 				03957A431C5A71DC005CC1DB /* Release */,
837 | 			);
838 | 			defaultConfigurationIsVisible = 0;
839 | 			defaultConfigurationName = Release;
840 | 		};
841 | 		03D1E6FD1B20897100AC49AC /* Build configuration list for PBXProject "SwiftWebSocket" */ = {
842 | 			isa = XCConfigurationList;
843 | 			buildConfigurations = (
844 | 				03D1E7171B20897100AC49AC /* Debug */,
845 | 				03D1E7181B20897100AC49AC /* Release */,
846 | 			);
847 | 			defaultConfigurationIsVisible = 0;
848 | 			defaultConfigurationName = Release;
849 | 		};
850 | 		03D1E7191B20897100AC49AC /* Build configuration list for PBXNativeTarget "SwiftWebSocket-iOS" */ = {
851 | 			isa = XCConfigurationList;
852 | 			buildConfigurations = (
853 | 				03D1E71A1B20897100AC49AC /* Debug */,
854 | 				03D1E71B1B20897100AC49AC /* Release */,
855 | 			);
856 | 			defaultConfigurationIsVisible = 0;
857 | 			defaultConfigurationName = Release;
858 | 		};
859 | 		03D70CD61BDAC5EC00D245C3 /* Build configuration list for PBXNativeTarget "Test-OSX" */ = {
860 | 			isa = XCConfigurationList;
861 | 			buildConfigurations = (
862 | 				03D70CD41BDAC5EC00D245C3 /* Debug */,
863 | 				03D70CD51BDAC5EC00D245C3 /* Release */,
864 | 			);
865 | 			defaultConfigurationIsVisible = 0;
866 | 			defaultConfigurationName = Release;
867 | 		};
868 | 		03D70CE31BDAC63600D245C3 /* Build configuration list for PBXNativeTarget "Test-iOS" */ = {
869 | 			isa = XCConfigurationList;
870 | 			buildConfigurations = (
871 | 				03D70CE41BDAC63600D245C3 /* Debug */,
872 | 				03D70CE51BDAC63600D245C3 /* Release */,
873 | 			);
874 | 			defaultConfigurationIsVisible = 0;
875 | 			defaultConfigurationName = Release;
876 | 		};
877 | 		D71949141B35E6160015C529 /* Build configuration list for PBXNativeTarget "SwiftWebSocket-OSX" */ = {
878 | 			isa = XCConfigurationList;
879 | 			buildConfigurations = (
880 | 				D71949151B35E6160015C529 /* Debug */,
881 | 				D71949161B35E6160015C529 /* Release */,
882 | 			);
883 | 			defaultConfigurationIsVisible = 0;
884 | 			defaultConfigurationName = Release;
885 | 		};
886 | 		D78C403A1C232C3800EB72AA /* Build configuration list for PBXNativeTarget "Test-ObjectiveC" */ = {
887 | 			isa = XCConfigurationList;
888 | 			buildConfigurations = (
889 | 				D78C40381C232C3800EB72AA /* Debug */,
890 | 				D78C40391C232C3800EB72AA /* Release */,
891 | 			);
892 | 			defaultConfigurationIsVisible = 0;
893 | 			defaultConfigurationName = Release;
894 | 		};
895 | /* End XCConfigurationList section */
896 | 	};
897 | 	rootObject = 03D1E6FA1B20897100AC49AC /* Project object */;
898 | }
899 | 


--------------------------------------------------------------------------------
/Source/WebSocket.swift:
--------------------------------------------------------------------------------
   1 | /*
   2 | * SwiftWebSocket (websocket.swift)
   3 | *
   4 | * Copyright (C) Josh Baker. All Rights Reserved.
   5 | * Contact: @tidwall, joshbaker77@gmail.com
   6 | *
   7 | * This software may be modified and distributed under the terms
   8 | * of the MIT license.  See the LICENSE file for details.
   9 | *
  10 | */
  11 | 
  12 | import Foundation
  13 | 
  14 | private let windowBufferSize = 0x2000
  15 | 
  16 | private class Payload {
  17 |     var ptr : UnsafeMutableRawPointer
  18 |     var cap : Int
  19 |     var len : Int
  20 |     init(){
  21 |         len = 0
  22 |         cap = windowBufferSize
  23 |         ptr = malloc(cap)
  24 |     }
  25 |     deinit{
  26 |         free(ptr)
  27 |     }
  28 |     var count : Int {
  29 |         get {
  30 |             return len
  31 |         }
  32 |         set {
  33 |             if newValue > cap {
  34 |                 while cap < newValue {
  35 |                     cap *= 2
  36 |                 }
  37 |                 ptr = realloc(ptr, cap)
  38 |             }
  39 |             len = newValue
  40 |         }
  41 |     }
  42 |     func append(_ bytes: UnsafePointer, length: Int){
  43 |         let prevLen = len
  44 |         count = len+length
  45 |         memcpy(ptr+prevLen, bytes, length)
  46 |     }
  47 |     var array : [UInt8] {
  48 |         get {
  49 |             var array = [UInt8](repeating: 0, count: count)
  50 |             memcpy(&array, ptr, count)
  51 |             return array
  52 |         }
  53 |         set {
  54 |             count = 0
  55 |             append(newValue, length: newValue.count)
  56 |         }
  57 |     }
  58 |     var nsdata : Data {
  59 |         get {
  60 |             return Data(bytes: ptr.assumingMemoryBound(to: UInt8.self), count: count)
  61 |         }
  62 |         set {
  63 |             count = 0
  64 |             append((newValue as NSData).bytes.bindMemory(to: UInt8.self, capacity: newValue.count), length: newValue.count)
  65 |         }
  66 |     }
  67 |     var buffer : UnsafeBufferPointer {
  68 |         get {
  69 |             return UnsafeBufferPointer(start: ptr.assumingMemoryBound(to: UInt8.self), count: count)
  70 |         }
  71 |         set {
  72 |             count = 0
  73 |             append(newValue.baseAddress!, length: newValue.count)
  74 |         }
  75 |     }
  76 | }
  77 | 
  78 | private enum OpCode : UInt8, CustomStringConvertible {
  79 |     case `continue` = 0x0, text = 0x1, binary = 0x2, close = 0x8, ping = 0x9, pong = 0xA
  80 |     var isControl : Bool {
  81 |         switch self {
  82 |         case .close, .ping, .pong:
  83 |             return true
  84 |         default:
  85 |             return false
  86 |         }
  87 |     }
  88 |     var description : String {
  89 |         switch self {
  90 |         case .`continue`: return "Continue"
  91 |         case .text: return "Text"
  92 |         case .binary: return "Binary"
  93 |         case .close: return "Close"
  94 |         case .ping: return "Ping"
  95 |         case .pong: return "Pong"
  96 |         }
  97 |     }
  98 | }
  99 | 
 100 | /// The WebSocketEvents struct is used by the events property and manages the events for the WebSocket connection.
 101 | public struct WebSocketEvents {
 102 |     /// An event to be called when the WebSocket connection's readyState changes to .Open; this indicates that the connection is ready to send and receive data.
 103 |     public var open : ()->() = {}
 104 |     /// An event to be called when the WebSocket connection's readyState changes to .Closed.
 105 |     public var close : (_ code : Int, _ reason : String, _ wasClean : Bool)->() = {(code, reason, wasClean) in}
 106 |     /// An event to be called when an error occurs.
 107 |     public var error : (_ error : Error)->() = {(error) in}
 108 |     /// An event to be called when a message is received from the server.
 109 |     public var message : (_ data : Any)->() = {(data) in}
 110 |     /// An event to be called when a pong is received from the server.
 111 |     public var pong : (_ data : Any)->() = {(data) in}
 112 |     /// An event to be called when the WebSocket process has ended; this event is guarenteed to be called once and can be used as an alternative to the "close" or "error" events.
 113 |     public var end : (_ code : Int, _ reason : String, _ wasClean : Bool, _ error : Error?)->() = {(code, reason, wasClean, error) in}
 114 | }
 115 | 
 116 | /// The WebSocketBinaryType enum is used by the binaryType property and indicates the type of binary data being transmitted by the WebSocket connection.
 117 | public enum WebSocketBinaryType : CustomStringConvertible {
 118 |     /// The WebSocket should transmit [UInt8] objects.
 119 |     case uInt8Array
 120 |     /// The WebSocket should transmit NSData objects.
 121 |     case nsData
 122 |     /// The WebSocket should transmit UnsafeBufferPointer objects. This buffer is only valid during the scope of the message event. Use at your own risk.
 123 |     case uInt8UnsafeBufferPointer
 124 |     public var description : String {
 125 |         switch self {
 126 |         case .uInt8Array: return "UInt8Array"
 127 |         case .nsData: return "NSData"
 128 |         case .uInt8UnsafeBufferPointer: return "UInt8UnsafeBufferPointer"
 129 |         }
 130 |     }
 131 | }
 132 | 
 133 | /// The WebSocketReadyState enum is used by the readyState property to describe the status of the WebSocket connection.
 134 | @objc public enum WebSocketReadyState : Int, CustomStringConvertible {
 135 |     /// The connection is not yet open.
 136 |     case connecting = 0
 137 |     /// The connection is open and ready to communicate.
 138 |     case open = 1
 139 |     /// The connection is in the process of closing.
 140 |     case closing = 2
 141 |     /// The connection is closed or couldn't be opened.
 142 |     case closed = 3
 143 |     fileprivate var isClosed : Bool {
 144 |         switch self {
 145 |         case .closing, .closed:
 146 |             return true
 147 |         default:
 148 |             return false
 149 |         }
 150 |     }
 151 |     /// Returns a string that represents the ReadyState value.
 152 |     public var description : String {
 153 |         switch self {
 154 |         case .connecting: return "Connecting"
 155 |         case .open: return "Open"
 156 |         case .closing: return "Closing"
 157 |         case .closed: return "Closed"
 158 |         }
 159 |     }
 160 | }
 161 | 
 162 | private let defaultMaxWindowBits = 15
 163 | /// The WebSocketCompression struct is used by the compression property and manages the compression options for the WebSocket connection.
 164 | public struct WebSocketCompression {
 165 |     /// Used to accept compressed messages from the server. Default is true.
 166 |     public var on = false
 167 |     /// request no context takeover.
 168 |     public var noContextTakeover = false
 169 |     /// request max window bits.
 170 |     public var maxWindowBits = defaultMaxWindowBits
 171 | }
 172 | 
 173 | /// The WebSocketService options are used by the services property and manages the underlying socket services.
 174 | public struct WebSocketService :  OptionSet {
 175 |     public typealias RawValue = UInt
 176 |     var value: UInt = 0
 177 |     init(_ value: UInt) { self.value = value }
 178 |     public init(rawValue value: UInt) { self.value = value }
 179 |     public init(nilLiteral: ()) { self.value = 0 }
 180 |     public static var allZeros: WebSocketService { return self.init(0) }
 181 |     static func fromMask(_ raw: UInt) -> WebSocketService { return self.init(raw) }
 182 |     public var rawValue: UInt { return self.value }
 183 |     /// No services.
 184 |     public static var None: WebSocketService { return self.init(0) }
 185 |     /// Allow socket to handle VoIP.
 186 |     public static var VoIP: WebSocketService { return self.init(1 << 0) }
 187 |     /// Allow socket to handle video.
 188 |     public static var Video: WebSocketService { return self.init(1 << 1) }
 189 |     /// Allow socket to run in background.
 190 |     public static var Background: WebSocketService { return self.init(1 << 2) }
 191 |     /// Allow socket to handle voice.
 192 |     public static var Voice: WebSocketService { return self.init(1 << 3) }
 193 | }
 194 | 
 195 | private let atEndDetails = "streamStatus.atEnd"
 196 | private let timeoutDetails = "The operation couldn’t be completed. Operation timed out"
 197 | private let timeoutDuration : CFTimeInterval = 30
 198 | 
 199 | public enum WebSocketError : Error, CustomStringConvertible {
 200 |     case memory
 201 |     case needMoreInput
 202 |     case invalidHeader
 203 |     case invalidAddress
 204 |     case network(String)
 205 |     case libraryError(String)
 206 |     case payloadError(String)
 207 |     case protocolError(String)
 208 |     case invalidResponse(String)
 209 |     case invalidCompressionOptions(String)
 210 |     public var description : String {
 211 |         switch self {
 212 |         case .memory: return "Memory"
 213 |         case .needMoreInput: return "NeedMoreInput"
 214 |         case .invalidAddress: return "InvalidAddress"
 215 |         case .invalidHeader: return "InvalidHeader"
 216 |         case let .invalidResponse(details): return "InvalidResponse(\(details))"
 217 |         case let .invalidCompressionOptions(details): return "InvalidCompressionOptions(\(details))"
 218 |         case let .libraryError(details): return "LibraryError(\(details))"
 219 |         case let .protocolError(details): return "ProtocolError(\(details))"
 220 |         case let .payloadError(details): return "PayloadError(\(details))"
 221 |         case let .network(details): return "Network(\(details))"
 222 |         }
 223 |     }
 224 |     public var details : String {
 225 |         switch self {
 226 |         case .invalidResponse(let details): return details
 227 |         case .invalidCompressionOptions(let details): return details
 228 |         case .libraryError(let details): return details
 229 |         case .protocolError(let details): return details
 230 |         case .payloadError(let details): return details
 231 |         case .network(let details): return details
 232 |         default: return ""
 233 |         }
 234 |     }
 235 | }
 236 | 
 237 | private class UTF8 {
 238 |     var text : String = ""
 239 |     var count : UInt32 = 0          // number of bytes
 240 |     var procd : UInt32 = 0          // number of bytes processed
 241 |     var codepoint : UInt32 = 0      // the actual codepoint
 242 |     var bcount = 0
 243 |     init() { text = "" }
 244 |     func append(_ byte : UInt8) throws {
 245 |         if count == 0 {
 246 |             if byte <= 0x7F {
 247 |                 text.append(String(UnicodeScalar(byte)))
 248 |                 return
 249 |             }
 250 |             if byte == 0xC0 || byte == 0xC1 {
 251 |                 throw WebSocketError.payloadError("invalid codepoint: invalid byte")
 252 |             }
 253 |             if byte >> 5 & 0x7 == 0x6 {
 254 |                 count = 2
 255 |             } else if byte >> 4 & 0xF == 0xE {
 256 |                 count = 3
 257 |             } else if byte >> 3 & 0x1F == 0x1E {
 258 |                 count = 4
 259 |             } else {
 260 |                 throw WebSocketError.payloadError("invalid codepoint: frames")
 261 |             }
 262 |             procd = 1
 263 |             codepoint = (UInt32(byte) & (0xFF >> count)) << ((count-1) * 6)
 264 |             return
 265 |         }
 266 |         if byte >> 6 & 0x3 != 0x2 {
 267 |             throw WebSocketError.payloadError("invalid codepoint: signature")
 268 |         }
 269 |         codepoint += UInt32(byte & 0x3F) << ((count-procd-1) * 6)
 270 |         if codepoint > 0x10FFFF || (codepoint >= 0xD800 && codepoint <= 0xDFFF) {
 271 |             throw WebSocketError.payloadError("invalid codepoint: out of bounds")
 272 |         }
 273 |         procd += 1
 274 |         if procd == count {
 275 |             if codepoint <= 0x7FF && count > 2 {
 276 |                 throw WebSocketError.payloadError("invalid codepoint: overlong")
 277 |             }
 278 |             if codepoint <= 0xFFFF && count > 3 {
 279 |                 throw WebSocketError.payloadError("invalid codepoint: overlong")
 280 |             }
 281 |             procd = 0
 282 |             count = 0
 283 |             text.append(String.init(describing: UnicodeScalar(codepoint)!))
 284 |         }
 285 |         return
 286 |     }
 287 |     func append(_ bytes : UnsafePointer, length : Int) throws {
 288 |         if length == 0 {
 289 |             return
 290 |         }
 291 |         if count == 0 {
 292 |             var ascii = true
 293 |             for i in 0 ..< length {
 294 |                 if bytes[i] > 0x7F {
 295 |                     ascii = false
 296 |                     break
 297 |                 }
 298 |             }
 299 |             if ascii {
 300 |                 text += NSString(bytes: bytes, length: length, encoding: String.Encoding.ascii.rawValue)! as String
 301 |                 bcount += length
 302 |                 return
 303 |             }
 304 |         }
 305 |         for i in 0 ..< length {
 306 |             try append(bytes[i])
 307 |         }
 308 |         bcount += length
 309 |     }
 310 |     var completed : Bool {
 311 |         return count == 0
 312 |     }
 313 |     static func bytes(_ string : String) -> [UInt8]{
 314 |         let data = string.data(using: String.Encoding.utf8)!
 315 |         return [UInt8](UnsafeBufferPointer(start: (data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count), count: data.count))
 316 |     }
 317 |     static func string(_ bytes : [UInt8]) -> String{
 318 |         if let str = NSString(bytes: bytes, length: bytes.count, encoding: String.Encoding.utf8.rawValue) {
 319 |             return str as String
 320 |         }
 321 |         return ""
 322 |     }
 323 | }
 324 | 
 325 | private class Frame {
 326 |     var inflate = false
 327 |     var code = OpCode.continue
 328 |     var utf8 = UTF8()
 329 |     var payload = Payload()
 330 |     var statusCode = UInt16(0)
 331 |     var finished = true
 332 |     static func makeClose(_ statusCode: UInt16, reason: String) -> Frame {
 333 |         let f = Frame()
 334 |         f.code = .close
 335 |         f.statusCode = statusCode
 336 |         f.utf8.text = reason
 337 |         return f
 338 |     }
 339 |     func copy() -> Frame {
 340 |         let f = Frame()
 341 |         f.code = code
 342 |         f.utf8.text = utf8.text
 343 |         f.payload.buffer = payload.buffer
 344 |         f.statusCode = statusCode
 345 |         f.finished = finished
 346 |         f.inflate = inflate
 347 |         return f
 348 |     }
 349 | }
 350 | 
 351 | private class Delegate : NSObject, StreamDelegate {
 352 |     @objc func stream(_ aStream: Stream, handle eventCode: Stream.Event){
 353 |         manager.signal()
 354 |     }
 355 | }
 356 | 
 357 | 
 358 | @_silgen_name("zlibVersion") private func zlibVersion() -> OpaquePointer
 359 | @_silgen_name("deflateInit2_") private func deflateInit2(_ strm : UnsafeMutableRawPointer, level : CInt, method : CInt, windowBits : CInt, memLevel : CInt, strategy : CInt, version : OpaquePointer, stream_size : CInt) -> CInt
 360 | @_silgen_name("deflateInit_") private func deflateInit(_ strm : UnsafeMutableRawPointer, level : CInt, version : OpaquePointer, stream_size : CInt) -> CInt
 361 | @_silgen_name("deflateEnd") private func deflateEnd(_ strm : UnsafeMutableRawPointer) -> CInt
 362 | @_silgen_name("deflate") private func deflate(_ strm : UnsafeMutableRawPointer, flush : CInt) -> CInt
 363 | @_silgen_name("inflateInit2_") private func inflateInit2(_ strm : UnsafeMutableRawPointer, windowBits : CInt, version : OpaquePointer, stream_size : CInt) -> CInt
 364 | @_silgen_name("inflateInit_") private func inflateInit(_ strm : UnsafeMutableRawPointer, version : OpaquePointer, stream_size : CInt) -> CInt
 365 | @_silgen_name("inflate") private func inflateG(_ strm : UnsafeMutableRawPointer, flush : CInt) -> CInt
 366 | @_silgen_name("inflateEnd") private func inflateEndG(_ strm : UnsafeMutableRawPointer) -> CInt
 367 | 
 368 | private func zerror(_ res : CInt) -> Error? {
 369 |     var err = ""
 370 |     switch res {
 371 |     case 0: return nil
 372 |     case 1: err = "stream end"
 373 |     case 2: err = "need dict"
 374 |     case -1: err = "errno"
 375 |     case -2: err = "stream error"
 376 |     case -3: err = "data error"
 377 |     case -4: err = "mem error"
 378 |     case -5: err = "buf error"
 379 |     case -6: err = "version error"
 380 |     default: err = "undefined error"
 381 |     }
 382 |     return WebSocketError.payloadError("zlib: \(err): \(res)")
 383 | }
 384 | 
 385 | private struct z_stream {
 386 |     var next_in : UnsafePointer? = nil
 387 |     var avail_in : CUnsignedInt = 0
 388 |     var total_in : CUnsignedLong = 0
 389 | 
 390 |     var next_out : UnsafeMutablePointer? = nil
 391 |     var avail_out : CUnsignedInt = 0
 392 |     var total_out : CUnsignedLong = 0
 393 | 
 394 |     var msg : UnsafePointer? = nil
 395 |     var state : OpaquePointer? = nil
 396 | 
 397 |     var zalloc : OpaquePointer? = nil
 398 |     var zfree : OpaquePointer? = nil
 399 |     var opaque : OpaquePointer? = nil
 400 | 
 401 |     var data_type : CInt = 0
 402 |     var adler : CUnsignedLong = 0
 403 |     var reserved : CUnsignedLong = 0
 404 | }
 405 | 
 406 | private class Inflater {
 407 |     var windowBits = 0
 408 |     var strm = z_stream()
 409 |     var tInput = [[UInt8]]()
 410 |     var inflateEnd : [UInt8] = [0x00, 0x00, 0xFF, 0xFF]
 411 |     var bufferSize = windowBufferSize
 412 |     var buffer = malloc(windowBufferSize)
 413 |     init?(windowBits : Int){
 414 |         if buffer == nil {
 415 |             return nil
 416 |         }
 417 |         self.windowBits = windowBits
 418 |         let ret = inflateInit2(&strm, windowBits: -CInt(windowBits), version: zlibVersion(), stream_size: CInt(MemoryLayout.size))
 419 |         if ret != 0 {
 420 |             return nil
 421 |         }
 422 |     }
 423 |     deinit{
 424 |         _ = inflateEndG(&strm)
 425 |         free(buffer)
 426 |     }
 427 |     func inflate(_ bufin : UnsafePointer, length : Int, final : Bool) throws -> (p : UnsafeMutablePointer, n : Int){
 428 |         var buf = buffer
 429 |         var bufsiz = bufferSize
 430 |         var buflen = 0
 431 |         for i in 0 ..< 2{
 432 |             if i == 0 {
 433 |                 strm.avail_in = CUnsignedInt(length)
 434 |                 strm.next_in = UnsafePointer(bufin)
 435 |             } else {
 436 |                 if !final {
 437 |                     break
 438 |                 }
 439 |                 strm.avail_in = CUnsignedInt(inflateEnd.count)
 440 |                 strm.next_in = UnsafePointer(inflateEnd)
 441 |             }
 442 |             while true {
 443 |                 strm.avail_out = CUnsignedInt(bufsiz)
 444 |                 strm.next_out = buf?.assumingMemoryBound(to: UInt8.self)
 445 |                 _ = inflateG(&strm, flush: 0)
 446 |                 let have = bufsiz - Int(strm.avail_out)
 447 |                 bufsiz -= have
 448 |                 buflen += have
 449 |                 if strm.avail_out != 0{
 450 |                     break
 451 |                 }
 452 |                 if bufsiz == 0 {
 453 |                     bufferSize *= 2
 454 |                     let nbuf = realloc(buffer, bufferSize)
 455 |                     if nbuf == nil {
 456 |                         throw WebSocketError.payloadError("memory")
 457 |                     }
 458 |                     buffer = nbuf
 459 |                     buf = buffer?.advanced(by: Int(buflen))
 460 |                     bufsiz = bufferSize - buflen
 461 |                 }
 462 |             }
 463 |         }
 464 |         return (buffer!.assumingMemoryBound(to: UInt8.self), buflen)
 465 |     }
 466 | }
 467 | 
 468 | private class Deflater {
 469 |     var windowBits = 0
 470 |     var memLevel = 0
 471 |     var strm = z_stream()
 472 |     var bufferSize = windowBufferSize
 473 |     var buffer = malloc(windowBufferSize)
 474 |     init?(windowBits : Int, memLevel : Int){
 475 |         if buffer == nil {
 476 |             return nil
 477 |         }
 478 |         self.windowBits = windowBits
 479 |         self.memLevel = memLevel
 480 |         let ret = deflateInit2(&strm, level: 6, method: 8, windowBits: -CInt(windowBits), memLevel: CInt(memLevel), strategy: 0, version: zlibVersion(), stream_size: CInt(MemoryLayout.size))
 481 |         if ret != 0 {
 482 |             return nil
 483 |         }
 484 |     }
 485 |     deinit{
 486 |         _ = deflateEnd(&strm)
 487 |         free(buffer)
 488 |     }
 489 |     /*func deflate(_ bufin : UnsafePointer, length : Int, final : Bool) -> (p : UnsafeMutablePointer, n : Int, err : NSError?){
 490 |         return (nil, 0, nil)
 491 |     }*/
 492 | }
 493 | 
 494 | /// WebSocketDelegate is an Objective-C alternative to WebSocketEvents and is used to delegate the events for the WebSocket connection.
 495 | @objc public protocol WebSocketDelegate {
 496 |     /// A function to be called when the WebSocket connection's readyState changes to .Open; this indicates that the connection is ready to send and receive data.
 497 |     func webSocketOpen()
 498 |     /// A function to be called when the WebSocket connection's readyState changes to .Closed.
 499 |     func webSocketClose(_ code: Int, reason: String, wasClean: Bool)
 500 |     /// A function to be called when an error occurs.
 501 |     func webSocketError(_ error: NSError)
 502 |     /// A function to be called when a message (string) is received from the server.
 503 |     @objc optional func webSocketMessageText(_ text: String)
 504 |     /// A function to be called when a message (binary) is received from the server.
 505 |     @objc optional func webSocketMessageData(_ data: Data)
 506 |     /// A function to be called when a pong is received from the server.
 507 |     @objc optional func webSocketPong()
 508 |     /// A function to be called when the WebSocket process has ended; this event is guarenteed to be called once and can be used as an alternative to the "close" or "error" events.
 509 |     @objc optional func webSocketEnd(_ code: Int, reason: String, wasClean: Bool, error: NSError?)
 510 | }
 511 | 
 512 | /// WebSocket objects are bidirectional network streams that communicate over HTTP. RFC 6455.
 513 | private class InnerWebSocket: Hashable {
 514 |     var id : Int
 515 |     var mutex = pthread_mutex_t()
 516 |     let request : URLRequest!
 517 |     let subProtocols : [String]!
 518 |     var frames : [Frame] = []
 519 |     var delegate : Delegate
 520 |     var inflater : Inflater!
 521 |     var deflater : Deflater!
 522 |     var outputBytes : UnsafeMutablePointer?
 523 |     var outputBytesSize : Int = 0
 524 |     var outputBytesStart : Int = 0
 525 |     var outputBytesLength : Int = 0
 526 |     var inputBytes : UnsafeMutablePointer?
 527 |     var inputBytesSize : Int = 0
 528 |     var inputBytesStart : Int = 0
 529 |     var inputBytesLength : Int = 0
 530 |     var createdAt = CFAbsoluteTimeGetCurrent()
 531 |     var connectionTimeout = false
 532 |     var eclose : ()->() = {}
 533 |     var _eventQueue : DispatchQueue? = DispatchQueue.main
 534 |     var _subProtocol = ""
 535 |     var _compression = WebSocketCompression()
 536 |     var _allowSelfSignedSSL = false
 537 |     var _services = WebSocketService.None
 538 |     var _event = WebSocketEvents()
 539 |     var _eventDelegate: WebSocketDelegate?
 540 |     var _binaryType = WebSocketBinaryType.uInt8Array
 541 |     var _readyState = WebSocketReadyState.connecting
 542 |     var _networkTimeout = TimeInterval(-1)
 543 | 
 544 | 
 545 |     var url : String {
 546 |         return request.url!.description
 547 |     }
 548 |     var subProtocol : String {
 549 |         get { return privateSubProtocol }
 550 |     }
 551 |     var privateSubProtocol : String {
 552 |         get { lock(); defer { unlock() }; return _subProtocol }
 553 |         set { lock(); defer { unlock() }; _subProtocol = newValue }
 554 |     }
 555 |     var compression : WebSocketCompression {
 556 |         get { lock(); defer { unlock() }; return _compression }
 557 |         set { lock(); defer { unlock() }; _compression = newValue }
 558 |     }
 559 |     var allowSelfSignedSSL : Bool {
 560 |         get { lock(); defer { unlock() }; return _allowSelfSignedSSL }
 561 |         set { lock(); defer { unlock() }; _allowSelfSignedSSL = newValue }
 562 |     }
 563 |     var services : WebSocketService {
 564 |         get { lock(); defer { unlock() }; return _services }
 565 |         set { lock(); defer { unlock() }; _services = newValue }
 566 |     }
 567 |     var event : WebSocketEvents {
 568 |         get { lock(); defer { unlock() }; return _event }
 569 |         set { lock(); defer { unlock() }; _event = newValue }
 570 |     }
 571 |     var eventDelegate : WebSocketDelegate? {
 572 |         get { lock(); defer { unlock() }; return _eventDelegate }
 573 |         set { lock(); defer { unlock() }; _eventDelegate = newValue }
 574 |     }
 575 |     var eventQueue : DispatchQueue? {
 576 |         get { lock(); defer { unlock() }; return _eventQueue; }
 577 |         set { lock(); defer { unlock() }; _eventQueue = newValue }
 578 |     }
 579 |     var binaryType : WebSocketBinaryType {
 580 |         get { lock(); defer { unlock() }; return _binaryType }
 581 |         set { lock(); defer { unlock() }; _binaryType = newValue }
 582 |     }
 583 |     var readyState : WebSocketReadyState {
 584 |         get { return privateReadyState }
 585 |     }
 586 |     var privateReadyState : WebSocketReadyState {
 587 |         get { lock(); defer { unlock() }; return _readyState }
 588 |         set { lock(); defer { unlock() }; _readyState = newValue }
 589 |     }
 590 | 
 591 |     func copyOpen(_ request: URLRequest, subProtocols : [String] = []) -> InnerWebSocket{
 592 |         let ws = InnerWebSocket(request: request, subProtocols: subProtocols, stub: false)
 593 |         ws.eclose = eclose
 594 |         ws.compression = compression
 595 |         ws.allowSelfSignedSSL = allowSelfSignedSSL
 596 |         ws.services = services
 597 |         ws.event = event
 598 |         ws.eventQueue = eventQueue
 599 |         ws.binaryType = binaryType
 600 |         return ws
 601 |     }
 602 | 
 603 |     var hashValue: Int { return id }
 604 | 
 605 |     func hash(into hasher: inout Hasher) {
 606 |         hasher.combine(id)
 607 |     }
 608 | 
 609 |     init(request: URLRequest, subProtocols : [String] = [], stub : Bool = false){
 610 |         pthread_mutex_init(&mutex, nil)
 611 |         self.id = manager.nextId()
 612 |         self.request = request
 613 |         self.subProtocols = subProtocols
 614 |         self.outputBytes = UnsafeMutablePointer.allocate(capacity: windowBufferSize)
 615 |         self.outputBytesSize = windowBufferSize
 616 |         self.inputBytes = UnsafeMutablePointer.allocate(capacity: windowBufferSize)
 617 |         self.inputBytesSize = windowBufferSize
 618 |         self.delegate = Delegate()
 619 |         if stub{
 620 |             manager.queue.asyncAfter(deadline: DispatchTime.now() + Double(0) / Double(NSEC_PER_SEC)){
 621 |                 _ = self
 622 |             }
 623 |         } else {
 624 |             manager.queue.asyncAfter(deadline: DispatchTime.now() + Double(0) / Double(NSEC_PER_SEC)){
 625 |                 manager.add(self)
 626 |             }
 627 |         }
 628 |     }
 629 |     deinit{
 630 |         if outputBytes != nil {
 631 |             free(outputBytes)
 632 |         }
 633 |         if inputBytes != nil {
 634 |             free(inputBytes)
 635 |         }
 636 |         pthread_mutex_init(&mutex, nil)
 637 |     }
 638 |     @inline(__always) fileprivate func lock(){
 639 |         pthread_mutex_lock(&mutex)
 640 |     }
 641 |     @inline(__always) fileprivate func unlock(){
 642 |         pthread_mutex_unlock(&mutex)
 643 |     }
 644 | 
 645 |     fileprivate var dirty : Bool {
 646 |         lock()
 647 |         defer { unlock() }
 648 |         if exit {
 649 |             return false
 650 |         }
 651 |         if connectionTimeout {
 652 |             return true
 653 |         }
 654 |         if stage != .readResponse && stage != .handleFrames {
 655 |             return true
 656 |         }
 657 |         if rd.streamStatus == .opening && wr.streamStatus == .opening {
 658 |             return false;
 659 |         }
 660 |         if rd.streamStatus != .open || wr.streamStatus != .open {
 661 |             return true
 662 |         }
 663 |         if rd.streamError != nil || wr.streamError != nil {
 664 |             return true
 665 |         }
 666 |         if rd.hasBytesAvailable || frames.count > 0 || inputBytesLength > 0 {
 667 |             return true
 668 |         }
 669 |         if outputBytesLength > 0 && wr.hasSpaceAvailable{
 670 |             return true
 671 |         }
 672 |         return false
 673 |     }
 674 |     enum Stage : Int {
 675 |         case openConn
 676 |         case readResponse
 677 |         case handleFrames
 678 |         case closeConn
 679 |         case end
 680 |     }
 681 |     var stage = Stage.openConn
 682 |     var rd : InputStream!
 683 |     var wr : OutputStream!
 684 |     var atEnd = false
 685 |     var closeCode = UInt16(0)
 686 |     var closeReason = ""
 687 |     var closeClean = false
 688 |     var closeFinal = false
 689 |     var finalError : Error?
 690 |     var exit = false
 691 |     var more = true
 692 |     func step(){
 693 |         if exit {
 694 |             return
 695 |         }
 696 |         do {
 697 |             try stepBuffers(more)
 698 |             try stepStreamErrors()
 699 |             more = false
 700 |             switch stage {
 701 |             case .openConn:
 702 |                 try openConn()
 703 |                 stage = .readResponse
 704 |             case .readResponse:
 705 |                 try readResponse()
 706 |                 privateReadyState = .open
 707 |                 fire {
 708 |                     self.event.open()
 709 |                     self.eventDelegate?.webSocketOpen()
 710 |                 }
 711 |                 stage = .handleFrames
 712 |             case .handleFrames:
 713 |                 try stepOutputFrames()
 714 |                 if closeFinal {
 715 |                     privateReadyState = .closing
 716 |                     stage = .closeConn
 717 |                     return
 718 |                 }
 719 |                 let frame = try readFrame()
 720 |                 switch frame.code {
 721 |                 case .text:
 722 |                     fire {
 723 |                         self.event.message(frame.utf8.text)
 724 |                         self.eventDelegate?.webSocketMessageText?(frame.utf8.text)
 725 |                     }
 726 |                 case .binary:
 727 |                     fire {
 728 |                         switch self.binaryType {
 729 |                         case .uInt8Array:
 730 |                             self.event.message(frame.payload.array)
 731 |                         case .nsData:
 732 |                             self.event.message(frame.payload.nsdata)
 733 |                             // The WebSocketDelegate is necessary to add Objective-C compability and it is only possible to send binary data with NSData.
 734 |                             self.eventDelegate?.webSocketMessageData?(frame.payload.nsdata)
 735 |                         case .uInt8UnsafeBufferPointer:
 736 |                             self.event.message(frame.payload.buffer)
 737 |                         }
 738 |                     }
 739 |                 case .ping:
 740 |                     let nframe = frame.copy()
 741 |                     nframe.code = .pong
 742 |                     lock()
 743 |                     frames += [nframe]
 744 |                     unlock()
 745 |                 case .pong:
 746 |                     fire {
 747 |                         switch self.binaryType {
 748 |                         case .uInt8Array:
 749 |                             self.event.pong(frame.payload.array)
 750 |                         case .nsData:
 751 |                             self.event.pong(frame.payload.nsdata)
 752 |                         case .uInt8UnsafeBufferPointer:
 753 |                             self.event.pong(frame.payload.buffer)
 754 |                         }
 755 |                         self.eventDelegate?.webSocketPong?()
 756 |                     }
 757 |                 case .close:
 758 |                     lock()
 759 |                     frames += [frame]
 760 |                     unlock()
 761 |                 default:
 762 |                     break
 763 |                 }
 764 |             case .closeConn:
 765 |                 if let error = finalError {
 766 |                     self.event.error(error)
 767 |                     self.eventDelegate?.webSocketError(error as NSError)
 768 |                 }
 769 |                 privateReadyState = .closed
 770 |                 if rd != nil {
 771 |                     closeConn()
 772 |                     fire {
 773 |                         self.eclose()
 774 |                         self.event.close(Int(self.closeCode), self.closeReason, self.closeFinal)
 775 |                         self.eventDelegate?.webSocketClose(Int(self.closeCode), reason: self.closeReason, wasClean: self.closeFinal)
 776 |                     }
 777 |                 }
 778 |                 stage = .end
 779 |             case .end:
 780 |                 fire {
 781 |                     self.event.end(Int(self.closeCode), self.closeReason, self.closeClean, self.finalError)
 782 |                     self.eventDelegate?.webSocketEnd?(Int(self.closeCode), reason: self.closeReason, wasClean: self.closeClean, error: self.finalError as NSError?)
 783 |                 }
 784 |                 exit = true
 785 |                 manager.remove(self)
 786 |             }
 787 |         } catch WebSocketError.needMoreInput {
 788 |             more = true
 789 |         } catch {
 790 |             if finalError != nil {
 791 |                 return
 792 |             }
 793 |             finalError = error
 794 |             if stage == .openConn || stage == .readResponse {
 795 |                 stage = .closeConn
 796 |             } else {
 797 |                 var frame : Frame?
 798 |                 if let error = error as? WebSocketError{
 799 |                     switch error {
 800 |                     case .network(let details):
 801 |                         if details == atEndDetails{
 802 |                             stage = .closeConn
 803 |                             frame = Frame.makeClose(1006, reason: "Abnormal Closure")
 804 |                             atEnd = true
 805 |                             finalError = nil
 806 |                         }
 807 |                     case .protocolError:
 808 |                         frame = Frame.makeClose(1002, reason: "Protocol error")
 809 |                     case .payloadError:
 810 |                         frame = Frame.makeClose(1007, reason: "Payload error")
 811 |                     default:
 812 |                         break
 813 |                     }
 814 |                 }
 815 |                 if frame == nil {
 816 |                     frame = Frame.makeClose(1006, reason: "Abnormal Closure")
 817 |                 }
 818 |                 if let frame = frame {
 819 |                     if frame.statusCode == 1007 {
 820 |                         self.lock()
 821 |                         self.frames = [frame]
 822 |                         self.unlock()
 823 |                         manager.signal()
 824 |                     } else {
 825 |                         manager.queue.asyncAfter(deadline: DispatchTime.now() + Double(0) / Double(NSEC_PER_SEC)){
 826 |                             self.lock()
 827 |                             self.frames += [frame]
 828 |                             self.unlock()
 829 |                             manager.signal()
 830 |                         }
 831 |                     }
 832 |                 }
 833 |             }
 834 |         }
 835 |     }
 836 |     func stepBuffers(_ more: Bool) throws {
 837 |         if rd != nil {
 838 |             if stage != .closeConn && rd.streamStatus == Stream.Status.atEnd  {
 839 |                 if atEnd {
 840 |                     return;
 841 |                 }
 842 |                 throw WebSocketError.network(atEndDetails)
 843 |             }
 844 |             if more {
 845 |                 while rd.hasBytesAvailable {
 846 |                     var size = inputBytesSize
 847 |                     while size-(inputBytesStart+inputBytesLength) < windowBufferSize {
 848 |                         size *= 2
 849 |                     }
 850 |                     if size > inputBytesSize {
 851 |                         let ptr = realloc(inputBytes, size)
 852 |                         if ptr == nil {
 853 |                             throw WebSocketError.memory
 854 |                         }
 855 |                         inputBytes = ptr?.assumingMemoryBound(to: UInt8.self)
 856 |                         inputBytesSize = size
 857 |                     }
 858 |                     let n = rd.read(inputBytes!+inputBytesStart+inputBytesLength, maxLength: inputBytesSize-inputBytesStart-inputBytesLength)
 859 |                     if n > 0 {
 860 |                         inputBytesLength += n
 861 |                     }
 862 |                 }
 863 |             }
 864 |         }
 865 |         if wr != nil && wr.hasSpaceAvailable && outputBytesLength > 0 {
 866 |             let n = wr.write(outputBytes!+outputBytesStart, maxLength: outputBytesLength)
 867 |             if n > 0 {
 868 |                 outputBytesLength -= n
 869 |                 if outputBytesLength == 0 {
 870 |                     outputBytesStart = 0
 871 |                 } else {
 872 |                     outputBytesStart += n
 873 |                 }
 874 |             }
 875 |         }
 876 |     }
 877 |     func stepStreamErrors() throws {
 878 |         if finalError == nil {
 879 |             if connectionTimeout {
 880 |                 throw WebSocketError.network(timeoutDetails)
 881 |             }
 882 |             if let error = rd?.streamError {
 883 |                 throw WebSocketError.network(error.localizedDescription)
 884 |             }
 885 |             if let error = wr?.streamError {
 886 |                 throw WebSocketError.network(error.localizedDescription)
 887 |             }
 888 |         }
 889 |     }
 890 |     func stepOutputFrames() throws {
 891 |         lock()
 892 |         defer {
 893 |             frames = []
 894 |             unlock()
 895 |         }
 896 |         if !closeFinal {
 897 |             for frame in frames {
 898 |                 try writeFrame(frame)
 899 |                 if frame.code == .close {
 900 |                     closeCode = frame.statusCode
 901 |                     closeReason = frame.utf8.text
 902 |                     closeFinal = true
 903 |                     return
 904 |                 }
 905 |             }
 906 |         }
 907 |     }
 908 |     @inline(__always) func fire(_ block: ()->()){
 909 |         if let queue = eventQueue {
 910 |             queue.sync {
 911 |                 block()
 912 |             }
 913 |         } else {
 914 |             block()
 915 |         }
 916 |     }
 917 | 
 918 |     var readStateSaved = false
 919 |     var readStateFrame : Frame?
 920 |     var readStateFinished = false
 921 |     var leaderFrame : Frame?
 922 |     func readFrame() throws -> Frame {
 923 |         var frame : Frame
 924 |         var finished : Bool
 925 |         if !readStateSaved {
 926 |             if leaderFrame != nil {
 927 |                 frame = leaderFrame!
 928 |                 finished = false
 929 |                 leaderFrame = nil
 930 |             } else {
 931 |                 frame = try readFrameFragment(nil)
 932 |                 finished = frame.finished
 933 |             }
 934 |             if frame.code == .continue{
 935 |                 throw WebSocketError.protocolError("leader frame cannot be a continue frame")
 936 |             }
 937 |             if !finished {
 938 |                 readStateSaved = true
 939 |                 readStateFrame = frame
 940 |                 readStateFinished = finished
 941 |                 throw WebSocketError.needMoreInput
 942 |             }
 943 |         } else {
 944 |             frame = readStateFrame!
 945 |             finished = readStateFinished
 946 |             if !finished {
 947 |                 let cf = try readFrameFragment(frame)
 948 |                 finished = cf.finished
 949 |                 if cf.code != .continue {
 950 |                     if !cf.code.isControl {
 951 |                         throw WebSocketError.protocolError("only ping frames can be interlaced with fragments")
 952 |                     }
 953 |                     leaderFrame = frame
 954 |                     return cf
 955 |                 }
 956 |                 if !finished {
 957 |                     readStateSaved = true
 958 |                     readStateFrame = frame
 959 |                     readStateFinished = finished
 960 |                     throw WebSocketError.needMoreInput
 961 |                 }
 962 |             }
 963 |         }
 964 |         if !frame.utf8.completed {
 965 |             throw WebSocketError.payloadError("incomplete utf8")
 966 |         }
 967 |         readStateSaved = false
 968 |         readStateFrame = nil
 969 |         readStateFinished = false
 970 |         return frame
 971 |     }
 972 | 
 973 |     func closeConn() {
 974 |         rd.remove(from: RunLoop.main, forMode: RunLoop.Mode.default)
 975 |         wr.remove(from: RunLoop.main, forMode: RunLoop.Mode.default)
 976 |         rd.delegate = nil
 977 |         wr.delegate = nil
 978 |         rd.close()
 979 |         wr.close()
 980 |     }
 981 | 
 982 |     func openConn() throws {
 983 |         var req = request!
 984 |         req.setValue("websocket", forHTTPHeaderField: "Upgrade")
 985 |         req.setValue("Upgrade", forHTTPHeaderField: "Connection")
 986 |         if req.value(forHTTPHeaderField: "User-Agent") == nil {
 987 |                 req.setValue("SwiftWebSocket", forHTTPHeaderField: "User-Agent")
 988 |         }
 989 |         req.setValue("13", forHTTPHeaderField: "Sec-WebSocket-Version")
 990 | 
 991 |         if req.url == nil || req.url!.host == nil{
 992 |             throw WebSocketError.invalidAddress
 993 |         }
 994 |         if req.url!.port == nil || req.url!.port! == 80 || req.url!.port! == 443 {
 995 |             req.setValue(req.url!.host!, forHTTPHeaderField: "Host")
 996 |         } else {
 997 |             req.setValue("\(req.url!.host!):\(req.url!.port!)", forHTTPHeaderField: "Host")
 998 |         }
 999 |         let origin = req.value(forHTTPHeaderField: "Origin")
1000 |         if origin == nil || origin! == ""{
1001 |             req.setValue(req.url!.absoluteString, forHTTPHeaderField: "Origin")
1002 |         }
1003 |         if subProtocols.count > 0 {
1004 |             req.setValue(subProtocols.joined(separator: ","), forHTTPHeaderField: "Sec-WebSocket-Protocol")
1005 |         }
1006 |         if req.url!.scheme != "wss" && req.url!.scheme != "ws" {
1007 |             throw WebSocketError.invalidAddress
1008 |         }
1009 |         if compression.on {
1010 |             var val = "permessage-deflate"
1011 |             if compression.noContextTakeover {
1012 |                 val += "; client_no_context_takeover; server_no_context_takeover"
1013 |             }
1014 |             val += "; client_max_window_bits"
1015 |             if compression.maxWindowBits != 0 {
1016 |                 val += "; server_max_window_bits=\(compression.maxWindowBits)"
1017 |             }
1018 |             req.setValue(val, forHTTPHeaderField: "Sec-WebSocket-Extensions")
1019 |         }
1020 | 		
1021 | 		let security: TCPConnSecurity
1022 | 		let port : Int
1023 | 		if req.url!.scheme == "wss" {
1024 | 			port = req.url!.port ?? 443
1025 | 			security = .negoticatedSSL
1026 | 		} else {
1027 | 			port = req.url!.port ?? 80
1028 | 			security = .none
1029 | 		}
1030 | 
1031 | 		var path = CFURLCopyPath(req.url! as CFURL) as String
1032 |         if path == "" {
1033 |             path = "/"
1034 |         }
1035 |         if let q = req.url!.query {
1036 |             if q != "" {
1037 |                 path += "?" + q
1038 |             }
1039 |         }
1040 |         var reqs = "GET \(path) HTTP/1.1\r\n"
1041 |         for key in req.allHTTPHeaderFields!.keys {
1042 |             if let val = req.value(forHTTPHeaderField: key) {
1043 |                 reqs += "\(key): \(val)\r\n"
1044 |             }
1045 |         }
1046 |         var keyb = [UInt32](repeating: 0, count: 4)
1047 |         for i in 0 ..< 4 {
1048 |             keyb[i] = arc4random()
1049 |         }
1050 |         let rkey = Data(bytes: UnsafePointer(keyb), count: 16).base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
1051 |         reqs += "Sec-WebSocket-Key: \(rkey)\r\n"
1052 |         reqs += "\r\n"
1053 |         var header = [UInt8]()
1054 |         for b in reqs.utf8 {
1055 |             header += [b]
1056 |         }
1057 |         let addr = ["\(req.url!.host!)", "\(port)"]
1058 |         if addr.count != 2 || Int(addr[1]) == nil {
1059 |             throw WebSocketError.invalidAddress
1060 |         }
1061 | 
1062 |         var (rdo, wro) : (InputStream?, OutputStream?)
1063 |         var readStream:  Unmanaged?
1064 |         var writeStream: Unmanaged?
1065 |         CFStreamCreatePairWithSocketToHost(nil, addr[0] as CFString, UInt32(Int(addr[1])!), &readStream, &writeStream);
1066 |         rdo = readStream!.takeRetainedValue()
1067 |         wro = writeStream!.takeRetainedValue()
1068 |         (rd, wr) = (rdo!, wro!)
1069 |         rd.setProperty(security.level, forKey: Stream.PropertyKey.socketSecurityLevelKey)
1070 | 		wr.setProperty(security.level, forKey: Stream.PropertyKey.socketSecurityLevelKey)
1071 |         if services.contains(.VoIP) {
1072 |             rd.setProperty(StreamNetworkServiceTypeValue.voIP.rawValue, forKey: Stream.PropertyKey.networkServiceType)
1073 |             wr.setProperty(StreamNetworkServiceTypeValue.voIP.rawValue, forKey: Stream.PropertyKey.networkServiceType)
1074 |         }
1075 |         if services.contains(.Video) {
1076 |             rd.setProperty(StreamNetworkServiceTypeValue.video.rawValue, forKey: Stream.PropertyKey.networkServiceType)
1077 |             wr.setProperty(StreamNetworkServiceTypeValue.video.rawValue, forKey: Stream.PropertyKey.networkServiceType)
1078 |         }
1079 |         if services.contains(.Background) {
1080 |             rd.setProperty(StreamNetworkServiceTypeValue.background.rawValue, forKey: Stream.PropertyKey.networkServiceType)
1081 |             wr.setProperty(StreamNetworkServiceTypeValue.background.rawValue, forKey: Stream.PropertyKey.networkServiceType)
1082 |         }
1083 |         if services.contains(.Voice) {
1084 |             rd.setProperty(StreamNetworkServiceTypeValue.voice.rawValue, forKey: Stream.PropertyKey.networkServiceType)
1085 |             wr.setProperty(StreamNetworkServiceTypeValue.voice.rawValue, forKey: Stream.PropertyKey.networkServiceType)
1086 |         }
1087 |         if allowSelfSignedSSL {
1088 |             let prop: Dictionary = [kCFStreamSSLPeerName: kCFNull, kCFStreamSSLValidatesCertificateChain: NSNumber(value: false)]
1089 |             rd.setProperty(prop, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertySSLSettings as String as String))
1090 |             wr.setProperty(prop, forKey: Stream.PropertyKey(rawValue: kCFStreamPropertySSLSettings as String as String))
1091 |         }
1092 |         rd.delegate = delegate
1093 |         wr.delegate = delegate
1094 |         rd.schedule(in: RunLoop.main, forMode: RunLoop.Mode.default)
1095 |         wr.schedule(in: RunLoop.main, forMode: RunLoop.Mode.default)
1096 |         rd.open()
1097 |         wr.open()
1098 |         try write(header, length: header.count)
1099 |     }
1100 | 
1101 |     func write(_ bytes: UnsafePointer, length: Int) throws {
1102 |         if outputBytesStart+outputBytesLength+length > outputBytesSize {
1103 |             var size = outputBytesSize
1104 |             while outputBytesStart+outputBytesLength+length > size {
1105 |                 size *= 2
1106 |             }
1107 |             let ptr = realloc(outputBytes, size)
1108 |             if ptr == nil {
1109 |                 throw WebSocketError.memory
1110 |             }
1111 |             outputBytes = ptr?.assumingMemoryBound(to: UInt8.self)
1112 |             outputBytesSize = size
1113 |         }
1114 |         memcpy(outputBytes!+outputBytesStart+outputBytesLength, bytes, length)
1115 |         outputBytesLength += length
1116 |     }
1117 | 
1118 |     func readResponse() throws {
1119 |         let end : [UInt8] = [ 0x0D, 0x0A, 0x0D, 0x0A ]
1120 |         let ptr = memmem(inputBytes!+inputBytesStart, inputBytesLength, end, 4)
1121 |         if ptr == nil {
1122 |             throw WebSocketError.needMoreInput
1123 |         }
1124 |         let buffer = inputBytes!+inputBytesStart
1125 |         let bufferCount = ptr!.assumingMemoryBound(to: UInt8.self)-(inputBytes!+inputBytesStart)
1126 |         let string = NSString(bytesNoCopy: buffer, length: bufferCount, encoding: String.Encoding.utf8.rawValue, freeWhenDone: false) as String?
1127 |         if string == nil {
1128 |             throw WebSocketError.invalidHeader
1129 |         }
1130 |         let header = string!
1131 |         var needsCompression = false
1132 |         var serverMaxWindowBits = 15
1133 |         let clientMaxWindowBits = 15
1134 |         var key = ""
1135 |         let trim : (String)->(String) = { (text) in return text.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)}
1136 |         let eqval : (String,String)->(String) = { (line, del) in return trim(line.components(separatedBy: del)[1]) }
1137 |         let lines = header.components(separatedBy: "\r\n")
1138 |         for i in 0 ..< lines.count {
1139 |             let line = trim(lines[i])
1140 |             if i == 0  {
1141 |                 if !line.hasPrefix("HTTP/1.1 101"){
1142 |                     throw WebSocketError.invalidResponse(line)
1143 |                 }
1144 |             } else if line != "" {
1145 |                 var value = ""
1146 |                 if line.hasPrefix("\t") || line.hasPrefix(" ") {
1147 |                     value = trim(line)
1148 |                 } else {
1149 |                     key = ""
1150 |                     if let r = line.range(of: ":") {
1151 |                         key = trim(String(line[.. 15 {
1178 |                 throw WebSocketError.invalidCompressionOptions("server_max_window_bits")
1179 |             }
1180 |             if serverMaxWindowBits < 8 || serverMaxWindowBits > 15 {
1181 |                 throw WebSocketError.invalidCompressionOptions("client_max_window_bits")
1182 |             }
1183 |             inflater = Inflater(windowBits: serverMaxWindowBits)
1184 |             if inflater == nil {
1185 |                 throw WebSocketError.invalidCompressionOptions("inflater init")
1186 |             }
1187 |             deflater = Deflater(windowBits: clientMaxWindowBits, memLevel: 8)
1188 |             if deflater == nil {
1189 |                 throw WebSocketError.invalidCompressionOptions("deflater init")
1190 |             }
1191 |         }
1192 |         inputBytesLength -= bufferCount+4
1193 |         if inputBytesLength == 0 {
1194 |             inputBytesStart = 0
1195 |         } else {
1196 |             inputBytesStart += bufferCount+4
1197 |         }
1198 |     }
1199 | 
1200 |     class ByteReader {
1201 |         var start : UnsafePointer
1202 |         var end : UnsafePointer
1203 |         var bytes : UnsafePointer
1204 |         init(bytes: UnsafePointer, length: Int){
1205 |             self.bytes = bytes
1206 |             start = bytes
1207 |             end = bytes+length
1208 |         }
1209 |         func readByte() throws -> UInt8 {
1210 |             if bytes >= end {
1211 |                 throw WebSocketError.needMoreInput
1212 |             }
1213 |             let b = bytes.pointee
1214 |             bytes += 1
1215 |             return b
1216 |         }
1217 |         var length : Int {
1218 |             return end - bytes
1219 |         }
1220 |         var position : Int {
1221 |             get {
1222 |                 return bytes - start
1223 |             }
1224 |             set {
1225 |                 bytes = start + newValue
1226 |             }
1227 |         }
1228 |     }
1229 | 
1230 |     var fragStateSaved = false
1231 |     var fragStatePosition = 0
1232 |     var fragStateInflate = false
1233 |     var fragStateLen = 0
1234 |     var fragStateFin = false
1235 |     var fragStateCode = OpCode.continue
1236 |     var fragStateLeaderCode = OpCode.continue
1237 |     var fragStateUTF8 = UTF8()
1238 |     var fragStatePayload = Payload()
1239 |     var fragStateStatusCode = UInt16(0)
1240 |     var fragStateHeaderLen = 0
1241 |     var buffer = [UInt8](repeating: 0, count: windowBufferSize)
1242 |     var reusedPayload = Payload()
1243 |     func readFrameFragment(_ leader : Frame?) throws -> Frame {
1244 |         var inflate : Bool
1245 |         var len : Int
1246 |         var fin = false
1247 |         var code : OpCode
1248 |         var leaderCode : OpCode
1249 |         var utf8 : UTF8
1250 |         var payload : Payload
1251 |         var statusCode : UInt16
1252 |         var headerLen : Int
1253 |         var leader = leader
1254 | 
1255 |         let reader = ByteReader(bytes: inputBytes!+inputBytesStart, length: inputBytesLength)
1256 |         if fragStateSaved {
1257 |             // load state
1258 |             reader.position += fragStatePosition
1259 |             inflate = fragStateInflate
1260 |             len = fragStateLen
1261 |             fin = fragStateFin
1262 |             code = fragStateCode
1263 |             leaderCode = fragStateLeaderCode
1264 |             utf8 = fragStateUTF8
1265 |             payload = fragStatePayload
1266 |             statusCode = fragStateStatusCode
1267 |             headerLen = fragStateHeaderLen
1268 |             fragStateSaved = false
1269 |         } else {
1270 |             var b = try reader.readByte()
1271 |             fin = b >> 7 & 0x1 == 0x1
1272 |             let rsv1 = b >> 6 & 0x1 == 0x1
1273 |             let rsv2 = b >> 5 & 0x1 == 0x1
1274 |             let rsv3 = b >> 4 & 0x1 == 0x1
1275 |             if inflater != nil && (rsv1 || (leader != nil && leader!.inflate)) {
1276 |                 inflate = true
1277 |             } else if rsv1 || rsv2 || rsv3 {
1278 |                 throw WebSocketError.protocolError("invalid extension")
1279 |             } else {
1280 |                 inflate = false
1281 |             }
1282 |             code = OpCode.binary
1283 |             if let c = OpCode(rawValue: (b & 0xF)){
1284 |                 code = c
1285 |             } else {
1286 |                 throw WebSocketError.protocolError("invalid opcode")
1287 |             }
1288 |             if !fin && code.isControl {
1289 |                 throw WebSocketError.protocolError("unfinished control frame")
1290 |             }
1291 |             b = try reader.readByte()
1292 |             if b >> 7 & 0x1 == 0x1 {
1293 |                 throw WebSocketError.protocolError("server sent masked frame")
1294 |             }
1295 |             var len64 = Int64(b & 0x7F)
1296 |             var bcount = 0
1297 |             if b & 0x7F == 126 {
1298 |                 bcount = 2
1299 |             } else if len64 == 127 {
1300 |                 bcount = 8
1301 |             }
1302 |             if bcount != 0 {
1303 |                 if code.isControl {
1304 |                     throw WebSocketError.protocolError("invalid payload size for control frame")
1305 |                 }
1306 |                 len64 = 0
1307 |                 var i = bcount-1
1308 |                 while i >= 0 {
1309 |                     b = try reader.readByte()
1310 |                     len64 += Int64(b) << Int64(i*8)
1311 |                     i -= 1
1312 |                 }
1313 |             }
1314 |             len = Int(len64)
1315 |             if code == .continue {
1316 |                 if code.isControl {
1317 |                     throw WebSocketError.protocolError("control frame cannot have the 'continue' opcode")
1318 |                 }
1319 |                 if leader == nil {
1320 |                     throw WebSocketError.protocolError("continue frame is missing it's leader")
1321 |                 }
1322 |             }
1323 |             if code.isControl {
1324 |                 if leader != nil {
1325 |                     leader = nil
1326 |                 }
1327 |                 if inflate {
1328 |                     throw WebSocketError.protocolError("control frame cannot be compressed")
1329 |                 }
1330 |             }
1331 |             statusCode = 0
1332 |             if leader != nil {
1333 |                 leaderCode = leader!.code
1334 |                 utf8 = leader!.utf8
1335 |                 payload = leader!.payload
1336 |             } else {
1337 |                 leaderCode = code
1338 |                 utf8 = UTF8()
1339 |                 payload = reusedPayload
1340 |                 payload.count = 0
1341 |             }
1342 |             if leaderCode == .close {
1343 |                 if len == 1 {
1344 |                     throw WebSocketError.protocolError("invalid payload size for close frame")
1345 |                 }
1346 |                 if len >= 2 {
1347 |                     let b1 = try reader.readByte()
1348 |                     let b2 = try reader.readByte()
1349 |                     statusCode = (UInt16(b1) << 8) + UInt16(b2)
1350 |                     len -= 2
1351 |                     if statusCode < 1000 || statusCode > 4999  || (statusCode >= 1004 && statusCode <= 1006) || (statusCode >= 1012 && statusCode <= 2999) {
1352 |                         throw WebSocketError.protocolError("invalid status code for close frame")
1353 |                     }
1354 |                 }
1355 |             }
1356 |             headerLen = reader.position
1357 |         }
1358 | 
1359 |         let rlen : Int
1360 |         let rfin : Bool
1361 |         let chopped : Bool
1362 |         if reader.length+reader.position-headerLen < len {
1363 |             rlen = reader.length
1364 |             rfin = false
1365 |             chopped = true
1366 |         } else {
1367 |             rlen = len-reader.position+headerLen
1368 |             rfin = fin
1369 |             chopped = false
1370 |         }
1371 |         let bytes : UnsafeMutablePointer
1372 |         let bytesLen : Int
1373 |         if inflate {
1374 |             (bytes, bytesLen) = try inflater!.inflate(reader.bytes, length: rlen, final: rfin)
1375 |         } else {
1376 |             (bytes, bytesLen) = (UnsafeMutablePointer.init(mutating: reader.bytes), rlen)
1377 |         }
1378 |         reader.bytes += rlen
1379 | 
1380 |         if leaderCode == .text || leaderCode == .close {
1381 |             try utf8.append(bytes, length: bytesLen)
1382 |         } else {
1383 |             payload.append(bytes, length: bytesLen)
1384 |         }
1385 | 
1386 |         if chopped {
1387 |             // save state
1388 |             fragStateHeaderLen = headerLen
1389 |             fragStateStatusCode = statusCode
1390 |             fragStatePayload = payload
1391 |             fragStateUTF8 = utf8
1392 |             fragStateLeaderCode = leaderCode
1393 |             fragStateCode = code
1394 |             fragStateFin = fin
1395 |             fragStateLen = len
1396 |             fragStateInflate = inflate
1397 |             fragStatePosition = reader.position
1398 |             fragStateSaved = true
1399 |             throw WebSocketError.needMoreInput
1400 |         }
1401 | 
1402 |         inputBytesLength -= reader.position
1403 |         if inputBytesLength == 0 {
1404 |             inputBytesStart = 0
1405 |         } else {
1406 |             inputBytesStart += reader.position
1407 |         }
1408 | 
1409 |         let f = Frame()
1410 |         (f.code, f.payload, f.utf8, f.statusCode, f.inflate, f.finished) = (code, payload, utf8, statusCode, inflate, fin)
1411 |         return f
1412 |     }
1413 | 
1414 |     var head = [UInt8](repeating: 0, count: 0xFF)
1415 |     func writeFrame(_ f : Frame) throws {
1416 |         if !f.finished{
1417 |             throw WebSocketError.libraryError("cannot send unfinished frames")
1418 |         }
1419 |         var hlen = 0
1420 |         let b : UInt8 = 0x80
1421 |         var deflate = false
1422 |         if deflater != nil {
1423 |             if f.code == .binary || f.code == .text {
1424 |                 deflate = true
1425 |                 // b |= 0x40
1426 |             }
1427 |         }
1428 |         head[hlen] = b | f.code.rawValue
1429 |         hlen += 1
1430 |         var payloadBytes : [UInt8]
1431 |         var payloadLen = 0
1432 |         if f.utf8.text != "" {
1433 |             payloadBytes = UTF8.bytes(f.utf8.text)
1434 |         } else {
1435 |             payloadBytes = f.payload.array
1436 |         }
1437 |         payloadLen += payloadBytes.count
1438 |         if deflate {
1439 | 
1440 |         }
1441 |         var usingStatusCode = false
1442 |         if f.statusCode != 0 && payloadLen != 0 {
1443 |             payloadLen += 2
1444 |             usingStatusCode = true
1445 |         }
1446 |         if payloadLen < 126 {
1447 |             head[hlen] = 0x80 | UInt8(payloadLen)
1448 |             hlen += 1
1449 |         } else if payloadLen <= 0xFFFF {
1450 |             head[hlen] = 0x80 | 126
1451 |             hlen += 1
1452 |             var i = 1
1453 |             while i >= 0 {
1454 |                 head[hlen] = UInt8((UInt16(payloadLen) >> UInt16(i*8)) & 0xFF)
1455 |                 hlen += 1
1456 |                 i -= 1
1457 |             }
1458 |         } else {
1459 |             head[hlen] = UInt8((0x1 << 7) + 127)
1460 |             hlen += 1
1461 |             var i = 7
1462 |             while i >= 0 {
1463 |                 head[hlen] = UInt8((UInt64(payloadLen) >> UInt64(i*8)) & 0xFF)
1464 |                 hlen += 1
1465 |                 i -= 1
1466 |             }
1467 |         }
1468 |         let r = arc4random()
1469 |         var maskBytes : [UInt8] = [UInt8(r >> 0 & 0xFF), UInt8(r >> 8 & 0xFF), UInt8(r >> 16 & 0xFF), UInt8(r >> 24 & 0xFF)]
1470 |         for i in 0 ..< 4 {
1471 |             head[hlen] = maskBytes[i]
1472 |             hlen += 1
1473 |         }
1474 |         if payloadLen > 0 {
1475 |             if usingStatusCode {
1476 |                 var sc = [UInt8(f.statusCode >> 8 & 0xFF), UInt8(f.statusCode >> 0 & 0xFF)]
1477 |                 for i in 0 ..< 2 {
1478 |                     sc[i] ^= maskBytes[i % 4]
1479 |                 }
1480 |                 head[hlen] = sc[0]
1481 |                 hlen += 1
1482 |                 head[hlen] = sc[1]
1483 |                 hlen += 1
1484 |                 for i in 2 ..< payloadLen {
1485 |                     payloadBytes[i-2] ^= maskBytes[i % 4]
1486 |                 }
1487 |             } else {
1488 |                 for i in 0 ..< payloadLen {
1489 |                     payloadBytes[i] ^= maskBytes[i % 4]
1490 |                 }
1491 |             }
1492 |         }
1493 |         try write(head, length: hlen)
1494 |         try write(payloadBytes, length: payloadBytes.count)
1495 |     }
1496 |     func close(_ code : Int = 1000, reason : String = "Normal Closure") {
1497 |         let f = Frame()
1498 |         f.code = .close
1499 |         f.statusCode = UInt16(truncatingIfNeeded: code)
1500 |         f.utf8.text = reason
1501 |         sendFrame(f)
1502 |     }
1503 |     func sendFrame(_ f : Frame) {
1504 |         lock()
1505 |         frames += [f]
1506 |         unlock()
1507 |         manager.signal()
1508 |     }
1509 |     func send(_ message : Any) {
1510 |         let f = Frame()
1511 |         if let message = message as? String {
1512 |             f.code = .text
1513 |             f.utf8.text = message
1514 |         } else if let message = message as? [UInt8] {
1515 |             f.code = .binary
1516 |             f.payload.array = message
1517 |         } else if let message = message as? UnsafeBufferPointer {
1518 |             f.code = .binary
1519 |             f.payload.append(message.baseAddress!, length: message.count)
1520 |         } else if let message = message as? Data {
1521 |             f.code = .binary
1522 |             f.payload.nsdata = message
1523 |         } else {
1524 |             f.code = .text
1525 |             f.utf8.text = "\(message)"
1526 |         }
1527 |         sendFrame(f)
1528 |     }
1529 |     func ping() {
1530 |         let f = Frame()
1531 |         f.code = .ping
1532 |         sendFrame(f)
1533 |     }
1534 |     func ping(_ message : Any){
1535 |         let f = Frame()
1536 |         f.code = .ping
1537 |         if let message = message as? String {
1538 |             f.payload.array = UTF8.bytes(message)
1539 |         } else if let message = message as? [UInt8] {
1540 |             f.payload.array = message
1541 |         } else if let message = message as? UnsafeBufferPointer {
1542 |             f.payload.append(message.baseAddress!, length: message.count)
1543 |         } else if let message = message as? Data {
1544 |             f.payload.nsdata = message
1545 |         } else {
1546 |             f.utf8.text = "\(message)"
1547 |         }
1548 |         sendFrame(f)
1549 |     }
1550 | }
1551 | private func ==(lhs: InnerWebSocket, rhs: InnerWebSocket) -> Bool {
1552 |     return lhs.id == rhs.id
1553 | }
1554 | 
1555 | private enum TCPConnSecurity {
1556 |     case none
1557 |     case negoticatedSSL
1558 | 	
1559 | 	var level: String {
1560 | 		switch self {
1561 | 		case .none: return StreamSocketSecurityLevel.none.rawValue
1562 | 		case .negoticatedSSL: return StreamSocketSecurityLevel.negotiatedSSL.rawValue
1563 | 		}
1564 | 	}
1565 | }
1566 | 
1567 | // Manager class is used to minimize the number of dispatches and cycle through network events
1568 | // using fewers threads. Helps tremendously with lowing system resources when many conncurrent
1569 | // sockets are opened.
1570 | private class Manager {
1571 |     var queue = DispatchQueue(label: "SwiftWebSocketInstance", attributes: [])
1572 |     var once = Int()
1573 |     var mutex = pthread_mutex_t()
1574 |     var cond = pthread_cond_t()
1575 |     var websockets = Set()
1576 |     var _nextId = 0
1577 |     init(){
1578 |         pthread_mutex_init(&mutex, nil)
1579 |         pthread_cond_init(&cond, nil)
1580 |         DispatchQueue(label: "SwiftWebSocket", attributes: []).async {
1581 |             var wss : [InnerWebSocket] = []
1582 |             while true {
1583 |                 var wait = true
1584 |                 wss.removeAll()
1585 |                 pthread_mutex_lock(&self.mutex)
1586 |                 for ws in self.websockets {
1587 |                     wss.append(ws)
1588 |                 }
1589 |                 for ws in wss {
1590 |                     self.checkForConnectionTimeout(ws)
1591 |                     if ws.dirty {
1592 |                         pthread_mutex_unlock(&self.mutex)
1593 |                         ws.step()
1594 |                         pthread_mutex_lock(&self.mutex)
1595 |                         wait = false
1596 |                     }
1597 |                 }
1598 |                 if wait {
1599 |                     _ = self.wait(250)
1600 |                 }
1601 |                 pthread_mutex_unlock(&self.mutex)
1602 |             }
1603 |         }
1604 |     }
1605 |     func checkForConnectionTimeout(_ ws : InnerWebSocket) {
1606 |         if ws.rd != nil && ws.wr != nil && (ws.rd.streamStatus == .opening || ws.wr.streamStatus == .opening) {
1607 |             let age = CFAbsoluteTimeGetCurrent() - ws.createdAt
1608 |             if age >= timeoutDuration {
1609 |                 ws.connectionTimeout = true
1610 |             }
1611 |         }
1612 |     }
1613 |     func wait(_ timeInMs : Int) -> Int32 {
1614 |         var ts = timespec()
1615 |         var tv = timeval()
1616 |         gettimeofday(&tv, nil)
1617 |         ts.tv_sec = time(nil) + timeInMs / 1000;
1618 |         let v1 = Int(tv.tv_usec * 1000)
1619 |         let v2 = Int(1000 * 1000 * Int(timeInMs % 1000))
1620 |         ts.tv_nsec = v1 + v2;
1621 |         ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000);
1622 |         ts.tv_nsec %= (1000 * 1000 * 1000);
1623 |         return pthread_cond_timedwait(&self.cond, &self.mutex, &ts)
1624 |     }
1625 |     func signal(){
1626 |         pthread_mutex_lock(&mutex)
1627 |         pthread_cond_signal(&cond)
1628 |         pthread_mutex_unlock(&mutex)
1629 |     }
1630 |     func add(_ websocket: InnerWebSocket) {
1631 |         pthread_mutex_lock(&mutex)
1632 |         websockets.insert(websocket)
1633 |         pthread_cond_signal(&cond)
1634 |         pthread_mutex_unlock(&mutex)
1635 |     }
1636 |     func remove(_ websocket: InnerWebSocket) {
1637 |         pthread_mutex_lock(&mutex)
1638 |         websockets.remove(websocket)
1639 |         pthread_cond_signal(&cond)
1640 |         pthread_mutex_unlock(&mutex)
1641 |     }
1642 |     func nextId() -> Int {
1643 |         pthread_mutex_lock(&mutex)
1644 |         defer { pthread_mutex_unlock(&mutex) }
1645 |         _nextId += 1
1646 |         return _nextId
1647 |     }
1648 | }
1649 | 
1650 | private let manager = Manager()
1651 | 
1652 | /// WebSocket objects are bidirectional network streams that communicate over HTTP. RFC 6455.
1653 | @objcMembers
1654 | open class WebSocket: NSObject {
1655 |     fileprivate var ws: InnerWebSocket
1656 |     fileprivate var id = manager.nextId()
1657 |     fileprivate var opened: Bool
1658 | 
1659 |     open override var hash: Int { return id }
1660 |     open override func isEqual(_ other: Any?) -> Bool {
1661 |         guard let other = other as? WebSocket else { return false }
1662 |         return self.id == other.id
1663 |     }
1664 |     
1665 |     /// Create a WebSocket connection to a URL; this should be the URL to which the WebSocket server will respond.
1666 |     public convenience init(_ url: String){
1667 |         self.init(request: URLRequest(url: URL(string: url)!), subProtocols: [])
1668 |     }
1669 |     /// Create a WebSocket connection to a URL; this should be the URL to which the WebSocket server will respond.
1670 |     public convenience init(url: URL){
1671 |         self.init(request: URLRequest(url: url), subProtocols: [])
1672 |     }
1673 |     /// Create a WebSocket connection to a URL; this should be the URL to which the WebSocket server will respond. Also include a list of protocols.
1674 |     public convenience init(_ url: String, subProtocols : [String]){
1675 |         self.init(request: URLRequest(url: URL(string: url)!), subProtocols: subProtocols)
1676 |     }
1677 |     /// Create a WebSocket connection to a URL; this should be the URL to which the WebSocket server will respond. Also include a protocol.
1678 |     public convenience init(_ url: String, subProtocol : String){
1679 |         self.init(request: URLRequest(url: URL(string: url)!), subProtocols: [subProtocol])
1680 |     }
1681 |     /// Create a WebSocket connection from an NSURLRequest; Also include a list of protocols.
1682 |     public init(request: URLRequest, subProtocols : [String] = []){
1683 |         let hasURL = request.url != nil
1684 |         opened = hasURL
1685 |         ws = InnerWebSocket(request: request, subProtocols: subProtocols, stub: !hasURL)
1686 |         super.init()
1687 |         // weak/strong pattern from:
1688 |         // http://stackoverflow.com/a/17105368/424124
1689 |         // https://dhoerl.wordpress.com/2013/04/23/i-finally-figured-out-weakself-and-strongself/
1690 |         ws.eclose = { [weak self] in
1691 |             if let strongSelf = self {
1692 |                 strongSelf.opened = false
1693 |             }
1694 |         }
1695 |     }
1696 |     /// Create a WebSocket object with a deferred connection; the connection is not opened until the .open() method is called.
1697 |     public convenience override init(){
1698 |         var request = URLRequest(url: URL(string: "http://apple.com")!)
1699 |         request.url = nil
1700 |         self.init(request: request, subProtocols: [])
1701 |     }
1702 |     /// The URL as resolved by the constructor. This is always an absolute URL. Read only.
1703 |     open var url : String{ return ws.url }
1704 |     /// A string indicating the name of the sub-protocol the server selected; this will be one of the strings specified in the protocols parameter when creating the WebSocket object.
1705 |     open var subProtocol : String{ return ws.subProtocol }
1706 |     /// The compression options of the WebSocket.
1707 |     open var compression : WebSocketCompression{
1708 |         get { return ws.compression }
1709 |         set { ws.compression = newValue }
1710 |     }
1711 |     /// Allow for Self-Signed SSL Certificates. Default is false.
1712 |     open var allowSelfSignedSSL : Bool{
1713 |         get { return ws.allowSelfSignedSSL }
1714 |         set { ws.allowSelfSignedSSL = newValue }
1715 |     }
1716 |     /// The services of the WebSocket.
1717 |     open var services : WebSocketService{
1718 |         get { return ws.services }
1719 |         set { ws.services = newValue }
1720 |     }
1721 |     /// The events of the WebSocket.
1722 |     open var event : WebSocketEvents{
1723 |         get { return ws.event }
1724 |         set { ws.event = newValue }
1725 |     }
1726 |     /// The queue for firing off events. default is main_queue
1727 |     open var eventQueue : DispatchQueue?{
1728 |         get { return ws.eventQueue }
1729 |         set { ws.eventQueue = newValue }
1730 |     }
1731 |     /// A WebSocketBinaryType value indicating the type of binary data being transmitted by the connection. Default is .UInt8Array.
1732 |     open var binaryType : WebSocketBinaryType{
1733 |         get { return ws.binaryType }
1734 |         set { ws.binaryType = newValue }
1735 |     }
1736 |     /// The current state of the connection; this is one of the WebSocketReadyState constants. Read only.
1737 |     open var readyState : WebSocketReadyState{
1738 |         return ws.readyState
1739 |     }
1740 |     /// Opens a deferred or closed WebSocket connection to a URL; this should be the URL to which the WebSocket server will respond.
1741 |     open func open(_ url: String){
1742 |         open(request: URLRequest(url: URL(string: url)!), subProtocols: [])
1743 |     }
1744 |     /// Opens a deferred or closed WebSocket connection to a URL; this should be the URL to which the WebSocket server will respond.
1745 |     open func open(nsurl url: URL){
1746 |         open(request: URLRequest(url: url), subProtocols: [])
1747 |     }
1748 |     /// Opens a deferred or closed WebSocket connection to a URL; this should be the URL to which the WebSocket server will respond. Also include a list of protocols.
1749 |     open func open(_ url: String, subProtocols : [String]){
1750 |         open(request: URLRequest(url: URL(string: url)!), subProtocols: subProtocols)
1751 |     }
1752 |     /// Opens a deferred or closed WebSocket connection to a URL; this should be the URL to which the WebSocket server will respond. Also include a protocol.
1753 |     open func open(_ url: String, subProtocol : String){
1754 |         open(request: URLRequest(url: URL(string: url)!), subProtocols: [subProtocol])
1755 |     }
1756 |     /// Opens a deferred or closed WebSocket connection from an NSURLRequest; Also include a list of protocols.
1757 |     open func open(request: URLRequest, subProtocols : [String] = []){
1758 |         if opened{
1759 |             return
1760 |         }
1761 |         opened = true
1762 |         ws = ws.copyOpen(request, subProtocols: subProtocols)
1763 |     }
1764 |     /// Opens a closed WebSocket connection from an NSURLRequest; Uses the same request and protocols as previously closed WebSocket
1765 |     open func open(){
1766 |         open(request: ws.request, subProtocols: ws.subProtocols)
1767 |     }
1768 |     /**
1769 |      Closes the WebSocket connection or connection attempt, if any. If the connection is already closed or in the state of closing, this method does nothing.
1770 | 
1771 |      :param: code An integer indicating the status code explaining why the connection is being closed. If this parameter is not specified, a default value of 1000 (indicating a normal closure) is assumed.
1772 |      :param: reason A human-readable string explaining why the connection is closing. This string must be no longer than 123 bytes of UTF-8 text (not characters).
1773 |      */
1774 |     open func close(_ code : Int = 1000, reason : String = "Normal Closure"){
1775 |         if !opened{
1776 |             return
1777 |         }
1778 |         opened = false
1779 |         ws.close(code, reason: reason)
1780 |     }
1781 |     /**
1782 |      Transmits message to the server over the WebSocket connection.
1783 | 
1784 |      :param: message The message to be sent to the server.
1785 |      */
1786 |     open func send(_ message : Any){
1787 |         if !opened{
1788 |             return
1789 |         }
1790 |         ws.send(message)
1791 |     }
1792 |     /**
1793 |      Transmits a ping to the server over the WebSocket connection.
1794 | 
1795 |      :param: optional message The data to be sent to the server.
1796 |      */
1797 |     open func ping(_ message : Any){
1798 |         if !opened{
1799 |             return
1800 |         }
1801 |         ws.ping(message)
1802 |     }
1803 |     /**
1804 |      Transmits a ping to the server over the WebSocket connection.
1805 |      */
1806 |     open func ping(){
1807 |         if !opened{
1808 |             return
1809 |         }
1810 |         ws.ping()
1811 |     }
1812 | }
1813 | 
1814 | public func ==(lhs: WebSocket, rhs: WebSocket) -> Bool {
1815 |     return lhs.id == rhs.id
1816 | }
1817 | 
1818 | extension WebSocket {
1819 |     /// The events of the WebSocket using a delegate.
1820 |     @objc
1821 |     public var delegate : WebSocketDelegate? {
1822 |         get { return ws.eventDelegate }
1823 |         set { ws.eventDelegate = newValue }
1824 |     }
1825 |     /**
1826 |      Transmits message to the server over the WebSocket connection.
1827 | 
1828 |      :param: text The message (string) to be sent to the server.
1829 |      */
1830 |     @objc
1831 |     public func send(text: String){
1832 |         send(text)
1833 |     }
1834 |     /**
1835 |      Transmits message to the server over the WebSocket connection.
1836 | 
1837 |      :param: data The message (binary) to be sent to the server.
1838 |      */
1839 |     @objc
1840 |     public func send(data: Data){
1841 |         send(data)
1842 |     }
1843 | }
1844 | 


--------------------------------------------------------------------------------