Compare commits
267 Commits
lewis/rxsw
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a023409b1 | ||
|
|
28035e1a98 | ||
|
|
877ac7438b | ||
|
|
20686b9990 | ||
|
|
41b57bb2fc | ||
|
|
04b28a7c56 | ||
|
|
d09212793c | ||
|
|
eff1db8da7 | ||
|
|
103dc2623b | ||
|
|
45f03ed9b9 | ||
|
|
35e965f2c5 | ||
|
|
3e5be70f1b | ||
|
|
fc0aad7844 | ||
|
|
00a8a66240 | ||
|
|
47971ebe5e | ||
|
|
9c2201f099 | ||
|
|
88cdc0b586 | ||
|
|
ee6dd82992 | ||
|
|
abea58207e | ||
|
|
cccbf71a7b | ||
|
|
682be00378 | ||
|
|
85dd530191 | ||
|
|
691241f102 | ||
|
|
ff03b396a0 | ||
|
|
471b99ccde | ||
|
|
130967bcb9 | ||
|
|
ecf7f75de3 | ||
|
|
4184f74f18 | ||
|
|
0632997fe7 | ||
|
|
84424d470a | ||
|
|
f2e6387948 | ||
|
|
ab462e597c | ||
|
|
e698ab30eb | ||
|
|
b666a4559a | ||
|
|
676948f555 | ||
|
|
eda51aba1b | ||
|
|
a944d5fcbb | ||
|
|
e84a8fd596 | ||
|
|
4605a70d61 | ||
|
|
2abdcb9614 | ||
|
|
71a52a57a2 | ||
|
|
8096fef47d | ||
|
|
b4e7932a59 | ||
|
|
fcd482898a | ||
|
|
48465aeaae | ||
|
|
16abb5debd | ||
|
|
e22b67e56a | ||
|
|
52017adfb4 | ||
|
|
29d8e0e832 | ||
|
|
0af058c221 | ||
|
|
86df623629 | ||
|
|
d3beb35574 | ||
|
|
8dafa6908a | ||
|
|
aaab4836c3 | ||
|
|
14d35acf6c | ||
|
|
dc0b2a7ff6 | ||
|
|
49ab8461b7 | ||
|
|
e17d8ce173 | ||
|
|
50399656e5 | ||
|
|
d6934198a3 | ||
|
|
e404333501 | ||
|
|
c1dc8647ce | ||
|
|
d6ed65d084 | ||
|
|
7d0593f1df | ||
|
|
8e203e15c2 | ||
|
|
8c41a73038 | ||
|
|
56e471ae94 | ||
|
|
510d1b1bae | ||
|
|
06b8e936eb | ||
|
|
5ce5f5d1d8 | ||
|
|
8065053898 | ||
|
|
f239c15ed4 | ||
|
|
840c9050c6 | ||
|
|
7e97284178 | ||
|
|
1f122e7de5 | ||
|
|
c25405ec4b | ||
|
|
b0aabde705 | ||
|
|
383c787add | ||
|
|
77a4b66b99 | ||
|
|
1218028be5 | ||
|
|
9872cdc8e3 | ||
|
|
e5afe618b5 | ||
|
|
d8a3a39187 | ||
|
|
c5954964ad | ||
|
|
8835ee3b13 | ||
|
|
de9ac0c9ec | ||
|
|
b33ea540bc | ||
|
|
a2e21423f0 | ||
|
|
d71f74ee2b | ||
|
|
2a25ad1599 | ||
|
|
e465e2c4ff | ||
|
|
23a36beea4 | ||
|
|
2884b57723 | ||
|
|
9a9739af20 | ||
|
|
c85fa8c17c | ||
|
|
8b5ce6a0ea | ||
|
|
792255b6e0 | ||
|
|
b5d9155229 | ||
|
|
836c660c2e | ||
|
|
0a8b420c99 | ||
|
|
a39e0cc00b | ||
|
|
eadb8938eb | ||
|
|
621d3961bb | ||
|
|
00148bbb9e | ||
|
|
615ed2277d | ||
|
|
eca38afd5c | ||
|
|
6f43175832 | ||
|
|
d5f90ac633 | ||
|
|
7d36cd4beb | ||
|
|
331c2fb64e | ||
|
|
baf79e0869 | ||
|
|
111f35258e | ||
|
|
e5dea1008e | ||
|
|
3a27290e55 | ||
|
|
99fd7576e4 | ||
|
|
d0904552ee | ||
|
|
c3320bda72 | ||
|
|
e8efe0bfb2 | ||
|
|
7a62f4869f | ||
|
|
bf9252bcd6 | ||
|
|
396002c4a6 | ||
|
|
cc4e5dc41a | ||
|
|
ba8252279e | ||
|
|
6f5e1d3175 | ||
|
|
6d9bf91425 | ||
|
|
ea95661662 | ||
|
|
3ccc928a62 | ||
|
|
53fd24acd9 | ||
|
|
22a3c97827 | ||
|
|
15f86e8eba | ||
|
|
3b995adaf3 | ||
|
|
b0758413ce | ||
|
|
d358cc0c90 | ||
|
|
d22287c928 | ||
|
|
e11ac5d93d | ||
|
|
069120d9aa | ||
|
|
5b9406d3a8 | ||
|
|
c3abc9a356 | ||
|
|
93bc3e46c2 | ||
|
|
2d029d2084 | ||
|
|
58012cfeb7 | ||
|
|
3fbd9411e9 | ||
|
|
ee395defcf | ||
|
|
fa129d34c8 | ||
|
|
8c228c812e | ||
|
|
8a686b0e7e | ||
|
|
ef22e3a488 | ||
|
|
85030a57c4 | ||
|
|
f491809c9c | ||
|
|
b804836b93 | ||
|
|
9a64f67547 | ||
|
|
0aefc947aa | ||
|
|
e4101f6ad1 | ||
|
|
277ae665d2 | ||
|
|
ca2b585541 | ||
|
|
9613157ead | ||
|
|
685f756f22 | ||
|
|
36e68c11ab | ||
|
|
8016b968b3 | ||
|
|
f661897c4b | ||
|
|
0eee9071aa | ||
|
|
ad3ad77909 | ||
|
|
43c4a5295d | ||
|
|
2d1372590c | ||
|
|
e6dc61452d | ||
|
|
19643946d5 | ||
|
|
802ec4ead2 | ||
|
|
541f7b6a11 | ||
|
|
4c0d9340fa | ||
|
|
8d23062298 | ||
|
|
5c5f115798 | ||
|
|
77e89f07e2 | ||
|
|
6e5de1a8f5 | ||
|
|
521818db64 | ||
|
|
ec3c39cd37 | ||
|
|
9ca0ceefe5 | ||
|
|
b437daeaaa | ||
|
|
b0e0bba262 | ||
|
|
7bff4f0ac3 | ||
|
|
29dbaa8b7b | ||
|
|
cc6fea0a4d | ||
|
|
192e7b769b | ||
|
|
1185f16186 | ||
|
|
0ea20993d3 | ||
|
|
91a2c2b68a | ||
|
|
19f0c5eb1d | ||
|
|
f19d973c8c | ||
|
|
b93aa9097b | ||
|
|
0c31c8b0bb | ||
|
|
45352e4c0e | ||
|
|
818cec9d83 | ||
|
|
af4ddaed75 | ||
|
|
a770a5fd52 | ||
|
|
60cec3a09e | ||
|
|
b11f4be5f6 | ||
|
|
2c95c1364d | ||
|
|
d2230743f0 | ||
|
|
a46422f9b9 | ||
|
|
826ed64078 | ||
|
|
75017d14a8 | ||
|
|
dea0d428f8 | ||
|
|
efe1f98b05 | ||
|
|
ead962232d | ||
|
|
954c9478b5 | ||
|
|
53c17b879c | ||
|
|
9ff02e49d8 | ||
|
|
00279da31e | ||
|
|
8ae2d77943 | ||
|
|
b47e74ed50 | ||
|
|
fd84880ad1 | ||
|
|
9b248a6286 | ||
|
|
bd130dc902 | ||
|
|
51b8e2b0c2 | ||
|
|
dd1bee8c88 | ||
|
|
6e0ca9b68c | ||
|
|
1a12a7c473 | ||
|
|
efb080f712 | ||
|
|
eefd7e0426 | ||
|
|
bf9bffa0b8 | ||
|
|
23dd00dd7e | ||
|
|
21eaa2b7d1 | ||
|
|
00b4b90309 | ||
|
|
60f4713a81 | ||
|
|
dc96e61640 | ||
|
|
b14672c505 | ||
|
|
c1b140e341 | ||
|
|
4d7b75b9b2 | ||
|
|
8449f84211 | ||
|
|
b4c58f8fc1 | ||
|
|
20b501e79e | ||
|
|
94fa0305af | ||
|
|
75ffad8f61 | ||
|
|
bf09594e3f | ||
|
|
371723add4 | ||
|
|
f14f5255cd | ||
|
|
d65f09b667 | ||
|
|
37b196de51 | ||
|
|
8dd567e56e | ||
|
|
21f4b4326b | ||
|
|
3c867d8b00 | ||
|
|
b4c224c888 | ||
|
|
5ba1c8a675 | ||
|
|
db8c68134b | ||
|
|
a9574d3cbd | ||
|
|
483aef0ced | ||
|
|
99be5aebfd | ||
|
|
3ff6038ad9 | ||
|
|
c13b301047 | ||
|
|
04b9c1d135 | ||
|
|
ad7890a6c6 | ||
|
|
bcac6bd87f | ||
|
|
7d734d2b5d | ||
|
|
a1a32d6770 | ||
|
|
3244d812c5 | ||
|
|
1bb40484ff | ||
|
|
cb476d4cb0 | ||
|
|
b434079f84 | ||
|
|
7e5129f951 | ||
|
|
194bc17f2b | ||
|
|
08f26e4f1c | ||
|
|
c02f775d95 | ||
|
|
27390c7516 | ||
|
|
c136536a14 | ||
|
|
2c241b52c9 | ||
|
|
1d4ba4905d | ||
|
|
018abc742f | ||
|
|
a17be5ae3d |
3
.gitignore
vendored
3
.gitignore
vendored
@ -17,7 +17,10 @@ xcuserdata/
|
||||
*.xcworkspace
|
||||
!default.xcworkspace
|
||||
*xcuserdata
|
||||
*.xccheckout
|
||||
profile
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
extern/
|
||||
|
||||
*.pyc
|
||||
|
||||
9
.gitmodules
vendored
9
.gitmodules
vendored
@ -1,6 +1,3 @@
|
||||
[submodule "pages"]
|
||||
path = pages
|
||||
url = git://github.com/square/SocketRocket.git
|
||||
[submodule "Frameworks/RxSwift"]
|
||||
path = Frameworks/RxSwift
|
||||
url = https://github.com/ReactiveX/RxSwift.git
|
||||
[submodule "Vendor/xctoolchain"]
|
||||
path = Vendor/xctoolchain
|
||||
url = https://github.com/ParsePlatform/xctoolchain.git
|
||||
|
||||
1
.ruby-version
Normal file
1
.ruby-version
Normal file
@ -0,0 +1 @@
|
||||
2.3.1
|
||||
47
.travis.yml
47
.travis.yml
@ -1,8 +1,43 @@
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
language: objective-c
|
||||
osx_image: xcode6.4
|
||||
before_script:
|
||||
- bundle install
|
||||
os: osx
|
||||
osx_image: xcode7.3
|
||||
env:
|
||||
matrix:
|
||||
- TEST_TYPE=iOS
|
||||
- TEST_TYPE=OSX
|
||||
- TEST_TYPE=tvOS
|
||||
- TEST_TYPE=CocoaPods
|
||||
- TEST_TYPE=Carthage
|
||||
before_install:
|
||||
- |
|
||||
if [ "$TEST_TYPE" = iOS ] || [ "$TEST_TYPE" = OSX ] || [ "$TEST_TYPE" = tvOS ]; then
|
||||
bundle install
|
||||
elif [ "$TEST_TYPE" = Carthage ]; then
|
||||
brew update
|
||||
brew install carthage || brew upgrade carthage
|
||||
fi
|
||||
install:
|
||||
- |
|
||||
if [ "$TEST_TYPE" = iOS ]; then
|
||||
./TestSupport/setup_env.sh .env
|
||||
fi
|
||||
script:
|
||||
- xcodebuild -project SocketRocket.xcodeproj -scheme "SocketRocket" -sdk iphonesimulator -configuration Debug -PBXBuildsContinueAfterErrors=0 ACTIVE_ARCH_ONLY=0 build test
|
||||
- xcodebuild -project SocketRocket.xcodeproj -scheme "SocketRocketOSX" -sdk macosx10.10 -configuration Debug -destination "platform=OS X" -PBXBuildsContinueAfterErrors=0 build
|
||||
- pod lib lint --verbose --fail-fast
|
||||
- |
|
||||
if [ "$TEST_TYPE" = iOS ]; then
|
||||
set -o pipefail
|
||||
xcodebuild -project SocketRocket.xcodeproj -scheme "SocketRocket-iOS" -sdk iphonesimulator build test
|
||||
elif [ "$TEST_TYPE" = OSX ]; then
|
||||
set -o pipefail
|
||||
xcodebuild -project SocketRocket.xcodeproj -scheme "SocketRocket-macOS" -sdk macosx build | xcpretty -c
|
||||
elif [ "$TEST_TYPE" = tvOS ]; then
|
||||
set -o pipefail
|
||||
xcodebuild -project SocketRocket.xcodeproj -scheme "SocketRocket-tvOS" -sdk appletvsimulator build | xcpretty -c
|
||||
elif [ "$TEST_TYPE" = CocoaPods ]; then
|
||||
pod lib lint SocketRocket.podspec
|
||||
pod lib lint --use-libraries SocketRocket.podspec
|
||||
elif [ "$TEST_TYPE" = Carthage ]; then
|
||||
carthage build --no-skip-current
|
||||
fi
|
||||
|
||||
33
CONTRIBUTING.md
Normal file
33
CONTRIBUTING.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Contributing to SocketRocket
|
||||
We want to make contributing to this project as easy and transparent as possible.
|
||||
|
||||
## Pull Requests
|
||||
We actively welcome your pull requests.
|
||||
|
||||
1. Fork the repo and create your branch from `master`.
|
||||
2. If you've added code that should be tested, add tests.
|
||||
3. If you've changed APIs, update the documentation.
|
||||
4. Ensure the test suite passes.
|
||||
5. Make sure your code lints.
|
||||
6. If you haven't already, complete the Contributor License Agreement ("CLA").
|
||||
|
||||
## Contributor License Agreement ("CLA")
|
||||
In order to accept your pull request, we need you to submit a CLA.
|
||||
You only need to do this once to work on any of Facebook's open source projects.
|
||||
|
||||
Complete your CLA here: <https://code.facebook.com/cla>
|
||||
|
||||
## Issues
|
||||
We use GitHub issues to track public bugs. Please ensure your description is
|
||||
clear and has sufficient instructions to be able to reproduce the issue.
|
||||
|
||||
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
|
||||
disclosure of security bugs. In those cases, please go through the process
|
||||
outlined on that page and do not file a public issue.
|
||||
|
||||
## Coding Style
|
||||
* Most importantly, match the existing code style as much as possible.
|
||||
* Try to keep lines under 140 characters, if possible.
|
||||
|
||||
## License
|
||||
By contributing to SocketRocket, you agree that your contributions will be licensed under its BSD license.
|
||||
1
Configurations/Shared
Symbolic link
1
Configurations/Shared
Symbolic link
@ -0,0 +1 @@
|
||||
../Vendor/xctoolchain/Configurations/
|
||||
17
Configurations/SocketRocket-iOS-Dynamic.xcconfig
Normal file
17
Configurations/SocketRocket-iOS-Dynamic.xcconfig
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#include "Shared/Platform/iOS.xcconfig"
|
||||
#include "Shared/Product/DynamicFramework.xcconfig"
|
||||
|
||||
PRODUCT_NAME = SocketRocket
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.ios
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0
|
||||
|
||||
INFOPLIST_FILE = $(SRCROOT)/SocketRocket/Resources/Info.plist
|
||||
20
Configurations/SocketRocket-iOS.xcconfig
Normal file
20
Configurations/SocketRocket-iOS.xcconfig
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#include "Shared/Platform/iOS.xcconfig"
|
||||
#include "Shared/Product/StaticFramework.xcconfig"
|
||||
|
||||
PRODUCT_NAME = SocketRocket
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.ios
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0
|
||||
|
||||
INFOPLIST_FILE = $(SRCROOT)/SocketRocket/Resources/Info.plist
|
||||
|
||||
OTHER_CFLAGS[sdk=iphoneos9.*] = $(inherited) -fembed-bitcode
|
||||
OTHER_LDFLAGS = $(inherited) -Licucore
|
||||
17
Configurations/SocketRocket-macOS.xcconfig
Normal file
17
Configurations/SocketRocket-macOS.xcconfig
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#include "Shared/Platform/macOS.xcconfig"
|
||||
#include "Shared/Product/DynamicFramework.xcconfig"
|
||||
|
||||
PRODUCT_NAME = SocketRocket
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.macos
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8
|
||||
|
||||
INFOPLIST_FILE = $(SRCROOT)/SocketRocket/Resources/Info.plist
|
||||
16
Configurations/SocketRocket-tvOS.xcconfig
Normal file
16
Configurations/SocketRocket-tvOS.xcconfig
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#include "Shared/Platform/tvOS.xcconfig"
|
||||
#include "Shared/Product/DynamicFramework.xcconfig"
|
||||
|
||||
PRODUCT_NAME = SocketRocket
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.tvos
|
||||
|
||||
INFOPLIST_FILE = $(SRCROOT)/SocketRocket/Resources/Info.plist
|
||||
19
Configurations/SocketRocketTests-iOS.xcconfig
Normal file
19
Configurations/SocketRocketTests-iOS.xcconfig
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#include "Shared/Platform/iOS.xcconfig"
|
||||
#include "Shared/Product/LogicTests.xcconfig"
|
||||
|
||||
PRODUCT_NAME = SocketRocketTests-iOS
|
||||
PRODUCT_MODULE_NAME = SocketRocketTests
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.tests.ios
|
||||
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0
|
||||
|
||||
INFOPLIST_FILE = $(SRCROOT)/Tests/Resources/Info.plist
|
||||
19
Configurations/TestChat-iOS.xcconfig
Normal file
19
Configurations/TestChat-iOS.xcconfig
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#include "Shared/Platform/iOS.xcconfig"
|
||||
#include "Shared/Product/Application.xcconfig"
|
||||
|
||||
PRODUCT_NAME = TestChat
|
||||
PRODUCT_MODULE_NAME = TestChat
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.socketrocket.testchat
|
||||
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0
|
||||
|
||||
INFOPLIST_FILE = $(SRCROOT)/TestChat/TestChat-Info.plist
|
||||
@ -1 +0,0 @@
|
||||
Subproject commit 1c47f0a79231f889a6c954023d857a3b5344ac88
|
||||
3
Gemfile
3
Gemfile
@ -1,3 +1,4 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'cocoapods', '0.37.2'
|
||||
gem 'cocoapods'
|
||||
gem 'xcpretty'
|
||||
|
||||
35
LICENSE
35
LICENSE
@ -1,15 +1,30 @@
|
||||
BSD License
|
||||
|
||||
Copyright 2012 Square Inc.
|
||||
For SocketRocket software
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name Facebook nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
11
LICENSE-examples
Normal file
11
LICENSE-examples
Normal file
@ -0,0 +1,11 @@
|
||||
Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
|
||||
|
||||
The examples provided by Facebook are for non-commercial testing and evaluation
|
||||
purposes only. Facebook reserves all rights not expressly granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
22
Makefile
22
Makefile
@ -1,20 +1,30 @@
|
||||
TEST_SCENARIOS="[1-8]*"
|
||||
TEST_URL='ws://localhost:9001/'
|
||||
|
||||
test:
|
||||
all:
|
||||
$(MAKE) -C SocketRocket
|
||||
|
||||
clean:
|
||||
$(MAKE) -C SocketRocket clean
|
||||
|
||||
.env:
|
||||
|
||||
./TestSupport/setup_env.sh .env
|
||||
|
||||
test: .env
|
||||
|
||||
mkdir -p pages/results
|
||||
bash ./TestSupport/run_test.sh $(TEST_SCENARIOS) $(TEST_URL) Debug || open pages/results/index.html && false
|
||||
bash ./TestSupport/run_test_server.sh $(TEST_SCENARIOS) $(TEST_URL) Debug || open pages/results/index.html && false
|
||||
open pages/results/index.html
|
||||
|
||||
test_all:
|
||||
test_all: .env
|
||||
|
||||
mkdir -p pages/results
|
||||
bash ./TestSupport/run_test.sh '*' $(TEST_URL) Debug || open pages/results/index.html && false
|
||||
bash ./TestSupport/run_test_server.sh '*' $(TEST_URL) Debug || open pages/results/index.html && false
|
||||
open pages/results/index.html
|
||||
|
||||
test_perf:
|
||||
test_perf: .env
|
||||
|
||||
mkdir -p pages/results
|
||||
bash ./TestSupport/run_test.sh '9.*' $(TEST_URL) Release || open pages/results/index.html && false
|
||||
bash ./TestSupport/run_test_server.sh '9.*' $(TEST_URL) Release || open pages/results/index.html && false
|
||||
open pages/results/index.html
|
||||
|
||||
33
PATENTS
Normal file
33
PATENTS
Normal file
@ -0,0 +1,33 @@
|
||||
Additional Grant of Patent Rights Version 2
|
||||
|
||||
"Software" means the SocketRocket software distributed by Facebook, Inc.
|
||||
|
||||
Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
|
||||
("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
|
||||
(subject to the termination provision below) license under any Necessary
|
||||
Claims, to make, have made, use, sell, offer to sell, import, and otherwise
|
||||
transfer the Software. For avoidance of doubt, no license is granted under
|
||||
Facebook’s rights in any patent claims that are infringed by (i) modifications
|
||||
to the Software made by you or any third party or (ii) the Software in
|
||||
combination with any software or other technology.
|
||||
|
||||
The license granted hereunder will terminate, automatically and without notice,
|
||||
if you (or any of your subsidiaries, corporate affiliates or agents) initiate
|
||||
directly or indirectly, or take a direct financial interest in, any Patent
|
||||
Assertion: (i) against Facebook or any of its subsidiaries or corporate
|
||||
affiliates, (ii) against any party if such Patent Assertion arises in whole or
|
||||
in part from any software, technology, product or service of Facebook or any of
|
||||
its subsidiaries or corporate affiliates, or (iii) against any party relating
|
||||
to the Software. Notwithstanding the foregoing, if Facebook or any of its
|
||||
subsidiaries or corporate affiliates files a lawsuit alleging patent
|
||||
infringement against you in the first instance, and you respond by filing a
|
||||
patent infringement counterclaim in that lawsuit against that party that is
|
||||
unrelated to the Software, the license granted hereunder will not terminate
|
||||
under section (i) of this paragraph due to such counterclaim.
|
||||
|
||||
A "Necessary Claim" is a claim of a patent owned by Facebook that is
|
||||
necessarily infringed by the Software standing alone.
|
||||
|
||||
A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
|
||||
or contributory infringement or inducement to infringe any patent, including a
|
||||
cross-claim or counterclaim.
|
||||
213
README.md
Normal file
213
README.md
Normal file
@ -0,0 +1,213 @@
|
||||
# SocketRocket
|
||||
|
||||
![Platforms][platforms-svg]
|
||||
[![License][license-svg]][license-link]
|
||||
|
||||
[![Podspec][podspec-svg]][podspec-link]
|
||||
[![Carthage Compatible][carthage-svg]](carthage-link)
|
||||
|
||||
[![Build Status][build-status-svg]][build-status-link]
|
||||
|
||||
A conforming WebSocket ([RFC 6455](https://tools.ietf.org/html/rfc6455>)) client library for iOS, macOS and tvOS.
|
||||
|
||||
Test results for SocketRocket [here](http://facebook.github.io/SocketRocket/results/).
|
||||
You can compare to what modern browsers look like [here](http://autobahn.ws/testsuite/reports/clients/index.html).
|
||||
|
||||
SocketRocket currently conforms to all core ~300 of [Autobahn](http://autobahn.ws/testsuite/>)'s fuzzing tests
|
||||
(aside from two UTF-8 ones where it is merely *non-strict* tests 6.4.2 and 6.4.4).
|
||||
|
||||
## Features/Design
|
||||
|
||||
- TLS (wss) support, including self-signed certificates.
|
||||
- Seems to perform quite well.
|
||||
- Supports HTTP Proxies.
|
||||
- Supports IPv4/IPv6.
|
||||
- Supports SSL certificate pinning.
|
||||
- Sends `ping` and can process `pong` events.
|
||||
- Asynchronous and non-blocking. Most of the work is done on a background thread.
|
||||
- Supports iOS, macOS, tvOS.
|
||||
|
||||
## Installing
|
||||
|
||||
There are a few options. Choose one, or just figure it out:
|
||||
|
||||
- **[CocoaPods](https://cocoapods.org)**
|
||||
|
||||
Add the following line to your Podfile:
|
||||
```ruby
|
||||
pod 'SocketRocket'
|
||||
```
|
||||
Run `pod install`, and you are all set.
|
||||
|
||||
- **[Carthage](https://github.com/carthage/carthage)**
|
||||
|
||||
Add the following line to your Cartfile:
|
||||
```
|
||||
github "facebook/SocketRocket"
|
||||
```
|
||||
Run `carthage update`, and you should now have the latest version of `SocketRocket` in your `Carthage` folder.
|
||||
|
||||
- **Using SocketRocket as a sub-project**
|
||||
|
||||
You can also include `SocketRocket` as a subproject inside of your application if you'd prefer, although we do not recommend this, as it will increase your indexing time significantly. To do so, just drag and drop the `SocketRocket.xcodeproj` file into your workspace.
|
||||
|
||||
## API
|
||||
|
||||
### `SRWebSocket`
|
||||
|
||||
The Web Socket.
|
||||
|
||||
#### Note:
|
||||
|
||||
`SRWebSocket` will retain itself between `-(void)open` and when it closes, errors, or fails.
|
||||
This is similar to how `NSURLConnection` behaves (unlike `NSURLConnection`, `SRWebSocket` won't retain the delegate).
|
||||
|
||||
#### Interface
|
||||
|
||||
```objective-c
|
||||
@interface SRWebSocket : NSObject
|
||||
|
||||
// Make it with this
|
||||
- (instancetype)initWithURLRequest:(NSURLRequest *)request;
|
||||
|
||||
// Set this before opening
|
||||
@property (nonatomic, weak) id <SRWebSocketDelegate> delegate;
|
||||
|
||||
// Open with this
|
||||
- (void)open;
|
||||
|
||||
// Close it with this
|
||||
- (void)close;
|
||||
|
||||
// Send a Data
|
||||
- (void)sendData:(nullable NSData *)data error:(NSError **)error;
|
||||
|
||||
// Send a UTF8 String
|
||||
- (void)sendString:(NSString *)string error:(NSError **)error;
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
### `SRWebSocketDelegate`
|
||||
|
||||
You implement this
|
||||
|
||||
```objective-c
|
||||
@protocol SRWebSocketDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(NSString *)string;
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithData:(NSData *)data;
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(nullable NSString *)reason wasClean:(BOOL)wasClean;
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Included are setup scripts for the python testing environment.
|
||||
It comes packaged with vitualenv so all the dependencies are installed in userland.
|
||||
|
||||
To run the short test from the command line, run:
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
|
||||
To run all the tests, run:
|
||||
```bash
|
||||
make test_all
|
||||
```
|
||||
|
||||
The short tests don't include the performance tests
|
||||
(the test harness is actually the bottleneck, not SocketRocket).
|
||||
|
||||
The first time this is run, it may take a while to install the dependencies. It will be smooth sailing after that.
|
||||
|
||||
You can also run tests inside Xcode, which runs the same thing, but makes it easier to debug.
|
||||
|
||||
- Choose the `SocketRocket` target
|
||||
- Run the test action (`⌘+U`)
|
||||
|
||||
### TestChat Demo Application
|
||||
|
||||
SocketRocket includes a demo app, TestChat.
|
||||
It will "chat" with a listening websocket on port 9900.
|
||||
|
||||
#### TestChat Server
|
||||
|
||||
The sever takes a message and broadcasts it to all other connected clients.
|
||||
|
||||
It requires some dependencies though to run.
|
||||
We also want to reuse the virtualenv we made when we ran the tests.
|
||||
If you haven't run the tests yet, go into the SocketRocket root directory and type:
|
||||
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
|
||||
This will set up your [virtualenv](https://pypi.python.org/pypi/virtualenv).
|
||||
|
||||
Now, in your terminal:
|
||||
|
||||
```bash
|
||||
source .env/bin/activate
|
||||
pip install git+https://github.com/tornadoweb/tornado.git
|
||||
```
|
||||
|
||||
In the same terminal session, start the chatroom server:
|
||||
|
||||
```bash
|
||||
python TestChatServer/py/chatroom.py
|
||||
```
|
||||
|
||||
There's also a Go implementation (with the latest weekly) where you can:
|
||||
|
||||
```bash
|
||||
cd TestChatServer/go
|
||||
go run chatroom.go
|
||||
```
|
||||
|
||||
#### Chatting
|
||||
|
||||
Now, start TestChat.app (just run the target in the Xcode project).
|
||||
If you had it started already you can hit the refresh button to reconnect.
|
||||
It should say "Connected!" on top.
|
||||
|
||||
To talk with the app, open up your browser to [http://localhost:9000](http://localhost:9000) and start chatting.
|
||||
|
||||
|
||||
## WebSocket Server Implementation Recommendations
|
||||
|
||||
SocketRocket has been used with the following libraries:
|
||||
|
||||
- [Tornado](https://github.com/tornadoweb/tornado)
|
||||
- Go's [WebSocket package](https://godoc.org/golang.org/x/net/websocket) or Gorilla's [version](http://www.gorillatoolkit.org/pkg/websocket).
|
||||
- [Autobahn](http://autobahn.ws/testsuite/) (using its fuzzing client).
|
||||
|
||||
The Tornado one is dirt simple and works like a charm.
|
||||
([IPython notebook](http://ipython.org/ipython-doc/dev/interactive/htmlnotebook.html) uses it too).
|
||||
It's much easier to configure handlers and routes than in Autobahn/twisted.
|
||||
|
||||
## Contributing
|
||||
|
||||
We’re glad you’re interested in SocketRocket, and we’d love to see where you take it.
|
||||
Please read our [contributing guidelines](https://github.com/facebook/SocketRocket/blob/master/CONTRIBUTING.md) prior to submitting a Pull Request.
|
||||
|
||||
[build-status-svg]: https://img.shields.io/travis/facebook/SocketRocket/master.svg
|
||||
[build-status-link]: https://travis-ci.org/facebook/SocketRocket/branches
|
||||
|
||||
[license-svg]: https://img.shields.io/badge/license-BSD-lightgrey.svg
|
||||
[license-link]: https://github.com/facebook/SocketRocket/blob/master/LICENSE
|
||||
|
||||
[podspec-svg]: https://img.shields.io/cocoapods/v/SocketRocket.svg
|
||||
[podspec-link]: https://cocoapods.org/pods/SocketRocket
|
||||
|
||||
[carthage-svg]: https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat
|
||||
[carthage-link]: https://github.com/carthage/carthage
|
||||
|
||||
[platforms-svg]: http://img.shields.io/cocoapods/p/SocketRocket.svg?style=flat
|
||||
255
README.rst
255
README.rst
@ -1,255 +0,0 @@
|
||||
SocketRocket Objective-C WebSocket Client (beta)
|
||||
================================================
|
||||
A conforming WebSocket (`RFC 6455 <http://tools.ietf.org/html/rfc6455>`_)
|
||||
client library.
|
||||
|
||||
`Test results for SocketRocket here <http://square.github.com/SocketRocket/results/>`_.
|
||||
You can compare to what `modern browsers look like here
|
||||
<http://www.tavendo.de/autobahn/testsuite/report/clients/index.html>`_.
|
||||
|
||||
SocketRocket currently conforms to all ~300 of `Autobahn
|
||||
<http://www.tavendo.de/autobahn/testsuite.html>`_'s fuzzing tests (aside from
|
||||
two UTF-8 ones where it is merely *non-strict*. tests 6.4.2 and 6.4.4)
|
||||
|
||||
Features/Design
|
||||
---------------
|
||||
- TLS (wss) support. It uses CFStream so we get this for *free*
|
||||
- Uses NSStream/CFNetworking. Earlier implementations used ``dispatch_io``,
|
||||
however, this proved to be make TLS nearly impossible. Also I wanted this to
|
||||
work in iOS 4.x. (SocketRocket only supports 5.0 and above now)
|
||||
- Uses ARC. It uses the 4.0 compatible subset (no weak refs).
|
||||
- Seems to perform quite well
|
||||
- Parallel architecture. Most of the work is done in background worker queues.
|
||||
- Delegate-based. Had older versions that could use blocks too, but I felt it
|
||||
didn't blend well with retain cycles and just objective C in general.
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
v0.3.1-beta2 - 2013-01-12
|
||||
`````````````````````````
|
||||
|
||||
- Stability fix for ``closeWithCode:reason:`` (Thanks @michaelpetrov!)
|
||||
- Actually clean up the NSStreams and remove them from their runloops
|
||||
- ``_SRRunLoopThread``'s ``main`` wasn't correctly wrapped with
|
||||
``@autoreleasepool``
|
||||
|
||||
v0.3.1-beta1 - 2013-01-12
|
||||
`````````````````````````
|
||||
|
||||
- Cleaned up GCD so OS_OBJECT_USE_OBJC_RETAIN_RELEASE is optional
|
||||
- Removed deprecated ``dispatch_get_current_queue`` in favor of ``dispatch_queue_set_specific`` and ``dispatch_get_specific``
|
||||
- Dropping support for iOS 4.0 (it may still work)
|
||||
|
||||
|
||||
Installing (iOS)
|
||||
----------------
|
||||
There's a few options. Choose one, or just figure it out
|
||||
|
||||
- You can copy all the files in the SocketRocket group into your app.
|
||||
- Include SocketRocket as a subproject and use libSocketRocket
|
||||
|
||||
If you do this, you must add -ObjC to your "other linker flags" option
|
||||
|
||||
- For OS X you will have to repackage make a .framework target. I will take
|
||||
contributions. Message me if you are interested.
|
||||
|
||||
|
||||
Depending on how you configure your project you may need to ``#import`` either
|
||||
``<SocketRocket/SRWebSocket.h>`` or ``"SRWebSocket.h"``
|
||||
|
||||
Framework Dependencies
|
||||
``````````````````````
|
||||
Your .app must be linked against the following frameworks/dylibs
|
||||
|
||||
- libicucore.dylib
|
||||
- CFNetwork.framework
|
||||
- Security.framework
|
||||
- Foundation.framework
|
||||
|
||||
Installing (OS X)
|
||||
-----------------
|
||||
SocketRocket now has (64-bit only) OS X support. ``SocketRocket.framework``
|
||||
inside Xcode project is for OS X only. It should be identical in function aside
|
||||
from the unicode validation. ICU isn't shipped with OS X which is what the
|
||||
original implementation used for unicode validation. The workaround is much
|
||||
more rudimentary and less robust.
|
||||
|
||||
1. Add SocketRocket.xcodeproj as either a subproject of your app or in your workspace.
|
||||
2. Add ``SocketRocket.framework`` to the link libraries
|
||||
3. If you don't have a "copy files" step for ``Framework``, create one
|
||||
4. Add ``SocketRocket.framework`` to the "copy files" step.
|
||||
|
||||
API
|
||||
---
|
||||
The classes
|
||||
|
||||
``SRWebSocket``
|
||||
```````````````
|
||||
The Web Socket.
|
||||
|
||||
.. note:: ``SRWebSocket`` will retain itself between ``-(void)open`` and when it
|
||||
closes, errors, or fails. This is similar to how ``NSURLConnection`` behaves.
|
||||
(unlike ``NSURLConnection``, ``SRWebSocket`` won't retain the delegate)
|
||||
|
||||
What you need to know
|
||||
|
||||
.. code-block:: objective-c
|
||||
|
||||
@interface SRWebSocket : NSObject
|
||||
|
||||
// Make it with this
|
||||
- (id)initWithURLRequest:(NSURLRequest *)request;
|
||||
|
||||
// Set this before opening
|
||||
@property (nonatomic, assign) id <SRWebSocketDelegate> delegate;
|
||||
|
||||
- (void)open;
|
||||
|
||||
// Close it with this
|
||||
- (void)close;
|
||||
|
||||
// Send a UTF8 String or Data
|
||||
- (void)send:(id)data;
|
||||
|
||||
@end
|
||||
|
||||
``SRWebSocketDelegate``
|
||||
```````````````````````
|
||||
You implement this
|
||||
|
||||
.. code-block:: objective-c
|
||||
|
||||
@protocol SRWebSocketDelegate <NSObject>
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
|
||||
|
||||
@optional
|
||||
|
||||
- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
|
||||
|
||||
@end
|
||||
|
||||
Known Issues/Server Todo's
|
||||
--------------------------
|
||||
- Needs auth delegates (like in NSURLConnection)
|
||||
- Move the streams off the main runloop (most of the work is backgrounded uses
|
||||
GCD, but I just haven't gotten around to moving it off the main loop since I
|
||||
converted it from dispatch_io)
|
||||
- Re-implement server. I removed an existing implementation as well because it
|
||||
wasn't being used and I wasn't super happy with the interface. Will revisit
|
||||
this.
|
||||
- Separate framer and client logic. This will make it nicer when having a
|
||||
server.
|
||||
|
||||
Testing
|
||||
-------
|
||||
Included are setup scripts for the python testing environment. It comes
|
||||
packaged with vitualenv so all the dependencies are installed in userland.
|
||||
|
||||
To run the short test from the command line, run::
|
||||
|
||||
make test
|
||||
|
||||
To run all the tests, run::
|
||||
|
||||
make test_all
|
||||
|
||||
The short tests don't include the performance tests. (the test harness is
|
||||
actually the bottleneck, not SocketRocket).
|
||||
|
||||
The first time this is run, it may take a while to install the dependencies. It
|
||||
will be smooth sailing after that. After the test runs the makefile will open
|
||||
the results page in your browser. If nothing comes up, you failed. Working on
|
||||
making this interface a bit nicer.
|
||||
|
||||
To run from the app, choose the ``SocketRocket`` target and run the test action
|
||||
(``cmd+u``). It runs the same thing, but makes it easier to debug. There is
|
||||
some serious pre/post hooks in the Test action. You can edit it to customize
|
||||
behavior.
|
||||
|
||||
.. note:: Xcode only up to version 4.4 is currently supported for the test
|
||||
harness
|
||||
|
||||
TestChat Demo Application
|
||||
-------------------------
|
||||
SocketRocket includes a demo app, TestChat. It will "chat" with a listening
|
||||
websocket on port 9900.
|
||||
|
||||
It's a simple project. Uses storyboard. Storyboard is sweet.
|
||||
|
||||
|
||||
TestChat Server
|
||||
```````````````
|
||||
We've included a small server for the chat app. It has a simple function.
|
||||
It will take a message and broadcast it to all other connected clients.
|
||||
|
||||
We have to get some dependencies. We also want to reuse the virtualenv we made
|
||||
when we ran the tests. If you haven't run the tests yet, go into the
|
||||
SocketRocket root directory and type::
|
||||
|
||||
make test
|
||||
|
||||
This will set up your `virtualenv <http://pypi.python.org/pypi/virtualenv>`_.
|
||||
Now, in your terminal::
|
||||
|
||||
source .env/bin/activate
|
||||
pip install git+https://github.com/facebook/tornado.git
|
||||
|
||||
In the same terminal session, start the chatroom server::
|
||||
|
||||
python TestChatServer/py/chatroom.py
|
||||
|
||||
There's also a Go implementation (with the latest weekly) where you can::
|
||||
|
||||
cd TestChatServer/go
|
||||
go run chatroom.go
|
||||
|
||||
Chatting
|
||||
````````
|
||||
Now, start TestChat.app (just run the target in the XCode project). If you had
|
||||
it started already you can hit the refresh button to reconnect. It should say
|
||||
"Connected!" on top.
|
||||
|
||||
To talk with the app, open up your browser to `http://localhost:9000 <http://localhost:9000>`_ and
|
||||
start chatting.
|
||||
|
||||
|
||||
WebSocket Server Implementation Recommendations
|
||||
-----------------------------------------------
|
||||
SocketRocket has been used with the following libraries:
|
||||
|
||||
- `Tornado <https://github.com/facebook/tornado>`_
|
||||
- Go's `WebSocket package <http://godoc.org/code.google.com/p/go.net/websocket>`_ or Gorilla's `version <http://www.gorillatoolkit.org/pkg/websocket>`_
|
||||
- `Autobahn <http://www.tavendo.de/autobahn/testsuite.html>`_ (using its fuzzing
|
||||
client)
|
||||
|
||||
The Tornado one is dirt simple and works like a charm. (`IPython notebook
|
||||
<http://ipython.org/ipython-doc/dev/interactive/htmlnotebook.html>`_ uses it
|
||||
too). It's much easier to configure handlers and routes than in
|
||||
Autobahn/twisted.
|
||||
|
||||
As far as Go's goes, it works in my limited testing. I much prefer go's
|
||||
concurrency model as well. Try it! You may like it.
|
||||
It could use some more control over things such as pings, etc., but I
|
||||
am sure it will come in time.
|
||||
|
||||
Autobahn is a great test suite. The Python server code is good, and conforms
|
||||
well (obviously). However for me, twisted would be a deal-breaker for writing
|
||||
something new. I find it a bit too complex and heavy for a simple service. If
|
||||
you are already using twisted though, Autobahn is probably for you.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
Any contributors to the master SocketRocket repository must sign the `Individual
|
||||
Contributor License Agreement
|
||||
(CLA)
|
||||
<https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1>`_.
|
||||
It's a short form that covers our bases and makes sure you're eligible to
|
||||
contribute.
|
||||
|
||||
When you have a change you'd like to see in the master repository, `send a pull
|
||||
request <https://github.com/square/SocketRocket/pulls>`_. Before we merge your
|
||||
request, we'll make sure you're in the list of people who have signed a CLA.
|
||||
@ -1,339 +0,0 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <XCTest/XCTestRun.h>
|
||||
#import "SRWebSocket.h"
|
||||
#import "SRTWebSocketOperation.h"
|
||||
#import "SenTestCase+SRTAdditions.h"
|
||||
|
||||
#define SRLogDebug(format, ...)
|
||||
//#define SRLogDebug(format, ...) NSLog(format, __VA_ARGS__)
|
||||
|
||||
@interface SRTAutobahnTests : XCTestCase
|
||||
@end
|
||||
|
||||
@interface TestOperation : SRTWebSocketOperation <SRWebSocketDelegate>
|
||||
- (id)initWithBaseURL:(NSURL *)url testNumber:(NSInteger)testNumber agent:(NSString *)agent;
|
||||
@end
|
||||
|
||||
|
||||
@interface CaseGetterOperation : SRTWebSocketOperation <SRWebSocketDelegate>
|
||||
- (id)initWithBaseURL:(NSURL *)url;
|
||||
|
||||
@property (nonatomic, readonly) NSInteger caseCount;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSInvocation (SRTBlockInvocation)
|
||||
|
||||
+ (NSInvocation *)invocationWithBlock:(dispatch_block_t)block;
|
||||
|
||||
@end
|
||||
|
||||
@interface SRTBlockInvoker
|
||||
|
||||
- (id)initWithBlock:(dispatch_block_t)block;
|
||||
|
||||
- (void)invoke;
|
||||
|
||||
@end
|
||||
|
||||
@interface UpdateOperation : SRTWebSocketOperation <SRWebSocketDelegate>
|
||||
|
||||
- (id)initWithBaseURL:(NSURL *)url agent:(NSString *)agent;
|
||||
|
||||
@end
|
||||
|
||||
@interface TestInfoOperation : SRTWebSocketOperation <SRWebSocketDelegate>
|
||||
|
||||
@property (nonatomic) NSDictionary *info;
|
||||
|
||||
- (id)initWithBaseURL:(NSURL *)url caseNumber:(NSInteger)caseNumber;
|
||||
|
||||
@end
|
||||
|
||||
@interface TestResultsOperation : SRTWebSocketOperation <SRWebSocketDelegate>
|
||||
|
||||
@property (nonatomic) NSDictionary *info;
|
||||
|
||||
- (id)initWithBaseURL:(NSURL *)url caseNumber:(NSInteger)caseNumber agent:(NSString *)agent;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SRTAutobahnTests {
|
||||
SRWebSocket *_curWebSocket;
|
||||
NSInteger _testCount;
|
||||
NSInteger _curTest;
|
||||
NSMutableArray *_sockets;
|
||||
NSString *_testURLString;
|
||||
NSURL *_prefixURL;
|
||||
NSString *_agent;
|
||||
NSString *_description;
|
||||
}
|
||||
|
||||
- (id)initWithInvocation:(NSInvocation *)anInvocation description:(NSString *)description;
|
||||
{
|
||||
self = [self initWithInvocation:anInvocation];
|
||||
if (self) {
|
||||
_description = description;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithInvocation:(NSInvocation *)anInvocation;
|
||||
{
|
||||
self = [super initWithInvocation:anInvocation];
|
||||
if (self) {
|
||||
_testURLString = [[NSProcessInfo processInfo].environment objectForKey:@"SR_TEST_URL"];
|
||||
_prefixURL = [NSURL URLWithString:_testURLString];
|
||||
_agent = [NSBundle bundleForClass:[self class]].bundleIdentifier;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)testCaseCount;
|
||||
{
|
||||
return 0;
|
||||
if (self.invocation) {
|
||||
return [super testCaseCount];
|
||||
}
|
||||
|
||||
CaseGetterOperation *caseGetter = [[CaseGetterOperation alloc] initWithBaseURL:_prefixURL];
|
||||
|
||||
[caseGetter start];
|
||||
|
||||
[self runCurrentRunLoopUntilTestPasses:^BOOL{
|
||||
return caseGetter.isFinished;
|
||||
} timeout:20.0];
|
||||
|
||||
XCTAssertNil(caseGetter.error, @"CaseGetter should have successfully returned the number of testCases. Instead got error %@", caseGetter.error);
|
||||
|
||||
NSInteger caseCount = caseGetter.caseCount;
|
||||
|
||||
return caseCount;
|
||||
}
|
||||
|
||||
- (BOOL)isEmpty;
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)performTest:(XCTestCaseRun *) aRun
|
||||
{
|
||||
if (self.invocation) {
|
||||
[super performTest:aRun];
|
||||
return;
|
||||
}
|
||||
[aRun start];
|
||||
for (NSUInteger i = 1; i <= aRun.test.testCaseCount; i++) {
|
||||
SEL sel = @selector(performTestWithNumber:);
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:sel]];
|
||||
|
||||
invocation.selector = sel;
|
||||
invocation.target = self;
|
||||
|
||||
[invocation setArgument:&i atIndex:2];
|
||||
|
||||
NSString *description = [self caseDescriptionForCaseNumber:i];
|
||||
|
||||
XCTestCase *testCase = [[[self class] alloc] initWithInvocation:invocation description:description];
|
||||
|
||||
XCTestCaseRun *run = [[XCTestCaseRun alloc] initWithTest:testCase];
|
||||
|
||||
[testCase performTest:run];
|
||||
}
|
||||
[aRun stop];
|
||||
|
||||
[self updateReports];
|
||||
}
|
||||
|
||||
- (NSInteger)testNum;
|
||||
{
|
||||
NSInteger i;
|
||||
[self.invocation getArgument:&i atIndex:2];
|
||||
return i;
|
||||
}
|
||||
|
||||
- (NSString *)caseDescriptionForCaseNumber:(NSInteger)caseNumber;
|
||||
{
|
||||
TestInfoOperation *testInfoOperation = [[TestInfoOperation alloc] initWithBaseURL:_prefixURL caseNumber:caseNumber];
|
||||
|
||||
[testInfoOperation start];
|
||||
|
||||
[self runCurrentRunLoopUntilTestPasses:^BOOL{
|
||||
return testInfoOperation.isFinished;
|
||||
} timeout:60 * 60];
|
||||
|
||||
XCTAssertNil(testInfoOperation.error, @"Updating the report should not have errored");
|
||||
|
||||
return [NSString stringWithFormat:@"%@ - %@", [testInfoOperation.info objectForKey:@"id"], [testInfoOperation.info objectForKey:@"description"]];
|
||||
}
|
||||
|
||||
- (NSString *)description;
|
||||
{
|
||||
if (_description) {
|
||||
return _description;
|
||||
} else {
|
||||
return @"Autobahn Test Harness";
|
||||
}
|
||||
}
|
||||
|
||||
+ (id) defaultTestSuite
|
||||
{
|
||||
return [[[self class] alloc] init];
|
||||
}
|
||||
|
||||
- (void)performTestWithNumber:(NSInteger)testNumber;
|
||||
{
|
||||
NSOperationQueue *testQueue = [[NSOperationQueue alloc] init];
|
||||
|
||||
testQueue.maxConcurrentOperationCount = 1;
|
||||
|
||||
TestOperation *testOp = [[TestOperation alloc] initWithBaseURL:_prefixURL testNumber:testNumber agent:_agent];
|
||||
[testQueue addOperation:testOp];
|
||||
|
||||
TestResultsOperation *resultOp = [[TestResultsOperation alloc] initWithBaseURL:_prefixURL caseNumber:testNumber agent:_agent];
|
||||
[resultOp addDependency:testOp];
|
||||
[testQueue addOperation:resultOp];
|
||||
|
||||
testQueue.suspended = NO;
|
||||
|
||||
[self runCurrentRunLoopUntilTestPasses:^BOOL{
|
||||
return resultOp.isFinished;
|
||||
} timeout:60 * 60];
|
||||
|
||||
XCTAssertTrue(!testOp.error, @"Test operation should not have failed");
|
||||
XCTAssertEqualObjects(@"OK", [resultOp.info objectForKey:@"behavior"], @"Test behavior should be OK");
|
||||
}
|
||||
|
||||
- (void)updateReports;
|
||||
{
|
||||
UpdateOperation *updateReportOperation = [[UpdateOperation alloc] initWithBaseURL:_prefixURL agent:_agent];
|
||||
|
||||
[updateReportOperation start];
|
||||
|
||||
[self runCurrentRunLoopUntilTestPasses:^BOOL{
|
||||
return updateReportOperation.isFinished;
|
||||
} timeout:60 * 60];
|
||||
|
||||
XCTAssertNil(updateReportOperation.error, @"Updating the report should not have errored");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TestOperation {
|
||||
NSInteger _testNumber;
|
||||
}
|
||||
|
||||
- (id)initWithBaseURL:(NSURL *)url testNumber:(NSInteger)testNumber agent:(NSString *)agent;
|
||||
{
|
||||
|
||||
NSString *path = [[url URLByAppendingPathComponent:@"runCase"] absoluteString];
|
||||
path = [path stringByAppendingFormat:@"?case=%@&agent=%@", @(testNumber), agent];
|
||||
|
||||
self = [super initWithURL:[NSURL URLWithString:path]];
|
||||
if (self) {
|
||||
_testNumber = testNumber;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
|
||||
{
|
||||
[webSocket send:message];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CaseGetterOperation
|
||||
|
||||
@synthesize caseCount = _caseCount;
|
||||
|
||||
- (id)initWithBaseURL:(NSURL *)url;
|
||||
{
|
||||
self = [super initWithURL:[url URLByAppendingPathComponent:@"getCaseCount"]];
|
||||
if (self) {
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
|
||||
{
|
||||
_caseCount = [message integerValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation UpdateOperation
|
||||
|
||||
- (id)initWithBaseURL:(NSURL *)url agent:(NSString *)agent;
|
||||
{
|
||||
NSString *path = [[url URLByAppendingPathComponent:@"updateReports"] absoluteString];
|
||||
path = [path stringByAppendingFormat:@"?agent=%@", agent];
|
||||
|
||||
return [super initWithURL:[NSURL URLWithString:path]];
|
||||
}
|
||||
|
||||
- (void)start;
|
||||
{
|
||||
[super start];
|
||||
NSLog(@"Updating Reports!");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation TestInfoOperation
|
||||
|
||||
@synthesize info = _info;
|
||||
|
||||
- (id)initWithBaseURL:(NSURL *)url caseNumber:(NSInteger)caseNumber;
|
||||
{
|
||||
NSString *path = [[url URLByAppendingPathComponent:@"getCaseInfo"] absoluteString];
|
||||
path = [path stringByAppendingFormat:@"?case=%@", @(caseNumber)];
|
||||
|
||||
return [super initWithURL:[NSURL URLWithString:path]];
|
||||
}
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(NSString *)message;
|
||||
{
|
||||
self.info = [NSJSONSerialization JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation TestResultsOperation
|
||||
|
||||
@synthesize info = _info;
|
||||
|
||||
- (id)initWithBaseURL:(NSURL *)url caseNumber:(NSInteger)caseNumber agent:(NSString *)agent;
|
||||
{
|
||||
NSString *path = [[url URLByAppendingPathComponent:@"getCaseStatus"] absoluteString];
|
||||
path = [path stringByAppendingFormat:@"?case=%@&agent=%@", @(caseNumber), agent];
|
||||
|
||||
return [super initWithURL:[NSURL URLWithString:path]];
|
||||
}
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(NSString *)message;
|
||||
{
|
||||
self.info = [NSJSONSerialization JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,26 +0,0 @@
|
||||
//
|
||||
// SRTWebSocketOperation.h
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 1/28/12.
|
||||
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SRWebSocket.h"
|
||||
|
||||
@interface SRTWebSocketOperation : NSOperation <SRWebSocketDelegate>
|
||||
|
||||
- (id)initWithURL:(NSURL *)URL;
|
||||
|
||||
@property (nonatomic) BOOL isFinished;
|
||||
@property (nonatomic) BOOL isExecuting;
|
||||
|
||||
@property (nonatomic, readonly, retain) NSError *error;
|
||||
|
||||
// We override these methods. Please call super
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
|
||||
|
||||
@end
|
||||
@ -1,21 +0,0 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "SenTestCase+SRTAdditions.h"
|
||||
#endif
|
||||
@ -1,13 +0,0 @@
|
||||
//
|
||||
// STRDispatchTest.h
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Michael Lewis on 1/12/13.
|
||||
//
|
||||
//
|
||||
|
||||
#import <SenTestingKit/SenTestingKit.h>
|
||||
|
||||
@interface STRDispatchTest : SenTestCase
|
||||
|
||||
@end
|
||||
@ -1,151 +0,0 @@
|
||||
//
|
||||
// STRDispatchTest.m
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Michael Lewis on 1/12/13.
|
||||
//
|
||||
//
|
||||
extern "C" {
|
||||
#import <SenTestingKit/SenTestingKit.h>
|
||||
}
|
||||
|
||||
#include <Security/SecureTransport.h>
|
||||
|
||||
#include "DispatchIO.h"
|
||||
#include "DispatchData.h"
|
||||
#include "SecureIO.h"
|
||||
|
||||
using namespace squareup::dispatch;
|
||||
|
||||
@interface STRDispatchTest : SenTestCase
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation STRDispatchTest
|
||||
|
||||
- (void)testConnect;
|
||||
{
|
||||
bool finished = false;
|
||||
dispatch_queue_t workQueue = dispatch_queue_create("dispatch queue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
Dial(workQueue, "localhost", "9932", dispatch_get_main_queue(), [&](dispatch_fd_t fd, int error_code, const char *error_message) {
|
||||
NSLog(@"code: %d, msg: %s", error_code, error_message);
|
||||
STAssertEquals(error_code, 0, @"Should not error but got %s", error_message);
|
||||
finished = true;
|
||||
});
|
||||
|
||||
[self runCurrentRunLoopUntilTestPasses:[&finished](){
|
||||
return (BOOL)finished;
|
||||
} timeout:100.0];
|
||||
}
|
||||
|
||||
|
||||
- (void)testSimpleDial;
|
||||
{
|
||||
RawIO *raw_io = nullptr;
|
||||
bool finished = false;
|
||||
|
||||
auto cleanupBlock = [&finished](int error) {
|
||||
finished = true;
|
||||
};
|
||||
|
||||
SimpleDial("localhost", "9934", dispatch_get_main_queue(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [&raw_io, self, &finished](squareup::dispatch::RawIO *io, int error, const char *error_message) {
|
||||
|
||||
STAssertEquals(error, 0, @"Should not have errored, but got %s", error_message);
|
||||
STAssertTrue(io != nullptr, @"io should be valid");
|
||||
|
||||
|
||||
if (!io) {
|
||||
finished = true;
|
||||
return;
|
||||
}
|
||||
raw_io = io;
|
||||
|
||||
io->Write(Data("HELLO THERE!", dispatch_get_main_queue()), dispatch_get_main_queue(), [self, &raw_io](bool done, dispatch_data_t data, int error) {
|
||||
STAssertEquals(error, 0, @"Error should == 0");
|
||||
if (done) {
|
||||
raw_io->Close(0);
|
||||
}
|
||||
});
|
||||
|
||||
}, cleanupBlock);
|
||||
|
||||
[self runCurrentRunLoopUntilTestPasses:[&finished](){
|
||||
return (BOOL)finished;
|
||||
} timeout:100.0];
|
||||
}
|
||||
|
||||
- (void)testDialTLS;
|
||||
{
|
||||
SecureIO *raw_io = nullptr;
|
||||
bool finished = false;
|
||||
|
||||
auto cleanupBlock = [&finished, raw_io](int error) {
|
||||
dispatch_async(dispatch_get_main_queue(), [raw_io]{
|
||||
delete raw_io;
|
||||
});
|
||||
NSLog(@"FINISHED");
|
||||
finished = true;
|
||||
};
|
||||
|
||||
SSLContextRef ctx = SSLCreateContext(CFAllocatorGetDefault(), kSSLClientSide, kSSLStreamType);
|
||||
|
||||
DialTLS("10.0.1.15", "10248", ctx, dispatch_get_main_queue(), dispatch_get_main_queue(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [&raw_io, self, &finished](squareup::dispatch::SecureIO *io, int error, const char *error_message) {
|
||||
|
||||
STAssertEquals(error, 0, @"Should not have errored, but got %s", error_message);
|
||||
STAssertTrue(io != nullptr, @"io should be valid");
|
||||
|
||||
if (!io) {
|
||||
finished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
raw_io = io;
|
||||
|
||||
__block bool seenInner = false;
|
||||
__block bool seenOuter = false;
|
||||
//
|
||||
// raw_io->Write(Data("HELLO THERE!\n", dispatch_get_main_queue()), ^(bool done, dispatch_data_t data, int error) {
|
||||
// STAssertEquals(error, 0, @"Error should == 0");
|
||||
// STAssertFalse(seenOuter, @"Should only see the outer once");
|
||||
// if (done) {
|
||||
// seenOuter = true;
|
||||
// }
|
||||
// if (done && !error) {
|
||||
//
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// raw_io->Write(Data("HELLO THERE2!\n", dispatch_get_main_queue()), ^(bool done, dispatch_data_t data, int error) {
|
||||
// STAssertFalse(seenInner, @"Shouldn't have seen inner yet");
|
||||
// if (done) {
|
||||
// seenInner = done;
|
||||
// }
|
||||
// STAssertEquals(error, 0, @"Error should == 0");
|
||||
// STAssertFalse(finished, @"Shouldn't have finished");
|
||||
// });
|
||||
|
||||
|
||||
raw_io->Read(SIZE_MAX, dispatch_get_main_queue(), ^(bool done, dispatch_data_t data, int error) {
|
||||
if (!error) {
|
||||
raw_io->Write(data, dispatch_get_main_queue(), ^(bool done, dispatch_data_t data, int error) {
|
||||
|
||||
});
|
||||
} else {
|
||||
STAssertTrue(error == ECANCELED, @"Server should terminate");
|
||||
|
||||
if (done && !error) {
|
||||
raw_io->Close(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}, cleanupBlock);
|
||||
|
||||
[self runCurrentRunLoopUntilTestPasses:[&finished](){
|
||||
return (BOOL)finished;
|
||||
} timeout:100.0];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,27 +0,0 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
|
||||
typedef BOOL (^PXPredicateBlock)();
|
||||
|
||||
|
||||
@interface XCTest (PXAdditions)
|
||||
|
||||
- (void)runCurrentRunLoopUntilTestPasses:(PXPredicateBlock)predicate timeout:(NSTimeInterval)timeout;
|
||||
|
||||
@end
|
||||
@ -1,38 +0,0 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#import "SenTestCase+SRTAdditions.h"
|
||||
|
||||
|
||||
@implementation XCTestCase (SRTAdditions)
|
||||
|
||||
- (void)runCurrentRunLoopUntilTestPasses:(PXPredicateBlock)predicate timeout:(NSTimeInterval)timeout;
|
||||
{
|
||||
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
|
||||
|
||||
NSTimeInterval timeoutTime = [timeoutDate timeIntervalSinceReferenceDate];
|
||||
NSTimeInterval currentTime;
|
||||
|
||||
for (currentTime = [NSDate timeIntervalSinceReferenceDate];
|
||||
!predicate() && currentTime < timeoutTime;
|
||||
currentTime = [NSDate timeIntervalSinceReferenceDate]) {
|
||||
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
}
|
||||
|
||||
XCTAssertTrue(currentTime <= timeoutTime, @"Timed out");
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,2 +0,0 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
//
|
||||
// foo.m
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 10/31/11.
|
||||
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
@ -1,19 +1,22 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "SocketRocket"
|
||||
s.version = '0.4'
|
||||
s.summary = 'A conforming WebSocket (RFC 6455) client library.'
|
||||
s.homepage = 'https://github.com/square/SocketRocket'
|
||||
s.authors = 'Square'
|
||||
s.license = 'Apache License, Version 2.0'
|
||||
s.source = { :git => 'https://github.com/square/SocketRocket.git', :tag => s.version.to_s }
|
||||
s.source_files = 'SocketRocket/*.{h,m}'
|
||||
s.name = 'SocketRocket'
|
||||
s.version = '0.5.1'
|
||||
s.summary = 'A conforming WebSocket (RFC 6455) client library for iOS, macOS and tvOS.'
|
||||
s.homepage = 'https://github.com/facebook/SocketRocket'
|
||||
s.authors = { 'Nikita Lutsenko' => 'nlutsenko@me.com', 'Dan Federman' => 'federman@squareup.com', 'Mike Lewis' => 'mikelikespie@gmail.com' }
|
||||
s.license = 'BSD'
|
||||
s.source = { :git => 'https://github.com/facebook/SocketRocket.git', :tag => s.version.to_s }
|
||||
s.requires_arc = true
|
||||
|
||||
s.source_files = 'SocketRocket/**/*.{h,m}'
|
||||
s.public_header_files = 'SocketRocket/*.h'
|
||||
|
||||
s.ios.deployment_target = '6.0'
|
||||
s.osx.deployment_target = '10.8'
|
||||
s.ios.deployment_target = '6.0'
|
||||
s.osx.deployment_target = '10.8'
|
||||
s.tvos.deployment_target = '9.0'
|
||||
|
||||
s.ios.frameworks = 'CFNetwork', 'Security'
|
||||
s.osx.frameworks = 'CoreServices', 'Security'
|
||||
|
||||
s.libraries = "icucore"
|
||||
s.tvos.frameworks = 'CFNetwork', 'Security'
|
||||
s.libraries = 'icucore'
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,878 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
<<<<<<< HEAD
|
||||
2725F85B16A29D18007018E9 /* SecureIO.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2725F85916A29D18007018E9 /* SecureIO.mm */; };
|
||||
2725F85C16A29D18007018E9 /* SecureIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 2725F85A16A29D18007018E9 /* SecureIO.h */; };
|
||||
27CA136116A21E4D00A35C2D /* DispatchIO.mm in Sources */ = {isa = PBXBuildFile; fileRef = 27CA135F16A21E4D00A35C2D /* DispatchIO.mm */; };
|
||||
27CA136216A21E4D00A35C2D /* DispatchIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 27CA136016A21E4D00A35C2D /* DispatchIO.h */; };
|
||||
27CA136616A2243000A35C2D /* DispatchData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 27CA136416A2243000A35C2D /* DispatchData.mm */; };
|
||||
27CA136716A2243000A35C2D /* DispatchData.h in Headers */ = {isa = PBXBuildFile; fileRef = 27CA136516A2243000A35C2D /* DispatchData.h */; };
|
||||
27CA136A16A23AAE00A35C2D /* STRDispatchTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 27CA136916A23AAE00A35C2D /* STRDispatchTest.mm */; };
|
||||
27DC4A9516A64EB800E9C084 /* Common.h in Headers */ = {isa = PBXBuildFile; fileRef = 27DC4A9416A64EB800E9C084 /* Common.h */; };
|
||||
F6016C7C146124B20037BB3D /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = F6016C7B146124B20037BB3D /* base64.c */; };
|
||||
F6016C7F146124ED0037BB3D /* base64.h in Headers */ = {isa = PBXBuildFile; fileRef = F6016C7E146124ED0037BB3D /* base64.h */; };
|
||||
=======
|
||||
>>>>>>> origin/master
|
||||
F6016C8814620EC70037BB3D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; };
|
||||
F6016C8914620ECC0037BB3D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; };
|
||||
F6016C8A1462143C0037BB3D /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; };
|
||||
F60CC2A114D4EA0500A005E4 /* SRTWebSocketOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F60CC2A014D4EA0500A005E4 /* SRTWebSocketOperation.m */; };
|
||||
F61A0DC81625F44D00365EBD /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F61A0DC71625F44D00365EBD /* Default-568h@2x.png */; };
|
||||
F62417E614D52F3C003CE997 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F62417E514D52F3C003CE997 /* UIKit.framework */; };
|
||||
F62417E714D52F3C003CE997 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
|
||||
F62417E914D52F3C003CE997 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F62417E814D52F3C003CE997 /* CoreGraphics.framework */; };
|
||||
F62417EF14D52F3C003CE997 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F62417ED14D52F3C003CE997 /* InfoPlist.strings */; };
|
||||
F62417F114D52F3C003CE997 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F62417F014D52F3C003CE997 /* main.m */; };
|
||||
F62417F514D52F3C003CE997 /* TCAppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = F62417F414D52F3C003CE997 /* TCAppDelegate.mm */; };
|
||||
F62417F814D52F3C003CE997 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F62417F614D52F3C003CE997 /* MainStoryboard.storyboard */; };
|
||||
F62417FB14D52F3C003CE997 /* TCViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F62417FA14D52F3C003CE997 /* TCViewController.m */; };
|
||||
F624180114D5300C003CE997 /* TCChatCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F624180014D5300C003CE997 /* TCChatCell.m */; };
|
||||
F624180214D532E0003CE997 /* libSocketRocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B2082D1450F597009315AF /* libSocketRocket.a */; };
|
||||
F624180314D53449003CE997 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; };
|
||||
F624180414D53449003CE997 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; };
|
||||
F624180614D53451003CE997 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F6C41C95145F7C4700641356 /* libicucore.dylib */; };
|
||||
<<<<<<< HEAD
|
||||
F6396B86153E67EC00345B5E /* SRWebSocket.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.mm */; };
|
||||
F6396B87153E67EC00345B5E /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = F6016C7B146124B20037BB3D /* base64.c */; };
|
||||
F6396B88153E67EC00345B5E /* NSData+SRB64Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = F6572124146C7B6A00D6B8A9 /* NSData+SRB64Additions.m */; };
|
||||
F6572126146C7B6A00D6B8A9 /* NSData+SRB64Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = F6572124146C7B6A00D6B8A9 /* NSData+SRB64Additions.m */; };
|
||||
=======
|
||||
F6396B86153E67EC00345B5E /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.m */; };
|
||||
>>>>>>> origin/master
|
||||
F668C899153E923C0044DBAC /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6396BA4153E6D7400345B5E /* CoreServices.framework */; };
|
||||
F668C89A153E923C0044DBAC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6396BA1153E6D4800345B5E /* Foundation.framework */; };
|
||||
F668C89B153E923C0044DBAC /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6396B9F153E6D3700345B5E /* Security.framework */; };
|
||||
F668C8AA153E92F90044DBAC /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
<<<<<<< HEAD
|
||||
F6A12CD1145119B700C1D980 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; };
|
||||
F6A12CD2145119B700C1D980 /* SRWebSocket.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.mm */; };
|
||||
=======
|
||||
F6A12CD1145119B700C1D980 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F6A12CD2145119B700C1D980 /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.m */; };
|
||||
>>>>>>> origin/master
|
||||
F6AE451D145906A70022AF3C /* libSocketRocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B2082D1450F597009315AF /* libSocketRocket.a */; };
|
||||
F6AE4520145906B20022AF3C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
|
||||
F6AE45241459071C0022AF3C /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; };
|
||||
F6AE4528145907D30022AF3C /* SenTestCase+SRTAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = F6AE4527145907D30022AF3C /* SenTestCase+SRTAdditions.m */; };
|
||||
F6BDA806145900D200FE3253 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
|
||||
F6BDA80C145900D200FE3253 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F6BDA80A145900D200FE3253 /* InfoPlist.strings */; };
|
||||
F6BDA8161459016900FE3253 /* SRTAutobahnTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F6BDA8151459016900FE3253 /* SRTAutobahnTests.m */; };
|
||||
F6C41C98145F7C6100641356 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F6C41C95145F7C4700641356 /* libicucore.dylib */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
F62417D514D50869003CE997 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = F6B208241450F597009315AF /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = F6B2082C1450F597009315AF;
|
||||
remoteInfo = SocketRocket;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
<<<<<<< HEAD
|
||||
2725F85916A29D18007018E9 /* SecureIO.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SecureIO.mm; sourceTree = "<group>"; };
|
||||
2725F85A16A29D18007018E9 /* SecureIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecureIO.h; sourceTree = "<group>"; };
|
||||
27CA135F16A21E4D00A35C2D /* DispatchIO.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatchIO.mm; sourceTree = "<group>"; };
|
||||
27CA136016A21E4D00A35C2D /* DispatchIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DispatchIO.h; sourceTree = "<group>"; };
|
||||
27CA136416A2243000A35C2D /* DispatchData.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatchData.mm; sourceTree = "<group>"; };
|
||||
27CA136516A2243000A35C2D /* DispatchData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DispatchData.h; sourceTree = "<group>"; };
|
||||
27CA136916A23AAE00A35C2D /* STRDispatchTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = STRDispatchTest.mm; sourceTree = "<group>"; };
|
||||
27DC4A9416A64EB800E9C084 /* Common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Common.h; sourceTree = "<group>"; };
|
||||
F6016C7B146124B20037BB3D /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = "<group>"; };
|
||||
F6016C7E146124ED0037BB3D /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = "<group>"; };
|
||||
=======
|
||||
>>>>>>> origin/master
|
||||
F60CC29F14D4EA0500A005E4 /* SRTWebSocketOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRTWebSocketOperation.h; sourceTree = "<group>"; };
|
||||
F60CC2A014D4EA0500A005E4 /* SRTWebSocketOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRTWebSocketOperation.m; sourceTree = "<group>"; };
|
||||
F61A0DC71625F44D00365EBD /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "en.lproj/Default-568h@2x.png"; sourceTree = "<group>"; };
|
||||
F62417E314D52F3C003CE997 /* TestChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestChat.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F62417E514D52F3C003CE997 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||
F62417E814D52F3C003CE997 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
|
||||
F62417EC14D52F3C003CE997 /* TestChat-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TestChat-Info.plist"; sourceTree = "<group>"; };
|
||||
F62417EE14D52F3C003CE997 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
F62417F014D52F3C003CE997 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
F62417F214D52F3C003CE997 /* TestChat-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TestChat-Prefix.pch"; sourceTree = "<group>"; };
|
||||
F62417F314D52F3C003CE997 /* TCAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TCAppDelegate.h; sourceTree = "<group>"; };
|
||||
F62417F414D52F3C003CE997 /* TCAppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TCAppDelegate.mm; sourceTree = "<group>"; };
|
||||
F62417F714D52F3C003CE997 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = "<group>"; };
|
||||
F62417F914D52F3C003CE997 /* TCViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TCViewController.h; sourceTree = "<group>"; };
|
||||
F62417FA14D52F3C003CE997 /* TCViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TCViewController.m; sourceTree = "<group>"; };
|
||||
F62417FF14D5300C003CE997 /* TCChatCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCChatCell.h; sourceTree = "<group>"; };
|
||||
F624180014D5300C003CE997 /* TCChatCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCChatCell.m; sourceTree = "<group>"; };
|
||||
F6396B9F153E6D3700345B5E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
|
||||
F6396BA1153E6D4800345B5E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
|
||||
F6396BA4153E6D7400345B5E /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/CoreServices.framework; sourceTree = DEVELOPER_DIR; };
|
||||
F668C880153E91210044DBAC /* SocketRocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketRocket.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F668C884153E91210044DBAC /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||
F668C885153E91210044DBAC /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
|
||||
F668C886153E91210044DBAC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
F668C889153E91210044DBAC /* SocketRocketOSX-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SocketRocketOSX-Info.plist"; sourceTree = "<group>"; };
|
||||
F6A12CCF145119B700C1D980 /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocket.h; sourceTree = "<group>"; };
|
||||
F6A12CD0145119B700C1D980 /* SRWebSocket.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SRWebSocket.mm; sourceTree = "<group>"; };
|
||||
F6A12CD3145122FC00C1D980 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
||||
F6A12CD51451231B00C1D980 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
|
||||
F6AE4526145907D30022AF3C /* SenTestCase+SRTAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SenTestCase+SRTAdditions.h"; sourceTree = "<group>"; };
|
||||
F6AE4527145907D30022AF3C /* SenTestCase+SRTAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SenTestCase+SRTAdditions.m"; sourceTree = "<group>"; };
|
||||
F6B2082D1450F597009315AF /* libSocketRocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSocketRocket.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F6B208301450F597009315AF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
F6B208341450F597009315AF /* SocketRocket-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketRocket-Prefix.pch"; sourceTree = "<group>"; };
|
||||
F6BDA802145900D200FE3253 /* SRWebSocketTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SRWebSocketTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F6BDA809145900D200FE3253 /* SRWebSocketTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SRWebSocketTests-Info.plist"; sourceTree = "<group>"; };
|
||||
F6BDA80B145900D200FE3253 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
F6BDA810145900D200FE3253 /* SRWebSocketTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SRWebSocketTests-Prefix.pch"; sourceTree = "<group>"; };
|
||||
F6BDA8151459016900FE3253 /* SRTAutobahnTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRTAutobahnTests.m; sourceTree = "<group>"; };
|
||||
F6C41C95145F7C4700641356 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
F62417E014D52F3C003CE997 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F62417E614D52F3C003CE997 /* UIKit.framework in Frameworks */,
|
||||
F62417E714D52F3C003CE997 /* Foundation.framework in Frameworks */,
|
||||
F62417E914D52F3C003CE997 /* CoreGraphics.framework in Frameworks */,
|
||||
F624180214D532E0003CE997 /* libSocketRocket.a in Frameworks */,
|
||||
F624180314D53449003CE997 /* CFNetwork.framework in Frameworks */,
|
||||
F624180414D53449003CE997 /* Security.framework in Frameworks */,
|
||||
F624180614D53451003CE997 /* libicucore.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F668C87C153E91210044DBAC /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F668C899153E923C0044DBAC /* CoreServices.framework in Frameworks */,
|
||||
F668C89A153E923C0044DBAC /* Foundation.framework in Frameworks */,
|
||||
F668C89B153E923C0044DBAC /* Security.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F6B2082A1450F597009315AF /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F6AE4520145906B20022AF3C /* Foundation.framework in Frameworks */,
|
||||
F6016C8914620ECC0037BB3D /* Security.framework in Frameworks */,
|
||||
F6016C8A1462143C0037BB3D /* CFNetwork.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F6BDA7FE145900D200FE3253 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F6C41C98145F7C6100641356 /* libicucore.dylib in Frameworks */,
|
||||
F6BDA806145900D200FE3253 /* Foundation.framework in Frameworks */,
|
||||
F6AE451D145906A70022AF3C /* libSocketRocket.a in Frameworks */,
|
||||
F6AE45241459071C0022AF3C /* CFNetwork.framework in Frameworks */,
|
||||
F6016C8814620EC70037BB3D /* Security.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
F62417EA14D52F3C003CE997 /* TestChat */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F62417EB14D52F3C003CE997 /* Supporting Files */,
|
||||
F62417F314D52F3C003CE997 /* TCAppDelegate.h */,
|
||||
F62417F414D52F3C003CE997 /* TCAppDelegate.mm */,
|
||||
F62417F614D52F3C003CE997 /* MainStoryboard.storyboard */,
|
||||
F62417F914D52F3C003CE997 /* TCViewController.h */,
|
||||
F62417FA14D52F3C003CE997 /* TCViewController.m */,
|
||||
F62417FF14D5300C003CE997 /* TCChatCell.h */,
|
||||
F624180014D5300C003CE997 /* TCChatCell.m */,
|
||||
);
|
||||
path = TestChat;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F62417EB14D52F3C003CE997 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F61A0DC71625F44D00365EBD /* Default-568h@2x.png */,
|
||||
F62417EC14D52F3C003CE997 /* TestChat-Info.plist */,
|
||||
F62417ED14D52F3C003CE997 /* InfoPlist.strings */,
|
||||
F62417F014D52F3C003CE997 /* main.m */,
|
||||
F62417F214D52F3C003CE997 /* TestChat-Prefix.pch */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F6396BA3153E6D4D00345B5E /* OSX Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F6396BA4153E6D7400345B5E /* CoreServices.framework */,
|
||||
F6396BA1153E6D4800345B5E /* Foundation.framework */,
|
||||
F6396B9F153E6D3700345B5E /* Security.framework */,
|
||||
);
|
||||
name = "OSX Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F668C883153E91210044DBAC /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F668C884153E91210044DBAC /* AppKit.framework */,
|
||||
F668C885153E91210044DBAC /* CoreData.framework */,
|
||||
F668C886153E91210044DBAC /* Foundation.framework */,
|
||||
);
|
||||
name = "Other Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F668C887153E91210044DBAC /* SocketRocketOSX */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F668C889153E91210044DBAC /* SocketRocketOSX-Info.plist */,
|
||||
);
|
||||
path = SocketRocketOSX;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F6B208221450F597009315AF = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F6B208321450F597009315AF /* SocketRocket */,
|
||||
F6BDA807145900D200FE3253 /* SRWebSocketTests */,
|
||||
F62417EA14D52F3C003CE997 /* TestChat */,
|
||||
F668C887153E91210044DBAC /* SocketRocketOSX */,
|
||||
F6B2082F1450F597009315AF /* Frameworks */,
|
||||
F6B2082E1450F597009315AF /* Products */,
|
||||
);
|
||||
indentWidth = 4;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 4;
|
||||
};
|
||||
F6B2082E1450F597009315AF /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F6B2082D1450F597009315AF /* libSocketRocket.a */,
|
||||
F6BDA802145900D200FE3253 /* SRWebSocketTests.xctest */,
|
||||
F62417E314D52F3C003CE997 /* TestChat.app */,
|
||||
F668C880153E91210044DBAC /* SocketRocket.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F6B2082F1450F597009315AF /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F6396BA3153E6D4D00345B5E /* OSX Frameworks */,
|
||||
F6C41C95145F7C4700641356 /* libicucore.dylib */,
|
||||
F6A12CD51451231B00C1D980 /* CFNetwork.framework */,
|
||||
F6A12CD3145122FC00C1D980 /* Security.framework */,
|
||||
F6B208301450F597009315AF /* Foundation.framework */,
|
||||
F62417E514D52F3C003CE997 /* UIKit.framework */,
|
||||
F62417E814D52F3C003CE997 /* CoreGraphics.framework */,
|
||||
F668C883153E91210044DBAC /* Other Frameworks */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F6B208321450F597009315AF /* SocketRocket */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F6B208331450F597009315AF /* Supporting Files */,
|
||||
F6A12CCF145119B700C1D980 /* SRWebSocket.h */,
|
||||
<<<<<<< HEAD
|
||||
F6A12CD0145119B700C1D980 /* SRWebSocket.mm */,
|
||||
F6572123146C7B6A00D6B8A9 /* NSData+SRB64Additions.h */,
|
||||
F6572124146C7B6A00D6B8A9 /* NSData+SRB64Additions.m */,
|
||||
27CA135F16A21E4D00A35C2D /* DispatchIO.mm */,
|
||||
27CA136016A21E4D00A35C2D /* DispatchIO.h */,
|
||||
2725F85916A29D18007018E9 /* SecureIO.mm */,
|
||||
2725F85A16A29D18007018E9 /* SecureIO.h */,
|
||||
27CA136416A2243000A35C2D /* DispatchData.mm */,
|
||||
27CA136516A2243000A35C2D /* DispatchData.h */,
|
||||
27DC4A9416A64EB800E9C084 /* Common.h */,
|
||||
=======
|
||||
F6A12CD0145119B700C1D980 /* SRWebSocket.m */,
|
||||
>>>>>>> origin/master
|
||||
);
|
||||
path = SocketRocket;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F6B208331450F597009315AF /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F6B208341450F597009315AF /* SocketRocket-Prefix.pch */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F6BDA807145900D200FE3253 /* SRWebSocketTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F6BDA808145900D200FE3253 /* Supporting Files */,
|
||||
F6BDA8151459016900FE3253 /* SRTAutobahnTests.m */,
|
||||
F6BDA810145900D200FE3253 /* SRWebSocketTests-Prefix.pch */,
|
||||
F6AE4526145907D30022AF3C /* SenTestCase+SRTAdditions.h */,
|
||||
F6AE4527145907D30022AF3C /* SenTestCase+SRTAdditions.m */,
|
||||
F60CC29F14D4EA0500A005E4 /* SRTWebSocketOperation.h */,
|
||||
F60CC2A014D4EA0500A005E4 /* SRTWebSocketOperation.m */,
|
||||
27CA136916A23AAE00A35C2D /* STRDispatchTest.mm */,
|
||||
);
|
||||
path = SRWebSocketTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F6BDA808145900D200FE3253 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F6BDA809145900D200FE3253 /* SRWebSocketTests-Info.plist */,
|
||||
F6BDA80A145900D200FE3253 /* InfoPlist.strings */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
F668C87D153E91210044DBAC /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F668C8AA153E92F90044DBAC /* SRWebSocket.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F6B2082B1450F597009315AF /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F6A12CD1145119B700C1D980 /* SRWebSocket.h in Headers */,
|
||||
<<<<<<< HEAD
|
||||
F6016C7F146124ED0037BB3D /* base64.h in Headers */,
|
||||
27CA136216A21E4D00A35C2D /* DispatchIO.h in Headers */,
|
||||
27CA136716A2243000A35C2D /* DispatchData.h in Headers */,
|
||||
2725F85C16A29D18007018E9 /* SecureIO.h in Headers */,
|
||||
27DC4A9516A64EB800E9C084 /* Common.h in Headers */,
|
||||
=======
|
||||
>>>>>>> origin/master
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
F62417E214D52F3C003CE997 /* TestChat */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = F62417FC14D52F3C003CE997 /* Build configuration list for PBXNativeTarget "TestChat" */;
|
||||
buildPhases = (
|
||||
F62417DF14D52F3C003CE997 /* Sources */,
|
||||
F62417E014D52F3C003CE997 /* Frameworks */,
|
||||
F62417E114D52F3C003CE997 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = TestChat;
|
||||
productName = TestChat;
|
||||
productReference = F62417E314D52F3C003CE997 /* TestChat.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
F668C87F153E91210044DBAC /* SocketRocketOSX */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = F668C891153E91210044DBAC /* Build configuration list for PBXNativeTarget "SocketRocketOSX" */;
|
||||
buildPhases = (
|
||||
F6396B85153E67EC00345B5E /* Sources */,
|
||||
F668C87C153E91210044DBAC /* Frameworks */,
|
||||
F668C87D153E91210044DBAC /* Headers */,
|
||||
F668C87E153E91210044DBAC /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = SocketRocketOSX;
|
||||
productName = SocketRocketOSX;
|
||||
productReference = F668C880153E91210044DBAC /* SocketRocket.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
F6B2082C1450F597009315AF /* SocketRocket */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = F6B2083A1450F597009315AF /* Build configuration list for PBXNativeTarget "SocketRocket" */;
|
||||
buildPhases = (
|
||||
F6B208291450F597009315AF /* Sources */,
|
||||
F6B2082A1450F597009315AF /* Frameworks */,
|
||||
F6B2082B1450F597009315AF /* Headers */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = SocketRocket;
|
||||
productName = SocketRocket;
|
||||
productReference = F6B2082D1450F597009315AF /* libSocketRocket.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
F6BDA801145900D200FE3253 /* SRWebSocketTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = F6BDA813145900D200FE3253 /* Build configuration list for PBXNativeTarget "SRWebSocketTests" */;
|
||||
buildPhases = (
|
||||
F6BDA7FD145900D200FE3253 /* Sources */,
|
||||
F6BDA7FE145900D200FE3253 /* Frameworks */,
|
||||
F6BDA7FF145900D200FE3253 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
F62417D614D50869003CE997 /* PBXTargetDependency */,
|
||||
);
|
||||
name = SRWebSocketTests;
|
||||
productName = SRWebSocketTests;
|
||||
productReference = F6BDA802145900D200FE3253 /* SRWebSocketTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
F6B208241450F597009315AF /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastTestingUpgradeCheck = 0640;
|
||||
LastUpgradeCheck = 0640;
|
||||
};
|
||||
buildConfigurationList = F6B208271450F597009315AF /* Build configuration list for PBXProject "SocketRocket" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = F6B208221450F597009315AF;
|
||||
productRefGroup = F6B2082E1450F597009315AF /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
F6B2082C1450F597009315AF /* SocketRocket */,
|
||||
F668C87F153E91210044DBAC /* SocketRocketOSX */,
|
||||
F6BDA801145900D200FE3253 /* SRWebSocketTests */,
|
||||
F62417E214D52F3C003CE997 /* TestChat */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
F62417E114D52F3C003CE997 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F62417EF14D52F3C003CE997 /* InfoPlist.strings in Resources */,
|
||||
F62417F814D52F3C003CE997 /* MainStoryboard.storyboard in Resources */,
|
||||
F61A0DC81625F44D00365EBD /* Default-568h@2x.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F668C87E153E91210044DBAC /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F6BDA7FF145900D200FE3253 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F6BDA80C145900D200FE3253 /* InfoPlist.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
F62417DF14D52F3C003CE997 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F62417F114D52F3C003CE997 /* main.m in Sources */,
|
||||
F62417F514D52F3C003CE997 /* TCAppDelegate.mm in Sources */,
|
||||
F62417FB14D52F3C003CE997 /* TCViewController.m in Sources */,
|
||||
F624180114D5300C003CE997 /* TCChatCell.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F6396B85153E67EC00345B5E /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
<<<<<<< HEAD
|
||||
F6396B86153E67EC00345B5E /* SRWebSocket.mm in Sources */,
|
||||
F6396B87153E67EC00345B5E /* base64.c in Sources */,
|
||||
F6396B88153E67EC00345B5E /* NSData+SRB64Additions.m in Sources */,
|
||||
=======
|
||||
F6396B86153E67EC00345B5E /* SRWebSocket.m in Sources */,
|
||||
>>>>>>> origin/master
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F6B208291450F597009315AF /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
<<<<<<< HEAD
|
||||
F6A12CD2145119B700C1D980 /* SRWebSocket.mm in Sources */,
|
||||
F6016C7C146124B20037BB3D /* base64.c in Sources */,
|
||||
F6572126146C7B6A00D6B8A9 /* NSData+SRB64Additions.m in Sources */,
|
||||
27CA136116A21E4D00A35C2D /* DispatchIO.mm in Sources */,
|
||||
27CA136616A2243000A35C2D /* DispatchData.mm in Sources */,
|
||||
2725F85B16A29D18007018E9 /* SecureIO.mm in Sources */,
|
||||
=======
|
||||
F6A12CD2145119B700C1D980 /* SRWebSocket.m in Sources */,
|
||||
>>>>>>> origin/master
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F6BDA7FD145900D200FE3253 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F6BDA8161459016900FE3253 /* SRTAutobahnTests.m in Sources */,
|
||||
F6AE4528145907D30022AF3C /* SenTestCase+SRTAdditions.m in Sources */,
|
||||
F60CC2A114D4EA0500A005E4 /* SRTWebSocketOperation.m in Sources */,
|
||||
27CA136A16A23AAE00A35C2D /* STRDispatchTest.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
F62417D614D50869003CE997 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = F6B2082C1450F597009315AF /* SocketRocket */;
|
||||
targetProxy = F62417D514D50869003CE997 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
F62417ED14D52F3C003CE997 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
F62417EE14D52F3C003CE997 /* en */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F62417F614D52F3C003CE997 /* MainStoryboard.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
F62417F714D52F3C003CE997 /* en */,
|
||||
);
|
||||
name = MainStoryboard.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F6BDA80A145900D200FE3253 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
F6BDA80B145900D200FE3253 /* en */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
F62417FD14D52F3C003CE997 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
|
||||
);
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "TestChat/TestChat-Prefix.pch";
|
||||
INFOPLIST_FILE = "TestChat/TestChat-Info.plist";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
F62417FE14D52F3C003CE997 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
|
||||
);
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "TestChat/TestChat-Prefix.pch";
|
||||
INFOPLIST_FILE = "TestChat/TestChat-Info.plist";
|
||||
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
F668C892153E91210044DBAC /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Library/Frameworks\"",
|
||||
);
|
||||
FRAMEWORK_VERSION = A;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "SocketRocket/SocketRocket-Prefix.pch";
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
INFOPLIST_FILE = "SocketRocketOSX/SocketRocketOSX-Info.plist";
|
||||
LD_DYLIB_INSTALL_NAME = "@executable_path/../Frameworks/$(EXECUTABLE_PATH)";
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = SocketRocket;
|
||||
SDKROOT = macosx;
|
||||
WRAPPER_EXTENSION = framework;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
F668C893153E91210044DBAC /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Library/Frameworks\"",
|
||||
);
|
||||
FRAMEWORK_VERSION = A;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "SocketRocket/SocketRocket-Prefix.pch";
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
INFOPLIST_FILE = "SocketRocketOSX/SocketRocketOSX-Info.plist";
|
||||
LD_DYLIB_INSTALL_NAME = "@executable_path/../Frameworks/$(EXECUTABLE_PATH)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = SocketRocket;
|
||||
SDKROOT = macosx;
|
||||
WRAPPER_EXTENSION = framework;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
F6B208381450F597009315AF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 5.1;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||
SDKROOT = iphoneos6.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
F6B208391450F597009315AF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
NDEBUG,
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 5.1;
|
||||
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||
SDKROOT = iphoneos6.0;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
F6B2083B1450F597009315AF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
DSTROOT = /tmp/SocketRocket.dst;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "SocketRocket/SocketRocket-Prefix.pch";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/usr/lib/system\"",
|
||||
"\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/usr/lib\"",
|
||||
);
|
||||
OTHER_LDFLAGS = "-Licucore";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
F6B2083C1450F597009315AF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
DSTROOT = /tmp/SocketRocket.dst;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "SocketRocket/SocketRocket-Prefix.pch";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/usr/lib/system\"",
|
||||
"\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/usr/lib\"",
|
||||
);
|
||||
OTHER_LDFLAGS = "-Licucore";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
F6BDA811145900D200FE3253 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "SRWebSocketTests/SRWebSocketTests-Prefix.pch";
|
||||
INFOPLIST_FILE = "SRWebSocketTests/SRWebSocketTests-Info.plist";
|
||||
OTHER_LDFLAGS = (
|
||||
"-all_load",
|
||||
"-ObjC",
|
||||
"-framework",
|
||||
XCTest,
|
||||
);
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
F6BDA812145900D200FE3253 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "SRWebSocketTests/SRWebSocketTests-Prefix.pch";
|
||||
INFOPLIST_FILE = "SRWebSocketTests/SRWebSocketTests-Info.plist";
|
||||
OTHER_LDFLAGS = (
|
||||
"-all_load",
|
||||
"-ObjC",
|
||||
"-framework",
|
||||
XCTest,
|
||||
);
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
F62417FC14D52F3C003CE997 /* Build configuration list for PBXNativeTarget "TestChat" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
F62417FD14D52F3C003CE997 /* Debug */,
|
||||
F62417FE14D52F3C003CE997 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
F668C891153E91210044DBAC /* Build configuration list for PBXNativeTarget "SocketRocketOSX" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
F668C892153E91210044DBAC /* Debug */,
|
||||
F668C893153E91210044DBAC /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
F6B208271450F597009315AF /* Build configuration list for PBXProject "SocketRocket" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
F6B208381450F597009315AF /* Debug */,
|
||||
F6B208391450F597009315AF /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
F6B2083A1450F597009315AF /* Build configuration list for PBXNativeTarget "SocketRocket" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
F6B2083B1450F597009315AF /* Debug */,
|
||||
F6B2083C1450F597009315AF /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
F6BDA813145900D200FE3253 /* Build configuration list for PBXNativeTarget "SRWebSocketTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
F6BDA811145900D200FE3253 /* Debug */,
|
||||
F6BDA812145900D200FE3253 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = F6B208241450F597009315AF /* Project object */;
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0630"
|
||||
LastUpgradeVersion = "0730"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -14,95 +14,61 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F62417E214D52F3C003CE997"
|
||||
BuildableName = "TestChat.app"
|
||||
BlueprintName = "TestChat"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS-Dynamic"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F62417E214D52F3C003CE997"
|
||||
BuildableName = "TestChat.app"
|
||||
BlueprintName = "TestChat"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F62417E214D52F3C003CE997"
|
||||
BuildableName = "TestChat.app"
|
||||
BlueprintName = "TestChat"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS-Dynamic"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
<AdditionalOption
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
key = "NSZombieEnabled"
|
||||
value = "YES"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
>>>>>>> origin/master
|
||||
key = "OBJC_PRINT_EXCEPTIONS"
|
||||
value = "YES"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
<AdditionalOption
|
||||
key = "MallocGuardEdges"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
key = "MallocScribble"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
>>>>>>> origin/master
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F62417E214D52F3C003CE997"
|
||||
BuildableName = "TestChat.app"
|
||||
BlueprintName = "TestChat"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS-Dynamic"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0710"
|
||||
version = "1.3">
|
||||
LastUpgradeVersion = "0730"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@ -14,51 +14,23 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6F199B51C3E48A70068E02B"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForRunning = "NO"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F69DAA1C1B6AEF6900B456D0"
|
||||
BuildableName = "SocketRocketIOTests.xctest"
|
||||
BlueprintName = "SocketRocketIOTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F69DAA131B6AEF6900B456D0"
|
||||
BuildableName = "SocketRocketIO.framework"
|
||||
BlueprintName = "SocketRocketIO"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6F199BE1C3E48A70068E02B"
|
||||
BuildableName = "SocketRocketTests.xctest"
|
||||
BlueprintName = "SocketRocketTests"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
@ -69,24 +41,50 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<PreActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Script"
|
||||
scriptText = "PIDFILE=$TMPDIR/sr_test_server.pid if [ -r $PIDFILE ]; then EXISTING_PID=`cat $PIDFILE` echo "Killing Dangling Autobahn Server PID:" $EXISTING_PID kill $EXISTING_PID || true rm $PIDFILE fi pushd $PROJECT_DIR source .env/bin/activate nohup ./TestSupport/run_test_server.sh & echo $! > $PIDFILE popd ">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PreActions>
|
||||
<PostActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Script"
|
||||
scriptText = "PIDFILE=$TMPDIR/sr_test_server.pid if [ -r $PIDFILE ]; then EXISTING_PID=`cat $PIDFILE` echo "Killing SR TestServer PID:" $EXISTING_PID kill $EXISTING_PID rm $PIDFILE fi ">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PostActions>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F69DAA1C1B6AEF6900B456D0"
|
||||
BuildableName = "SocketRocketIOTests.xctest"
|
||||
BlueprintName = "SocketRocketIOTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6F199BE1C3E48A70068E02B"
|
||||
BuildableName = "SocketRocketTests.xctest"
|
||||
BlueprintName = "SocketRocketTests"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
@ -94,9 +92,9 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6F199B51C3E48A70068E02B"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
@ -116,9 +114,9 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6F199B51C3E48A70068E02B"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
@ -134,9 +132,9 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6F199B51C3E48A70068E02B"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
LastUpgradeVersion = "0730"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -16,7 +16,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F668C87F153E91210044DBAC"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocketOSX"
|
||||
BlueprintName = "SocketRocket-macOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
@ -47,7 +47,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F668C87F153E91210044DBAC"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocketOSX"
|
||||
BlueprintName = "SocketRocket-macOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
@ -60,6 +60,15 @@
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F668C87F153E91210044DBAC"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-macOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3345DC821C52ACD70083CCB8"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-tvOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS-Dynamic"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3345DC821C52ACD70083CCB8"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-tvOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
LastUpgradeVersion = "0730"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -15,8 +15,8 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
@ -33,8 +33,8 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
@ -56,8 +56,8 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
LastUpgradeVersion = "0730"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -25,49 +25,9 @@
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6191F401B6C4694003DF396"
|
||||
BuildableName = "SystemShimsTests.xctest"
|
||||
BlueprintName = "SystemShimsTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6F199741C3E476C0068E02B"
|
||||
BuildableName = "SocketRocketBaseTests.xctest"
|
||||
BlueprintName = "SocketRocketBaseTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6F1999C1C3E486D0068E02B"
|
||||
BuildableName = "SocketRocketLiteTests.xctest"
|
||||
BlueprintName = "SocketRocketLiteTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6F199BE1C3E48A70068E02B"
|
||||
BuildableName = "SocketRocketTests.xctest"
|
||||
BlueprintName = "SocketRocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
@ -102,6 +62,26 @@
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
<AdditionalOption
|
||||
key = "NSZombieEnabled"
|
||||
value = "YES"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
key = "OBJC_PRINT_EXCEPTIONS"
|
||||
value = "YES"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
key = "MallocGuardEdges"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
key = "MallocScribble"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
//
|
||||
// Common.h
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Michael Lewis on 1/15/13.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef SocketRocket_Common_h
|
||||
#define SocketRocket_Common_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||
#define sr_dispatch_retain(x)
|
||||
#define sr_dispatch_release(x)
|
||||
#define __sr_maybe_bridge__ __bridge
|
||||
#define __sr_maybe_strong__ __strong
|
||||
#else
|
||||
#define sr_dispatch_retain(x) dispatch_retain(x)
|
||||
#define sr_dispatch_release(x) dispatch_release(x)
|
||||
#define __sr_maybe_bridge__
|
||||
#define __sr_maybe_strong__
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -1,77 +0,0 @@
|
||||
//
|
||||
// WebSocketVersionNumber.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 1/7/16.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Versions defined in RFC6455 section 11.6 https://tools.ietf.org/html/rfc6455#section-11.6
|
||||
/// We only care about one currently
|
||||
public enum WebSocketVersionNumber : Int {
|
||||
/// Standard version number
|
||||
case RFC6455 = 13
|
||||
}
|
||||
|
||||
/// opcodes defined in RFC6455 section 11.8 https://tools.ietf.org/html/rfc6455#section-11.8
|
||||
public enum WebSocketOpcode : Int {
|
||||
case Continuation = 0
|
||||
case Text = 1
|
||||
case Binary = 2
|
||||
case ConnectionClose = 3
|
||||
case Ping = 9
|
||||
case Pong = 10
|
||||
}
|
||||
|
||||
/// Known close codes. These are defined in https://tools.ietf.org/html/rfc6455#section-7.4
|
||||
public enum WebSocketCloseCode : Int {
|
||||
case NormalClosure = 1000
|
||||
case GoingAway = 1001
|
||||
case ProtocolError = 1002
|
||||
case UnsupportedData = 1003
|
||||
// 1004 is reserved
|
||||
case NoStatusRcvd = 1005
|
||||
case AbnormalClosure = 1006
|
||||
case InvalidFramePayloadData = 1007
|
||||
case PolicyViolation = 1008
|
||||
case MessageTooBig = 1009
|
||||
case MandatoryExt = 1010
|
||||
case InternalServerError = 1011
|
||||
case TLSHandshake = 1015
|
||||
}
|
||||
|
||||
/// Represents either a known close code or unknown
|
||||
public enum AnyWebSocketCloseCode {
|
||||
/// This is for known close codes
|
||||
case Known(WebSocketCloseCode)
|
||||
/// Unknown close codes
|
||||
case Unknown(Int)
|
||||
|
||||
init(_ code: Int){
|
||||
if let knownCode = WebSocketCloseCode(rawValue: code) {
|
||||
self = .Known(knownCode)
|
||||
} else {
|
||||
self = .Unknown(code)
|
||||
}
|
||||
}
|
||||
|
||||
public var code: Int {
|
||||
switch self {
|
||||
case let .Known(val):
|
||||
return val.rawValue
|
||||
case let .Unknown(val):
|
||||
return val
|
||||
}
|
||||
}
|
||||
/// Whether or not it was a clean closure
|
||||
public var isClean: Bool {
|
||||
switch self {
|
||||
case .Known(.NormalClosure):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,149 +0,0 @@
|
||||
//
|
||||
// DispatchData.h
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Michael Lewis on 1/12/13.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __SocketRocket__DispatchData__
|
||||
#define __SocketRocket__DispatchData__
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <deque>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
namespace squareup {
|
||||
namespace dispatch {
|
||||
class Data {
|
||||
__sr_maybe_strong__ dispatch_data_t _data;
|
||||
public:
|
||||
inline Data() : Data(dispatch_data_empty) {
|
||||
|
||||
}
|
||||
|
||||
// Initializes a new data. retains the data by default
|
||||
// Data always releases _data;
|
||||
inline Data(dispatch_data_t data, bool retain = true) : _data(data) {
|
||||
if (retain && _data) {
|
||||
sr_dispatch_retain(_data);
|
||||
}
|
||||
}
|
||||
|
||||
// Will copy the data
|
||||
inline Data(const char *str, dispatch_queue_t release_queue) :
|
||||
_data(dispatch_data_create(reinterpret_cast<const void *>(str), strlen(str), release_queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT)) {
|
||||
}
|
||||
|
||||
// Copy constructor
|
||||
inline Data(const Data &other) : Data(static_cast<dispatch_data_t>(other), true) {
|
||||
|
||||
}
|
||||
|
||||
inline Data(const void *bytes, size_t length, dispatch_queue_t release_queue) :
|
||||
_data(dispatch_data_create(bytes, length, release_queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT)) {
|
||||
}
|
||||
|
||||
|
||||
inline Data Concat(dispatch_data_t other) const {
|
||||
return Data(dispatch_data_create_concat(_data, other), false);
|
||||
}
|
||||
|
||||
|
||||
inline Data operator + (dispatch_data_t other) const {
|
||||
return Concat(other);
|
||||
}
|
||||
|
||||
inline Data Subrange(size_t offset, size_t length) const {
|
||||
return Data(dispatch_data_create_subrange(_data, offset, length), false);
|
||||
}
|
||||
|
||||
// copies bytes into the buffer, and returns the remaining.
|
||||
inline Data TakeInto(size_t length, void *bytes) const {
|
||||
size_t size = Size();
|
||||
assert(length <= size);
|
||||
|
||||
|
||||
Apply([&](dispatch_data_t region, size_t offset, const void *buffer, size_t size) -> bool {
|
||||
size_t numToCopy = std::min(size, length - offset);
|
||||
|
||||
memcpy(reinterpret_cast<void *>(reinterpret_cast<uint8_t *>(bytes) + offset), buffer, numToCopy);
|
||||
|
||||
return size + offset <= length;
|
||||
});
|
||||
|
||||
if (length == size) {
|
||||
return dispatch_data_empty;
|
||||
}
|
||||
return Subrange(length, size - length);
|
||||
}
|
||||
|
||||
inline Data Map(const void **buffer_ptr, size_t *size_ptr) const {
|
||||
return Data(dispatch_data_create_map(_data, buffer_ptr, size_ptr), false);
|
||||
}
|
||||
|
||||
inline Data CopyRegion(size_t location, size_t *offset_ptr) const {
|
||||
return Data(dispatch_data_copy_region(_data, location, offset_ptr), false);
|
||||
}
|
||||
|
||||
inline bool Apply(dispatch_data_applier_t applier) const {
|
||||
return dispatch_data_apply(_data, applier);
|
||||
}
|
||||
|
||||
inline size_t Size() const {
|
||||
return dispatch_data_get_size(static_cast<dispatch_data_t>(*this));
|
||||
}
|
||||
|
||||
// This is a workaround since the memory gets quite fragmented if you use it as a stream
|
||||
inline void FlattenIfNecessary() {
|
||||
if (NumRegions() > 100) {
|
||||
size_t size;
|
||||
const void * buffer;
|
||||
(*this) = Data(Map(&buffer, &size));
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t NumRegions() {
|
||||
size_t ret = 0;
|
||||
Apply([&ret](dispatch_data_t region, size_t offset, const void *buffer, size_t size) -> bool {
|
||||
ret += 1;
|
||||
return true;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline operator dispatch_data_t() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
inline Data &operator = (const dispatch_data_t &other) {
|
||||
if (other) {
|
||||
sr_dispatch_retain(other);
|
||||
}
|
||||
if (_data) {
|
||||
sr_dispatch_release(_data);
|
||||
}
|
||||
_data = other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Data &operator += (const Data &other) {
|
||||
return (*this = Concat(other));
|
||||
}
|
||||
|
||||
virtual ~Data() {
|
||||
if (_data) {
|
||||
sr_dispatch_release(_data);
|
||||
}
|
||||
}
|
||||
|
||||
static const Data empty;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(__SocketRocket__DispatchData__) */
|
||||
@ -1,15 +0,0 @@
|
||||
//
|
||||
// DispatchData.cpp
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Michael Lewis on 1/12/13.
|
||||
//
|
||||
//
|
||||
|
||||
#include "DispatchData.h"
|
||||
|
||||
namespace squareup {
|
||||
namespace dispatch {
|
||||
const Data Data::empty(dispatch_data_empty);
|
||||
}
|
||||
}
|
||||
@ -1,91 +0,0 @@
|
||||
//
|
||||
// DispatchChannel.h
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Michael Lewis on 1/12/13.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __SocketRocket__DispatchChannel__
|
||||
#define __SocketRocket__DispatchChannel__
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
#include <netdb.h>
|
||||
}
|
||||
|
||||
namespace squareup {
|
||||
namespace dispatch {
|
||||
class RawIO;
|
||||
|
||||
|
||||
typedef void (^dial_callback)(dispatch_fd_t fd, int error_code, const char *error_message);
|
||||
|
||||
void Dial(dispatch_queue_t workQueue, const char *hostname, const char *servname, dispatch_queue_t callback_queue, dial_callback callback);
|
||||
|
||||
// You are responsible for removing inputStream and outputStream
|
||||
typedef void (^simple_dial_callback)(RawIO *io, int error, const char *error_message);
|
||||
|
||||
// callback_queue is what is sent to the clients but also
|
||||
// parent_io_queue can be a parallel queue. the
|
||||
// close_handler is passed to the RawIO
|
||||
void SimpleDial(const char *hostname, const char *servname, dispatch_queue_t callback_queue, dispatch_queue_t parent_io_queue, simple_dial_callback dial_callback, void(^close_handler)(int error) = nullptr);
|
||||
|
||||
class IO {
|
||||
public:
|
||||
virtual void Close(dispatch_io_close_flags_t flags) = 0;
|
||||
virtual void Read(size_t length, dispatch_queue_t queue, dispatch_io_handler_t handler) = 0;
|
||||
virtual void Write(dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t handler) = 0;
|
||||
virtual void Barrier(dispatch_block_t barrier) = 0;
|
||||
|
||||
|
||||
virtual void SetHighWater(size_t high_water) = 0;
|
||||
virtual void SetLowWater(size_t low_water) = 0;
|
||||
|
||||
virtual ~IO();
|
||||
};
|
||||
|
||||
class RawIO : public IO {
|
||||
dispatch_io_t _channel;
|
||||
public:
|
||||
|
||||
void Close(dispatch_io_close_flags_t flags);
|
||||
void Read(size_t length, dispatch_queue_t queue, dispatch_io_handler_t handler);
|
||||
void Write(dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t handler);
|
||||
void Barrier(dispatch_block_t barrier);
|
||||
|
||||
void SetHighWater(size_t high_water);
|
||||
void SetLowWater(size_t low_water);
|
||||
|
||||
RawIO(dispatch_fd_t fd,
|
||||
dispatch_queue_t cleanupQueue,
|
||||
dispatch_queue_t callbackQueue,
|
||||
dispatch_queue_t ioQueue,
|
||||
void (^cleanup_handler)(int error));
|
||||
|
||||
// Takes ownership of channel
|
||||
// retain is only for the channel
|
||||
RawIO(dispatch_io_t channel,
|
||||
dispatch_queue_t callbackQueue,
|
||||
bool retain = true);
|
||||
|
||||
// Clones an existing IO
|
||||
RawIO(dispatch_io_t otherIo,
|
||||
dispatch_queue_t queue,
|
||||
dispatch_queue_t callbackQueue,
|
||||
dispatch_queue_t ioQueue,
|
||||
void (^cleanup_handler)(int error));
|
||||
|
||||
inline operator dispatch_io_t () const {
|
||||
return _channel;
|
||||
}
|
||||
|
||||
virtual ~RawIO();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(__SocketRocket__DispatchChannel__) */
|
||||
@ -1,298 +0,0 @@
|
||||
//
|
||||
// DispatchChannel.cpp
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Michael Lewis on 1/12/13.
|
||||
//
|
||||
//
|
||||
|
||||
#include "DispatchIO.h"
|
||||
#include <Block.h>
|
||||
#include <string>
|
||||
#include "Common.h"
|
||||
|
||||
extern "C" {
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
}
|
||||
|
||||
// TODO: add deadlines
|
||||
namespace squareup {
|
||||
namespace dispatch {
|
||||
|
||||
IO::~IO() {};
|
||||
class Connector;
|
||||
|
||||
typedef void(^finish_callback)(Connector *connector, addrinfo *res0, dispatch_fd_t fd, int error_code, const char *error_message);
|
||||
|
||||
class Connector {
|
||||
addrinfo *_res;
|
||||
addrinfo *_res0;
|
||||
__strong finish_callback _finishCallback;
|
||||
dispatch_queue_t _workQueue;
|
||||
int _lastError;
|
||||
const char *_lastErrorMessage;
|
||||
bool _foundAddr = false;
|
||||
|
||||
public:
|
||||
// Takes ownership of res0;
|
||||
inline Connector(dispatch_queue_t workQueue, struct addrinfo *res0, finish_callback finishCallback) : _res(res0), _res0(res0), _finishCallback([finishCallback copy]), _workQueue(workQueue) {
|
||||
sr_dispatch_retain(_workQueue);
|
||||
}
|
||||
|
||||
virtual ~Connector() {
|
||||
sr_dispatch_release(_workQueue);
|
||||
}
|
||||
|
||||
inline void NextIter() {
|
||||
_res = _res->ai_next;
|
||||
dispatch_async(_workQueue, ^{
|
||||
DoNext();
|
||||
});
|
||||
}
|
||||
|
||||
inline void DoNext() {
|
||||
// We ran out of addresses
|
||||
if (!_res) {
|
||||
assert(!_foundAddr);
|
||||
_finishCallback(this, _res0, -1, _lastError, _lastErrorMessage);
|
||||
_finishCallback = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_fd_t fd = socket(_res->ai_family, _res->ai_socktype,
|
||||
_res->ai_protocol);
|
||||
|
||||
if (fd < 0) {
|
||||
_lastError = errno;
|
||||
_lastErrorMessage = strerror(_lastError);
|
||||
NextIter();
|
||||
return;
|
||||
}
|
||||
|
||||
int curflags = fcntl(fd, F_GETFL);
|
||||
if (curflags < 0) {
|
||||
_lastError = errno;
|
||||
_lastErrorMessage = strerror(_lastError);
|
||||
NextIter();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set it to be nonblocking
|
||||
int error = fcntl(fd, F_SETFL, curflags | O_NONBLOCK);
|
||||
if (error) {
|
||||
_lastError = errno;
|
||||
_lastErrorMessage = strerror(_lastError);
|
||||
NextIter();
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_source_t readSource = nullptr;
|
||||
|
||||
readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, _workQueue);
|
||||
|
||||
dispatch_resume(readSource);
|
||||
|
||||
dispatch_source_set_event_handler(readSource, [this, fd, readSource]{
|
||||
TryConnect(false, fd, readSource);
|
||||
});
|
||||
|
||||
dispatch_source_set_cancel_handler(readSource, [fd]{
|
||||
close(fd);
|
||||
});
|
||||
|
||||
TryConnect(true, fd, readSource);
|
||||
}
|
||||
|
||||
private:
|
||||
// returns true if it is done
|
||||
// if this is the first try we call connect. otherwise we check the status
|
||||
inline void TryConnect(bool firstTry, dispatch_fd_t fd, dispatch_source_t readSource) {
|
||||
// Now, set connection to be non-blocking
|
||||
|
||||
assert(!_foundAddr);
|
||||
|
||||
assert(fd != -1);
|
||||
|
||||
_lastError = 0;
|
||||
if (firstTry) {
|
||||
int error = connect(fd, _res->ai_addr, _res->ai_addrlen);
|
||||
if (error != 0) {
|
||||
_lastError = errno;
|
||||
}
|
||||
} else {
|
||||
socklen_t len = sizeof(_lastError);
|
||||
int sockErr = getsockopt(fd, SOL_SOCKET, SO_ERROR, &_lastError, &len);
|
||||
assert(sockErr == 0);
|
||||
}
|
||||
|
||||
if (_lastError != 0) {
|
||||
if (_lastError != EINPROGRESS) {
|
||||
_lastErrorMessage = strerror(_lastError);
|
||||
dispatch_source_cancel(readSource);
|
||||
sr_dispatch_release(readSource);
|
||||
readSource = nullptr;
|
||||
NextIter();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_foundAddr = true;
|
||||
|
||||
// We don't want to close the FD, so make the cancel handler a noop
|
||||
dispatch_suspend(readSource);
|
||||
dispatch_source_set_cancel_handler(readSource, ^{});
|
||||
dispatch_resume(readSource);
|
||||
dispatch_source_cancel(readSource);
|
||||
|
||||
// Dispose of it without canceling it
|
||||
sr_dispatch_release(readSource);
|
||||
|
||||
// Successful connections get here. Then we don't try anymore
|
||||
|
||||
// If we get this far, we're done
|
||||
dispatch_async(_workQueue, [this, fd]{
|
||||
_finishCallback(this, _res0, fd, 0, nullptr);
|
||||
_finishCallback = nullptr;
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
void Dial(dispatch_queue_t workQueue, const char *hostname, const char *servname, dispatch_queue_t callback_queue, dial_callback callback) {
|
||||
callback = [callback copy];
|
||||
sr_dispatch_retain(callback_queue);
|
||||
|
||||
// Does cleanup and whatnot
|
||||
dispatch_async(workQueue, ^{
|
||||
addrinfo *res0 = nullptr;
|
||||
int error;
|
||||
const char *cause = nullptr;
|
||||
|
||||
struct addrinfo hints = {0};
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
error = getaddrinfo(hostname, servname, &hints, &res0);
|
||||
|
||||
auto finish = [callback_queue, callback, res0, workQueue](Connector *connector, addrinfo *res0, dispatch_fd_t fd, int error_code, const char *error_message){
|
||||
NSLog(@"Pew");
|
||||
dispatch_async(callback_queue, [fd, callback_queue, workQueue, error_message, callback, res0, connector, error_code]{
|
||||
callback(fd, error_code, error_message);
|
||||
|
||||
if (res0) {
|
||||
freeaddrinfo(res0);
|
||||
}
|
||||
|
||||
if (connector) {
|
||||
// Delete it after it doesn't reference this block anymore
|
||||
dispatch_async(callback_queue, [workQueue, connector, callback_queue]{
|
||||
delete connector;
|
||||
sr_dispatch_release(callback_queue);
|
||||
});
|
||||
} else {
|
||||
sr_dispatch_release(callback_queue);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (error) {
|
||||
cause = gai_strerror(error);
|
||||
finish(nullptr, res0, -1, error, cause);
|
||||
return;
|
||||
}
|
||||
|
||||
Connector *connector = new Connector(workQueue, res0, finish);
|
||||
connector->DoNext();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// callback_queue is what is sent to the clients but also
|
||||
// parent_io_queue can be a parallel queue. the
|
||||
void SimpleDial(const char *hostname, const char *servname, dispatch_queue_t callback_queue, dispatch_queue_t parent_io_queue, simple_dial_callback dial_callback, void (^close_handler)(int error)) {
|
||||
dispatch_queue_t io_queue = dispatch_queue_create("squareup.dispatch.SimpleDial IO dispatch_queue_t", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_set_target_queue(io_queue, parent_io_queue);
|
||||
|
||||
sr_dispatch_retain(callback_queue);
|
||||
if (close_handler != nullptr) {
|
||||
close_handler = [close_handler copy];
|
||||
}
|
||||
|
||||
Dial(callback_queue, hostname, servname, callback_queue, [=](dispatch_fd_t fd, int error_code, const char *error_message) {
|
||||
RawIO *io = nullptr;
|
||||
|
||||
if (error_code == 0) {
|
||||
// Going to make the writer the primary.
|
||||
// The write stream is also appropriate for closing
|
||||
io = new RawIO(fd, callback_queue, callback_queue, io_queue, [fd, close_handler](int error) {
|
||||
close(fd);
|
||||
if (close_handler != nullptr) {
|
||||
close_handler(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dial_callback(io, error_code, error_message);
|
||||
|
||||
sr_dispatch_release(io_queue);
|
||||
sr_dispatch_release(parent_io_queue);
|
||||
});
|
||||
}
|
||||
|
||||
RawIO::RawIO(dispatch_fd_t fd,
|
||||
dispatch_queue_t cleanupQueue,
|
||||
dispatch_queue_t callbackQueue,
|
||||
dispatch_queue_t ioQueue,
|
||||
void (^cleanup_handler)(int error)) {
|
||||
_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, cleanupQueue, cleanup_handler);
|
||||
dispatch_set_target_queue(_channel, ioQueue);
|
||||
}
|
||||
|
||||
// Takes ownership of channel
|
||||
// retain is only for the channel
|
||||
RawIO::RawIO(dispatch_io_t channel, dispatch_queue_t callbackQueue, bool retain) : _channel(channel) {
|
||||
if (retain) {
|
||||
sr_dispatch_retain(_channel);
|
||||
}
|
||||
};
|
||||
|
||||
// Clones an existing IO
|
||||
RawIO::RawIO(dispatch_io_t otherIo, dispatch_queue_t queue, dispatch_queue_t callbackQueue, dispatch_queue_t ioQueue, void (^cleanup_handler)(int error)) :
|
||||
RawIO(dispatch_io_create_with_io(DISPATCH_IO_STREAM, otherIo, queue, cleanup_handler), callbackQueue, false) {
|
||||
dispatch_set_target_queue(_channel, ioQueue);
|
||||
}
|
||||
|
||||
RawIO::~RawIO() {
|
||||
sr_dispatch_release(_channel);
|
||||
_channel = nullptr;
|
||||
}
|
||||
|
||||
void RawIO::Close(dispatch_io_close_flags_t flags) {
|
||||
dispatch_io_close(_channel, flags);
|
||||
}
|
||||
|
||||
void RawIO::Read(size_t length, dispatch_queue_t queue, dispatch_io_handler_t handler) {
|
||||
dispatch_io_read(_channel, 0, length, queue, handler);
|
||||
}
|
||||
|
||||
void RawIO::Write(dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t handler) {
|
||||
dispatch_io_write(_channel, 0, data, queue, handler);
|
||||
}
|
||||
|
||||
void RawIO::Barrier(dispatch_block_t barrier) {
|
||||
dispatch_io_barrier(_channel, barrier);
|
||||
}
|
||||
|
||||
void RawIO::SetHighWater(size_t high_water) {
|
||||
dispatch_io_set_high_water(_channel, high_water);
|
||||
}
|
||||
|
||||
void RawIO::SetLowWater(size_t low_water) {
|
||||
dispatch_io_set_low_water(_channel, low_water);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
SocketRocket/Internal/Delegate/SRDelegateController.h
Normal file
67
SocketRocket/Internal/Delegate/SRDelegateController.h
Normal file
@ -0,0 +1,67 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <SocketRocket/SRWebSocket.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#if OBJC_BOOL_IS_BOOL
|
||||
|
||||
struct SRDelegateAvailableMethods {
|
||||
BOOL didReceiveMessage : 1;
|
||||
BOOL didReceiveMessageWithString : 1;
|
||||
BOOL didReceiveMessageWithData : 1;
|
||||
BOOL didOpen : 1;
|
||||
BOOL didFailWithError : 1;
|
||||
BOOL didCloseWithCode : 1;
|
||||
BOOL didReceivePing : 1;
|
||||
BOOL didReceivePong : 1;
|
||||
BOOL shouldConvertTextFrameToString : 1;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
struct SRDelegateAvailableMethods {
|
||||
BOOL didReceiveMessage;
|
||||
BOOL didReceiveMessageWithString;
|
||||
BOOL didReceiveMessageWithData;
|
||||
BOOL didOpen;
|
||||
BOOL didFailWithError;
|
||||
BOOL didCloseWithCode;
|
||||
BOOL didReceivePing;
|
||||
BOOL didReceivePong;
|
||||
BOOL shouldConvertTextFrameToString;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct SRDelegateAvailableMethods SRDelegateAvailableMethods;
|
||||
|
||||
typedef void(^SRDelegateBlock)(id<SRWebSocketDelegate> _Nullable delegate, SRDelegateAvailableMethods availableMethods);
|
||||
|
||||
@interface SRDelegateController : NSObject
|
||||
|
||||
@property (nonatomic, weak) id<SRWebSocketDelegate> delegate;
|
||||
@property (atomic, readonly) SRDelegateAvailableMethods availableDelegateMethods;
|
||||
|
||||
@property (nullable, nonatomic, strong) dispatch_queue_t dispatchQueue;
|
||||
@property (nullable, nonatomic, strong) NSOperationQueue *operationQueue;
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Perform
|
||||
///--------------------------------------
|
||||
|
||||
- (void)performDelegateBlock:(SRDelegateBlock)block;
|
||||
- (void)performDelegateQueueBlock:(dispatch_block_t)block;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
138
SocketRocket/Internal/Delegate/SRDelegateController.m
Normal file
138
SocketRocket/Internal/Delegate/SRDelegateController.m
Normal file
@ -0,0 +1,138 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRDelegateController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SRDelegateController ()
|
||||
|
||||
@property (nonatomic, strong, readonly) dispatch_queue_t accessQueue;
|
||||
|
||||
@property (atomic, assign, readwrite) SRDelegateAvailableMethods availableDelegateMethods;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SRDelegateController
|
||||
|
||||
@synthesize delegate = _delegate;
|
||||
@synthesize dispatchQueue = _dispatchQueue;
|
||||
@synthesize operationQueue = _operationQueue;
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Init
|
||||
///--------------------------------------
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
_accessQueue = dispatch_queue_create("com.facebook.socketrocket.delegate.access", DISPATCH_QUEUE_CONCURRENT);
|
||||
_dispatchQueue = dispatch_get_main_queue();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Accessors
|
||||
///--------------------------------------
|
||||
|
||||
- (void)setDelegate:(id<SRWebSocketDelegate> _Nullable)delegate
|
||||
{
|
||||
dispatch_barrier_async(self.accessQueue, ^{
|
||||
_delegate = delegate;
|
||||
|
||||
self.availableDelegateMethods = (SRDelegateAvailableMethods){
|
||||
.didReceiveMessage = [delegate respondsToSelector:@selector(webSocket:didReceiveMessage:)],
|
||||
.didReceiveMessageWithString = [delegate respondsToSelector:@selector(webSocket:didReceiveMessageWithString:)],
|
||||
.didReceiveMessageWithData = [delegate respondsToSelector:@selector(webSocket:didReceiveMessageWithData:)],
|
||||
.didOpen = [delegate respondsToSelector:@selector(webSocketDidOpen:)],
|
||||
.didFailWithError = [delegate respondsToSelector:@selector(webSocket:didFailWithError:)],
|
||||
.didCloseWithCode = [delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)],
|
||||
.didReceivePing = [delegate respondsToSelector:@selector(webSocket:didReceivePingWithData:)],
|
||||
.didReceivePong = [delegate respondsToSelector:@selector(webSocket:didReceivePong:)],
|
||||
.shouldConvertTextFrameToString = [delegate respondsToSelector:@selector(webSocketShouldConvertTextFrameToString:)]
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
- (id<SRWebSocketDelegate> _Nullable)delegate
|
||||
{
|
||||
__block id<SRWebSocketDelegate> delegate = nil;
|
||||
dispatch_sync(self.accessQueue, ^{
|
||||
delegate = _delegate;
|
||||
});
|
||||
return delegate;
|
||||
}
|
||||
|
||||
- (void)setDispatchQueue:(dispatch_queue_t _Nullable)queue
|
||||
{
|
||||
dispatch_barrier_async(self.accessQueue, ^{
|
||||
_dispatchQueue = queue ?: dispatch_get_main_queue();
|
||||
_operationQueue = nil;
|
||||
});
|
||||
}
|
||||
|
||||
- (dispatch_queue_t _Nullable)dispatchQueue
|
||||
{
|
||||
__block dispatch_queue_t queue = nil;
|
||||
dispatch_sync(self.accessQueue, ^{
|
||||
queue = _dispatchQueue;
|
||||
});
|
||||
return queue;
|
||||
}
|
||||
|
||||
- (void)setOperationQueue:(NSOperationQueue *_Nullable)queue
|
||||
{
|
||||
dispatch_barrier_async(self.accessQueue, ^{
|
||||
_dispatchQueue = queue ? nil : dispatch_get_main_queue();
|
||||
_operationQueue = queue;
|
||||
});
|
||||
}
|
||||
|
||||
- (NSOperationQueue *_Nullable)operationQueue
|
||||
{
|
||||
__block NSOperationQueue *queue = nil;
|
||||
dispatch_sync(self.accessQueue, ^{
|
||||
queue = _operationQueue;
|
||||
});
|
||||
return queue;
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Perform
|
||||
///--------------------------------------
|
||||
|
||||
- (void)performDelegateBlock:(SRDelegateBlock)block
|
||||
{
|
||||
__block __strong id<SRWebSocketDelegate> delegate = nil;
|
||||
__block SRDelegateAvailableMethods availableMethods = {};
|
||||
dispatch_sync(self.accessQueue, ^{
|
||||
delegate = _delegate; // Not `OK` to go through `self`, since queue sync.
|
||||
availableMethods = self.availableDelegateMethods; // `OK` to call through `self`, since no queue sync.
|
||||
});
|
||||
[self performDelegateQueueBlock:^{
|
||||
block(delegate, availableMethods);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)performDelegateQueueBlock:(dispatch_block_t)block
|
||||
{
|
||||
dispatch_queue_t dispatchQueue = self.dispatchQueue;
|
||||
if (dispatchQueue) {
|
||||
dispatch_async(dispatchQueue, block);
|
||||
} else {
|
||||
[self.operationQueue addOperationWithBlock:block];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
40
SocketRocket/Internal/IOConsumer/SRIOConsumer.h
Normal file
40
SocketRocket/Internal/IOConsumer/SRIOConsumer.h
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class SRWebSocket; // TODO: (nlutsenko) Remove dependency on SRWebSocket here.
|
||||
|
||||
// Returns number of bytes consumed. Returning 0 means you didn't match.
|
||||
// Sends bytes to callback handler;
|
||||
typedef size_t (^stream_scanner)(NSData *collected_data);
|
||||
typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data);
|
||||
|
||||
@interface SRIOConsumer : NSObject {
|
||||
stream_scanner _scanner;
|
||||
data_callback _handler;
|
||||
size_t _bytesNeeded;
|
||||
BOOL _readToCurrentFrame;
|
||||
BOOL _unmaskBytes;
|
||||
}
|
||||
@property (nonatomic, copy, readonly) stream_scanner consumer;
|
||||
@property (nonatomic, copy, readonly) data_callback handler;
|
||||
@property (nonatomic, assign) size_t bytesNeeded;
|
||||
@property (nonatomic, assign, readonly) BOOL readToCurrentFrame;
|
||||
@property (nonatomic, assign, readonly) BOOL unmaskBytes;
|
||||
|
||||
- (void)resetWithScanner:(stream_scanner)scanner
|
||||
handler:(data_callback)handler
|
||||
bytesNeeded:(size_t)bytesNeeded
|
||||
readToCurrentFrame:(BOOL)readToCurrentFrame
|
||||
unmaskBytes:(BOOL)unmaskBytes;
|
||||
|
||||
@end
|
||||
36
SocketRocket/Internal/IOConsumer/SRIOConsumer.m
Normal file
36
SocketRocket/Internal/IOConsumer/SRIOConsumer.m
Normal file
@ -0,0 +1,36 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRIOConsumer.h"
|
||||
|
||||
@implementation SRIOConsumer
|
||||
|
||||
@synthesize bytesNeeded = _bytesNeeded;
|
||||
@synthesize consumer = _scanner;
|
||||
@synthesize handler = _handler;
|
||||
@synthesize readToCurrentFrame = _readToCurrentFrame;
|
||||
@synthesize unmaskBytes = _unmaskBytes;
|
||||
|
||||
- (void)resetWithScanner:(stream_scanner)scanner
|
||||
handler:(data_callback)handler
|
||||
bytesNeeded:(size_t)bytesNeeded
|
||||
readToCurrentFrame:(BOOL)readToCurrentFrame
|
||||
unmaskBytes:(BOOL)unmaskBytes
|
||||
{
|
||||
_scanner = [scanner copy];
|
||||
_handler = [handler copy];
|
||||
_bytesNeeded = bytesNeeded;
|
||||
_readToCurrentFrame = readToCurrentFrame;
|
||||
_unmaskBytes = unmaskBytes;
|
||||
assert(_scanner || _bytesNeeded);
|
||||
}
|
||||
|
||||
@end
|
||||
28
SocketRocket/Internal/IOConsumer/SRIOConsumerPool.h
Normal file
28
SocketRocket/Internal/IOConsumer/SRIOConsumerPool.h
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SRIOConsumer.h" // TODO: (nlutsenko) Convert to @class and constants file for block types
|
||||
|
||||
// This class is not thread-safe, and is expected to always be run on the same queue.
|
||||
@interface SRIOConsumerPool : NSObject
|
||||
|
||||
- (instancetype)initWithBufferCapacity:(NSUInteger)poolSize;
|
||||
|
||||
- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner
|
||||
handler:(data_callback)handler
|
||||
bytesNeeded:(size_t)bytesNeeded
|
||||
readToCurrentFrame:(BOOL)readToCurrentFrame
|
||||
unmaskBytes:(BOOL)unmaskBytes;
|
||||
- (void)returnConsumer:(SRIOConsumer *)consumer;
|
||||
|
||||
@end
|
||||
64
SocketRocket/Internal/IOConsumer/SRIOConsumerPool.m
Normal file
64
SocketRocket/Internal/IOConsumer/SRIOConsumerPool.m
Normal file
@ -0,0 +1,64 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRIOConsumerPool.h"
|
||||
|
||||
@implementation SRIOConsumerPool {
|
||||
NSUInteger _poolSize;
|
||||
NSMutableArray<SRIOConsumer *> *_bufferedConsumers;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBufferCapacity:(NSUInteger)poolSize;
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_poolSize = poolSize;
|
||||
_bufferedConsumers = [NSMutableArray arrayWithCapacity:poolSize];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithBufferCapacity:8];
|
||||
}
|
||||
|
||||
- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner
|
||||
handler:(data_callback)handler
|
||||
bytesNeeded:(size_t)bytesNeeded
|
||||
readToCurrentFrame:(BOOL)readToCurrentFrame
|
||||
unmaskBytes:(BOOL)unmaskBytes
|
||||
{
|
||||
SRIOConsumer *consumer = nil;
|
||||
if (_bufferedConsumers.count) {
|
||||
consumer = [_bufferedConsumers lastObject];
|
||||
[_bufferedConsumers removeLastObject];
|
||||
} else {
|
||||
consumer = [[SRIOConsumer alloc] init];
|
||||
}
|
||||
|
||||
[consumer resetWithScanner:scanner
|
||||
handler:handler
|
||||
bytesNeeded:bytesNeeded
|
||||
readToCurrentFrame:readToCurrentFrame
|
||||
unmaskBytes:unmaskBytes];
|
||||
|
||||
return consumer;
|
||||
}
|
||||
|
||||
- (void)returnConsumer:(SRIOConsumer *)consumer;
|
||||
{
|
||||
if (_bufferedConsumers.count < _poolSize) {
|
||||
[_bufferedConsumers addObject:consumer];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
13
SocketRocket/Internal/NSRunLoop+SRWebSocketPrivate.h
Normal file
13
SocketRocket/Internal/NSRunLoop+SRWebSocketPrivate.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <SocketRocket/NSRunLoop+SRWebSocket.h>
|
||||
|
||||
// Empty function that force links the object file for the category.
|
||||
extern void import_NSRunLoop_SRWebSocket(void);
|
||||
13
SocketRocket/Internal/NSURLRequest+SRWebSocketPrivate.h
Normal file
13
SocketRocket/Internal/NSURLRequest+SRWebSocketPrivate.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <SocketRocket/NSURLRequest+SRWebSocket.h>
|
||||
|
||||
// Empty function that force links the object file for the category.
|
||||
extern void import_NSURLRequest_SRWebSocket(void);
|
||||
26
SocketRocket/Internal/Proxy/SRProxyConnect.h
Normal file
26
SocketRocket/Internal/Proxy/SRProxyConnect.h
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^SRProxyConnectCompletion)(NSError *_Nullable error,
|
||||
NSInputStream *_Nullable readStream,
|
||||
NSOutputStream *_Nullable writeStream);
|
||||
|
||||
@interface SRProxyConnect : NSObject
|
||||
|
||||
- (instancetype)initWithURL:(NSURL *)url;
|
||||
|
||||
- (void)openNetworkStreamWithCompletion:(SRProxyConnectCompletion)completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
481
SocketRocket/Internal/Proxy/SRProxyConnect.m
Normal file
481
SocketRocket/Internal/Proxy/SRProxyConnect.m
Normal file
@ -0,0 +1,481 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRProxyConnect.h"
|
||||
|
||||
#import "NSRunLoop+SRWebSocket.h"
|
||||
#import "SRConstants.h"
|
||||
#import "SRError.h"
|
||||
#import "SRLog.h"
|
||||
#import "SRURLUtilities.h"
|
||||
|
||||
@interface SRProxyConnect() <NSStreamDelegate>
|
||||
|
||||
@property (nonatomic, strong) NSURL *url;
|
||||
@property (nonatomic, strong) NSInputStream *inputStream;
|
||||
@property (nonatomic, strong) NSOutputStream *outputStream;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SRProxyConnect
|
||||
{
|
||||
SRProxyConnectCompletion _completion;
|
||||
|
||||
NSString *_httpProxyHost;
|
||||
uint32_t _httpProxyPort;
|
||||
|
||||
CFHTTPMessageRef _receivedHTTPHeaders;
|
||||
|
||||
NSString *_socksProxyHost;
|
||||
uint32_t _socksProxyPort;
|
||||
NSString *_socksProxyUsername;
|
||||
NSString *_socksProxyPassword;
|
||||
|
||||
BOOL _connectionRequiresSSL;
|
||||
|
||||
NSMutableArray<NSData *> *_inputQueue;
|
||||
dispatch_queue_t _writeQueue;
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Init
|
||||
///--------------------------------------
|
||||
|
||||
-(instancetype)initWithURL:(NSURL *)url
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
_url = url;
|
||||
_connectionRequiresSSL = SRURLRequiresSSL(url);
|
||||
|
||||
_writeQueue = dispatch_queue_create("com.facebook.socketrocket.proxyconnect.write", DISPATCH_QUEUE_SERIAL);
|
||||
_inputQueue = [NSMutableArray arrayWithCapacity:2];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// If we get deallocated before the socket open finishes - we need to cleanup everything.
|
||||
|
||||
[self.inputStream removeFromRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
self.inputStream.delegate = nil;
|
||||
[self.inputStream close];
|
||||
self.inputStream = nil;
|
||||
|
||||
self.outputStream.delegate = nil;
|
||||
[self.outputStream close];
|
||||
self.outputStream = nil;
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Open
|
||||
///--------------------------------------
|
||||
|
||||
- (void)openNetworkStreamWithCompletion:(SRProxyConnectCompletion)completion
|
||||
{
|
||||
_completion = completion;
|
||||
[self _configureProxy];
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Flow
|
||||
///--------------------------------------
|
||||
|
||||
- (void)_didConnect
|
||||
{
|
||||
SRDebugLog(@"_didConnect, return streams");
|
||||
if (_connectionRequiresSSL) {
|
||||
if (_httpProxyHost) {
|
||||
// Must set the real peer name before turning on SSL
|
||||
SRDebugLog(@"proxy set peer name to real host %@", self.url.host);
|
||||
[self.outputStream setProperty:self.url.host forKey:@"_kCFStreamPropertySocketPeerName"];
|
||||
}
|
||||
}
|
||||
if (_receivedHTTPHeaders) {
|
||||
CFRelease(_receivedHTTPHeaders);
|
||||
_receivedHTTPHeaders = NULL;
|
||||
}
|
||||
|
||||
NSInputStream *inputStream = self.inputStream;
|
||||
NSOutputStream *outputStream = self.outputStream;
|
||||
|
||||
self.inputStream = nil;
|
||||
self.outputStream = nil;
|
||||
|
||||
[inputStream removeFromRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
inputStream.delegate = nil;
|
||||
outputStream.delegate = nil;
|
||||
|
||||
_completion(nil, inputStream, outputStream);
|
||||
}
|
||||
|
||||
- (void)_failWithError:(NSError *)error
|
||||
{
|
||||
SRDebugLog(@"_failWithError, return error");
|
||||
if (!error) {
|
||||
error = SRHTTPErrorWithCodeDescription(500, 2132,@"Proxy Error");
|
||||
}
|
||||
|
||||
if (_receivedHTTPHeaders) {
|
||||
CFRelease(_receivedHTTPHeaders);
|
||||
_receivedHTTPHeaders = NULL;
|
||||
}
|
||||
|
||||
self.inputStream.delegate = nil;
|
||||
self.outputStream.delegate = nil;
|
||||
|
||||
[self.inputStream removeFromRunLoop:[NSRunLoop SR_networkRunLoop]
|
||||
forMode:NSDefaultRunLoopMode];
|
||||
[self.inputStream close];
|
||||
[self.outputStream close];
|
||||
self.inputStream = nil;
|
||||
self.outputStream = nil;
|
||||
_completion(error, nil, nil);
|
||||
}
|
||||
|
||||
// get proxy setting from device setting
|
||||
- (void)_configureProxy
|
||||
{
|
||||
SRDebugLog(@"configureProxy");
|
||||
NSDictionary *proxySettings = CFBridgingRelease(CFNetworkCopySystemProxySettings());
|
||||
|
||||
// CFNetworkCopyProxiesForURL doesn't understand ws:// or wss://
|
||||
NSURL *httpURL;
|
||||
if (_connectionRequiresSSL) {
|
||||
httpURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@", _url.host]];
|
||||
} else {
|
||||
httpURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@", _url.host]];
|
||||
}
|
||||
|
||||
NSArray *proxies = CFBridgingRelease(CFNetworkCopyProxiesForURL((__bridge CFURLRef)httpURL, (__bridge CFDictionaryRef)proxySettings));
|
||||
if (proxies.count == 0) {
|
||||
SRDebugLog(@"configureProxy no proxies");
|
||||
[self _openConnection];
|
||||
return; // no proxy
|
||||
}
|
||||
NSDictionary *settings = [proxies objectAtIndex:0];
|
||||
NSString *proxyType = settings[(NSString *)kCFProxyTypeKey];
|
||||
if ([proxyType isEqualToString:(NSString *)kCFProxyTypeAutoConfigurationURL]) {
|
||||
NSURL *pacURL = settings[(NSString *)kCFProxyAutoConfigurationURLKey];
|
||||
if (pacURL) {
|
||||
[self _fetchPAC:pacURL withProxySettings:proxySettings];
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ([proxyType isEqualToString:(__bridge NSString *)kCFProxyTypeAutoConfigurationJavaScript]) {
|
||||
NSString *script = settings[(__bridge NSString *)kCFProxyAutoConfigurationJavaScriptKey];
|
||||
if (script) {
|
||||
[self _runPACScript:script withProxySettings:proxySettings];
|
||||
return;
|
||||
}
|
||||
}
|
||||
[self _readProxySettingWithType:proxyType settings:settings];
|
||||
|
||||
[self _openConnection];
|
||||
}
|
||||
|
||||
- (void)_readProxySettingWithType:(NSString *)proxyType settings:(NSDictionary *)settings
|
||||
{
|
||||
if ([proxyType isEqualToString:(NSString *)kCFProxyTypeHTTP] ||
|
||||
[proxyType isEqualToString:(NSString *)kCFProxyTypeHTTPS]) {
|
||||
_httpProxyHost = settings[(NSString *)kCFProxyHostNameKey];
|
||||
NSNumber *portValue = settings[(NSString *)kCFProxyPortNumberKey];
|
||||
if (portValue) {
|
||||
_httpProxyPort = [portValue intValue];
|
||||
}
|
||||
}
|
||||
if ([proxyType isEqualToString:(NSString *)kCFProxyTypeSOCKS]) {
|
||||
_socksProxyHost = settings[(NSString *)kCFProxyHostNameKey];
|
||||
NSNumber *portValue = settings[(NSString *)kCFProxyPortNumberKey];
|
||||
if (portValue)
|
||||
_socksProxyPort = [portValue intValue];
|
||||
_socksProxyUsername = settings[(NSString *)kCFProxyUsernameKey];
|
||||
_socksProxyPassword = settings[(NSString *)kCFProxyPasswordKey];
|
||||
}
|
||||
if (_httpProxyHost) {
|
||||
SRDebugLog(@"Using http proxy %@:%u", _httpProxyHost, _httpProxyPort);
|
||||
} else if (_socksProxyHost) {
|
||||
SRDebugLog(@"Using socks proxy %@:%u", _socksProxyHost, _socksProxyPort);
|
||||
} else {
|
||||
SRDebugLog(@"configureProxy no proxies");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_fetchPAC:(NSURL *)PACurl withProxySettings:(NSDictionary *)proxySettings
|
||||
{
|
||||
SRDebugLog(@"SRWebSocket fetchPAC:%@", PACurl);
|
||||
|
||||
if ([PACurl isFileURL]) {
|
||||
NSError *error = nil;
|
||||
NSString *script = [NSString stringWithContentsOfURL:PACurl
|
||||
usedEncoding:NULL
|
||||
error:&error];
|
||||
|
||||
if (error) {
|
||||
[self _openConnection];
|
||||
} else {
|
||||
[self _runPACScript:script withProxySettings:proxySettings];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *scheme = [PACurl.scheme lowercaseString];
|
||||
if (![scheme isEqualToString:@"http"] && ![scheme isEqualToString:@"https"]) {
|
||||
// Don't know how to read data from this URL, we'll have to give up
|
||||
// We'll simply assume no proxies, and start the request as normal
|
||||
[self _openConnection];
|
||||
return;
|
||||
}
|
||||
__weak typeof(self) wself = self;
|
||||
NSURLRequest *request = [NSURLRequest requestWithURL:PACurl];
|
||||
NSURLSession *session = [NSURLSession sharedSession];
|
||||
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
__strong typeof(wself) sself = wself;
|
||||
if (!error) {
|
||||
NSString *script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
[sself _runPACScript:script withProxySettings:proxySettings];
|
||||
} else {
|
||||
[sself _openConnection];
|
||||
}
|
||||
}] resume];
|
||||
}
|
||||
|
||||
- (void)_runPACScript:(NSString *)script withProxySettings:(NSDictionary *)proxySettings
|
||||
{
|
||||
if (!script) {
|
||||
[self _openConnection];
|
||||
return;
|
||||
}
|
||||
SRDebugLog(@"runPACScript");
|
||||
// From: http://developer.apple.com/samplecode/CFProxySupportTool/listing1.html
|
||||
// Work around <rdar://problem/5530166>. This dummy call to
|
||||
// CFNetworkCopyProxiesForURL initialise some state within CFNetwork
|
||||
// that is required by CFNetworkCopyProxiesForAutoConfigurationScript.
|
||||
CFBridgingRelease(CFNetworkCopyProxiesForURL((__bridge CFURLRef)_url, (__bridge CFDictionaryRef)proxySettings));
|
||||
|
||||
// Obtain the list of proxies by running the autoconfiguration script
|
||||
CFErrorRef err = NULL;
|
||||
|
||||
// CFNetworkCopyProxiesForAutoConfigurationScript doesn't understand ws:// or wss://
|
||||
NSURL *httpURL;
|
||||
if (_connectionRequiresSSL)
|
||||
httpURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@", _url.host]];
|
||||
else
|
||||
httpURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@", _url.host]];
|
||||
|
||||
NSArray *proxies = CFBridgingRelease(CFNetworkCopyProxiesForAutoConfigurationScript((__bridge CFStringRef)script,(__bridge CFURLRef)httpURL, &err));
|
||||
if (!err && [proxies count] > 0) {
|
||||
NSDictionary *settings = [proxies objectAtIndex:0];
|
||||
NSString *proxyType = settings[(NSString *)kCFProxyTypeKey];
|
||||
[self _readProxySettingWithType:proxyType settings:settings];
|
||||
}
|
||||
[self _openConnection];
|
||||
}
|
||||
|
||||
- (void)_openConnection
|
||||
{
|
||||
[self _initializeStreams];
|
||||
|
||||
[self.inputStream scheduleInRunLoop:[NSRunLoop SR_networkRunLoop]
|
||||
forMode:NSDefaultRunLoopMode];
|
||||
//[self.outputStream scheduleInRunLoop:[NSRunLoop SR_networkRunLoop]
|
||||
// forMode:NSDefaultRunLoopMode];
|
||||
[self.outputStream open];
|
||||
[self.inputStream open];
|
||||
}
|
||||
|
||||
- (void)_initializeStreams
|
||||
{
|
||||
assert(_url.port.unsignedIntValue <= UINT32_MAX);
|
||||
uint32_t port = _url.port.unsignedIntValue;
|
||||
if (port == 0) {
|
||||
port = (_connectionRequiresSSL ? 443 : 80);
|
||||
}
|
||||
NSString *host = _url.host;
|
||||
|
||||
if (_httpProxyHost) {
|
||||
host = _httpProxyHost;
|
||||
port = (_httpProxyPort ?: 80);
|
||||
}
|
||||
|
||||
CFReadStreamRef readStream = NULL;
|
||||
CFWriteStreamRef writeStream = NULL;
|
||||
|
||||
SRDebugLog(@"ProxyConnect connect stream to %@:%u", host, port);
|
||||
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
|
||||
|
||||
self.outputStream = CFBridgingRelease(writeStream);
|
||||
self.inputStream = CFBridgingRelease(readStream);
|
||||
|
||||
if (_socksProxyHost) {
|
||||
SRDebugLog(@"ProxyConnect set sock property stream to %@:%u user %@ password %@", _socksProxyHost, _socksProxyPort, _socksProxyUsername, _socksProxyPassword);
|
||||
NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:4];
|
||||
settings[NSStreamSOCKSProxyHostKey] = _socksProxyHost;
|
||||
if (_socksProxyPort) {
|
||||
settings[NSStreamSOCKSProxyPortKey] = @(_socksProxyPort);
|
||||
}
|
||||
if (_socksProxyUsername) {
|
||||
settings[NSStreamSOCKSProxyUserKey] = _socksProxyUsername;
|
||||
}
|
||||
if (_socksProxyPassword) {
|
||||
settings[NSStreamSOCKSProxyPasswordKey] = _socksProxyPassword;
|
||||
}
|
||||
[self.inputStream setProperty:settings forKey:NSStreamSOCKSProxyConfigurationKey];
|
||||
[self.outputStream setProperty:settings forKey:NSStreamSOCKSProxyConfigurationKey];
|
||||
}
|
||||
self.inputStream.delegate = self;
|
||||
self.outputStream.delegate = self;
|
||||
}
|
||||
|
||||
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;
|
||||
{
|
||||
SRDebugLog(@"stream handleEvent %u", eventCode);
|
||||
switch (eventCode) {
|
||||
case NSStreamEventOpenCompleted: {
|
||||
if (aStream == self.inputStream) {
|
||||
if (_httpProxyHost) {
|
||||
[self _proxyDidConnect];
|
||||
} else {
|
||||
[self _didConnect];
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case NSStreamEventErrorOccurred: {
|
||||
[self _failWithError:aStream.streamError];
|
||||
} break;
|
||||
case NSStreamEventEndEncountered: {
|
||||
[self _failWithError:aStream.streamError];
|
||||
} break;
|
||||
case NSStreamEventHasBytesAvailable: {
|
||||
if (aStream == _inputStream) {
|
||||
[self _processInputStream];
|
||||
}
|
||||
} break;
|
||||
case NSStreamEventHasSpaceAvailable:
|
||||
case NSStreamEventNone:
|
||||
SRDebugLog(@"(default) %@", aStream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_proxyDidConnect
|
||||
{
|
||||
SRDebugLog(@"Proxy Connected");
|
||||
uint32_t port = _url.port.unsignedIntValue;
|
||||
if (port == 0) {
|
||||
port = (_connectionRequiresSSL ? 443 : 80);
|
||||
}
|
||||
// Send HTTP CONNECT Request
|
||||
NSString *connectRequestStr = [NSString stringWithFormat:@"CONNECT %@:%u HTTP/1.1\r\nHost: %@\r\nConnection: keep-alive\r\nProxy-Connection: keep-alive\r\n\r\n", _url.host, port, _url.host];
|
||||
|
||||
NSData *message = [connectRequestStr dataUsingEncoding:NSUTF8StringEncoding];
|
||||
SRDebugLog(@"Proxy sending %@", connectRequestStr);
|
||||
|
||||
[self _writeData:message];
|
||||
}
|
||||
|
||||
///handles the incoming bytes and sending them to the proper processing method
|
||||
- (void)_processInputStream
|
||||
{
|
||||
NSMutableData *buf = [NSMutableData dataWithCapacity:SRDefaultBufferSize()];
|
||||
uint8_t *buffer = buf.mutableBytes;
|
||||
NSInteger length = [_inputStream read:buffer maxLength:SRDefaultBufferSize()];
|
||||
|
||||
if (length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL process = (_inputQueue.count == 0);
|
||||
[_inputQueue addObject:[NSData dataWithBytes:buffer length:length]];
|
||||
|
||||
if (process) {
|
||||
[self _dequeueInput];
|
||||
}
|
||||
}
|
||||
|
||||
// dequeue the incoming input so it is processed in order
|
||||
|
||||
- (void)_dequeueInput
|
||||
{
|
||||
while (_inputQueue.count > 0) {
|
||||
NSData *data = _inputQueue.firstObject;
|
||||
[_inputQueue removeObjectAtIndex:0];
|
||||
|
||||
// No need to process any data further, we got the full header data.
|
||||
if ([self _proxyProcessHTTPResponseWithData:data]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//handle checking the proxy connection status
|
||||
- (BOOL)_proxyProcessHTTPResponseWithData:(NSData *)data
|
||||
{
|
||||
if (_receivedHTTPHeaders == NULL) {
|
||||
_receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO);
|
||||
}
|
||||
|
||||
CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);
|
||||
if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) {
|
||||
SRDebugLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders)));
|
||||
[self _proxyHTTPHeadersDidFinish];
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)_proxyHTTPHeadersDidFinish
|
||||
{
|
||||
NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders);
|
||||
|
||||
if (responseCode >= 299) {
|
||||
SRDebugLog(@"Connect to Proxy Request failed with response code %d", responseCode);
|
||||
NSError *error = SRHTTPErrorWithCodeDescription(responseCode, 2132,
|
||||
[NSString stringWithFormat:@"Received bad response code from proxy server: %d.",
|
||||
(int)responseCode]);
|
||||
[self _failWithError:error];
|
||||
return;
|
||||
}
|
||||
SRDebugLog(@"proxy connect return %d, call socket connect", responseCode);
|
||||
[self _didConnect];
|
||||
}
|
||||
|
||||
static NSTimeInterval const SRProxyConnectWriteTimeout = 5.0;
|
||||
|
||||
- (void)_writeData:(NSData *)data
|
||||
{
|
||||
const uint8_t * bytes = data.bytes;
|
||||
__block NSInteger timeout = (NSInteger)(SRProxyConnectWriteTimeout * 1000000); // wait timeout before giving up
|
||||
__weak typeof(self) wself = self;
|
||||
dispatch_async(_writeQueue, ^{
|
||||
__strong typeof(wself) sself = self;
|
||||
if (!sself) {
|
||||
return;
|
||||
}
|
||||
NSOutputStream *outStream = sself.outputStream;
|
||||
if (!outStream) {
|
||||
return;
|
||||
}
|
||||
while (![outStream hasSpaceAvailable]) {
|
||||
usleep(100); //wait until the socket is ready
|
||||
timeout -= 100;
|
||||
if (timeout < 0) {
|
||||
NSError *error = SRHTTPErrorWithCodeDescription(408, 2132, @"Proxy timeout");
|
||||
[sself _failWithError:error];
|
||||
} else if (outStream.streamError != nil) {
|
||||
[sself _failWithError:outStream.streamError];
|
||||
}
|
||||
}
|
||||
[outStream write:bytes maxLength:data.length];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
24
SocketRocket/Internal/RunLoop/SRRunLoopThread.h
Normal file
24
SocketRocket/Internal/RunLoop/SRRunLoopThread.h
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SRRunLoopThread : NSThread
|
||||
|
||||
@property (nonatomic, strong, readonly) NSRunLoop *runLoop;
|
||||
|
||||
+ (instancetype)sharedThread;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
83
SocketRocket/Internal/RunLoop/SRRunLoopThread.m
Normal file
83
SocketRocket/Internal/RunLoop/SRRunLoopThread.m
Normal file
@ -0,0 +1,83 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRRunLoopThread.h"
|
||||
|
||||
@interface SRRunLoopThread ()
|
||||
{
|
||||
dispatch_group_t _waitGroup;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong, readwrite) NSRunLoop *runLoop;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SRRunLoopThread
|
||||
|
||||
+ (instancetype)sharedThread
|
||||
{
|
||||
static SRRunLoopThread *thread;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
thread = [[SRRunLoopThread alloc] init];
|
||||
thread.name = @"com.facebook.SocketRocket.NetworkThread";
|
||||
[thread start];
|
||||
});
|
||||
return thread;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_waitGroup = dispatch_group_create();
|
||||
dispatch_group_enter(_waitGroup);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)main
|
||||
{
|
||||
@autoreleasepool {
|
||||
_runLoop = [NSRunLoop currentRunLoop];
|
||||
dispatch_group_leave(_waitGroup);
|
||||
|
||||
// Add an empty run loop source to prevent runloop from spinning.
|
||||
CFRunLoopSourceContext sourceCtx = {
|
||||
.version = 0,
|
||||
.info = NULL,
|
||||
.retain = NULL,
|
||||
.release = NULL,
|
||||
.copyDescription = NULL,
|
||||
.equal = NULL,
|
||||
.hash = NULL,
|
||||
.schedule = NULL,
|
||||
.cancel = NULL,
|
||||
.perform = NULL
|
||||
};
|
||||
CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx);
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
|
||||
CFRelease(source);
|
||||
|
||||
while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) {
|
||||
|
||||
}
|
||||
assert(NO);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSRunLoop *)runLoop;
|
||||
{
|
||||
dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER);
|
||||
return _runLoop;
|
||||
}
|
||||
|
||||
@end
|
||||
26
SocketRocket/Internal/SRConstants.h
Normal file
26
SocketRocket/Internal/SRConstants.h
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, SROpCode)
|
||||
{
|
||||
SROpCodeTextFrame = 0x1,
|
||||
SROpCodeBinaryFrame = 0x2,
|
||||
// 3-7 reserved.
|
||||
SROpCodeConnectionClose = 0x8,
|
||||
SROpCodePing = 0x9,
|
||||
SROpCodePong = 0xA,
|
||||
// B-F reserved.
|
||||
};
|
||||
|
||||
/**
|
||||
Default buffer size that is used for reading/writing to streams.
|
||||
*/
|
||||
extern size_t SRDefaultBufferSize(void);
|
||||
19
SocketRocket/Internal/SRConstants.m
Normal file
19
SocketRocket/Internal/SRConstants.m
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRConstants.h"
|
||||
|
||||
size_t SRDefaultBufferSize(void) {
|
||||
static size_t size;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
size = getpagesize();
|
||||
});
|
||||
return size;
|
||||
}
|
||||
27
SocketRocket/Internal/Security/SRPinningSecurityPolicy.h
Normal file
27
SocketRocket/Internal/Security/SRPinningSecurityPolicy.h
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <SocketRocket/SRSecurityPolicy.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* NOTE: While publicly, SocketRocket does not support configuring the security policy with pinned certificates,
|
||||
* it is still possible to manually construct a security policy of this class. If you do this, note that you may
|
||||
* be open to MitM attacks, and we will not support any issues you may have. Dive at your own risk.
|
||||
*/
|
||||
@interface SRPinningSecurityPolicy : SRSecurityPolicy
|
||||
|
||||
- (instancetype)initWithCertificates:(NSArray *)pinnedCertificates;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
73
SocketRocket/Internal/Security/SRPinningSecurityPolicy.m
Normal file
73
SocketRocket/Internal/Security/SRPinningSecurityPolicy.m
Normal file
@ -0,0 +1,73 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRPinningSecurityPolicy.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SRLog.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SRPinningSecurityPolicy ()
|
||||
|
||||
@property (nonatomic, copy, readonly) NSArray *pinnedCertificates;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SRPinningSecurityPolicy
|
||||
|
||||
- (instancetype)initWithCertificates:(NSArray *)pinnedCertificates
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
|
||||
// Do not validate certificate chain since we're pinning to specific certificates.
|
||||
self = [super initWithCertificateChainValidationEnabled:NO];
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
if (!self) { return self; }
|
||||
|
||||
if (pinnedCertificates.count == 0) {
|
||||
@throw [NSException exceptionWithName:@"Creating security policy failed."
|
||||
reason:@"Must specify at least one certificate when creating a pinning policy."
|
||||
userInfo:nil];
|
||||
}
|
||||
_pinnedCertificates = [pinnedCertificates copy];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain
|
||||
{
|
||||
SRDebugLog(@"Pinned cert count: %d", self.pinnedCertificates.count);
|
||||
NSUInteger requiredCertCount = self.pinnedCertificates.count;
|
||||
|
||||
NSUInteger validatedCertCount = 0;
|
||||
CFIndex serverCertCount = SecTrustGetCertificateCount(serverTrust);
|
||||
for (CFIndex i = 0; i < serverCertCount; i++) {
|
||||
SecCertificateRef cert = SecTrustGetCertificateAtIndex(serverTrust, i);
|
||||
NSData *data = CFBridgingRelease(SecCertificateCopyData(cert));
|
||||
for (id ref in self.pinnedCertificates) {
|
||||
SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref;
|
||||
// TODO: (nlutsenko) Add caching, so we don't copy the data for every pinned cert all the time.
|
||||
NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert));
|
||||
if ([trustedCertData isEqualToData:data]) {
|
||||
validatedCertCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (requiredCertCount == validatedCertCount);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
20
SocketRocket/Internal/Utilities/SRError.h
Normal file
20
SocketRocket/Internal/Utilities/SRError.h
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSError *SRErrorWithDomainCodeDescription(NSString *domain, NSInteger code, NSString *description);
|
||||
extern NSError *SRErrorWithCodeDescription(NSInteger code, NSString *description);
|
||||
extern NSError *SRErrorWithCodeDescriptionUnderlyingError(NSInteger code, NSString *description, NSError *underlyingError);
|
||||
|
||||
extern NSError *SRHTTPErrorWithCodeDescription(NSInteger httpCode, NSInteger errorCode, NSString *description);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
42
SocketRocket/Internal/Utilities/SRError.m
Normal file
42
SocketRocket/Internal/Utilities/SRError.m
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRError.h"
|
||||
|
||||
#import "SRWebSocket.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSError *SRErrorWithDomainCodeDescription(NSString *domain, NSInteger code, NSString *description)
|
||||
{
|
||||
return [NSError errorWithDomain:domain code:code userInfo:@{ NSLocalizedDescriptionKey: description }];
|
||||
}
|
||||
|
||||
NSError *SRErrorWithCodeDescription(NSInteger code, NSString *description)
|
||||
{
|
||||
return SRErrorWithDomainCodeDescription(SRWebSocketErrorDomain, code, description);
|
||||
}
|
||||
|
||||
NSError *SRErrorWithCodeDescriptionUnderlyingError(NSInteger code, NSString *description, NSError *underlyingError)
|
||||
{
|
||||
return [NSError errorWithDomain:SRWebSocketErrorDomain
|
||||
code:code
|
||||
userInfo:@{ NSLocalizedDescriptionKey: description,
|
||||
NSUnderlyingErrorKey: underlyingError }];
|
||||
}
|
||||
|
||||
NSError *SRHTTPErrorWithCodeDescription(NSInteger httpCode, NSInteger errorCode, NSString *description)
|
||||
{
|
||||
return [NSError errorWithDomain:SRWebSocketErrorDomain
|
||||
code:errorCode
|
||||
userInfo:@{ NSLocalizedDescriptionKey: description,
|
||||
SRHTTPResponseErrorKey: @(httpCode) }];
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
20
SocketRocket/Internal/Utilities/SRHTTPConnectMessage.h
Normal file
20
SocketRocket/Internal/Utilities/SRHTTPConnectMessage.h
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern CFHTTPMessageRef SRHTTPConnectMessageCreate(NSURLRequest *request,
|
||||
NSString *securityKey,
|
||||
uint8_t webSocketProtocolVersion,
|
||||
NSArray<NSHTTPCookie *> *_Nullable cookies,
|
||||
NSArray<NSString *> *_Nullable requestedProtocols);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
79
SocketRocket/Internal/Utilities/SRHTTPConnectMessage.m
Normal file
79
SocketRocket/Internal/Utilities/SRHTTPConnectMessage.m
Normal file
@ -0,0 +1,79 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRHTTPConnectMessage.h"
|
||||
|
||||
#import "SRURLUtilities.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString *_SRHTTPConnectMessageHost(NSURL *url)
|
||||
{
|
||||
NSString *host = url.host;
|
||||
if (url.port) {
|
||||
host = [host stringByAppendingFormat:@":%@", url.port];
|
||||
}
|
||||
return host;
|
||||
}
|
||||
|
||||
CFHTTPMessageRef SRHTTPConnectMessageCreate(NSURLRequest *request,
|
||||
NSString *securityKey,
|
||||
uint8_t webSocketProtocolVersion,
|
||||
NSArray<NSHTTPCookie *> *_Nullable cookies,
|
||||
NSArray<NSString *> *_Nullable requestedProtocols)
|
||||
{
|
||||
NSURL *url = request.URL;
|
||||
|
||||
CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)url, kCFHTTPVersion1_1);
|
||||
|
||||
// Set host first so it defaults
|
||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Host"), (__bridge CFStringRef)_SRHTTPConnectMessageHost(url));
|
||||
|
||||
NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16];
|
||||
int result = SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes);
|
||||
if (result != 0) {
|
||||
//TODO: (nlutsenko) Check if there was an error.
|
||||
}
|
||||
|
||||
// Apply cookies if any have been provided
|
||||
if (cookies) {
|
||||
NSDictionary<NSString *, NSString *> *messageCookies = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
|
||||
[messageCookies enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) {
|
||||
if (key.length && obj.length) {
|
||||
CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
// set header for http basic auth
|
||||
NSString *basicAuthorizationString = SRBasicAuthorizationHeaderFromURL(url);
|
||||
if (basicAuthorizationString) {
|
||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Authorization"), (__bridge CFStringRef)basicAuthorizationString);
|
||||
}
|
||||
|
||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Upgrade"), CFSTR("websocket"));
|
||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Connection"), CFSTR("Upgrade"));
|
||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)securityKey);
|
||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)@(webSocketProtocolVersion).stringValue);
|
||||
|
||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Origin"), (__bridge CFStringRef)SRURLOrigin(url));
|
||||
|
||||
if (requestedProtocols.count) {
|
||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Sec-WebSocket-Protocol"),
|
||||
(__bridge CFStringRef)[requestedProtocols componentsJoinedByString:@", "]);
|
||||
}
|
||||
|
||||
[request.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||
CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
|
||||
}];
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
19
SocketRocket/Internal/Utilities/SRHash.h
Normal file
19
SocketRocket/Internal/Utilities/SRHash.h
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSData *SRSHA1HashFromString(NSString *string);
|
||||
extern NSData *SRSHA1HashFromBytes(const char *bytes, size_t length);
|
||||
|
||||
extern NSString *SRBase64EncodedStringFromData(NSData *data);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
43
SocketRocket/Internal/Utilities/SRHash.m
Normal file
43
SocketRocket/Internal/Utilities/SRHash.m
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRHash.h"
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSData *SRSHA1HashFromString(NSString *string)
|
||||
{
|
||||
size_t length = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
return SRSHA1HashFromBytes(string.UTF8String, length);
|
||||
}
|
||||
|
||||
NSData *SRSHA1HashFromBytes(const char *bytes, size_t length)
|
||||
{
|
||||
uint8_t outputLength = CC_SHA1_DIGEST_LENGTH;
|
||||
unsigned char output[outputLength];
|
||||
CC_SHA1(bytes, (CC_LONG)length, output);
|
||||
|
||||
return [NSData dataWithBytes:output length:outputLength];
|
||||
}
|
||||
|
||||
NSString *SRBase64EncodedStringFromData(NSData *data)
|
||||
{
|
||||
if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
|
||||
return [data base64EncodedStringWithOptions:0];
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [data base64Encoding];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
20
SocketRocket/Internal/Utilities/SRLog.h
Normal file
20
SocketRocket/Internal/Utilities/SRLog.h
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// Uncomment this line to enable debug logging
|
||||
//#define SR_DEBUG_LOG_ENABLED
|
||||
|
||||
extern void SRErrorLog(NSString *format, ...);
|
||||
extern void SRDebugLog(NSString *format, ...);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
33
SocketRocket/Internal/Utilities/SRLog.m
Normal file
33
SocketRocket/Internal/Utilities/SRLog.m
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRLog.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern void SRErrorLog(NSString *format, ...)
|
||||
{
|
||||
__block va_list arg_list;
|
||||
va_start (arg_list, format);
|
||||
|
||||
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
|
||||
|
||||
va_end(arg_list);
|
||||
|
||||
NSLog(@"[SocketRocket] %@", formattedString);
|
||||
}
|
||||
|
||||
extern void SRDebugLog(NSString *format, ...)
|
||||
{
|
||||
#ifdef SR_DEBUG_LOG_ENABLED
|
||||
SRErrorLog(tag, format);
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
22
SocketRocket/Internal/Utilities/SRMutex.h
Normal file
22
SocketRocket/Internal/Utilities/SRMutex.h
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef __attribute__((capability("mutex"))) pthread_mutex_t *SRMutex;
|
||||
|
||||
extern SRMutex SRMutexInitRecursive(void);
|
||||
extern void SRMutexDestroy(SRMutex mutex);
|
||||
|
||||
extern void SRMutexLock(SRMutex mutex) __attribute__((acquire_capability(mutex)));
|
||||
extern void SRMutexUnlock(SRMutex mutex) __attribute__((release_capability(mutex)));
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
47
SocketRocket/Internal/Utilities/SRMutex.m
Normal file
47
SocketRocket/Internal/Utilities/SRMutex.m
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRMutex.h"
|
||||
|
||||
#import <pthread/pthread.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
SRMutex SRMutexInitRecursive(void)
|
||||
{
|
||||
pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t));
|
||||
pthread_mutexattr_t attributes;
|
||||
|
||||
pthread_mutexattr_init(&attributes);
|
||||
pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(mutex, &attributes);
|
||||
pthread_mutexattr_destroy(&attributes);
|
||||
|
||||
return mutex;
|
||||
}
|
||||
|
||||
void SRMutexDestroy(SRMutex mutex)
|
||||
{
|
||||
pthread_mutex_destroy(mutex);
|
||||
free(mutex);
|
||||
}
|
||||
|
||||
__attribute__((no_thread_safety_analysis))
|
||||
void SRMutexLock(SRMutex mutex)
|
||||
{
|
||||
pthread_mutex_lock(mutex);
|
||||
}
|
||||
|
||||
__attribute__((no_thread_safety_analysis))
|
||||
void SRMutexUnlock(SRMutex mutex)
|
||||
{
|
||||
pthread_mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
16
SocketRocket/Internal/Utilities/SRRandom.h
Normal file
16
SocketRocket/Internal/Utilities/SRRandom.h
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSData *SRRandomData(NSUInteger length);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
26
SocketRocket/Internal/Utilities/SRRandom.m
Normal file
26
SocketRocket/Internal/Utilities/SRRandom.m
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRRandom.h"
|
||||
|
||||
#import <Security/SecRandom.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSData *SRRandomData(NSUInteger length)
|
||||
{
|
||||
NSMutableData *data = [NSMutableData dataWithLength:length];
|
||||
int result = SecRandomCopyBytes(kSecRandomDefault, data.length, data.mutableBytes);
|
||||
if (result != 0) {
|
||||
[NSException raise:NSInternalInconsistencyException format:@"Failed to generate random bytes with OSStatus: %d", result];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
19
SocketRocket/Internal/Utilities/SRSIMDHelpers.h
Normal file
19
SocketRocket/Internal/Utilities/SRSIMDHelpers.h
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
Unmask bytes using XOR via SIMD.
|
||||
|
||||
@param bytes The bytes to unmask.
|
||||
@param length The number of bytes to unmask.
|
||||
@param maskKey The mask to XOR with MUST be of length sizeof(uint32_t).
|
||||
*/
|
||||
void SRMaskBytesSIMD(uint8_t *bytes, size_t length, uint8_t *maskKey);
|
||||
73
SocketRocket/Internal/Utilities/SRSIMDHelpers.m
Normal file
73
SocketRocket/Internal/Utilities/SRSIMDHelpers.m
Normal file
@ -0,0 +1,73 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRSIMDHelpers.h"
|
||||
|
||||
typedef uint8_t uint8x32_t __attribute__((vector_size(32)));
|
||||
|
||||
static void SRMaskBytesManual(uint8_t *bytes, size_t length, uint8_t *maskKey) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
bytes[i] = bytes[i] ^ maskKey[i % sizeof(uint32_t)];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Right-shift the elements of a vector, circularly.
|
||||
|
||||
@param vector The vector to circular shift.
|
||||
@param by The number of elements to shift by.
|
||||
|
||||
@return A shifted vector.
|
||||
*/
|
||||
static uint8x32_t SRShiftVector(uint8x32_t vector, size_t by) {
|
||||
uint8x32_t vectorCopy = vector;
|
||||
by = by % _Alignof(uint8x32_t);
|
||||
|
||||
uint8_t *vectorPointer = (uint8_t *)&vector;
|
||||
uint8_t *vectorCopyPointer = (uint8_t *)&vectorCopy;
|
||||
|
||||
memmove(vectorPointer + by, vectorPointer, sizeof(vector) - by);
|
||||
memcpy(vectorPointer, vectorCopyPointer + (sizeof(vector) - by), by);
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
void SRMaskBytesSIMD(uint8_t *bytes, size_t length, uint8_t *maskKey) {
|
||||
size_t alignmentBytes = _Alignof(uint8x32_t) - ((uintptr_t)bytes % _Alignof(uint8x32_t));
|
||||
if (alignmentBytes == _Alignof(uint8x32_t)) {
|
||||
alignmentBytes = 0;
|
||||
}
|
||||
|
||||
// If the number of bytes that can be processed after aligning is
|
||||
// less than the number of bytes we can put into a vector,
|
||||
// then there's no work to do with SIMD, just call the manual version.
|
||||
if (alignmentBytes > length || (length - alignmentBytes) < sizeof(uint8x32_t)) {
|
||||
SRMaskBytesManual(bytes, length, maskKey);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t vectorLength = (length - alignmentBytes) / sizeof(uint8x32_t);
|
||||
size_t manualStartOffset = alignmentBytes + (vectorLength * sizeof(uint8x32_t));
|
||||
size_t manualLength = length - manualStartOffset;
|
||||
|
||||
uint8x32_t *vector = (uint8x32_t *)(bytes + alignmentBytes);
|
||||
uint8x32_t maskVector = { };
|
||||
|
||||
memset_pattern4(&maskVector, maskKey, sizeof(uint8x32_t));
|
||||
maskVector = SRShiftVector(maskVector, alignmentBytes);
|
||||
|
||||
SRMaskBytesManual(bytes, alignmentBytes, maskKey);
|
||||
|
||||
for (size_t vectorIndex = 0; vectorIndex < vectorLength; vectorIndex++) {
|
||||
vector[vectorIndex] = vector[vectorIndex] ^ maskVector;
|
||||
}
|
||||
|
||||
// Use the shifted mask for the final manual part.
|
||||
SRMaskBytesManual(bytes + manualStartOffset, manualLength, (uint8_t *) &maskVector);
|
||||
}
|
||||
26
SocketRocket/Internal/Utilities/SRURLUtilities.h
Normal file
26
SocketRocket/Internal/Utilities/SRURLUtilities.h
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// The origin isn't really applicable for a native application.
|
||||
// So instead, just map ws -> http and wss -> https.
|
||||
extern NSString *SRURLOrigin(NSURL *url);
|
||||
|
||||
extern BOOL SRURLRequiresSSL(NSURL *url);
|
||||
|
||||
// Extracts `user` and `password` from url (if available) into `Basic base64(user:password)`.
|
||||
extern NSString *_Nullable SRBasicAuthorizationHeaderFromURL(NSURL *url);
|
||||
|
||||
// Returns a valid value for `NSStreamNetworkServiceType` or `nil`.
|
||||
extern NSString *_Nullable SRStreamNetworkServiceTypeFromURLRequest(NSURLRequest *request);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
77
SocketRocket/Internal/Utilities/SRURLUtilities.m
Normal file
77
SocketRocket/Internal/Utilities/SRURLUtilities.m
Normal file
@ -0,0 +1,77 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRURLUtilities.h"
|
||||
|
||||
#import "SRHash.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *SRURLOrigin(NSURL *url)
|
||||
{
|
||||
NSMutableString *origin = [NSMutableString string];
|
||||
|
||||
NSString *scheme = url.scheme.lowercaseString;
|
||||
if ([scheme isEqualToString:@"wss"]) {
|
||||
scheme = @"https";
|
||||
} else if ([scheme isEqualToString:@"ws"]) {
|
||||
scheme = @"http";
|
||||
}
|
||||
[origin appendFormat:@"%@://%@", scheme, url.host];
|
||||
|
||||
NSNumber *port = url.port;
|
||||
BOOL portIsDefault = (!port ||
|
||||
([scheme isEqualToString:@"http"] && port.integerValue == 80) ||
|
||||
([scheme isEqualToString:@"https"] && port.integerValue == 443));
|
||||
if (!portIsDefault) {
|
||||
[origin appendFormat:@":%@", port.stringValue];
|
||||
}
|
||||
return origin;
|
||||
}
|
||||
|
||||
extern BOOL SRURLRequiresSSL(NSURL *url)
|
||||
{
|
||||
NSString *scheme = url.scheme.lowercaseString;
|
||||
return ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]);
|
||||
}
|
||||
|
||||
extern NSString *_Nullable SRBasicAuthorizationHeaderFromURL(NSURL *url)
|
||||
{
|
||||
NSData *data = [[NSString stringWithFormat:@"%@:%@", url.user, url.password] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [NSString stringWithFormat:@"Basic %@", SRBase64EncodedStringFromData(data)];
|
||||
}
|
||||
|
||||
extern NSString *_Nullable SRStreamNetworkServiceTypeFromURLRequest(NSURLRequest *request)
|
||||
{
|
||||
NSString *networkServiceType = nil;
|
||||
switch (request.networkServiceType) {
|
||||
case NSURLNetworkServiceTypeDefault:
|
||||
break;
|
||||
case NSURLNetworkServiceTypeVoIP:
|
||||
networkServiceType = NSStreamNetworkServiceTypeVoIP;
|
||||
break;
|
||||
case NSURLNetworkServiceTypeVideo:
|
||||
networkServiceType = NSStreamNetworkServiceTypeVideo;
|
||||
break;
|
||||
case NSURLNetworkServiceTypeBackground:
|
||||
networkServiceType = NSStreamNetworkServiceTypeBackground;
|
||||
break;
|
||||
case NSURLNetworkServiceTypeVoice:
|
||||
networkServiceType = NSStreamNetworkServiceTypeVoice;
|
||||
break;
|
||||
#if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 || __TV_OS_VERSION_MAX_ALLOWED >= 100000 || __WATCH_OS_VERSION_MAX_ALLOWED >= 30000)
|
||||
case NSURLNetworkServiceTypeCallSignaling:
|
||||
networkServiceType = NSStreamNetworkServiceTypeCallSignaling;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return networkServiceType;
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
27
SocketRocket/NSRunLoop+SRWebSocket.h
Normal file
27
SocketRocket/NSRunLoop+SRWebSocket.h
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSRunLoop (SRWebSocket)
|
||||
|
||||
/**
|
||||
Default run loop that will be used to schedule all instances of `SRWebSocket`.
|
||||
|
||||
@return An instance of `NSRunLoop`.
|
||||
*/
|
||||
+ (NSRunLoop *)SR_networkRunLoop;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
27
SocketRocket/NSRunLoop+SRWebSocket.m
Normal file
27
SocketRocket/NSRunLoop+SRWebSocket.m
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "NSRunLoop+SRWebSocket.h"
|
||||
#import "NSRunLoop+SRWebSocketPrivate.h"
|
||||
|
||||
#import "SRRunLoopThread.h"
|
||||
|
||||
// Required for object file to always be linked.
|
||||
void import_NSRunLoop_SRWebSocket() { }
|
||||
|
||||
@implementation NSRunLoop (SRWebSocket)
|
||||
|
||||
+ (NSRunLoop *)SR_networkRunLoop
|
||||
{
|
||||
return [SRRunLoopThread sharedThread].runLoop;
|
||||
}
|
||||
|
||||
@end
|
||||
38
SocketRocket/NSURLRequest+SRWebSocket.h
Normal file
38
SocketRocket/NSURLRequest+SRWebSocket.h
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSURLRequest (SRWebSocket)
|
||||
|
||||
/**
|
||||
An array of pinned `SecCertificateRef` SSL certificates that `SRWebSocket` will use for validation.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy, readonly) NSArray *SR_SSLPinnedCertificates
|
||||
DEPRECATED_MSG_ATTRIBUTE("Using pinned certificates is neither secure nor supported in SocketRocket, "
|
||||
"and leads to security issues. Please use a proper, trust chain validated certificate.");
|
||||
|
||||
@end
|
||||
|
||||
@interface NSMutableURLRequest (SRWebSocket)
|
||||
|
||||
/**
|
||||
An array of pinned `SecCertificateRef` SSL certificates that `SRWebSocket` will use for validation.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSArray *SR_SSLPinnedCertificates
|
||||
DEPRECATED_MSG_ATTRIBUTE("Using pinned certificates is neither secure nor supported in SocketRocket, "
|
||||
"and leads to security issues. Please use a proper, trust chain validated certificate.");
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
42
SocketRocket/NSURLRequest+SRWebSocket.m
Normal file
42
SocketRocket/NSURLRequest+SRWebSocket.m
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "NSURLRequest+SRWebSocket.h"
|
||||
#import "NSURLRequest+SRWebSocketPrivate.h"
|
||||
|
||||
// Required for object file to always be linked.
|
||||
void import_NSURLRequest_SRWebSocket() { }
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString *const SRSSLPinnnedCertificatesKey = @"SocketRocket_SSLPinnedCertificates";
|
||||
|
||||
@implementation NSURLRequest (SRWebSocket)
|
||||
|
||||
- (nullable NSArray *)SR_SSLPinnedCertificates
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSMutableURLRequest (SRWebSocket)
|
||||
|
||||
- (void)setSR_SSLPinnedCertificates:(nullable NSArray *)SR_SSLPinnedCertificates
|
||||
{
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Using pinned certificates is neither secure nor supported in SocketRocket, "
|
||||
"and leads to security issues. Please use a proper, trust chain validated certificate."];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
72
SocketRocket/SRSecurityPolicy.h
Normal file
72
SocketRocket/SRSecurityPolicy.h
Normal file
@ -0,0 +1,72 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/Security.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SRSecurityPolicy : NSObject
|
||||
|
||||
/**
|
||||
A default `SRSecurityPolicy` implementation specifies socket security and
|
||||
validates the certificate chain.
|
||||
|
||||
Use a subclass of `SRSecurityPolicy` for more fine grained customization.
|
||||
*/
|
||||
+ (instancetype)defaultPolicy;
|
||||
|
||||
/**
|
||||
Specifies socket security and provider certificate pinning, disregarding certificate
|
||||
chain validation.
|
||||
|
||||
@param pinnedCertificates Array of `SecCertificateRef` SSL certificates to use for validation.
|
||||
*/
|
||||
+ (instancetype)pinnningPolicyWithCertificates:(NSArray *)pinnedCertificates
|
||||
DEPRECATED_MSG_ATTRIBUTE("Using pinned certificates is neither secure nor supported in SocketRocket, "
|
||||
"and leads to security issues. Please use a proper, trust chain validated certificate.");
|
||||
|
||||
/**
|
||||
Specifies socket security and optional certificate chain validation.
|
||||
|
||||
@param enabled Whether or not to validate the SSL certificate chain. If you
|
||||
are considering using this method because your certificate was not issued by a
|
||||
recognized certificate authority, consider using `pinningPolicyWithCertificates` instead.
|
||||
*/
|
||||
- (instancetype)initWithCertificateChainValidationEnabled:(BOOL)enabled
|
||||
DEPRECATED_MSG_ATTRIBUTE("Disabling certificate chain validation is unsafe. "
|
||||
"Please use a proper Certificate Authority to issue your TLS certificates.")
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Updates all the security options for input and output streams, for example you
|
||||
can set your socket security level here.
|
||||
|
||||
@param stream Stream to update the options in.
|
||||
*/
|
||||
- (void)updateSecurityOptionsInStream:(NSStream *)stream;
|
||||
|
||||
/**
|
||||
Whether or not the specified server trust should be accepted, based on the security policy.
|
||||
|
||||
This method should be used when responding to an authentication challenge from
|
||||
a server. In the default implemenation, no further validation is done here, but
|
||||
you're free to override it in a subclass. See `SRPinningSecurityPolicy.h` for
|
||||
an example.
|
||||
|
||||
@param serverTrust The X.509 certificate trust of the server.
|
||||
@param domain The domain of serverTrust.
|
||||
|
||||
@return Whether or not to trust the server.
|
||||
*/
|
||||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
75
SocketRocket/SRSecurityPolicy.m
Normal file
75
SocketRocket/SRSecurityPolicy.m
Normal file
@ -0,0 +1,75 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "SRSecurityPolicy.h"
|
||||
#import "SRPinningSecurityPolicy.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SRSecurityPolicy ()
|
||||
|
||||
@property (nonatomic, assign, readonly) BOOL certificateChainValidationEnabled;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SRSecurityPolicy
|
||||
|
||||
+ (instancetype)defaultPolicy
|
||||
{
|
||||
return [self new];
|
||||
}
|
||||
|
||||
+ (instancetype)pinnningPolicyWithCertificates:(NSArray *)pinnedCertificates
|
||||
{
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Using pinned certificates is neither secure nor supported in SocketRocket, "
|
||||
"and leads to security issues. Please use a proper, trust chain validated certificate."];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCertificateChainValidationEnabled:(BOOL)enabled
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) { return self; }
|
||||
|
||||
_certificateChainValidationEnabled = enabled;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
|
||||
return [self initWithCertificateChainValidationEnabled:YES];
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
- (void)updateSecurityOptionsInStream:(NSStream *)stream
|
||||
{
|
||||
// Enforce TLS 1.2
|
||||
[stream setProperty:(__bridge id)CFSTR("kCFStreamSocketSecurityLevelTLSv1_2") forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel];
|
||||
|
||||
// Validate certificate chain for this stream if enabled.
|
||||
NSDictionary<NSString *, id> *sslOptions = @{ (__bridge NSString *)kCFStreamSSLValidatesCertificateChain : @(self.certificateChainValidationEnabled) };
|
||||
[stream setProperty:sslOptions forKey:(__bridge NSString *)kCFStreamPropertySSLSettings];
|
||||
}
|
||||
|
||||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain
|
||||
{
|
||||
// No further evaluation happens in the default policy.
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,21 +1,17 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// All rights reserved.
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/SecCertificate.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, SRReadyState) {
|
||||
SR_CONNECTING = 0,
|
||||
@ -24,109 +20,398 @@ typedef NS_ENUM(NSInteger, SRReadyState) {
|
||||
SR_CLOSED = 3,
|
||||
};
|
||||
|
||||
typedef enum SRStatusCode : NSInteger {
|
||||
typedef NS_ENUM(NSInteger, SRStatusCode) {
|
||||
// 0-999: Reserved and not used.
|
||||
SRStatusCodeNormal = 1000,
|
||||
SRStatusCodeGoingAway = 1001,
|
||||
SRStatusCodeProtocolError = 1002,
|
||||
SRStatusCodeUnhandledType = 1003,
|
||||
// 1004 reserved.
|
||||
SRStatusNoStatusReceived = 1005,
|
||||
// 1004-1006 reserved.
|
||||
SRStatusCodeAbnormal = 1006,
|
||||
SRStatusCodeInvalidUTF8 = 1007,
|
||||
SRStatusCodePolicyViolated = 1008,
|
||||
SRStatusCodeMessageTooBig = 1009,
|
||||
} SRStatusCode;
|
||||
SRStatusCodeMissingExtension = 1010,
|
||||
SRStatusCodeInternalError = 1011,
|
||||
SRStatusCodeServiceRestart = 1012,
|
||||
SRStatusCodeTryAgainLater = 1013,
|
||||
// 1014: Reserved for future use by the WebSocket standard.
|
||||
SRStatusCodeTLSHandshake = 1015,
|
||||
// 1016-1999: Reserved for future use by the WebSocket standard.
|
||||
// 2000-2999: Reserved for use by WebSocket extensions.
|
||||
// 3000-3999: Available for use by libraries and frameworks. May not be used by applications. Available for registration at the IANA via first-come, first-serve.
|
||||
// 4000-4999: Available for use by applications.
|
||||
};
|
||||
|
||||
@class SRWebSocket;
|
||||
@class SRSecurityPolicy;
|
||||
|
||||
/**
|
||||
Error domain used for errors reported by SRWebSocket.
|
||||
*/
|
||||
extern NSString *const SRWebSocketErrorDomain;
|
||||
extern NSString *const SRHTTPResponseErrorKey;
|
||||
|
||||
#pragma mark - SRWebSocketDelegate
|
||||
/**
|
||||
Key used for HTTP status code if bad response was received from the server.
|
||||
*/
|
||||
extern NSString *const SRHTTPResponseErrorKey;
|
||||
|
||||
@protocol SRWebSocketDelegate;
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - SRWebSocket
|
||||
///--------------------------------------
|
||||
|
||||
/**
|
||||
A `SRWebSocket` object lets you connect, send and receive data to a remote Web Socket.
|
||||
*/
|
||||
@interface SRWebSocket : NSObject <NSStreamDelegate>
|
||||
|
||||
/**
|
||||
The delegate of the web socket.
|
||||
|
||||
The web socket delegate is notified on all state changes that happen to the web socket.
|
||||
*/
|
||||
@property (nonatomic, weak) id <SRWebSocketDelegate> delegate;
|
||||
|
||||
@property (nonatomic, readonly) SRReadyState readyState;
|
||||
@property (nonatomic, readonly, retain) NSURL *url;
|
||||
/**
|
||||
A dispatch queue for scheduling the delegate calls. The queue doesn't need be a serial queue.
|
||||
|
||||
// This returns the negotiated protocol.
|
||||
// It will be nil until after the handshake completes.
|
||||
@property (nonatomic, readonly, copy) NSString *protocol;
|
||||
If `nil` and `delegateOperationQueue` is `nil`, the socket uses main queue for performing all delegate method calls.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong) dispatch_queue_t delegateDispatchQueue;
|
||||
|
||||
// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol.
|
||||
- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols;
|
||||
- (id)initWithURLRequest:(NSURLRequest *)request;
|
||||
/**
|
||||
An operation queue for scheduling the delegate calls.
|
||||
|
||||
// Some helper constructors.
|
||||
- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols;
|
||||
- (id)initWithURL:(NSURL *)url;
|
||||
If `nil` and `delegateOperationQueue` is `nil`, the socket uses main queue for performing all delegate method calls.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong) NSOperationQueue *delegateOperationQueue;
|
||||
|
||||
// Delegate queue will be dispatch_main_queue by default.
|
||||
// You cannot set both OperationQueue and dispatch_queue.
|
||||
- (void)setDelegateOperationQueue:(NSOperationQueue*) queue;
|
||||
- (void)setDelegateDispatchQueue:(dispatch_queue_t) queue;
|
||||
/**
|
||||
Current ready state of the socket. Default: `SR_CONNECTING`.
|
||||
|
||||
// By default, it will schedule itself on +[NSRunLoop SR_networkRunLoop] using defaultModes.
|
||||
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
|
||||
- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
|
||||
This property is Key-Value Observable and fully thread-safe.
|
||||
*/
|
||||
@property (atomic, assign, readonly) SRReadyState readyState;
|
||||
|
||||
// SRWebSockets are intended for one-time-use only. Open should be called once and only once.
|
||||
/**
|
||||
An instance of `NSURL` that this socket connects to.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong, readonly) NSURL *url;
|
||||
|
||||
/**
|
||||
All HTTP headers that were received by socket or `nil` if none were received so far.
|
||||
*/
|
||||
@property (nullable, nonatomic, assign, readonly) CFHTTPMessageRef receivedHTTPHeaders;
|
||||
|
||||
/**
|
||||
Array of `NSHTTPCookie` cookies to apply to the connection.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSArray<NSHTTPCookie *> *requestCookies;
|
||||
|
||||
/**
|
||||
The negotiated web socket protocol or `nil` if handshake did not yet complete.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy, readonly) NSString *protocol;
|
||||
|
||||
/**
|
||||
A boolean value indicating whether this socket will allow connection without SSL trust chain evaluation.
|
||||
For DEBUG builds this flag is ignored, and SSL connections are allowed regardless of the certificate trust configuration
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL allowsUntrustedSSLCertificates;
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Constructors
|
||||
///--------------------------------------
|
||||
|
||||
/**
|
||||
Initializes a web socket with a given `NSURLRequest`.
|
||||
|
||||
@param request Request to initialize with.
|
||||
*/
|
||||
- (instancetype)initWithURLRequest:(NSURLRequest *)request;
|
||||
|
||||
/**
|
||||
Initializes a web socket with a given `NSURLRequest`, specifying a transport security policy (e.g. SSL configuration).
|
||||
|
||||
@param request Request to initialize with.
|
||||
@param securityPolicy Policy object describing transport security behavior.
|
||||
*/
|
||||
- (instancetype)initWithURLRequest:(NSURLRequest *)request securityPolicy:(SRSecurityPolicy *)securityPolicy;
|
||||
|
||||
/**
|
||||
Initializes a web socket with a given `NSURLRequest` and list of sub-protocols.
|
||||
|
||||
@param request Request to initialize with.
|
||||
@param protocols An array of strings that turn into `Sec-WebSocket-Protocol`. Default: `nil`.
|
||||
*/
|
||||
- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(nullable NSArray<NSString *> *)protocols;
|
||||
|
||||
/**
|
||||
Initializes a web socket with a given `NSURLRequest`, list of sub-protocols and whether untrusted SSL certificates are allowed.
|
||||
|
||||
@param request Request to initialize with.
|
||||
@param protocols An array of strings that turn into `Sec-WebSocket-Protocol`. Default: `nil`.
|
||||
@param allowsUntrustedSSLCertificates Boolean value indicating whether untrusted SSL certificates are allowed. Default: `false`.
|
||||
*/
|
||||
- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(nullable NSArray<NSString *> *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates
|
||||
DEPRECATED_MSG_ATTRIBUTE("Disabling certificate chain validation is unsafe. "
|
||||
"Please use a proper Certificate Authority to issue your TLS certificates.");
|
||||
|
||||
/**
|
||||
Initializes a web socket with a given `NSURLRequest`, list of sub-protocols and whether untrusted SSL certificates are allowed.
|
||||
|
||||
@param request Request to initialize with.
|
||||
@param protocols An array of strings that turn into `Sec-WebSocket-Protocol`. Default: `nil`.
|
||||
@param securityPolicy Policy object describing transport security behavior.
|
||||
*/
|
||||
- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(nullable NSArray<NSString *> *)protocols securityPolicy:(SRSecurityPolicy *)securityPolicy NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Initializes a web socket with a given `NSURL`.
|
||||
|
||||
@param url URL to initialize with.
|
||||
*/
|
||||
- (instancetype)initWithURL:(NSURL *)url;
|
||||
|
||||
/**
|
||||
Initializes a web socket with a given `NSURL` and list of sub-protocols.
|
||||
|
||||
@param url URL to initialize with.
|
||||
@param protocols An array of strings that turn into `Sec-WebSocket-Protocol`. Default: `nil`.
|
||||
*/
|
||||
- (instancetype)initWithURL:(NSURL *)url protocols:(nullable NSArray<NSString *> *)protocols;
|
||||
|
||||
/**
|
||||
Initializes a web socket with a given `NSURL`, specifying a transport security policy (e.g. SSL configuration).
|
||||
|
||||
@param url URL to initialize with.
|
||||
@param securityPolicy Policy object describing transport security behavior.
|
||||
*/
|
||||
- (instancetype)initWithURL:(NSURL *)url securityPolicy:(SRSecurityPolicy *)securityPolicy;
|
||||
|
||||
/**
|
||||
Initializes a web socket with a given `NSURL`, list of sub-protocols and whether untrusted SSL certificates are allowed.
|
||||
|
||||
@param url URL to initialize with.
|
||||
@param protocols An array of strings that turn into `Sec-WebSocket-Protocol`. Default: `nil`.
|
||||
@param allowsUntrustedSSLCertificates Boolean value indicating whether untrusted SSL certificates are allowed. Default: `false`.
|
||||
*/
|
||||
- (instancetype)initWithURL:(NSURL *)url protocols:(nullable NSArray<NSString *> *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates
|
||||
DEPRECATED_MSG_ATTRIBUTE("Disabling certificate chain validation is unsafe. "
|
||||
"Please use a proper Certificate Authority to issue your TLS certificates.");
|
||||
|
||||
/**
|
||||
Unavailable initializer. Please use any other initializer.
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
Unavailable constructor. Please use any other initializer.
|
||||
*/
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Schedule
|
||||
///--------------------------------------
|
||||
|
||||
/**
|
||||
Schedules a received on a given run loop in a given mode.
|
||||
By default, a web socket will schedule itself on `+[NSRunLoop SR_networkRunLoop]` using `NSDefaultRunLoopMode`.
|
||||
|
||||
@param runLoop The run loop on which to schedule the receiver.
|
||||
@param mode The mode for the run loop.
|
||||
*/
|
||||
- (void)scheduleInRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode NS_SWIFT_NAME(schedule(in:forMode:));
|
||||
|
||||
/**
|
||||
Removes the receiver from a given run loop running in a given mode.
|
||||
|
||||
@param runLoop The run loop on which the receiver was scheduled.
|
||||
@param mode The mode for the run loop.
|
||||
*/
|
||||
- (void)unscheduleFromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode NS_SWIFT_NAME(unschedule(from:forMode:));
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Open / Close
|
||||
///--------------------------------------
|
||||
|
||||
/**
|
||||
Opens web socket, which will trigger connection, authentication and start receiving/sending events.
|
||||
An instance of `SRWebSocket` is intended for one-time-use only. This method should be called once and only once.
|
||||
*/
|
||||
- (void)open;
|
||||
|
||||
/**
|
||||
Closes a web socket using `SRStatusCodeNormal` code and no reason.
|
||||
*/
|
||||
- (void)close;
|
||||
- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
|
||||
|
||||
// Send a UTF8 String or Data.
|
||||
- (void)send:(id)data;
|
||||
/**
|
||||
Closes a web socket using a given code and reason.
|
||||
|
||||
// Send Data (can be nil) in a ping message.
|
||||
- (void)sendPing:(NSData *)data;
|
||||
@param code Code to close the socket with.
|
||||
@param reason Reason to send to the server or `nil`.
|
||||
*/
|
||||
- (void)closeWithCode:(NSInteger)code reason:(nullable NSString *)reason;
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark Send
|
||||
///--------------------------------------
|
||||
|
||||
/**
|
||||
Send a UTF-8 string or binary data to the server.
|
||||
|
||||
@param message UTF-8 String or Data to send.
|
||||
|
||||
@deprecated Please use `sendString:` or `sendData` instead.
|
||||
*/
|
||||
- (void)send:(nullable id)message __attribute__((deprecated("Please use `sendString:error:` or `sendData:error:` instead.")));
|
||||
|
||||
/**
|
||||
Send a UTF-8 String to the server.
|
||||
|
||||
@param string String to send.
|
||||
@param error On input, a pointer to variable for an `NSError` object.
|
||||
If an error occurs, this pointer is set to an `NSError` object containing information about the error.
|
||||
You may specify `nil` to ignore the error information.
|
||||
|
||||
@return `YES` if the string was scheduled to send, otherwise - `NO`.
|
||||
*/
|
||||
- (BOOL)sendString:(NSString *)string error:(NSError **)error NS_SWIFT_NAME(send(string:));
|
||||
|
||||
/**
|
||||
Send binary data to the server.
|
||||
|
||||
@param data Data to send.
|
||||
@param error On input, a pointer to variable for an `NSError` object.
|
||||
If an error occurs, this pointer is set to an `NSError` object containing information about the error.
|
||||
You may specify `nil` to ignore the error information.
|
||||
|
||||
@return `YES` if the string was scheduled to send, otherwise - `NO`.
|
||||
*/
|
||||
- (BOOL)sendData:(nullable NSData *)data error:(NSError **)error NS_SWIFT_NAME(send(data:));
|
||||
|
||||
/**
|
||||
Send binary data to the server, without making a defensive copy of it first.
|
||||
|
||||
@param data Data to send.
|
||||
@param error On input, a pointer to variable for an `NSError` object.
|
||||
If an error occurs, this pointer is set to an `NSError` object containing information about the error.
|
||||
You may specify `nil` to ignore the error information.
|
||||
|
||||
@return `YES` if the string was scheduled to send, otherwise - `NO`.
|
||||
*/
|
||||
- (BOOL)sendDataNoCopy:(nullable NSData *)data error:(NSError **)error NS_SWIFT_NAME(send(dataNoCopy:));
|
||||
|
||||
/**
|
||||
Send Ping message to the server with optional data.
|
||||
|
||||
@param data Instance of `NSData` or `nil`.
|
||||
@param error On input, a pointer to variable for an `NSError` object.
|
||||
If an error occurs, this pointer is set to an `NSError` object containing information about the error.
|
||||
You may specify `nil` to ignore the error information.
|
||||
|
||||
@return `YES` if the string was scheduled to send, otherwise - `NO`.
|
||||
*/
|
||||
- (BOOL)sendPing:(nullable NSData *)data error:(NSError **)error NS_SWIFT_NAME(sendPing(_:));
|
||||
|
||||
@end
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - SRWebSocketDelegate
|
||||
///--------------------------------------
|
||||
|
||||
/**
|
||||
The `SRWebSocketDelegate` protocol describes the methods that `SRWebSocket` objects
|
||||
call on their delegates to handle status and messsage events.
|
||||
*/
|
||||
@protocol SRWebSocketDelegate <NSObject>
|
||||
|
||||
// message will either be an NSString if the server is using text
|
||||
// or NSData if the server is using binary.
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
|
||||
|
||||
@optional
|
||||
|
||||
#pragma mark Receive Messages
|
||||
|
||||
/**
|
||||
Called when any message was received from a web socket.
|
||||
This method is suboptimal and might be deprecated in a future release.
|
||||
|
||||
@param webSocket An instance of `SRWebSocket` that received a message.
|
||||
@param message Received message. Either a `String` or `NSData`.
|
||||
*/
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
|
||||
|
||||
/**
|
||||
Called when a frame was received from a web socket.
|
||||
|
||||
@param webSocket An instance of `SRWebSocket` that received a message.
|
||||
@param string Received text in a form of UTF-8 `String`.
|
||||
*/
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(NSString *)string;
|
||||
|
||||
/**
|
||||
Called when a frame was received from a web socket.
|
||||
|
||||
@param webSocket An instance of `SRWebSocket` that received a message.
|
||||
@param data Received data in a form of `NSData`.
|
||||
*/
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithData:(NSData *)data;
|
||||
|
||||
#pragma mark Status & Connection
|
||||
|
||||
/**
|
||||
Called when a given web socket was open and authenticated.
|
||||
|
||||
@param webSocket An instance of `SRWebSocket` that was open.
|
||||
*/
|
||||
- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
|
||||
|
||||
/**
|
||||
Called when a given web socket encountered an error.
|
||||
|
||||
@param webSocket An instance of `SRWebSocket` that failed with an error.
|
||||
@param error An instance of `NSError`.
|
||||
*/
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload;
|
||||
|
||||
/**
|
||||
Called when a given web socket was closed.
|
||||
|
||||
@param webSocket An instance of `SRWebSocket` that was closed.
|
||||
@param code Code reported by the server.
|
||||
@param reason Reason in a form of a String that was reported by the server or `nil`.
|
||||
@param wasClean Boolean value indicating whether a socket was closed in a clean state.
|
||||
*/
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(nullable NSString *)reason wasClean:(BOOL)wasClean;
|
||||
|
||||
/**
|
||||
Called on receive of a ping message from the server.
|
||||
|
||||
@param webSocket An instance of `SRWebSocket` that received a ping frame.
|
||||
@param data Payload that was received or `nil` if there was no payload.
|
||||
*/
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceivePingWithData:(nullable NSData *)data;
|
||||
|
||||
/**
|
||||
Called when a pong data was received in response to ping.
|
||||
|
||||
@param webSocket An instance of `SRWebSocket` that received a pong frame.
|
||||
@param pongData Payload that was received or `nil` if there was no payload.
|
||||
*/
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(nullable NSData *)pongData;
|
||||
|
||||
/**
|
||||
Sent before reporting a text frame to be able to configure if it shuold be convert to a UTF-8 String or passed as `NSData`.
|
||||
If the method is not implemented - it will always convert text frames to String.
|
||||
|
||||
@param webSocket An instance of `SRWebSocket` that received a text frame.
|
||||
|
||||
@return `YES` if text frame should be converted to UTF-8 String, otherwise - `NO`. Default: `YES`.
|
||||
*/
|
||||
- (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket NS_SWIFT_NAME(webSocketShouldConvertTextFrameToString(_:));
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - NSURLRequest (CertificateAdditions)
|
||||
|
||||
@interface NSURLRequest (CertificateAdditions)
|
||||
|
||||
@property (nonatomic, retain, readonly) NSArray *SR_SSLPinnedCertificates;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - NSMutableURLRequest (CertificateAdditions)
|
||||
|
||||
@interface NSMutableURLRequest (CertificateAdditions)
|
||||
|
||||
@property (nonatomic, retain) NSArray *SR_SSLPinnedCertificates;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - NSRunLoop (SRWebSocket)
|
||||
|
||||
@interface NSRunLoop (SRWebSocket)
|
||||
|
||||
+ (NSRunLoop *)SR_networkRunLoop;
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
1628
SocketRocket/SRWebSocket.m
Normal file
1628
SocketRocket/SRWebSocket.m
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,176 +0,0 @@
|
||||
//
|
||||
// SecureIO.h
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Michael Lewis on 1/12/13.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __SocketRocket__SecureIO__
|
||||
#define __SocketRocket__SecureIO__
|
||||
|
||||
#include "DispatchIO.h"
|
||||
#include "DispatchData.h"
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
struct SSLContext;
|
||||
|
||||
namespace squareup {
|
||||
namespace dispatch {
|
||||
class SecureIO;
|
||||
|
||||
// You are responsible for removing inputStream and outputStream
|
||||
typedef void (^dial_tls_callback)(SecureIO *io, int error, const char *error_message);
|
||||
|
||||
void DialTLS(const char *hostname, const char *servname, SSLContextRef ssl_context, dispatch_queue_t callback_queue, dispatch_queue_t work_queue, dispatch_queue_t parent_io_queue, dial_tls_callback dial_callback, void(^close_handler)(int error) = nullptr);
|
||||
|
||||
|
||||
template <typename FuncType>
|
||||
class QueuedHandle {
|
||||
private:
|
||||
__strong FuncType _handler = nullptr;
|
||||
__sr_maybe_strong__ dispatch_queue_t _queue = nullptr;
|
||||
public:
|
||||
inline QueuedHandle() {
|
||||
}
|
||||
|
||||
inline QueuedHandle(dispatch_queue_t queue, FuncType handler) {
|
||||
_handler = [handler copy];
|
||||
_queue = queue;
|
||||
sr_dispatch_retain(_queue);
|
||||
}
|
||||
|
||||
inline QueuedHandle(const QueuedHandle<FuncType> &other) : QueuedHandle(other._queue, other._handler){
|
||||
}
|
||||
|
||||
|
||||
inline QueuedHandle &operator=(const QueuedHandle &other){
|
||||
_handler = other._handler;
|
||||
_queue = other._queue;
|
||||
sr_dispatch_release(_queue);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ~QueuedHandle() {
|
||||
if (_queue) {
|
||||
sr_dispatch_release(_queue);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool Valid() const {
|
||||
return _handler != nullptr;
|
||||
}
|
||||
|
||||
inline void Invalidate() {
|
||||
_handler = nullptr;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void operator () (Args... args) const {
|
||||
assert(Valid());
|
||||
FuncType handler = _handler;
|
||||
dispatch_async(_queue, [handler, args...]{
|
||||
handler(args...);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
typedef QueuedHandle<dispatch_io_handler_t> DispatchHandler;
|
||||
|
||||
struct WriteJob {
|
||||
bool isLast;
|
||||
size_t rawBytes = 0;
|
||||
size_t cryptedBytes = 0;
|
||||
DispatchHandler handler;
|
||||
};
|
||||
|
||||
struct ReadRequest {
|
||||
// How many rawBytes we're expecting to read.
|
||||
// This is populated in SSLRead.
|
||||
size_t rawBytesRemaining = 0;
|
||||
|
||||
DispatchHandler handler;
|
||||
};
|
||||
|
||||
class SecureIO : public IO {
|
||||
IO *_io;
|
||||
SSLContext *_context;
|
||||
dispatch_queue_t _workQueue;
|
||||
|
||||
std::deque<WriteJob> _writeJobs;
|
||||
std::deque<ReadRequest> _readRequests;
|
||||
|
||||
Data _waitingCryptedData;
|
||||
size_t _cryptedBytesRequested = 0;
|
||||
size_t _rawBytesRequested = 0;
|
||||
|
||||
size_t _highWater = SIZE_MAX;
|
||||
size_t _lowWater = 1024 * 8;
|
||||
|
||||
bool _cancelled = false;
|
||||
bool _closing = false;
|
||||
|
||||
// Set when we're inside of HandleSSLRead
|
||||
// If we're reading and get a call to our SSLWriteHandler, the connection is probably being closed
|
||||
bool _handlingRead = false;
|
||||
|
||||
// presence of this means handshake is in progress
|
||||
DispatchHandler _handshakeHandler;
|
||||
|
||||
// This is set to true when we send a NULL data ptr. This is so we can request data we need.
|
||||
bool _calculatingRequestSize = false;
|
||||
|
||||
std::vector<uint8_t> _sslReadBuffer;
|
||||
|
||||
public:
|
||||
// Takes ownership of IO and delete it when done
|
||||
SecureIO(IO *io, SSLContextRef context, dispatch_queue_t workQueue);
|
||||
~SecureIO();
|
||||
|
||||
// This performs the handshake.
|
||||
// There will be no data sent back to handler, but it will be called on completion
|
||||
void Handshake(dispatch_queue_t queue, dispatch_io_handler_t handler);
|
||||
|
||||
void Close(dispatch_io_close_flags_t flags);
|
||||
void Read(size_t length, dispatch_queue_t queue, dispatch_io_handler_t handler);
|
||||
void Write(dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t handler);
|
||||
void Barrier(dispatch_block_t barrier);
|
||||
|
||||
|
||||
inline void SetHighWater(size_t high_water) {
|
||||
_highWater = high_water;
|
||||
}
|
||||
|
||||
void SetLowWater(size_t low_water) {
|
||||
_lowWater = low_water;
|
||||
}
|
||||
|
||||
private:
|
||||
// Requests will ask the underlying stream for lenght - _rawBytesRequested
|
||||
void RequestBytes(size_t length);
|
||||
|
||||
void Cancel(dispatch_io_close_flags_t flags, int error);
|
||||
|
||||
void CheckHandshake();
|
||||
void InnerWrite(dispatch_data_t data, const DispatchHandler &handler);
|
||||
|
||||
// These really should be private
|
||||
public:
|
||||
OSStatus SSLReadHandler(void *data, size_t *dataLength);
|
||||
OSStatus SSLWriteHandler(const void *data, size_t *dataLength);
|
||||
|
||||
|
||||
private:
|
||||
void HandleSSLWrite(bool done, size_t requestedLength, int error);
|
||||
void HandleSSLRead(bool done, dispatch_data_t data, int error);
|
||||
|
||||
void PumpSSLRead();
|
||||
void DoSSLRead();
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(__SocketRocket__SecureIO__) */
|
||||
@ -1,475 +0,0 @@
|
||||
//
|
||||
// SecureIO.cpp
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Michael Lewis on 1/12/13.
|
||||
//
|
||||
//
|
||||
|
||||
#include <Security/SecureTransport.h>
|
||||
|
||||
#include "SecureIO.h"
|
||||
#include "DispatchData.h"
|
||||
|
||||
#define ALLOW_INSECURE_SSL 1
|
||||
|
||||
namespace squareup {
|
||||
namespace dispatch {
|
||||
static OSStatus readFunc(SSLConnectionRef connection, void *data, size_t *dataLength);
|
||||
static OSStatus writeFunc(SSLConnectionRef connection, const void *data, size_t *dataLength);
|
||||
|
||||
void DialTLS(const char *hostname,
|
||||
const char *servname,
|
||||
SSLContextRef ssl_context,
|
||||
dispatch_queue_t callback_queue,
|
||||
dispatch_queue_t work_queue,
|
||||
dispatch_queue_t parent_io_queue,
|
||||
dial_tls_callback dial_callback,
|
||||
void(^close_handler)(int error)) {
|
||||
|
||||
if (close_handler != nullptr) {
|
||||
close_handler = [close_handler copy];
|
||||
}
|
||||
|
||||
dial_callback = [dial_callback copy];
|
||||
sr_dispatch_retain(callback_queue);
|
||||
|
||||
// The work queue is the outer ones callback_queue
|
||||
SimpleDial(hostname, servname, work_queue, parent_io_queue, [=](squareup::dispatch::RawIO *io, int error, const char *error_message) {
|
||||
if (error != 0 || io == nullptr) {
|
||||
dispatch_async(callback_queue, ^{
|
||||
dial_callback(nullptr, error, error_message);
|
||||
});
|
||||
sr_dispatch_release(callback_queue);
|
||||
return;
|
||||
}
|
||||
|
||||
SecureIO *newIO = new SecureIO(io, ssl_context, work_queue);
|
||||
|
||||
sr_dispatch_release(callback_queue);
|
||||
|
||||
newIO->Handshake(callback_queue, [newIO, dial_callback](bool done, dispatch_data_t data, int error) {
|
||||
SecureIO *io = newIO;
|
||||
if (error) {
|
||||
delete io;
|
||||
io = nullptr;
|
||||
}
|
||||
|
||||
// TODO: maybe add message?
|
||||
dial_callback(io, error, nullptr);
|
||||
});
|
||||
|
||||
}, close_handler);
|
||||
}
|
||||
|
||||
|
||||
SecureIO::SecureIO(IO *io, SSLContextRef context, dispatch_queue_t workQueue) :
|
||||
_io(io), _context(context), _workQueue(workQueue) {
|
||||
sr_dispatch_retain(_workQueue);
|
||||
CFRetain(_context);
|
||||
|
||||
SSLSetConnection(_context, reinterpret_cast<const void *>(this));
|
||||
SSLSetIOFuncs(_context, readFunc, writeFunc);
|
||||
|
||||
// TODO: delegate certificate authentication
|
||||
#if ALLOW_INSECURE_SSL
|
||||
SSLSetSessionOption(_context, kSSLSessionOptionBreakOnServerAuth, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
SecureIO::~SecureIO() {
|
||||
sr_dispatch_release(_workQueue);
|
||||
CFRelease(_context);
|
||||
|
||||
// make sure things closed before we delete the io
|
||||
assert(!_io);
|
||||
}
|
||||
|
||||
void SecureIO::Handshake(dispatch_queue_t queue, dispatch_io_handler_t handler) {
|
||||
_handshakeHandler = DispatchHandler(queue, handler);
|
||||
dispatch_async(_workQueue, [this]{
|
||||
CheckHandshake();
|
||||
});
|
||||
}
|
||||
|
||||
void SecureIO::Close(dispatch_io_close_flags_t flags) {
|
||||
assert(!_closing);
|
||||
dispatch_async(_workQueue, [this, flags]{
|
||||
assert(!_closing);
|
||||
_closing = true;
|
||||
OSStatus status = SSLClose(_context);
|
||||
|
||||
// This shouldn't be a blocking operation
|
||||
assert(status != errSSLWouldBlock);
|
||||
|
||||
Cancel(flags, status);
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
void SecureIO::Read(size_t length, dispatch_queue_t queue, dispatch_io_handler_t handler) {
|
||||
DispatchHandler dispatchHandler(queue, [handler copy]);
|
||||
|
||||
dispatch_async(_workQueue, [length, dispatchHandler, this]{
|
||||
if (_cancelled) {
|
||||
dispatchHandler(true, (dispatch_data_t)nullptr, ECANCELED);
|
||||
return;
|
||||
}
|
||||
|
||||
ReadRequest readRequest;
|
||||
readRequest.handler = dispatchHandler;
|
||||
readRequest.rawBytesRemaining += length;
|
||||
|
||||
|
||||
_readRequests.push_back(readRequest);
|
||||
// TODO: run on queue
|
||||
_rawBytesRequested += length;
|
||||
PumpSSLRead();
|
||||
});
|
||||
}
|
||||
|
||||
void SecureIO::PumpSSLRead() {
|
||||
|
||||
size_t buffSize = SIZE_MAX;
|
||||
|
||||
// This can happen if we have leftover data and haven't requested more.
|
||||
while (buffSize > 0) {
|
||||
// Make sure we are requesting enough;
|
||||
OSStatus status = SSLGetBufferedReadSize(_context, &buffSize);
|
||||
assert(status == 0);
|
||||
if (buffSize > 0) {
|
||||
if (_readRequests.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DoSSLRead();
|
||||
}
|
||||
}
|
||||
|
||||
_calculatingRequestSize = true;
|
||||
|
||||
const size_t maxBufferSize = 32 * 1024;
|
||||
|
||||
// We don't actually want to read anything into the _sslReadBuffer, howevr there's a bug when we go through 2g of data we have a memory issue
|
||||
|
||||
_sslReadBuffer.resize(std::max(_sslReadBuffer.capacity(), std::min(_rawBytesRequested, maxBufferSize)));
|
||||
|
||||
size_t dummyProcessed;
|
||||
|
||||
assert(buffSize == 0);
|
||||
OSStatus status = ::SSLRead(_context, _sslReadBuffer.data(), _sslReadBuffer.size(), &dummyProcessed);
|
||||
assert(dummyProcessed == 0);
|
||||
_calculatingRequestSize = false;
|
||||
|
||||
if (status == errSSLClosedGraceful) {
|
||||
if (!_closing) {
|
||||
_closing = true;
|
||||
Cancel(0, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(status == errSSLWouldBlock);
|
||||
|
||||
}
|
||||
|
||||
void SecureIO::Write(dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t handler) {
|
||||
sr_dispatch_retain(data);
|
||||
DispatchHandler dispatchHandler(queue, handler);
|
||||
|
||||
Data(data).Apply(^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) {
|
||||
return true;
|
||||
});
|
||||
|
||||
dispatch_async(_workQueue, [data, dispatchHandler, this]{
|
||||
InnerWrite(data, dispatchHandler);
|
||||
sr_dispatch_release(data);
|
||||
});
|
||||
}
|
||||
|
||||
void SecureIO::InnerWrite(dispatch_data_t data, const DispatchHandler &handler) {
|
||||
if (_cancelled) {
|
||||
handler(true, (dispatch_data_t)nullptr, ECANCELED);
|
||||
return;
|
||||
}
|
||||
|
||||
OSStatus result = 0;
|
||||
|
||||
size_t totalSize = dispatch_data_get_size(data);
|
||||
|
||||
dispatch_data_apply(data, [&](dispatch_data_t region, size_t offset, const void *buffer, size_t size) -> bool {
|
||||
WriteJob newJob;
|
||||
newJob.handler = handler;
|
||||
newJob.rawBytes = size;
|
||||
newJob.isLast = offset + size == totalSize;
|
||||
newJob.cryptedBytes = 0;
|
||||
|
||||
_writeJobs.push_back(newJob);
|
||||
|
||||
// WriteSSL will fill in the crypted bytes
|
||||
// isLast will be set to True for the last one
|
||||
size_t sizeWritten = 0;
|
||||
result = ::SSLWrite(_context, buffer, size, &sizeWritten);
|
||||
|
||||
// I think we can make this assumption since we don't block at all in our handler.
|
||||
|
||||
if (result != 0) {
|
||||
return false;
|
||||
};
|
||||
|
||||
// We should be able to make this assumptions since our write func never blocks
|
||||
assert(sizeWritten == size);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (result) {
|
||||
Cancel(0, result);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SecureIO::RequestBytes(size_t bytesWanted) {
|
||||
if (bytesWanted > _cryptedBytesRequested) {
|
||||
size_t requestSize = bytesWanted - _cryptedBytesRequested;
|
||||
|
||||
_cryptedBytesRequested += requestSize;
|
||||
|
||||
_io->Read(requestSize, _workQueue, [this](bool done, dispatch_data_t data, int error) {
|
||||
HandleSSLRead(done, data, error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
OSStatus SecureIO::SSLReadHandler(void *data, size_t *dataLength) {
|
||||
if (_calculatingRequestSize) {
|
||||
RequestBytes(*dataLength);
|
||||
*dataLength = 0;
|
||||
return errSSLWouldBlock;
|
||||
}
|
||||
|
||||
size_t bytesRequested = *dataLength;
|
||||
|
||||
// If _calculatingRequestSize is true, always return errSSLWouldBlock and pretend we don't have any data
|
||||
|
||||
size_t bytesToCopy = std::min(_waitingCryptedData.Size(), bytesRequested);
|
||||
|
||||
*dataLength = bytesToCopy;
|
||||
|
||||
if (bytesToCopy > 0) {
|
||||
// Advance it forward
|
||||
_waitingCryptedData = _waitingCryptedData.TakeInto(bytesToCopy, data);
|
||||
|
||||
_waitingCryptedData.FlattenIfNecessary();
|
||||
}
|
||||
|
||||
if (bytesToCopy < bytesRequested) {
|
||||
RequestBytes(bytesRequested - bytesToCopy);
|
||||
return errSSLWouldBlock;
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
OSStatus SecureIO::SSLWriteHandler(const void *data, size_t *dataLength) {
|
||||
size_t requestedLength = *dataLength;
|
||||
|
||||
if (!_handshakeHandler.Valid() && !_closing && !_handlingRead) {
|
||||
assert(_writeJobs.size() > 0);
|
||||
_writeJobs.back().cryptedBytes += requestedLength;
|
||||
}
|
||||
|
||||
_io->Write(Data(data, requestedLength, _workQueue), _workQueue, [this, requestedLength](bool done, dispatch_data_t data, int error) {
|
||||
HandleSSLWrite(done, requestedLength, error);
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SecureIO::HandleSSLRead(bool done, dispatch_data_t data, int error) {
|
||||
if (!_handshakeHandler.Valid() && _readRequests.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (error != 0) {
|
||||
if (_handshakeHandler.Valid()) {
|
||||
_handshakeHandler(done, (dispatch_data_t)nullptr, error);
|
||||
_handshakeHandler.Invalidate();
|
||||
} else {
|
||||
assert(_readRequests.size() > 0);
|
||||
Cancel(0, error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We got some data, so append it
|
||||
Data d(data);
|
||||
_cryptedBytesRequested -= d.Size();
|
||||
_waitingCryptedData += d;
|
||||
|
||||
if (_handshakeHandler.Valid()) {
|
||||
CheckHandshake();
|
||||
return;
|
||||
}
|
||||
|
||||
DoSSLRead();
|
||||
PumpSSLRead();
|
||||
}
|
||||
|
||||
void SecureIO::DoSSLRead() {
|
||||
assert(_readRequests.size() > 0);
|
||||
|
||||
ReadRequest *frontRead = &_readRequests.front();
|
||||
const DispatchHandler &handler = frontRead->handler;
|
||||
|
||||
size_t length = frontRead->rawBytesRemaining;
|
||||
|
||||
// Let's cap length at 2x the bytes we have available. probably won't use all of it (it will probably be less than 1x)
|
||||
length = std::min(length, 2 * _waitingCryptedData.Size());
|
||||
length = std::min(length, _highWater);
|
||||
|
||||
size_t buffSize = 0;
|
||||
SSLGetBufferedReadSize(_context, &buffSize);
|
||||
length = std::max(buffSize, length);
|
||||
|
||||
size_t sizeRead = 0;
|
||||
|
||||
void *buffer = malloc(length);
|
||||
assert(buffer);
|
||||
|
||||
// TODO: optimize this and not malloc memory each time
|
||||
assert(_calculatingRequestSize == false);
|
||||
|
||||
assert(_handlingRead == false);
|
||||
_handlingRead = true;
|
||||
OSStatus status = ::SSLRead(_context, buffer, length, &sizeRead);
|
||||
_handlingRead = false;
|
||||
|
||||
if (status != 0 && status != errSSLWouldBlock && status != errSSLClosedGraceful) {
|
||||
free(buffer);
|
||||
buffer = nullptr;
|
||||
// TODO: handle error better
|
||||
Cancel(0, status);
|
||||
return;
|
||||
}
|
||||
|
||||
Data rawData(dispatch_data_create(buffer, sizeRead, _workQueue, DISPATCH_DATA_DESTRUCTOR_FREE), false);
|
||||
|
||||
frontRead->rawBytesRemaining -= sizeRead;
|
||||
|
||||
bool isDone = (frontRead->rawBytesRemaining == 0);
|
||||
|
||||
// TODO: honor watermarks
|
||||
handler(isDone, rawData, 0);
|
||||
|
||||
if (status == errSSLClosedGraceful) {
|
||||
// TODO: handle close
|
||||
}
|
||||
|
||||
if (isDone) {
|
||||
_readRequests.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void SecureIO::HandleSSLWrite(bool done, size_t requestedLength, int error) {
|
||||
if (_handshakeHandler.Valid()) {
|
||||
CheckHandshake();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_closing) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(_writeJobs.size() > 0);
|
||||
|
||||
// TODO(lewis): handle rest of errors better
|
||||
if (error != 0) {
|
||||
_writeJobs.front().handler(true, dispatch_data_empty, error);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(_writeJobs.size());
|
||||
|
||||
// We only want to call the handlers when we are "done".
|
||||
// This way we can approximate the bytes that are written
|
||||
if (done) {
|
||||
// if we're an error we want to go to the last one
|
||||
if (error) {
|
||||
Cancel(0, error);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(_writeJobs.size());
|
||||
|
||||
_writeJobs.front().cryptedBytes -= requestedLength;
|
||||
|
||||
WriteJob writeJob = _writeJobs.front();
|
||||
|
||||
// Only call them when we're "done" for now, because we don't want to do bookkeeping of remaining data to consume
|
||||
// TODO: probably change this
|
||||
|
||||
if (writeJob.cryptedBytes == 0) {
|
||||
_writeJobs.pop_front();
|
||||
if (writeJob.isLast) {
|
||||
writeJob.handler(true, dispatch_data_empty, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SecureIO::CheckHandshake() {
|
||||
assert(_handshakeHandler.Valid());
|
||||
OSStatus status = ::SSLHandshake(_context);
|
||||
// If it would block we got nothing to do
|
||||
if (status == errSSLWouldBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if ALLOW_INSECURE_SSL
|
||||
|
||||
// TODO: make this better
|
||||
if (status == errSSLPeerAuthCompleted) {
|
||||
CheckHandshake();
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
_handshakeHandler(true, (dispatch_data_t)nullptr, status);
|
||||
_handshakeHandler.Invalidate();
|
||||
}
|
||||
|
||||
void SecureIO::Cancel(dispatch_io_close_flags_t flags, int error) {
|
||||
_cancelled = true;
|
||||
|
||||
for (const ReadRequest &req : _readRequests) {
|
||||
req.handler(true, (dispatch_data_t)nullptr, ECANCELED);
|
||||
}
|
||||
|
||||
for (const WriteJob &job : _writeJobs) {
|
||||
job.handler(true, (dispatch_data_t)nullptr, ECANCELED);
|
||||
}
|
||||
|
||||
_readRequests.clear();
|
||||
_writeJobs.clear();
|
||||
|
||||
_io->Close(flags);
|
||||
delete _io;
|
||||
_io = nullptr;
|
||||
}
|
||||
|
||||
void SecureIO::Barrier(dispatch_block_t barrier) {
|
||||
_io->Barrier(barrier);
|
||||
}
|
||||
|
||||
OSStatus readFunc(SSLConnectionRef connection, void *data, size_t *dataLength) {
|
||||
return reinterpret_cast<SecureIO *>((void *)connection)->SSLReadHandler(data, dataLength);
|
||||
};
|
||||
|
||||
OSStatus writeFunc(SSLConnectionRef connection, const void *data, size_t *dataLength) {
|
||||
return reinterpret_cast<SecureIO *>((void *)connection)->SSLWriteHandler(data, dataLength);
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,19 +1,15 @@
|
||||
//
|
||||
// SocketRocket.h
|
||||
// SocketRocket
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
//
|
||||
// Created by Mike Lewis on 1/6/16.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
//! Project version number for SocketRocket.
|
||||
FOUNDATION_EXPORT double SocketRocketVersionNumber;
|
||||
|
||||
//! Project version string for SocketRocket.
|
||||
FOUNDATION_EXPORT const unsigned char SocketRocketVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <SocketRocket/PublicHeader.h>
|
||||
|
||||
|
||||
#import <SocketRocket/NSRunLoop+SRWebSocket.h>
|
||||
#import <SocketRocket/NSURLRequest+SRWebSocket.h>
|
||||
#import <SocketRocket/SRSecurityPolicy.h>
|
||||
#import <SocketRocket/SRWebSocket.h>
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
////
|
||||
//// WebSocket.swift
|
||||
//// SocketRocket
|
||||
////
|
||||
//// Created by Mike Lewis on 1/6/16.
|
||||
////
|
||||
////
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
@ -1,299 +0,0 @@
|
||||
//
|
||||
// WebSocketProtocol.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 1/6/16.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import SocketRocketIO
|
||||
|
||||
public protocol MessageOutputStream {
|
||||
|
||||
/// This is the raw way to write messages. This probably shouldn't be used by general implementations (it is unsafe). By using unsafeBufferPointer we can avoid most copying of data
|
||||
func writeMessageRaw(buffers: Observable<UnsafeBufferPointer<UInt8>>)
|
||||
}
|
||||
|
||||
public protocol MessageInputStream {
|
||||
@warn_unused_result
|
||||
func readMessagesRaw(scheduler: ImmediateSchedulerType) -> Observable<Observable<UnsafeBufferPointer<UInt8>>>
|
||||
}
|
||||
|
||||
private let controlFrameRange = 0x8..<0xF
|
||||
|
||||
func == (lhs: WebSocketFrameHeader, rhs: WebSocketFrameHeader) -> Bool {
|
||||
return lhs.fin == rhs.fin
|
||||
&& lhs.opcode == rhs.opcode
|
||||
&& lhs.mask == rhs.mask
|
||||
&& lhs.payloadLen == rhs.payloadLen
|
||||
&& lhs.maskingKey == rhs.maskingKey
|
||||
}
|
||||
|
||||
struct WebSocketFrameHeader : Equatable {
|
||||
let fin: Bool
|
||||
let opcode: Int // We're not using normal opcodes for this incase they're undefined
|
||||
let mask: Bool
|
||||
|
||||
let payloadLen: Int
|
||||
|
||||
let maskingKey: UInt32?
|
||||
|
||||
static let initialBytesNeeded = 2
|
||||
|
||||
/// Constructs a complete frame
|
||||
/// Mask is inferred based on maskingKey
|
||||
init(fin: Bool, opcode: WebSocketOpcode, payloadLen: Int, maskingKey: UInt32? = nil) {
|
||||
self.fin = fin
|
||||
self.mask = maskingKey != nil
|
||||
self.maskingKey = maskingKey
|
||||
self.payloadLen = payloadLen
|
||||
self.opcode = opcode.rawValue
|
||||
}
|
||||
|
||||
/// Whether or not we're just the first part of a frame
|
||||
var isComplete: Bool {
|
||||
if mask && self.maskingKey == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if payloadLen == 126 || payloadLen == 127 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/// Initializes from 16 bits of data.
|
||||
init(data: UnsafeBufferPointer<UInt8>) {
|
||||
precondition(data.count == WebSocketFrameHeader.initialBytesNeeded, "Must only initialize with 16 bits of data")
|
||||
|
||||
let firstByte = data[data.startIndex]
|
||||
let secondByte = data[data.startIndex.advancedBy(1)]
|
||||
|
||||
fin = firstByte & 0b1000_0000 != 0
|
||||
opcode = Int(firstByte & 0b0000_1111)
|
||||
mask = secondByte & 0b1000_0000 != 0
|
||||
payloadLen = Int(secondByte & 0b0111_1111)
|
||||
maskingKey = nil
|
||||
}
|
||||
|
||||
/// Constructor for creating a websocket with additional data
|
||||
init(original: WebSocketFrameHeader, additionalData: UnsafeBufferPointer<UInt8>) {
|
||||
precondition(original.additionalBytesNeeded == additionalData.count)
|
||||
|
||||
self.fin = original.fin
|
||||
self.opcode = original.opcode
|
||||
self.mask = original.mask
|
||||
|
||||
let maskOffset: Int
|
||||
// Now we have to read the payload length
|
||||
switch original.payloadLen {
|
||||
case 126:
|
||||
var networkOrderLen: UInt16 = 0
|
||||
let lenSize = sizeofValue(networkOrderLen)
|
||||
memcpy(&networkOrderLen, additionalData.baseAddress, lenSize)
|
||||
payloadLen = Int(networkOrderLen.bigEndian)
|
||||
maskOffset = lenSize
|
||||
case 127:
|
||||
var networkOrderLen: UInt64 = 0
|
||||
let lenSize = sizeofValue(networkOrderLen)
|
||||
memcpy(&networkOrderLen, additionalData.baseAddress, lenSize)
|
||||
payloadLen = Int(networkOrderLen.bigEndian)
|
||||
maskOffset = lenSize
|
||||
default:
|
||||
payloadLen = original.payloadLen
|
||||
maskOffset = 0
|
||||
}
|
||||
|
||||
/// If there's a mask we need to read that
|
||||
if mask {
|
||||
let startPtr = additionalData.baseAddress.advancedBy(maskOffset)
|
||||
var key: UInt32 = 0
|
||||
memcpy(&key, startPtr, sizeofValue(key))
|
||||
maskingKey = key
|
||||
} else {
|
||||
maskingKey = nil
|
||||
}
|
||||
}
|
||||
|
||||
/// These are additional bytes we need to complete the frame. This accounts of additional
|
||||
var additionalBytesNeeded: Int {
|
||||
let extendedPayloadNeeded: Int
|
||||
|
||||
switch self.payloadLen {
|
||||
case 126:
|
||||
extendedPayloadNeeded = 2
|
||||
case 127:
|
||||
extendedPayloadNeeded = 8
|
||||
default:
|
||||
extendedPayloadNeeded = 0
|
||||
}
|
||||
|
||||
let maskingLengthNeeded = mask ? sizeof(UInt32) : 0
|
||||
|
||||
return extendedPayloadNeeded + maskingLengthNeeded
|
||||
}
|
||||
|
||||
|
||||
var isControlFrame : Bool {
|
||||
return controlFrameRange.contains(opcode)
|
||||
}
|
||||
}
|
||||
|
||||
public struct WebSocketFrame {
|
||||
let header: WebSocketFrameHeader
|
||||
let payload: Observable<UnsafeBufferPointer<UInt8>>
|
||||
}
|
||||
|
||||
public struct WebSocketFrameReader<I: InputStream where I.Element == UInt8> {
|
||||
let stream: I
|
||||
|
||||
public init(stream: I) {
|
||||
self.stream = stream
|
||||
}
|
||||
|
||||
@warn_unused_result
|
||||
private func readFrameHeader() -> Observable<WebSocketFrameHeader> {
|
||||
return self.stream
|
||||
.readAndBufferExactly(WebSocketFrameHeader.initialBytesNeeded, allowEmpty: true)
|
||||
.map { WebSocketFrameHeader(data: $0) }
|
||||
.flatMap { header -> Observable<WebSocketFrameHeader> in
|
||||
/// Read more of the header if we need to
|
||||
switch header.additionalBytesNeeded {
|
||||
case 0:
|
||||
return Observable.just(header)
|
||||
|
||||
case let needed:
|
||||
return self
|
||||
.stream
|
||||
.readAndBufferExactly(needed)
|
||||
.map { WebSocketFrameHeader(original: header, additionalData: $0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@warn_unused_result
|
||||
private func readFrame() -> Observable<WebSocketFrame> {
|
||||
return readFrameHeader()
|
||||
.map { header -> WebSocketFrame in
|
||||
let payload: Observable<UnsafeBufferPointer<UInt8>>
|
||||
|
||||
precondition(header.isComplete)
|
||||
|
||||
if header.payloadLen == 0 {
|
||||
payload = Observable.empty()
|
||||
} else {
|
||||
if let maskingKey = header.maskingKey {
|
||||
payload = self.stream.readExactly(header.payloadLen).mask(maskingKey)
|
||||
} else {
|
||||
payload = self.stream.readExactly(header.payloadLen)
|
||||
}
|
||||
}
|
||||
|
||||
return WebSocketFrame(header: header, payload: payload)
|
||||
}
|
||||
}
|
||||
|
||||
@warn_unused_result
|
||||
public func readFrames(scheduler: ImmediateSchedulerType) -> Observable<WebSocketFrame> {
|
||||
return Observable.create { observer in
|
||||
let disposable = SerialDisposable()
|
||||
|
||||
func doNext() -> Disposable {
|
||||
var seenOne = false
|
||||
return self
|
||||
.readFrame()
|
||||
.subscribe { e in
|
||||
switch e {
|
||||
case let .Next(v):
|
||||
seenOne = true
|
||||
/// Rewrite the payload to have a side effect of pumping the next frame once it is completely read
|
||||
let newPayload = v.payload.doOn { e in
|
||||
switch e {
|
||||
/// We can start reading the next one once they've finished this one
|
||||
case .Completed:
|
||||
disposable.disposable = doNext()
|
||||
/// If there's an error reading the data, then propagate that to our observer as well
|
||||
case let .Error(e):
|
||||
observer.onError(e)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let newFrame = WebSocketFrame(header: v.header, payload: newPayload)
|
||||
|
||||
observer.onNext(newFrame)
|
||||
case .Error:
|
||||
observer.on(e)
|
||||
case .Completed:
|
||||
/// If we didn't see one, we're at the end of the stream
|
||||
if !seenOne {
|
||||
observer.onCompleted()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disposable.disposable = doNext()
|
||||
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ObservableType where E == UnsafeBufferPointer<UInt8> {
|
||||
/// This will mask or unmask the data via xor
|
||||
/// TODO: make buffer size configurable
|
||||
/// Not thread safe
|
||||
func mask(mask: UInt32) -> Observable<UnsafeBufferPointer<UInt8>> {
|
||||
var mask = mask
|
||||
|
||||
return Observable.create { observer in
|
||||
var writeBuffer = [UInt8]()
|
||||
var lastOffset = 0
|
||||
|
||||
return self
|
||||
.subscribe { evt in
|
||||
switch evt {
|
||||
case let .Next(data):
|
||||
withUnsafePointer(&mask) { ptr in
|
||||
let keyBytesStart = unsafeBitCast(ptr, UnsafePointer<UInt8>.self)
|
||||
let keyLen = sizeof(UInt32)
|
||||
assert(writeBuffer.isEmpty)
|
||||
writeBuffer.reserveCapacity(data.count)
|
||||
|
||||
// TODO: determine if this slows us down too much
|
||||
for (i, byte) in data.enumerate() {
|
||||
writeBuffer.append(byte ^ keyBytesStart.advancedBy((i + lastOffset) % keyLen).memory)
|
||||
}
|
||||
|
||||
lastOffset += data.count
|
||||
}
|
||||
|
||||
writeBuffer.withUnsafeBufferPointer { ptr in
|
||||
observer.onNext(ptr)
|
||||
}
|
||||
|
||||
writeBuffer.removeAll(keepCapacity: true)
|
||||
case .Error, .Completed:
|
||||
observer.on(evt)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the wire format of a websocket. This is pretty much everything past a handshake. It is server/client agnostic
|
||||
public class WebSocketProtocol {
|
||||
|
||||
}
|
||||
|
||||
/// Default contains implementation is O(N) for range. This is much faster
|
||||
extension Range {
|
||||
public func contains(element: Element) -> Bool {
|
||||
return startIndex.distanceTo(element) >= 0 && endIndex.distanceTo(element) < 0
|
||||
}
|
||||
}
|
||||
@ -1,387 +0,0 @@
|
||||
//
|
||||
// BufferedReading.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 8/11/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
|
||||
private enum BufferedReadRequestType<C: CollectionType> {
|
||||
|
||||
/// If splitfunc finds a match
|
||||
///
|
||||
/// - parameter currentBuffer: current buffer to match against
|
||||
/// - parameter atEnd: if we're at the end of the file. If there's no guaranteed match, the function should throws
|
||||
typealias SplitFunc = (currentBuffer: C, atEnd: Bool) throws -> (sizeToProduce: C.Index.Distance, finished: Bool)
|
||||
|
||||
case Size(sizeRequested: C.Index.Distance)
|
||||
case OnSplit(splitFunc: SplitFunc)
|
||||
}
|
||||
|
||||
private struct PendingReadRequest<O: ObserverType where O.E: CollectionType> {
|
||||
typealias C = O.E
|
||||
typealias RequestType = BufferedReadRequestType<C>
|
||||
|
||||
init(type: RequestType, observer: O) {
|
||||
self.type = type
|
||||
self.observer = observer
|
||||
}
|
||||
|
||||
/// Contains the request type
|
||||
let type: RequestType
|
||||
|
||||
let observer: O
|
||||
|
||||
/// How much we have produced so far
|
||||
var countProduced: C.Index.Distance = 0
|
||||
}
|
||||
|
||||
/// We will try to read this much when trying to buffer for split requests
|
||||
private let defaultChunkSizeForSplit = 2048
|
||||
|
||||
public protocol SplittableInputStream : StreamBase {
|
||||
func read(splitFunc splitFunc: (currentBuffer: UnsafeBufferPointer<Element>, atEnd: Bool) throws -> (sizeToProduce: Int, finished: Bool)) -> Observable<UnsafeBufferPointer<Element>>
|
||||
}
|
||||
|
||||
public class BufferedInputStream<Wrapped: InputStream> : InputStream, SplittableInputStream {
|
||||
private let stream: Wrapped
|
||||
|
||||
public typealias Element = Wrapped.Element
|
||||
|
||||
private var buffer = Array<Wrapped.Element>()
|
||||
|
||||
private typealias PendingRequest = PendingReadRequest<AnyObserver<UnsafeBufferPointer<Wrapped.Element>>>
|
||||
|
||||
private var lock = SpinLock()
|
||||
|
||||
/// The following must only read or mutated within a lock
|
||||
private var pendingRequests = [PendingRequest]()
|
||||
private var readingInProgress = false
|
||||
|
||||
/// These are to be called after we're unlocked
|
||||
private var deferredCalls = Array<() -> ()>()
|
||||
|
||||
/// This is set if we get any error back from the stream. Subsequent requests will
|
||||
private var failError: ErrorType?
|
||||
|
||||
/// TODO: make this work
|
||||
private var atEOF = false
|
||||
|
||||
|
||||
private var isOkToDeferCall = false
|
||||
|
||||
/// Disposable for reading
|
||||
private var currentDisposable: Disposable? = nil
|
||||
|
||||
private var outstandingBytes: Int = 0
|
||||
|
||||
private var reachedEnd = false
|
||||
|
||||
|
||||
private func locked(@noescape body: () throws -> ()) rethrows {
|
||||
let deferredCalls: [() -> ()] = try self.lock.locked {
|
||||
isOkToDeferCall = true
|
||||
try body()
|
||||
|
||||
defer {
|
||||
isOkToDeferCall = false
|
||||
self.deferredCalls.removeAll()
|
||||
}
|
||||
return self.deferredCalls
|
||||
}
|
||||
|
||||
for c in deferredCalls {
|
||||
c()
|
||||
}
|
||||
}
|
||||
|
||||
private func deferCall(call: () -> ()) {
|
||||
precondition(isOkToDeferCall)
|
||||
deferredCalls.append(call)
|
||||
}
|
||||
|
||||
init(stream: Wrapped) {
|
||||
self.stream = stream
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.currentDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func pump() {
|
||||
locked {
|
||||
do {
|
||||
if readingInProgress {
|
||||
return
|
||||
}
|
||||
|
||||
if pendingRequests.isEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
// If we have an error, fail all pending requests
|
||||
if let error = failError {
|
||||
let requests = pendingRequests
|
||||
pendingRequests.removeAll()
|
||||
|
||||
deferCall {
|
||||
for r in requests {
|
||||
r.observer.onError(error)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/// Consume buffer until we can't anymore
|
||||
repeat {
|
||||
} while (try tryConsumeBuffer() > 0)
|
||||
|
||||
|
||||
|
||||
// If we reached the end of the stream then we want to flush all the existing requests
|
||||
if reachedEnd {
|
||||
let requests = pendingRequests
|
||||
pendingRequests.removeAll()
|
||||
|
||||
deferCall {
|
||||
for r in requests {
|
||||
r.observer.onCompleted()
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
guard let nextRequest = pendingRequests.first else {
|
||||
return
|
||||
}
|
||||
|
||||
/// Now we take our next request and fetch data for it
|
||||
|
||||
let readSize: Int
|
||||
|
||||
switch nextRequest.type {
|
||||
case .OnSplit:
|
||||
readSize = defaultChunkSizeForSplit
|
||||
case let .Size(sizeRequested: requested):
|
||||
/// We should have already consumed this if it were the case
|
||||
precondition(self.buffer.count < requested)
|
||||
readSize = requested - self.buffer.count
|
||||
}
|
||||
|
||||
precondition(!readingInProgress)
|
||||
precondition(currentDisposable == nil)
|
||||
readingInProgress = true
|
||||
precondition(outstandingBytes == 0)
|
||||
outstandingBytes = readSize
|
||||
|
||||
deferCall { self.requestData(readSize) }
|
||||
|
||||
} catch let e {
|
||||
/// If we failed, we want to re-run pump
|
||||
failWhileLocked(e)
|
||||
|
||||
deferCall { self.pump() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func read(count: Int) -> Observable<UnsafeBufferPointer<Element>> {
|
||||
return self.read(.Size(sizeRequested: count))
|
||||
}
|
||||
|
||||
public typealias SplitFunc = (currentBuffer: UnsafeBufferPointer<Element>, atEnd: Bool) throws -> (sizeToProduce: Int, finished: Bool)
|
||||
|
||||
public func read(splitFunc splitFunc: SplitFunc) -> Observable<UnsafeBufferPointer<Element>> {
|
||||
return self.read(.OnSplit(splitFunc: splitFunc))
|
||||
}
|
||||
|
||||
private func requestData(count: Int) {
|
||||
precondition(currentDisposable == nil)
|
||||
precondition(readingInProgress)
|
||||
|
||||
let compositeDisposable = CompositeDisposable()
|
||||
self.currentDisposable = compositeDisposable
|
||||
let disposable = self
|
||||
.stream
|
||||
.read(count)
|
||||
.subscribe(self.handle)
|
||||
|
||||
compositeDisposable.addDisposable(disposable)
|
||||
}
|
||||
|
||||
private func handle(event: Event<UnsafeBufferPointer<Wrapped.Element>>) {
|
||||
locked {
|
||||
switch event {
|
||||
case .Completed:
|
||||
currentDisposable?.dispose()
|
||||
currentDisposable = nil
|
||||
readingInProgress = false
|
||||
|
||||
// If we requested more bytes than we received, we have reached the end of input stream
|
||||
if outstandingBytes > 0 {
|
||||
reachedEnd = true
|
||||
}
|
||||
case let .Next(value):
|
||||
|
||||
outstandingBytes -= value.count
|
||||
precondition(outstandingBytes >= 0)
|
||||
|
||||
do {
|
||||
try consumeNewData(value)
|
||||
} catch let e {
|
||||
failWhileLocked(e)
|
||||
}
|
||||
case let .Error(e):
|
||||
failWhileLocked(e)
|
||||
}
|
||||
}
|
||||
pump()
|
||||
}
|
||||
|
||||
private func failWhileLocked(e: ErrorType) {
|
||||
readingInProgress = false
|
||||
self.failError = e
|
||||
currentDisposable?.dispose()
|
||||
currentDisposable = nil
|
||||
}
|
||||
|
||||
/// Produces data to the pending requests
|
||||
private func consumeNewData(newData: UnsafeBufferPointer<Wrapped.Element>) throws {
|
||||
repeat {
|
||||
} while (try tryConsumeBuffer() > 0)
|
||||
|
||||
// if the buffer is empty we can try to consume the new data. Otherwise, append to buffer
|
||||
if buffer.isEmpty {
|
||||
let consumed = try tryConsumeData(newData)
|
||||
buffer.appendContentsOf(newData[consumed..<newData.endIndex])
|
||||
} else {
|
||||
// if the buffer isn't empty, just append the contents
|
||||
buffer.appendContentsOf(newData)
|
||||
// Try to consume the buffer again just in case
|
||||
}
|
||||
|
||||
repeat {
|
||||
} while (try tryConsumeBuffer() > 0)
|
||||
}
|
||||
|
||||
/// Attemps to consume buffered data. Must be called while locked
|
||||
private func tryConsumeBuffer() throws -> Int {
|
||||
if buffer.isEmpty {
|
||||
return 0
|
||||
}
|
||||
let consumed = try buffer.withUnsafeBufferPointer(tryConsumeData)
|
||||
if consumed > 0 {
|
||||
buffer.removeFirst(consumed)
|
||||
}
|
||||
return consumed
|
||||
}
|
||||
|
||||
/// Attempts to consume the data passed in. Must be called while locked
|
||||
@warn_unused_result
|
||||
private func tryConsumeData(data: UnsafeBufferPointer<Wrapped.Element>) throws -> Int {
|
||||
guard var request = self.pendingRequests.first else {
|
||||
/// if we don't have any pending requests, we didn't consume any
|
||||
return 0
|
||||
}
|
||||
|
||||
let sizeToProduce: Int
|
||||
let finished: Bool
|
||||
|
||||
switch request.type {
|
||||
case let .OnSplit(splitFunc: splitFunc):
|
||||
(sizeToProduce, finished) = try buffer.withUnsafeBufferPointer { try splitFunc(currentBuffer: $0, atEnd: atEOF) }
|
||||
case let .Size(sizeRequested: requestedSize):
|
||||
let sizeRemaining = requestedSize - request.countProduced
|
||||
sizeToProduce = min(data.count, sizeRemaining)
|
||||
finished = sizeRemaining - sizeToProduce == 0
|
||||
}
|
||||
|
||||
if finished {
|
||||
self.pendingRequests.removeFirst()
|
||||
} else {
|
||||
request.countProduced += sizeToProduce
|
||||
pendingRequests[0] = request
|
||||
}
|
||||
|
||||
|
||||
if sizeToProduce > 0 {
|
||||
precondition(sizeToProduce <= data.count)
|
||||
let dataToProduce = sizeToProduce == data.count ? data : UnsafeBufferPointer(start: data.baseAddress, count: sizeToProduce)
|
||||
request.observer.onNext(dataToProduce)
|
||||
}
|
||||
|
||||
if finished {
|
||||
deferCall {
|
||||
request.observer.onCompleted()
|
||||
}
|
||||
}
|
||||
|
||||
return sizeToProduce
|
||||
}
|
||||
|
||||
private func read(requestType: PendingRequest.RequestType) -> Observable<UnsafeBufferPointer<Element>> {
|
||||
return Observable.create { observer in
|
||||
let request = PendingRequest(type: requestType, observer: observer)
|
||||
|
||||
self.lock.locked {
|
||||
self.pendingRequests.append(request)
|
||||
}
|
||||
|
||||
self.pump()
|
||||
|
||||
return NopDisposable.instance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let crlf: [UInt8] = [UInt8]("\r\n".utf8)
|
||||
private let crlfCrlf: [UInt8] = crlf + crlf
|
||||
|
||||
let crlfSplitFunc = makeSplitFunc(crlf)
|
||||
let crlfCrlfSplitFunc = makeSplitFunc(crlfCrlf)
|
||||
|
||||
private func makeSplitFunc<E: Equatable
|
||||
>(separator: [E]) -> (currentBuffer: UnsafeBufferPointer<E>, atEnd: Bool) throws -> (sizeToProduce: Int, finished: Bool) {
|
||||
let separatorCount = separator.count
|
||||
return { currentBuffer, atEnd in
|
||||
|
||||
var lastPartialMatchCount = 0
|
||||
continueLabel:
|
||||
for startPos in currentBuffer.startIndex..<(currentBuffer.startIndex.advancedBy(currentBuffer.count)) {
|
||||
for checkOffset in 0..<separatorCount {
|
||||
if startPos.advancedBy(checkOffset) >= currentBuffer.endIndex {
|
||||
lastPartialMatchCount = checkOffset
|
||||
break continueLabel
|
||||
}
|
||||
if currentBuffer[startPos + checkOffset] != separator[checkOffset] {
|
||||
continue continueLabel
|
||||
}
|
||||
}
|
||||
// if we got to here, we have a match!
|
||||
return (startPos + separatorCount, true)
|
||||
}
|
||||
|
||||
|
||||
/// If we got this far, we can produce all data up to the length
|
||||
|
||||
// We need to keep any data that we had a partial match with
|
||||
return (currentBuffer.count - lastPartialMatchCount, false)
|
||||
}
|
||||
}
|
||||
|
||||
extension SplittableInputStream where Element == UInt8 {
|
||||
/// Yields elements until line is read. It may produce multiple chunks
|
||||
public func readLine() -> Observable<UnsafeBufferPointer<Element>> {
|
||||
return read(splitFunc: crlfSplitFunc)
|
||||
}
|
||||
|
||||
/// Reads until we get a '\r\n\r\n'. Useful for reading HTTP header
|
||||
public func readCrlfCrlf() -> Observable<UnsafeBufferPointer<Element>> {
|
||||
return read(splitFunc: crlfCrlfSplitFunc)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
//
|
||||
// Codec.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 1/6/16.
|
||||
//
|
||||
//
|
||||
|
||||
import RxSwift
|
||||
|
||||
enum ValueOrEnd<V> {
|
||||
/// :param consumed: the size of the input stream consumed
|
||||
/// :param value: result value for the coded
|
||||
case Value(V)
|
||||
|
||||
/// If we're out of data
|
||||
case End
|
||||
}
|
||||
|
||||
//
|
||||
protocol Codec {
|
||||
typealias InputElement
|
||||
typealias OutputElement
|
||||
|
||||
/// Consumes all the input. Appends
|
||||
mutating func code<
|
||||
I: CollectionType,
|
||||
O: RangeReplaceableCollectionType
|
||||
where
|
||||
I.Generator.Element == InputElement,
|
||||
O.Generator.Element == OutputElement,
|
||||
I.Index.Distance == Int
|
||||
>(input: ValueOrEnd<I>, inout output: O) throws
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension ObservableType where E: CollectionType, E.Index.Distance == Int {
|
||||
func encode<C: Codec where C.InputElement == E.Generator.Element>(codecFactory: () -> C) -> Observable<[C.OutputElement]>{
|
||||
return Observable.create { observer in
|
||||
var codec = codecFactory()
|
||||
var outputBuffer = Array<C.OutputElement>()
|
||||
|
||||
return self.subscribe { event in
|
||||
defer { outputBuffer.removeAll(keepCapacity: true) }
|
||||
|
||||
do {
|
||||
switch event {
|
||||
case .Completed:
|
||||
try codec.code(ValueOrEnd<Array<C.InputElement>>.End, output: &outputBuffer)
|
||||
if !outputBuffer.isEmpty {
|
||||
observer.onNext(outputBuffer)
|
||||
}
|
||||
observer.onCompleted()
|
||||
case let .Error(e):
|
||||
throw e
|
||||
|
||||
case let .Next(value):
|
||||
try codec.code(.Value(value), output: &outputBuffer)
|
||||
observer.onNext(outputBuffer)
|
||||
}
|
||||
} catch let e {
|
||||
observer.onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
//
|
||||
// Dial.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 1/5/16.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@ -1,83 +0,0 @@
|
||||
//
|
||||
// DispatchIO.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 1/6/16.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import RxSwift
|
||||
|
||||
/// Wraps an io since it is a protocol
|
||||
public struct DispatchIO {
|
||||
public let io: dispatch_io_t
|
||||
/// queue that results are called on
|
||||
public let resultQueue: dispatch_queue_t
|
||||
|
||||
// We're bound to using a result queue if we want to use dispatch_io underlying
|
||||
public init(io: dispatch_io_t, resultQueue: dispatch_queue_t) {
|
||||
self.io = io
|
||||
self.resultQueue = resultQueue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension DispatchIO : StreamBase {
|
||||
public typealias Element = UInt8
|
||||
}
|
||||
|
||||
extension DispatchIO : InputStream {
|
||||
public func read(count: Int) -> Observable<UnsafeBufferPointer<UInt8>> {
|
||||
let io = self.io
|
||||
return Observable.create { observer in
|
||||
dispatch_io_read(io, 0, count, self.resultQueue) { done, data, error in
|
||||
if error != 0 {
|
||||
observer.onError(POSIXError(rawValue: error)!)
|
||||
}
|
||||
|
||||
let dataSize = dispatch_data_get_size(data)
|
||||
|
||||
var buffer = [UInt8]()
|
||||
|
||||
buffer.reserveCapacity(dataSize)
|
||||
|
||||
data.apply { rawBuffer in
|
||||
observer.onNext(rawBuffer)
|
||||
}
|
||||
|
||||
|
||||
if done {
|
||||
observer.onCompleted()
|
||||
}
|
||||
}
|
||||
|
||||
return NopDisposable.instance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension DispatchIO : OutputStream {
|
||||
public func write(data: UnsafeBufferPointer<UInt8>) -> Observable<Void> {
|
||||
let doneSubject: ReplaySubject<Void> = ReplaySubject.create(bufferSize: 1)
|
||||
|
||||
let dispatchData = dispatch_data_create(data.baseAddress, data.count, nil, nil)
|
||||
|
||||
dispatch_io_write(io, 0, dispatchData, resultQueue) { done, _, error in
|
||||
guard error == 0 else {
|
||||
doneSubject.onError(POSIXError(rawValue: error)!)
|
||||
return
|
||||
}
|
||||
|
||||
if done {
|
||||
doneSubject.onNext()
|
||||
doneSubject.onCompleted()
|
||||
}
|
||||
}
|
||||
|
||||
return doneSubject
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
//
|
||||
// Error.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 8/7/15.
|
||||
//
|
||||
//
|
||||
|
||||
/// Wraps errors. Has an uknown type if it cant resolve to an oserror
|
||||
enum Error: ErrorType {
|
||||
case Unknown(status: Int32)
|
||||
case CodecError
|
||||
case UTF8DecodeError
|
||||
case Canceled
|
||||
case NoAddressesRemaining
|
||||
case UnknownSockaddrType(family: sa_family_t)
|
||||
// Getaddrinfo error
|
||||
case GAIError(status: Int32)
|
||||
|
||||
// when inet_pton returns 0
|
||||
case NotParseableAddress
|
||||
|
||||
/// For functions that return negative value on error and expect errno to be set
|
||||
static func checkReturnCode(returnCode: Int32) -> ErrorType? {
|
||||
guard returnCode < 0 else {
|
||||
return nil
|
||||
}
|
||||
return errorFromStatusCode(errno)
|
||||
}
|
||||
|
||||
/// Returns an error type based on status code
|
||||
static func errorFromStatusCode(status: Int32) -> ErrorType? {
|
||||
guard status != 0 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let e = POSIXError(rawValue: status) {
|
||||
return e
|
||||
}
|
||||
|
||||
return Error.Unknown(status: status)
|
||||
}
|
||||
|
||||
static func throwIfNotSuccess(status: Int32) throws {
|
||||
if let e = errorFromStatusCode(status) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
// Same as above, but checks if less than 0, and uses errno as the varaible
|
||||
static func throwIfNotSuccessLessThan0(returnCode: Int32) throws -> Int32 {
|
||||
if let e = checkReturnCode(returnCode) {
|
||||
throw e
|
||||
}
|
||||
return returnCode
|
||||
}
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
//
|
||||
// Extensions.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 8/6/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Dispatch
|
||||
|
||||
extension dispatch_data_t {
|
||||
func apply(applier: UnsafeBufferPointer<UInt8> -> Bool) -> Bool {
|
||||
return dispatch_data_apply(self) { (_, offset, buffer, size) -> Bool in
|
||||
let mappedBuffer = unsafeBitCast(buffer, UnsafePointer<UInt8>.self)
|
||||
let buffer = UnsafeBufferPointer(start: mappedBuffer.advancedBy(offset), count: size - offset)
|
||||
return applier(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
func apply(applier: UnsafeBufferPointer<UInt8> -> ()) -> Bool {
|
||||
return self.apply { d -> Bool in
|
||||
applier(d)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func apply(applier: UnsafeBufferPointer<UInt8> throws -> ()) throws -> Bool {
|
||||
var error: ErrorType? = nil
|
||||
let ret = self.apply { d -> Bool in
|
||||
do {
|
||||
try applier(d)
|
||||
return true
|
||||
} catch let e {
|
||||
error = e
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if let e = error {
|
||||
throw e
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension dispatch_queue_t {
|
||||
func dispatchAsync(block: () -> ()) {
|
||||
dispatch_async(self, block)
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user