├── .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 | [](http://tidwall.com/SwiftWebSocket/docs/)
4 | [](https://developer.apple.com/swift/)
5 | [](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 |
--------------------------------------------------------------------------------