Merge branch 'master' of github.com:ratchetphp/Ratchet
This commit is contained in:
		
						commit
						82f2505b13
					
				
							
								
								
									
										11
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								.travis.yml
									
									
									
									
									
								
							| @ -1,13 +1,20 @@ | |||||||
| language: php | language: php | ||||||
| 
 | 
 | ||||||
| php: | php: | ||||||
|   - 5.3 |  | ||||||
|   - 5.4 |   - 5.4 | ||||||
|   - 5.5 |   - 5.5 | ||||||
|   - 5.6 |   - 5.6 | ||||||
|   - 7 |   - 7.0 | ||||||
|  |   - 7.1 | ||||||
|   - hhvm |   - hhvm | ||||||
| 
 | 
 | ||||||
|  | dist: trusty | ||||||
|  | 
 | ||||||
|  | matrix: | ||||||
|  |   allow_failures: | ||||||
|  |     - php: hhvm | ||||||
|  | 
 | ||||||
| before_script: | before_script: | ||||||
|   - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "session.serialize_handler = php" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;' |   - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "session.serialize_handler = php" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;' | ||||||
|  |   - php -m | ||||||
|   - composer install --dev --prefer-source |   - composer install --dev --prefer-source | ||||||
|  | |||||||
							
								
								
									
										169
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										169
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @ -1,138 +1,135 @@ | |||||||
| CHANGELOG | CHANGELOG | ||||||
| ========= | ========= | ||||||
| 
 | 
 | ||||||
| ###Legend | ### Legend | ||||||
| 
 | 
 | ||||||
| * "BC": Backwards compatibility break (from public component APIs) | * "BC": Backwards compatibility break (from public component APIs) | ||||||
| * "BF": Bug fix | * "BF": Bug fix | ||||||
| 
 | 
 | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
|  | * 0.4.1 (2017-12-11) | ||||||
|  |   * Only enableKeepAlive in App if no WsServer passed allowing user to set their own timeout duration | ||||||
|  |   * Support Symfony 4 | ||||||
|  |   * BF: Plug NOOP controller in connection from router in case of misbehaving client | ||||||
|  |   * BF: Raise error from invalid WAMP payload | ||||||
|  | 
 | ||||||
|  | * 0.4 (2017-09-14) | ||||||
|  |   * BC: $conn->WebSocket->request replaced with $conn->httpRequest which is a PSR-7 object | ||||||
|  |   * Binary messages now supported via Ratchet\WebSocket\MessageComponentInterface | ||||||
|  |   * Added heartbeat support via ping/pong in WsServer | ||||||
|  |   * BC: No longer support old (and insecure) Hixie76 and Hybi protocols | ||||||
|  |   * BC: No longer support disabling UTF-8 checks | ||||||
|  |   * BC: The Session component implements HttpServerInterface instead of WsServerInterface | ||||||
|  |   * BC: PHP 5.3 no longer supported | ||||||
|  |   * BC: Update to newer version of react/socket dependency | ||||||
|  |   * BC: WAMP topics reduced to 0 subscriptions are deleted, new subs to same name will result in new Topic instance | ||||||
|  |   * Significant performance enhancements | ||||||
|  | 
 | ||||||
| * 0.3.6 (2017-01-06) | * 0.3.6 (2017-01-06) | ||||||
|  * BF: Keep host and scheme in HTTP request object attatched to connection |   * BF: Keep host and scheme in HTTP request object attatched to connection | ||||||
|  * BF: Return correct HTTP response (405) when non-GET request made |   * BF: Return correct HTTP response (405) when non-GET request made | ||||||
| 
 | 
 | ||||||
| * 0.3.5 (2016-05-25) | * 0.3.5 (2016-05-25) | ||||||
| 
 |   * BF: Unmask responding close frame | ||||||
|  * BF: Unmask responding close frame |   * Added write handler for PHP session serializer | ||||||
|  * Added write handler for PHP session serializer |  | ||||||
| 
 | 
 | ||||||
| * 0.3.4 (2015-12-23) | * 0.3.4 (2015-12-23) | ||||||
| 
 |   * BF: Edge case where version check wasn't run on message coalesce | ||||||
|  * BF: Edge case where version check wasn't run on message coalesce |   * BF: Session didn't start when using pdo_sqlite | ||||||
|  * BF: Session didn't start when using pdo_sqlite |   * BF: WAMP currie prefix check when using '#' | ||||||
|  * BF: WAMP currie prefix check when using '#' |   * Compatibility with Symfony 3 | ||||||
|  * Compatibility with Symfony 3 |  | ||||||
| 
 | 
 | ||||||
| * 0.3.3 (2015-05-26) | * 0.3.3 (2015-05-26) | ||||||
| 
 |   * BF: Framing bug on large messages upon TCP fragmentation | ||||||
|  * BF: Framing bug on large messages upon TCP fragmentation |   * BF: Symfony Router query parameter defaults applied to Request | ||||||
|  * BF: Symfony Router query parameter defaults applied to Request |   * BF: WAMP CURIE on all URIs | ||||||
|  * BF: WAMP CURIE on all URIs |   * OriginCheck rules applied to FlashPolicy | ||||||
|  * OriginCheck rules applied to FlashPolicy |   * Switched from PSR-0 to PSR-4 | ||||||
|  * Switched from PSR-0 to PSR-4 |  | ||||||
| 
 | 
 | ||||||
| * 0.3.2 (2014-06-08) | * 0.3.2 (2014-06-08) | ||||||
| 
 |   * BF: No messages after closing handshake (fixed rare race condition causing 100% CPU) | ||||||
|  * BF: No messages after closing handshake (fixed rare race condition causing 100% CPU) |   * BF: Fixed accidental BC break from v0.3.1 | ||||||
|  * BF: Fixed accidental BC break from v0.3.1 |   * Added autoDelete parameter to Topic to destroy when empty of connections | ||||||
|  * Added autoDelete parameter to Topic to destroy when empty of connections |   * Exposed React Socket on IoServer (allowing FlashPolicy shutdown in App) | ||||||
|  * Exposed React Socket on IoServer (allowing FlashPolicy shutdown in App) |   * Normalized Exceptions in WAMP | ||||||
|  * Normalized Exceptions in WAMP |  | ||||||
| 
 | 
 | ||||||
