Compare commits
9 Commits
master
...
signal-ios
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
587ad297eb | ||
|
|
d235027f7a | ||
|
|
676f8f9b55 | ||
|
|
5a26ea8bbf | ||
|
|
a319238e62 | ||
|
|
066a3c0fbb | ||
|
|
bf0414499b | ||
|
|
d6d9121183 | ||
|
|
39df52b56c |
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
|
||||
57
.travis.yml
57
.travis.yml
@ -2,42 +2,29 @@ branches:
|
||||
only:
|
||||
- master
|
||||
language: objective-c
|
||||
os: osx
|
||||
osx_image: xcode7.3
|
||||
sudo: false
|
||||
xcode_project: SocketRocket.xcodeproj
|
||||
env:
|
||||
global:
|
||||
- IOS_SDK=iphonesimulator9.3
|
||||
- IOS_SCHEME="SocketRocket-iOS"
|
||||
- MACOS_SDK=macosx10.11
|
||||
- MACOS_SCHEME="SocketRocketOSX"
|
||||
- TVOS_SDK=appletvsimulator9.2
|
||||
- TVOS_SCHEME="SocketRocket-tvOS"
|
||||
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
|
||||
- DESTINATION="OS=9.3,name=iPad 2" SDK="$IOS_SDK" SCHEME="$IOS_SCHEME"
|
||||
- DESTINATION="OS=9.3,name=iPad Air" SDK="$IOS_SDK" SCHEME="$IOS_SCHEME"
|
||||
- DESTINATION="OS=9.3,name=iPhone 5" SDK="$IOS_SDK" SCHEME="$IOS_SCHEME"
|
||||
- DESTINATION="OS=9.3,name=iPhone 6s" SDK="$IOS_SDK" SCHEME="$IOS_SCHEME"
|
||||
- DESTINATION="OS=8.4,name=iPhone 6" SDK="$IOS_SDK" SCHEME="$IOS_SCHEME"
|
||||
- DESTINATION="OS=8.4,name=iPad Air" SDK="$IOS_SDK" SCHEME="$IOS_SCHEME"
|
||||
- DESTINATION="OS=9.2,name=Apple TV 1080p" SDK="$TVOS_SDK" SCHEME="$TVOS_SCHEME"
|
||||
- DESTINATION="platform=OS X" SDK="$MACOS_SDK" SCHEME="$MACOS_SCHEME"
|
||||
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 -version
|
||||
- xcodebuild -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug -PBXBuildsContinueAfterErrors=0 ACTIVE_ARCH_ONLY=0 build test | xcpretty -tc
|
||||
- pod lib lint --verbose --fail-fast
|
||||
|
||||
@ -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
|
||||
@ -1,5 +1,9 @@
|
||||
# Contributing to SocketRocket
|
||||
We want to make contributing to this project as easy and transparent as possible.
|
||||
We want to make contributing to this project as easy and transparent as
|
||||
possible.
|
||||
|
||||
## Our Development Process
|
||||
... (in particular how this is synced with internal changes to the project)
|
||||
|
||||
## Pull Requests
|
||||
We actively welcome your pull requests.
|
||||
@ -12,8 +16,8 @@ We actively welcome your pull requests.
|
||||
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.
|
||||
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>
|
||||
|
||||
@ -30,4 +34,5 @@ outlined on that page and do not file a public issue.
|
||||
* 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.
|
||||
By contributing to SocketRocket, you agree that your contributions will be licensed
|
||||
under its BSD license.
|
||||
3
Gemfile
3
Gemfile
@ -1,4 +1,3 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'cocoapods'
|
||||
gem 'xcpretty'
|
||||
gem 'cocoapods', '0.39.0'
|
||||
|
||||
16
Makefile
16
Makefile
@ -7,24 +7,20 @@ all:
|
||||
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
|
||||
|
||||
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
|
||||
246
README.rst
Normal file
246
README.rst
Normal file
@ -0,0 +1,246 @@
|
||||
SocketRocket Objective-C WebSocket Client (beta)
|
||||
================================================
|
||||
A conforming WebSocket (`RFC 6455 <https://tools.ietf.org/html/rfc6455>`_)
|
||||
client library.
|
||||
|
||||
`Test results for SocketRocket here <http://square.github.io/SocketRocket/results/>`_.
|
||||
You can compare to what `modern browsers look like here
|
||||
<http://tavendo.com/autobahn/testsuite/report/clients/index.html>`_.
|
||||
|
||||
SocketRocket currently conforms to all ~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. 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 <https://pypi.python.org/pypi/virtualenv>`_.
|
||||
Now, in your terminal::
|
||||
|
||||
source .env/bin/activate
|
||||
pip install git+https://github.com/tornadoweb/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/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://tavendo.com/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
|
||||
------------
|
||||
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.
|
||||
@ -1,15 +1,15 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'SocketRocket'
|
||||
s.name = "SocketRocket"
|
||||
s.version = '0.5.1'
|
||||
s.summary = 'A conforming WebSocket (RFC 6455) client library for iOS, macOS and tvOS.'
|
||||
s.summary = 'A conforming WebSocket (RFC 6455) client library.'
|
||||
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.authors = 'Square'
|
||||
s.license = 'BSD'
|
||||
s.source = { :git => 'https://github.com/facebook/SocketRocket.git', :tag => s.version.to_s }
|
||||
s.requires_arc = true
|
||||
|
||||
s.source_files = 'SocketRocket/**/*.{h,m}'
|
||||
s.public_header_files = 'SocketRocket/*.h'
|
||||
s.public_header_files = 'SocketRocket/*.h'
|
||||
|
||||
s.ios.deployment_target = '6.0'
|
||||
s.osx.deployment_target = '10.8'
|
||||
@ -18,5 +18,6 @@ Pod::Spec.new do |s|
|
||||
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
@ -16,7 +16,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F668C87F153E91210044DBAC"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-macOS"
|
||||
BlueprintName = "SocketRocket-OSX"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
@ -47,7 +47,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F668C87F153E91210044DBAC"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-macOS"
|
||||
BlueprintName = "SocketRocket-OSX"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
@ -65,7 +65,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F668C87F153E91210044DBAC"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-macOS"
|
||||
BlueprintName = "SocketRocket-OSX"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
@ -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 = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS-Dynamic"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<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 = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS-Dynamic"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS-Dynamic"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
version = "1.7">
|
||||
LastUpgradeVersion = "0720"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@ -14,26 +14,12 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "NO"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
@ -41,63 +27,8 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<PreActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Script"
|
||||
scriptText = "PIDFILE=$TMPDIR/sr_test_server.pid if [ -r $PIDFILE ]; then EXISTING_PID=`cat $PIDFILE` echo "Killing Dangling Autobahn Server PID:" $EXISTING_PID kill $EXISTING_PID || true rm $PIDFILE fi pushd $PROJECT_DIR source .env/bin/activate nohup ./TestSupport/run_test_server.sh & echo $! > $PIDFILE popd ">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PreActions>
|
||||
<PostActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Script"
|
||||
scriptText = "PIDFILE=$TMPDIR/sr_test_server.pid if [ -r $PIDFILE ]; then EXISTING_PID=`cat $PIDFILE` echo "Killing SR TestServer PID:" $EXISTING_PID kill $EXISTING_PID rm $PIDFILE fi ">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PostActions>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SocketRocketTests-iOS.xctest"
|
||||
BlueprintName = "SocketRocketTests-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
@ -114,7 +45,7 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
@ -132,7 +63,7 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "81D6475F1D2CA78800690609"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0720"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -34,7 +34,7 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2D4227611BB4358C000C1A6C"
|
||||
BuildableName = "SocketRocket.framework"
|
||||
BlueprintName = "SocketRocket-iOS-Dynamic"
|
||||
BlueprintName = "SocketRocket-iOS"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
|
||||
@ -0,0 +1,169 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0720"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6B2082C1450F597009315AF"
|
||||
BuildableName = "libSocketRocket.a"
|
||||
BlueprintName = "SocketRocket"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "NO"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "NO">
|
||||
<PreActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Script"
|
||||
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 = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PreActions>
|
||||
<PostActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Script"
|
||||
scriptText = "PIDFILE=$TMPDIR/srtharness.pid if [ -r $PIDFILE ]; then EXISTING_PID=`cat $PIDFILE` echo "Killing SRTestharness PID:" $EXISTING_PID kill $EXISTING_PID rm $PIDFILE fi open $PROJECT_DIR/pages/results/index.html "
|
||||
shellToInvoke = "/bin/bash">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PostActions>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F6BDA801145900D200FE3253"
|
||||
BuildableName = "SRWebSocketTests.xctest"
|
||||
BlueprintName = "SRWebSocketTests"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
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>
|
||||
<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 = "F6B2082C1450F597009315AF"
|
||||
BuildableName = "libSocketRocket.a"
|
||||
BlueprintName = "SocketRocket"
|
||||
ReferencedContainer = "container:SocketRocket.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
<AdditionalOption
|
||||
key = "NSZombieEnabled"
|
||||
value = "YES"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
key = "OBJC_PRINT_EXCEPTIONS"
|
||||
value = "YES"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
key = "MallocGuardEdges"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
key = "MallocScribble"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
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 = "0720"
|
||||
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 = "0720"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@ -9,12 +9,10 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <SocketRocket/SRWebSocket.h>
|
||||
#import "SRWebSocket.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#if OBJC_BOOL_IS_BOOL
|
||||
|
||||
struct SRDelegateAvailableMethods {
|
||||
BOOL didReceiveMessage : 1;
|
||||
BOOL didReceiveMessageWithString : 1;
|
||||
@ -22,27 +20,9 @@ struct SRDelegateAvailableMethods {
|
||||
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);
|
||||
|
||||
@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property (nonatomic, strong, readonly) dispatch_queue_t accessQueue;
|
||||
|
||||
@property (atomic, assign, readwrite) SRDelegateAvailableMethods availableDelegateMethods;
|
||||
@property (atomic, readwrite) SRDelegateAvailableMethods availableDelegateMethods;
|
||||
|
||||
@end
|
||||
|
||||
@ -56,7 +56,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
.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:)]
|
||||
};
|
||||
|
||||
@ -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);
|
||||
@ -8,13 +8,12 @@
|
||||
//
|
||||
|
||||
#import "SRProxyConnect.h"
|
||||
|
||||
#import "NSRunLoop+SRWebSocket.h"
|
||||
#import "SRConstants.h"
|
||||
#import "SRError.h"
|
||||
#import "SRLog.h"
|
||||
#import "NSRunLoop+SRWebSocket.h"
|
||||
#import "SRURLUtilities.h"
|
||||
|
||||
static inline void SRProxyFastLog(NSString *format, ...);
|
||||
|
||||
@interface SRProxyConnect() <NSStreamDelegate>
|
||||
|
||||
@property (nonatomic, strong) NSURL *url;
|
||||
@ -40,7 +39,7 @@
|
||||
BOOL _connectionRequiresSSL;
|
||||
|
||||
NSMutableArray<NSData *> *_inputQueue;
|
||||
dispatch_queue_t _writeQueue;
|
||||
NSOperationQueue * _writeQueue;
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
@ -55,26 +54,12 @@
|
||||
_url = url;
|
||||
_connectionRequiresSSL = SRURLRequiresSSL(url);
|
||||
|
||||
_writeQueue = dispatch_queue_create("com.facebook.socketrocket.proxyconnect.write", DISPATCH_QUEUE_SERIAL);
|
||||
_writeQueue = [[NSOperationQueue alloc] init];
|
||||
_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
|
||||
///--------------------------------------
|
||||
@ -91,11 +76,11 @@
|
||||
|
||||
- (void)_didConnect
|
||||
{
|
||||
SRDebugLog(@"_didConnect, return streams");
|
||||
SRProxyFastLog(@"_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);
|
||||
SRProxyFastLog(@"proxy set peer name to real host %@", self.url.host);
|
||||
[self.outputStream setProperty:self.url.host forKey:@"_kCFStreamPropertySocketPeerName"];
|
||||
}
|
||||
}
|
||||
@ -119,7 +104,7 @@
|
||||
|
||||
- (void)_failWithError:(NSError *)error
|
||||
{
|
||||
SRDebugLog(@"_failWithError, return error");
|
||||
SRProxyFastLog(@"_failWithError, return error");
|
||||
if (!error) {
|
||||
error = SRHTTPErrorWithCodeDescription(500, 2132,@"Proxy Error");
|
||||
}
|
||||
@ -128,10 +113,6 @@
|
||||
CFRelease(_receivedHTTPHeaders);
|
||||
_receivedHTTPHeaders = NULL;
|
||||
}
|
||||
|
||||
self.inputStream.delegate = nil;
|
||||
self.outputStream.delegate = nil;
|
||||
|
||||
[self.inputStream removeFromRunLoop:[NSRunLoop SR_networkRunLoop]
|
||||
forMode:NSDefaultRunLoopMode];
|
||||
[self.inputStream close];
|
||||
@ -144,7 +125,7 @@
|
||||
// get proxy setting from device setting
|
||||
- (void)_configureProxy
|
||||
{
|
||||
SRDebugLog(@"configureProxy");
|
||||
SRProxyFastLog(@"configureProxy");
|
||||
NSDictionary *proxySettings = CFBridgingRelease(CFNetworkCopySystemProxySettings());
|
||||
|
||||
// CFNetworkCopyProxiesForURL doesn't understand ws:// or wss://
|
||||
@ -157,7 +138,7 @@
|
||||
|
||||
NSArray *proxies = CFBridgingRelease(CFNetworkCopyProxiesForURL((__bridge CFURLRef)httpURL, (__bridge CFDictionaryRef)proxySettings));
|
||||
if (proxies.count == 0) {
|
||||
SRDebugLog(@"configureProxy no proxies");
|
||||
SRProxyFastLog(@"configureProxy no proxies");
|
||||
[self _openConnection];
|
||||
return; // no proxy
|
||||
}
|
||||
@ -166,14 +147,14 @@
|
||||
if ([proxyType isEqualToString:(NSString *)kCFProxyTypeAutoConfigurationURL]) {
|
||||
NSURL *pacURL = settings[(NSString *)kCFProxyAutoConfigurationURLKey];
|
||||
if (pacURL) {
|
||||
[self _fetchPAC:pacURL withProxySettings:proxySettings];
|
||||
[self _fetchPAC:pacURL];
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ([proxyType isEqualToString:(__bridge NSString *)kCFProxyTypeAutoConfigurationJavaScript]) {
|
||||
NSString *script = settings[(__bridge NSString *)kCFProxyAutoConfigurationJavaScriptKey];
|
||||
if (script) {
|
||||
[self _runPACScript:script withProxySettings:proxySettings];
|
||||
[self _runPACScript:script];
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -188,9 +169,8 @@
|
||||
[proxyType isEqualToString:(NSString *)kCFProxyTypeHTTPS]) {
|
||||
_httpProxyHost = settings[(NSString *)kCFProxyHostNameKey];
|
||||
NSNumber *portValue = settings[(NSString *)kCFProxyPortNumberKey];
|
||||
if (portValue) {
|
||||
if (portValue)
|
||||
_httpProxyPort = [portValue intValue];
|
||||
}
|
||||
}
|
||||
if ([proxyType isEqualToString:(NSString *)kCFProxyTypeSOCKS]) {
|
||||
_socksProxyHost = settings[(NSString *)kCFProxyHostNameKey];
|
||||
@ -201,17 +181,17 @@
|
||||
_socksProxyPassword = settings[(NSString *)kCFProxyPasswordKey];
|
||||
}
|
||||
if (_httpProxyHost) {
|
||||
SRDebugLog(@"Using http proxy %@:%u", _httpProxyHost, _httpProxyPort);
|
||||
SRProxyFastLog(@"Using http proxy %@:%u", _httpProxyHost, _httpProxyPort);
|
||||
} else if (_socksProxyHost) {
|
||||
SRDebugLog(@"Using socks proxy %@:%u", _socksProxyHost, _socksProxyPort);
|
||||
SRProxyFastLog(@"Using socks proxy %@:%u", _socksProxyHost, _socksProxyPort);
|
||||
} else {
|
||||
SRDebugLog(@"configureProxy no proxies");
|
||||
SRProxyFastLog(@"configureProxy no proxies");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_fetchPAC:(NSURL *)PACurl withProxySettings:(NSDictionary *)proxySettings
|
||||
- (void)_fetchPAC:(NSURL *)PACurl
|
||||
{
|
||||
SRDebugLog(@"SRWebSocket fetchPAC:%@", PACurl);
|
||||
SRProxyFastLog(@"SRWebSocket fetchPAC:%@", PACurl);
|
||||
|
||||
if ([PACurl isFileURL]) {
|
||||
NSError *error = nil;
|
||||
@ -222,7 +202,7 @@
|
||||
if (error) {
|
||||
[self _openConnection];
|
||||
} else {
|
||||
[self _runPACScript:script withProxySettings:proxySettings];
|
||||
[self _runPACScript:script];
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -234,32 +214,36 @@
|
||||
[self _openConnection];
|
||||
return;
|
||||
}
|
||||
__weak typeof(self) wself = self;
|
||||
__weak typeof(self) weakSelf = 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];
|
||||
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
|
||||
completionHandler:
|
||||
^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (!error) {
|
||||
NSString* script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
[weakSelf _runPACScript:script];
|
||||
} else {
|
||||
[weakSelf _openConnection];
|
||||
}
|
||||
|
||||
}];
|
||||
[task resume];
|
||||
}
|
||||
|
||||
- (void)_runPACScript:(NSString *)script withProxySettings:(NSDictionary *)proxySettings
|
||||
- (void)_runPACScript:(NSString *)script
|
||||
{
|
||||
if (!script) {
|
||||
[self _openConnection];
|
||||
return;
|
||||
}
|
||||
SRDebugLog(@"runPACScript");
|
||||
SRProxyFastLog(@"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));
|
||||
NSDictionary *empty = nil;
|
||||
CFBridgingRelease(CFNetworkCopyProxiesForURL((__bridge CFURLRef)_url, (__bridge CFDictionaryRef)empty));
|
||||
|
||||
// Obtain the list of proxies by running the autoconfiguration script
|
||||
CFErrorRef err = NULL;
|
||||
@ -309,14 +293,14 @@
|
||||
CFReadStreamRef readStream = NULL;
|
||||
CFWriteStreamRef writeStream = NULL;
|
||||
|
||||
SRDebugLog(@"ProxyConnect connect stream to %@:%u", host, port);
|
||||
SRProxyFastLog(@"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);
|
||||
SRProxyFastLog(@"ProxyConnect set sock property stream to %@:%u user %@ password %@", _socksProxyHost, _socksProxyPort, _socksProxyUsername, _socksProxyPassword);
|
||||
NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:4];
|
||||
settings[NSStreamSOCKSProxyHostKey] = _socksProxyHost;
|
||||
if (_socksProxyPort) {
|
||||
@ -337,7 +321,7 @@
|
||||
|
||||
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;
|
||||
{
|
||||
SRDebugLog(@"stream handleEvent %u", eventCode);
|
||||
SRProxyFastLog(@"stream handleEvent %u", eventCode);
|
||||
switch (eventCode) {
|
||||
case NSStreamEventOpenCompleted: {
|
||||
if (aStream == self.inputStream) {
|
||||
@ -359,16 +343,15 @@
|
||||
[self _processInputStream];
|
||||
}
|
||||
} break;
|
||||
case NSStreamEventHasSpaceAvailable:
|
||||
case NSStreamEventNone:
|
||||
SRDebugLog(@"(default) %@", aStream);
|
||||
default:
|
||||
SRProxyFastLog(@"(default) %@", aStream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_proxyDidConnect
|
||||
{
|
||||
SRDebugLog(@"Proxy Connected");
|
||||
SRProxyFastLog(@"Proxy Connected");
|
||||
uint32_t port = _url.port.unsignedIntValue;
|
||||
if (port == 0) {
|
||||
port = (_connectionRequiresSSL ? 443 : 80);
|
||||
@ -377,17 +360,19 @@
|
||||
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);
|
||||
SRProxyFastLog(@"Proxy sending %@", connectRequestStr);
|
||||
|
||||
[self _writeData:message];
|
||||
}
|
||||
|
||||
static size_t const SRProxyConnectBufferMaxSize = 4096;
|
||||
|
||||
///handles the incoming bytes and sending them to the proper processing method
|
||||
- (void)_processInputStream
|
||||
{
|
||||
NSMutableData *buf = [NSMutableData dataWithCapacity:SRDefaultBufferSize()];
|
||||
NSMutableData *buf = [NSMutableData dataWithCapacity:SRProxyConnectBufferMaxSize];
|
||||
uint8_t *buffer = buf.mutableBytes;
|
||||
NSInteger length = [_inputStream read:buffer maxLength:SRDefaultBufferSize()];
|
||||
NSInteger length = [_inputStream read:buffer maxLength:SRProxyConnectBufferMaxSize];
|
||||
|
||||
if (length <= 0) {
|
||||
return;
|
||||
@ -406,17 +391,13 @@
|
||||
- (void)_dequeueInput
|
||||
{
|
||||
while (_inputQueue.count > 0) {
|
||||
NSData *data = _inputQueue.firstObject;
|
||||
NSData * data = _inputQueue[0];
|
||||
[self _proxyProcessHTTPResponseWithData:data];
|
||||
[_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
|
||||
- (void)_proxyProcessHTTPResponseWithData:(NSData *)data
|
||||
{
|
||||
if (_receivedHTTPHeaders == NULL) {
|
||||
_receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO);
|
||||
@ -424,12 +405,9 @@
|
||||
|
||||
CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);
|
||||
if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) {
|
||||
SRDebugLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders)));
|
||||
SRProxyFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders)));
|
||||
[self _proxyHTTPHeadersDidFinish];
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)_proxyHTTPHeadersDidFinish
|
||||
@ -437,14 +415,14 @@
|
||||
NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders);
|
||||
|
||||
if (responseCode >= 299) {
|
||||
SRDebugLog(@"Connect to Proxy Request failed with response code %d", responseCode);
|
||||
SRProxyFastLog(@"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);
|
||||
SRProxyFastLog(@"proxy connect return %d, call socket connect", responseCode);
|
||||
[self _didConnect];
|
||||
}
|
||||
|
||||
@ -453,14 +431,13 @@ 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
|
||||
__block NSInteger timeout = SRProxyConnectWriteTimeout * 1000000; // wait timeout before giving up
|
||||
__weak typeof(self) wself = self;
|
||||
dispatch_async(_writeQueue, ^{
|
||||
__strong typeof(wself) sself = self;
|
||||
if (!sself) {
|
||||
[_writeQueue addOperationWithBlock:^() {
|
||||
if (!wself) {
|
||||
return;
|
||||
}
|
||||
NSOutputStream *outStream = sself.outputStream;
|
||||
NSOutputStream *outStream = wself.outputStream;
|
||||
if (!outStream) {
|
||||
return;
|
||||
}
|
||||
@ -469,13 +446,27 @@ static NSTimeInterval const SRProxyConnectWriteTimeout = 5.0;
|
||||
timeout -= 100;
|
||||
if (timeout < 0) {
|
||||
NSError *error = SRHTTPErrorWithCodeDescription(408, 2132, @"Proxy timeout");
|
||||
[sself _failWithError:error];
|
||||
[self _failWithError:error];
|
||||
} else if (outStream.streamError != nil) {
|
||||
[sself _failWithError:outStream.streamError];
|
||||
[self _failWithError:outStream.streamError];
|
||||
}
|
||||
}
|
||||
[outStream write:bytes maxLength:data.length];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//#define SRPROXY_ENABLE_LOG
|
||||
|
||||
static inline void SRProxyFastLog(NSString *format, ...) {
|
||||
#ifdef SRPROXY_ENABLE_LOG
|
||||
__block va_list arg_list;
|
||||
va_start (arg_list, format);
|
||||
|
||||
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
|
||||
|
||||
va_end(arg_list);
|
||||
|
||||
NSLog(@"[Proxy] %@", formattedString);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -12,15 +12,21 @@
|
||||
#import "SRRunLoopThread.h"
|
||||
|
||||
@interface SRRunLoopThread ()
|
||||
{
|
||||
dispatch_group_t _waitGroup;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong, readwrite) NSRunLoop *runLoop;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SRRunLoopThread
|
||||
@implementation SRRunLoopThread {
|
||||
dispatch_group_t _waitGroup;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||
dispatch_release(_waitGroup);
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (instancetype)sharedThread
|
||||
{
|
||||
|
||||
@ -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
|
||||
@ -8,7 +8,6 @@
|
||||
//
|
||||
|
||||
#import "SRError.h"
|
||||
|
||||
#import "SRWebSocket.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -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,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);
|
||||
}
|
||||
@ -20,7 +20,4 @@ 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
|
||||
|
||||
@ -9,8 +9,6 @@
|
||||
|
||||
#import "SRURLUtilities.h"
|
||||
|
||||
#import "SRHash.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *SRURLOrigin(NSURL *url)
|
||||
@ -44,34 +42,16 @@ extern BOOL SRURLRequiresSSL(NSURL *url)
|
||||
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
|
||||
NSString *userAndPasswordBase64Encoded;
|
||||
if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
|
||||
userAndPasswordBase64Encoded = [data base64EncodedStringWithOptions:0];
|
||||
} else {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
userAndPasswordBase64Encoded = [data base64Encoding];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
return networkServiceType;
|
||||
return [NSString stringWithFormat:@"Basic %@", userAndPasswordBase64Encoded];
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
20
SocketRocket/Makefile
Normal file
20
SocketRocket/Makefile
Normal file
@ -0,0 +1,20 @@
|
||||
BINS := SocketRocket.framework libSocketRocket.a
|
||||
|
||||
all: $(BINS)
|
||||
|
||||
HEADERS := SRWebSocket.h
|
||||
SRCS := SRWebSocket.m
|
||||
OBJS := $(SRCS:%.m=%.o)
|
||||
|
||||
CFLAGS += -fobjc-arc
|
||||
|
||||
libSocketRocket.a: $(OBJS)
|
||||
$(AR) -rc $(AFLAGS) $@ $^
|
||||
|
||||
SocketRocket.framework: libSocketRocket.a
|
||||
mkdir -p $@/Headers
|
||||
cp -f $(HEADERS) $@/Headers
|
||||
cp $^ $@/SocketRocket
|
||||
|
||||
clean:
|
||||
rm -r $(OBJS) $(BINS)
|
||||
@ -10,13 +10,9 @@
|
||||
//
|
||||
|
||||
#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
|
||||
|
||||
@ -10,28 +10,19 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "SRSecurityPolicy.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.");
|
||||
@property (nullable, nonatomic, retain, readonly) id<SRSecurityPolicy> SR_securityPolicy;
|
||||
|
||||
@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.");
|
||||
@property (nullable, nonatomic, retain) id<SRSecurityPolicy> SR_securityPolicy;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -10,31 +10,30 @@
|
||||
//
|
||||
|
||||
#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";
|
||||
NSString * const kSRSecurityPolicy = @"SRSecurityPolicy";
|
||||
|
||||
@implementation NSURLRequest (SRWebSocket)
|
||||
|
||||
- (nullable NSArray *)SR_SSLPinnedCertificates
|
||||
- (nullable id<SRSecurityPolicy>)SR_securityPolicy;
|
||||
{
|
||||
return nil;
|
||||
return [NSURLProtocol propertyForKey:kSRSecurityPolicy inRequest:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSMutableURLRequest (SRWebSocket)
|
||||
|
||||
- (void)setSR_SSLPinnedCertificates:(nullable NSArray *)SR_SSLPinnedCertificates
|
||||
- (void)setSR_securityPolicy:(nullable id<SRSecurityPolicy>)securityPolicy
|
||||
{
|
||||
[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."];
|
||||
if (![securityPolicy respondsToSelector:@selector(evaluateServerTrust:forDomain:)]) {
|
||||
@throw [NSException exceptionWithName:@"Assigning security policy failed."
|
||||
reason:@"Trying to assign a security policy that doesn't respond to required selector"
|
||||
userInfo:nil];
|
||||
}
|
||||
[NSURLProtocol setProperty:securityPolicy forKey:kSRSecurityPolicy inRequest:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
//
|
||||
// Copyright (c) 2016-present, Facebook, Inc.
|
||||
// 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
|
||||
@ -8,65 +10,10 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/Security.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@protocol SRSecurityPolicy <NSObject>
|
||||
|
||||
@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;
|
||||
- (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
|
||||
@ -10,6 +10,7 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/SecCertificate.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -20,8 +21,8 @@ typedef NS_ENUM(NSInteger, SRReadyState) {
|
||||
SR_CLOSED = 3,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, SRStatusCode) {
|
||||
// 0-999: Reserved and not used.
|
||||
typedef enum SRStatusCode : NSInteger {
|
||||
// 0–999: Reserved and not used.
|
||||
SRStatusCodeNormal = 1000,
|
||||
SRStatusCodeGoingAway = 1001,
|
||||
SRStatusCodeProtocolError = 1002,
|
||||
@ -38,14 +39,13 @@ typedef NS_ENUM(NSInteger, SRStatusCode) {
|
||||
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.
|
||||
};
|
||||
// 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.
|
||||
@ -91,10 +91,8 @@ extern NSString *const SRHTTPResponseErrorKey;
|
||||
|
||||
/**
|
||||
Current ready state of the socket. Default: `SR_CONNECTING`.
|
||||
|
||||
This property is Key-Value Observable and fully thread-safe.
|
||||
*/
|
||||
@property (atomic, assign, readonly) SRReadyState readyState;
|
||||
@property (nonatomic, assign, readonly) SRReadyState readyState;
|
||||
|
||||
/**
|
||||
An instance of `NSURL` that this socket connects to.
|
||||
@ -133,14 +131,6 @@ extern NSString *const SRHTTPResponseErrorKey;
|
||||
*/
|
||||
- (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.
|
||||
|
||||
@ -157,17 +147,7 @@ extern NSString *const SRHTTPResponseErrorKey;
|
||||
@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;
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
Initializes a web socket with a given `NSURL`.
|
||||
@ -184,14 +164,6 @@ extern NSString *const SRHTTPResponseErrorKey;
|
||||
*/
|
||||
- (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.
|
||||
|
||||
@ -199,9 +171,7 @@ extern NSString *const SRHTTPResponseErrorKey;
|
||||
@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.");
|
||||
- (instancetype)initWithURL:(NSURL *)url protocols:(nullable NSArray<NSString *> *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates;
|
||||
|
||||
/**
|
||||
Unavailable initializer. Please use any other initializer.
|
||||
@ -224,7 +194,7 @@ extern NSString *const SRHTTPResponseErrorKey;
|
||||
@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:));
|
||||
- (void)scheduleInRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode;
|
||||
|
||||
/**
|
||||
Removes the receiver from a given run loop running in a given mode.
|
||||
@ -232,7 +202,7 @@ extern NSString *const SRHTTPResponseErrorKey;
|
||||
@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:));
|
||||
- (void)unscheduleFromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode;
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Open / Close
|
||||
@ -268,55 +238,28 @@ extern NSString *const SRHTTPResponseErrorKey;
|
||||
|
||||
@deprecated Please use `sendString:` or `sendData` instead.
|
||||
*/
|
||||
- (void)send:(nullable id)message __attribute__((deprecated("Please use `sendString:error:` or `sendData:error:` instead.")));
|
||||
- (void)send:(nullable id)message __attribute__((deprecated("Please use `sendString:` or `sendData` 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:));
|
||||
- (void)sendString:(NSString *)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`.
|
||||
@param data Data to send.
|
||||
*/
|
||||
- (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:));
|
||||
- (void)sendData:(nullable NSData *)data;
|
||||
|
||||
/**
|
||||
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`.
|
||||
@param data Instance of `NSData` or `nil`.
|
||||
*/
|
||||
- (BOOL)sendPing:(nullable NSData *)data error:(NSError **)error NS_SWIFT_NAME(sendPing(_:));
|
||||
- (void)sendPing:(nullable NSData *)data;
|
||||
|
||||
@end
|
||||
|
||||
@ -386,19 +329,11 @@ extern NSString *const SRHTTPResponseErrorKey;
|
||||
*/
|
||||
- (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.
|
||||
@param webSocket An instance of `SRWebSocket` that received a pong frame.
|
||||
@param pongPayload Payload that was received or `nil` if there was no payload.
|
||||
*/
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(nullable NSData *)pongData;
|
||||
|
||||
@ -410,7 +345,7 @@ extern NSString *const SRHTTPResponseErrorKey;
|
||||
|
||||
@return `YES` if text frame should be converted to UTF-8 String, otherwise - `NO`. Default: `YES`.
|
||||
*/
|
||||
- (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket NS_SWIFT_NAME(webSocketShouldConvertTextFrameToString(_:));
|
||||
- (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,15 @@
|
||||
//
|
||||
// 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
|
||||
// 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/SRWebSocket.h>
|
||||
#import <SocketRocket/SRSecurityPolicy.h>
|
||||
#import <SocketRocket/NSRunLoop+SRWebSocket.h>
|
||||
#import <SocketRocket/NSURLRequest+SRWebSocket.h>
|
||||
#import <SocketRocket/SRSecurityPolicy.h>
|
||||
#import <SocketRocket/SRWebSocket.h>
|
||||
|
||||
@ -11,6 +11,6 @@
|
||||
|
||||
@interface TCAppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property (nonatomic, strong) UIWindow *window;
|
||||
@property (strong, nonatomic) UIWindow *window;
|
||||
|
||||
@end
|
||||
|
||||
@ -11,9 +11,51 @@
|
||||
|
||||
@implementation TCAppDelegate
|
||||
|
||||
@synthesize window = _window;
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
// Override point for customization after application launch.
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application
|
||||
{
|
||||
/*
|
||||
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
|
||||
*/
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application
|
||||
{
|
||||
/*
|
||||
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
*/
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication *)application
|
||||
{
|
||||
/*
|
||||
Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
|
||||
*/
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application
|
||||
{
|
||||
/*
|
||||
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
*/
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application
|
||||
{
|
||||
/*
|
||||
Called when the application is about to terminate.
|
||||
Save data if appropriate.
|
||||
See also applicationDidEnterBackground:.
|
||||
*/
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
@interface TCViewController : UITableViewController
|
||||
|
||||
@property (nonatomic, strong) IBOutlet UITextView *inputView;
|
||||
@property (nonatomic, retain) IBOutlet UITextView *inputView;
|
||||
|
||||
- (IBAction)reconnect:(id)sender;
|
||||
- (IBAction)sendPing:(id)sender;
|
||||
|
||||
@ -8,137 +8,109 @@
|
||||
//
|
||||
|
||||
#import "TCViewController.h"
|
||||
|
||||
#import <SocketRocket/SocketRocket.h>
|
||||
|
||||
#import <SocketRocket/SRWebSocket.h>
|
||||
#import "TCChatCell.h"
|
||||
|
||||
@interface TCMessage : NSObject
|
||||
|
||||
- (instancetype)initWithMessage:(NSString *)message incoming:(BOOL)incoming;
|
||||
- (instancetype)initWithMessage:(NSString *)message fromMe:(BOOL)fromMe;
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *message;
|
||||
@property (nonatomic, assign, readonly, getter=isIncoming) BOOL incoming;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TCMessage
|
||||
|
||||
- (instancetype)initWithMessage:(NSString *)message incoming:(BOOL)incoming
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) return self;
|
||||
|
||||
_incoming = incoming;
|
||||
_message = message;
|
||||
|
||||
return self;
|
||||
}
|
||||
@property (nonatomic, retain, readonly) NSString *message;
|
||||
@property (nonatomic, readonly) BOOL fromMe;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TCViewController () <SRWebSocketDelegate, UITextViewDelegate>
|
||||
{
|
||||
@interface TCViewController () <SRWebSocketDelegate, UITextViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation TCViewController {
|
||||
SRWebSocket *_webSocket;
|
||||
NSMutableArray<TCMessage *> *_messages;
|
||||
NSMutableArray *_messages;
|
||||
}
|
||||
|
||||
@end
|
||||
@synthesize inputView = _inputView;
|
||||
|
||||
@implementation TCViewController
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - View
|
||||
///--------------------------------------
|
||||
#pragma mark - View lifecycle
|
||||
|
||||
- (void)viewDidLoad;
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
_messages = [[NSMutableArray alloc] init];
|
||||
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)_reconnect;
|
||||
{
|
||||
_webSocket.delegate = nil;
|
||||
[_webSocket close];
|
||||
|
||||
_webSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"ws://localhost:9000/chat"]]];
|
||||
_webSocket.delegate = self;
|
||||
|
||||
self.title = @"Opening Connection...";
|
||||
[_webSocket open];
|
||||
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[self reconnect:nil];
|
||||
[self _reconnect];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
- (void)reconnect:(id)sender;
|
||||
{
|
||||
[self _reconnect];
|
||||
}
|
||||
|
||||
- (void)sendPing:(id)sender;
|
||||
{
|
||||
[_webSocket sendPing:nil];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated;
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
|
||||
[_inputView becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewDidDisappear:animated];
|
||||
|
||||
[super viewDidDisappear:animated];
|
||||
|
||||
_webSocket.delegate = nil;
|
||||
[_webSocket close];
|
||||
_webSocket = nil;
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Actions
|
||||
///--------------------------------------
|
||||
|
||||
- (IBAction)reconnect:(id)sender
|
||||
{
|
||||
_webSocket.delegate = nil;
|
||||
[_webSocket close];
|
||||
|
||||
_webSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:@"wss://echo.websocket.org"]];
|
||||
_webSocket.delegate = self;
|
||||
|
||||
self.title = @"Opening Connection...";
|
||||
[_webSocket open];
|
||||
}
|
||||
|
||||
- (void)sendPing:(id)sender;
|
||||
{
|
||||
[_webSocket sendPing:nil error:NULL];
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - Messages
|
||||
///--------------------------------------
|
||||
|
||||
- (void)_addMessage:(TCMessage *)message
|
||||
{
|
||||
[_messages addObject:message];
|
||||
[self.tableView insertRowsAtIndexPaths:@[ [NSIndexPath indexPathForRow:_messages.count - 1 inSection:0] ]
|
||||
withRowAnimation:UITableViewRowAnimationNone];
|
||||
[self.tableView scrollRectToVisible:self.tableView.tableFooterView.frame animated:YES];
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - UITableViewController
|
||||
///--------------------------------------
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
|
||||
{
|
||||
return _messages.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
{
|
||||
TCMessage *message = _messages[indexPath.row];
|
||||
|
||||
TCChatCell *cell = [self.tableView dequeueReusableCellWithIdentifier:message.incoming ? @"ReceivedCell" : @"SentCell"
|
||||
forIndexPath:indexPath];
|
||||
|
||||
cell.textView.text = message.message;
|
||||
cell.nameLabel.text = message.incoming ? @"Other" : @"Me";
|
||||
|
||||
return cell;
|
||||
TCChatCell *chatCell = (id)cell;
|
||||
TCMessage *message = [_messages objectAtIndex:indexPath.row];
|
||||
chatCell.textView.text = message.message;
|
||||
chatCell.nameLabel.text = message.fromMe ? @"Me" : @"Other";
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
{
|
||||
TCMessage *message = [_messages objectAtIndex:indexPath.row];
|
||||
|
||||
return [self.tableView dequeueReusableCellWithIdentifier:message.fromMe ? @"SentCell" : @"ReceivedCell"];
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - SRWebSocketDelegate
|
||||
///--------------------------------------
|
||||
|
||||
- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
|
||||
{
|
||||
@ -149,15 +121,17 @@
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
|
||||
{
|
||||
NSLog(@":( Websocket Failed With Error %@", error);
|
||||
|
||||
|
||||
self.title = @"Connection Failed! (see logs)";
|
||||
_webSocket = nil;
|
||||
}
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessageWithString:(nonnull NSString *)string
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
|
||||
{
|
||||
NSLog(@"Received \"%@\"", string);
|
||||
[self _addMessage:[[TCMessage alloc] initWithMessage:string incoming:YES]];
|
||||
NSLog(@"Received \"%@\"", message);
|
||||
[_messages addObject:[[TCMessage alloc] initWithMessage:message fromMe:NO]];
|
||||
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:_messages.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
|
||||
[self.tableView scrollRectToVisible:self.tableView.tableFooterView.frame animated:YES];
|
||||
}
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
|
||||
@ -169,27 +143,46 @@
|
||||
|
||||
- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload;
|
||||
{
|
||||
NSLog(@"WebSocket received pong");
|
||||
NSLog(@"Websocket received pong");
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
#pragma mark - UITextViewDelegate
|
||||
///--------------------------------------
|
||||
|
||||
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
|
||||
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
|
||||
{
|
||||
if ([text rangeOfString:@"\n"].location != NSNotFound) {
|
||||
NSString *message = [textView.text stringByReplacingCharactersInRange:range withString:text];
|
||||
message = [message stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *message = [[textView.text stringByReplacingCharactersInRange:range withString:text] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
[_webSocket send:message];
|
||||
[_messages addObject:[[TCMessage alloc] initWithMessage:message fromMe:YES]];
|
||||
|
||||
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:_messages.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
|
||||
[self.tableView scrollRectToVisible:self.tableView.tableFooterView.frame animated:YES];
|
||||
|
||||
[_webSocket sendString:message error:NULL];
|
||||
|
||||
[self _addMessage:[[TCMessage alloc] initWithMessage:message incoming:NO]];
|
||||
|
||||
textView.text = nil;
|
||||
textView.text = @"";
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation;
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TCMessage
|
||||
|
||||
@synthesize message = _message;
|
||||
@synthesize fromMe = _fromMe;
|
||||
|
||||
- (instancetype)initWithMessage:(NSString *)message fromMe:(BOOL)fromMe;
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_fromMe = fromMe;
|
||||
_message = message;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="J5d-9g-n8O">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="5056" systemVersion="13E28" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="J5d-9g-n8O">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
<deployment defaultVersion="1280" identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3733"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
@ -89,7 +89,7 @@
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" systemItem="refresh" id="cNK-1A-b66">
|
||||
<connections>
|
||||
<action selector="reconnect:" destination="X5f-jW-I9m" id="yjV-1U-Tjn"/>
|
||||
<action selector="reconnect:" destination="X5f-jW-I9m" id="V5L-sy-yng"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
{
|
||||
"url": "ws://127.0.0.1:9001",
|
||||
"outdir": "./pages/results",
|
||||
"cases": ["*"],
|
||||
"exclude-cases": [],
|
||||
"exclude-agent-cases": {}
|
||||
}
|
||||
21
TestSupport/ensure_virtualenv.sh
Executable file
21
TestSupport/ensure_virtualenv.sh
Executable file
@ -0,0 +1,21 @@
|
||||
#
|
||||
# Copyright 2012 Square Inc.
|
||||
# Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the license found in the
|
||||
# LICENSE-examples file in the root directory of this source tree.
|
||||
#
|
||||
|
||||
VIRTUALENV_PATH=$1
|
||||
|
||||
if [ -d "$VIRTUALENV_PATH" ]; then
|
||||
echo "Virtual Env already installed"
|
||||
else
|
||||
python extern/virtualenv/virtualenv.py $VIRTUALENV_PATH
|
||||
source $VIRTUALENV_PATH/bin/activate
|
||||
pushd TestSupport/sr-testharness/
|
||||
env LDFLAGS="-L$(brew --prefix openssl)/lib" CFLAGS="-I$(brew --prefix openssl)/include" pip install cryptography
|
||||
python setup.py develop
|
||||
popd
|
||||
fi
|
||||
37
TestSupport/run_test.sh
Executable file
37
TestSupport/run_test.sh
Executable file
@ -0,0 +1,37 @@
|
||||
#
|
||||
# Copyright 2012 Square Inc.
|
||||
# Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the license found in the
|
||||
# LICENSE-examples file in the root directory of this source tree.
|
||||
#
|
||||
|
||||
TEST_SCENARIOS=$1
|
||||
TEST_URL=$2
|
||||
CONFIGURATION=$3
|
||||
|
||||
|
||||
export SR_TEST_URL=$TEST_URL
|
||||
|
||||
bash TestSupport/ensure_virtualenv.sh .env
|
||||
|
||||
pushd TestSupport/sr-testharness/
|
||||
python setup.py develop
|
||||
popd
|
||||
|
||||
source .env/bin/activate
|
||||
sr-testharness -i '' -c "$TEST_SCENARIOS" &
|
||||
|
||||
CHILD_PID=$!
|
||||
|
||||
DESTINATION="OS=9.2,name=iPhone 6s"
|
||||
SDK="iphonesimulator"
|
||||
SHARED_ARGS="-configuration $CONFIGURATION -sdk $SDK"
|
||||
|
||||
xcodebuild -scheme SocketRocketTests -destination "$DESTINATION" $SHARED_ARGS TEST_AFTER_BUILD=YES clean build
|
||||
RESULT=$?
|
||||
|
||||
kill $CHILD_PID
|
||||
|
||||
exit $RESULT
|
||||
@ -1,18 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2016-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the license found in the
|
||||
# LICENSE-examples file in the root directory of this source tree.
|
||||
#
|
||||
|
||||
PYENV_PATH=$(pwd)/.env
|
||||
|
||||
echo $PYENV_PATH
|
||||
if [ -d "$PYENV_PATH" ]; then
|
||||
source $PYENV_PATH/bin/activate
|
||||
$PYENV_PATH/bin/wstest -m fuzzingserver -s TestSupport/autobahn_fuzzingserver.json
|
||||
else
|
||||
echo "Python Virtualenv not set up. Please run './TestSupport/setup_env.sh .env' first."
|
||||
fi
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2016-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the license found in the
|
||||
# LICENSE-examples file in the root directory of this source tree.
|
||||
#
|
||||
|
||||
VIRTUALENV_PATH=$1
|
||||
VIRTUALENV_VERSION=15.0.1
|
||||
|
||||
if [ -d "$VIRTUALENV_PATH" ]; then
|
||||
echo "Virtual Env already installed"
|
||||
elif [ -z "$VIRTUALENV_PATH" ]; then
|
||||
echo "Usage: ./setup_env.sh <folder path>"
|
||||
else
|
||||
mkdir $VIRTUALENV_PATH
|
||||
|
||||
pushd $VIRTUALENV_PATH
|
||||
|
||||
curl -L -o virtualenv.tar.gz https://pypi.python.org/packages/source/v/virtualenv/virtualenv-$VIRTUALENV_VERSION.tar.gz
|
||||
tar xvfz virtualenv.tar.gz
|
||||
|
||||
pushd virtualenv-$VIRTUALENV_VERSION
|
||||
python setup.py install --user
|
||||
popd
|
||||
|
||||
popd
|
||||
|
||||
python $VIRTUALENV_PATH/virtualenv-$VIRTUALENV_VERSION/virtualenv.py $VIRTUALENV_PATH
|
||||
|
||||
source $VIRTUALENV_PATH/bin/activate
|
||||
pip install autobahntestsuite
|
||||
|
||||
echo "Environment succesfully set up in $VIRTUALENV_PATH."
|
||||
fi
|
||||
3
TestSupport/sr-testharness/setup.cfg
Normal file
3
TestSupport/sr-testharness/setup.cfg
Normal file
@ -0,0 +1,3 @@
|
||||
[egg_info]
|
||||
tag_build = dev
|
||||
tag_svn_revision = true
|
||||
31
TestSupport/sr-testharness/setup.py
Normal file
31
TestSupport/sr-testharness/setup.py
Normal file
@ -0,0 +1,31 @@
|
||||
from setuptools import setup, find_packages
|
||||
import sys, os
|
||||
|
||||
version = '0.0'
|
||||
|
||||
setup(name='srtestharness',
|
||||
version=version,
|
||||
description="",
|
||||
long_description="""\
|
||||
""",
|
||||
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
keywords='',
|
||||
author='',
|
||||
author_email='',
|
||||
url='',
|
||||
license='',
|
||||
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
|
||||
include_package_data=True,
|
||||
zip_safe=True,
|
||||
install_requires=[
|
||||
# -*- Extra requirements: -*-
|
||||
'autobahntestsuite',
|
||||
'autobahn==0.10.1',
|
||||
'cryptography>=0.7'
|
||||
],
|
||||
entry_points="""
|
||||
# -*- Entry points: -*-
|
||||
[console_scripts]
|
||||
sr-testharness = srtestharness.runner:main
|
||||
""",
|
||||
)
|
||||
1
TestSupport/sr-testharness/srtestharness/__init__.py
Normal file
1
TestSupport/sr-testharness/srtestharness/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
#
|
||||
84
TestSupport/sr-testharness/srtestharness/runner.py
Normal file
84
TestSupport/sr-testharness/srtestharness/runner.py
Normal file
@ -0,0 +1,84 @@
|
||||
#
|
||||
# Copyright 2012 Square Inc.
|
||||
# Portions Copyright (c) 2016-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the license found in the
|
||||
# LICENSE-examples file in the root directory of this source tree.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import subprocess
|
||||
import urlparse
|
||||
|
||||
from twisted.python import log
|
||||
from twisted.internet import reactor
|
||||
from twisted.web.server import Site
|
||||
from twisted.web.static import File
|
||||
from autobahntestsuite.fuzzing import FuzzingServerFactory
|
||||
from autobahn.twisted.websocket import listenWS
|
||||
|
||||
class jsondict(dict):
|
||||
def __init__(self, json_value):
|
||||
if isinstance(json_value, dict):
|
||||
dict.__init__(self, json_value)
|
||||
else:
|
||||
dict.__init__(self, json.loads(json_value))
|
||||
|
||||
def append(self, other):
|
||||
self.update(jsondict(other))
|
||||
|
||||
def __repr__(self):
|
||||
return "'%s'" % json.dumps(self)
|
||||
|
||||
def parse_opts(s):
|
||||
return dict(s.split('='))
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-u', '--url', default='ws://localhost:9001', help='Listen URL [default: %(default)s]')
|
||||
parser.add_argument('-O', '--options', default=jsondict({'failByDrop':False}), type=jsondict, action='append', help='extra options (overwrites existing) [default: %(default)s]')
|
||||
|
||||
parser.add_argument('-p', '--webport', default=9090, type=int)
|
||||
parser.add_argument('-i', '--listen-interface', default='localhost', help='interface to listen on')
|
||||
parser.add_argument('-w', '--webdir', default='.')
|
||||
parser.add_argument('-d', '--debug', default=False, action='store_true', help='Debug Mode [default: %(default)s]')
|
||||
|
||||
parser.add_argument('-o', '--outdir', default='./pages/results', metavar='DIR', help='Output Directory [default: %(default)s]')
|
||||
|
||||
parser.add_argument('-c', '--cases', default=['*'], nargs='+', help='test cases [default: %(default)s]')
|
||||
parser.add_argument('-x', '--exclude-cases', default=[], nargs='+', help='test cases to exclude [default: %(default)s]')
|
||||
|
||||
parser.add_argument('-l', '--logfile', type=argparse.FileType('w'), default=sys.stdout, help='logging file [default: stdout]')
|
||||
|
||||
parser.add_argument('-t', '--exit-timeout', metavar='SECONDS', default=None, type=float, help='Will automatically exit after %(metavar)s seconds [default: %(default)s]')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
spec = args.__dict__
|
||||
|
||||
log.startLogging(args.logfile)
|
||||
|
||||
## fuzzing server
|
||||
fuzzer = FuzzingServerFactory(spec)
|
||||
listenWS(fuzzer, interface=args.listen_interface)
|
||||
|
||||
## web server
|
||||
webdir = File(args.webdir)
|
||||
web = Site(webdir)
|
||||
reactor.listenTCP(args.webport, web, interface=args.listen_interface)
|
||||
|
||||
log.msg("Using Twisted reactor class %s" % str(reactor.__class__))
|
||||
|
||||
if args.exit_timeout:
|
||||
def exit_callback():
|
||||
log.msg("Exiting due to timeout (--exit-timeout/-t)")
|
||||
reactor.fireSystemEvent('shutdown')
|
||||
#reactor.stop()
|
||||
sys.exit(12)
|
||||
|
||||
reactor.callLater(args.exit_timeout, exit_callback)
|
||||
|
||||
reactor.run()
|
||||
@ -70,10 +70,10 @@ SRAutobahnOperation *SRAutobahnTestOperation(NSURL *serverURL, NSInteger caseNum
|
||||
caseNumber:@(caseNumber)
|
||||
agent:agent
|
||||
textMessageHandler:^(SRWebSocket * _Nonnull socket, NSString * _Nullable message) {
|
||||
[socket sendString:message error:nil];
|
||||
[socket sendString:message];
|
||||
}
|
||||
dataMessageHandler:^(SRWebSocket * _Nonnull socket, NSData * _Nullable message) {
|
||||
[socket sendData:message error:nil];
|
||||
[socket sendData:message];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@ -85,8 +85,8 @@
|
||||
NSString *selectorName = [NSString stringWithFormat:@"Case #%@", identifier];
|
||||
SEL selector = NSSelectorFromString(selectorName);
|
||||
|
||||
IMP implementation = imp_implementationWithBlock(^(SRAutobahnTests *sself) {
|
||||
[sself performTestWithCaseNumber:caseNumber identifier:identifier];
|
||||
IMP implementation = imp_implementationWithBlock(^(SRAutobahnTests *self) {
|
||||
[self performTestWithCaseNumber:caseNumber identifier:identifier];
|
||||
});
|
||||
NSString *typeString = [NSString stringWithFormat:@"%s%s%s", @encode(id), @encode(id), @encode(SEL)];
|
||||
class_addMethod(self, selector, implementation, typeString.UTF8String);
|
||||
|
||||
@ -27,7 +27,8 @@ NSString *SRAutobahnTestAgentName(void)
|
||||
|
||||
NSURL *SRAutobahnTestServerURL(void)
|
||||
{
|
||||
return [NSURL URLWithString:@"ws://localhost:9001"];
|
||||
NSString *serverURLString = [[NSProcessInfo processInfo].environment objectForKey:@"SR_TEST_URL"];
|
||||
return [NSURL URLWithString:serverURLString];
|
||||
}
|
||||
|
||||
///--------------------------------------
|
||||
|
||||
1
Vendor/xctoolchain
vendored
1
Vendor/xctoolchain
vendored
@ -1 +0,0 @@
|
||||
Subproject commit a66c63fe948cf8b4d77a51b69f35459fe0c68973
|
||||
22
extern/virtualenv/LICENSE.txt
vendored
Normal file
22
extern/virtualenv/LICENSE.txt
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Copyright (c) 2007 Ian Bicking and Contributors
|
||||
Copyright (c) 2009 Ian Bicking, The Open Planning Project
|
||||
Copyright (c) 2011 The virtualenv developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
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 THE AUTHORS OR COPYRIGHT HOLDERS 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.
|
||||
2246
extern/virtualenv/virtualenv.py
vendored
Executable file
2246
extern/virtualenv/virtualenv.py
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1
pages
Submodule
1
pages
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit f103cccb1d138c74a02404dfefcb875b540286a1
|
||||
Loading…
Reference in New Issue
Block a user