Compare commits
24 Commits
master
...
lewis/disp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95de1ea335 | ||
|
|
e3c402beab | ||
|
|
f616386954 | ||
|
|
61b19ab633 | ||
|
|
c62bfb90ad | ||
|
|
d7078b082c | ||
|
|
49c79072d2 | ||
|
|
a797a0f568 | ||
|
|
7c6e18e858 | ||
|
|
e6c3850c9b | ||
|
|
fe572dc752 | ||
|
|
9b0d5134f8 | ||
|
|
1883c521d6 | ||
|
|
5214ed5347 | ||
|
|
7ea9d25976 | ||
|
|
5278664ab7 | ||
|
|
2a2ed772c3 | ||
|
|
d237938434 | ||
|
|
ac861d6ff2 | ||
|
|
2ab4b7d421 | ||
|
|
edd6faceed | ||
|
|
f58b21618c | ||
|
|
fdf93c6f9c | ||
|
|
72f7f81ff4 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -17,10 +17,7 @@ xcuserdata/
|
||||
*.xcworkspace
|
||||
!default.xcworkspace
|
||||
*xcuserdata
|
||||
*.xccheckout
|
||||
profile
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
extern/
|
||||
|
||||
*.pyc
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,3 +1,3 @@
|
||||
[submodule "Vendor/xctoolchain"]
|
||||
path = Vendor/xctoolchain
|
||||
url = https://github.com/ParsePlatform/xctoolchain.git
|
||||
[submodule "pages"]
|
||||
path = pages
|
||||
url = git://github.com/square/SocketRocket.git
|
||||
|
||||
@ -1 +0,0 @@
|
||||
2.3.1
|
||||
47
.travis.yml
47
.travis.yml
@ -1,43 +1,8 @@
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
language: objective-c
|
||||
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
|
||||
osx_image: xcode6.4
|
||||
before_script:
|
||||
- bundle install
|
||||
script:
|
||||
- |
|
||||
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
|
||||
- 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
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
# 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 +0,0 @@
|
||||
../Vendor/xctoolchain/Configurations/
|
||||
@ -1,17 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,20 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,17 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,16 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,19 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,19 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
3
Gemfile
3
Gemfile
@ -1,4 +1,3 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'cocoapods'
|
||||
gem 'xcpretty'
|
||||
gem 'cocoapods', '0.37.2'
|
||||
|
||||
35
LICENSE
35
LICENSE
@ -1,30 +1,15 @@
|
||||
BSD License
|
||||
|
||||
For SocketRocket software
|
||||
Copyright 2012 Square Inc.
|
||||
|
||||
Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
|
||||
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
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
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 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.
|
||||
@ -1,11 +0,0 @@
|
||||
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,30 +1,20 @@
|
||||
TEST_SCENARIOS="[1-8]*"
|
||||
TEST_URL='ws://localhost:9001/'
|
||||
|
||||
all:
|
||||
$(MAKE) -C SocketRocket
|
||||
|
||||
clean:
|
||||
$(MAKE) -C SocketRocket clean
|
||||
|
||||
.env:
|
||||
|
||||
./TestSupport/setup_env.sh .env
|
||||
|
||||
test: .env
|
||||
test:
|
||||
|
||||
mkdir -p pages/results
|
||||
bash ./TestSupport/run_test_server.sh $(TEST_SCENARIOS) $(TEST_URL) Debug || open pages/results/index.html && false
|
||||
bash ./TestSupport/run_test.sh $(TEST_SCENARIOS) $(TEST_URL) Debug || open pages/results/index.html && false
|
||||
open pages/results/index.html
|
||||
|
||||
test_all: .env
|
||||
test_all:
|
||||
|
||||
mkdir -p pages/results
|
||||
bash ./TestSupport/run_test_server.sh '*' $(TEST_URL) Debug || open pages/results/index.html && false
|
||||
bash ./TestSupport/run_test.sh '*' $(TEST_URL) Debug || open pages/results/index.html && false
|
||||
open pages/results/index.html
|
||||
|
||||
test_perf: .env
|
||||
test_perf:
|
||||
|
||||
mkdir -p pages/results
|
||||
bash ./TestSupport/run_test_server.sh '9.*' $(TEST_URL) Release || open pages/results/index.html && false
|
||||
bash ./TestSupport/run_test.sh '9.*' $(TEST_URL) Release || open pages/results/index.html && false
|
||||
open pages/results/index.html
|
||||
|
||||
33
PATENTS
33
PATENTS
@ -1,33 +0,0 @@
|
||||
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
213
README.md
@ -1,213 +0,0 @@
|
||||
# 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
Normal file
255
README.rst
Normal file
@ -0,0 +1,255 @@
|
||||
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.
|
||||
339
SRWebSocketTests/SRTAutobahnTests.m
Normal file
339
SRWebSocketTests/SRTAutobahnTests.m
Normal file
@ -0,0 +1,339 @@
|
||||
//
|
||||
// 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
|
||||
26
SRWebSocketTests/SRTWebSocketOperation.h
Normal file
26
SRWebSocketTests/SRTWebSocketOperation.h
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// 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,17 +1,13 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
// SRTWebSocketOperation.m
|
||||
// SocketRocket
|
||||
//
|
||||
// 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.
|
||||
// Created by Mike Lewis on 1/28/12.
|
||||
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SRTWebSocketOperation.h"
|
||||
|
||||
#import "SRAutobahnUtilities.h"
|
||||
#import "SRWebSocket.h"
|
||||
|
||||
@interface SRTWebSocketOperation ()
|
||||
|
||||
@ -27,7 +23,7 @@
|
||||
@synthesize isExecuting = _isExecuting;
|
||||
@synthesize error = _error;
|
||||
|
||||
- (instancetype)initWithURL:(NSURL *)URL;
|
||||
- (id)initWithURL:(NSURL *)URL;
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
@ -65,6 +61,11 @@
|
||||
_webSocket = nil;
|
||||
}
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
|
||||
{
|
||||
NSAssert(NO, @"Not implemented");
|
||||
}
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
|
||||
{
|
||||
_error = error;
|
||||
@ -78,14 +79,4 @@
|
||||
_webSocket = nil;
|
||||
}
|
||||
|
||||
- (BOOL)waitUntilFinishedWithTimeout:(NSTimeInterval)timeout
|
||||
{
|
||||
if (self.isFinished) {
|
||||
return YES;
|
||||
}
|
||||
return SRRunLoopRunUntil(^BOOL{
|
||||
return self.isFinished;
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
@end
|
||||
21
SRWebSocketTests/SRWebSocketTests-Prefix.pch
Normal file
21
SRWebSocketTests/SRWebSocketTests-Prefix.pch
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// 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
|
||||
13
SRWebSocketTests/STRDispatchTest.h
Normal file
13
SRWebSocketTests/STRDispatchTest.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// STRDispatchTest.h
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Michael Lewis on 1/12/13.
|
||||
//
|
||||
//
|
||||
|
||||
#import <SenTestingKit/SenTestingKit.h>
|
||||
|
||||
@interface STRDispatchTest : SenTestCase
|
||||
|
||||
@end
|
||||
151
SRWebSocketTests/STRDispatchTest.mm
Normal file
151
SRWebSocketTests/STRDispatchTest.mm
Normal file
@ -0,0 +1,151 @@
|
||||
//
|
||||
// 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
|
||||
27
SRWebSocketTests/SenTestCase+SRTAdditions.h
Normal file
27
SRWebSocketTests/SenTestCase+SRTAdditions.h
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// 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
|
||||
38
SRWebSocketTests/SenTestCase+SRTAdditions.m
Normal file
38
SRWebSocketTests/SenTestCase+SRTAdditions.m
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// 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
|
||||
2
SRWebSocketTests/en.lproj/InfoPlist.strings
Normal file
2
SRWebSocketTests/en.lproj/InfoPlist.strings
Normal file
@ -0,0 +1,2 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
8
SRWebSocketTests/foo.mm
Normal file
8
SRWebSocketTests/foo.mm
Normal file
@ -0,0 +1,8 @@
|
||||
//
|
||||
// foo.m
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 10/31/11.
|
||||
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
@ -1,22 +1,19 @@
|
||||
Pod::Spec.new do |s|
|
||||
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.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.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.tvos.deployment_target = '9.0'
|
||||
s.ios.deployment_target = '6.0'
|
||||
s.osx.deployment_target = '10.8'
|
||||
|
||||
s.ios.frameworks = 'CFNetwork', 'Security'
|
||||
s.osx.frameworks = 'CoreServices', 'Security'
|
||||
s.tvos.frameworks = 'CFNetwork', 'Security'
|
||||
s.libraries = 'icucore'
|
||||
|
||||
s.libraries = "icucore"
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
878
SocketRocket.xcodeproj/project.pbxproj.orig
Normal file
878
SocketRocket.xcodeproj/project.pbxproj.orig
Normal file
@ -0,0 +1,878 @@
|
||||
// !$*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,80 +0,0 @@
|
||||
<?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 = "0730"
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -14,9 +14,9 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
BlueprintIdentifier = "F6B2082C1450F597009315AF"
|
||||
BuildableName = "libSocketRocket.a"
|
||||
BlueprintName = "SocketRocket"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
@ -25,12 +25,12 @@
|
||||
buildForRunning = "NO"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
buildForAnalyzing = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
@ -40,19 +40,20 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
shouldUseLaunchSchemeArgsEnv = "NO">
|
||||
<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 ">
|
||||
scriptText = "PIDFILE=$TMPDIR/srtharness.pid if [ -r $PIDFILE ]; then EXISTING_PID=`cat $PIDFILE` echo "Killing Dangling SRTextharneess PID:" $EXISTING_PID kill $EXISTING_PID rm $PIDFILE fi pushd $PROJECT_DIR export MACOSX_DEPLOYMENT_TARGET= bash TestSupport/ensure_virtualenv.sh $PROJECT_DIR/.env source .env/bin/activate rm -rf "$PROJECT_DIR/reports/clients/" nohup sr-testharness -i '' -c '*' & #nohup sr-testharness -k hello_test_harness -i '' -c '*' & echo $! > $PIDFILE popd"
|
||||
shellToInvoke = "/bin/bash">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
@ -64,13 +65,14 @@
|
||||
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 ">
|
||||
scriptText = "PIDFILE=$TMPDIR/srtharness.pid if [ -r $PIDFILE ]; then EXISTING_PID=`cat $PIDFILE` echo "Killing SRTextharneess PID:" $EXISTING_PID kill $EXISTING_PID rm $PIDFILE fi open $PROJECT_DIR/reports/clients/index.html"
|
||||
shellToInvoke = "/bin/bash">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
@ -83,21 +85,39 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
<SkippedTests>
|
||||
<Test
|
||||
Identifier = "SRTAutobahnTests">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "STRDispatchTest/testConnect">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "STRDispatchTest/testSimpleDial">
|
||||
</Test>
|
||||
</SkippedTests>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
BlueprintIdentifier = "F6B2082C1450F597009315AF"
|
||||
BuildableName = "libSocketRocket.a"
|
||||
BlueprintName = "SocketRocket"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "SR_TEST_URL"
|
||||
value = "ws://localhost:9001"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
@ -114,13 +134,38 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
BlueprintIdentifier = "F6B2082C1450F597009315AF"
|
||||
BuildableName = "libSocketRocket.a"
|
||||
BlueprintName = "SocketRocket"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
<AdditionalOption
|
||||
key = "DYLD_INSERT_LIBRARIES"
|
||||
value = "/usr/lib/libgmalloc.dylib"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<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
|
||||
@ -129,15 +174,6 @@
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -16,7 +16,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F668C87F153E91210044DBAC"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-macOS"
|
||||
BlueprintName = "SocketRocketOSX"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
@ -47,7 +47,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F668C87F153E91210044DBAC"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-macOS"
|
||||
BlueprintName = "SocketRocketOSX"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
@ -60,15 +60,6 @@
|
||||
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">
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -15,8 +15,8 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
@ -33,8 +33,8 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
@ -56,8 +56,8 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0630"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -23,10 +23,10 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
@ -38,18 +38,15 @@
|
||||
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">
|
||||
@ -65,30 +62,19 @@
|
||||
<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
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0630"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -14,61 +14,95 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS-Dynamic"
|
||||
BlueprintIdentifier = "F62417E214D52F3C003CE997"
|
||||
BuildableName = "TestChat.app"
|
||||
BlueprintName = "TestChat"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F62417E214D52F3C003CE997"
|
||||
BuildableName = "TestChat.app"
|
||||
BlueprintName = "TestChat"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS-Dynamic"
|
||||
BlueprintIdentifier = "F62417E214D52F3C003CE997"
|
||||
BuildableName = "TestChat.app"
|
||||
BlueprintName = "TestChat"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</BuildableProductRunnable>
|
||||
<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">
|
||||
<MacroExpansion>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS-Dynamic"
|
||||
BlueprintIdentifier = "F62417E214D52F3C003CE997"
|
||||
BuildableName = "TestChat.app"
|
||||
BlueprintName = "TestChat"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
32
SocketRocket/Common.h
Normal file
32
SocketRocket/Common.h
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// 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
|
||||
149
SocketRocket/DispatchData.h
Normal file
149
SocketRocket/DispatchData.h
Normal file
@ -0,0 +1,149 @@
|
||||
//
|
||||
// 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__) */
|
||||
15
SocketRocket/DispatchData.mm
Normal file
15
SocketRocket/DispatchData.mm
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
91
SocketRocket/DispatchIO.h
Normal file
91
SocketRocket/DispatchIO.h
Normal file
@ -0,0 +1,91 @@
|
||||
//
|
||||
// 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__) */
|
||||
298
SocketRocket/DispatchIO.mm
Normal file
298
SocketRocket/DispatchIO.mm
Normal file
@ -0,0 +1,298 @@
|
||||
//
|
||||
// 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 Queue", 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), 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,67 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,138 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,40 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,36 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,28 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,64 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,13 +0,0 @@
|
||||
//
|
||||
// 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);
|
||||
@ -1,13 +0,0 @@
|
||||
//
|
||||
// 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);
|
||||
@ -1,26 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,481 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,24 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,83 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,26 +0,0 @@
|
||||
//
|
||||
// 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);
|
||||
@ -1,19 +0,0 @@
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,73 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,20 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,42 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,20 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,79 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,19 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,43 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,20 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,33 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,22 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,47 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,16 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,26 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,19 +0,0 @@
|
||||
//
|
||||
// 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);
|
||||
@ -1,73 +0,0 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,77 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,27 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,27 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,38 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,42 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,72 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
@ -1,75 +0,0 @@
|
||||
//
|
||||
// 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,17 +1,21 @@
|
||||
//
|
||||
// Copyright 2012 Square Inc.
|
||||
// Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
// Copyright 2012 Square Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
#import <Security/SecCertificate.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, SRReadyState) {
|
||||
SR_CONNECTING = 0,
|
||||
@ -20,398 +24,109 @@ typedef NS_ENUM(NSInteger, SRReadyState) {
|
||||
SR_CLOSED = 3,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, SRStatusCode) {
|
||||
// 0-999: Reserved and not used.
|
||||
typedef enum SRStatusCode : NSInteger {
|
||||
SRStatusCodeNormal = 1000,
|
||||
SRStatusCodeGoingAway = 1001,
|
||||
SRStatusCodeProtocolError = 1002,
|
||||
SRStatusCodeUnhandledType = 1003,
|
||||
// 1004 reserved.
|
||||
SRStatusNoStatusReceived = 1005,
|
||||
SRStatusCodeAbnormal = 1006,
|
||||
// 1004-1006 reserved.
|
||||
SRStatusCodeInvalidUTF8 = 1007,
|
||||
SRStatusCodePolicyViolated = 1008,
|
||||
SRStatusCodeMessageTooBig = 1009,
|
||||
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.
|
||||
};
|
||||
} SRStatusCode;
|
||||
|
||||
@class SRWebSocket;
|
||||
@class SRSecurityPolicy;
|
||||
|
||||
/**
|
||||
Error domain used for errors reported by SRWebSocket.
|
||||
*/
|
||||
extern NSString *const SRWebSocketErrorDomain;
|
||||
|
||||
/**
|
||||
Key used for HTTP status code if bad response was received from the server.
|
||||
*/
|
||||
extern NSString *const SRHTTPResponseErrorKey;
|
||||
|
||||
#pragma mark - SRWebSocketDelegate
|
||||
|
||||
@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;
|
||||
|
||||
/**
|
||||
A dispatch queue for scheduling the delegate calls. The queue doesn't need be a serial queue.
|
||||
@property (nonatomic, readonly) SRReadyState readyState;
|
||||
@property (nonatomic, readonly, retain) NSURL *url;
|
||||
|
||||
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;
|
||||
// This returns the negotiated protocol.
|
||||
// It will be nil until after the handshake completes.
|
||||
@property (nonatomic, readonly, copy) NSString *protocol;
|
||||
|
||||
/**
|
||||
An operation queue for scheduling the delegate calls.
|
||||
// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol.
|
||||
- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols;
|
||||
- (id)initWithURLRequest:(NSURLRequest *)request;
|
||||
|
||||
If `nil` and `delegateOperationQueue` is `nil`, the socket uses main queue for performing all delegate method calls.
|
||||
*/
|
||||
@property (nullable, nonatomic, strong) NSOperationQueue *delegateOperationQueue;
|
||||
// Some helper constructors.
|
||||
- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols;
|
||||
- (id)initWithURL:(NSURL *)url;
|
||||
|
||||
/**
|
||||
Current ready state of the socket. Default: `SR_CONNECTING`.
|
||||
// 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;
|
||||
|
||||
This property is Key-Value Observable and fully thread-safe.
|
||||
*/
|
||||
@property (atomic, assign, readonly) SRReadyState readyState;
|
||||
// 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;
|
||||
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
// SRWebSockets are intended for one-time-use only. Open 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;
|
||||
|
||||
/**
|
||||
Closes a web socket using a given code and reason.
|
||||
// Send a UTF8 String or Data.
|
||||
- (void)send:(id)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(_:));
|
||||
// Send Data (can be nil) in a ping message.
|
||||
- (void)sendPing:(NSData *)data;
|
||||
|
||||
@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;
|
||||
|
||||
/**
|
||||
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(_:));
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1748
SocketRocket/SRWebSocket.mm
Normal file
1748
SocketRocket/SRWebSocket.mm
Normal file
File diff suppressed because it is too large
Load Diff
1768
SocketRocket/SRWebSocket.mm.orig
Normal file
1768
SocketRocket/SRWebSocket.mm.orig
Normal file
File diff suppressed because it is too large
Load Diff
176
SocketRocket/SecureIO.h
Normal file
176
SocketRocket/SecureIO.h
Normal file
@ -0,0 +1,176 @@
|
||||
//
|
||||
// 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__) */
|
||||
475
SocketRocket/SecureIO.mm
Normal file
475
SocketRocket/SecureIO.mm
Normal file
@ -0,0 +1,475 @@
|
||||
//
|
||||
// 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);
|
||||
};
|
||||
}
|
||||
}
|
||||
27
SocketRocket/SocketRocket-Prefix.pch
Normal file
27
SocketRocket/SocketRocket-Prefix.pch
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// 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,15 +0,0 @@
|
||||
//
|
||||
// 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 <SocketRocket/NSRunLoop+SRWebSocket.h>
|
||||
#import <SocketRocket/NSURLRequest+SRWebSocket.h>
|
||||
#import <SocketRocket/SRSecurityPolicy.h>
|
||||
#import <SocketRocket/SRWebSocket.h>
|
||||
142
SocketRocketIO/Concurrent.swift
Normal file
142
SocketRocketIO/Concurrent.swift
Normal file
@ -0,0 +1,142 @@
|
||||
//
|
||||
// Concurrent.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 8/7/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// This is a nonblocking once. THis means that the once block
|
||||
/// may fire after the callers that don't get it succeed
|
||||
/// This is unlike the behavior of dispatch_once
|
||||
struct Once {
|
||||
var count: Int32 = 0
|
||||
|
||||
/// If this is the first one to call this, will invoke the block
|
||||
///
|
||||
/// :param: bit This structure can be used for more than one bit. We allow the first 16 bits to be used
|
||||
///
|
||||
/// :return: true if the block is ecuted
|
||||
mutating func doMaybe(bit: Int = 0, block: () -> ()) -> Bool {
|
||||
precondition(bit < 16)
|
||||
|
||||
guard !count.atomicTestAndSet(bit) else {
|
||||
return false
|
||||
}
|
||||
|
||||
block()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension Int32 {
|
||||
mutating func atomicTestAndSet(bit: Int) -> Bool {
|
||||
return withUnsafeMutablePointer(&self) { ptr in
|
||||
return OSAtomicTestAndSet(UInt32(bit), ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Building block for a
|
||||
enum PState<V> {
|
||||
case Pending
|
||||
case Fulfilled(V)
|
||||
case Rejected(ErrorType)
|
||||
}
|
||||
|
||||
struct PGroup<T> {
|
||||
/// TODO: remove locking somehow
|
||||
typealias LockType = OSSpinLock
|
||||
|
||||
typealias Handler = (ErrorOptional<T>) -> ()
|
||||
|
||||
private var lock = LockType()
|
||||
|
||||
// These should only be mutated inside a lock
|
||||
private var result: ErrorOptional<T>? = nil
|
||||
|
||||
// these are blocks enqueued while we don't have a result. Should be called when we actually fulfill
|
||||
private var pendingHandlers = [Handler]()
|
||||
|
||||
var state = PState<T>.Pending
|
||||
|
||||
mutating func cancel() -> Bool {
|
||||
return reject(Error.Canceled)
|
||||
}
|
||||
|
||||
mutating func reject(e: ErrorType) -> Bool {
|
||||
return fulfill(.Error(e))
|
||||
}
|
||||
|
||||
mutating func fulfill(v: ErrorOptional<T>) -> Bool {
|
||||
guard result == nil else {
|
||||
return false
|
||||
}
|
||||
|
||||
let blocksToRun: [Handler]? = lock.withLock() {
|
||||
guard self.result == nil else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.result = v
|
||||
|
||||
let ret = self.pendingHandlers
|
||||
self.pendingHandlers.removeAll()
|
||||
return ret
|
||||
}
|
||||
|
||||
guard let b = blocksToRun else {
|
||||
return false
|
||||
}
|
||||
|
||||
for bl in b {
|
||||
bl(v)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/// Adds a handler. Called immediately if result is set. Otherwise it will enqueue
|
||||
mutating func then(h: Handler) -> Void {
|
||||
// If we have a result, return it
|
||||
if let v = self.result {
|
||||
h(v)
|
||||
return
|
||||
}
|
||||
|
||||
let valueAgain: ErrorOptional<T>? = lock.withLock {
|
||||
if let r = result {
|
||||
return r
|
||||
}
|
||||
|
||||
self.pendingHandlers.append(h)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if let v = valueAgain {
|
||||
h(v)
|
||||
}
|
||||
}
|
||||
|
||||
init () {
|
||||
|
||||
}
|
||||
|
||||
private init(result: ErrorOptional<T>) {
|
||||
self.result = result
|
||||
}
|
||||
|
||||
static func resolve<T>(v: T) -> PGroup<T> {
|
||||
return PGroup<T>(result: ErrorOptional<T>(v))
|
||||
}
|
||||
|
||||
static func reject<T>(error: ErrorType) -> PGroup<T> {
|
||||
return PGroup<T>(result: ErrorOptional<T>(error))
|
||||
}
|
||||
}
|
||||
49
SocketRocketIO/Error.swift
Normal file
49
SocketRocketIO/Error.swift
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// 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 {
|
||||
if let e = checkReturnCode(returnCode) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
71
SocketRocketIO/ErrorOptional.swift
Normal file
71
SocketRocketIO/ErrorOptional.swift
Normal file
@ -0,0 +1,71 @@
|
||||
//
|
||||
// ErrorOptional.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 8/3/15.
|
||||
//
|
||||
//
|
||||
|
||||
// Like optional but instead of None, it can take a nil
|
||||
public enum ErrorOptional<T> {
|
||||
case Error(ErrorType)
|
||||
case Some(T)
|
||||
|
||||
/// Returns nil if ierrored. Using checkedGet is recommended
|
||||
var orNil: T? {
|
||||
get {
|
||||
switch self {
|
||||
case let Some(some):
|
||||
return some
|
||||
case Error:
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var hasError: Bool {
|
||||
get {
|
||||
switch self {
|
||||
case Some:
|
||||
return false
|
||||
case Error:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkedGet() throws -> T {
|
||||
switch self {
|
||||
case let Some(some):
|
||||
return some
|
||||
case let Error(e):
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a non-`nil` instance that stores `some`.
|
||||
init(_ some: T) {
|
||||
self = Some(some)
|
||||
}
|
||||
|
||||
init(_ error: ErrorType) {
|
||||
self = Error(error)
|
||||
}
|
||||
|
||||
// Will catch the first error and return with error wrapped in error optinal
|
||||
static func attempt(block: () throws -> T) -> ErrorOptional<T> {
|
||||
do {
|
||||
return .Some(try block())
|
||||
} catch let e {
|
||||
return .Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum Errors: ErrorType {
|
||||
case MultiError([ErrorType])
|
||||
}
|
||||
|
||||
|
||||
44
SocketRocketIO/Extensions.swift
Normal file
44
SocketRocketIO/Extensions.swift
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// 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
|
||||
}
|
||||
}
|
||||
319
SocketRocketIO/IO.swift
Normal file
319
SocketRocketIO/IO.swift
Normal file
@ -0,0 +1,319 @@
|
||||
//
|
||||
// IO.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 7/30/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SystemShims
|
||||
|
||||
public struct CloseFlags : OptionSetType {
|
||||
public init(rawValue: UInt) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
public let rawValue: UInt
|
||||
public static let Stop = CloseFlags(rawValue: DISPATCH_IO_STOP)
|
||||
}
|
||||
|
||||
public typealias DataHandler = (done: Bool, data: dispatch_data_t, error: ErrorType?) -> Void
|
||||
public typealias ErrorHandler = (error: ErrorType?) -> Void
|
||||
|
||||
public protocol IO {
|
||||
// Closes the IO. the callback will be called when finished. IO can only be closed once
|
||||
// I/Os only exist once they're open.
|
||||
func read(length: size_t, queue: Queue, handler: DataHandler)
|
||||
|
||||
func write(data: dispatch_data_t, queue: Queue, handler: DataHandler)
|
||||
|
||||
func close(queue: Queue, flags: CloseFlags)
|
||||
}
|
||||
|
||||
|
||||
public class RawIO: IO {
|
||||
private let channel: dispatch_io_t
|
||||
|
||||
public init(channel: dispatch_io_t) {
|
||||
self.channel = channel
|
||||
}
|
||||
|
||||
public convenience init(fd: dispatch_fd_t,
|
||||
cleanupQueue: Queue,
|
||||
callbackQueue: Queue,
|
||||
ioQueue: Queue,
|
||||
cleanupHandler: ErrorHandler) {
|
||||
let channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, cleanupQueue.queue) { errorCode in
|
||||
cleanupHandler(error: NSError.fromErrorCode(errorCode))
|
||||
}
|
||||
|
||||
dispatch_set_target_queue(channel, ioQueue.queue);
|
||||
|
||||
self.init(channel: channel)
|
||||
}
|
||||
|
||||
public func read(length: Int, queue: Queue, handler: DataHandler) {
|
||||
dispatch_io_read(channel, 0, length, queue.queue, dataHandlerToIoHandler(handler))
|
||||
}
|
||||
|
||||
public func write(data: dispatch_data_t, queue: Queue, handler: DataHandler) {
|
||||
dispatch_io_write(channel, 0, data, queue.queue, dataHandlerToIoHandler(handler))
|
||||
}
|
||||
|
||||
public func close(queue: Queue, flags: CloseFlags) {
|
||||
dispatch_io_close(channel, flags.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typealias IOFactory = (fd: dispatch_fd_t) -> IO
|
||||
|
||||
public class Listener {
|
||||
let workQueue: Queue
|
||||
let ioFactory: IOFactory
|
||||
var eventSource: dispatch_source_t!
|
||||
|
||||
var closeHandler: ErrorHandler!
|
||||
|
||||
private init(workQueue: Queue, ioFactory: IOFactory) {
|
||||
self.workQueue = workQueue
|
||||
self.ioFactory = ioFactory
|
||||
}
|
||||
|
||||
|
||||
/// Starts listening
|
||||
///
|
||||
/// :param: queue queue callback is called on
|
||||
/// :param: callback Called when listening starts. AN error if it failed
|
||||
static func startListening(port: UInt16, address: String, workQueue: Queue, ioFactory: IOFactory, queue: Queue, callback: ErrorHandler) -> Listener {
|
||||
|
||||
let l = Listener(workQueue: workQueue, ioFactory: ioFactory)
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
public func close(queue: Queue, handler: ErrorHandler) {
|
||||
workQueue.dispatchAsync {
|
||||
self.closeHandler = handler
|
||||
dispatch_source_cancel(self.eventSource)
|
||||
self.eventSource = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NSError {
|
||||
convenience init(osError: Int32) {
|
||||
self.init(domain: NSPOSIXErrorDomain, code: Int(osError), userInfo: nil)
|
||||
}
|
||||
|
||||
static func fromErrorCode(osError: Int32) -> NSError? {
|
||||
if osError == 0 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return NSError(osError: osError)
|
||||
}
|
||||
}
|
||||
|
||||
private func dataHandlerToIoHandler(handler: DataHandler) -> dispatch_io_handler_t {
|
||||
return { done, data, errorCode in
|
||||
handler(done: done, data: data, error: NSError.fromErrorCode(errorCode))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public protocol SockAddr {
|
||||
init()
|
||||
|
||||
mutating func setup(listenAddr: Address, listenPort: UInt16) throws
|
||||
|
||||
static var size : Int {get}
|
||||
static var addressFamily: Int32 { get }
|
||||
}
|
||||
|
||||
public enum Address {
|
||||
case Loopback
|
||||
case Any
|
||||
case IPV6Addr(address: String)
|
||||
case IPV4Addr(address: String)
|
||||
}
|
||||
extension sockaddr_in6: SockAddr {
|
||||
public mutating func setup(listenAddr: Address, listenPort: UInt16) throws {
|
||||
switch listenAddr {
|
||||
case .Any:
|
||||
self.sin6_addr = in6addr_any
|
||||
case let .IPV6Addr(address: address):
|
||||
try Error.throwIfNotSuccess(inet_pton(self.dynamicType.addressFamily, address, &self.sin6_addr))
|
||||
case .IPV4Addr:
|
||||
fatalError("Cannot listen to IPV4Address in an ipv6 socket")
|
||||
case .Loopback:
|
||||
self.sin6_addr = in6addr_loopback
|
||||
}
|
||||
|
||||
self.sin6_port = listenPort.bigEndian
|
||||
self.sin6_family = sa_family_t(self.dynamicType.addressFamily)
|
||||
self.sin6_len = UInt8(self.dynamicType.size)
|
||||
}
|
||||
|
||||
public static let size = sizeof(sockaddr_in6)
|
||||
public static let addressFamily = AF_INET6
|
||||
}
|
||||
|
||||
let INADDR_ANY = in_addr(s_addr: 0x00000000)
|
||||
let INADDR_LOOPBACK4 = in_addr(s_addr: UInt32(0x7f000001).bigEndian)
|
||||
|
||||
extension sockaddr_in: SockAddr {
|
||||
public static let size = sizeof(sockaddr_in)
|
||||
public static let addressFamily = AF_INET
|
||||
|
||||
public mutating func setup(listenAddr: Address, listenPort: UInt16) throws {
|
||||
switch listenAddr {
|
||||
case .Any:
|
||||
self.sin_addr = INADDR_ANY
|
||||
case let .IPV4Addr(address: address):
|
||||
try Error.throwIfNotSuccess(inet_pton(self.dynamicType.addressFamily, address, &self.sin_addr))
|
||||
case .IPV6Addr:
|
||||
fatalError("Cannot listen to IPV6Address in an ipv4 socket")
|
||||
case .Loopback:
|
||||
self.sin_addr = INADDR_LOOPBACK4
|
||||
}
|
||||
|
||||
self.sin_port = listenPort.bigEndian
|
||||
self.sin_family = sa_family_t(self.dynamicType.addressFamily)
|
||||
self.sin_len = UInt8(self.dynamicType.size)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension SockAddr {
|
||||
// func connect() {
|
||||
// AF_INET
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
let defaultBacklog: Int32 = 5
|
||||
|
||||
// Wrapper around a raw socket. Can do ipv6 or ipv4
|
||||
public class Socket<T: SockAddr> {
|
||||
var addr = T()
|
||||
|
||||
var fd: dispatch_fd_t = -1
|
||||
|
||||
// Initializes as a nonblocking socket and starts listening. DOes not create a dispatch source
|
||||
public init(address: Address, port: UInt16) throws {
|
||||
fd = socket(T.addressFamily, SOCK_STREAM, IPPROTO_TCP)
|
||||
try Error.throwIfNotSuccessLessThan0(fd)
|
||||
do {
|
||||
let flags = shim_fcntl(fd, F_GETFL, 0);
|
||||
try Error.throwIfNotSuccessLessThan0(shim_fcntl(fd, F_SETFL, flags | O_NONBLOCK))
|
||||
|
||||
|
||||
var val: Int32 = 1;
|
||||
|
||||
try Error.throwIfNotSuccessLessThan0(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, socklen_t(sizeofValue(val))))
|
||||
|
||||
try Error.throwIfNotSuccessLessThan0(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, socklen_t(sizeofValue(val))))
|
||||
|
||||
try addr.setup(address, listenPort: port)
|
||||
|
||||
try Error.throwIfNotSuccessLessThan0(shim_bind(fd, &addr, addr.dynamicType.size))
|
||||
|
||||
try Error.throwIfNotSuccessLessThan0(listen(fd, defaultBacklog))
|
||||
} catch let e {
|
||||
close(fd)
|
||||
self.fd = -1
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
/// Cancel handler isn't guaranteed to dispatch on a specific queue
|
||||
/// Returns a block that starts the cancel
|
||||
public func startAccepting(workQueue: Queue, cancelHandler: () -> Void, acceptHandler:(fd: dispatch_fd_t) -> Void) -> (() -> Void) {
|
||||
precondition(fd >= 0)
|
||||
|
||||
let eventSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, UInt(fd), 0, workQueue.queue)
|
||||
dispatch_source_set_event_handler(eventSource) {
|
||||
var remoteAddress = T()
|
||||
var len = socklen_t(remoteAddress.dynamicType.size);
|
||||
|
||||
let native: dispatch_fd_t = shim_accept(self.fd, &remoteAddress, &len)
|
||||
if native == -1 {
|
||||
return;
|
||||
}
|
||||
|
||||
acceptHandler(fd: native)
|
||||
}
|
||||
|
||||
dispatch_source_set_cancel_handler(eventSource) {
|
||||
precondition(self.fd >= 0)
|
||||
close(self.fd);
|
||||
self.fd = -1
|
||||
|
||||
cancelHandler()
|
||||
}
|
||||
|
||||
dispatch_resume(eventSource);
|
||||
|
||||
return {
|
||||
dispatch_source_cancel(eventSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension dispatch_data_t {
|
||||
var empty: Bool {
|
||||
get {
|
||||
return dispatch_data_empty === self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var hints: addrinfo = {
|
||||
var hints = addrinfo()
|
||||
hints.ai_family = PF_UNSPEC
|
||||
hints.ai_socktype = SOCK_STREAM
|
||||
return hints
|
||||
}()
|
||||
|
||||
|
||||
extension Queue {
|
||||
/// Used to dispatch synchronous operations on a specific queue
|
||||
func blockingPromise<T>(blockingFn: () throws -> T) -> Promise<T> {
|
||||
let (r, p) = Promise<T>.resolver()
|
||||
|
||||
self.dispatchAsync {
|
||||
r.attemptResolve { return try blockingFn()}
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
public func getaddrinfoAsync(hostname: String, servname: String, workQueue: Queue = Queue.defaultGlobalQueue) -> Promise<[addrinfo]> {
|
||||
return workQueue.blockingPromise {
|
||||
var ai: UnsafeMutablePointer<addrinfo> = nil
|
||||
defer {
|
||||
if ai != nil {
|
||||
freeaddrinfo(ai)
|
||||
}
|
||||
}
|
||||
|
||||
// ai won't be set if it doesn't work
|
||||
// TODO(lewis) propagate error up
|
||||
try Error.throwIfNotSuccess(getaddrinfo(hostname, servname, &hints, &ai))
|
||||
var ret = [addrinfo]()
|
||||
|
||||
var curAi = ai
|
||||
while curAi != nil {
|
||||
var mutatedAI = curAi.memory
|
||||
let next = mutatedAI.ai_next
|
||||
mutatedAI.ai_next = nil
|
||||
ret.append(mutatedAI)
|
||||
curAi = next
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
34
SocketRocketIO/Lock.swift
Normal file
34
SocketRocketIO/Lock.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// Lock.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 8/3/15.
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
|
||||
protocol Lock {
|
||||
mutating func lock()
|
||||
mutating func unlock()
|
||||
}
|
||||
|
||||
extension Lock {
|
||||
mutating func withLock<Result>(@noescape fn: () throws -> Result) rethrows -> Result {
|
||||
lock()
|
||||
defer {
|
||||
unlock()
|
||||
}
|
||||
return try fn()
|
||||
}
|
||||
}
|
||||
|
||||
extension OSSpinLock: Lock {
|
||||
mutating func lock() {
|
||||
withUnsafeMutablePointer(&self, OSSpinLockLock)
|
||||
}
|
||||
|
||||
mutating func unlock() {
|
||||
withUnsafeMutablePointer(&self, OSSpinLockUnlock)
|
||||
}
|
||||
}
|
||||
188
SocketRocketIO/Promise.swift
Normal file
188
SocketRocketIO/Promise.swift
Normal file
@ -0,0 +1,188 @@
|
||||
//
|
||||
// Promise.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 8/3/15.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
// Only part of a promise protocol. It defines that it can be terminated
|
||||
|
||||
|
||||
// Represents a return type that can either be a promise or a value
|
||||
public enum PromiseOrValue<V> {
|
||||
public typealias P = Promise<V>
|
||||
|
||||
case Promised(P)
|
||||
case Value(P.ET)
|
||||
|
||||
static func of<V>(val: V) -> PromiseOrValue<V> {
|
||||
return .Value(ErrorOptional<V>(val))
|
||||
}
|
||||
}
|
||||
|
||||
func wrap<T>(queue: Queue?, fn:(T) -> ()) -> (T) -> () {
|
||||
if let q = queue {
|
||||
return q.wrap(fn)
|
||||
}
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
extension Queue {
|
||||
/// wraps a void function and returns a new one. When
|
||||
/// the new one is called it will be dispatched on the sender
|
||||
func wrap<T>(fn:(T) -> ()) -> (T) -> () {
|
||||
return { v in
|
||||
self.dispatchAsync {
|
||||
fn(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct Resolver<T> {
|
||||
public typealias ET = ErrorOptional<T>
|
||||
|
||||
typealias P = Promise<T>
|
||||
private let promise: P
|
||||
|
||||
init(promise: P) {
|
||||
self.promise = promise
|
||||
}
|
||||
|
||||
public func resolve(value: T) {
|
||||
fulfill(ET(value))
|
||||
}
|
||||
|
||||
public func reject(error: ErrorType) {
|
||||
fulfill(ET(error))
|
||||
}
|
||||
|
||||
/// resolves with the return value. Otherwise if it throws, it will return as an error type
|
||||
public func attemptResolve(block: () throws -> T) {
|
||||
fulfill(ET.attempt { return try block() })
|
||||
}
|
||||
|
||||
public func fulfill(v: ET) {
|
||||
promise.fulfill(v)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Useful for stuff that can only succeed and not return any errors
|
||||
public typealias VoidPromiseType = Promise<Void>
|
||||
public typealias VoidResolverType = Resolver<Void>
|
||||
|
||||
public class Promise<T> {
|
||||
/// Error optional type
|
||||
public typealias ET = ErrorOptional<T>
|
||||
|
||||
typealias PG = PGroup<T>
|
||||
|
||||
var pgroup: PG
|
||||
|
||||
typealias PV = PromiseOrValue<T>
|
||||
|
||||
typealias ValueType = T
|
||||
|
||||
private init(pgroup: PG) {
|
||||
self.pgroup = pgroup
|
||||
}
|
||||
|
||||
public class func resolve(value: T) -> Promise<T> {
|
||||
return Promise<T>(pgroup: PGroup<T>.resolve(value))
|
||||
}
|
||||
|
||||
public class func reject(error: ErrorType) -> Promise<T> {
|
||||
return Promise<T>(pgroup: PGroup<T>.reject(error))
|
||||
}
|
||||
|
||||
// Returns a promise and the resolver for it
|
||||
public class func resolver() -> (Resolver<T>, Promise<T>) {
|
||||
let p = Promise<T>()
|
||||
let r = Resolver(promise: p)
|
||||
return (r, p)
|
||||
}
|
||||
|
||||
// An uninitialized one
|
||||
init() {
|
||||
pgroup = PG()
|
||||
}
|
||||
|
||||
// splits the call based one rror or success
|
||||
public func thenSplit<R>(queue: Queue? = nil, error: ((ErrorType) -> ())? = nil, success: T -> PromiseOrValue<R>) -> Promise<R> {
|
||||
return self.then { (r:ET) -> PromiseOrValue<R> in
|
||||
switch r {
|
||||
case let .Error(e):
|
||||
error?(e)
|
||||
// TODO: improve this .Not very efficient
|
||||
return PromiseOrValue.Value(Promise<R>.ET(e))
|
||||
case let .Some(val):
|
||||
return success(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func then<R>(queue: Queue? = nil, handler: ET -> PromiseOrValue<R>) -> Promise<R> {
|
||||
let (r, p) = Promise<R>.resolver()
|
||||
|
||||
self.then(queue) { v -> Void in
|
||||
switch handler(v) {
|
||||
case let .Promised(prom):
|
||||
prom.then(handler: r.fulfill)
|
||||
case let .Value(val):
|
||||
r.fulfill(val)
|
||||
}
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
/// Terminating
|
||||
func then(queue: Queue? = nil, handler: ET -> Void) {
|
||||
self.pgroup.then(wrap(queue, fn: handler))
|
||||
}
|
||||
|
||||
public func then<R>(queue: Queue? = nil, handler: ET -> Promise<R>.ET) -> Promise<R> {
|
||||
let (r, p) = Promise<R>.resolver()
|
||||
|
||||
self.then(queue) { v -> Void in
|
||||
r.fulfill(handler(v))
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
/// Catches an error and propagates it in the optitional
|
||||
///
|
||||
/// Callers of this should use the .checkedGet on the input type to make it easy to propagate errors
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// p.thenChecked{ v throws in
|
||||
/// return try v.checkedGet() + 3
|
||||
/// }
|
||||
public func thenChecked<R>(queue: Queue? = nil, handler: ET throws -> R) -> Promise<R> {
|
||||
typealias RP = Promise<R>
|
||||
typealias RET = RP.ET
|
||||
|
||||
return self.then(queue) { val -> ErrorOptional<R> in
|
||||
do {
|
||||
return RET(try handler(val))
|
||||
} catch let e {
|
||||
return RET(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func fulfill(value: ET) {
|
||||
pgroup.fulfill(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
58
SocketRocketIO/Queue.swift
Normal file
58
SocketRocketIO/Queue.swift
Normal file
@ -0,0 +1,58 @@
|
||||
//
|
||||
// Queue.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 7/30/15.
|
||||
//
|
||||
// Wrappers areound dispatch_queue_t
|
||||
|
||||
import Foundation
|
||||
import Dispatch
|
||||
|
||||
/// Simple wrapper around dispatch_queue_t
|
||||
public class Queue {
|
||||
let queue: dispatch_queue_t
|
||||
|
||||
// used to check current queue
|
||||
private var ctxKeyValue: UnsafeMutablePointer<Void> = nil
|
||||
|
||||
/// same as dispatch_get_main_queue()
|
||||
public static let mainQueue = Queue(queue: dispatch_get_main_queue())
|
||||
|
||||
public static let defaultGlobalQueue = Queue(queue: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))
|
||||
|
||||
public required init(queue: dispatch_queue_t) {
|
||||
self.queue = queue
|
||||
|
||||
withUnsafeMutablePointer(&self.ctxKeyValue) { ptr in
|
||||
self.ctxKeyValue = unsafeBitCast(ptr, UnsafeMutablePointer<Void>.self);
|
||||
}
|
||||
|
||||
let destructor: dispatch_function_t! = nil
|
||||
dispatch_queue_set_specific(self.queue, ctxKeyValue, ctxKeyValue, destructor)
|
||||
}
|
||||
|
||||
public convenience init(label: String, attr: dispatch_queue_attr_t? = DISPATCH_QUEUE_SERIAL) {
|
||||
self.init(queue: dispatch_queue_create(label, attr))
|
||||
}
|
||||
|
||||
deinit {
|
||||
let destructor: dispatch_function_t! = nil
|
||||
dispatch_queue_set_specific(self.queue, ctxKeyValue, nil, destructor)
|
||||
}
|
||||
|
||||
public func dispatchAsync(block: () -> Void) {
|
||||
dispatch_async(queue, block)
|
||||
}
|
||||
|
||||
/// Returns true if we're on the current queue
|
||||
public func isCurrentQueue() -> Bool {
|
||||
let currentContext = dispatch_get_specific(ctxKeyValue)
|
||||
return currentContext == ctxKeyValue
|
||||
}
|
||||
|
||||
public func checkIsCurrentQueue() {
|
||||
precondition(isCurrentQueue(), "Expected to be current queue")
|
||||
}
|
||||
}
|
||||
|
||||
18
SocketRocketIO/SocketRocketIO.h
Normal file
18
SocketRocketIO/SocketRocketIO.h
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// SocketRocketIO.h
|
||||
// SocketRocketIO
|
||||
//
|
||||
// Created by Mike Lewis on 7/30/15.
|
||||
//
|
||||
//
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//! Project version number for SocketRocketIO.
|
||||
FOUNDATION_EXPORT double SocketRocketIOVersionNumber;
|
||||
|
||||
//! Project version string for SocketRocketIO.
|
||||
FOUNDATION_EXPORT const unsigned char SocketRocketIOVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <SocketRocketIO/PublicHeader.h>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user