| * 0.3.1 (2014-05-26) | * 0.3.1 (2014-05-26) | ||||||
| 
 |   * Added query parameter support to Router, set in HTTP request (ws://server?hello=world) | ||||||
|  * Added query parameter support to Router, set in HTTP request (ws://server?hello=world) |   * HHVM compatibility | ||||||
|  * HHVM compatibility |   * BF: React/0.4 support; CPU starvation bug fixes | ||||||
|  * BF: React/0.4 support; CPU starvation bug fixes |   * BF: Allow App::route to ignore Host header | ||||||
|  * BF: Allow App::route to ignore Host header |   * Added expected filters to WAMP Topic broadcast method | ||||||
|  * Added expected filters to WAMP Topic broadcast method |   * Resource cleanup in WAMP TopicManager | ||||||
|  * Resource cleanup in WAMP TopicManager |  | ||||||
| 
 | 
 | ||||||
| * 0.3.0 (2013-10-14) | * 0.3.0 (2013-10-14) | ||||||
| 
 |   * Added the `App` class to help making Ratchet so easy to use it's silly | ||||||
|  * Added the `App` class to help making Ratchet so easy to use it's silly |   * BC: Require hostname to do HTTP Host header match and do Origin HTTP header check, verify same name by default, helping prevent CSRF attacks | ||||||
|  * BC: Require hostname to do HTTP Host header match and do Origin HTTP header check, verify same name by default, helping prevent CSRF attacks |   * Added Symfony/2.2 based HTTP Router component to allowing for a single Ratchet server to handle multiple apps -> Ratchet\Http\Router | ||||||
|  * Added Symfony/2.2 based HTTP Router component to allowing for a single Ratchet server to handle multiple apps -> Ratchet\Http\Router |   * BC: Decoupled HTTP from WebSocket component -> Ratchet\Http\HttpServer | ||||||
|  * BC: Decoupled HTTP from WebSocket component -> Ratchet\Http\HttpServer |   * BF: Single sub-protocol selection to conform with RFC6455 | ||||||
|  * BF: Single sub-protocol selection to conform with RFC6455 |   * BF: Sanity checks on WAMP protocol to prevent errors | ||||||
|  * BF: Sanity checks on WAMP protocol to prevent errors |  | ||||||
| 
 | 
 | ||||||
| * 0.2.8 (2013-09-19) | * 0.2.8 (2013-09-19) | ||||||
| 
 |   * React 0.3 support | ||||||
|  * React 0.3 support |  | ||||||
| 
 | 
 | ||||||
| * 0.2.7 (2013-06-09) | * 0.2.7 (2013-06-09) | ||||||
| 
 |   * BF: Sub-protocol negotation with Guzzle 3.6 | ||||||
|  * BF: Sub-protocol negotation with Guzzle 3.6 |  | ||||||
| 
 | 
 | ||||||
| * 0.2.6 (2013-06-01) | * 0.2.6 (2013-06-01) | ||||||
| 
 |   * Guzzle 3.6 support | ||||||
|  * Guzzle 3.6 support |  | ||||||
| 
 | 
 | ||||||
| * 0.2.5 (2013-04-01) | * 0.2.5 (2013-04-01) | ||||||
| 
 |   * Fixed Hixie-76 handshake bug | ||||||
|  * Fixed Hixie-76 handshake bug |  | ||||||
| 
 | 
 | ||||||
| * 0.2.4 (2013-03-09) | * 0.2.4 (2013-03-09) | ||||||
| 
 |   * Support for Symfony 2.2 and Guzzle 2.3 | ||||||
|  * Support for Symfony 2.2 and Guzzle 2.3 |   * Minor bug fixes when handling errors | ||||||
|  * Minor bug fixes when handling errors |  | ||||||
| 
 | 
 | ||||||
| * 0.2.3 (2012-11-21) | * 0.2.3 (2012-11-21) | ||||||
| 
 |   * Bumped dep: Guzzle to v3, React to v0.2.4 | ||||||
|  * Bumped dep: Guzzle to v3, React to v0.2.4 |   * More tests | ||||||
|  * More tests |  | ||||||
| 
 | 
 | ||||||
| * 0.2.2 (2012-10-20) | * 0.2.2 (2012-10-20) | ||||||
| 
 |   * Bumped deps to use React v0.2 | ||||||
|  * Bumped deps to use React v0.2 |  | ||||||
| 
 | 
 | ||||||
| * 0.2.1 (2012-10-13) | * 0.2.1 (2012-10-13) | ||||||
| 
 |   * BF: No more UTF-8 warnings in browsers (no longer sending empty sub-protocol string) | ||||||
|  * BF: No more UTF-8 warnings in browsers (no longer sending empty sub-protocol string) |   * Documentation corrections | ||||||
|  * Documentation corrections |   * Using new composer structure | ||||||
|  * Using new composer structure |  | ||||||
| 
 | 
 | ||||||
| * 0.2 (2012-09-07) | * 0.2 (2012-09-07) | ||||||
| 
 |   * Ratchet passes every non-binary-frame test from the Autobahn Testsuite | ||||||
|  * Ratchet passes every non-binary-frame test from the Autobahn Testsuite |   * Major performance improvements | ||||||
|  * Major performance improvements |   * BC: Renamed "WampServer" to "ServerProtocol" | ||||||
|  * BC: Renamed "WampServer" to "ServerProtocol" |   * BC: New "WampServer" component passes Topic container objects of subscribed Connections | ||||||
|  * BC: New "WampServer" component passes Topic container objects of subscribed Connections |   * Option to turn off UTF-8 checks in order to increase performance | ||||||
|  * Option to turn off UTF-8 checks in order to increase performance |   * Switched dependency guzzle/guzzle to guzzle/http (no API changes) | ||||||
|  * Switched dependency guzzle/guzzle to guzzle/http (no API changes) |   * mbstring no longer required | ||||||
|  * mbstring no longer required |  | ||||||
| 
 | 
 | ||||||
| * 0.1.5 (2012-07-12) | * 0.1.5 (2012-07-12) | ||||||
| 
 |   * BF: Error where service wouldn't run on PHP <= 5.3.8 | ||||||
|  * BF: Error where service wouldn't run on PHP <= 5.3.8 |   * Dependency library updates | ||||||
|  * Dependency library updates |  | ||||||
| 
 | 
 | ||||||
| * 0.1.4 (2012-06-17) | * 0.1.4 (2012-06-17) | ||||||
| 
 |   * Fixed dozens of failing AB tests | ||||||
|  * Fixed dozens of failing AB tests |   * BF: Proper socket buffer handling | ||||||
|  * BF: Proper socket buffer handling |  | ||||||
| 
 | 
 | ||||||
| * 0.1.3 (2012-06-15) | * 0.1.3 (2012-06-15) | ||||||
| 
 |   * Major refactor inside WebSocket protocol handling, more loosley coupled | ||||||
|  * Major refactor inside WebSocket protocol handling, more loosley coupled |   * BF: Proper error handling on failed WebSocket connections | ||||||
|  * BF: Proper error handling on failed WebSocket connections |   * BF: Handle TCP message concatenation | ||||||
|  * BF: Handle TCP message concatenation |   * Inclusion of the AutobahnTestSuite checking WebSocket protocol compliance | ||||||
|  * Inclusion of the AutobahnTestSuite checking WebSocket protocol compliance |   * mb_string now a requirement | ||||||
|  * mb_string now a requirement |  | ||||||
| 
 | 
 | ||||||
| * 0.1.2 (2012-05-19) | * 0.1.2 (2012-05-19) | ||||||
| 
 |   * BC/BF: Updated WAMP API to coincide with the official spec | ||||||
|  * BC/BF: Updated WAMP API to coincide with the official spec |   * Tweaks to improve running as a long lived process | ||||||
|  * Tweaks to improve running as a long lived process |  | ||||||
| 
 | 
 | ||||||
| * 0.1.1 (2012-05-14) | * 0.1.1 (2012-05-14) | ||||||
| 
 |   * Separated interfaces allowing WebSockets to support multiple sub protocols | ||||||
|  * Separated interfaces allowing WebSockets to support multiple sub protocols |   * BF: remoteAddress variable on connections returns proper value | ||||||
|  * BF: remoteAddress variable on connections returns proper value |  | ||||||
| 
 | 
 | ||||||
| * 0.1 (2012-05-11) | * 0.1 (2012-05-11) | ||||||
| 
 |   * First release with components: IoServer, WsServer, SessionProvider, WampServer, FlashPolicy, IpBlackList | ||||||
|  * First release with components: IoServer, WsServer, SessionProvider, WampServer, FlashPolicy, IpBlackList |   * I/O now handled by React, making Ratchet fully asynchronous | ||||||
|  * I/O now handled by React, making Ratchet fully asynchronous |  | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								Makefile
									
									
									
									
									
								
							| @ -10,26 +10,33 @@ cover: | |||||||
| abtests: | abtests: | ||||||
| 	ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8001 LibEvent & | 	ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8001 LibEvent & | ||||||
| 	ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8002 StreamSelect & | 	ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8002 StreamSelect & | ||||||
| 	ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver-noutf8.php 8003 StreamSelect & |  | ||||||
| 	ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8004 LibEv & | 	ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8004 LibEv & | ||||||
| 	wstest -m testeeserver -w ws://localhost:8000 & | 	wstest -m testeeserver -w ws://localhost:8000 & | ||||||
|  | 	sleep 1 | ||||||
| 	wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-all.json | 	wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-all.json | ||||||
| 	killall php wstest | 	killall php wstest | ||||||
| 
 | 
 | ||||||
| abtest: | abtest: | ||||||
| 	ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8000 StreamSelect & | 	ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8000 StreamSelect & | ||||||
|  | 	sleep 1 | ||||||
| 	wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-quick.json | 	wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-quick.json | ||||||
| 	killall php | 	killall php | ||||||
| 
 | 
 | ||||||
| profile: | profile: | ||||||
| 	php -d 'xdebug.profiler_enable=1' tests/autobahn/bin/fuzzingserver.php 8000 LibEvent & | 	php -d 'xdebug.profiler_enable=1' tests/autobahn/bin/fuzzingserver.php 8000 LibEvent & | ||||||
|  | 	sleep 1 | ||||||
| 	wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-profile.json | 	wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-profile.json | ||||||
| 	killall php | 	killall php | ||||||
| 
 | 
 | ||||||
| apidocs: | apidocs: | ||||||
| 	apigen --title Ratchet -d reports/api -s src/ \
 | 	apigen --title Ratchet -d reports/api \
 | ||||||
| 		-s vendor/react \
 | 		-s src/ \
 | ||||||
| 		-s vendor/guzzle \
 | 		-s vendor/ratchet/rfc6455/src \
 | ||||||
| 		-s vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Session \
 | 		-s vendor/react/event-loop/src \
 | ||||||
| 		-s vendor/symfony/routing/Symfony/Component/Routing \
 | 		-s vendor/react/socket/src \
 | ||||||
| 		-s vendor/evenement/evenement/src/Evenement | 		-s vendor/react/stream/src \
 | ||||||
|  | 		-s vendor/psr/http-message/src \
 | ||||||
|  | 		-s vendor/symfony/http-foundation/Session \
 | ||||||
|  | 		-s vendor/symfony/routing \
 | ||||||
|  | 		-s vendor/evenement/evenement/src/Evenement \
 | ||||||
|  | 		--exclude=vendor/symfony/routing/Tests \
 | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								README.md
									
									
									
									
									
								
							| @ -1,26 +1,19 @@ | |||||||
| #Ratchet | # Ratchet | ||||||
| 
 | 
 | ||||||
| [](http://travis-ci.org/ratchetphp/Ratchet) | [](http://travis-ci.org/ratchetphp/Ratchet) | ||||||
|  | [](http://socketo.me/reports/ab/index.html) | ||||||
| [](https://packagist.org/packages/cboden/ratchet) | [](https://packagist.org/packages/cboden/ratchet) | ||||||
| 
 | 
 | ||||||
| A PHP 5.3 library for asynchronously serving WebSockets. | A PHP library for asynchronously serving WebSockets. | ||||||
| Build up your application through simple interfaces and re-use your application without changing any of its code just by combining different components. | Build up your application through simple interfaces and re-use your application without changing any of its code just by combining different components. | ||||||
| 
 | 
 | ||||||
| ##WebSocket Compliance | ## Requirements | ||||||
| 
 |  | ||||||
| * Supports the RFC6455, HyBi-10+, and Hixie76 protocol versions (at the same time) |  | ||||||
| * Tested on Chrome 13+, Firefox 6+, Safari 5+, iOS 4.2+, IE 8+ |  | ||||||
| * Ratchet [passes](http://socketo.me/reports/ab/) the [Autobahn Testsuite](http://autobahn.ws/testsuite) (non-binary messages) |  | ||||||
| 
 |  | ||||||
| ##Requirements |  | ||||||
| 
 | 
 | ||||||
| Shell access is required and root access is recommended. | Shell access is required and root access is recommended. | ||||||
| To avoid proxy/firewall blockage it's recommended WebSockets are requested on port 80 or 443 (SSL), which requires root access. | To avoid proxy/firewall blockage it's recommended WebSockets are requested on port 80 or 443 (SSL), which requires root access. | ||||||
| In order to do this, along with your sync web stack, you can either use a reverse proxy or two separate machines. | In order to do this, along with your sync web stack, you can either use a reverse proxy or two separate machines. | ||||||
| You can find more details in the [server conf docs](http://socketo.me/docs/deploy#serverconfiguration). | You can find more details in the [server conf docs](http://socketo.me/docs/deploy#serverconfiguration). | ||||||
| 
 | 
 | ||||||
| PHP 5.3.9 (or higher) is required. If you have access, PHP 5.4 (or higher) is *highly* recommended for its performance improvements. |  | ||||||
| 
 |  | ||||||
| ### Documentation | ### Documentation | ||||||
| 
 | 
 | ||||||
| User and API documentation is available on Ratchet's website: http://socketo.me | User and API documentation is available on Ratchet's website: http://socketo.me | ||||||
| @ -31,7 +24,7 @@ Need help?  Have a question?  Want to provide feedback?  Write a message on the | |||||||
| 
 | 
 | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| ###A quick example | ### A quick example | ||||||
| 
 | 
 | ||||||
| ```php | ```php | ||||||
| <?php | <?php | ||||||
| @ -86,5 +79,5 @@ class MyChat implements MessageComponentInterface { | |||||||
|     // Then some JavaScript in the browser: |     // Then some JavaScript in the browser: | ||||||
|     var conn = new WebSocket('ws://localhost:8080/echo'); |     var conn = new WebSocket('ws://localhost:8080/echo'); | ||||||
|     conn.onmessage = function(e) { console.log(e.data); }; |     conn.onmessage = function(e) { console.log(e.data); }; | ||||||
|     conn.send('Hello Me!'); |     conn.onopen = function(e) { conn.send('Hello Me!'); }; | ||||||
| ``` | ``` | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
|     "name": "cboden/ratchet" |     "name": "cboden/ratchet" | ||||||
|   , "type": "library" |   , "type": "library" | ||||||
|   , "description": "PHP WebSocket library" |   , "description": "PHP WebSocket library" | ||||||
|   , "keywords": ["WebSockets", "Server", "Ratchet", "Sockets"] |   , "keywords": ["WebSockets", "Server", "Ratchet", "Sockets", "WebSocket"] | ||||||
|   , "homepage": "http://socketo.me" |   , "homepage": "http://socketo.me" | ||||||
|   , "license": "MIT" |   , "license": "MIT" | ||||||
|   , "authors": [ |   , "authors": [ | ||||||
| @ -23,10 +23,14 @@ | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   , "require": { |   , "require": { | ||||||
|         "php": ">=5.3.9" |         "php": ">=5.4.2" | ||||||
|       , "react/socket": "^0.3 || ^0.4" |       , "ratchet/rfc6455": "^0.2" | ||||||
|       , "guzzle/http": "^3.6" |       , "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5" | ||||||
|       , "symfony/http-foundation": "^2.2|^3.0" |       , "guzzlehttp/psr7": "^1.0" | ||||||
|       , "symfony/routing": "^2.2|^3.0" |       , "symfony/http-foundation": "^2.6|^3.0|^4.0" | ||||||
|  |       , "symfony/routing": "^2.6|^3.0|^4.0" | ||||||
|  |     } | ||||||
|  |   , "require-dev": { | ||||||
|  |         "phpunit/phpunit": "~4.8" | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ namespace Ratchet; | |||||||
| use React\EventLoop\LoopInterface; | use React\EventLoop\LoopInterface; | ||||||
| use React\EventLoop\Factory as LoopFactory; | use React\EventLoop\Factory as LoopFactory; | ||||||
| use React\Socket\Server as Reactor; | use React\Socket\Server as Reactor; | ||||||
|  | use React\Socket\SecureServer as SecureReactor; | ||||||
| use Ratchet\Http\HttpServerInterface; | use Ratchet\Http\HttpServerInterface; | ||||||
| use Ratchet\Http\OriginCheck; | use Ratchet\Http\OriginCheck; | ||||||
| use Ratchet\Wamp\WampServerInterface; | use Ratchet\Wamp\WampServerInterface; | ||||||
| @ -55,20 +56,16 @@ class App { | |||||||
|     protected $_routeCounter = 0; |     protected $_routeCounter = 0; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param string        $httpHost HTTP hostname clients intend to connect to. MUST match JS `new WebSocket('ws://$httpHost');` |      * @param string        $httpHost   HTTP hostname clients intend to connect to. MUST match JS `new WebSocket('ws://$httpHost');` | ||||||
|      * @param int           $port     Port to listen on. If 80, assuming production, Flash on 843 otherwise expecting Flash to be proxied through 8843 |      * @param int           $port       Port to listen on. If 80, assuming production, Flash on 843 otherwise expecting Flash to be proxied through 8843 | ||||||
|      * @param string        $address  IP address to bind to. Default is localhost/proxy only. '0.0.0.0' for any machine. |      * @param string        $address    IP address to bind to. Default is localhost/proxy only. '0.0.0.0' for any machine. | ||||||
|      * @param LoopInterface $loop     Specific React\EventLoop to bind the application to. null will create one for you. |      * @param LoopInterface $loop       Specific React\EventLoop to bind the application to. null will create one for you. | ||||||
|      */ |      */ | ||||||
|     public function __construct($httpHost = 'localhost', $port = 8080, $address = '127.0.0.1', LoopInterface $loop = null) { |     public function __construct($httpHost = 'localhost', $port = 8080, $address = '127.0.0.1', LoopInterface $loop = null) { | ||||||
|         if (extension_loaded('xdebug')) { |         if (extension_loaded('xdebug')) { | ||||||
|             trigger_error('XDebug extension detected. Remember to disable this if performance testing or going live!', E_USER_WARNING); |             trigger_error('XDebug extension detected. Remember to disable this if performance testing or going live!', E_USER_WARNING); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (3 !== strlen('✓')) { |  | ||||||
|             throw new \DomainException('Bad encoding, length of unicode character ✓ should be 3. Ensure charset UTF-8 and check ini val mbstring.func_autoload'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (null === $loop) { |         if (null === $loop) { | ||||||
|             $loop = LoopFactory::create(); |             $loop = LoopFactory::create(); | ||||||
|         } |         } | ||||||
| @ -76,8 +73,7 @@ class App { | |||||||
|         $this->httpHost = $httpHost; |         $this->httpHost = $httpHost; | ||||||
|         $this->port = $port; |         $this->port = $port; | ||||||
| 
 | 
 | ||||||
|         $socket = new Reactor($loop); |         $socket = new Reactor($address . ':' . $port, $loop); | ||||||
|         $socket->listen($port, $address); |  | ||||||
| 
 | 
 | ||||||
|         $this->routes  = new RouteCollection; |         $this->routes  = new RouteCollection; | ||||||
|         $this->_server = new IoServer(new HttpServer(new Router(new UrlMatcher($this->routes, new RequestContext))), $socket, $loop); |         $this->_server = new IoServer(new HttpServer(new Router(new UrlMatcher($this->routes, new RequestContext))), $socket, $loop); | ||||||
| @ -85,13 +81,14 @@ class App { | |||||||
|         $policy = new FlashPolicy; |         $policy = new FlashPolicy; | ||||||
|         $policy->addAllowedAccess($httpHost, 80); |         $policy->addAllowedAccess($httpHost, 80); | ||||||
|         $policy->addAllowedAccess($httpHost, $port); |         $policy->addAllowedAccess($httpHost, $port); | ||||||
|         $flashSock = new Reactor($loop); | 
 | ||||||
|         $this->flashServer = new IoServer($policy, $flashSock); |  | ||||||
|         if (80 == $port) { |         if (80 == $port) { | ||||||
|             $flashSock->listen(843, '0.0.0.0'); |             $flashUri = '0.0.0.0:843'; | ||||||
|         } else { |         } else { | ||||||
|             $flashSock->listen(8843); |             $flashUri = 8843; | ||||||
|         } |         } | ||||||
|  |         $flashSock = new Reactor($flashUri, $loop); | ||||||
|  |         $this->flashServer = new IoServer($policy, $flashSock); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -107,8 +104,10 @@ class App { | |||||||
|             $decorated = $controller; |             $decorated = $controller; | ||||||
|         } elseif ($controller instanceof WampServerInterface) { |         } elseif ($controller instanceof WampServerInterface) { | ||||||
|             $decorated = new WsServer(new WampServer($controller)); |             $decorated = new WsServer(new WampServer($controller)); | ||||||
|  |             $decorated->enableKeepAlive($this->_server->loop); | ||||||
|         } elseif ($controller instanceof MessageComponentInterface) { |         } elseif ($controller instanceof MessageComponentInterface) { | ||||||
|             $decorated = new WsServer($controller); |             $decorated = new WsServer($controller); | ||||||
|  |             $decorated->enableKeepAlive($this->_server->loop); | ||||||
|         } else { |         } else { | ||||||
|             $decorated = $controller; |             $decorated = $controller; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ namespace Ratchet; | |||||||
|  * The version of Ratchet being used |  * The version of Ratchet being used | ||||||
|  * @var string |  * @var string | ||||||
|  */ |  */ | ||||||
| const VERSION = 'Ratchet/0.3.6'; | const VERSION = 'Ratchet/0.4.1'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * A proxy object representing a connection to the application |  * A proxy object representing a connection to the application | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								src/Ratchet/Http/CloseResponseTrait.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/Ratchet/Http/CloseResponseTrait.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | <?php | ||||||
|  | namespace Ratchet\Http; | ||||||
|  | use Ratchet\ConnectionInterface; | ||||||
|  | use GuzzleHttp\Psr7 as gPsr; | ||||||
|  | use GuzzleHttp\Psr7\Response; | ||||||
|  | 
 | ||||||
|  | trait CloseResponseTrait { | ||||||
|  |     /** | ||||||
|  |      * Close a connection with an HTTP response | ||||||
|  |      * @param \Ratchet\ConnectionInterface $conn | ||||||
|  |      * @param int                          $code HTTP status code | ||||||
|  |      * @return null | ||||||
|  |      */ | ||||||
|  |     private function close(ConnectionInterface $conn, $code = 400, array $additional_headers = []) { | ||||||
|  |         $response = new Response($code, array_merge([ | ||||||
|  |             'X-Powered-By' => \Ratchet\VERSION | ||||||
|  |         ], $additional_headers)); | ||||||
|  | 
 | ||||||
|  |         $conn->send(gPsr\str($response)); | ||||||
|  |         $conn->close(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,34 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\Http\Guzzle\Http\Message; |  | ||||||
| use Guzzle\Http\Message\RequestFactory as GuzzleRequestFactory; |  | ||||||
| use Guzzle\Http\EntityBody; |  | ||||||
| 
 |  | ||||||
| class RequestFactory extends GuzzleRequestFactory { |  | ||||||
| 
 |  | ||||||
|     protected static $ratchetInstance; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public static function getInstance() |  | ||||||
|     { |  | ||||||
|         // @codeCoverageIgnoreStart
 |  | ||||||
|         if (!static::$ratchetInstance) { |  | ||||||
|             static::$ratchetInstance = new static(); |  | ||||||
|         } |  | ||||||
|         // @codeCoverageIgnoreEnd
 |  | ||||||
| 
 |  | ||||||
|         return static::$ratchetInstance; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function create($method, $url, $headers = null, $body = '', array $options = array()) { |  | ||||||
|         $c = $this->entityEnclosingRequestClass; |  | ||||||
|         $request = new $c($method, $url, $headers); |  | ||||||
|         $request->setBody(EntityBody::factory($body)); |  | ||||||
| 
 |  | ||||||
|         return $request; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -2,11 +2,11 @@ | |||||||
| namespace Ratchet\Http; | namespace Ratchet\Http; | ||||||
| use Ratchet\MessageInterface; | use Ratchet\MessageInterface; | ||||||
| use Ratchet\ConnectionInterface; | use Ratchet\ConnectionInterface; | ||||||
| use Ratchet\Http\Guzzle\Http\Message\RequestFactory; | use GuzzleHttp\Psr7 as gPsr; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * This class receives streaming data from a client request |  * This class receives streaming data from a client request | ||||||
|  * and parses HTTP headers, returning a Guzzle Request object |  * and parses HTTP headers, returning a PSR-7 Request object | ||||||
|  * once it's been buffered |  * once it's been buffered | ||||||
|  */ |  */ | ||||||
| class HttpRequestParser implements MessageInterface { | class HttpRequestParser implements MessageInterface { | ||||||
| @ -22,7 +22,7 @@ class HttpRequestParser implements MessageInterface { | |||||||
|     /** |     /** | ||||||
|      * @param \Ratchet\ConnectionInterface $context |      * @param \Ratchet\ConnectionInterface $context | ||||||
|      * @param string                       $data Data stream to buffer |      * @param string                       $data Data stream to buffer | ||||||
|      * @return \Guzzle\Http\Message\RequestInterface|null |      * @return \Psr\Http\Message\RequestInterface | ||||||
|      * @throws \OverflowException If the message buffer has become too large |      * @throws \OverflowException If the message buffer has become too large | ||||||
|      */ |      */ | ||||||
|     public function onMessage(ConnectionInterface $context, $data) { |     public function onMessage(ConnectionInterface $context, $data) { | ||||||
| @ -37,7 +37,7 @@ class HttpRequestParser implements MessageInterface { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if ($this->isEom($context->httpBuffer)) { |         if ($this->isEom($context->httpBuffer)) { | ||||||
|             $request = RequestFactory::getInstance()->fromMessage($context->httpBuffer); |             $request = $this->parse($context->httpBuffer); | ||||||
| 
 | 
 | ||||||
|             unset($context->httpBuffer); |             unset($context->httpBuffer); | ||||||
| 
 | 
 | ||||||
| @ -53,4 +53,12 @@ class HttpRequestParser implements MessageInterface { | |||||||
|     public function isEom($message) { |     public function isEom($message) { | ||||||
|         return (boolean)strpos($message, static::EOM); |         return (boolean)strpos($message, static::EOM); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @param string $headers | ||||||
|  |      * @return \Psr\Http\Message\RequestInterface | ||||||
|  |      */ | ||||||
|  |     public function parse($headers) { | ||||||
|  |         return gPsr\parse_request($headers); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,9 +2,10 @@ | |||||||
| namespace Ratchet\Http; | namespace Ratchet\Http; | ||||||
| use Ratchet\MessageComponentInterface; | use Ratchet\MessageComponentInterface; | ||||||
| use Ratchet\ConnectionInterface; | use Ratchet\ConnectionInterface; | ||||||
| use Guzzle\Http\Message\Response; |  | ||||||
| 
 | 
 | ||||||
| class HttpServer implements MessageComponentInterface { | class HttpServer implements MessageComponentInterface { | ||||||
|  |     use CloseResponseTrait; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Buffers incoming HTTP requests returning a Guzzle Request when coalesced |      * Buffers incoming HTTP requests returning a Guzzle Request when coalesced | ||||||
|      * @var HttpRequestParser |      * @var HttpRequestParser | ||||||
| @ -72,19 +73,4 @@ class HttpServer implements MessageComponentInterface { | |||||||
|             $this->close($conn, 500); |             $this->close($conn, 500); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Close a connection with an HTTP response |  | ||||||
|      * @param \Ratchet\ConnectionInterface $conn |  | ||||||
|      * @param int                          $code HTTP status code |  | ||||||
|      * @return null |  | ||||||
|      */ |  | ||||||
|     protected function close(ConnectionInterface $conn, $code = 400) { |  | ||||||
|         $response = new Response($code, array( |  | ||||||
|             'X-Powered-By' => \Ratchet\VERSION |  | ||||||
|         )); |  | ||||||
| 
 |  | ||||||
|         $conn->send((string)$response); |  | ||||||
|         $conn->close(); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,12 +2,12 @@ | |||||||
| namespace Ratchet\Http; | namespace Ratchet\Http; | ||||||
| use Ratchet\MessageComponentInterface; | use Ratchet\MessageComponentInterface; | ||||||
| use Ratchet\ConnectionInterface; | use Ratchet\ConnectionInterface; | ||||||
| use Guzzle\Http\Message\RequestInterface; | use Psr\Http\Message\RequestInterface; | ||||||
| 
 | 
 | ||||||
| interface HttpServerInterface extends MessageComponentInterface { | interface HttpServerInterface extends MessageComponentInterface { | ||||||
|     /** |     /** | ||||||
|      * @param \Ratchet\ConnectionInterface          $conn |      * @param \Ratchet\ConnectionInterface          $conn | ||||||
|      * @param \Guzzle\Http\Message\RequestInterface $request null is default because PHP won't let me overload; don't pass null!!! |      * @param \Psr\Http\Message\RequestInterface    $request null is default because PHP won't let me overload; don't pass null!!! | ||||||
|      * @throws \UnexpectedValueException if a RequestInterface is not passed |      * @throws \UnexpectedValueException if a RequestInterface is not passed | ||||||
|      */ |      */ | ||||||
|     public function onOpen(ConnectionInterface $conn, RequestInterface $request = null); |     public function onOpen(ConnectionInterface $conn, RequestInterface $request = null); | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								src/Ratchet/Http/NoOpHttpServerController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/Ratchet/Http/NoOpHttpServerController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | <?php | ||||||
|  | namespace Ratchet\Http; | ||||||
|  | use Ratchet\ConnectionInterface; | ||||||
|  | use Psr\Http\Message\RequestInterface; | ||||||
|  | 
 | ||||||
|  | class NoOpHttpServerController implements HttpServerInterface { | ||||||
|  |     public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function onMessage(ConnectionInterface $from, $msg) { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function onClose(ConnectionInterface $conn) { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function onError(ConnectionInterface $conn, \Exception $e) { | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,9 +1,8 @@ | |||||||
| <?php | <?php | ||||||
| namespace Ratchet\Http; | namespace Ratchet\Http; | ||||||
| use Guzzle\Http\Message\RequestInterface; |  | ||||||
| use Ratchet\ConnectionInterface; | use Ratchet\ConnectionInterface; | ||||||
| use Ratchet\MessageComponentInterface; | use Ratchet\MessageComponentInterface; | ||||||
| use Guzzle\Http\Message\Response; | use Psr\Http\Message\RequestInterface; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * A middleware to ensure JavaScript clients connecting are from the expected domain. |  * A middleware to ensure JavaScript clients connecting are from the expected domain. | ||||||
| @ -11,18 +10,20 @@ use Guzzle\Http\Message\Response; | |||||||
|  * Note: This can be spoofed from non-web browser clients |  * Note: This can be spoofed from non-web browser clients | ||||||
|  */ |  */ | ||||||
| class OriginCheck implements HttpServerInterface { | class OriginCheck implements HttpServerInterface { | ||||||
|  |     use CloseResponseTrait; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * @var \Ratchet\MessageComponentInterface |      * @var \Ratchet\MessageComponentInterface | ||||||
|      */ |      */ | ||||||
|     protected $_component; |     protected $_component; | ||||||
| 
 | 
 | ||||||
|     public $allowedOrigins = array(); |     public $allowedOrigins = []; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param MessageComponentInterface $component Component/Application to decorate |      * @param MessageComponentInterface $component Component/Application to decorate | ||||||
|      * @param array                     $allowed An array of allowed domains that are allowed to connect from |      * @param array                     $allowed   An array of allowed domains that are allowed to connect from | ||||||
|      */ |      */ | ||||||
|     public function __construct(MessageComponentInterface $component, array $allowed = array()) { |     public function __construct(MessageComponentInterface $component, array $allowed = []) { | ||||||
|         $this->_component = $component; |         $this->_component = $component; | ||||||
|         $this->allowedOrigins += $allowed; |         $this->allowedOrigins += $allowed; | ||||||
|     } |     } | ||||||
| @ -31,7 +32,7 @@ class OriginCheck implements HttpServerInterface { | |||||||
|      * {@inheritdoc} |      * {@inheritdoc} | ||||||
|      */ |      */ | ||||||
|     public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { |     public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { | ||||||
|         $header = (string)$request->getHeader('Origin'); |         $header = (string)$request->getHeader('Origin')[0]; | ||||||
|         $origin = parse_url($header, PHP_URL_HOST) ?: $header; |         $origin = parse_url($header, PHP_URL_HOST) ?: $header; | ||||||
| 
 | 
 | ||||||
|         if (!in_array($origin, $this->allowedOrigins)) { |         if (!in_array($origin, $this->allowedOrigins)) { | ||||||
| @ -61,19 +62,4 @@ class OriginCheck implements HttpServerInterface { | |||||||
|     function onError(ConnectionInterface $conn, \Exception $e) { |     function onError(ConnectionInterface $conn, \Exception $e) { | ||||||
|         return $this->_component->onError($conn, $e); |         return $this->_component->onError($conn, $e); | ||||||
|     } |     } | ||||||
| 
 | } | ||||||
|     /** |  | ||||||
|      * Close a connection with an HTTP response |  | ||||||
|      * @param \Ratchet\ConnectionInterface $conn |  | ||||||
|      * @param int                          $code HTTP status code |  | ||||||
|      * @return null |  | ||||||
|      */ |  | ||||||
|     protected function close(ConnectionInterface $conn, $code = 400) { |  | ||||||
|         $response = new Response($code, array( |  | ||||||
|             'X-Powered-By' => \Ratchet\VERSION |  | ||||||
|         )); |  | ||||||
| 
 |  | ||||||
|         $conn->send((string)$response); |  | ||||||
|         $conn->close(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,21 +1,25 @@ | |||||||
| <?php | <?php | ||||||
| namespace Ratchet\Http; | namespace Ratchet\Http; | ||||||
| use Ratchet\ConnectionInterface; | use Ratchet\ConnectionInterface; | ||||||
| use Guzzle\Http\Message\RequestInterface; | use Psr\Http\Message\RequestInterface; | ||||||
| use Guzzle\Http\Message\Response; |  | ||||||
| use Guzzle\Http\Url; |  | ||||||
| use Symfony\Component\Routing\Matcher\UrlMatcherInterface; | use Symfony\Component\Routing\Matcher\UrlMatcherInterface; | ||||||
| use Symfony\Component\Routing\Exception\MethodNotAllowedException; | use Symfony\Component\Routing\Exception\MethodNotAllowedException; | ||||||
| use Symfony\Component\Routing\Exception\ResourceNotFoundException; | use Symfony\Component\Routing\Exception\ResourceNotFoundException; | ||||||
|  | use GuzzleHttp\Psr7 as gPsr; | ||||||
| 
 | 
 | ||||||
| class Router implements HttpServerInterface { | class Router implements HttpServerInterface { | ||||||
|  |     use CloseResponseTrait; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface |      * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface | ||||||
|      */ |      */ | ||||||
|     protected $_matcher; |     protected $_matcher; | ||||||
| 
 | 
 | ||||||
|  |     private $_noopController; | ||||||
|  | 
 | ||||||
|     public function __construct(UrlMatcherInterface $matcher) { |     public function __construct(UrlMatcherInterface $matcher) { | ||||||
|         $this->_matcher = $matcher; |         $this->_matcher = $matcher; | ||||||
|  |         $this->_noopController = new NoOpHttpServerController; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -27,12 +31,16 @@ class Router implements HttpServerInterface { | |||||||
|             throw new \UnexpectedValueException('$request can not be null'); |             throw new \UnexpectedValueException('$request can not be null'); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         $conn->controller = $this->_noopController; | ||||||
|  | 
 | ||||||
|  |         $uri = $request->getUri(); | ||||||
|  | 
 | ||||||
|         $context = $this->_matcher->getContext(); |         $context = $this->_matcher->getContext(); | ||||||
|         $context->setMethod($request->getMethod()); |         $context->setMethod($request->getMethod()); | ||||||
|         $context->setHost($request->getHost()); |         $context->setHost($uri->getHost()); | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             $route = $this->_matcher->match($request->getPath()); |             $route = $this->_matcher->match($uri->getPath()); | ||||||
|         } catch (MethodNotAllowedException $nae) { |         } catch (MethodNotAllowedException $nae) { | ||||||
|             return $this->close($conn, 405, array('Allow' => $nae->getAllowedMethods())); |             return $this->close($conn, 405, array('Allow' => $nae->getAllowedMethods())); | ||||||
|         } catch (ResourceNotFoundException $nfe) { |         } catch (ResourceNotFoundException $nfe) { | ||||||
| @ -47,17 +55,15 @@ class Router implements HttpServerInterface { | |||||||
|             throw new \UnexpectedValueException('All routes must implement Ratchet\Http\HttpServerInterface'); |             throw new \UnexpectedValueException('All routes must implement Ratchet\Http\HttpServerInterface'); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $parameters = array(); |         $parameters = []; | ||||||
|         foreach($route as $key => $value) { |         foreach($route as $key => $value) { | ||||||
|             if ((is_string($key)) && ('_' !== substr($key, 0, 1))) { |             if ((is_string($key)) && ('_' !== substr($key, 0, 1))) { | ||||||
|                 $parameters[$key] = $value; |                 $parameters[$key] = $value; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         $parameters = array_merge($parameters, $request->getQuery()->getAll()); |         $parameters = array_merge($parameters, gPsr\parse_query($uri->getQuery() ?: '')); | ||||||
| 
 | 
 | ||||||
|         $url = Url::factory($request->getUrl()); |         $request = $request->withUri($uri->withQuery(gPsr\build_query($parameters))); | ||||||
|         $url->setQuery($parameters); |  | ||||||
|         $request->setUrl($url); |  | ||||||
| 
 | 
 | ||||||
|         $conn->controller = $route['_controller']; |         $conn->controller = $route['_controller']; | ||||||
|         $conn->controller->onOpen($conn, $request); |         $conn->controller->onOpen($conn, $request); | ||||||
| @ -66,14 +72,14 @@ class Router implements HttpServerInterface { | |||||||
|     /** |     /** | ||||||
|      * {@inheritdoc} |      * {@inheritdoc} | ||||||
|      */ |      */ | ||||||
|     function onMessage(ConnectionInterface $from, $msg) { |     public function onMessage(ConnectionInterface $from, $msg) { | ||||||
|         $from->controller->onMessage($from, $msg); |         $from->controller->onMessage($from, $msg); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * {@inheritdoc} |      * {@inheritdoc} | ||||||
|      */ |      */ | ||||||
|     function onClose(ConnectionInterface $conn) { |     public function onClose(ConnectionInterface $conn) { | ||||||
|         if (isset($conn->controller)) { |         if (isset($conn->controller)) { | ||||||
|             $conn->controller->onClose($conn); |             $conn->controller->onClose($conn); | ||||||
|         } |         } | ||||||
| @ -82,26 +88,9 @@ class Router implements HttpServerInterface { | |||||||
|     /** |     /** | ||||||
|      * {@inheritdoc} |      * {@inheritdoc} | ||||||
|      */ |      */ | ||||||
|     function onError(ConnectionInterface $conn, \Exception $e) { |     public function onError(ConnectionInterface $conn, \Exception $e) { | ||||||
|         if (isset($conn->controller)) { |         if (isset($conn->controller)) { | ||||||
|             $conn->controller->onError($conn, $e); |             $conn->controller->onError($conn, $e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Close a connection with an HTTP response |  | ||||||
|      * @param \Ratchet\ConnectionInterface $conn |  | ||||||
|      * @param int $code HTTP status code |  | ||||||
|      * @param array $additionalHeaders |  | ||||||
|      * @return null |  | ||||||
|      */ |  | ||||||
|     protected function close(ConnectionInterface $conn, $code = 400, array $additionalHeaders = array()) { |  | ||||||
|         $headers = array_merge(array( |  | ||||||
|             'X-Powered-By' => \Ratchet\VERSION |  | ||||||
|         ), $additionalHeaders); |  | ||||||
|         $response = new Response($code, $headers); |  | ||||||
| 
 |  | ||||||
|         $conn->send((string)$response); |  | ||||||
|         $conn->close(); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ use React\EventLoop\LoopInterface; | |||||||
| use React\Socket\ServerInterface; | use React\Socket\ServerInterface; | ||||||
| use React\EventLoop\Factory as LoopFactory; | use React\EventLoop\Factory as LoopFactory; | ||||||
| use React\Socket\Server as Reactor; | use React\Socket\Server as Reactor; | ||||||
|  | use React\Socket\SecureServer as SecureReactor; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Creates an open-ended socket to listen on a port for incoming connections. |  * Creates an open-ended socket to listen on a port for incoming connections. | ||||||
| @ -21,12 +22,6 @@ class IoServer { | |||||||
|      */ |      */ | ||||||
|     public $app; |     public $app; | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Array of React event handlers |  | ||||||
|      * @var \SplFixedArray |  | ||||||
|      */ |  | ||||||
|     protected $handlers; |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * The socket server the Ratchet Application is run off of |      * The socket server the Ratchet Application is run off of | ||||||
|      * @var \React\Socket\ServerInterface |      * @var \React\Socket\ServerInterface | ||||||
| @ -51,23 +46,17 @@ class IoServer { | |||||||
|         $this->socket = $socket; |         $this->socket = $socket; | ||||||
| 
 | 
 | ||||||
|         $socket->on('connection', array($this, 'handleConnect')); |         $socket->on('connection', array($this, 'handleConnect')); | ||||||
| 
 |  | ||||||
|         $this->handlers = new \SplFixedArray(3); |  | ||||||
|         $this->handlers[0] = array($this, 'handleData'); |  | ||||||
|         $this->handlers[1] = array($this, 'handleEnd'); |  | ||||||
|         $this->handlers[2] = array($this, 'handleError'); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param  \Ratchet\MessageComponentInterface $component The application that I/O will call when events are received |      * @param  \Ratchet\MessageComponentInterface $component  The application that I/O will call when events are received | ||||||
|      * @param  int                                $port      The port to server sockets on |      * @param  int                                $port       The port to server sockets on | ||||||
|      * @param  string                             $address   The address to receive sockets on (0.0.0.0 means receive connections from any) |      * @param  string                             $address    The address to receive sockets on (0.0.0.0 means receive connections from any) | ||||||
|      * @return IoServer |      * @return IoServer | ||||||
|      */ |      */ | ||||||
|     public static function factory(MessageComponentInterface $component, $port = 80, $address = '0.0.0.0') { |     public static function factory(MessageComponentInterface $component, $port = 80, $address = '0.0.0.0') { | ||||||
|         $loop   = LoopFactory::create(); |         $loop   = LoopFactory::create(); | ||||||
|         $socket = new Reactor($loop); |         $socket = new Reactor($address . ':' . $port, $loop); | ||||||
|         $socket->listen($port, $address); |  | ||||||
| 
 | 
 | ||||||
|         return new static($component, $socket, $loop); |         return new static($component, $socket, $loop); | ||||||
|     } |     } | ||||||
| @ -92,15 +81,25 @@ class IoServer { | |||||||
|      */ |      */ | ||||||
|     public function handleConnect($conn) { |     public function handleConnect($conn) { | ||||||
|         $conn->decor = new IoConnection($conn); |         $conn->decor = new IoConnection($conn); | ||||||
|  |         $conn->decor->resourceId = (int)$conn->stream; | ||||||
| 
 | 
 | ||||||
|         $conn->decor->resourceId    = (int)$conn->stream; |         $uri = $conn->getRemoteAddress(); | ||||||
|         $conn->decor->remoteAddress = $conn->getRemoteAddress(); |         $conn->decor->remoteAddress = trim( | ||||||
|  |             parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri, PHP_URL_HOST), | ||||||
|  |             '[]' | ||||||
|  |         ); | ||||||
| 
 | 
 | ||||||
|         $this->app->onOpen($conn->decor); |         $this->app->onOpen($conn->decor); | ||||||
| 
 | 
 | ||||||
|         $conn->on('data', $this->handlers[0]); |         $conn->on('data', function ($data) use ($conn) { | ||||||
|         $conn->on('end', $this->handlers[1]); |             $this->handleData($data, $conn); | ||||||
|         $conn->on('error', $this->handlers[2]); |         }); | ||||||
|  |         $conn->on('close', function () use ($conn) { | ||||||
|  |             $this->handleEnd($conn); | ||||||
|  |         }); | ||||||
|  |         $conn->on('error', function (\Exception $e) use ($conn) { | ||||||
|  |             $this->handleError($e, $conn); | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| <?php | <?php | ||||||
| namespace Ratchet\Session; | namespace Ratchet\Session; | ||||||
| use Ratchet\MessageComponentInterface; |  | ||||||
| use Ratchet\ConnectionInterface; | use Ratchet\ConnectionInterface; | ||||||
| use Ratchet\WebSocket\WsServerInterface; | use Ratchet\Http\HttpServerInterface; | ||||||
|  | use Psr\Http\Message\RequestInterface; | ||||||
| use Ratchet\Session\Storage\VirtualSessionStorage; | use Ratchet\Session\Storage\VirtualSessionStorage; | ||||||
| use Ratchet\Session\Serialize\HandlerInterface; | use Ratchet\Session\Serialize\HandlerInterface; | ||||||
| use Symfony\Component\HttpFoundation\Session\Session; | use Symfony\Component\HttpFoundation\Session\Session; | ||||||
| @ -14,7 +14,7 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; | |||||||
|  * Your website must also use Symfony HttpFoundation Sessions to read your sites session data |  * Your website must also use Symfony HttpFoundation Sessions to read your sites session data | ||||||
|  * If your are not using at least PHP 5.4 you must include a SessionHandlerInterface stub (is included in Symfony HttpFoundation, loaded w/ composer) |  * If your are not using at least PHP 5.4 you must include a SessionHandlerInterface stub (is included in Symfony HttpFoundation, loaded w/ composer) | ||||||
|  */ |  */ | ||||||
| class SessionProvider implements MessageComponentInterface, WsServerInterface { | class SessionProvider implements HttpServerInterface { | ||||||
|     /** |     /** | ||||||
|      * @var \Ratchet\MessageComponentInterface |      * @var \Ratchet\MessageComponentInterface | ||||||
|      */ |      */ | ||||||
| @ -38,13 +38,13 @@ class SessionProvider implements MessageComponentInterface, WsServerInterface { | |||||||
|     protected $_serializer; |     protected $_serializer; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param \Ratchet\MessageComponentInterface          $app |      * @param \Ratchet\Http\HttpServerInterface           $app | ||||||
|      * @param \SessionHandlerInterface                    $handler |      * @param \SessionHandlerInterface                    $handler | ||||||
|      * @param array                                       $options |      * @param array                                       $options | ||||||
|      * @param \Ratchet\Session\Serialize\HandlerInterface $serializer |      * @param \Ratchet\Session\Serialize\HandlerInterface $serializer | ||||||
|      * @throws \RuntimeException |      * @throws \RuntimeException | ||||||
|      */ |      */ | ||||||
|     public function __construct(MessageComponentInterface $app, \SessionHandlerInterface $handler, array $options = array(), HandlerInterface $serializer = null) { |     public function __construct(HttpServerInterface $app, \SessionHandlerInterface $handler, array $options = array(), HandlerInterface $serializer = null) { | ||||||
|         $this->_app     = $app; |         $this->_app     = $app; | ||||||
|         $this->_handler = $handler; |         $this->_handler = $handler; | ||||||
|         $this->_null    = new NullSessionHandler; |         $this->_null    = new NullSessionHandler; | ||||||
| @ -70,8 +70,20 @@ class SessionProvider implements MessageComponentInterface, WsServerInterface { | |||||||
|     /** |     /** | ||||||
|      * {@inheritdoc} |      * {@inheritdoc} | ||||||
|      */ |      */ | ||||||
|     function onOpen(ConnectionInterface $conn) { |     public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { | ||||||
|         if (!isset($conn->WebSocket) || null === ($id = $conn->WebSocket->request->getCookie(ini_get('session.name')))) { |         $sessionName = ini_get('session.name'); | ||||||
|  | 
 | ||||||
|  |         $id = array_reduce($request->getHeader('Cookie'), function($accumulator, $cookie) use ($sessionName) { | ||||||
|  |             if ($accumulator) { | ||||||
|  |                 return $accumulator; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             $crumbs = $this->parseCookie($cookie); | ||||||
|  | 
 | ||||||
|  |             return isset($crumbs['cookies'][$sessionName]) ? $crumbs['cookies'][$sessionName] : false; | ||||||
|  |         }, false); | ||||||
|  | 
 | ||||||
|  |         if (null === $request || false === $id) { | ||||||
|             $saveHandler = $this->_null; |             $saveHandler = $this->_null; | ||||||
|             $id = ''; |             $id = ''; | ||||||
|         } else { |         } else { | ||||||
| @ -84,7 +96,7 @@ class SessionProvider implements MessageComponentInterface, WsServerInterface { | |||||||
|             $conn->Session->start(); |             $conn->Session->start(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return $this->_app->onOpen($conn); |         return $this->_app->onOpen($conn, $request); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -110,17 +122,6 @@ class SessionProvider implements MessageComponentInterface, WsServerInterface { | |||||||
|         return $this->_app->onError($conn, $e); |         return $this->_app->onError($conn, $e); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getSubProtocols() { |  | ||||||
|         if ($this->_app instanceof WsServerInterface) { |  | ||||||
|             return $this->_app->getSubProtocols(); |  | ||||||
|         } else { |  | ||||||
|             return array(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * Set all the php session. ini options |      * Set all the php session. ini options | ||||||
|      * © Symfony |      * © Symfony | ||||||
| @ -158,4 +159,85 @@ class SessionProvider implements MessageComponentInterface, WsServerInterface { | |||||||
|     protected function toClassCase($langDef) { |     protected function toClassCase($langDef) { | ||||||
|         return str_replace(' ', '', ucwords(str_replace('_', ' ', $langDef))); |         return str_replace(' ', '', ucwords(str_replace('_', ' ', $langDef))); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Taken from Guzzle3 | ||||||
|  |      */ | ||||||
|  |     private static $cookieParts = array( | ||||||
|  |         'domain'      => 'Domain', | ||||||
|  |         'path'        => 'Path', | ||||||
|  |         'max_age'     => 'Max-Age', | ||||||
|  |         'expires'     => 'Expires', | ||||||
|  |         'version'     => 'Version', | ||||||
|  |         'secure'      => 'Secure', | ||||||
|  |         'port'        => 'Port', | ||||||
|  |         'discard'     => 'Discard', | ||||||
|  |         'comment'     => 'Comment', | ||||||
|  |         'comment_url' => 'Comment-Url', | ||||||
|  |         'http_only'   => 'HttpOnly' | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Taken from Guzzle3 | ||||||
|  |      */ | ||||||
|  |     private function parseCookie($cookie, $host = null, $path = null, $decode = false) { | ||||||
|  |         // Explode the cookie string using a series of semicolons
 | ||||||
|  |         $pieces = array_filter(array_map('trim', explode(';', $cookie))); | ||||||
|  | 
 | ||||||
|  |         // The name of the cookie (first kvp) must include an equal sign.
 | ||||||
|  |         if (empty($pieces) || !strpos($pieces[0], '=')) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Create the default return array
 | ||||||
|  |         $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array( | ||||||
|  |             'cookies'   => array(), | ||||||
|  |             'data'      => array(), | ||||||
|  |             'path'      => $path ?: '/', | ||||||
|  |             'http_only' => false, | ||||||
|  |             'discard'   => false, | ||||||
|  |             'domain'    => $host | ||||||
|  |         )); | ||||||
|  |         $foundNonCookies = 0; | ||||||
|  | 
 | ||||||
|  |         // Add the cookie pieces into the parsed data array
 | ||||||
|  |         foreach ($pieces as $part) { | ||||||
|  | 
 | ||||||
|  |             $cookieParts = explode('=', $part, 2); | ||||||
|  |             $key = trim($cookieParts[0]); | ||||||
|  | 
 | ||||||
|  |             if (count($cookieParts) == 1) { | ||||||
|  |                 // Can be a single value (e.g. secure, httpOnly)
 | ||||||
|  |                 $value = true; | ||||||
|  |             } else { | ||||||
|  |                 // Be sure to strip wrapping quotes
 | ||||||
|  |                 $value = trim($cookieParts[1], " \n\r\t\0\x0B\""); | ||||||
|  |                 if ($decode) { | ||||||
|  |                     $value = urldecode($value); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Only check for non-cookies when cookies have been found
 | ||||||
|  |             if (!empty($data['cookies'])) { | ||||||
|  |                 foreach (self::$cookieParts as $mapValue => $search) { | ||||||
|  |                     if (!strcasecmp($search, $key)) { | ||||||
|  |                         $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value; | ||||||
|  |                         $foundNonCookies++; | ||||||
|  |                         continue 2; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a
 | ||||||
|  |             // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data.
 | ||||||
|  |             $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Calculate the expires date
 | ||||||
|  |         if (!$data['expires'] && $data['max_age']) { | ||||||
|  |             $data['expires'] = time() + (int) $data['max_age']; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $data; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ use Ratchet\ConnectionInterface; | |||||||
|  * WebSocket Application Messaging Protocol |  * WebSocket Application Messaging Protocol | ||||||
|  * |  * | ||||||
|  * @link http://wamp.ws/spec |  * @link http://wamp.ws/spec | ||||||
|  * @link https://github.com/oberstet/AutobahnJS |  * @link https://github.com/oberstet/autobahn-js | ||||||
|  * |  * | ||||||
|  * +--------------+----+------------------+ |  * +--------------+----+------------------+ | ||||||
|  * | Message Type | ID | DIRECTION        | |  * | Message Type | ID | DIRECTION        | | ||||||
| @ -62,9 +62,9 @@ class ServerProtocol implements MessageComponentInterface, WsServerInterface { | |||||||
|             $subs[] = 'wamp'; |             $subs[] = 'wamp'; | ||||||
| 
 | 
 | ||||||
|             return $subs; |             return $subs; | ||||||
|         } else { |  | ||||||
|             return array('wamp'); |  | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         return ['wamp']; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -93,6 +93,10 @@ class ServerProtocol implements MessageComponentInterface, WsServerInterface { | |||||||
|             throw new Exception("Invalid WAMP message format"); |             throw new Exception("Invalid WAMP message format"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if (isset($json[1]) && !(is_string($json[1]) || is_numeric($json[1]))) { | ||||||
|  |             throw new Exception('Invalid Topic, must be a string'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         switch ($json[0]) { |         switch ($json[0]) { | ||||||
|             case static::MSG_PREFIX: |             case static::MSG_PREFIX: | ||||||
|                 $from->WAMP->prefixes[$json[1]] = $json[2]; |                 $from->WAMP->prefixes[$json[1]] = $json[2]; | ||||||
| @ -122,13 +126,13 @@ class ServerProtocol implements MessageComponentInterface, WsServerInterface { | |||||||
|                 $exclude  = (array_key_exists(3, $json) ? $json[3] : null); |                 $exclude  = (array_key_exists(3, $json) ? $json[3] : null); | ||||||
|                 if (!is_array($exclude)) { |                 if (!is_array($exclude)) { | ||||||
|                     if (true === (boolean)$exclude) { |                     if (true === (boolean)$exclude) { | ||||||
|                         $exclude = array($from->WAMP->sessionId); |                         $exclude = [$from->WAMP->sessionId]; | ||||||
|                     } else { |                     } else { | ||||||
|                         $exclude = array(); |                         $exclude = []; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 $eligible = (array_key_exists(4, $json) ? $json[4] : array()); |                 $eligible = (array_key_exists(4, $json) ? $json[4] : []); | ||||||
| 
 | 
 | ||||||
|                 $this->_decorating->onPublish($from, $from->getUri($json[1]), $json[2], $exclude, $eligible); |                 $this->_decorating->onPublish($from, $from->getUri($json[1]), $json[2], $exclude, $eligible); | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -6,13 +6,6 @@ use Ratchet\ConnectionInterface; | |||||||
|  * A topic/channel containing connections that have subscribed to it |  * A topic/channel containing connections that have subscribed to it | ||||||
|  */ |  */ | ||||||
| class Topic implements \IteratorAggregate, \Countable { | class Topic implements \IteratorAggregate, \Countable { | ||||||
|     /** |  | ||||||
|      * If true the TopicManager will destroy this object if it's ever empty of connections |  | ||||||
|      * @deprecated in v0.4 |  | ||||||
|      * @type bool |  | ||||||
|      */ |  | ||||||
|     public $autoDelete = false; |  | ||||||
| 
 |  | ||||||
|     private $id; |     private $id; | ||||||
| 
 | 
 | ||||||
|     private $subscribers; |     private $subscribers; | ||||||
| @ -38,7 +31,7 @@ class Topic implements \IteratorAggregate, \Countable { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Send a message to all the connections in this topic |      * Send a message to all the connections in this topic | ||||||
|      * @param string $msg Payload to publish |      * @param string|array $msg Payload to publish | ||||||
|      * @param array $exclude A list of session IDs the message should be excluded from (blacklist) |      * @param array $exclude A list of session IDs the message should be excluded from (blacklist) | ||||||
|      * @param array $eligible A list of session Ids the message should be send to (whitelist) |      * @param array $eligible A list of session Ids the message should be send to (whitelist) | ||||||
|      * @return Topic The same Topic object to chain |      * @return Topic The same Topic object to chain | ||||||
|  | |||||||
| @ -118,7 +118,7 @@ class TopicManager implements WsServerInterface, WampServerInterface { | |||||||
| 
 | 
 | ||||||
|         $this->topicLookup[$topic->getId()]->remove($conn); |         $this->topicLookup[$topic->getId()]->remove($conn); | ||||||
| 
 | 
 | ||||||
|         if ($topic->autoDelete && 0 === $topic->count()) { |         if (0 === $topic->count()) { | ||||||
|             unset($this->topicLookup[$topic->getId()]); |             unset($this->topicLookup[$topic->getId()]); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ use Ratchet\ConnectionInterface; | |||||||
|  * Enable support for the official WAMP sub-protocol in your application |  * Enable support for the official WAMP sub-protocol in your application | ||||||
|  * WAMP allows for Pub/Sub and RPC |  * WAMP allows for Pub/Sub and RPC | ||||||
|  * @link http://wamp.ws The WAMP specification |  * @link http://wamp.ws The WAMP specification | ||||||
|  * @link https://github.com/oberstet/AutobahnJS Souce for client side library |  * @link https://github.com/oberstet/autobahn-js Souce for client side library | ||||||
|  * @link http://autobahn.s3.amazonaws.com/js/autobahn.min.js Minified client side library |  * @link http://autobahn.s3.amazonaws.com/js/autobahn.min.js Minified client side library | ||||||
|  */ |  */ | ||||||
| class WampServer implements MessageComponentInterface, WsServerInterface { | class WampServer implements MessageComponentInterface, WsServerInterface { | ||||||
|  | |||||||
							
								
								
									
										20
									
								
								src/Ratchet/WebSocket/ConnContext.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/Ratchet/WebSocket/ConnContext.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | <?php | ||||||
|  | namespace Ratchet\WebSocket; | ||||||
|  | use Ratchet\RFC6455\Messaging\MessageBuffer; | ||||||
|  | 
 | ||||||
|  | class ConnContext { | ||||||
|  |     /** | ||||||
|  |      * @var \Ratchet\WebSocket\WsConnection | ||||||
|  |      */ | ||||||
|  |     public $connection; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @var \Ratchet\RFC6455\Messaging\MessageBuffer; | ||||||
|  |      */ | ||||||
|  |     public $buffer; | ||||||
|  | 
 | ||||||
|  |     public function __construct(WsConnection $conn, MessageBuffer $buffer) { | ||||||
|  |         $this->connection = $conn; | ||||||
|  |         $this->buffer = $buffer; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,31 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Encoding; |  | ||||||
| 
 |  | ||||||
| class ToggleableValidator implements ValidatorInterface { |  | ||||||
|     /** |  | ||||||
|      * Toggle if checkEncoding checks the encoding or not |  | ||||||
|      * @var bool |  | ||||||
|      */ |  | ||||||
|     public $on; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @var Validator |  | ||||||
|      */ |  | ||||||
|     private $validator; |  | ||||||
| 
 |  | ||||||
|     public function __construct($on = true) { |  | ||||||
|         $this->validator = new Validator; |  | ||||||
|         $this->on        = (boolean)$on; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function checkEncoding($str, $encoding) { |  | ||||||
|         if (!(boolean)$this->on) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $this->validator->checkEncoding($str, $encoding); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,93 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Encoding; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * This class handled encoding validation |  | ||||||
|  */ |  | ||||||
| class Validator { |  | ||||||
|     const UTF8_ACCEPT = 0; |  | ||||||
|     const UTF8_REJECT = 1; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Incremental UTF-8 validator with constant memory consumption (minimal state). |  | ||||||
|      * |  | ||||||
|      * Implements the algorithm "Flexible and Economical UTF-8 Decoder" by |  | ||||||
|      * Bjoern Hoehrmann (http://bjoern.hoehrmann.de/utf-8/decoder/dfa/). |  | ||||||
|      */ |  | ||||||
|     protected static $dfa = array( |  | ||||||
|         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 00..1f
 |  | ||||||
|         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 20..3f
 |  | ||||||
|         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 40..5f
 |  | ||||||
|         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 60..7f
 |  | ||||||
|         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, # 80..9f
 |  | ||||||
|         7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, # a0..bf
 |  | ||||||
|         8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, # c0..df
 |  | ||||||
|         0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, # e0..ef
 |  | ||||||
|         0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, # f0..ff
 |  | ||||||
|         0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, # s0..s0
 |  | ||||||
|         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, # s1..s2
 |  | ||||||
|         1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, # s3..s4
 |  | ||||||
|         1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, # s5..s6
 |  | ||||||
|         1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, # s7..s8
 |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Lookup if mbstring is available |  | ||||||
|      * @var bool |  | ||||||
|      */ |  | ||||||
|      private $hasMbString = false; |  | ||||||
| 
 |  | ||||||
|      /** |  | ||||||
|       * Lookup if iconv is available |  | ||||||
|       * @var bool |  | ||||||
|       */ |  | ||||||
|      private $hasIconv = false; |  | ||||||
| 
 |  | ||||||
|     public function __construct() { |  | ||||||
|         $this->hasMbString = extension_loaded('mbstring'); |  | ||||||
|         $this->hasIconv    = extension_loaded('iconv'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param  string $str     The value to check the encoding |  | ||||||
|      * @param  string $against The type of encoding to check against |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     public function checkEncoding($str, $against) { |  | ||||||
|         if ('UTF-8' == $against) { |  | ||||||
|             return $this->isUtf8($str); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if ($this->hasMbString) { |  | ||||||
|             return mb_check_encoding($str, $against); |  | ||||||
|         } elseif ($this->hasIconv) { |  | ||||||
|             return ($str == iconv($against, "{$against}//IGNORE", $str)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     protected function isUtf8($str) { |  | ||||||
|         if ($this->hasMbString) { |  | ||||||
|             if (false === mb_check_encoding($str, 'UTF-8')) { |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } elseif ($this->hasIconv) { |  | ||||||
|             if ($str != iconv('UTF-8', 'UTF-8//IGNORE', $str)) { |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $state = static::UTF8_ACCEPT; |  | ||||||
| 
 |  | ||||||
|         for ($i = 0, $len   = strlen($str); $i < $len; $i++) { |  | ||||||
|             $state = static::$dfa[256 + ($state << 4) + static::$dfa[ord($str[$i])]]; |  | ||||||
| 
 |  | ||||||
|             if (static::UTF8_REJECT === $state) { |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,12 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Encoding; |  | ||||||
| 
 |  | ||||||
| interface ValidatorInterface { |  | ||||||
|     /** |  | ||||||
|      * Verify a string matches the encoding type |  | ||||||
|      * @param  string $str      The string to check |  | ||||||
|      * @param  string $encoding The encoding type to check against |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     function checkEncoding($str, $encoding); |  | ||||||
| } |  | ||||||
							
								
								
									
										8
									
								
								src/Ratchet/WebSocket/MessageCallableInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/Ratchet/WebSocket/MessageCallableInterface.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | <?php | ||||||
|  | namespace Ratchet\WebSocket; | ||||||
|  | use Ratchet\ConnectionInterface; | ||||||
|  | use Ratchet\RFC6455\Messaging\MessageInterface; | ||||||
|  | 
 | ||||||
|  | interface MessageCallableInterface { | ||||||
|  |     public function onMessage(ConnectionInterface $conn, MessageInterface $msg); | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								src/Ratchet/WebSocket/MessageComponentInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/Ratchet/WebSocket/MessageComponentInterface.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | <?php | ||||||
|  | namespace Ratchet\WebSocket; | ||||||
|  | use Ratchet\ComponentInterface; | ||||||
|  | 
 | ||||||
|  | interface MessageComponentInterface extends ComponentInterface, MessageCallableInterface { | ||||||
|  | } | ||||||
| @ -1,28 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version; |  | ||||||
| 
 |  | ||||||
| interface DataInterface { |  | ||||||
|     /** |  | ||||||
|      * Determine if the message is complete or still fragmented |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     function isCoalesced(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get the number of bytes the payload is set to be |  | ||||||
|      * @return int |  | ||||||
|      */ |  | ||||||
|     function getPayloadLength(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get the payload (message) sent from peer |  | ||||||
|      * @return string |  | ||||||
|      */ |  | ||||||
|     function getPayload(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get raw contents of the message |  | ||||||
|      * @return string |  | ||||||
|      */ |  | ||||||
|     function getContents(); |  | ||||||
| } |  | ||||||
| @ -1,38 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version; |  | ||||||
| 
 |  | ||||||
| interface FrameInterface extends DataInterface { |  | ||||||
|     /** |  | ||||||
|      * Add incoming data to the frame from peer |  | ||||||
|      * @param string |  | ||||||
|      */ |  | ||||||
|     function addBuffer($buf); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Is this the final frame in a fragmented message? |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     function isFinal(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Is the payload masked? |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     function isMasked(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return int |  | ||||||
|      */ |  | ||||||
|     function getOpcode(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return int |  | ||||||
|      */ |  | ||||||
|     //function getReceivedPayloadLength();
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 32-big string |  | ||||||
|      * @return string |  | ||||||
|      */ |  | ||||||
|     function getMaskingKey(); |  | ||||||
| } |  | ||||||
| @ -1,120 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version; |  | ||||||
| use Ratchet\ConnectionInterface; |  | ||||||
| use Ratchet\MessageInterface; |  | ||||||
| use Ratchet\WebSocket\Version\Hixie76\Connection; |  | ||||||
| use Guzzle\Http\Message\RequestInterface; |  | ||||||
| use Guzzle\Http\Message\Response; |  | ||||||
| use Ratchet\WebSocket\Version\Hixie76\Frame; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * FOR THE LOVE OF BEER, PLEASE PLEASE PLEASE DON'T allow the use of this in your application! |  | ||||||
|  * Hixie76 is bad for 2 (there's more) reasons: |  | ||||||
|  *  1) The handshake is done in HTTP, which includes a key for signing in the body... |  | ||||||
|  *     BUT there is no Length defined in the header (as per HTTP spec) so the TCP buffer can't tell when the message is done! |  | ||||||
|  *  2) By nature it's insecure.  Google did a test study where they were able to do a |  | ||||||
|  *     man-in-the-middle attack on 10%-15% of the people who saw their ad who had a browser (currently only Safari) supporting the Hixie76 protocol. |  | ||||||
|  *     This was exploited by taking advantage of proxy servers in front of the user who ignored some HTTP headers in the handshake |  | ||||||
|  * The Hixie76 is currently implemented by Safari |  | ||||||
|  * @link http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 |  | ||||||
|  */ |  | ||||||
| class Hixie76 implements VersionInterface { |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function isProtocol(RequestInterface $request) { |  | ||||||
|         return !(null === $request->getHeader('Sec-WebSocket-Key2')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getVersionNumber() { |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param  \Guzzle\Http\Message\RequestInterface $request |  | ||||||
|      * @return \Guzzle\Http\Message\Response |  | ||||||
|      * @throws \UnderflowException If there hasn't been enough data received |  | ||||||
|      */ |  | ||||||
|     public function handshake(RequestInterface $request) { |  | ||||||
|         $body = substr($request->getBody(), 0, 8); |  | ||||||
|         if (8 !== strlen($body)) { |  | ||||||
|             throw new \UnderflowException("Not enough data received to issue challenge response"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $challenge = $this->sign((string)$request->getHeader('Sec-WebSocket-Key1'), (string)$request->getHeader('Sec-WebSocket-Key2'), $body); |  | ||||||
| 
 |  | ||||||
|         $headers = array( |  | ||||||
|             'Upgrade'                => 'WebSocket' |  | ||||||
|           , 'Connection'             => 'Upgrade' |  | ||||||
|           , 'Sec-WebSocket-Origin'   => (string)$request->getHeader('Origin') |  | ||||||
|           , 'Sec-WebSocket-Location' => 'ws://' . (string)$request->getHeader('Host') . $request->getPath() |  | ||||||
|         ); |  | ||||||
| 
 |  | ||||||
|         $response = new Response(101, $headers, $challenge); |  | ||||||
|         $response->setStatus(101, 'WebSocket Protocol Handshake'); |  | ||||||
| 
 |  | ||||||
|         return $response; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function upgradeConnection(ConnectionInterface $conn, MessageInterface $coalescedCallback) { |  | ||||||
|         $upgraded = new Connection($conn); |  | ||||||
| 
 |  | ||||||
|         if (!isset($upgraded->WebSocket)) { |  | ||||||
|             $upgraded->WebSocket = new \StdClass; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $upgraded->WebSocket->coalescedCallback = $coalescedCallback; |  | ||||||
| 
 |  | ||||||
|         return $upgraded; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function onMessage(ConnectionInterface $from, $data) { |  | ||||||
|         $overflow = ''; |  | ||||||
| 
 |  | ||||||
|         if (!isset($from->WebSocket->frame)) { |  | ||||||
|             $from->WebSocket->frame = $this->newFrame(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $from->WebSocket->frame->addBuffer($data); |  | ||||||
|         if ($from->WebSocket->frame->isCoalesced()) { |  | ||||||
|             $overflow = $from->WebSocket->frame->extractOverflow(); |  | ||||||
| 
 |  | ||||||
|             $parsed = $from->WebSocket->frame->getPayload(); |  | ||||||
|             unset($from->WebSocket->frame); |  | ||||||
| 
 |  | ||||||
|             $from->WebSocket->coalescedCallback->onMessage($from, $parsed); |  | ||||||
| 
 |  | ||||||
|             unset($from->WebSocket->frame); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (strlen($overflow) > 0) { |  | ||||||
|             $this->onMessage($from, $overflow); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function newFrame() { |  | ||||||
|         return new Frame; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function generateKeyNumber($key) { |  | ||||||
|         if (0 === substr_count($key, ' ')) { |  | ||||||
|             return 0; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return preg_replace('[\D]', '', $key) / substr_count($key, ' '); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     protected function sign($key1, $key2, $code) { |  | ||||||
|         return md5( |  | ||||||
|             pack('N', $this->generateKeyNumber($key1)) |  | ||||||
|           . pack('N', $this->generateKeyNumber($key2)) |  | ||||||
|           . $code |  | ||||||
|         , true); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,26 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version\Hixie76; |  | ||||||
| use Ratchet\AbstractConnectionDecorator; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * {@inheritdoc} |  | ||||||
|  * @property \StdClass $WebSocket |  | ||||||
|  */ |  | ||||||
| class Connection extends AbstractConnectionDecorator { |  | ||||||
|     public function send($msg) { |  | ||||||
|         if (!$this->WebSocket->closing) { |  | ||||||
|             $this->getConnection()->send(chr(0) . $msg . chr(255)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function close() { |  | ||||||
|         if (!$this->WebSocket->closing) { |  | ||||||
|             $this->getConnection()->send(chr(255)); |  | ||||||
|             $this->getConnection()->close(); |  | ||||||
| 
 |  | ||||||
|             $this->WebSocket->closing = true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,86 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version\Hixie76; |  | ||||||
| use Ratchet\WebSocket\Version\FrameInterface; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * This does not entirely follow the protocol to spec, but (mostly) works |  | ||||||
|  * Hixie76 probably should not even be supported |  | ||||||
|  */ |  | ||||||
| class Frame implements FrameInterface { |  | ||||||
|     /** |  | ||||||
|      * @type string |  | ||||||
|      */ |  | ||||||
|     protected $_data = ''; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function isCoalesced() { |  | ||||||
|         return (boolean)($this->_data[0] == chr(0) && substr($this->_data, -1) == chr(255)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function addBuffer($buf) { |  | ||||||
|         $this->_data .= (string)$buf; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function isFinal() { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function isMasked() { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getOpcode() { |  | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getPayloadLength() { |  | ||||||
|         if (!$this->isCoalesced()) { |  | ||||||
|             throw new \UnderflowException('Not enough of the message has been buffered to determine the length of the payload'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return strlen($this->_data) - 2; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getMaskingKey() { |  | ||||||
|         return ''; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getPayload() { |  | ||||||
|         if (!$this->isCoalesced()) { |  | ||||||
|             return new \UnderflowException('Not enough data buffered to read payload'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return substr($this->_data, 1, strlen($this->_data) - 2); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function getContents() { |  | ||||||
|         return $this->_data; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function extractOverflow() { |  | ||||||
|         return ''; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,15 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version; |  | ||||||
| use Guzzle\Http\Message\RequestInterface; |  | ||||||
| 
 |  | ||||||
| class HyBi10 extends RFC6455 { |  | ||||||
|     public function isProtocol(RequestInterface $request) { |  | ||||||
|         $version = (int)(string)$request->getHeader('Sec-WebSocket-Version'); |  | ||||||
| 
 |  | ||||||
|         return ($version >= 6 && $version < 13); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function getVersionNumber() { |  | ||||||
|         return 6; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,15 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version; |  | ||||||
| 
 |  | ||||||
| interface MessageInterface extends DataInterface { |  | ||||||
|     /** |  | ||||||
|      * @param FrameInterface $fragment |  | ||||||
|      * @return MessageInterface |  | ||||||
|      */ |  | ||||||
|     function addFrame(FrameInterface $fragment); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return int |  | ||||||
|      */ |  | ||||||
|     function getOpcode(); |  | ||||||
| } |  | ||||||
| @ -1,273 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version; |  | ||||||
| use Ratchet\ConnectionInterface; |  | ||||||
| use Ratchet\MessageInterface; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455\Message; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455\Frame; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455\Connection; |  | ||||||
| use Ratchet\WebSocket\Encoding\ValidatorInterface; |  | ||||||
| use Ratchet\WebSocket\Encoding\Validator; |  | ||||||
| use Guzzle\Http\Message\RequestInterface; |  | ||||||
| use Guzzle\Http\Message\Response; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * The latest version of the WebSocket protocol |  | ||||||
|  * @link http://tools.ietf.org/html/rfc6455 |  | ||||||
|  * @todo Unicode: return mb_convert_encoding(pack("N",$u), mb_internal_encoding(), 'UCS-4BE'); |  | ||||||
|  */ |  | ||||||
| class RFC6455 implements VersionInterface { |  | ||||||
|     const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @var RFC6455\HandshakeVerifier |  | ||||||
|      */ |  | ||||||
|     protected $_verifier; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * A lookup of the valid close codes that can be sent in a frame |  | ||||||
|      * @var array |  | ||||||
|      */ |  | ||||||
|     private $closeCodes = array(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @var \Ratchet\WebSocket\Encoding\ValidatorInterface |  | ||||||
|      */ |  | ||||||
|     protected $validator; |  | ||||||
| 
 |  | ||||||
|     public function __construct(ValidatorInterface $validator = null) { |  | ||||||
|         $this->_verifier = new HandshakeVerifier; |  | ||||||
|         $this->setCloseCodes(); |  | ||||||
| 
 |  | ||||||
|         if (null === $validator) { |  | ||||||
|             $validator = new Validator; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->validator = $validator; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function isProtocol(RequestInterface $request) { |  | ||||||
|         $version = (int)(string)$request->getHeader('Sec-WebSocket-Version'); |  | ||||||
| 
 |  | ||||||
|         return ($this->getVersionNumber() === $version); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getVersionNumber() { |  | ||||||
|         return 13; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function handshake(RequestInterface $request) { |  | ||||||
|         if (true !== $this->_verifier->verifyAll($request)) { |  | ||||||
|             return new Response(400); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return new Response(101, array( |  | ||||||
|             'Upgrade'              => 'websocket' |  | ||||||
|           , 'Connection'           => 'Upgrade' |  | ||||||
|           , 'Sec-WebSocket-Accept' => $this->sign((string)$request->getHeader('Sec-WebSocket-Key')) |  | ||||||
|         )); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param  \Ratchet\ConnectionInterface $conn |  | ||||||
|      * @param  \Ratchet\MessageInterface    $coalescedCallback |  | ||||||
|      * @return \Ratchet\WebSocket\Version\RFC6455\Connection |  | ||||||
|      */ |  | ||||||
|     public function upgradeConnection(ConnectionInterface $conn, MessageInterface $coalescedCallback) { |  | ||||||
|         $upgraded = new Connection($conn); |  | ||||||
| 
 |  | ||||||
|         if (!isset($upgraded->WebSocket)) { |  | ||||||
|             $upgraded->WebSocket = new \StdClass; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $upgraded->WebSocket->coalescedCallback = $coalescedCallback; |  | ||||||
| 
 |  | ||||||
|         return $upgraded; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param \Ratchet\WebSocket\Version\RFC6455\Connection $from |  | ||||||
|      * @param string                                        $data |  | ||||||
|      */ |  | ||||||
|     public function onMessage(ConnectionInterface $from, $data) { |  | ||||||
|         $overflow = ''; |  | ||||||
| 
 |  | ||||||
|         if (!isset($from->WebSocket->message)) { |  | ||||||
|             $from->WebSocket->message = $this->newMessage(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // There is a frame fragment attached to the connection, add to it
 |  | ||||||
|         if (!isset($from->WebSocket->frame)) { |  | ||||||
|             $from->WebSocket->frame = $this->newFrame(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $from->WebSocket->frame->addBuffer($data); |  | ||||||
|         if ($from->WebSocket->frame->isCoalesced()) { |  | ||||||
|             $frame = $from->WebSocket->frame; |  | ||||||
| 
 |  | ||||||
|             if (false !== $frame->getRsv1() || |  | ||||||
|                 false !== $frame->getRsv2() || |  | ||||||
|                 false !== $frame->getRsv3() |  | ||||||
|             ) { |  | ||||||
|                 return $from->close($frame::CLOSE_PROTOCOL); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (!$frame->isMasked()) { |  | ||||||
|                 return $from->close($frame::CLOSE_PROTOCOL); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             $opcode = $frame->getOpcode(); |  | ||||||
| 
 |  | ||||||
|             if ($opcode > 2) { |  | ||||||
|                 if ($frame->getPayloadLength() > 125 || !$frame->isFinal()) { |  | ||||||
|                     return $from->close($frame::CLOSE_PROTOCOL); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 switch ($opcode) { |  | ||||||
|                     case $frame::OP_CLOSE: |  | ||||||
|                         $closeCode = 0; |  | ||||||
| 
 |  | ||||||
|                         $bin = $frame->getPayload(); |  | ||||||
| 
 |  | ||||||
|                         if (empty($bin)) { |  | ||||||
|                             return $from->close(); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         if (strlen($bin) >= 2) { |  | ||||||
|                             list($closeCode) = array_merge(unpack('n*', substr($bin, 0, 2))); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         if (!$this->isValidCloseCode($closeCode)) { |  | ||||||
|                             return $from->close($frame::CLOSE_PROTOCOL); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         if (!$this->validator->checkEncoding(substr($bin, 2), 'UTF-8')) { |  | ||||||
|                             return $from->close($frame::CLOSE_BAD_PAYLOAD); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         $frame->unMaskPayload(); |  | ||||||
| 
 |  | ||||||
|                         return $from->close($frame); |  | ||||||
|                     break; |  | ||||||
|                     case $frame::OP_PING: |  | ||||||
|                         $from->send($this->newFrame($frame->getPayload(), true, $frame::OP_PONG)); |  | ||||||
|                     break; |  | ||||||
|                     case $frame::OP_PONG: |  | ||||||
|                     break; |  | ||||||
|                     default: |  | ||||||
|                         return $from->close($frame::CLOSE_PROTOCOL); |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 $overflow = $from->WebSocket->frame->extractOverflow(); |  | ||||||
| 
 |  | ||||||
|                 unset($from->WebSocket->frame, $frame, $opcode); |  | ||||||
| 
 |  | ||||||
|                 if (strlen($overflow) > 0) { |  | ||||||
|                     $this->onMessage($from, $overflow); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             $overflow = $from->WebSocket->frame->extractOverflow(); |  | ||||||
| 
 |  | ||||||
|             if ($frame::OP_CONTINUE == $frame->getOpcode() && 0 == count($from->WebSocket->message)) { |  | ||||||
|                 return $from->close($frame::CLOSE_PROTOCOL); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (count($from->WebSocket->message) > 0 && $frame::OP_CONTINUE != $frame->getOpcode()) { |  | ||||||
|                 return $from->close($frame::CLOSE_PROTOCOL); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             $from->WebSocket->message->addFrame($from->WebSocket->frame); |  | ||||||
|             unset($from->WebSocket->frame); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if ($from->WebSocket->message->isCoalesced()) { |  | ||||||
|             $parsed = $from->WebSocket->message->getPayload(); |  | ||||||
|             unset($from->WebSocket->message); |  | ||||||
| 
 |  | ||||||
|             if (!$this->validator->checkEncoding($parsed, 'UTF-8')) { |  | ||||||
|                 return $from->close(Frame::CLOSE_BAD_PAYLOAD); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             $from->WebSocket->coalescedCallback->onMessage($from, $parsed); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (strlen($overflow) > 0) { |  | ||||||
|             $this->onMessage($from, $overflow); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return RFC6455\Message |  | ||||||
|      */ |  | ||||||
|     public function newMessage() { |  | ||||||
|         return new Message; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param string|null $payload |  | ||||||
|      * @param bool|null   $final |  | ||||||
|      * @param int|null    $opcode |  | ||||||
|      * @return RFC6455\Frame |  | ||||||
|      */ |  | ||||||
|     public function newFrame($payload = null, $final = null, $opcode = null) { |  | ||||||
|         return new Frame($payload, $final, $opcode); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Used when doing the handshake to encode the key, verifying client/server are speaking the same language |  | ||||||
|      * @param  string $key |  | ||||||
|      * @return string |  | ||||||
|      * @internal |  | ||||||
|      */ |  | ||||||
|     public function sign($key) { |  | ||||||
|         return base64_encode(sha1($key . static::GUID, true)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Determine if a close code is valid |  | ||||||
|      * @param int|string |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     public function isValidCloseCode($val) { |  | ||||||
|         if (array_key_exists($val, $this->closeCodes)) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if ($val >= 3000 && $val <= 4999) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Creates a private lookup of valid, private close codes |  | ||||||
|      */ |  | ||||||
|     protected function setCloseCodes() { |  | ||||||
|         $this->closeCodes[Frame::CLOSE_NORMAL]      = true; |  | ||||||
|         $this->closeCodes[Frame::CLOSE_GOING_AWAY]  = true; |  | ||||||
|         $this->closeCodes[Frame::CLOSE_PROTOCOL]    = true; |  | ||||||
|         $this->closeCodes[Frame::CLOSE_BAD_DATA]    = true; |  | ||||||
|         //$this->closeCodes[Frame::CLOSE_NO_STATUS]   = true;
 |  | ||||||
|         //$this->closeCodes[Frame::CLOSE_ABNORMAL]    = true;
 |  | ||||||
|         $this->closeCodes[Frame::CLOSE_BAD_PAYLOAD] = true; |  | ||||||
|         $this->closeCodes[Frame::CLOSE_POLICY]      = true; |  | ||||||
|         $this->closeCodes[Frame::CLOSE_TOO_BIG]     = true; |  | ||||||
|         $this->closeCodes[Frame::CLOSE_MAND_EXT]    = true; |  | ||||||
|         $this->closeCodes[Frame::CLOSE_SRV_ERR]     = true; |  | ||||||
|         //$this->closeCodes[Frame::CLOSE_TLS]         = true;
 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,451 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version\RFC6455; |  | ||||||
| use Ratchet\WebSocket\Version\FrameInterface; |  | ||||||
| 
 |  | ||||||
| class Frame implements FrameInterface { |  | ||||||
|     const OP_CONTINUE =  0; |  | ||||||
|     const OP_TEXT     =  1; |  | ||||||
|     const OP_BINARY   =  2; |  | ||||||
|     const OP_CLOSE    =  8; |  | ||||||
|     const OP_PING     =  9; |  | ||||||
|     const OP_PONG     = 10; |  | ||||||
| 
 |  | ||||||
|     const CLOSE_NORMAL      = 1000; |  | ||||||
|     const CLOSE_GOING_AWAY  = 1001; |  | ||||||
|     const CLOSE_PROTOCOL    = 1002; |  | ||||||
|     const CLOSE_BAD_DATA    = 1003; |  | ||||||
|     const CLOSE_NO_STATUS   = 1005; |  | ||||||
|     const CLOSE_ABNORMAL    = 1006; |  | ||||||
|     const CLOSE_BAD_PAYLOAD = 1007; |  | ||||||
|     const CLOSE_POLICY      = 1008; |  | ||||||
|     const CLOSE_TOO_BIG     = 1009; |  | ||||||
|     const CLOSE_MAND_EXT    = 1010; |  | ||||||
|     const CLOSE_SRV_ERR     = 1011; |  | ||||||
|     const CLOSE_TLS         = 1015; |  | ||||||
| 
 |  | ||||||
|     const MASK_LENGTH = 4; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * The contents of the frame |  | ||||||
|      * @var string |  | ||||||
|      */ |  | ||||||
|     protected $data = ''; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Number of bytes received from the frame |  | ||||||
|      * @var int |  | ||||||
|      */ |  | ||||||
|     public $bytesRecvd = 0; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Number of bytes in the payload (as per framing protocol) |  | ||||||
|      * @var int |  | ||||||
|      */ |  | ||||||
|     protected $defPayLen = -1; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * If the frame is coalesced this is true |  | ||||||
|      * This is to prevent doing math every time ::isCoalesced is called |  | ||||||
|      * @var boolean |  | ||||||
|      */ |  | ||||||
|     private $isCoalesced = false; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * The unpacked first byte of the frame |  | ||||||
|      * @var int |  | ||||||
|      */ |  | ||||||
|     protected $firstByte = -1; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * The unpacked second byte of the frame |  | ||||||
|      * @var int |  | ||||||
|      */ |  | ||||||
|     protected $secondByte = -1; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param string|null $payload |  | ||||||
|      * @param bool        $final |  | ||||||
|      * @param int         $opcode |  | ||||||
|      */ |  | ||||||
|     public function __construct($payload = null, $final = true, $opcode = 1) { |  | ||||||
|         if (null === $payload) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->defPayLen   = strlen($payload); |  | ||||||
|         $this->firstByte   = ($final ? 128 : 0) + $opcode; |  | ||||||
|         $this->secondByte  = $this->defPayLen; |  | ||||||
|         $this->isCoalesced = true; |  | ||||||
| 
 |  | ||||||
|         $ext = ''; |  | ||||||
|         if ($this->defPayLen > 65535) { |  | ||||||
|             $ext = pack('NN', 0, $this->defPayLen); |  | ||||||
|             $this->secondByte = 127; |  | ||||||
|         } elseif ($this->defPayLen > 125) { |  | ||||||
|             $ext = pack('n', $this->defPayLen); |  | ||||||
|             $this->secondByte = 126; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->data       = chr($this->firstByte) . chr($this->secondByte) . $ext . $payload; |  | ||||||
|         $this->bytesRecvd = 2 + strlen($ext) + $this->defPayLen; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function isCoalesced() { |  | ||||||
|         if (true === $this->isCoalesced) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             $payload_length = $this->getPayloadLength(); |  | ||||||
|             $payload_start  = $this->getPayloadStartingByte(); |  | ||||||
|         } catch (\UnderflowException $e) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->isCoalesced = $this->bytesRecvd >= $payload_length + $payload_start; |  | ||||||
| 
 |  | ||||||
|         return $this->isCoalesced; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function addBuffer($buf) { |  | ||||||
|         $len = strlen($buf); |  | ||||||
| 
 |  | ||||||
|         $this->data       .= $buf; |  | ||||||
|         $this->bytesRecvd += $len; |  | ||||||
| 
 |  | ||||||
|         if ($this->firstByte === -1 && $this->bytesRecvd !== 0) { |  | ||||||
|             $this->firstByte = ord($this->data[0]); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if ($this->secondByte === -1 && $this->bytesRecvd >= 2) { |  | ||||||
|             $this->secondByte = ord($this->data[1]); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function isFinal() { |  | ||||||
|         if (-1 === $this->firstByte) { |  | ||||||
|             throw new \UnderflowException('Not enough bytes received to determine if this is the final frame in message'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return 128 === ($this->firstByte & 128); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return boolean |  | ||||||
|      * @throws \UnderflowException |  | ||||||
|      */ |  | ||||||
|     public function getRsv1() { |  | ||||||
|         if (-1 === $this->firstByte) { |  | ||||||
|             throw new \UnderflowException('Not enough bytes received to determine reserved bit'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return 64 === ($this->firstByte & 64); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return boolean |  | ||||||
|      * @throws \UnderflowException |  | ||||||
|      */ |  | ||||||
|     public function getRsv2() { |  | ||||||
|         if (-1 === $this->firstByte) { |  | ||||||
|             throw new \UnderflowException('Not enough bytes received to determine reserved bit'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return 32 === ($this->firstByte & 32); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return boolean |  | ||||||
|      * @throws \UnderflowException |  | ||||||
|      */ |  | ||||||
|     public function getRsv3() { |  | ||||||
|         if (-1 === $this->firstByte) { |  | ||||||
|             throw new \UnderflowException('Not enough bytes received to determine reserved bit'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return 16 == ($this->firstByte & 16); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function isMasked() { |  | ||||||
|         if (-1 === $this->secondByte) { |  | ||||||
|             throw new \UnderflowException("Not enough bytes received ({$this->bytesRecvd}) to determine if mask is set"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return 128 === ($this->secondByte & 128); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getMaskingKey() { |  | ||||||
|         if (!$this->isMasked()) { |  | ||||||
|             return ''; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $start  = 1 + $this->getNumPayloadBytes(); |  | ||||||
| 
 |  | ||||||
|         if ($this->bytesRecvd < $start + static::MASK_LENGTH) { |  | ||||||
|             throw new \UnderflowException('Not enough data buffered to calculate the masking key'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return substr($this->data, $start, static::MASK_LENGTH); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Create a 4 byte masking key |  | ||||||
|      * @return string |  | ||||||
|      */ |  | ||||||
|     public function generateMaskingKey() { |  | ||||||
|         $mask = ''; |  | ||||||
| 
 |  | ||||||
|         for ($i = 1; $i <= static::MASK_LENGTH; $i++) { |  | ||||||
|             $mask .= chr(rand(32, 126)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $mask; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Apply a mask to the payload |  | ||||||
|      * @param string|null If NULL is passed a masking key will be generated |  | ||||||
|      * @throws \OutOfBoundsException |  | ||||||
|      * @throws \InvalidArgumentException If there is an issue with the given masking key |  | ||||||
|      * @return Frame |  | ||||||
|      */ |  | ||||||
|     public function maskPayload($maskingKey = null) { |  | ||||||
|         if (null === $maskingKey) { |  | ||||||
|             $maskingKey = $this->generateMaskingKey(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (static::MASK_LENGTH !== strlen($maskingKey)) { |  | ||||||
|             throw new \InvalidArgumentException("Masking key must be " . static::MASK_LENGTH ." characters"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (extension_loaded('mbstring') && true !== mb_check_encoding($maskingKey, 'US-ASCII')) { |  | ||||||
|             throw new \OutOfBoundsException("Masking key MUST be ASCII"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->unMaskPayload(); |  | ||||||
| 
 |  | ||||||
|         $this->secondByte = $this->secondByte | 128; |  | ||||||
|         $this->data[1]    = chr($this->secondByte); |  | ||||||
| 
 |  | ||||||
|         $this->data = substr_replace($this->data, $maskingKey, $this->getNumPayloadBytes() + 1, 0); |  | ||||||
| 
 |  | ||||||
|         $this->bytesRecvd += static::MASK_LENGTH; |  | ||||||
|         $this->data        = substr_replace($this->data, $this->applyMask($maskingKey), $this->getPayloadStartingByte(), $this->getPayloadLength()); |  | ||||||
| 
 |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Remove a mask from the payload |  | ||||||
|      * @throws \UnderFlowException If the frame is not coalesced |  | ||||||
|      * @return Frame |  | ||||||
|      */ |  | ||||||
|     public function unMaskPayload() { |  | ||||||
|         if (!$this->isCoalesced()) { |  | ||||||
|             throw new \UnderflowException('Frame must be coalesced before applying mask'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (!$this->isMasked()) { |  | ||||||
|             return $this; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $maskingKey = $this->getMaskingKey(); |  | ||||||
| 
 |  | ||||||
|         $this->secondByte = $this->secondByte & ~128; |  | ||||||
|         $this->data[1] = chr($this->secondByte); |  | ||||||
| 
 |  | ||||||
|         $this->data = substr_replace($this->data, '', $this->getNumPayloadBytes() + 1, static::MASK_LENGTH); |  | ||||||
| 
 |  | ||||||
|         $this->bytesRecvd -= static::MASK_LENGTH; |  | ||||||
|         $this->data        = substr_replace($this->data, $this->applyMask($maskingKey), $this->getPayloadStartingByte(), $this->getPayloadLength()); |  | ||||||
| 
 |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Apply a mask to a string or the payload of the instance |  | ||||||
|      * @param string $maskingKey   The 4 character masking key to be applied |  | ||||||
|      * @param string|null $payload A string to mask or null to use the payload |  | ||||||
|      * @throws \UnderflowException If using the payload but enough hasn't been buffered |  | ||||||
|      * @return string              The masked string |  | ||||||
|      */ |  | ||||||
|     public function applyMask($maskingKey, $payload = null) { |  | ||||||
|         if (null === $payload) { |  | ||||||
|             if (!$this->isCoalesced()) { |  | ||||||
|                 throw new \UnderflowException('Frame must be coalesced to apply a mask'); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             $payload = substr($this->data, $this->getPayloadStartingByte(), $this->getPayloadLength()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $applied = ''; |  | ||||||
|         for ($i = 0, $len = strlen($payload); $i < $len; $i++) { |  | ||||||
|             $applied .= $payload[$i] ^ $maskingKey[$i % static::MASK_LENGTH]; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $applied; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getOpcode() { |  | ||||||
|         if (-1 === $this->firstByte) { |  | ||||||
|             throw new \UnderflowException('Not enough bytes received to determine opcode'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return ($this->firstByte & ~240); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Gets the decimal value of bits 9 (10th) through 15 inclusive |  | ||||||
|      * @return int |  | ||||||
|      * @throws \UnderflowException If the buffer doesn't have enough data to determine this |  | ||||||
|      */ |  | ||||||
|     protected function getFirstPayloadVal() { |  | ||||||
|         if (-1 === $this->secondByte) { |  | ||||||
|             throw new \UnderflowException('Not enough bytes received'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $this->secondByte & 127; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return int (7|23|71) Number of bits defined for the payload length in the fame |  | ||||||
|      * @throws \UnderflowException |  | ||||||
|      */ |  | ||||||
|     protected function getNumPayloadBits() { |  | ||||||
|         if (-1 === $this->secondByte) { |  | ||||||
|             throw new \UnderflowException('Not enough bytes received'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // By default 7 bits are used to describe the payload length
 |  | ||||||
|         // These are bits 9 (10th) through 15 inclusive
 |  | ||||||
|         $bits = 7; |  | ||||||
| 
 |  | ||||||
|         // Get the value of those bits
 |  | ||||||
|         $check = $this->getFirstPayloadVal(); |  | ||||||
| 
 |  | ||||||
|         // If the value is 126 the 7 bits plus the next 16 are used to describe the payload length
 |  | ||||||
|         if ($check >= 126) { |  | ||||||
|             $bits += 16; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // If the value of the initial payload length are is 127 an additional 48 bits are used to describe length
 |  | ||||||
|         // Note: The documentation specifies the length is to be 63 bits, but I think that's a typo and is 64 (16+48)
 |  | ||||||
|         if ($check === 127) { |  | ||||||
|             $bits += 48; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $bits; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This just returns the number of bytes used in the frame to describe the payload length (as opposed to # of bits)
 |  | ||||||
|      * @see getNumPayloadBits |  | ||||||
|      */ |  | ||||||
|     protected function getNumPayloadBytes() { |  | ||||||
|         return (1 + $this->getNumPayloadBits()) / 8; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getPayloadLength() { |  | ||||||
|         if ($this->defPayLen !== -1) { |  | ||||||
|             return $this->defPayLen; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->defPayLen = $this->getFirstPayloadVal(); |  | ||||||
|         if ($this->defPayLen <= 125) { |  | ||||||
|             return $this->getPayloadLength(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $byte_length = $this->getNumPayloadBytes(); |  | ||||||
|         if ($this->bytesRecvd < 1 + $byte_length) { |  | ||||||
|             $this->defPayLen = -1; |  | ||||||
|             throw new \UnderflowException('Not enough data buffered to determine payload length'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $len = 0; |  | ||||||
|         for ($i = 2; $i <= $byte_length; $i++) { |  | ||||||
|             $len <<= 8; |  | ||||||
|             $len  += ord($this->data[$i]); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->defPayLen = $len; |  | ||||||
| 
 |  | ||||||
|         return $this->getPayloadLength(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getPayloadStartingByte() { |  | ||||||
|         return 1 + $this->getNumPayloadBytes() + ($this->isMasked() ? static::MASK_LENGTH : 0); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      * @todo Consider not checking mask, always returning the payload, masked or not |  | ||||||
|      */ |  | ||||||
|     public function getPayload() { |  | ||||||
|         if (!$this->isCoalesced()) { |  | ||||||
|             throw new \UnderflowException('Can not return partial message'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $payload = substr($this->data, $this->getPayloadStartingByte(), $this->getPayloadLength()); |  | ||||||
| 
 |  | ||||||
|         if ($this->isMasked()) { |  | ||||||
|             $payload = $this->applyMask($this->getMaskingKey(), $payload); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $payload; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get the raw contents of the frame |  | ||||||
|      * @todo This is untested, make sure the substr is right - trying to return the frame w/o the overflow |  | ||||||
|      */ |  | ||||||
|     public function getContents() { |  | ||||||
|         return substr($this->data, 0, $this->getPayloadStartingByte() + $this->getPayloadLength()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Sometimes clients will concatenate more than one frame over the wire |  | ||||||
|      * This method will take the extra bytes off the end and return them |  | ||||||
|      * @todo Consider returning new Frame |  | ||||||
|      * @return string |  | ||||||
|      */ |  | ||||||
|     public function extractOverflow() { |  | ||||||
|         if ($this->isCoalesced()) { |  | ||||||
|             $endPoint  = $this->getPayloadLength(); |  | ||||||
|             $endPoint += $this->getPayloadStartingByte(); |  | ||||||
| 
 |  | ||||||
|             if ($this->bytesRecvd > $endPoint) { |  | ||||||
|                 $overflow   = substr($this->data, $endPoint); |  | ||||||
|                 $this->data = substr($this->data, 0, $endPoint); |  | ||||||
| 
 |  | ||||||
|                 return $overflow; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return ''; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,137 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version\RFC6455; |  | ||||||
| use Guzzle\Http\Message\RequestInterface; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * These are checks to ensure the client requested handshake are valid |  | ||||||
|  * Verification rules come from section 4.2.1 of the RFC6455 document |  | ||||||
|  * @todo Currently just returning invalid - should consider returning appropriate HTTP status code error #s
 |  | ||||||
|  */ |  | ||||||
| class HandshakeVerifier { |  | ||||||
|     /** |  | ||||||
|      * Given an array of the headers this method will run through all verification methods |  | ||||||
|      * @param \Guzzle\Http\Message\RequestInterface $request |  | ||||||
|      * @return bool TRUE if all headers are valid, FALSE if 1 or more were invalid |  | ||||||
|      */ |  | ||||||
|     public function verifyAll(RequestInterface $request) { |  | ||||||
|         $passes = 0; |  | ||||||
| 
 |  | ||||||
|         $passes += (int)$this->verifyMethod($request->getMethod()); |  | ||||||
|         $passes += (int)$this->verifyHTTPVersion($request->getProtocolVersion()); |  | ||||||
|         $passes += (int)$this->verifyRequestURI($request->getPath()); |  | ||||||
|         $passes += (int)$this->verifyHost((string)$request->getHeader('Host')); |  | ||||||
|         $passes += (int)$this->verifyUpgradeRequest((string)$request->getHeader('Upgrade')); |  | ||||||
|         $passes += (int)$this->verifyConnection((string)$request->getHeader('Connection')); |  | ||||||
|         $passes += (int)$this->verifyKey((string)$request->getHeader('Sec-WebSocket-Key')); |  | ||||||
|         //$passes += (int)$this->verifyVersion($headers['Sec-WebSocket-Version']); // Temporarily breaking functionality
 |  | ||||||
| 
 |  | ||||||
|         return (7 === $passes); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Test the HTTP method.  MUST be "GET" |  | ||||||
|      * @param string |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     public function verifyMethod($val) { |  | ||||||
|         return ('get' === strtolower($val)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Test the HTTP version passed.  MUST be 1.1 or greater |  | ||||||
|      * @param string|int |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     public function verifyHTTPVersion($val) { |  | ||||||
|         return (1.1 <= (double)$val); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param string |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     public function verifyRequestURI($val) { |  | ||||||
|         if ($val[0] != '/') { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (false !== strstr($val, '#')) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (!extension_loaded('mbstring')) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return mb_check_encoding($val, 'US-ASCII'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param string|null |  | ||||||
|      * @return bool |  | ||||||
|      * @todo Find out if I can find the master socket, ensure the port is attached to header if not 80 or 443 - not sure if this is possible, as I tried to hide it |  | ||||||
|      * @todo Once I fix HTTP::getHeaders just verify this isn't NULL or empty...or maybe need to verify it's a valid domain??? Or should it equal $_SERVER['HOST'] ? |  | ||||||
|      */ |  | ||||||
|     public function verifyHost($val) { |  | ||||||
|         return (null !== $val); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Verify the Upgrade request to WebSockets. |  | ||||||
|      * @param  string $val MUST equal "websocket" |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     public function verifyUpgradeRequest($val) { |  | ||||||
|         return ('websocket' === strtolower($val)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Verify the Connection header |  | ||||||
|      * @param  string $val MUST equal "Upgrade" |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     public function verifyConnection($val) { |  | ||||||
|         $val = strtolower($val); |  | ||||||
| 
 |  | ||||||
|         if ('upgrade' === $val) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $vals = explode(',', str_replace(', ', ',', $val)); |  | ||||||
| 
 |  | ||||||
|         return (false !== array_search('upgrade', $vals)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This function verifies the nonce is valid (64 big encoded, 16 bytes random string) |  | ||||||
|      * @param string|null |  | ||||||
|      * @return bool |  | ||||||
|      * @todo The spec says we don't need to base64_decode - can I just check if the length is 24 and not decode? |  | ||||||
|      * @todo Check the spec to see what the encoding of the key could be |  | ||||||
|      */ |  | ||||||
|     public function verifyKey($val) { |  | ||||||
|         return (16 === strlen(base64_decode((string)$val))); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Verify the version passed matches this RFC |  | ||||||
|      * @param string|int MUST equal 13|"13" |  | ||||||
|      * @return bool |  | ||||||
|      * @todo Ran in to a problem here...I'm having HyBi use the RFC files, this breaks it!  oops |  | ||||||
|      */ |  | ||||||
|     public function verifyVersion($val) { |  | ||||||
|         return (13 === (int)$val); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @todo Write logic for this method.  See section 4.2.1.8 |  | ||||||
|      */ |  | ||||||
|     public function verifyProtocol($val) { |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @todo Write logic for this method.  See section 4.2.1.9 |  | ||||||
|      */ |  | ||||||
|     public function verifyExtensions($val) { |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,107 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version\RFC6455; |  | ||||||
| use Ratchet\WebSocket\Version\MessageInterface; |  | ||||||
| use Ratchet\WebSocket\Version\FrameInterface; |  | ||||||
| 
 |  | ||||||
| class Message implements MessageInterface, \Countable { |  | ||||||
|     /** |  | ||||||
|      * @var \SplDoublyLinkedList |  | ||||||
|      */ |  | ||||||
|     protected $_frames; |  | ||||||
| 
 |  | ||||||
|     public function __construct() { |  | ||||||
|         $this->_frames = new \SplDoublyLinkedList; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function count() { |  | ||||||
|         return count($this->_frames); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function isCoalesced() { |  | ||||||
|         if (count($this->_frames) == 0) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $last = $this->_frames->top(); |  | ||||||
| 
 |  | ||||||
|         return ($last->isCoalesced() && $last->isFinal()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      * @todo Also, I should perhaps check the type...control frames (ping/pong/close) are not to be considered part of a message |  | ||||||
|      */ |  | ||||||
|     public function addFrame(FrameInterface $fragment) { |  | ||||||
|         $this->_frames->push($fragment); |  | ||||||
| 
 |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getOpcode() { |  | ||||||
|         if (count($this->_frames) == 0) { |  | ||||||
|             throw new \UnderflowException('No frames have been added to this message'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $this->_frames->bottom()->getOpcode(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getPayloadLength() { |  | ||||||
|         $len = 0; |  | ||||||
| 
 |  | ||||||
|         foreach ($this->_frames as $frame) { |  | ||||||
|             try { |  | ||||||
|                 $len += $frame->getPayloadLength(); |  | ||||||
|             } catch (\UnderflowException $e) { |  | ||||||
|                 // Not an error, want the current amount buffered
 |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $len; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getPayload() { |  | ||||||
|         if (!$this->isCoalesced()) { |  | ||||||
|             throw new \UnderflowException('Message has not been put back together yet'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $buffer = ''; |  | ||||||
| 
 |  | ||||||
|         foreach ($this->_frames as $frame) { |  | ||||||
|             $buffer .= $frame->getPayload(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $buffer; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      */ |  | ||||||
|     public function getContents() { |  | ||||||
|         if (!$this->isCoalesced()) { |  | ||||||
|             throw new \UnderflowException("Message has not been put back together yet"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $buffer = ''; |  | ||||||
| 
 |  | ||||||
|         foreach ($this->_frames as $frame) { |  | ||||||
|             $buffer .= $frame->getContents(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $buffer; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,57 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version; |  | ||||||
| use Ratchet\MessageInterface; |  | ||||||
| use Ratchet\ConnectionInterface; |  | ||||||
| use Guzzle\Http\Message\RequestInterface; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * A standard interface for interacting with the various version of the WebSocket protocol |  | ||||||
|  */ |  | ||||||
| interface VersionInterface extends MessageInterface { |  | ||||||
|     /** |  | ||||||
|      * Given an HTTP header, determine if this version should handle the protocol |  | ||||||
|      * @param \Guzzle\Http\Message\RequestInterface $request |  | ||||||
|      * @return bool |  | ||||||
|      * @throws \UnderflowException If the protocol thinks the headers are still fragmented |  | ||||||
|      */ |  | ||||||
|     function isProtocol(RequestInterface $request); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Although the version has a name associated with it the integer returned is the proper identification |  | ||||||
|      * @return int |  | ||||||
|      */ |  | ||||||
|     function getVersionNumber(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Perform the handshake and return the response headers |  | ||||||
|      * @param \Guzzle\Http\Message\RequestInterface $request |  | ||||||
|      * @return \Guzzle\Http\Message\Response |  | ||||||
|      * @throws \UnderflowException If the message hasn't finished buffering (not yet implemented, theoretically will only happen with Hixie version) |  | ||||||
|      */ |  | ||||||
|     function handshake(RequestInterface $request); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param  \Ratchet\ConnectionInterface $conn |  | ||||||
|      * @param  \Ratchet\MessageInterface    $coalescedCallback |  | ||||||
|      * @return \Ratchet\ConnectionInterface |  | ||||||
|      */ |  | ||||||
|     function upgradeConnection(ConnectionInterface $conn, MessageInterface $coalescedCallback); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return MessageInterface |  | ||||||
|      */ |  | ||||||
|     //function newMessage();
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return FrameInterface |  | ||||||
|      */ |  | ||||||
|     //function newFrame();
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param string |  | ||||||
|      * @param bool |  | ||||||
|      * @return string |  | ||||||
|      * @todo Change to use other classes, this will be removed eventually |  | ||||||
|      */ |  | ||||||
|     //function frame($message, $mask = true);
 |  | ||||||
| } |  | ||||||
| @ -1,90 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket; |  | ||||||
| use Ratchet\WebSocket\Version\VersionInterface; |  | ||||||
| use Guzzle\Http\Message\RequestInterface; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Manage the various versions of the WebSocket protocol |  | ||||||
|  * This accepts interfaces of versions to enable/disable |  | ||||||
|  */ |  | ||||||
| class VersionManager { |  | ||||||
|     /** |  | ||||||
|      * The header string to let clients know which versions are supported |  | ||||||
|      * @var string |  | ||||||
|      */ |  | ||||||
|     private $versionString = ''; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Storage of each version enabled |  | ||||||
|      * @var array |  | ||||||
|      */ |  | ||||||
|     protected $versions = array(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get the protocol negotiator for the request, if supported |  | ||||||
|      * @param  \Guzzle\Http\Message\RequestInterface $request |  | ||||||
|      * @throws \InvalidArgumentException |  | ||||||
|      * @return \Ratchet\WebSocket\Version\VersionInterface |  | ||||||
|      */ |  | ||||||
|     public function getVersion(RequestInterface $request) { |  | ||||||
|         foreach ($this->versions as $version) { |  | ||||||
|             if ($version->isProtocol($request)) { |  | ||||||
|                 return $version; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         throw new \InvalidArgumentException("Version not found"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param  \Guzzle\Http\Message\RequestInterface |  | ||||||
|      * @return bool |  | ||||||
|      */ |  | ||||||
|     public function isVersionEnabled(RequestInterface $request) { |  | ||||||
|         foreach ($this->versions as $version) { |  | ||||||
|             if ($version->isProtocol($request)) { |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Enable support for a specific version of the WebSocket protocol |  | ||||||
|      * @param  \Ratchet\WebSocket\Version\VersionInterface $version |  | ||||||
|      * @return VersionManager |  | ||||||
|      */ |  | ||||||
|     public function enableVersion(VersionInterface $version) { |  | ||||||
|         $this->versions[$version->getVersionNumber()] = $version; |  | ||||||
| 
 |  | ||||||
|         if (empty($this->versionString)) { |  | ||||||
|             $this->versionString = (string)$version->getVersionNumber(); |  | ||||||
|         } else { |  | ||||||
|             $this->versionString .= ", {$version->getVersionNumber()}"; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Disable support for a specific WebSocket protocol version |  | ||||||
|      * @param  int $versionId The version ID to un-support |  | ||||||
|      * @return VersionManager |  | ||||||
|      */ |  | ||||||
|     public function disableVersion($versionId) { |  | ||||||
|         unset($this->versions[$versionId]); |  | ||||||
| 
 |  | ||||||
|         $this->versionString = implode(',', array_keys($this->versions)); |  | ||||||
| 
 |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get a string of version numbers supported (comma delimited) |  | ||||||
|      * @return string |  | ||||||
|      */ |  | ||||||
|     public function getSupportedVersionString() { |  | ||||||
|         return $this->versionString; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,13 +1,14 @@ | |||||||
| <?php | <?php | ||||||
| namespace Ratchet\WebSocket\Version\RFC6455; | namespace Ratchet\WebSocket; | ||||||
| use Ratchet\AbstractConnectionDecorator; | use Ratchet\AbstractConnectionDecorator; | ||||||
| use Ratchet\WebSocket\Version\DataInterface; | use Ratchet\RFC6455\Messaging\DataInterface; | ||||||
|  | use Ratchet\RFC6455\Messaging\Frame; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * {@inheritdoc} |  * {@inheritdoc} | ||||||
|  * @property \StdClass $WebSocket |  * @property \StdClass $WebSocket | ||||||
|  */ |  */ | ||||||
| class Connection extends AbstractConnectionDecorator { | class WsConnection extends AbstractConnectionDecorator { | ||||||
|     /** |     /** | ||||||
|      * {@inheritdoc} |      * {@inheritdoc} | ||||||
|      */ |      */ | ||||||
| @ -24,7 +25,7 @@ class Connection extends AbstractConnectionDecorator { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * {@inheritdoc} |      * @param int|\Ratchet\RFC6455\Messaging\DataInterface | ||||||
|      */ |      */ | ||||||
|     public function close($code = 1000) { |     public function close($code = 1000) { | ||||||
|         if ($this->WebSocket->closing) { |         if ($this->WebSocket->closing) { | ||||||
| @ -1,12 +1,20 @@ | |||||||
| <?php | <?php | ||||||
| namespace Ratchet\WebSocket; | namespace Ratchet\WebSocket; | ||||||
| use Ratchet\MessageComponentInterface; | use Ratchet\ComponentInterface; | ||||||
| use Ratchet\ConnectionInterface; | use Ratchet\ConnectionInterface; | ||||||
|  | use Ratchet\MessageComponentInterface as DataComponentInterface; | ||||||
| use Ratchet\Http\HttpServerInterface; | use Ratchet\Http\HttpServerInterface; | ||||||
| use Guzzle\Http\Message\RequestInterface; | use Ratchet\Http\CloseResponseTrait; | ||||||
| use Guzzle\Http\Message\Response; | use Psr\Http\Message\RequestInterface; | ||||||
| use Ratchet\WebSocket\Version; | use Ratchet\RFC6455\Messaging\MessageInterface; | ||||||
| use Ratchet\WebSocket\Encoding\ToggleableValidator; | use Ratchet\RFC6455\Messaging\FrameInterface; | ||||||
|  | use Ratchet\RFC6455\Messaging\Frame; | ||||||
|  | use Ratchet\RFC6455\Messaging\MessageBuffer; | ||||||
|  | use Ratchet\RFC6455\Messaging\CloseFrameChecker; | ||||||
|  | use Ratchet\RFC6455\Handshake\ServerNegotiator; | ||||||
|  | use Ratchet\RFC6455\Handshake\RequestVerifier; | ||||||
|  | use React\EventLoop\LoopInterface; | ||||||
|  | use GuzzleHttp\Psr7 as gPsr; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * The adapter to handle WebSocket requests/responses |  * The adapter to handle WebSocket requests/responses | ||||||
| @ -15,18 +23,13 @@ use Ratchet\WebSocket\Encoding\ToggleableValidator; | |||||||
|  * @link http://dev.w3.org/html5/websockets/ |  * @link http://dev.w3.org/html5/websockets/ | ||||||
|  */ |  */ | ||||||
| class WsServer implements HttpServerInterface { | class WsServer implements HttpServerInterface { | ||||||
|     /** |     use CloseResponseTrait; | ||||||
|      * Manage the various WebSocket versions to support |  | ||||||
|      * @var VersionManager |  | ||||||
|      * @note May not expose this in the future, may do through facade methods |  | ||||||
|      */ |  | ||||||
|     public $versioner; |  | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Decorated component |      * Decorated component | ||||||
|      * @var \Ratchet\MessageComponentInterface |      * @var \Ratchet\ComponentInterface | ||||||
|      */ |      */ | ||||||
|     public $component; |     private $delegate; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @var \SplObjectStorage |      * @var \SplObjectStorage | ||||||
| @ -34,38 +37,68 @@ class WsServer implements HttpServerInterface { | |||||||
|     protected $connections; |     protected $connections; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Holder of accepted protocols, implement through WampServerInterface |      * @var \Ratchet\RFC6455\Messaging\CloseFrameChecker | ||||||
|      */ |      */ | ||||||
|     protected $acceptedSubProtocols = array(); |     private $closeFrameChecker; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * UTF-8 validator |      * @var \Ratchet\RFC6455\Handshake\ServerNegotiator | ||||||
|      * @var \Ratchet\WebSocket\Encoding\ValidatorInterface |  | ||||||
|      */ |      */ | ||||||
|     protected $validator; |     private $handshakeNegotiator; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Flag if we have checked the decorated component for sub-protocols |      * @var \Closure | ||||||
|      * @var boolean |  | ||||||
|      */ |      */ | ||||||
|     private $isSpGenerated = false; |     private $ueFlowFactory; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @param \Ratchet\MessageComponentInterface $component Your application to run with WebSockets |      * @var \Closure | ||||||
|      * If you want to enable sub-protocols have your component implement WsServerInterface as well |  | ||||||
|      */ |      */ | ||||||
|     public function __construct(MessageComponentInterface $component) { |     private $pongReceiver; | ||||||
|         $this->versioner = new VersionManager; |  | ||||||
|         $this->validator = new ToggleableValidator; |  | ||||||
| 
 | 
 | ||||||
|         $this->versioner |     /** | ||||||
|             ->enableVersion(new Version\RFC6455($this->validator)) |      * @var \Closure | ||||||
|             ->enableVersion(new Version\HyBi10($this->validator)) |      */ | ||||||
|             ->enableVersion(new Version\Hixie76) |     private $msgCb; | ||||||
|         ; |  | ||||||
| 
 | 
 | ||||||
|         $this->component   = $component; |     /** | ||||||
|  |      * @param \Ratchet\WebSocket\MessageComponentInterface|\Ratchet\MessageComponentInterface $component Your application to run with WebSockets | ||||||
|  |      * @note If you want to enable sub-protocols have your component implement WsServerInterface as well | ||||||
|  |      */ | ||||||
|  |     public function __construct(ComponentInterface $component) { | ||||||
|  |         if ($component instanceof MessageComponentInterface) { | ||||||
|  |             $this->msgCb = function(ConnectionInterface $conn, MessageInterface $msg) { | ||||||
|  |                 $this->delegate->onMessage($conn, $msg); | ||||||
|  |             }; | ||||||
|  |         } elseif ($component instanceof DataComponentInterface) { | ||||||
|  |             $this->msgCb = function(ConnectionInterface $conn, MessageInterface $msg) { | ||||||
|  |                 $this->delegate->onMessage($conn, $msg->getPayload()); | ||||||
|  |             }; | ||||||
|  |         } else { | ||||||
|  |             throw new \UnexpectedValueException('Expected instance of \Ratchet\WebSocket\MessageComponentInterface or \Ratchet\MessageComponentInterface'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (bin2hex('✓') !== 'e29c93') { | ||||||
|  |             throw new \DomainException('Bad encoding, unicode character ✓ did not match expected value. Ensure charset UTF-8 and check ini val mbstring.func_autoload'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $this->delegate    = $component; | ||||||
|         $this->connections = new \SplObjectStorage; |         $this->connections = new \SplObjectStorage; | ||||||
|  | 
 | ||||||
|  |         $this->closeFrameChecker   = new CloseFrameChecker; | ||||||
|  |         $this->handshakeNegotiator = new ServerNegotiator(new RequestVerifier); | ||||||
|  |         $this->handshakeNegotiator->setStrictSubProtocolCheck(true); | ||||||
|  | 
 | ||||||
|  |         if ($component instanceof WsServerInterface) { | ||||||
|  |             $this->handshakeNegotiator->setSupportedSubProtocols($component->getSubProtocols()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $this->pongReceiver = function() {}; | ||||||
|  | 
 | ||||||
|  |         $reusableUnderflowException = new \UnderflowException; | ||||||
|  |         $this->ueFlowFactory = function() use ($reusableUnderflowException) { | ||||||
|  |             return $reusableUnderflowException; | ||||||
|  |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -76,12 +109,37 @@ class WsServer implements HttpServerInterface { | |||||||
|             throw new \UnexpectedValueException('$request can not be null'); |             throw new \UnexpectedValueException('$request can not be null'); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $conn->WebSocket              = new \StdClass; |         $conn->httpRequest = $request; | ||||||
|         $conn->WebSocket->request     = $request; |  | ||||||
|         $conn->WebSocket->established = false; |  | ||||||
|         $conn->WebSocket->closing     = false; |  | ||||||
| 
 | 
 | ||||||
|         $this->attemptUpgrade($conn); |         $conn->WebSocket            = new \StdClass; | ||||||
|  |         $conn->WebSocket->closing   = false; | ||||||
|  | 
 | ||||||
|  |         $response = $this->handshakeNegotiator->handshake($request)->withHeader('X-Powered-By', \Ratchet\VERSION); | ||||||
|  | 
 | ||||||
|  |         $conn->send(gPsr\str($response)); | ||||||
|  | 
 | ||||||
|  |         if (101 !== $response->getStatusCode()) { | ||||||
|  |             return $conn->close(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $wsConn = new WsConnection($conn); | ||||||
|  | 
 | ||||||
|  |         $streamer = new MessageBuffer( | ||||||
|  |             $this->closeFrameChecker, | ||||||
|  |             function(MessageInterface $msg) use ($wsConn) { | ||||||
|  |                 $cb = $this->msgCb; | ||||||
|  |                 $cb($wsConn, $msg); | ||||||
|  |             }, | ||||||
|  |             function(FrameInterface $frame) use ($wsConn) { | ||||||
|  |                 $this->onControlFrame($frame, $wsConn); | ||||||
|  |             }, | ||||||
|  |             true, | ||||||
|  |             $this->ueFlowFactory | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         $this->connections->attach($conn, new ConnContext($wsConn, $streamer)); | ||||||
|  | 
 | ||||||
|  |         return $this->delegate->onOpen($wsConn); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -92,50 +150,7 @@ class WsServer implements HttpServerInterface { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (true === $from->WebSocket->established) { |         $this->connections[$from]->buffer->onData($msg); | ||||||
|             return $from->WebSocket->version->onMessage($this->connections[$from], $msg); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->attemptUpgrade($from, $msg); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     protected function attemptUpgrade(ConnectionInterface $conn, $data = '') { |  | ||||||
|         if ('' !== $data) { |  | ||||||
|             $conn->WebSocket->request->getBody()->write($data); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (!$this->versioner->isVersionEnabled($conn->WebSocket->request)) { |  | ||||||
|             return $this->close($conn); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $conn->WebSocket->version = $this->versioner->getVersion($conn->WebSocket->request); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             $response = $conn->WebSocket->version->handshake($conn->WebSocket->request); |  | ||||||
|         } catch (\UnderflowException $e) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (null !== ($subHeader = $conn->WebSocket->request->getHeader('Sec-WebSocket-Protocol'))) { |  | ||||||
|             if ('' !== ($agreedSubProtocols = $this->getSubProtocolString($subHeader->normalize()))) { |  | ||||||
|                 $response->setHeader('Sec-WebSocket-Protocol', $agreedSubProtocols); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $response->setHeader('X-Powered-By', \Ratchet\VERSION); |  | ||||||
|         $conn->send((string)$response); |  | ||||||
| 
 |  | ||||||
|         if (101 != $response->getStatusCode()) { |  | ||||||
|             return $conn->close(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $upgraded = $conn->WebSocket->version->upgradeConnection($conn, $this->component); |  | ||||||
| 
 |  | ||||||
|         $this->connections->attach($conn, $upgraded); |  | ||||||
| 
 |  | ||||||
|         $upgraded->WebSocket->established = true; |  | ||||||
| 
 |  | ||||||
|         return $this->component->onOpen($upgraded); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -143,10 +158,10 @@ class WsServer implements HttpServerInterface { | |||||||
|      */ |      */ | ||||||
|     public function onClose(ConnectionInterface $conn) { |     public function onClose(ConnectionInterface $conn) { | ||||||
|         if ($this->connections->contains($conn)) { |         if ($this->connections->contains($conn)) { | ||||||
|             $decor = $this->connections[$conn]; |             $context = $this->connections[$conn]; | ||||||
|             $this->connections->detach($conn); |             $this->connections->detach($conn); | ||||||
| 
 | 
 | ||||||
|             $this->component->onClose($decor); |             $this->delegate->onClose($context->connection); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -154,79 +169,57 @@ class WsServer implements HttpServerInterface { | |||||||
|      * {@inheritdoc} |      * {@inheritdoc} | ||||||
|      */ |      */ | ||||||
|     public function onError(ConnectionInterface $conn, \Exception $e) { |     public function onError(ConnectionInterface $conn, \Exception $e) { | ||||||
|         if ($conn->WebSocket->established && $this->connections->contains($conn)) { |         if ($this->connections->contains($conn)) { | ||||||
|             $this->component->onError($this->connections[$conn], $e); |             $this->delegate->onError($this->connections[$conn]->connection, $e); | ||||||
|         } else { |         } else { | ||||||
|             $conn->close(); |             $conn->close(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     public function onControlFrame(FrameInterface $frame, WsConnection $conn) { | ||||||
|      * Disable a specific version of the WebSocket protocol |         switch ($frame->getOpCode()) { | ||||||
|      * @param int $versionId Version ID to disable |             case Frame::OP_CLOSE: | ||||||
|      * @return WsServer |                 $conn->close($frame); | ||||||
|      */ |                 break; | ||||||
|     public function disableVersion($versionId) { |             case Frame::OP_PING: | ||||||
|         $this->versioner->disableVersion($versionId); |                 $conn->send(new Frame($frame->getPayload(), true, Frame::OP_PONG)); | ||||||
| 
 |                 break; | ||||||
|         return $this; |             case Frame::OP_PONG: | ||||||
|     } |                 $pongReceiver = $this->pongReceiver; | ||||||
| 
 |                 $pongReceiver($frame, $conn); | ||||||
|     /** |             break; | ||||||
|      * Toggle weather to check encoding of incoming messages |  | ||||||
|      * @param bool |  | ||||||
|      * @return WsServer |  | ||||||
|      */ |  | ||||||
|     public function setEncodingChecks($opt) { |  | ||||||
|         $this->validator->on = (boolean)$opt; |  | ||||||
| 
 |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param string |  | ||||||
|      * @return boolean |  | ||||||
|      */ |  | ||||||
|     public function isSubProtocolSupported($name) { |  | ||||||
|         if (!$this->isSpGenerated) { |  | ||||||
|             if ($this->component instanceof WsServerInterface) { |  | ||||||
|                 $this->acceptedSubProtocols = array_flip($this->component->getSubProtocols()); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             $this->isSpGenerated = true; |  | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         return array_key_exists($name, $this->acceptedSubProtocols); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     public function setStrictSubProtocolCheck($enable) { | ||||||
|      * @param  \Traversable|null $requested |         $this->handshakeNegotiator->setStrictSubProtocolCheck($enable); | ||||||
|      * @return string |     } | ||||||
|      */ | 
 | ||||||
|     protected function getSubProtocolString(\Traversable $requested = null) { |     public function enableKeepAlive(LoopInterface $loop, $interval = 30) { | ||||||
|         if (null !== $requested) { |         $lastPing = new Frame(uniqid(), true, Frame::OP_PING); | ||||||
|             foreach ($requested as $sub) { |         $pingedConnections = new \SplObjectStorage; | ||||||
|                 if ($this->isSubProtocolSupported($sub)) { |         $splClearer = new \SplObjectStorage; | ||||||
|                     return $sub; | 
 | ||||||
|                 } |         $this->pongReceiver = function(FrameInterface $frame, $wsConn) use ($pingedConnections, &$lastPing) { | ||||||
|  |             if ($frame->getPayload() === $lastPing->getPayload()) { | ||||||
|  |                 $pingedConnections->detach($wsConn); | ||||||
|             } |             } | ||||||
|         } |         }; | ||||||
| 
 | 
 | ||||||
|         return ''; |         $loop->addPeriodicTimer((int)$interval, function() use ($pingedConnections, &$lastPing, $splClearer) { | ||||||
|     } |             foreach ($pingedConnections as $wsConn) { | ||||||
|  |                 $wsConn->close(); | ||||||
|  |             } | ||||||
|  |             $pingedConnections->removeAllExcept($splClearer); | ||||||
| 
 | 
 | ||||||
|     /** |             $lastPing = new Frame(uniqid(), true, Frame::OP_PING); | ||||||
|      * Close a connection with an HTTP response |  | ||||||
|      * @param \Ratchet\ConnectionInterface $conn |  | ||||||
|      * @param int                          $code HTTP status code |  | ||||||
|      */ |  | ||||||
|     protected function close(ConnectionInterface $conn, $code = 400) { |  | ||||||
|         $response = new Response($code, array( |  | ||||||
|             'Sec-WebSocket-Version' => $this->versioner->getSupportedVersionString() |  | ||||||
|           , 'X-Powered-By'          => \Ratchet\VERSION |  | ||||||
|         )); |  | ||||||
| 
 | 
 | ||||||
|         $conn->send((string)$response); |             foreach ($this->connections as $key => $conn) { | ||||||
|         $conn->close(); |                 $wsConn  = $this->connections[$conn]->connection; | ||||||
|     } | 
 | ||||||
|  |                 $wsConn->send($lastPing); | ||||||
|  |                 $pingedConnections->attach($wsConn); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |    } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,17 +0,0 @@ | |||||||
| <?php |  | ||||||
| 
 |  | ||||||
|     require dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php'; |  | ||||||
| 
 |  | ||||||
|     $port = $argc > 1 ? $argv[1] : 8000; |  | ||||||
|     $impl = sprintf('React\EventLoop\%sLoop', $argc > 2 ? $argv[2] : 'StreamSelect'); |  | ||||||
| 
 |  | ||||||
|     $loop = new $impl; |  | ||||||
|     $sock = new React\Socket\Server($loop); |  | ||||||
|     $web  = new Ratchet\WebSocket\WsServer(new Ratchet\Server\EchoServer); |  | ||||||
|     $app  = new Ratchet\Http\HttpServer($web); |  | ||||||
|     $web->setEncodingChecks(false); |  | ||||||
| 
 |  | ||||||
|     $sock->listen($port, '0.0.0.0'); |  | ||||||
| 
 |  | ||||||
|     $server = new Ratchet\Server\IoServer($app, $sock, $loop); |  | ||||||
|     $server->run(); |  | ||||||
| @ -1,15 +1,36 @@ | |||||||
| <?php | <?php | ||||||
|  | use Ratchet\ConnectionInterface; | ||||||
| 
 | 
 | ||||||
|     require dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php'; |     require dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php'; | ||||||
| 
 | 
 | ||||||
|  | class BinaryEcho implements \Ratchet\WebSocket\MessageComponentInterface { | ||||||
|  |     public function onMessage(ConnectionInterface $from, \Ratchet\RFC6455\Messaging\MessageInterface $msg) { | ||||||
|  |         $from->send($msg); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function onOpen(ConnectionInterface $conn) { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function onClose(ConnectionInterface $conn) { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function onError(ConnectionInterface $conn, \Exception $e) { | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|     $port = $argc > 1 ? $argv[1] : 8000; |     $port = $argc > 1 ? $argv[1] : 8000; | ||||||
|     $impl = sprintf('React\EventLoop\%sLoop', $argc > 2 ? $argv[2] : 'StreamSelect'); |     $impl = sprintf('React\EventLoop\%sLoop', $argc > 2 ? $argv[2] : 'StreamSelect'); | ||||||
| 
 | 
 | ||||||
|     $loop = new $impl; |     $loop = new $impl; | ||||||
|     $sock = new React\Socket\Server($loop); |     $sock = new React\Socket\Server('0.0.0.0:' . $port, $loop); | ||||||
|     $app  = new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Server\EchoServer)); |  | ||||||
| 
 | 
 | ||||||
|     $sock->listen($port, '0.0.0.0'); |     $wsServer = new Ratchet\WebSocket\WsServer(new BinaryEcho); | ||||||
|  |     // This is enabled to test https://github.com/ratchetphp/Ratchet/issues/430
 | ||||||
|  |     // The time is left at 10 minutes so that it will not try to every ping anything
 | ||||||
|  |     // This causes the Ratchet server to crash on test 2.7
 | ||||||
|  |     $wsServer->enableKeepAlive($loop, 600); | ||||||
|  | 
 | ||||||
|  |     $app = new Ratchet\Http\HttpServer($wsServer); | ||||||
| 
 | 
 | ||||||
|     $server = new Ratchet\Server\IoServer($app, $sock, $loop); |     $server = new Ratchet\Server\IoServer($app, $sock, $loop); | ||||||
|     $server->run(); |     $server->run(); | ||||||
|  | |||||||
| @ -3,14 +3,13 @@ | |||||||
|   , "outdir": "reports/ab" |   , "outdir": "reports/ab" | ||||||
| 
 | 
 | ||||||
|   , "servers": [ |   , "servers": [ | ||||||
|         {"agent": "Ratchet/0.3 libevent", "url": "ws://localhost:8001", "options": {"version": 18}} |         {"agent": "Ratchet/0.4 libevent", "url": "ws://localhost:8001", "options": {"version": 18}} | ||||||
|       , {"agent": "Ratchet/0.3 libev", "url": "ws://localhost:8004", "options": {"version": 18}} |       , {"agent": "Ratchet/0.4 libev", "url": "ws://localhost:8004", "options": {"version": 18}} | ||||||
|       , {"agent": "Ratchet/0.3 streams", "url": "ws://localhost:8002", "options": {"version": 18}} |       , {"agent": "Ratchet/0.4 streams", "url": "ws://localhost:8002", "options": {"version": 18}} | ||||||
|       , {"agent": "Ratchet/0.3 -utf8", "url": "ws://localhost:8003", "options": {"version": 18}} |  | ||||||
|       , {"agent": "AutobahnTestSuite/0.5.9", "url": "ws://localhost:8000", "options": {"version": 18}} |       , {"agent": "AutobahnTestSuite/0.5.9", "url": "ws://localhost:8000", "options": {"version": 18}} | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|   , "cases": ["*"] |   , "cases": ["*"] | ||||||
|   , "exclude-cases": ["1.2.*", "2.3", "2.4", "2.6", "9.2.*", "9.4.*", "9.6.*", "9.8.*"] |   , "exclude-cases": [] | ||||||
|   , "exclude-agent-cases": {} |   , "exclude-agent-cases": {} | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,6 +7,6 @@ | |||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|   , "cases": ["*"] |   , "cases": ["*"] | ||||||
|   , "exclude-cases": ["1.2.*", "2.3", "2.4", "2.6", "9.2.*", "9.4.*", "9.6.*", "9.8.*"] |   , "exclude-cases": [] | ||||||
|   , "exclude-agent-cases": {} |   , "exclude-agent-cases": {} | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,53 +0,0 @@ | |||||||
| <?php |  | ||||||
| use Guzzle\Http\Message\Request; |  | ||||||
| 
 |  | ||||||
| class GuzzleTest extends \PHPUnit_Framework_TestCase { |  | ||||||
|     protected $_request; |  | ||||||
| 
 |  | ||||||
|     protected $_headers = array( |  | ||||||
|         'Upgrade' => 'websocket' |  | ||||||
|       , 'Connection' => 'Upgrade' |  | ||||||
|       , 'Host' => 'localhost:8080' |  | ||||||
|       , 'Origin' => 'chrome://newtab' |  | ||||||
|       , 'Sec-WebSocket-Protocol' => 'one, two, three' |  | ||||||
|       , 'Sec-WebSocket-Key' => '9bnXNp3ae6FbFFRtPdiPXA==' |  | ||||||
|       , 'Sec-WebSocket-Version' => '13' |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     public function setUp() { |  | ||||||
|         $this->_request = new Request('GET', 'http://localhost', $this->_headers); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testGetHeaderString() { |  | ||||||
|         $this->assertEquals('Upgrade', (string)$this->_request->getHeader('connection')); |  | ||||||
|         $this->assertEquals('9bnXNp3ae6FbFFRtPdiPXA==', (string)$this->_request->getHeader('Sec-Websocket-Key')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testGetHeaderInteger() { |  | ||||||
|         $this->assertSame('13', (string)$this->_request->getHeader('Sec-Websocket-Version')); |  | ||||||
|         $this->assertSame(13, (int)(string)$this->_request->getHeader('Sec-WebSocket-Version')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testGetHeaderObject() { |  | ||||||
|         $this->assertInstanceOf('Guzzle\Http\Message\Header', $this->_request->getHeader('Origin')); |  | ||||||
|         $this->assertNull($this->_request->getHeader('Non-existant-header')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testHeaderObjectNormalizeValues() { |  | ||||||
|         $expected  = 1 + substr_count($this->_headers['Sec-WebSocket-Protocol'], ','); |  | ||||||
|         $protocols = $this->_request->getHeader('Sec-WebSocket-Protocol')->normalize(); |  | ||||||
|         $count     = 0; |  | ||||||
| 
 |  | ||||||
|         foreach ($protocols as $protocol) { |  | ||||||
|             $count++; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($expected, $count); |  | ||||||
|         $this->assertEquals($expected, count($protocols)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testRequestFactoryCreateSignature() { |  | ||||||
|         $ref = new \ReflectionMethod('Guzzle\Http\Message\RequestFactory', 'create'); |  | ||||||
|         $this->assertEquals(2, $ref->getNumberOfRequiredParameters()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,67 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\Http\Guzzle\Http\Message; |  | ||||||
| use Ratchet\Http\Guzzle\Http\Message\RequestFactory; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @covers Ratchet\Http\Guzzle\Http\Message\RequestFactory |  | ||||||
|  */ |  | ||||||
| class RequestFactoryTest extends \PHPUnit_Framework_TestCase { |  | ||||||
|     protected $factory; |  | ||||||
| 
 |  | ||||||
|     public function setUp() { |  | ||||||
|         $this->factory = RequestFactory::getInstance(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testMessageProvider() { |  | ||||||
|         return array( |  | ||||||
|             'status' => 'GET / HTTP/1.1' |  | ||||||
|           , 'headers' => array( |  | ||||||
|                 'Upgrade'    => 'WebSocket' |  | ||||||
|               , 'Connection' => 'Upgrade' |  | ||||||
|               , 'Host'       => 'localhost:8000' |  | ||||||
|               , 'Sec-WebSocket-Key1' => '> b3lU Z0 fh f 3+83394 6  (zG4' |  | ||||||
|               , 'Sec-WebSocket-Key2' => ',3Z0X0677 dV-d [159 Z*4' |  | ||||||
|             ) |  | ||||||
|           , 'body' => "123456\r\n\r\n" |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function combineMessage($status, array $headers, $body = '') { |  | ||||||
|         $message = $status . "\r\n"; |  | ||||||
| 
 |  | ||||||
|         foreach ($headers as $key => $val) { |  | ||||||
|             $message .= "{$key}: {$val}\r\n"; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $message .= "\r\n{$body}"; |  | ||||||
| 
 |  | ||||||
|         return $message; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testExpectedDataFromGuzzleHeaders() { |  | ||||||
|         $parts   = $this->testMessageProvider(); |  | ||||||
|         $message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']); |  | ||||||
|         $object  = $this->factory->fromMessage($message); |  | ||||||
| 
 |  | ||||||
|         foreach ($parts['headers'] as $key => $val) { |  | ||||||
|             $this->assertEquals($val, $object->getHeader($key, true)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testExpectedDataFromNonGuzzleHeaders() { |  | ||||||
|         $parts   = $this->testMessageProvider(); |  | ||||||
|         $message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']); |  | ||||||
|         $object  = $this->factory->fromMessage($message); |  | ||||||
| 
 |  | ||||||
|         $this->assertNull($object->getHeader('Nope', true)); |  | ||||||
|         $this->assertNull($object->getHeader('Nope')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testExpectedDataFromNonGuzzleBody() { |  | ||||||
|         $parts   = $this->testMessageProvider(); |  | ||||||
|         $message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']); |  | ||||||
|         $object  = $this->factory->fromMessage($message); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($parts['body'], (string)$object->getBody()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,6 +1,5 @@ | |||||||
| <?php | <?php | ||||||
| namespace Ratchet\Http; | namespace Ratchet\Http; | ||||||
| use Ratchet\Http\HttpRequestParser; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @covers Ratchet\Http\HttpRequestParser |  * @covers Ratchet\Http\HttpRequestParser | ||||||
| @ -46,6 +45,6 @@ class HttpRequestParserTest extends \PHPUnit_Framework_TestCase { | |||||||
|         $conn = $this->getMock('\Ratchet\ConnectionInterface'); |         $conn = $this->getMock('\Ratchet\ConnectionInterface'); | ||||||
|         $return = $this->parser->onMessage($conn, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n"); |         $return = $this->parser->onMessage($conn, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n"); | ||||||
| 
 | 
 | ||||||
|         $this->assertInstanceOf('\Guzzle\Http\Message\RequestInterface', $return); |         $this->assertInstanceOf('\Psr\Http\Message\RequestInterface', $return); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -9,8 +9,8 @@ class OriginCheckTest extends AbstractMessageComponentTestCase { | |||||||
|     protected $_reqStub; |     protected $_reqStub; | ||||||
| 
 | 
 | ||||||
|     public function setUp() { |     public function setUp() { | ||||||
|         $this->_reqStub = $this->getMock('Guzzle\Http\Message\RequestInterface'); |         $this->_reqStub = $this->getMock('Psr\Http\Message\RequestInterface'); | ||||||
|         $this->_reqStub->expects($this->any())->method('getHeader')->will($this->returnValue('localhost')); |         $this->_reqStub->expects($this->any())->method('getHeader')->will($this->returnValue(['localhost'])); | ||||||
| 
 | 
 | ||||||
|         parent::setUp(); |         parent::setUp(); | ||||||
| 
 | 
 | ||||||
| @ -34,7 +34,7 @@ class OriginCheckTest extends AbstractMessageComponentTestCase { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testCloseOnNonMatchingOrigin() { |     public function testCloseOnNonMatchingOrigin() { | ||||||
|         $this->_serv->allowedOrigins = array('socketo.me'); |         $this->_serv->allowedOrigins = ['socketo.me']; | ||||||
|         $this->_conn->expects($this->once())->method('close'); |         $this->_conn->expects($this->once())->method('close'); | ||||||
| 
 | 
 | ||||||
|         $this->_serv->onOpen($this->_conn, $this->_reqStub); |         $this->_serv->onOpen($this->_conn, $this->_reqStub); | ||||||
|  | |||||||
| @ -1,9 +1,12 @@ | |||||||
| <?php | <?php | ||||||
| namespace Ratchet\Http; | namespace Ratchet\Http; | ||||||
| use Ratchet\Http\Router; |  | ||||||
| use Ratchet\WebSocket\WsServerInterface; | use Ratchet\WebSocket\WsServerInterface; | ||||||
| use Symfony\Component\Routing\Exception\ResourceNotFoundException; | use Symfony\Component\Routing\Exception\ResourceNotFoundException; | ||||||
| use Symfony\Component\Routing\Matcher\UrlMatcherInterface; | use Symfony\Component\Routing\Matcher\UrlMatcherInterface; | ||||||
|  | use Symfony\Component\Routing\RequestContext; | ||||||
|  | use Symfony\Component\Routing\RouteCollection; | ||||||
|  | use Symfony\Component\Routing\Matcher\UrlMatcher; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @covers Ratchet\Http\Router |  * @covers Ratchet\Http\Router | ||||||
| @ -12,21 +15,17 @@ class RouterTest extends \PHPUnit_Framework_TestCase { | |||||||
|     protected $_router; |     protected $_router; | ||||||
|     protected $_matcher; |     protected $_matcher; | ||||||
|     protected $_conn; |     protected $_conn; | ||||||
|  |     protected $_uri; | ||||||
|     protected $_req; |     protected $_req; | ||||||
| 
 | 
 | ||||||
|     public function setUp() { |     public function setUp() { | ||||||
|         $queryMock = $this->getMock('Guzzle\Http\QueryString'); |         $this->_conn = $this->getMock('\Ratchet\ConnectionInterface'); | ||||||
|         $queryMock |         $this->_uri  = $this->getMock('Psr\Http\Message\UriInterface'); | ||||||
|             ->expects($this->any()) |         $this->_req  = $this->getMock('\Psr\Http\Message\RequestInterface'); | ||||||
|             ->method('getAll') |  | ||||||
|             ->will($this->returnValue(array())); |  | ||||||
| 
 |  | ||||||
|         $this->_conn    = $this->getMock('\Ratchet\ConnectionInterface'); |  | ||||||
|         $this->_req     = $this->getMock('\Guzzle\Http\Message\RequestInterface'); |  | ||||||
|         $this->_req |         $this->_req | ||||||
|             ->expects($this->any()) |             ->expects($this->any()) | ||||||
|             ->method('getQuery') |             ->method('getUri') | ||||||
|             ->will($this->returnValue($queryMock)); |             ->will($this->returnValue($this->_uri)); | ||||||
|         $this->_matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface'); |         $this->_matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface'); | ||||||
|         $this->_matcher |         $this->_matcher | ||||||
|             ->expects($this->any()) |             ->expects($this->any()) | ||||||
| @ -34,7 +33,14 @@ class RouterTest extends \PHPUnit_Framework_TestCase { | |||||||
|             ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext'))); |             ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext'))); | ||||||
|         $this->_router  = new Router($this->_matcher); |         $this->_router  = new Router($this->_matcher); | ||||||
| 
 | 
 | ||||||
|         $this->_req->expects($this->any())->method('getPath')->will($this->returnValue('/whatever')); |         $this->_uri->expects($this->any())->method('getPath')->will($this->returnValue('ws://doesnt.matter/')); | ||||||
|  |         $this->_uri->expects($this->any())->method('withQuery')->with($this->callback(function($val) { | ||||||
|  |             $this->setResult($val); | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         }))->will($this->returnSelf()); | ||||||
|  |         $this->_uri->expects($this->any())->method('getQuery')->will($this->returnCallback([$this, 'getResult'])); | ||||||
|  |         $this->_req->expects($this->any())->method('withUri')->will($this->returnSelf()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testFourOhFour() { |     public function testFourOhFour() { | ||||||
| @ -103,41 +109,57 @@ class RouterTest extends \PHPUnit_Framework_TestCase { | |||||||
|         $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock(); |         $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock(); | ||||||
|         /** @var $matcher UrlMatcherInterface */ |         /** @var $matcher UrlMatcherInterface */ | ||||||
|         $this->_matcher->expects($this->any())->method('match')->will( |         $this->_matcher->expects($this->any())->method('match')->will( | ||||||
|             $this->returnValue(array('_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux')) |             $this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux']) | ||||||
|         ); |         ); | ||||||
|         $conn = $this->getMock('Ratchet\Mock\Connection'); |         $conn = $this->getMock('Ratchet\Mock\Connection'); | ||||||
| 
 | 
 | ||||||
|         $request = $this->getMock('Guzzle\Http\Message\Request', array('getPath'), array('GET', 'ws://random.url'), '', false); |  | ||||||
|         $request->expects($this->any())->method('getPath')->will($this->returnValue('ws://doesnt.matter/')); |  | ||||||
| 
 |  | ||||||
|         $request->setHeaderFactory($this->getMock('Guzzle\Http\Message\Header\HeaderFactoryInterface')); |  | ||||||
|         $request->setUrl('ws://doesnt.matter/'); |  | ||||||
| 
 |  | ||||||
|         $router = new Router($this->_matcher); |         $router = new Router($this->_matcher); | ||||||
| 
 | 
 | ||||||
|         $router->onOpen($conn, $request); |         $router->onOpen($conn, $this->_req); | ||||||
| 
 | 
 | ||||||
|         $this->assertEquals(array('foo' => 'bar', 'baz' => 'qux'), $request->getQuery()->getAll()); |         $this->assertEquals('foo=bar&baz=qux', $this->_req->getUri()->getQuery()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testQueryParams() { |     public function testQueryParams() { | ||||||
|         $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock(); |         $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock(); | ||||||
|         $this->_matcher->expects($this->any())->method('match')->will( |         $this->_matcher->expects($this->any())->method('match')->will( | ||||||
|             $this->returnValue(array('_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux')) |             $this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux']) | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         $conn    = $this->getMock('Ratchet\Mock\Connection'); |         $conn    = $this->getMock('Ratchet\Mock\Connection'); | ||||||
|         /**@var $request \Guzzle\Http\Message\Request */ |         $request = $this->getMock('Psr\Http\Message\RequestInterface'); | ||||||
|         $request = $this->getMock('Guzzle\Http\Message\Request', array('getPath'), array('GET', ''), '', false); |         $uri = new \GuzzleHttp\Psr7\Uri('ws://doesnt.matter/endpoint?hello=world&foo=nope'); | ||||||
| 
 | 
 | ||||||
|         $request->setHeaderFactory($this->getMock('Guzzle\Http\Message\Header\HeaderFactoryInterface')); |         $request->expects($this->any())->method('getUri')->will($this->returnCallback(function() use (&$uri) { | ||||||
|         $request->setUrl('ws://doesnt.matter?hello=world&foo=nope'); |             return $uri; | ||||||
|  |         })); | ||||||
|  |         $request->expects($this->any())->method('withUri')->with($this->callback(function($url) use (&$uri) { | ||||||
|  |             $uri = $url; | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         }))->will($this->returnSelf()); | ||||||
| 
 | 
 | ||||||
|         $router = new Router($this->_matcher); |         $router = new Router($this->_matcher); | ||||||
|         $router->onOpen($conn, $request); |         $router->onOpen($conn, $request); | ||||||
| 
 | 
 | ||||||
|         $this->assertEquals(array('foo' => 'nope', 'baz' => 'qux', 'hello' => 'world'), $request->getQuery()->getAll()); |         $this->assertEquals('foo=nope&baz=qux&hello=world', $request->getUri()->getQuery()); | ||||||
|         $this->assertEquals('ws', $request->getScheme()); |         $this->assertEquals('ws', $request->getUri()->getScheme()); | ||||||
|         $this->assertEquals('doesnt.matter', $request->getHost()); |         $this->assertEquals('doesnt.matter', $request->getUri()->getHost()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testImpatientClientOverflow() { | ||||||
|  |         $this->_conn->expects($this->once())->method('close'); | ||||||
|  | 
 | ||||||
|  |         $header = "GET /nope HTTP/1.1
 | ||||||
|  | Upgrade: websocket                                    | ||||||
|  | Connection: upgrade                                   | ||||||
|  | Host: localhost                                  | ||||||
|  | Origin: http://localhost                         | ||||||
|  | Sec-WebSocket-Version: 13\r\n\r\n";
 | ||||||
|  | 
 | ||||||
|  |         $app = new HttpServer(new Router(new UrlMatcher(new RouteCollection, new RequestContext))); | ||||||
|  |         $app->onOpen($this->_conn); | ||||||
|  |         $app->onMessage($this->_conn, $header); | ||||||
|  |         $app->onMessage($this->_conn, 'Silly body'); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -20,10 +20,10 @@ class IoServerTest extends \PHPUnit_Framework_TestCase { | |||||||
|         $this->app = $this->getMock('\\Ratchet\\MessageComponentInterface'); |         $this->app = $this->getMock('\\Ratchet\\MessageComponentInterface'); | ||||||
| 
 | 
 | ||||||
|         $loop = new StreamSelectLoop; |         $loop = new StreamSelectLoop; | ||||||
|         $this->reactor = new Server($loop); |         $this->reactor = new Server(0, $loop); | ||||||
|         $this->reactor->listen(0); |  | ||||||
| 
 | 
 | ||||||
|         $this->port   = $this->reactor->getPort(); |         $uri = $this->reactor->getAddress(); | ||||||
|  |         $this->port   = parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri, PHP_URL_PORT); | ||||||
|         $this->server = new IoServer($this->app, $this->reactor, $loop); |         $this->server = new IoServer($this->app, $this->reactor, $loop); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,11 +1,8 @@ | |||||||
| <?php | <?php | ||||||
| namespace Ratchet\Session; | namespace Ratchet\Session; | ||||||
| use Ratchet\AbstractMessageComponentTestCase; | use Ratchet\AbstractMessageComponentTestCase; | ||||||
| use Ratchet\Session\SessionProvider; |  | ||||||
| use Ratchet\Mock\MemorySessionHandler; |  | ||||||
| use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; | use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; | ||||||
| use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; | use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; | ||||||
| use Guzzle\Http\Message\Request; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @covers Ratchet\Session\SessionProvider |  * @covers Ratchet\Session\SessionProvider | ||||||
| @ -35,7 +32,7 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function getComponentClassString() { |     public function getComponentClassString() { | ||||||
|         return '\Ratchet\MessageComponentInterface'; |         return '\Ratchet\Http\HttpServerInterface'; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function classCaseProvider() { |     public function classCaseProvider() { | ||||||
| @ -53,7 +50,7 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { | |||||||
|         $method = $ref->getMethod('toClassCase'); |         $method = $ref->getMethod('toClassCase'); | ||||||
|         $method->setAccessible(true); |         $method->setAccessible(true); | ||||||
| 
 | 
 | ||||||
|         $component = new SessionProvider($this->getMock('Ratchet\\MessageComponentInterface'), $this->getMock('\SessionHandlerInterface')); |         $component = new SessionProvider($this->getMock($this->getComponentClassString()), $this->getMock('\SessionHandlerInterface')); | ||||||
|         $this->assertEquals($out, $method->invokeArgs($component, array($in))); |         $this->assertEquals($out, $method->invokeArgs($component, array($in))); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -82,16 +79,13 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { | |||||||
|         $pdoHandler = new PdoSessionHandler($pdo, $dbOptions); |         $pdoHandler = new PdoSessionHandler($pdo, $dbOptions); | ||||||
|         $pdoHandler->write($sessionId, '_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}'); |         $pdoHandler->write($sessionId, '_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}'); | ||||||
| 
 | 
 | ||||||
|         $component  = new SessionProvider($this->getMock('Ratchet\\MessageComponentInterface'), $pdoHandler, array('auto_start' => 1)); |         $component  = new SessionProvider($this->getMock($this->getComponentClassString()), $pdoHandler, array('auto_start' => 1)); | ||||||
|         $connection = $this->getMock('Ratchet\\ConnectionInterface'); |         $connection = $this->getMock('Ratchet\\ConnectionInterface'); | ||||||
| 
 | 
 | ||||||
|         $headers = $this->getMock('Guzzle\\Http\\Message\\Request', array('getCookie'), array('POST', '/', array())); |         $headers = $this->getMock('Psr\Http\Message\RequestInterface'); | ||||||
|         $headers->expects($this->once())->method('getCookie', array(ini_get('session.name')))->will($this->returnValue($sessionId)); |         $headers->expects($this->once())->method('getHeader')->will($this->returnValue([ini_get('session.name') . "={$sessionId};"])); | ||||||
| 
 | 
 | ||||||
|         $connection->WebSocket          = new \StdClass; |         $component->onOpen($connection, $headers); | ||||||
|         $connection->WebSocket->request = $headers; |  | ||||||
| 
 |  | ||||||
|         $component->onOpen($connection); |  | ||||||
| 
 | 
 | ||||||
|         $this->assertEquals('world', $connection->Session->get('hello')); |         $this->assertEquals('world', $connection->Session->get('hello')); | ||||||
|     } |     } | ||||||
| @ -99,12 +93,9 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { | |||||||
|     protected function newConn() { |     protected function newConn() { | ||||||
|         $conn = $this->getMock('Ratchet\ConnectionInterface'); |         $conn = $this->getMock('Ratchet\ConnectionInterface'); | ||||||
| 
 | 
 | ||||||
|         $headers = $this->getMock('Guzzle\Http\Message\Request', array('getCookie'), array('POST', '/', array())); |         $headers = $this->getMock('Psr\Http\Message\Request', array('getCookie'), array('POST', '/', array())); | ||||||
|         $headers->expects($this->once())->method('getCookie', array(ini_get('session.name')))->will($this->returnValue(null)); |         $headers->expects($this->once())->method('getCookie', array(ini_get('session.name')))->will($this->returnValue(null)); | ||||||
| 
 | 
 | ||||||
|         $conn->WebSocket          = new \StdClass; |  | ||||||
|         $conn->WebSocket->request = $headers; |  | ||||||
| 
 |  | ||||||
|         return $conn; |         return $conn; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -114,21 +105,6 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { | |||||||
|         $this->_serv->onMessage($this->_conn, $message); |         $this->_serv->onMessage($this->_conn, $message); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testGetSubProtocolsReturnsArray() { |  | ||||||
|         $mock = $this->getMock('Ratchet\\MessageComponentInterface'); |  | ||||||
|         $comp = new SessionProvider($mock, new NullSessionHandler); |  | ||||||
| 
 |  | ||||||
|         $this->assertInternalType('array', $comp->getSubProtocols()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testGetSubProtocolsGetFromApp() { |  | ||||||
|         $mock = $this->getMock('Ratchet\WebSocket\Stub\WsMessageComponentInterface'); |  | ||||||
|         $mock->expects($this->once())->method('getSubProtocols')->will($this->returnValue(array('hello', 'world'))); |  | ||||||
|         $comp = new SessionProvider($mock, new NullSessionHandler); |  | ||||||
| 
 |  | ||||||
|         $this->assertGreaterThanOrEqual(2, count($comp->getSubProtocols())); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testRejectInvalidSeralizers() { |     public function testRejectInvalidSeralizers() { | ||||||
|         if (!function_exists('wddx_serialize_value')) { |         if (!function_exists('wddx_serialize_value')) { | ||||||
|             $this->markTestSkipped(); |             $this->markTestSkipped(); | ||||||
| @ -136,6 +112,13 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { | |||||||
| 
 | 
 | ||||||
|         ini_set('session.serialize_handler', 'wddx'); |         ini_set('session.serialize_handler', 'wddx'); | ||||||
|         $this->setExpectedException('\RuntimeException'); |         $this->setExpectedException('\RuntimeException'); | ||||||
|         new SessionProvider($this->getMock('\Ratchet\MessageComponentInterface'), $this->getMock('\SessionHandlerInterface')); |         new SessionProvider($this->getMock($this->getComponentClassString()), $this->getMock('\SessionHandlerInterface')); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected function doOpen($conn) { | ||||||
|  |         $request = $this->getMock('Psr\Http\Message\RequestInterface'); | ||||||
|  |         $request->expects($this->any())->method('getHeader')->will($this->returnValue([])); | ||||||
|  | 
 | ||||||
|  |         $this->_serv->onOpen($conn, $request); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,15 +1,11 @@ | |||||||
| <?php | <?php | ||||||
| 
 |  | ||||||
| namespace Ratchet\Session\Storage; | namespace Ratchet\Session\Storage; | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| use Ratchet\Session\Serialize\PhpHandler; | use Ratchet\Session\Serialize\PhpHandler; | ||||||
| use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; | use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; | ||||||
| use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; | use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; | ||||||
| use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; | use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; | ||||||
| 
 | 
 | ||||||
| class VirtualSessionStoragePDOTest extends \PHPUnit_Framework_TestCase | class VirtualSessionStoragePDOTest extends \PHPUnit_Framework_TestCase { | ||||||
| { |  | ||||||
|     /** |     /** | ||||||
|      * @var VirtualSessionStorage |      * @var VirtualSessionStorage | ||||||
|      */ |      */ | ||||||
| @ -17,8 +13,11 @@ class VirtualSessionStoragePDOTest extends \PHPUnit_Framework_TestCase | |||||||
| 
 | 
 | ||||||
|     protected $_pathToDB; |     protected $_pathToDB; | ||||||
| 
 | 
 | ||||||
|     public function setUp() |     public function setUp() { | ||||||
|     { |         if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) { | ||||||
|  |             return $this->markTestSkipped('Session test requires PDO and pdo_sqlite'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         $schema = <<<SQL |         $schema = <<<SQL | ||||||
| CREATE TABLE `sessions` ( | CREATE TABLE `sessions` ( | ||||||
|     `sess_id` VARBINARY(128) NOT NULL PRIMARY KEY, |     `sess_id` VARBINARY(128) NOT NULL PRIMARY KEY, | ||||||
| @ -42,17 +41,13 @@ SQL; | |||||||
|         $this->_virtualSessionStorage->registerBag(new AttributeBag()); |         $this->_virtualSessionStorage->registerBag(new AttributeBag()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function tearDown() |     public function tearDown() { | ||||||
|     { |  | ||||||
|         unlink($this->_pathToDB); |         unlink($this->_pathToDB); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testStartWithDSN() |     public function testStartWithDSN() { | ||||||
|     { |  | ||||||
|         $this->_virtualSessionStorage->start(); |         $this->_virtualSessionStorage->start(); | ||||||
| 
 | 
 | ||||||
|         $this->assertTrue($this->_virtualSessionStorage->isStarted()); |         $this->assertTrue($this->_virtualSessionStorage->isStarted()); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,9 +4,9 @@ use Ratchet\Mock\Connection; | |||||||
| use Ratchet\Mock\WampComponent as TestComponent; | use Ratchet\Mock\WampComponent as TestComponent; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @covers Ratchet\Wamp\ServerProtocol |  * @covers \Ratchet\Wamp\ServerProtocol | ||||||
|  * @covers Ratchet\Wamp\WampServerInterface |  * @covers \Ratchet\Wamp\WampServerInterface | ||||||
|  * @covers Ratchet\Wamp\WampConnection |  * @covers \Ratchet\Wamp\WampConnection | ||||||
|  */ |  */ | ||||||
| class ServerProtocolTest extends \PHPUnit_Framework_TestCase { | class ServerProtocolTest extends \PHPUnit_Framework_TestCase { | ||||||
|     protected $_comp; |     protected $_comp; | ||||||
| @ -23,13 +23,13 @@ class ServerProtocolTest extends \PHPUnit_Framework_TestCase { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function invalidMessageProvider() { |     public function invalidMessageProvider() { | ||||||
|         return array( |         return [ | ||||||
|             array(0) |             [0] | ||||||
|           , array(3) |           , [3] | ||||||
|           , array(4) |           , [4] | ||||||
|           , array(8) |           , [8] | ||||||
|           , array(9) |           , [9] | ||||||
|         ); |         ]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -40,7 +40,7 @@ class ServerProtocolTest extends \PHPUnit_Framework_TestCase { | |||||||
| 
 | 
 | ||||||
|         $conn = $this->newConn(); |         $conn = $this->newConn(); | ||||||
|         $this->_comp->onOpen($conn); |         $this->_comp->onOpen($conn); | ||||||
|         $this->_comp->onMessage($conn, json_encode(array($type))); |         $this->_comp->onMessage($conn, json_encode([$type])); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testWelcomeMessage() { |     public function testWelcomeMessage() { | ||||||
| @ -82,16 +82,16 @@ class ServerProtocolTest extends \PHPUnit_Framework_TestCase { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function callProvider() { |     public function callProvider() { | ||||||
|         return array( |         return [ | ||||||
|             array(2, 'a', 'b') |             [2, 'a', 'b'] | ||||||
|           , array(2, array('a', 'b')) |           , [2, ['a', 'b']] | ||||||
|           , array(1, 'one') |           , [1, 'one'] | ||||||
|           , array(3, 'one', 'two', 'three') |           , [3, 'one', 'two', 'three'] | ||||||
|           , array(3, array('un', 'deux', 'trois')) |           , [3, ['un', 'deux', 'trois']] | ||||||
|           , array(2, 'hi', array('hello', 'world')) |           , [2, 'hi', ['hello', 'world']] | ||||||
|           , array(2, array('hello', 'world'), 'hi') |           , [2, ['hello', 'world'], 'hi'] | ||||||
|           , array(2, array('hello' => 'world', 'herp' => 'derp')) |           , [2, ['hello' => 'world', 'herp' => 'derp']] | ||||||
|         ); |         ]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -102,7 +102,7 @@ class ServerProtocolTest extends \PHPUnit_Framework_TestCase { | |||||||
|         $paramNum = array_shift($args); |         $paramNum = array_shift($args); | ||||||
| 
 | 
 | ||||||
|         $uri = 'http://example.com/endpoint/' . rand(1, 100); |         $uri = 'http://example.com/endpoint/' . rand(1, 100); | ||||||
|         $id  = uniqid(); |         $id  = uniqid('', false); | ||||||
|         $clientMessage = array_merge(array(2, $id, $uri), $args); |         $clientMessage = array_merge(array(2, $id, $uri), $args); | ||||||
| 
 | 
 | ||||||
|         $conn = $this->newConn(); |         $conn = $this->newConn(); | ||||||
| @ -145,8 +145,8 @@ class ServerProtocolTest extends \PHPUnit_Framework_TestCase { | |||||||
|     public function testPublishAndEligible() { |     public function testPublishAndEligible() { | ||||||
|         $conn = $this->newConn(); |         $conn = $this->newConn(); | ||||||
| 
 | 
 | ||||||
|         $buddy  = uniqid(); |         $buddy  = uniqid('', false); | ||||||
|         $friend = uniqid(); |         $friend = uniqid('', false); | ||||||
| 
 | 
 | ||||||
|         $this->_comp->onOpen($conn); |         $this->_comp->onOpen($conn); | ||||||
|         $this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', false, array($buddy, $friend)))); |         $this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', false, array($buddy, $friend)))); | ||||||
| @ -265,4 +265,31 @@ class ServerProtocolTest extends \PHPUnit_Framework_TestCase { | |||||||
|         $this->_comp->onOpen($conn); |         $this->_comp->onOpen($conn); | ||||||
|         $this->_comp->onMessage($conn, $message); |         $this->_comp->onMessage($conn, $message); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     public function testBadClientInputFromNonStringTopic() { | ||||||
|  |         $this->setExpectedException('\Ratchet\Wamp\Exception'); | ||||||
|  | 
 | ||||||
|  |         $conn = new WampConnection($this->newConn()); | ||||||
|  |         $this->_comp->onOpen($conn); | ||||||
|  | 
 | ||||||
|  |         $this->_comp->onMessage($conn, json_encode([5, ['hells', 'nope']])); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testBadPrefixWithNonStringTopic() { | ||||||
|  |         $this->setExpectedException('\Ratchet\Wamp\Exception'); | ||||||
|  | 
 | ||||||
|  |         $conn = new WampConnection($this->newConn()); | ||||||
|  |         $this->_comp->onOpen($conn); | ||||||
|  | 
 | ||||||
|  |         $this->_comp->onMessage($conn, json_encode([1, ['hells', 'nope'], ['bad', 'input']])); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testBadPublishWithNonStringTopic() { | ||||||
|  |         $this->setExpectedException('\Ratchet\Wamp\Exception'); | ||||||
|  | 
 | ||||||
|  |         $conn = new WampConnection($this->newConn()); | ||||||
|  |         $this->_comp->onOpen($conn); | ||||||
|  | 
 | ||||||
|  |         $this->_comp->onMessage($conn, json_encode([7, ['bad', 'input'], 'Hider'])); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -185,21 +185,18 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static function topicConnExpectationProvider() { |     public static function topicConnExpectationProvider() { | ||||||
|         return array( |         return [ | ||||||
|             array(true, 'onClose', 0) |             [ 'onClose', 0] | ||||||
|           , array(true, 'onUnsubscribe', 0) |           , ['onUnsubscribe', 0] | ||||||
|           , array(false, 'onClose', 1) |         ]; | ||||||
|           , array(false, 'onUnsubscribe', 1) |  | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @dataProvider topicConnExpectationProvider |      * @dataProvider topicConnExpectationProvider | ||||||
|      */ |      */ | ||||||
|     public function testTopicRetentionFromLeavingConnections($autoDelete, $methodCall, $expectation) { |     public function testTopicRetentionFromLeavingConnections($methodCall, $expectation) { | ||||||
|         $topicName = 'checkTopic'; |         $topicName = 'checkTopic'; | ||||||
|         list($topic, $attribute) = $this->topicProvider($topicName); |         list($topic, $attribute) = $this->topicProvider($topicName); | ||||||
|         $topic->autoDelete = $autoDelete; |  | ||||||
| 
 | 
 | ||||||
|         $this->mngr->onSubscribe($this->conn, $topicName); |         $this->mngr->onSubscribe($this->conn, $topicName); | ||||||
|         call_user_func_array(array($this->mngr, $methodCall), array($this->conn, $topicName)); |         call_user_func_array(array($this->mngr, $methodCall), array($this->conn, $topicName)); | ||||||
|  | |||||||
| @ -1,103 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version; |  | ||||||
| use Ratchet\WebSocket\Version\Hixie76; |  | ||||||
| use Ratchet\Http\HttpServer; |  | ||||||
| use Ratchet\WebSocket\WsServer; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @covers Ratchet\WebSocket\Version\Hixie76 |  | ||||||
|  */ |  | ||||||
| class Hixie76Test extends \PHPUnit_Framework_TestCase { |  | ||||||
|     protected $_crlf = "\r\n"; |  | ||||||
|     protected $_body = '6dW+XgKfWV0='; |  | ||||||
| 
 |  | ||||||
|     protected $_version; |  | ||||||
| 
 |  | ||||||
|     public function setUp() { |  | ||||||
|         $this->_version = new Hixie76; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testClassImplementsVersionInterface() { |  | ||||||
|         $constraint = $this->isInstanceOf('\\Ratchet\\WebSocket\\Version\\VersionInterface'); |  | ||||||
|         $this->assertThat($this->_version, $constraint); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider keyProvider |  | ||||||
|      */ |  | ||||||
|     public function testKeySigningForHandshake($accept, $key) { |  | ||||||
|         $this->assertEquals($accept, $this->_version->generateKeyNumber($key)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function keyProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(179922739, '17  9 G`ZD9   2 2b 7X 3 /r90') |  | ||||||
|           , array(906585445, '3e6b263  4 17 80') |  | ||||||
|           , array(0, '3e6b26341780') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function headerProvider() { |  | ||||||
|         $key1 = base64_decode('QTN+ICszNiA2IDJvICBWOG4gNyAgc08yODhZ'); |  | ||||||
|         $key2 = base64_decode('TzEyICAgeVsgIFFSNDUgM1IgLiAyOFggNC00dn4z'); |  | ||||||
| 
 |  | ||||||
|         $headers  = "GET / HTTP/1.1"; |  | ||||||
|         $headers .= "Upgrade: WebSocket{$this->_crlf}"; |  | ||||||
|         $headers .= "Connection: Upgrade{$this->_crlf}"; |  | ||||||
|         $headers .= "Host: socketo.me{$this->_crlf}"; |  | ||||||
|         $headers .= "Origin: http://fiddle.jshell.net{$this->_crlf}"; |  | ||||||
|         $headers .= "Sec-WebSocket-Key1:17 Z4< F94 N3  7P41  7{$this->_crlf}"; |  | ||||||
|         $headers .= "Sec-WebSocket-Key2:1 23C3:,2% 1-29  4 f0{$this->_crlf}"; |  | ||||||
|         $headers .= "(Key3):70:00:EE:6E:33:20:90:69{$this->_crlf}"; |  | ||||||
|         $headers .= $this->_crlf; |  | ||||||
| 
 |  | ||||||
|         return $headers; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testNoUpgradeBeforeBody() { |  | ||||||
|         $headers = $this->headerProvider(); |  | ||||||
| 
 |  | ||||||
|         $mockConn = $this->getMock('\Ratchet\ConnectionInterface'); |  | ||||||
|         $mockApp  = $this->getMock('\Ratchet\MessageComponentInterface'); |  | ||||||
| 
 |  | ||||||
|         $server = new HttpServer(new WsServer($mockApp)); |  | ||||||
|         $server->onOpen($mockConn); |  | ||||||
|         $mockApp->expects($this->exactly(0))->method('onOpen'); |  | ||||||
|         $server->onMessage($mockConn, $headers); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testTcpFragmentedUpgrade() { |  | ||||||
|         $headers = $this->headerProvider(); |  | ||||||
|         $body    = base64_decode($this->_body); |  | ||||||
| 
 |  | ||||||
|         $mockConn = $this->getMock('\Ratchet\ConnectionInterface'); |  | ||||||
|         $mockApp  = $this->getMock('\Ratchet\MessageComponentInterface'); |  | ||||||
| 
 |  | ||||||
|         $server = new HttpServer(new WsServer($mockApp)); |  | ||||||
|         $server->onOpen($mockConn); |  | ||||||
|         $server->onMessage($mockConn, $headers); |  | ||||||
| 
 |  | ||||||
|         $mockApp->expects($this->once())->method('onOpen'); |  | ||||||
|         $server->onMessage($mockConn, $body . $this->_crlf . $this->_crlf); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testTcpFragmentedBodyUpgrade() { |  | ||||||
|         $headers = $this->headerProvider(); |  | ||||||
|         $body    = base64_decode($this->_body); |  | ||||||
|         $body1   = substr($body, 0, 4); |  | ||||||
|         $body2   = substr($body, 4); |  | ||||||
| 
 |  | ||||||
|         $mockConn = $this->getMock('\Ratchet\ConnectionInterface'); |  | ||||||
|         $mockApp  = $this->getMock('\Ratchet\MessageComponentInterface'); |  | ||||||
| 
 |  | ||||||
|         $server = new HttpServer(new WsServer($mockApp)); |  | ||||||
|         $server->onOpen($mockConn); |  | ||||||
|         $server->onMessage($mockConn, $headers); |  | ||||||
| 
 |  | ||||||
|         $mockApp->expects($this->once())->method('onOpen'); |  | ||||||
| 
 |  | ||||||
|         $server->onMessage($mockConn, $body1); |  | ||||||
|         $server->onMessage($mockConn, $body2); |  | ||||||
|         $server->onMessage($mockConn, $this->_crlf . $this->_crlf); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,67 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version; |  | ||||||
| use Ratchet\WebSocket\Version\HyBi10; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455\Frame; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @covers Ratchet\WebSocket\Version\Hybi10 |  | ||||||
|  */ |  | ||||||
| class HyBi10Test extends \PHPUnit_Framework_TestCase { |  | ||||||
|     protected $_version; |  | ||||||
| 
 |  | ||||||
|     public function setUp() { |  | ||||||
|         $this->_version = new HyBi10(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Is this useful? |  | ||||||
|      */ |  | ||||||
|     public function testClassImplementsVersionInterface() { |  | ||||||
|         $constraint = $this->isInstanceOf('\\Ratchet\\WebSocket\\Version\\VersionInterface'); |  | ||||||
|         $this->assertThat($this->_version, $constraint); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider HandshakeProvider |  | ||||||
|      */ |  | ||||||
|     public function testKeySigningForHandshake($key, $accept) { |  | ||||||
|         $this->assertEquals($accept, $this->_version->sign($key)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function HandshakeProvider() { |  | ||||||
|         return array( |  | ||||||
|             array('x3JJHMbDL1EzLkh9GBhXDw==', 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=') |  | ||||||
|           , array('dGhlIHNhbXBsZSBub25jZQ==', 's3pPLMBiTxaQ9kYGzzhZRbK+xOo=') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider UnframeMessageProvider |  | ||||||
|      */ |  | ||||||
|     public function testUnframeMessage($message, $framed) { |  | ||||||
| //        $decoded = $this->_version->unframe(base64_decode($framed));
 |  | ||||||
|         $frame = new Frame; |  | ||||||
|         $frame->addBuffer(base64_decode($framed)); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($message, $frame->getPayload()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function UnframeMessageProvider() { |  | ||||||
|         return array( |  | ||||||
|             array('Hello World!',                'gYydAIfa1WXrtvIg0LXvbOP7') |  | ||||||
|           , array('!@#$%^&*()-=_+[]{}\|/.,<>`~', 'gZv+h96r38f9j9vZ+IHWrvOWoayF9oX6gtfRqfKXwOeg') |  | ||||||
|           , array('ಠ_ಠ',                         'gYfnSpu5B/g75gf4Ow==') |  | ||||||
|           , array("The quick brown fox jumps over the lazy dog.  All work and no play makes Chris a dull boy.  I'm trying to get past 128 characters for a unit test here...", 'gf4Amahb14P8M7Kj2S6+4MN7tfHHLLmjzjSvo8IuuvPbe7j1zSn398A+9+/JIa6jzDSwrYh7lu/Ee6Ds2jD34sY/9+3He6fvySL37skwsvCIGL/xwSj34og/ou/Ee7Xs0XX3o+F8uqPcKa7qxjz398d7sObce6fi2y/3sppj9+DAOqXiyy+y8dt7sezae7aj3TW+94gvsvDce7/m2j75rYY=') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testUnframeMatchesPreFraming() { |  | ||||||
|         $string   = 'Hello World!'; |  | ||||||
|         $framed   = $this->_version->newFrame($string)->getContents(); |  | ||||||
| 
 |  | ||||||
|         $frame = new Frame; |  | ||||||
|         $frame->addBuffer($framed); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($string, $frame->getPayload()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,543 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version\RFC6455; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455\Frame; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @todo getMaskingKey, getPayloadStartingByte don't have tests yet |  | ||||||
|  * @todo Could use some clean up in general, I had to rush to fix a bug for a deadline, sorry. |  | ||||||
|  */ |  | ||||||
| class FrameTest extends \PHPUnit_Framework_TestCase { |  | ||||||
|     protected $_firstByteFinText    = '10000001'; |  | ||||||
|     protected $_secondByteMaskedSPL = '11111101'; |  | ||||||
| 
 |  | ||||||
|     protected $_frame; |  | ||||||
| 
 |  | ||||||
|     protected $_packer; |  | ||||||
| 
 |  | ||||||
|     public function setUp() { |  | ||||||
|         $this->_frame = new Frame; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Encode the fake binary string to send over the wire |  | ||||||
|      * @param string of 1's and 0's |  | ||||||
|      * @return string |  | ||||||
|      */ |  | ||||||
|     public static function encode($in) { |  | ||||||
|         if (strlen($in) > 8) { |  | ||||||
|             $out = ''; |  | ||||||
| 
 |  | ||||||
|             while (strlen($in) >= 8) { |  | ||||||
|                 $out .= static::encode(substr($in, 0, 8)); |  | ||||||
|                 $in   = substr($in, 8); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return $out; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return chr(bindec($in)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This is a data provider |  | ||||||
|      * @param string The UTF8 message |  | ||||||
|      * @param string The WebSocket framed message, then base64_encoded |  | ||||||
|      */ |  | ||||||
|     public static function UnframeMessageProvider() { |  | ||||||
|         return array( |  | ||||||
|             array('Hello World!',                'gYydAIfa1WXrtvIg0LXvbOP7') |  | ||||||
|           , array('!@#$%^&*()-=_+[]{}\|/.,<>`~', 'gZv+h96r38f9j9vZ+IHWrvOWoayF9oX6gtfRqfKXwOeg') |  | ||||||
|           , array('ಠ_ಠ',                         'gYfnSpu5B/g75gf4Ow==') |  | ||||||
|           , array("The quick brown fox jumps over the lazy dog.  All work and no play makes Chris a dull boy.  I'm trying to get past 128 characters for a unit test here...", 'gf4Amahb14P8M7Kj2S6+4MN7tfHHLLmjzjSvo8IuuvPbe7j1zSn398A+9+/JIa6jzDSwrYh7lu/Ee6Ds2jD34sY/9+3He6fvySL37skwsvCIGL/xwSj34og/ou/Ee7Xs0XX3o+F8uqPcKa7qxjz398d7sObce6fi2y/3sppj9+DAOqXiyy+y8dt7sezae7aj3TW+94gvsvDce7/m2j75rYY=') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function underflowProvider() { |  | ||||||
|         return array( |  | ||||||
|             array('isFinal', '') |  | ||||||
|           , array('getRsv1', '') |  | ||||||
|           , array('getRsv2', '') |  | ||||||
|           , array('getRsv3', '') |  | ||||||
|           , array('getOpcode', '') |  | ||||||
|           , array('isMasked', '10000001') |  | ||||||
|           , array('getPayloadLength', '10000001') |  | ||||||
|           , array('getPayloadLength', '1000000111111110') |  | ||||||
|           , array('getMaskingKey', '1000000110000111') |  | ||||||
|           , array('getPayload', '100000011000000100011100101010101001100111110100') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider underflowProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::isFinal |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getRsv1 |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getRsv2 |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getRsv3 |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getOpcode |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::isMasked |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getPayloadLength |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getMaskingKey |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getPayload |  | ||||||
|      */ |  | ||||||
|     public function testUnderflowExceptionFromAllTheMethodsMimickingBuffering($method, $bin) { |  | ||||||
|         $this->setExpectedException('\UnderflowException'); |  | ||||||
| 
 |  | ||||||
|         if (!empty($bin)) { |  | ||||||
|             $this->_frame->addBuffer(static::encode($bin)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         call_user_func(array($this->_frame, $method)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * A data provider for testing the first byte of a WebSocket frame |  | ||||||
|      * @param bool Given, is the byte indicate this is the final frame |  | ||||||
|      * @param int Given, what is the expected opcode |  | ||||||
|      * @param string of 0|1 Each character represents a bit in the byte |  | ||||||
|      */ |  | ||||||
|     public static function firstByteProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(false, false, false, true,   8, '00011000') |  | ||||||
|           , array(true,  false, true,  false, 10, '10101010') |  | ||||||
|           , array(false, false, false, false, 15, '00001111') |  | ||||||
|           , array(true,  false, false, false,  1, '10000001') |  | ||||||
|           , array(true,  true,  true,  true,  15, '11111111') |  | ||||||
|           , array(true,  true,  false, false,  7, '11000111') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider firstByteProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::isFinal |  | ||||||
|      */ |  | ||||||
|     public function testFinCodeFromBits($fin, $rsv1, $rsv2, $rsv3, $opcode, $bin) { |  | ||||||
|         $this->_frame->addBuffer(static::encode($bin)); |  | ||||||
|         $this->assertEquals($fin, $this->_frame->isFinal()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider firstByteProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getRsv1 |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getRsv2 |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getRsv3 |  | ||||||
|      */ |  | ||||||
|     public function testGetRsvFromBits($fin, $rsv1, $rsv2, $rsv3, $opcode, $bin) { |  | ||||||
|         $this->_frame->addBuffer(static::encode($bin)); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($rsv1, $this->_frame->getRsv1()); |  | ||||||
|         $this->assertEquals($rsv2, $this->_frame->getRsv2()); |  | ||||||
|         $this->assertEquals($rsv3, $this->_frame->getRsv3()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider firstByteProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getOpcode |  | ||||||
|      */ |  | ||||||
|     public function testOpcodeFromBits($fin, $rsv1, $rsv2, $rsv3, $opcode, $bin) { |  | ||||||
|         $this->_frame->addBuffer(static::encode($bin)); |  | ||||||
|         $this->assertEquals($opcode, $this->_frame->getOpcode()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider UnframeMessageProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::isFinal |  | ||||||
|      */ |  | ||||||
|     public function testFinCodeFromFullMessage($msg, $encoded) { |  | ||||||
|         $this->_frame->addBuffer(base64_decode($encoded)); |  | ||||||
|         $this->assertTrue($this->_frame->isFinal()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider UnframeMessageProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getOpcode |  | ||||||
|      */ |  | ||||||
|     public function testOpcodeFromFullMessage($msg, $encoded) { |  | ||||||
|         $this->_frame->addBuffer(base64_decode($encoded)); |  | ||||||
|         $this->assertEquals(1, $this->_frame->getOpcode()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function payloadLengthDescriptionProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(7,  '01110101') |  | ||||||
|           , array(7,  '01111101') |  | ||||||
|           , array(23, '01111110') |  | ||||||
|           , array(71, '01111111') |  | ||||||
|           , array(7,  '00000000') // Should this throw an exception?  Can a payload be empty?
 |  | ||||||
|           , array(7,  '00000001') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider payloadLengthDescriptionProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::addBuffer |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getFirstPayloadVal |  | ||||||
|      */ |  | ||||||
|     public function testFirstPayloadDesignationValue($bits, $bin) { |  | ||||||
|         $this->_frame->addBuffer(static::encode($this->_firstByteFinText)); |  | ||||||
|         $this->_frame->addBuffer(static::encode($bin)); |  | ||||||
| 
 |  | ||||||
|         $ref = new \ReflectionClass($this->_frame); |  | ||||||
|         $cb  = $ref->getMethod('getFirstPayloadVal'); |  | ||||||
|         $cb->setAccessible(true); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals(bindec($bin), $cb->invoke($this->_frame)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getFirstPayloadVal |  | ||||||
|      */ |  | ||||||
|     public function testFirstPayloadValUnderflow() { |  | ||||||
|         $ref = new \ReflectionClass($this->_frame); |  | ||||||
|         $cb  = $ref->getMethod('getFirstPayloadVal'); |  | ||||||
|         $cb->setAccessible(true); |  | ||||||
| 
 |  | ||||||
|         $this->setExpectedException('UnderflowException'); |  | ||||||
|         $cb->invoke($this->_frame); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider payloadLengthDescriptionProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getNumPayloadBits |  | ||||||
|      */ |  | ||||||
|     public function testDetermineHowManyBitsAreUsedToDescribePayload($expected_bits, $bin) { |  | ||||||
|         $this->_frame->addBuffer(static::encode($this->_firstByteFinText)); |  | ||||||
|         $this->_frame->addBuffer(static::encode($bin)); |  | ||||||
| 
 |  | ||||||
|         $ref = new \ReflectionClass($this->_frame); |  | ||||||
|         $cb  = $ref->getMethod('getNumPayloadBits'); |  | ||||||
|         $cb->setAccessible(true); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($expected_bits, $cb->invoke($this->_frame)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getNumPayloadBits |  | ||||||
|      */ |  | ||||||
|     public function testgetNumPayloadBitsUnderflow() { |  | ||||||
|         $ref = new \ReflectionClass($this->_frame); |  | ||||||
|         $cb  = $ref->getMethod('getNumPayloadBits'); |  | ||||||
|         $cb->setAccessible(true); |  | ||||||
| 
 |  | ||||||
|         $this->setExpectedException('UnderflowException'); |  | ||||||
|         $cb->invoke($this->_frame); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function secondByteProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(true,   1, '10000001') |  | ||||||
|           , array(false,  1, '00000001') |  | ||||||
|           , array(true, 125, $this->_secondByteMaskedSPL) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider secondByteProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::isMasked |  | ||||||
|      */ |  | ||||||
|     public function testIsMaskedReturnsExpectedValue($masked, $payload_length, $bin) { |  | ||||||
|         $this->_frame->addBuffer(static::encode($this->_firstByteFinText)); |  | ||||||
|         $this->_frame->addBuffer(static::encode($bin)); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($masked, $this->_frame->isMasked()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider UnframeMessageProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::isMasked |  | ||||||
|      */ |  | ||||||
|     public function testIsMaskedFromFullMessage($msg, $encoded) { |  | ||||||
|         $this->_frame->addBuffer(base64_decode($encoded)); |  | ||||||
|         $this->assertTrue($this->_frame->isMasked()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider secondByteProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getPayloadLength |  | ||||||
|      */ |  | ||||||
|     public function testGetPayloadLengthWhenOnlyFirstFrameIsUsed($masked, $payload_length, $bin) { |  | ||||||
|         $this->_frame->addBuffer(static::encode($this->_firstByteFinText)); |  | ||||||
|         $this->_frame->addBuffer(static::encode($bin)); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($payload_length, $this->_frame->getPayloadLength()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider UnframeMessageProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getPayloadLength |  | ||||||
|      * @todo Not yet testing when second additional payload length descriptor |  | ||||||
|      */ |  | ||||||
|     public function testGetPayloadLengthFromFullMessage($msg, $encoded) { |  | ||||||
|         $this->_frame->addBuffer(base64_decode($encoded)); |  | ||||||
|         $this->assertEquals(strlen($msg), $this->_frame->getPayloadLength()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function maskingKeyProvider() { |  | ||||||
|         $frame = new Frame; |  | ||||||
| 
 |  | ||||||
|         return array( |  | ||||||
|             array($frame->generateMaskingKey()) |  | ||||||
|           , array($frame->generateMaskingKey()) |  | ||||||
|           , array($frame->generateMaskingKey()) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider maskingKeyProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getMaskingKey |  | ||||||
|      * @todo I I wrote the dataProvider incorrectly, skipping for now |  | ||||||
|      */ |  | ||||||
|     public function testGetMaskingKey($mask) { |  | ||||||
|         $this->_frame->addBuffer(static::encode($this->_firstByteFinText)); |  | ||||||
|         $this->_frame->addBuffer(static::encode($this->_secondByteMaskedSPL)); |  | ||||||
|         $this->_frame->addBuffer($mask); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($mask, $this->_frame->getMaskingKey()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getMaskingKey |  | ||||||
|      */ |  | ||||||
|     public function testGetMaskingKeyOnUnmaskedPayload() { |  | ||||||
|         $frame = new Frame('Hello World!'); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals('', $frame->getMaskingKey()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider UnframeMessageProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getPayload |  | ||||||
|      * @todo Move this test to bottom as it requires all methods of the class |  | ||||||
|      */ |  | ||||||
|     public function testUnframeFullMessage($unframed, $base_framed) { |  | ||||||
|         $this->_frame->addBuffer(base64_decode($base_framed)); |  | ||||||
|         $this->assertEquals($unframed, $this->_frame->getPayload()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function messageFragmentProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(false, '', '', '', '', '') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider UnframeMessageProvider |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getPayload |  | ||||||
|      */ |  | ||||||
|     public function testCheckPiecingTogetherMessage($msg, $encoded) { |  | ||||||
|         $framed = base64_decode($encoded); |  | ||||||
|         for ($i = 0, $len = strlen($framed);$i < $len; $i++) { |  | ||||||
|             $this->_frame->addBuffer(substr($framed, $i, 1)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($msg, $this->_frame->getPayload()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::__construct |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getPayloadLength |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getPayload |  | ||||||
|      */ |  | ||||||
|     public function testLongCreate() { |  | ||||||
|         $len = 65525; |  | ||||||
|         $pl  = $this->generateRandomString($len); |  | ||||||
| 
 |  | ||||||
|         $frame = new Frame($pl, true, Frame::OP_PING); |  | ||||||
| 
 |  | ||||||
|         $this->assertTrue($frame->isFinal()); |  | ||||||
|         $this->assertEquals(Frame::OP_PING, $frame->getOpcode()); |  | ||||||
|         $this->assertFalse($frame->isMasked()); |  | ||||||
|         $this->assertEquals($len, $frame->getPayloadLength()); |  | ||||||
|         $this->assertEquals($pl, $frame->getPayload()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::__construct |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getPayloadLength |  | ||||||
|      */ |  | ||||||
|     public function testReallyLongCreate() { |  | ||||||
|         $len = 65575; |  | ||||||
| 
 |  | ||||||
|         $frame = new Frame($this->generateRandomString($len)); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($len, $frame->getPayloadLength()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::__construct |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::extractOverflow |  | ||||||
|      */ |  | ||||||
|     public function testExtractOverflow() { |  | ||||||
|         $string1 = $this->generateRandomString(); |  | ||||||
|         $frame1  = new Frame($string1); |  | ||||||
| 
 |  | ||||||
|         $string2 = $this->generateRandomString(); |  | ||||||
|         $frame2  = new Frame($string2); |  | ||||||
| 
 |  | ||||||
|         $cat = new Frame; |  | ||||||
|         $cat->addBuffer($frame1->getContents() . $frame2->getContents()); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($frame1->getContents(), $cat->getContents()); |  | ||||||
|         $this->assertEquals($string1, $cat->getPayload()); |  | ||||||
| 
 |  | ||||||
|         $uncat = new Frame; |  | ||||||
|         $uncat->addBuffer($cat->extractOverflow()); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($string1, $cat->getPayload()); |  | ||||||
|         $this->assertEquals($string2, $uncat->getPayload()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::extractOverflow |  | ||||||
|      */ |  | ||||||
|     public function testEmptyExtractOverflow() { |  | ||||||
|         $string = $this->generateRandomString(); |  | ||||||
|         $frame  = new Frame($string); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($string, $frame->getPayload()); |  | ||||||
|         $this->assertEquals('', $frame->extractOverflow()); |  | ||||||
|         $this->assertEquals($string, $frame->getPayload()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getContents |  | ||||||
|      */ |  | ||||||
|     public function testGetContents() { |  | ||||||
|         $msg = 'The quick brown fox jumps over the lazy dog.'; |  | ||||||
| 
 |  | ||||||
|         $frame1 = new Frame($msg); |  | ||||||
|         $frame2 = new Frame($msg); |  | ||||||
|         $frame2->maskPayload(); |  | ||||||
| 
 |  | ||||||
|         $this->assertNotEquals($frame1->getContents(), $frame2->getContents()); |  | ||||||
|         $this->assertEquals(strlen($frame1->getContents()) + 4, strlen($frame2->getContents())); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::maskPayload |  | ||||||
|      */ |  | ||||||
|     public function testMasking() { |  | ||||||
|         $msg   = 'The quick brown fox jumps over the lazy dog.'; |  | ||||||
|         $frame = new Frame($msg); |  | ||||||
|         $frame->maskPayload(); |  | ||||||
| 
 |  | ||||||
|         $this->assertTrue($frame->isMasked()); |  | ||||||
|         $this->assertEquals($msg, $frame->getPayload()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::unMaskPayload |  | ||||||
|      */ |  | ||||||
|     public function testUnMaskPayload() { |  | ||||||
|         $string = $this->generateRandomString(); |  | ||||||
|         $frame  = new Frame($string); |  | ||||||
|         $frame->maskPayload()->unMaskPayload(); |  | ||||||
| 
 |  | ||||||
|         $this->assertFalse($frame->isMasked()); |  | ||||||
|         $this->assertEquals($string, $frame->getPayload()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::generateMaskingKey |  | ||||||
|      */ |  | ||||||
|     public function testGenerateMaskingKey() { |  | ||||||
|         $dupe = false; |  | ||||||
|         $done = array(); |  | ||||||
| 
 |  | ||||||
|         for ($i = 0; $i < 10; $i++) { |  | ||||||
|             $new = $this->_frame->generateMaskingKey(); |  | ||||||
| 
 |  | ||||||
|             if (in_array($new, $done)) { |  | ||||||
|                 $dupe = true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             $done[] = $new; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals(4, strlen($new)); |  | ||||||
|         $this->assertFalse($dupe); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::maskPayload |  | ||||||
|      */ |  | ||||||
|     public function testGivenMaskIsValid() { |  | ||||||
|         $this->setExpectedException('InvalidArgumentException'); |  | ||||||
|         $this->_frame->maskPayload('hello world'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::maskPayload |  | ||||||
|      */ |  | ||||||
|     public function testGivenMaskIsValidAscii() { |  | ||||||
|         if (!extension_loaded('mbstring')) { |  | ||||||
|             return $this->markTestSkipped("mbstring required for this test"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $this->setExpectedException('OutOfBoundsException'); |  | ||||||
|         $this->_frame->maskPayload('x✖'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     protected function generateRandomString($length = 10, $addSpaces = true, $addNumbers = true) { |  | ||||||
|         $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$%&/()=[]{}'; // ยง
 |  | ||||||
| 
 |  | ||||||
|         $useChars = array(); |  | ||||||
|         for($i = 0; $i < $length; $i++) { |  | ||||||
|             $useChars[] = $characters[mt_rand(0, strlen($characters) - 1)]; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if($addSpaces === true) { |  | ||||||
|             array_push($useChars, ' ', ' ', ' ', ' ', ' ', ' '); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if($addNumbers === true) { |  | ||||||
|             array_push($useChars, rand(0, 9), rand(0, 9), rand(0, 9)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         shuffle($useChars); |  | ||||||
| 
 |  | ||||||
|         $randomString = trim(implode('', $useChars)); |  | ||||||
|         $randomString = substr($randomString, 0, $length); |  | ||||||
| 
 |  | ||||||
|         return $randomString; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * There was a frame boundary issue when the first 3 bytes of a frame with a payload greater than |  | ||||||
|      * 126 was added to the frame buffer and then Frame::getPayloadLength was called. It would cause the frame |  | ||||||
|      * to set the payload length to 126 and then not recalculate it once the full length information was available. |  | ||||||
|      * |  | ||||||
|      * This is fixed by setting the defPayLen back to -1 before the underflow exception is thrown. |  | ||||||
|      * |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::getPayloadLength |  | ||||||
|      * @covers Ratchet\WebSocket\Version\RFC6455\Frame::extractOverflow |  | ||||||
|      */ |  | ||||||
|     public function testFrameDeliveredOneByteAtATime() { |  | ||||||
|         $startHeader = "\x01\x7e\x01\x00"; // header for a text frame of 256 - non-final
 |  | ||||||
|         $framePayload = str_repeat("*", 256); |  | ||||||
|         $rawOverflow = "xyz"; |  | ||||||
|         $rawFrame = $startHeader . $framePayload . $rawOverflow; |  | ||||||
| 
 |  | ||||||
|         $frame = new Frame(); |  | ||||||
|         $payloadLen = 256; |  | ||||||
| 
 |  | ||||||
|         for ($i = 0; $i < strlen($rawFrame); $i++) { |  | ||||||
|             $frame->addBuffer($rawFrame[$i]); |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
|                 // payloadLen will
 |  | ||||||
|                 $payloadLen = $frame->getPayloadLength(); |  | ||||||
|             } catch (\UnderflowException $e) { |  | ||||||
|                 if ($i > 2) { // we should get an underflow on 0,1,2
 |  | ||||||
|                     $this->fail("Underflow exception when the frame length should be available"); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if ($payloadLen !== 256) { |  | ||||||
|                 $this->fail("Payload length of " . $payloadLen . " should have been 256."); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // make sure the overflow is good
 |  | ||||||
|         $this->assertEquals($rawOverflow, $frame->extractOverflow()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,170 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version\RFC6455; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @covers Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier |  | ||||||
|  */ |  | ||||||
| class HandshakeVerifierTest extends \PHPUnit_Framework_TestCase { |  | ||||||
|     /** |  | ||||||
|      * @var Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier |  | ||||||
|      */ |  | ||||||
|     protected $_v; |  | ||||||
| 
 |  | ||||||
|     public function setUp() { |  | ||||||
|         $this->_v = new HandshakeVerifier; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function methodProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(true,  'GET') |  | ||||||
|           , array(true,  'get') |  | ||||||
|           , array(true,  'Get') |  | ||||||
|           , array(false, 'POST') |  | ||||||
|           , array(false, 'DELETE') |  | ||||||
|           , array(false, 'PUT') |  | ||||||
|           , array(false, 'PATCH') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider methodProvider |  | ||||||
|      */ |  | ||||||
|     public function testMethodMustBeGet($result, $in) { |  | ||||||
|         $this->assertEquals($result, $this->_v->verifyMethod($in)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function httpVersionProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(true,  1.1) |  | ||||||
|           , array(true,  '1.1') |  | ||||||
|           , array(true,  1.2) |  | ||||||
|           , array(true,  '1.2') |  | ||||||
|           , array(true,  2) |  | ||||||
|           , array(true,  '2') |  | ||||||
|           , array(true,  '2.0') |  | ||||||
|           , array(false, '1.0') |  | ||||||
|           , array(false, 1) |  | ||||||
|           , array(false, '0.9') |  | ||||||
|           , array(false, '') |  | ||||||
|           , array(false, 'hello') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider httpVersionProvider |  | ||||||
|      */ |  | ||||||
|     public function testHttpVersionIsAtLeast1Point1($expected, $in) { |  | ||||||
|         $this->assertEquals($expected, $this->_v->verifyHTTPVersion($in)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function uRIProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(true, '/chat') |  | ||||||
|           , array(true, '/hello/world?key=val') |  | ||||||
|           , array(false, '/chat#bad') |  | ||||||
|           , array(false, 'nope') |  | ||||||
|           , array(false, '/ ಠ_ಠ ') |  | ||||||
|           , array(false, '/✖') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider URIProvider |  | ||||||
|      */ |  | ||||||
|     public function testRequestUri($expected, $in) { |  | ||||||
|         $this->assertEquals($expected, $this->_v->verifyRequestURI($in)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function hostProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(true, 'server.example.com') |  | ||||||
|           , array(false, null) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider HostProvider |  | ||||||
|      */ |  | ||||||
|     public function testVerifyHostIsSet($expected, $in) { |  | ||||||
|         $this->assertEquals($expected, $this->_v->verifyHost($in)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function upgradeProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(true,  'websocket') |  | ||||||
|           , array(true,  'Websocket') |  | ||||||
|           , array(true,  'webSocket') |  | ||||||
|           , array(false, null) |  | ||||||
|           , array(false, '') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider upgradeProvider |  | ||||||
|      */ |  | ||||||
|     public function testVerifyUpgradeIsWebSocket($expected, $val) { |  | ||||||
|         $this->assertEquals($expected, $this->_v->verifyUpgradeRequest($val)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function connectionProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(true,  'Upgrade') |  | ||||||
|           , array(true,  'upgrade') |  | ||||||
|           , array(true,  'keep-alive, Upgrade') |  | ||||||
|           , array(true,  'Upgrade, keep-alive') |  | ||||||
|           , array(true,  'keep-alive, Upgrade, something') |  | ||||||
|           , array(false, '') |  | ||||||
|           , array(false, null) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider connectionProvider |  | ||||||
|      */ |  | ||||||
|     public function testConnectionHeaderVerification($expected, $val) { |  | ||||||
|         $this->assertEquals($expected, $this->_v->verifyConnection($val)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function keyProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(true,  'hkfa1L7uwN6DCo4IS3iWAw==') |  | ||||||
|           , array(true,  '765vVoQpKSGJwPzJIMM2GA==') |  | ||||||
|           , array(true,  'AQIDBAUGBwgJCgsMDQ4PEC==') |  | ||||||
|           , array(true,  'axa2B/Yz2CdpfQAY2Q5P7w==') |  | ||||||
|           , array(false, 0) |  | ||||||
|           , array(false, 'Hello World') |  | ||||||
|           , array(false, '1234567890123456') |  | ||||||
|           , array(false, '123456789012345678901234') |  | ||||||
|           , array(true,  base64_encode('UTF8allthngs+✓')) |  | ||||||
|           , array(true,  'dGhlIHNhbXBsZSBub25jZQ==') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider keyProvider |  | ||||||
|      */ |  | ||||||
|     public function testKeyIsBase64Encoded16BitNonce($expected, $val) { |  | ||||||
|         $this->assertEquals($expected, $this->_v->verifyKey($val)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function versionProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(true,  13) |  | ||||||
|           , array(true,  '13') |  | ||||||
|           , array(false, 12) |  | ||||||
|           , array(false, 14) |  | ||||||
|           , array(false, '14') |  | ||||||
|           , array(false, 'hi') |  | ||||||
|           , array(false, '') |  | ||||||
|           , array(false, null) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider versionProvider |  | ||||||
|      */ |  | ||||||
|     public function testVersionEquals13($expected, $in) { |  | ||||||
|         $this->assertEquals($expected, $this->_v->verifyVersion($in)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,63 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version\RFC6455\Message; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455\Message; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455\Frame; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @covers Ratchet\WebSocket\Version\RFC6455\Message |  | ||||||
|  */ |  | ||||||
| class MessageTest extends \PHPUnit_Framework_TestCase { |  | ||||||
|     protected $message; |  | ||||||
| 
 |  | ||||||
|     public function setUp() { |  | ||||||
|         $this->message = new Message; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testNoFrames() { |  | ||||||
|         $this->assertFalse($this->message->isCoalesced()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testNoFramesOpCode() { |  | ||||||
|         $this->setExpectedException('UnderflowException'); |  | ||||||
|         $this->message->getOpCode(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testFragmentationPayload() { |  | ||||||
|         $a = 'Hello '; |  | ||||||
|         $b = 'World!'; |  | ||||||
| 
 |  | ||||||
|         $f1 = new Frame($a, false); |  | ||||||
|         $f2 = new Frame($b, true, Frame::OP_CONTINUE); |  | ||||||
| 
 |  | ||||||
|         $this->message->addFrame($f1)->addFrame($f2); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals(strlen($a . $b), $this->message->getPayloadLength()); |  | ||||||
|         $this->assertEquals($a . $b, $this->message->getPayload()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testUnbufferedFragment() { |  | ||||||
|         $this->message->addFrame(new Frame('The quick brow', false)); |  | ||||||
| 
 |  | ||||||
|         $this->setExpectedException('UnderflowException'); |  | ||||||
|         $this->message->getPayload(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testGetOpCode() { |  | ||||||
|         $this->message |  | ||||||
|             ->addFrame(new Frame('The quick brow', false, Frame::OP_TEXT)) |  | ||||||
|             ->addFrame(new Frame('n fox jumps ov', false, Frame::OP_CONTINUE)) |  | ||||||
|             ->addFrame(new Frame('er the lazy dog', true, Frame::OP_CONTINUE)) |  | ||||||
|         ; |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals(Frame::OP_TEXT, $this->message->getOpCode()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testGetUnBufferedPayloadLength() { |  | ||||||
|         $this->message |  | ||||||
|             ->addFrame(new Frame('The quick brow', false, Frame::OP_TEXT)) |  | ||||||
|             ->addFrame(new Frame('n fox jumps ov', false, Frame::OP_CONTINUE)) |  | ||||||
|         ; |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals(28, $this->message->getPayloadLength()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,151 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket\Version; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455\Frame; |  | ||||||
| use Guzzle\Http\Message\RequestFactory; |  | ||||||
| use Guzzle\Http\Message\EntityEnclosingRequest; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @covers Ratchet\WebSocket\Version\RFC6455 |  | ||||||
|  */ |  | ||||||
| class RFC6455Test extends \PHPUnit_Framework_TestCase { |  | ||||||
|     protected $version; |  | ||||||
| 
 |  | ||||||
|     public function setUp() { |  | ||||||
|         $this->version = new RFC6455; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider handshakeProvider |  | ||||||
|      */ |  | ||||||
|     public function testKeySigningForHandshake($key, $accept) { |  | ||||||
|         $this->assertEquals($accept, $this->version->sign($key)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function handshakeProvider() { |  | ||||||
|         return array( |  | ||||||
|             array('x3JJHMbDL1EzLkh9GBhXDw==', 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=') |  | ||||||
|           , array('dGhlIHNhbXBsZSBub25jZQ==', 's3pPLMBiTxaQ9kYGzzhZRbK+xOo=') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider UnframeMessageProvider |  | ||||||
|      */ |  | ||||||
|     public function testUnframeMessage($message, $framed) { |  | ||||||
|         $frame = new Frame; |  | ||||||
|         $frame->addBuffer(base64_decode($framed)); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($message, $frame->getPayload()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function UnframeMessageProvider() { |  | ||||||
|         return array( |  | ||||||
|             array('Hello World!',                'gYydAIfa1WXrtvIg0LXvbOP7') |  | ||||||
|           , array('!@#$%^&*()-=_+[]{}\|/.,<>`~', 'gZv+h96r38f9j9vZ+IHWrvOWoayF9oX6gtfRqfKXwOeg') |  | ||||||
|           , array('ಠ_ಠ',                         'gYfnSpu5B/g75gf4Ow==') |  | ||||||
|           , array("The quick brown fox jumps over the lazy dog.  All work and no play makes Chris a dull boy.  I'm trying to get past 128 characters for a unit test here...", 'gf4Amahb14P8M7Kj2S6+4MN7tfHHLLmjzjSvo8IuuvPbe7j1zSn398A+9+/JIa6jzDSwrYh7lu/Ee6Ds2jD34sY/9+3He6fvySL37skwsvCIGL/xwSj34og/ou/Ee7Xs0XX3o+F8uqPcKa7qxjz398d7sObce6fi2y/3sppj9+DAOqXiyy+y8dt7sezae7aj3TW+94gvsvDce7/m2j75rYY=') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testUnframeMatchesPreFraming() { |  | ||||||
|         $string = 'Hello World!'; |  | ||||||
|         $framed = $this->version->newFrame($string)->getContents(); |  | ||||||
| 
 |  | ||||||
|         $frame = new Frame; |  | ||||||
|         $frame->addBuffer($framed); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals($string, $frame->getPayload()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static $good_rest = 'GET /chat HTTP/1.1'; |  | ||||||
| 
 |  | ||||||
|     public static $good_header = array( |  | ||||||
|         'Host'                   => 'server.example.com' |  | ||||||
|       , 'Upgrade'                => 'websocket' |  | ||||||
|       , 'Connection'             => 'Upgrade' |  | ||||||
|       , 'Sec-WebSocket-Key'      => 'dGhlIHNhbXBsZSBub25jZQ==' |  | ||||||
|       , 'Origin'                 => 'http://example.com' |  | ||||||
|       , 'Sec-WebSocket-Protocol' => 'chat, superchat' |  | ||||||
|       , 'Sec-WebSocket-Version'  => 13 |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     public function caseVariantProvider() { |  | ||||||
|         return array( |  | ||||||
|             array('Sec-Websocket-Version') |  | ||||||
|           , array('sec-websocket-version') |  | ||||||
|           , array('SEC-WEBSOCKET-VERSION') |  | ||||||
|           , array('sEC-wEBsOCKET-vERSION') |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider caseVariantProvider |  | ||||||
|      */ |  | ||||||
|     public function testIsProtocolWithCaseInsensitivity($headerName) { |  | ||||||
|         $header = static::$good_header; |  | ||||||
|         unset($header['Sec-WebSocket-Version']); |  | ||||||
|         $header[$headerName] = 13; |  | ||||||
| 
 |  | ||||||
|         $this->assertTrue($this->version->isProtocol(new EntityEnclosingRequest('get', '/', $header))); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * A helper function to try and quickly put together a valid WebSocket HTTP handshake |  | ||||||
|      * but optionally replace a piece to an invalid value for failure testing |  | ||||||
|      */ |  | ||||||
|     public static function getAndSpliceHeader($key = null, $val = null) { |  | ||||||
|         $headers = static::$good_header; |  | ||||||
| 
 |  | ||||||
|         if (null !== $key && null !== $val) { |  | ||||||
|             $headers[$key] = $val; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $header = ''; |  | ||||||
|         foreach ($headers as $key => $val) { |  | ||||||
|             if (!empty($key)) { |  | ||||||
|                 $header .= "{$key}: "; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             $header .= "{$val}\r\n"; |  | ||||||
|         } |  | ||||||
|         $header .= "\r\n"; |  | ||||||
| 
 |  | ||||||
|         return $header; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static function headerHandshakeProvider() { |  | ||||||
|         return array( |  | ||||||
|             array(false, "GET /test HTTP/1.0\r\n" . static::getAndSpliceHeader()) |  | ||||||
|           , array(true,  static::$good_rest . "\r\n" . static::getAndSpliceHeader()) |  | ||||||
|           , array(false, "POST / HTTP:/1.1\r\n" . static::getAndSpliceHeader()) |  | ||||||
|           , array(false, static::$good_rest . "\r\n" . static::getAndSpliceHeader('Upgrade', 'useless')) |  | ||||||
|           , array(false, "GET /ಠ_ಠ HTTP/1.1\r\n" . static::getAndSpliceHeader()) |  | ||||||
|           , array(true, static::$good_rest . "\r\n" . static::getAndSpliceHeader('Connection', 'Herp, Upgrade, Derp')) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider headerHandshakeProvider |  | ||||||
|      */ |  | ||||||
|     public function testVariousHeadersToCheckHandshakeTolerance($pass, $header) { |  | ||||||
|         $request  = RequestFactory::getInstance()->fromMessage($header); |  | ||||||
|         $response = $this->version->handshake($request); |  | ||||||
| 
 |  | ||||||
|         $this->assertInstanceOf('\\Guzzle\\Http\\Message\\Response', $response); |  | ||||||
| 
 |  | ||||||
|         if ($pass) { |  | ||||||
|             $this->assertEquals(101, $response->getStatusCode()); |  | ||||||
|         } else { |  | ||||||
|             $this->assertGreaterThanOrEqual(400, $response->getStatusCode()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testNewMessage() { |  | ||||||
|         $this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Message', $this->version->newMessage()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testNewFrame() { |  | ||||||
|         $this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Frame', $this->version->newFrame()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,91 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket; |  | ||||||
| use Ratchet\WebSocket\VersionManager; |  | ||||||
| use Ratchet\WebSocket\Version\RFC6455; |  | ||||||
| use Ratchet\WebSocket\Version\HyBi10; |  | ||||||
| use Ratchet\WebSocket\Version\Hixie76; |  | ||||||
| use Guzzle\Http\Message\EntityEnclosingRequest; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @covers Ratchet\WebSocket\VersionManager |  | ||||||
|  */ |  | ||||||
| class VersionManagerTest extends \PHPUnit_Framework_TestCase { |  | ||||||
|     protected $vm; |  | ||||||
| 
 |  | ||||||
|     public function setUp() { |  | ||||||
|         $this->vm = new VersionManager; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testFluentInterface() { |  | ||||||
|         $rfc = new RFC6455; |  | ||||||
| 
 |  | ||||||
|         $this->assertSame($this->vm, $this->vm->enableVersion($rfc)); |  | ||||||
|         $this->assertSame($this->vm, $this->vm->disableVersion(13)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testGetVersion() { |  | ||||||
|         $rfc = new RFC6455; |  | ||||||
|         $this->vm->enableVersion($rfc); |  | ||||||
| 
 |  | ||||||
|         $req = new EntityEnclosingRequest('get', '/', array( |  | ||||||
|             'Host' => 'socketo.me' |  | ||||||
|           , 'Sec-WebSocket-Version' => 13 |  | ||||||
|         )); |  | ||||||
| 
 |  | ||||||
|         $this->assertSame($rfc, $this->vm->getVersion($req)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testGetNopeVersionAndDisable() { |  | ||||||
|         $req = new EntityEnclosingRequest('get', '/', array( |  | ||||||
|             'Host' => 'socketo.me' |  | ||||||
|           , 'Sec-WebSocket-Version' => 13 |  | ||||||
|         )); |  | ||||||
| 
 |  | ||||||
|         $this->setExpectedException('InvalidArgumentException'); |  | ||||||
| 
 |  | ||||||
|         $this->vm->getVersion($req); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testYesIsVersionEnabled() { |  | ||||||
|         $this->vm->enableVersion(new RFC6455); |  | ||||||
| 
 |  | ||||||
|         $this->assertTrue($this->vm->isVersionEnabled(new EntityEnclosingRequest('get', '/', array( |  | ||||||
|             'Host' => 'socketo.me' |  | ||||||
|           , 'Sec-WebSocket-Version' => 13 |  | ||||||
|         )))); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testNoIsVersionEnabled() { |  | ||||||
|         $this->assertFalse($this->vm->isVersionEnabled(new EntityEnclosingRequest('get', '/', array( |  | ||||||
|             'Host' => 'socketo.me' |  | ||||||
|           , 'Sec-WebSocket-Version' => 9000 |  | ||||||
|         )))); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testGetSupportedVersionString() { |  | ||||||
|         $v1 = new RFC6455; |  | ||||||
|         $v2 = new HyBi10; |  | ||||||
| 
 |  | ||||||
|         $this->vm->enableVersion($v1); |  | ||||||
|         $this->vm->enableVersion($v2); |  | ||||||
| 
 |  | ||||||
|         $string = $this->vm->getSupportedVersionString(); |  | ||||||
|         $values = explode(',', $string); |  | ||||||
| 
 |  | ||||||
|         $this->assertContains($v1->getVersionNumber(), $values); |  | ||||||
|         $this->assertContains($v2->getVersionNumber(), $values); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testGetSupportedVersionAfterRemoval() { |  | ||||||
|         $this->vm->enableVersion(new RFC6455); |  | ||||||
|         $this->vm->enableVersion(new HyBi10); |  | ||||||
|         $this->vm->enableVersion(new Hixie76); |  | ||||||
| 
 |  | ||||||
|         $this->vm->disableVersion(0); |  | ||||||
| 
 |  | ||||||
|         $values = explode(',', $this->vm->getSupportedVersionString()); |  | ||||||
| 
 |  | ||||||
|         $this->assertEquals(2, count($values)); |  | ||||||
|         $this->assertFalse(array_search(0, $values)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,51 +0,0 @@ | |||||||
| <?php |  | ||||||
| namespace Ratchet\WebSocket; |  | ||||||
| use Ratchet\WebSocket\WsServer; |  | ||||||
| use Ratchet\Mock\Component as MockComponent; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * @covers Ratchet\WebSocket\WsServer |  | ||||||
|  * @covers Ratchet\ComponentInterface |  | ||||||
|  * @covers Ratchet\MessageComponentInterface |  | ||||||
|  */ |  | ||||||
| class WsServerTest extends \PHPUnit_Framework_TestCase { |  | ||||||
|     protected $comp; |  | ||||||
| 
 |  | ||||||
|     protected $serv; |  | ||||||
| 
 |  | ||||||
|     public function setUp() { |  | ||||||
|         $this->comp = new MockComponent; |  | ||||||
|         $this->serv = new WsServer($this->comp); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function testIsSubProtocolSupported() { |  | ||||||
|         $this->comp->protocols = array('hello', 'world'); |  | ||||||
| 
 |  | ||||||
|         $this->assertTrue($this->serv->isSubProtocolSupported('hello')); |  | ||||||
|         $this->assertFalse($this->serv->isSubProtocolSupported('nope')); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function protocolProvider() { |  | ||||||
|         return array( |  | ||||||
|             array('hello', array('hello', 'world'), array('hello', 'world')) |  | ||||||
|           , array('', array('hello', 'world'), array('wamp')) |  | ||||||
|           , array('', array(), null) |  | ||||||
|           , array('wamp', array('hello', 'wamp', 'world'), array('herp', 'derp', 'wamp')) |  | ||||||
|           , array('wamp', array('wamp'), array('wamp')) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @dataProvider protocolProvider |  | ||||||
|      */ |  | ||||||
|     public function testGetSubProtocolString($expected, $supported, $requested) { |  | ||||||
|         $this->comp->protocols = $supported; |  | ||||||
|         $req = (null === $requested ? $requested : new \ArrayIterator($requested)); |  | ||||||
| 
 |  | ||||||
|         $class  = new \ReflectionClass('Ratchet\\WebSocket\\WsServer'); |  | ||||||
|         $method = $class->getMethod('getSubProtocolString'); |  | ||||||
|         $method->setAccessible(true); |  | ||||||
| 
 |  | ||||||
|         $this->assertSame($expected, $method->invokeArgs($this->serv, array($req))); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 samizdam
						samizdam