fixed
This commit is contained in:
@@ -13,6 +13,7 @@ use app\api\controller\BaseWssApi;
|
||||
use GatewayWorker\Lib\Gateway;
|
||||
use think\Db;
|
||||
use wanghua\general_utility_tools_php\gpt\chat\ChatGPT;
|
||||
use wanghua\general_utility_tools_php\Mmodel;
|
||||
use wanghua\general_utility_tools_php\tool\Tools;
|
||||
|
||||
/**
|
||||
@@ -22,25 +23,73 @@ use wanghua\general_utility_tools_php\tool\Tools;
|
||||
*/
|
||||
class TychatLogic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* desc:听译-保存听译聊天记录
|
||||
*
|
||||
* Tyuser/saveChatHistory
|
||||
*
|
||||
* author:wh
|
||||
*/
|
||||
function saveChatHistory($client_id,$data){
|
||||
return Mmodel::catchJson(function ()use ($client_id,$data){
|
||||
$action = 'Tyuser/saveChatHistory';
|
||||
if(empty($data['items'])){
|
||||
$json = Tools::wss_json($action, 'items参数错误');
|
||||
return Gateway::sendToClient($client_id, $json);
|
||||
}
|
||||
$obj = new TychatLogic();
|
||||
if(empty($data['items']['chat_msg'])){
|
||||
$json = Tools::wss_json($action, 'chat_msg参数错误');
|
||||
return Gateway::sendToClient($client_id, $json);
|
||||
}
|
||||
//if(empty($data['items']['username'])){
|
||||
// $json = Tools::wss_json($action, 'username参数错误');
|
||||
// return Gateway::sendToClient($client_id, $json);
|
||||
//}
|
||||
$chat_msg = $data['items']['chat_msg'];
|
||||
|
||||
$user = Db::table('fa_ty_users')
|
||||
->where('clientid',$client_id)
|
||||
->find();
|
||||
$username = $user['username'];
|
||||
//保存记录
|
||||
$obj->saveHistory($username,$chat_msg);
|
||||
|
||||
//将消息转发到绑定关系的对方
|
||||
//判断用户类型,医生还是病人
|
||||
if($user['type']=='doc'){
|
||||
//消息发给病人
|
||||
$client_id = Db::table('fa_ty_users')
|
||||
->where('doctor',$user['username'])
|
||||
->value('clientid');
|
||||
if(empty($client_id)){
|
||||
$json = Tools::wss_json($action, '病人未上线');
|
||||
return Gateway::sendToClient($client_id, $json);
|
||||
}
|
||||
$json = Tools::wss_json($action, '成功',['chat_msg'=>$chat_msg]);
|
||||
return Gateway::sendToClient($client_id, $json);
|
||||
}else{
|
||||
//消息发给医生
|
||||
$client_id = Db::table('fa_ty_users')
|
||||
->where('username',$user['doctor'])
|
||||
->value('clientid');
|
||||
if(empty($client_id)){
|
||||
$json = Tools::wss_json($action, '医生未上线');
|
||||
return Gateway::sendToClient($client_id, $json);
|
||||
}
|
||||
$json = Tools::wss_json($action, '成功',['chat_msg'=>$chat_msg]);
|
||||
return Gateway::sendToClient($client_id, $json);
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* desc:保存听译聊天记录
|
||||
*
|
||||
* 注:
|
||||
* 在websocket 的onMessage中send消息,消息格式如下:
|
||||
*
|
||||
* json参数:
|
||||
* [
|
||||
'action'=>'TychatLogic/saveChatHistory',
|
||||
'items'=>[
|
||||
'content'=>'聊天内容',
|
||||
'username'=>'用户名',//医生说话就传医生的username,病人说话就传病人的username
|
||||
]
|
||||
];
|
||||
*/
|
||||
function saveChatHistory($chat_content){
|
||||
private function saveHistory($username,$chat_content){
|
||||
$data = [
|
||||
'chat_msg'=>$chat_content,
|
||||
'username'=>$username,
|
||||
];
|
||||
Db::table('fa_ty_chathistory')->insert($data);
|
||||
|
||||
|
||||
107
digital_doctor/application/api/logic/TyuserLogic.php
Normal file
107
digital_doctor/application/api/logic/TyuserLogic.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/*
|
||||
* description:
|
||||
* author:wh
|
||||
* email:
|
||||
* createTime:{2024/5/23} {18:01}
|
||||
*/
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
|
||||
use app\api\controller\BaseWssApi;
|
||||
use app\common\model\TabConf;
|
||||
use GatewayWorker\Lib\Gateway;
|
||||
use think\Db;
|
||||
use wanghua\general_utility_tools_php\Mmodel;
|
||||
use wanghua\general_utility_tools_php\tool\Tools;
|
||||
|
||||
class TyuserLogic extends BaseLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* desc:客户端离线
|
||||
* author:wh
|
||||
*/
|
||||
//function offline($client_id){
|
||||
// Mmodel::catchTrans(function () use ($client_id){
|
||||
// Tools::log_to_write_txt(['服务端收到客户端离线消息:client_id:' . $client_id]);
|
||||
//
|
||||
// $user = Db::table('fa_ty_users')
|
||||
// ->where('clientid', $client_id)
|
||||
// ->find();
|
||||
// if(empty($user)){
|
||||
// Tools::log_to_write_txt(['error:客户端离线,用户不存在:client_id:' . $client_id]);
|
||||
// return ;
|
||||
// }
|
||||
//
|
||||
// Tools::log_to_write_txt(['写入离线记录:openid:' . $user['openid']]);
|
||||
// //写入离线记录
|
||||
// Db::table(TabConf::$fa_user_offline_record)
|
||||
// ->insert([
|
||||
// 'openid'=>$user['openid'],
|
||||
// ]);
|
||||
//
|
||||
// Tools::log_to_write_txt(['设置离线时间:clientid:' . $client_id]);
|
||||
// Db::table('fa_ty_users')
|
||||
// ->where('clientid', $client_id)
|
||||
// ->data([
|
||||
// 'clientid'=>'',
|
||||
// 'offline_time'=>Tools::get_now_date(),//离线时间
|
||||
// ])
|
||||
// ->update();
|
||||
//
|
||||
// Tools::log_to_write_txt(['好友列表设置为离线:openid:' . $user['openid']]);
|
||||
// //好友列表设置为离线
|
||||
// Db::table(TabConf::$fa_gamefriend)
|
||||
// ->data([
|
||||
// 'offline_time'=>Tools::get_now_date(),//离线时间
|
||||
// ])
|
||||
// ->where('friend_openid',$user['openid'])
|
||||
// ->update();
|
||||
//
|
||||
// Tools::log_to_write_txt(['返回离线消息']);
|
||||
//
|
||||
// //在$client_id无效的情况下可能会抛出异常
|
||||
// //$json = BaseWssApi::wss_json('ok', '用户已离线');
|
||||
// //Gateway::sendToClient($client_id, json_encode($json, JSON_UNESCAPED_UNICODE));
|
||||
// });
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* desc:听译-绑定医患关系,医患客户端初始化后绑定关系
|
||||
*请求参数:
|
||||
* json消息格式:[
|
||||
'action'=>'Tyuser/bindRelation',
|
||||
'items'=>[
|
||||
'username'=>'13333322323',
|
||||
]
|
||||
]
|
||||
* author:wh
|
||||
*/
|
||||
function bindRelation($clientid,$data){
|
||||
$action = 'Tyuser/bindRelation';
|
||||
if(empty($data['items'])){
|
||||
$json = Tools::wss_json($action,'items参数错误');
|
||||
return Gateway::sendToClient($clientid, $json);
|
||||
}
|
||||
$items = $data['items'];
|
||||
if(empty($items['username'])){
|
||||
$json = Tools::wss_json($action,'username参数错误');
|
||||
return Gateway::sendToClient($clientid, $json);
|
||||
}
|
||||
Db::table('fa_ty_users')
|
||||
->data([
|
||||
'clientid'=>$clientid,
|
||||
])
|
||||
->where('username',$items['username'])
|
||||
->update();
|
||||
|
||||
//$json = Tools::wss_json('ok','username参数错误');
|
||||
//return Gateway::sendToClient($clientid, $json);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* description:
|
||||
* author:wh
|
||||
* email:
|
||||
* createTime:{2024/5/23} {18:01}
|
||||
*/
|
||||
|
||||
namespace app\api\logic;
|
||||
|
||||
|
||||
use app\api\controller\BaseWssApi;
|
||||
use app\common\model\TabConf;
|
||||
use GatewayWorker\Lib\Gateway;
|
||||
use think\Db;
|
||||
use wanghua\general_utility_tools_php\Mmodel;
|
||||
use wanghua\general_utility_tools_php\tool\Tools;
|
||||
|
||||
class UserLogic extends BaseLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* desc:客户端离线
|
||||
* author:wh
|
||||
*/
|
||||
function offline($client_id){
|
||||
Mmodel::catchTrans(function () use ($client_id){
|
||||
Tools::log_to_write_txt(['服务端收到客户端离线消息:client_id:' . $client_id]);
|
||||
|
||||
$user = Db::table('fa_ty_users')
|
||||
->where('clientid', $client_id)
|
||||
->find();
|
||||
if(empty($user)){
|
||||
Tools::log_to_write_txt(['error:客户端离线,用户不存在:client_id:' . $client_id]);
|
||||
return ;
|
||||
}
|
||||
|
||||
Tools::log_to_write_txt(['写入离线记录:openid:' . $user['openid']]);
|
||||
//写入离线记录
|
||||
Db::table(TabConf::$fa_user_offline_record)
|
||||
->insert([
|
||||
'openid'=>$user['openid'],
|
||||
]);
|
||||
|
||||
Tools::log_to_write_txt(['设置离线时间:clientid:' . $client_id]);
|
||||
Db::table('fa_ty_users')
|
||||
->where('clientid', $client_id)
|
||||
->data([
|
||||
'clientid'=>'',
|
||||
'offline_time'=>Tools::get_now_date(),//离线时间
|
||||
])
|
||||
->update();
|
||||
|
||||
Tools::log_to_write_txt(['好友列表设置为离线:openid:' . $user['openid']]);
|
||||
//好友列表设置为离线
|
||||
Db::table(TabConf::$fa_gamefriend)
|
||||
->data([
|
||||
'offline_time'=>Tools::get_now_date(),//离线时间
|
||||
])
|
||||
->where('friend_openid',$user['openid'])
|
||||
->update();
|
||||
|
||||
Tools::log_to_write_txt(['返回离线消息']);
|
||||
|
||||
//在$client_id无效的情况下可能会抛出异常
|
||||
//$json = BaseWssApi::json_wss('ok', '用户已离线');
|
||||
//Gateway::sendToClient($client_id, json_encode($json, JSON_UNESCAPED_UNICODE));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* desc:听译-保存听译聊天记录
|
||||
*
|
||||
* User/saveChatHistory
|
||||
*
|
||||
* author:wh
|
||||
*/
|
||||
function saveChatHistory($client_id,$data){
|
||||
return Mmodel::catchJson(function ()use ($client_id){
|
||||
$obj = new TychatLogic();
|
||||
$chat_content = input('chat_content');
|
||||
if(empty($chat_content)){
|
||||
$json = Tools::json_wss('error', '参数错误');
|
||||
Gateway::sendToClient($client_id, $json);
|
||||
|
||||
}
|
||||
$res = $obj->saveChatHistory($chat_content);
|
||||
return Tools::set_ok('ok',$res);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* desc:绑定医患关系,医患客户端初始化后绑定关系
|
||||
*请求参数:
|
||||
* json消息格式:[
|
||||
'action'=>'user/bindRelation',
|
||||
'items'=>[
|
||||
'username'=>'13333322323',
|
||||
]
|
||||
]
|
||||
* author:wh
|
||||
*/
|
||||
function bindRelation($clientid,$data){
|
||||
if(empty($data['items'])){
|
||||
$json = Tools::json_wss('error','items参数错误');
|
||||
return Gateway::sendToClient($clientid, $json);
|
||||
}
|
||||
$items = $data['items'];
|
||||
if(empty($items['username'])){
|
||||
$json = Tools::json_wss('error','username参数错误');
|
||||
return Gateway::sendToClient($clientid, $json);
|
||||
}
|
||||
Db::table('fa_ty_users')
|
||||
->data([
|
||||
'clientid'=>$clientid,
|
||||
])
|
||||
->where('username',$items['username'])
|
||||
->update();
|
||||
|
||||
//$json = Tools::json_wss('ok','username参数错误');
|
||||
//return Gateway::sendToClient($clientid, $json);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ use app\api\controller\BaseWssApi;
|
||||
use app\api\logic\AudioRevertLogic;
|
||||
use app\api\logic\BaseLogic;
|
||||
use app\api\logic\PartnerOfflineLogic;
|
||||
use app\api\logic\UserLogic;
|
||||
use app\api\logic\WssMessageLogic;
|
||||
use GatewayWorker\Lib\Gateway;
|
||||
use IFlytek\Xfyun\Speech\LfasrClient;
|
||||
|
||||
@@ -24,13 +24,7 @@
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"doing/phpqrcode": "dev-master",
|
||||
"iflytekop/xfyun-sdk": "^2.0",
|
||||
"react/socket": "^1.15",
|
||||
"evenement/evenement": "^3.0",
|
||||
"cboden/ratchet": "^0.4.4",
|
||||
"react/event-loop": "^1.5",
|
||||
"react/http": "^1.10",
|
||||
"react/promise": "^3.2"
|
||||
"iflytekop/xfyun-sdk": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -49,7 +43,7 @@
|
||||
"repositories": {
|
||||
"packagist": {
|
||||
"type": "composer",
|
||||
"url": "https://mirrors.huaweicloud.com/repository/php/"
|
||||
"url": "https://mirrors.aliyun.com/composer/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
name: "CI"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
schedule:
|
||||
- cron: "42 3 * * *"
|
||||
|
||||
jobs:
|
||||
phpunit:
|
||||
name: "PHPUnit"
|
||||
runs-on: "ubuntu-20.04"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "5.4"
|
||||
- "5.5"
|
||||
- "5.6"
|
||||
- "7.0"
|
||||
- "7.1"
|
||||
- "7.2"
|
||||
- "7.3"
|
||||
- "7.4"
|
||||
dependencies:
|
||||
- "highest"
|
||||
include:
|
||||
- dependencies: "lowest"
|
||||
php-version: "5.4"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
coverage: "none"
|
||||
ini-values: "zend.assertions=1"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "${{ matrix.dependencies }}"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit"
|
||||
@@ -1,5 +0,0 @@
|
||||
phpunit.xml
|
||||
reports
|
||||
sandbox
|
||||
vendor
|
||||
composer.lock
|
||||
150
digital_doctor/vendor/cboden/ratchet/CHANGELOG.md
vendored
150
digital_doctor/vendor/cboden/ratchet/CHANGELOG.md
vendored
@@ -1,150 +0,0 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
### Legend
|
||||
|
||||
* "BC": Backwards compatibility break (from public component APIs)
|
||||
* "BF": Bug fix
|
||||
|
||||
---
|
||||
|
||||
* 0.4.4 (2021-12-11)
|
||||
* Correct and update dependencies for forward compatibility
|
||||
* Added context for React Socket server to App
|
||||
* Use non-deprecated Guzzle API calls
|
||||
|
||||
* 0.4.3 (2020-06-04)
|
||||
* BF: Fixed interface acceptable regression in `App`
|
||||
* Update RFC6455 library with latest fixes
|
||||
|
||||
* 0.4.2 (2020-01-27)
|
||||
* Support Symfony 5
|
||||
* BF: Use phpunit from vendor directory
|
||||
* Allow disabling of xdebug warning by defining `RATCHET_DISABLE_XDEBUG_WARN`
|
||||
* Stop using `LoopInterface::tick()` for testing
|
||||
|
||||
* 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)
|
||||
* BF: Keep host and scheme in HTTP request object attatched to connection
|
||||
* BF: Return correct HTTP response (405) when non-GET request made
|
||||
|
||||
* 0.3.5 (2016-05-25)
|
||||
* BF: Unmask responding close frame
|
||||
* Added write handler for PHP session serializer
|
||||
|
||||
* 0.3.4 (2015-12-23)
|
||||
* BF: Edge case where version check wasn't run on message coalesce
|
||||
* BF: Session didn't start when using pdo_sqlite
|
||||
* BF: WAMP currie prefix check when using '#'
|
||||
* Compatibility with Symfony 3
|
||||
|
||||
* 0.3.3 (2015-05-26)
|
||||
* BF: Framing bug on large messages upon TCP fragmentation
|
||||
* BF: Symfony Router query parameter defaults applied to Request
|
||||
* BF: WAMP CURIE on all URIs
|
||||
* OriginCheck rules applied to FlashPolicy
|
||||
* Switched from PSR-0 to PSR-4
|
||||
|
||||
* 0.3.2 (2014-06-08)
|
||||
* BF: No messages after closing handshake (fixed rare race condition causing 100% CPU)
|
||||
* BF: Fixed accidental BC break from v0.3.1
|
||||
* Added autoDelete parameter to Topic to destroy when empty of connections
|
||||
* Exposed React Socket on IoServer (allowing FlashPolicy shutdown in App)
|
||||
* Normalized Exceptions in WAMP
|
||||
|
||||
* 0.3.1 (2014-05-26)
|
||||
* Added query parameter support to Router, set in HTTP request (ws://server?hello=world)
|
||||
* HHVM compatibility
|
||||
* BF: React/0.4 support; CPU starvation bug fixes
|
||||
* BF: Allow App::route to ignore Host header
|
||||
* Added expected filters to WAMP Topic broadcast method
|
||||
* Resource cleanup in WAMP TopicManager
|
||||
|
||||
* 0.3.0 (2013-10-14)
|
||||
* 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
|
||||
* 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
|
||||
* BF: Single sub-protocol selection to conform with RFC6455
|
||||
* BF: Sanity checks on WAMP protocol to prevent errors
|
||||
|
||||
* 0.2.8 (2013-09-19)
|
||||
* React 0.3 support
|
||||
|
||||
* 0.2.7 (2013-06-09)
|
||||
* BF: Sub-protocol negotation with Guzzle 3.6
|
||||
|
||||
* 0.2.6 (2013-06-01)
|
||||
* Guzzle 3.6 support
|
||||
|
||||
* 0.2.5 (2013-04-01)
|
||||
* Fixed Hixie-76 handshake bug
|
||||
|
||||
* 0.2.4 (2013-03-09)
|
||||
* Support for Symfony 2.2 and Guzzle 2.3
|
||||
* Minor bug fixes when handling errors
|
||||
|
||||
* 0.2.3 (2012-11-21)
|
||||
* Bumped dep: Guzzle to v3, React to v0.2.4
|
||||
* More tests
|
||||
|
||||
* 0.2.2 (2012-10-20)
|
||||
* Bumped deps to use React v0.2
|
||||
|
||||
* 0.2.1 (2012-10-13)
|
||||
* BF: No more UTF-8 warnings in browsers (no longer sending empty sub-protocol string)
|
||||
* Documentation corrections
|
||||
* Using new composer structure
|
||||
|
||||
* 0.2 (2012-09-07)
|
||||
* Ratchet passes every non-binary-frame test from the Autobahn Testsuite
|
||||
* Major performance improvements
|
||||
* BC: Renamed "WampServer" to "ServerProtocol"
|
||||
* BC: New "WampServer" component passes Topic container objects of subscribed Connections
|
||||
* Option to turn off UTF-8 checks in order to increase performance
|
||||
* Switched dependency guzzle/guzzle to guzzle/http (no API changes)
|
||||
* mbstring no longer required
|
||||
|
||||
* 0.1.5 (2012-07-12)
|
||||
* BF: Error where service wouldn't run on PHP <= 5.3.8
|
||||
* Dependency library updates
|
||||
|
||||
* 0.1.4 (2012-06-17)
|
||||
* Fixed dozens of failing AB tests
|
||||
* BF: Proper socket buffer handling
|
||||
|
||||
* 0.1.3 (2012-06-15)
|
||||
* Major refactor inside WebSocket protocol handling, more loosley coupled
|
||||
* BF: Proper error handling on failed WebSocket connections
|
||||
* BF: Handle TCP message concatenation
|
||||
* Inclusion of the AutobahnTestSuite checking WebSocket protocol compliance
|
||||
* mb_string now a requirement
|
||||
|
||||
* 0.1.2 (2012-05-19)
|
||||
* BC/BF: Updated WAMP API to coincide with the official spec
|
||||
* Tweaks to improve running as a long lived process
|
||||
|
||||
* 0.1.1 (2012-05-14)
|
||||
* Separated interfaces allowing WebSockets to support multiple sub protocols
|
||||
* BF: remoteAddress variable on connections returns proper value
|
||||
|
||||
* 0.1 (2012-05-11)
|
||||
* First release with components: IoServer, WsServer, SessionProvider, WampServer, FlashPolicy, IpBlackList
|
||||
* I/O now handled by React, making Ratchet fully asynchronous
|
||||
19
digital_doctor/vendor/cboden/ratchet/LICENSE
vendored
19
digital_doctor/vendor/cboden/ratchet/LICENSE
vendored
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2011 Chris Boden
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
42
digital_doctor/vendor/cboden/ratchet/Makefile
vendored
42
digital_doctor/vendor/cboden/ratchet/Makefile
vendored
@@ -1,42 +0,0 @@
|
||||
# This file is intended to ease the author's development and testing process
|
||||
# Users do not need to use `make`; Ratchet does not need to be compiled
|
||||
|
||||
test:
|
||||
vendor/bin/phpunit
|
||||
|
||||
cover:
|
||||
vendor/bin/phpunit --coverage-text --coverage-html=reports/coverage
|
||||
|
||||
abtests:
|
||||
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 8004 LibEv &
|
||||
wstest -m testeeserver -w ws://localhost:8000 &
|
||||
sleep 1
|
||||
wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-all.json
|
||||
killall php wstest
|
||||
|
||||
abtest:
|
||||
ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8000 StreamSelect &
|
||||
sleep 1
|
||||
wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-quick.json
|
||||
killall php
|
||||
|
||||
profile:
|
||||
php -d 'xdebug.profiler_enable=1' tests/autobahn/bin/fuzzingserver.php 8000 LibEvent &
|
||||
sleep 1
|
||||
wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-profile.json
|
||||
killall php
|
||||
|
||||
apidocs:
|
||||
apigen --title Ratchet -d reports/api \
|
||||
-s src/ \
|
||||
-s vendor/ratchet/rfc6455/src \
|
||||
-s vendor/react/event-loop/src \
|
||||
-s vendor/react/socket/src \
|
||||
-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 \
|
||||
87
digital_doctor/vendor/cboden/ratchet/README.md
vendored
87
digital_doctor/vendor/cboden/ratchet/README.md
vendored
@@ -1,87 +0,0 @@
|
||||
# Ratchet
|
||||
|
||||
[![GitHub Actions][GA Image]][GA Link]
|
||||
[](http://socketo.me/reports/ab/index.html)
|
||||
[](https://packagist.org/packages/cboden/ratchet)
|
||||
|
||||
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.
|
||||
|
||||
## Requirements
|
||||
|
||||
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.
|
||||
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#server_configuration).
|
||||
|
||||
### Documentation
|
||||
|
||||
User and API documentation is available on Ratchet's website: http://socketo.me
|
||||
|
||||
See https://github.com/cboden/Ratchet-examples for some out-of-the-box working demos using Ratchet.
|
||||
|
||||
Need help? Have a question? Want to provide feedback? Write a message on the [Google Groups Mailing List](https://groups.google.com/forum/#!forum/ratchet-php).
|
||||
|
||||
---
|
||||
|
||||
### A quick example
|
||||
|
||||
```php
|
||||
<?php
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
// Make sure composer dependencies have been installed
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* chat.php
|
||||
* Send any incoming messages to all connected clients (except sender)
|
||||
*/
|
||||
class MyChat implements MessageComponentInterface {
|
||||
protected $clients;
|
||||
|
||||
public function __construct() {
|
||||
$this->clients = new \SplObjectStorage;
|
||||
}
|
||||
|
||||
public function onOpen(ConnectionInterface $conn) {
|
||||
$this->clients->attach($conn);
|
||||
}
|
||||
|
||||
public function onMessage(ConnectionInterface $from, $msg) {
|
||||
foreach ($this->clients as $client) {
|
||||
if ($from != $client) {
|
||||
$client->send($msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
$this->clients->detach($conn);
|
||||
}
|
||||
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
$conn->close();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the server application through the WebSocket protocol on port 8080
|
||||
$app = new Ratchet\App('localhost', 8080);
|
||||
$app->route('/chat', new MyChat, array('*'));
|
||||
$app->route('/echo', new Ratchet\Server\EchoServer, array('*'));
|
||||
$app->run();
|
||||
```
|
||||
|
||||
$ php chat.php
|
||||
|
||||
```javascript
|
||||
// Then some JavaScript in the browser:
|
||||
var conn = new WebSocket('ws://localhost:8080/echo');
|
||||
conn.onmessage = function(e) { console.log(e.data); };
|
||||
conn.onopen = function(e) { conn.send('Hello Me!'); };
|
||||
```
|
||||
|
||||
[GA Image]: https://github.com/ratchetphp/Ratchet/workflows/CI/badge.svg
|
||||
|
||||
[GA Link]: https://github.com/ratchetphp/Ratchet/actions?query=workflow%3A%22CI%22+branch%3Amaster
|
||||
@@ -1,8 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report security issues to:
|
||||
|
||||
* Chris Boden [cboden@gmail.com](cboden@gmail.com)
|
||||
* Matt Bonneau [matt@bonneau.net](matt@bonneau.net)
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"name": "cboden/ratchet"
|
||||
, "type": "library"
|
||||
, "description": "PHP WebSocket library"
|
||||
, "keywords": ["WebSockets", "Server", "Ratchet", "Sockets", "WebSocket"]
|
||||
, "homepage": "http://socketo.me"
|
||||
, "license": "MIT"
|
||||
, "authors": [
|
||||
{
|
||||
"name": "Chris Boden"
|
||||
, "email": "cboden@gmail.com"
|
||||
, "role": "Developer"
|
||||
}
|
||||
, {
|
||||
"name": "Matt Bonneau"
|
||||
, "role": "Developer"
|
||||
}
|
||||
]
|
||||
, "support": {
|
||||
"issues": "https://github.com/ratchetphp/Ratchet/issues"
|
||||
, "chat": "https://gitter.im/reactphp/reactphp"
|
||||
}
|
||||
, "autoload": {
|
||||
"psr-4": {
|
||||
"Ratchet\\": "src/Ratchet"
|
||||
}
|
||||
}
|
||||
, "require": {
|
||||
"php": ">=5.4.2"
|
||||
, "ratchet/rfc6455": "^0.3.1"
|
||||
, "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5"
|
||||
, "react/event-loop": ">=0.4"
|
||||
, "guzzlehttp/psr7": "^1.7|^2.0"
|
||||
, "symfony/http-foundation": "^2.6|^3.0|^4.0|^5.0|^6.0"
|
||||
, "symfony/routing": "^2.6|^3.0|^4.0|^5.0|^6.0"
|
||||
}
|
||||
, "require-dev": {
|
||||
"phpunit/phpunit": "~4.8"
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
forceCoversAnnotation="true"
|
||||
mapTestClassNameToCoveredClassName="true"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
colors="true"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
syntaxCheck="false"
|
||||
stopOnError="false"
|
||||
>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="unit">
|
||||
<directory>./tests/unit/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="integration">
|
||||
<directory>./tests/integration/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet;
|
||||
|
||||
/**
|
||||
* Wraps ConnectionInterface objects via the decorator pattern but allows
|
||||
* parameters to bubble through with magic methods
|
||||
* @todo It sure would be nice if I could make most of this a trait...
|
||||
*/
|
||||
abstract class AbstractConnectionDecorator implements ConnectionInterface {
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $wrappedConn;
|
||||
|
||||
public function __construct(ConnectionInterface $conn) {
|
||||
$this->wrappedConn = $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
protected function getConnection() {
|
||||
return $this->wrappedConn;
|
||||
}
|
||||
|
||||
public function __set($name, $value) {
|
||||
$this->wrappedConn->$name = $value;
|
||||
}
|
||||
|
||||
public function __get($name) {
|
||||
return $this->wrappedConn->$name;
|
||||
}
|
||||
|
||||
public function __isset($name) {
|
||||
return isset($this->wrappedConn->$name);
|
||||
}
|
||||
|
||||
public function __unset($name) {
|
||||
unset($this->wrappedConn->$name);
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\EventLoop\Factory as LoopFactory;
|
||||
use React\Socket\Server as Reactor;
|
||||
use React\Socket\SecureServer as SecureReactor;
|
||||
use Ratchet\Http\HttpServerInterface;
|
||||
use Ratchet\Http\OriginCheck;
|
||||
use Ratchet\Wamp\WampServerInterface;
|
||||
use Ratchet\Server\IoServer;
|
||||
use Ratchet\Server\FlashPolicy;
|
||||
use Ratchet\Http\HttpServer;
|
||||
use Ratchet\Http\Router;
|
||||
use Ratchet\WebSocket\MessageComponentInterface as WsMessageComponentInterface;
|
||||
use Ratchet\WebSocket\WsServer;
|
||||
use Ratchet\Wamp\WampServer;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcher;
|
||||
|
||||
/**
|
||||
* An opinionated facade class to quickly and easily create a WebSocket server.
|
||||
* A few configuration assumptions are made and some best-practice security conventions are applied by default.
|
||||
*/
|
||||
class App {
|
||||
/**
|
||||
* @var \Symfony\Component\Routing\RouteCollection
|
||||
*/
|
||||
public $routes;
|
||||
|
||||
/**
|
||||
* @var \Ratchet\Server\IoServer
|
||||
*/
|
||||
public $flashServer;
|
||||
|
||||
/**
|
||||
* @var \Ratchet\Server\IoServer
|
||||
*/
|
||||
protected $_server;
|
||||
|
||||
/**
|
||||
* The Host passed in construct used for same origin policy
|
||||
* @var string
|
||||
*/
|
||||
protected $httpHost;
|
||||
|
||||
/***
|
||||
* The port the socket is listening
|
||||
* @var int
|
||||
*/
|
||||
protected $port;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $_routeCounter = 0;
|
||||
|
||||
/**
|
||||
* @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 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 array $context
|
||||
*/
|
||||
public function __construct($httpHost = 'localhost', $port = 8080, $address = '127.0.0.1', LoopInterface $loop = null, $context = array()) {
|
||||
if (extension_loaded('xdebug') && getenv('RATCHET_DISABLE_XDEBUG_WARN') === false) {
|
||||
trigger_error('XDebug extension detected. Remember to disable this if performance testing or going live!', E_USER_WARNING);
|
||||
}
|
||||
|
||||
if (null === $loop) {
|
||||
$loop = LoopFactory::create();
|
||||
}
|
||||
|
||||
$this->httpHost = $httpHost;
|
||||
$this->port = $port;
|
||||
|
||||
$socket = new Reactor($address . ':' . $port, $loop, $context);
|
||||
|
||||
$this->routes = new RouteCollection;
|
||||
$this->_server = new IoServer(new HttpServer(new Router(new UrlMatcher($this->routes, new RequestContext))), $socket, $loop);
|
||||
|
||||
$policy = new FlashPolicy;
|
||||
$policy->addAllowedAccess($httpHost, 80);
|
||||
$policy->addAllowedAccess($httpHost, $port);
|
||||
|
||||
if (80 == $port) {
|
||||
$flashUri = '0.0.0.0:843';
|
||||
} else {
|
||||
$flashUri = 8843;
|
||||
}
|
||||
$flashSock = new Reactor($flashUri, $loop);
|
||||
$this->flashServer = new IoServer($policy, $flashSock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an endpoint/application to the server
|
||||
* @param string $path The URI the client will connect to
|
||||
* @param ComponentInterface $controller Your application to server for the route. If not specified, assumed to be for a WebSocket
|
||||
* @param array $allowedOrigins An array of hosts allowed to connect (same host by default), ['*'] for any
|
||||
* @param string $httpHost Override the $httpHost variable provided in the __construct
|
||||
* @return ComponentInterface|WsServer
|
||||
*/
|
||||
public function route($path, ComponentInterface $controller, array $allowedOrigins = array(), $httpHost = null) {
|
||||
if ($controller instanceof HttpServerInterface || $controller instanceof WsServer) {
|
||||
$decorated = $controller;
|
||||
} elseif ($controller instanceof WampServerInterface) {
|
||||
$decorated = new WsServer(new WampServer($controller));
|
||||
$decorated->enableKeepAlive($this->_server->loop);
|
||||
} elseif ($controller instanceof MessageComponentInterface || $controller instanceof WsMessageComponentInterface) {
|
||||
$decorated = new WsServer($controller);
|
||||
$decorated->enableKeepAlive($this->_server->loop);
|
||||
} else {
|
||||
$decorated = $controller;
|
||||
}
|
||||
|
||||
if ($httpHost === null) {
|
||||
$httpHost = $this->httpHost;
|
||||
}
|
||||
|
||||
$allowedOrigins = array_values($allowedOrigins);
|
||||
if (0 === count($allowedOrigins)) {
|
||||
$allowedOrigins[] = $httpHost;
|
||||
}
|
||||
if ('*' !== $allowedOrigins[0]) {
|
||||
$decorated = new OriginCheck($decorated, $allowedOrigins);
|
||||
}
|
||||
|
||||
//allow origins in flash policy server
|
||||
if(empty($this->flashServer) === false) {
|
||||
foreach($allowedOrigins as $allowedOrgin) {
|
||||
$this->flashServer->app->addAllowedAccess($allowedOrgin, $this->port);
|
||||
}
|
||||
}
|
||||
|
||||
$this->routes->add('rr-' . ++$this->_routeCounter, new Route($path, array('_controller' => $decorated), array('Origin' => $this->httpHost), array(), $httpHost, array(), array('GET')));
|
||||
|
||||
return $decorated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the server by entering the event loop
|
||||
*/
|
||||
public function run() {
|
||||
$this->_server->run();
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet;
|
||||
|
||||
/**
|
||||
* This is the interface to build a Ratchet application with.
|
||||
* It implements the decorator pattern to build an application stack
|
||||
*/
|
||||
interface ComponentInterface {
|
||||
/**
|
||||
* When a new connection is opened it will be passed to this method
|
||||
* @param ConnectionInterface $conn The socket/connection that just connected to your application
|
||||
* @throws \Exception
|
||||
*/
|
||||
function onOpen(ConnectionInterface $conn);
|
||||
|
||||
/**
|
||||
* This is called before or after a socket is closed (depends on how it's closed). SendMessage to $conn will not result in an error if it has already been closed.
|
||||
* @param ConnectionInterface $conn The socket/connection that is closing/closed
|
||||
* @throws \Exception
|
||||
*/
|
||||
function onClose(ConnectionInterface $conn);
|
||||
|
||||
/**
|
||||
* If there is an error with one of the sockets, or somewhere in the application where an Exception is thrown,
|
||||
* the Exception is sent back down the stack, handled by the Server and bubbled back up the application through this method
|
||||
* @param ConnectionInterface $conn
|
||||
* @param \Exception $e
|
||||
* @throws \Exception
|
||||
*/
|
||||
function onError(ConnectionInterface $conn, \Exception $e);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet;
|
||||
|
||||
/**
|
||||
* The version of Ratchet being used
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = 'Ratchet/0.4.4';
|
||||
|
||||
/**
|
||||
* A proxy object representing a connection to the application
|
||||
* This acts as a container to store data (in memory) about the connection
|
||||
*/
|
||||
interface ConnectionInterface {
|
||||
/**
|
||||
* Send data to the connection
|
||||
* @param string $data
|
||||
* @return \Ratchet\ConnectionInterface
|
||||
*/
|
||||
function send($data);
|
||||
|
||||
/**
|
||||
* Close the connection
|
||||
*/
|
||||
function close();
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Http;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use GuzzleHttp\Psr7\Message;
|
||||
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(Message::toString($response));
|
||||
$conn->close();
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Http;
|
||||
use Ratchet\MessageInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use GuzzleHttp\Psr7\Message;
|
||||
|
||||
/**
|
||||
* This class receives streaming data from a client request
|
||||
* and parses HTTP headers, returning a PSR-7 Request object
|
||||
* once it's been buffered
|
||||
*/
|
||||
class HttpRequestParser implements MessageInterface {
|
||||
const EOM = "\r\n\r\n";
|
||||
|
||||
/**
|
||||
* The maximum number of bytes the request can be
|
||||
* This is a security measure to prevent attacks
|
||||
* @var int
|
||||
*/
|
||||
public $maxSize = 4096;
|
||||
|
||||
/**
|
||||
* @param \Ratchet\ConnectionInterface $context
|
||||
* @param string $data Data stream to buffer
|
||||
* @return \Psr\Http\Message\RequestInterface
|
||||
* @throws \OverflowException If the message buffer has become too large
|
||||
*/
|
||||
public function onMessage(ConnectionInterface $context, $data) {
|
||||
if (!isset($context->httpBuffer)) {
|
||||
$context->httpBuffer = '';
|
||||
}
|
||||
|
||||
$context->httpBuffer .= $data;
|
||||
|
||||
if (strlen($context->httpBuffer) > (int)$this->maxSize) {
|
||||
throw new \OverflowException("Maximum buffer size of {$this->maxSize} exceeded parsing HTTP header");
|
||||
}
|
||||
|
||||
if ($this->isEom($context->httpBuffer)) {
|
||||
$request = $this->parse($context->httpBuffer);
|
||||
|
||||
unset($context->httpBuffer);
|
||||
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message has been buffered as per the HTTP specification
|
||||
* @param string $message
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEom($message) {
|
||||
return (boolean)strpos($message, static::EOM);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $headers
|
||||
* @return \Psr\Http\Message\RequestInterface
|
||||
*/
|
||||
public function parse($headers) {
|
||||
return Message::parseRequest($headers);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Http;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
class HttpServer implements MessageComponentInterface {
|
||||
use CloseResponseTrait;
|
||||
|
||||
/**
|
||||
* Buffers incoming HTTP requests returning a Guzzle Request when coalesced
|
||||
* @var HttpRequestParser
|
||||
* @note May not expose this in the future, may do through facade methods
|
||||
*/
|
||||
protected $_reqParser;
|
||||
|
||||
/**
|
||||
* @var \Ratchet\Http\HttpServerInterface
|
||||
*/
|
||||
protected $_httpServer;
|
||||
|
||||
/**
|
||||
* @param HttpServerInterface
|
||||
*/
|
||||
public function __construct(HttpServerInterface $component) {
|
||||
$this->_httpServer = $component;
|
||||
$this->_reqParser = new HttpRequestParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn) {
|
||||
$conn->httpHeadersReceived = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onMessage(ConnectionInterface $from, $msg) {
|
||||
if (true !== $from->httpHeadersReceived) {
|
||||
try {
|
||||
if (null === ($request = $this->_reqParser->onMessage($from, $msg))) {
|
||||
return;
|
||||
}
|
||||
} catch (\OverflowException $oe) {
|
||||
return $this->close($from, 413);
|
||||
}
|
||||
|
||||
$from->httpHeadersReceived = true;
|
||||
|
||||
return $this->_httpServer->onOpen($from, $request);
|
||||
}
|
||||
|
||||
$this->_httpServer->onMessage($from, $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
if ($conn->httpHeadersReceived) {
|
||||
$this->_httpServer->onClose($conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
if ($conn->httpHeadersReceived) {
|
||||
$this->_httpServer->onError($conn, $e);
|
||||
} else {
|
||||
$this->close($conn, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Http;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
interface HttpServerInterface extends MessageComponentInterface {
|
||||
/**
|
||||
* @param \Ratchet\ConnectionInterface $conn
|
||||
* @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
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<?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,65 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Http;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* A middleware to ensure JavaScript clients connecting are from the expected domain.
|
||||
* This protects other websites from open WebSocket connections to your application.
|
||||
* Note: This can be spoofed from non-web browser clients
|
||||
*/
|
||||
class OriginCheck implements HttpServerInterface {
|
||||
use CloseResponseTrait;
|
||||
|
||||
/**
|
||||
* @var \Ratchet\MessageComponentInterface
|
||||
*/
|
||||
protected $_component;
|
||||
|
||||
public $allowedOrigins = [];
|
||||
|
||||
/**
|
||||
* @param MessageComponentInterface $component Component/Application to decorate
|
||||
* @param array $allowed An array of allowed domains that are allowed to connect from
|
||||
*/
|
||||
public function __construct(MessageComponentInterface $component, array $allowed = []) {
|
||||
$this->_component = $component;
|
||||
$this->allowedOrigins += $allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||
$header = (string)$request->getHeader('Origin')[0];
|
||||
$origin = parse_url($header, PHP_URL_HOST) ?: $header;
|
||||
|
||||
if (!in_array($origin, $this->allowedOrigins)) {
|
||||
return $this->close($conn, 403);
|
||||
}
|
||||
|
||||
return $this->_component->onOpen($conn, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function onMessage(ConnectionInterface $from, $msg) {
|
||||
return $this->_component->onMessage($from, $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function onClose(ConnectionInterface $conn) {
|
||||
return $this->_component->onClose($conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
return $this->_component->onError($conn, $e);
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Http;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use GuzzleHttp\Psr7\Query;
|
||||
|
||||
class Router implements HttpServerInterface {
|
||||
use CloseResponseTrait;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface
|
||||
*/
|
||||
protected $_matcher;
|
||||
|
||||
private $_noopController;
|
||||
|
||||
public function __construct(UrlMatcherInterface $matcher) {
|
||||
$this->_matcher = $matcher;
|
||||
$this->_noopController = new NoOpHttpServerController;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws \UnexpectedValueException If a controller is not \Ratchet\Http\HttpServerInterface
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||
if (null === $request) {
|
||||
throw new \UnexpectedValueException('$request can not be null');
|
||||
}
|
||||
|
||||
$conn->controller = $this->_noopController;
|
||||
|
||||
$uri = $request->getUri();
|
||||
|
||||
$context = $this->_matcher->getContext();
|
||||
$context->setMethod($request->getMethod());
|
||||
$context->setHost($uri->getHost());
|
||||
|
||||
try {
|
||||
$route = $this->_matcher->match($uri->getPath());
|
||||
} catch (MethodNotAllowedException $nae) {
|
||||
return $this->close($conn, 405, array('Allow' => $nae->getAllowedMethods()));
|
||||
} catch (ResourceNotFoundException $nfe) {
|
||||
return $this->close($conn, 404);
|
||||
}
|
||||
|
||||
if (is_string($route['_controller']) && class_exists($route['_controller'])) {
|
||||
$route['_controller'] = new $route['_controller'];
|
||||
}
|
||||
|
||||
if (!($route['_controller'] instanceof HttpServerInterface)) {
|
||||
throw new \UnexpectedValueException('All routes must implement Ratchet\Http\HttpServerInterface');
|
||||
}
|
||||
|
||||
$parameters = [];
|
||||
foreach($route as $key => $value) {
|
||||
if ((is_string($key)) && ('_' !== substr($key, 0, 1))) {
|
||||
$parameters[$key] = $value;
|
||||
}
|
||||
}
|
||||
$parameters = array_merge($parameters, Query::parse($uri->getQuery() ?: ''));
|
||||
|
||||
$request = $request->withUri($uri->withQuery(Query::build($parameters)));
|
||||
|
||||
$conn->controller = $route['_controller'];
|
||||
$conn->controller->onOpen($conn, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onMessage(ConnectionInterface $from, $msg) {
|
||||
$from->controller->onMessage($from, $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
if (isset($conn->controller)) {
|
||||
$conn->controller->onClose($conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
if (isset($conn->controller)) {
|
||||
$conn->controller->onError($conn, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet;
|
||||
|
||||
interface MessageComponentInterface extends ComponentInterface, MessageInterface {
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet;
|
||||
|
||||
interface MessageInterface {
|
||||
/**
|
||||
* Triggered when a client sends data through the socket
|
||||
* @param \Ratchet\ConnectionInterface $from The socket/connection that sent the message to your application
|
||||
* @param string $msg The message received
|
||||
* @throws \Exception
|
||||
*/
|
||||
function onMessage(ConnectionInterface $from, $msg);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Server;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* A simple Ratchet application that will reply to all messages with the message it received
|
||||
*/
|
||||
class EchoServer implements MessageComponentInterface {
|
||||
public function onOpen(ConnectionInterface $conn) {
|
||||
}
|
||||
|
||||
public function onMessage(ConnectionInterface $from, $msg) {
|
||||
$from->send($msg);
|
||||
}
|
||||
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
}
|
||||
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
$conn->close();
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Server;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* An app to go on a server stack to pass a policy file to a Flash socket
|
||||
* Useful if you're using Flash as a WebSocket polyfill on IE
|
||||
* Be sure to run your server instance on port 843
|
||||
* By default this lets accepts everything, make sure you tighten the rules up for production
|
||||
* @final
|
||||
* @link http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html
|
||||
* @link http://learn.adobe.com/wiki/download/attachments/64389123/CrossDomain_PolicyFile_Specification.pdf?version=1
|
||||
* @link view-source:http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd
|
||||
*/
|
||||
class FlashPolicy implements MessageComponentInterface {
|
||||
|
||||
/**
|
||||
* Contains the root policy node
|
||||
* @var string
|
||||
*/
|
||||
protected $_policy = '<?xml version="1.0"?><!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"><cross-domain-policy></cross-domain-policy>';
|
||||
|
||||
/**
|
||||
* Stores an array of allowed domains and their ports
|
||||
* @var array
|
||||
*/
|
||||
protected $_access = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $_siteControl = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $_cache = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $_cacheValid = false;
|
||||
|
||||
/**
|
||||
* Add a domain to an allowed access list.
|
||||
*
|
||||
* @param string $domain Specifies a requesting domain to be granted access. Both named domains and IP
|
||||
* addresses are acceptable values. Subdomains are considered different domains. A wildcard (*) can
|
||||
* be used to match all domains when used alone, or multiple domains (subdomains) when used as a
|
||||
* prefix for an explicit, second-level domain name separated with a dot (.)
|
||||
* @param string $ports A comma-separated list of ports or range of ports that a socket connection
|
||||
* is allowed to connect to. A range of ports is specified through a dash (-) between two port numbers.
|
||||
* Ranges can be used with individual ports when separated with a comma. A single wildcard (*) can
|
||||
* be used to allow all ports.
|
||||
* @param bool $secure
|
||||
* @throws \UnexpectedValueException
|
||||
* @return FlashPolicy
|
||||
*/
|
||||
public function addAllowedAccess($domain, $ports = '*', $secure = false) {
|
||||
if (!$this->validateDomain($domain)) {
|
||||
throw new \UnexpectedValueException('Invalid domain');
|
||||
}
|
||||
|
||||
if (!$this->validatePorts($ports)) {
|
||||
throw new \UnexpectedValueException('Invalid Port');
|
||||
}
|
||||
|
||||
$this->_access[] = array($domain, $ports, (boolean)$secure);
|
||||
$this->_cacheValid = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all domains from the allowed access list.
|
||||
*
|
||||
* @return \Ratchet\Server\FlashPolicy
|
||||
*/
|
||||
public function clearAllowedAccess() {
|
||||
$this->_access = array();
|
||||
$this->_cacheValid = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* site-control defines the meta-policy for the current domain. A meta-policy specifies acceptable
|
||||
* domain policy files other than the master policy file located in the target domain's root and named
|
||||
* crossdomain.xml.
|
||||
*
|
||||
* @param string $permittedCrossDomainPolicies
|
||||
* @throws \UnexpectedValueException
|
||||
* @return FlashPolicy
|
||||
*/
|
||||
public function setSiteControl($permittedCrossDomainPolicies = 'all') {
|
||||
if (!$this->validateSiteControl($permittedCrossDomainPolicies)) {
|
||||
throw new \UnexpectedValueException('Invalid site control set');
|
||||
}
|
||||
|
||||
$this->_siteControl = $permittedCrossDomainPolicies;
|
||||
$this->_cacheValid = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onMessage(ConnectionInterface $from, $msg) {
|
||||
if (!$this->_cacheValid) {
|
||||
$this->_cache = $this->renderPolicy()->asXML();
|
||||
$this->_cacheValid = true;
|
||||
}
|
||||
|
||||
$from->send($this->_cache . "\0");
|
||||
$from->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
$conn->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the crossdomain file based on the template policy
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
* @return \SimpleXMLElement
|
||||
*/
|
||||
public function renderPolicy() {
|
||||
$policy = new \SimpleXMLElement($this->_policy);
|
||||
|
||||
$siteControl = $policy->addChild('site-control');
|
||||
|
||||
if ($this->_siteControl == '') {
|
||||
$this->setSiteControl();
|
||||
}
|
||||
|
||||
$siteControl->addAttribute('permitted-cross-domain-policies', $this->_siteControl);
|
||||
|
||||
if (empty($this->_access)) {
|
||||
throw new \UnexpectedValueException('You must add a domain through addAllowedAccess()');
|
||||
}
|
||||
|
||||
foreach ($this->_access as $access) {
|
||||
$tmp = $policy->addChild('allow-access-from');
|
||||
$tmp->addAttribute('domain', $access[0]);
|
||||
$tmp->addAttribute('to-ports', $access[1]);
|
||||
$tmp->addAttribute('secure', ($access[2] === true) ? 'true' : 'false');
|
||||
}
|
||||
|
||||
return $policy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the proper site control was passed
|
||||
*
|
||||
* @param string $permittedCrossDomainPolicies
|
||||
* @return bool
|
||||
*/
|
||||
public function validateSiteControl($permittedCrossDomainPolicies) {
|
||||
//'by-content-type' and 'by-ftp-filename' are not available for sockets
|
||||
return (bool)in_array($permittedCrossDomainPolicies, array('none', 'master-only', 'all'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate for proper domains (wildcards allowed)
|
||||
*
|
||||
* @param string $domain
|
||||
* @return bool
|
||||
*/
|
||||
public function validateDomain($domain) {
|
||||
return (bool)preg_match("/^((http(s)?:\/\/)?([a-z0-9-_]+\.|\*\.)*([a-z0-9-_\.]+)|\*)$/i", $domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure valid ports were passed
|
||||
*
|
||||
* @param string $port
|
||||
* @return bool
|
||||
*/
|
||||
public function validatePorts($port) {
|
||||
return (bool)preg_match('/^(\*|(\d+[,-]?)*\d+)$/', $port);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Server;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use React\Socket\ConnectionInterface as ReactConn;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class IoConnection implements ConnectionInterface {
|
||||
/**
|
||||
* @var \React\Socket\ConnectionInterface
|
||||
*/
|
||||
protected $conn;
|
||||
|
||||
|
||||
/**
|
||||
* @param \React\Socket\ConnectionInterface $conn
|
||||
*/
|
||||
public function __construct(ReactConn $conn) {
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function send($data) {
|
||||
$this->conn->write($data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close() {
|
||||
$this->conn->end();
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Server;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\Socket\ServerInterface;
|
||||
use React\EventLoop\Factory as LoopFactory;
|
||||
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.
|
||||
* Events are delegated through this to attached applications
|
||||
*/
|
||||
class IoServer {
|
||||
/**
|
||||
* @var \React\EventLoop\LoopInterface
|
||||
*/
|
||||
public $loop;
|
||||
|
||||
/**
|
||||
* @var \Ratchet\MessageComponentInterface
|
||||
*/
|
||||
public $app;
|
||||
|
||||
/**
|
||||
* The socket server the Ratchet Application is run off of
|
||||
* @var \React\Socket\ServerInterface
|
||||
*/
|
||||
public $socket;
|
||||
|
||||
/**
|
||||
* @param \Ratchet\MessageComponentInterface $app The Ratchet application stack to host
|
||||
* @param \React\Socket\ServerInterface $socket The React socket server to run the Ratchet application off of
|
||||
* @param \React\EventLoop\LoopInterface|null $loop The React looper to run the Ratchet application off of
|
||||
*/
|
||||
public function __construct(MessageComponentInterface $app, ServerInterface $socket, LoopInterface $loop = null) {
|
||||
if (false === strpos(PHP_VERSION, "hiphop")) {
|
||||
gc_enable();
|
||||
}
|
||||
|
||||
set_time_limit(0);
|
||||
ob_implicit_flush();
|
||||
|
||||
$this->loop = $loop;
|
||||
$this->app = $app;
|
||||
$this->socket = $socket;
|
||||
|
||||
$socket->on('connection', array($this, 'handleConnect'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 string $address The address to receive sockets on (0.0.0.0 means receive connections from any)
|
||||
* @return IoServer
|
||||
*/
|
||||
public static function factory(MessageComponentInterface $component, $port = 80, $address = '0.0.0.0') {
|
||||
$loop = LoopFactory::create();
|
||||
$socket = new Reactor($address . ':' . $port, $loop);
|
||||
|
||||
return new static($component, $socket, $loop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the application by entering the event loop
|
||||
* @throws \RuntimeException If a loop was not previously specified
|
||||
*/
|
||||
public function run() {
|
||||
if (null === $this->loop) {
|
||||
throw new \RuntimeException("A React Loop was not provided during instantiation");
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
$this->loop->run();
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when a new connection is received from React
|
||||
* @param \React\Socket\ConnectionInterface $conn
|
||||
*/
|
||||
public function handleConnect($conn) {
|
||||
$conn->decor = new IoConnection($conn);
|
||||
$conn->decor->resourceId = (int)$conn->stream;
|
||||
|
||||
$uri = $conn->getRemoteAddress();
|
||||
$conn->decor->remoteAddress = trim(
|
||||
parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri, PHP_URL_HOST),
|
||||
'[]'
|
||||
);
|
||||
|
||||
$this->app->onOpen($conn->decor);
|
||||
|
||||
$conn->on('data', function ($data) use ($conn) {
|
||||
$this->handleData($data, $conn);
|
||||
});
|
||||
$conn->on('close', function () use ($conn) {
|
||||
$this->handleEnd($conn);
|
||||
});
|
||||
$conn->on('error', function (\Exception $e) use ($conn) {
|
||||
$this->handleError($e, $conn);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Data has been received from React
|
||||
* @param string $data
|
||||
* @param \React\Socket\ConnectionInterface $conn
|
||||
*/
|
||||
public function handleData($data, $conn) {
|
||||
try {
|
||||
$this->app->onMessage($conn->decor, $data);
|
||||
} catch (\Exception $e) {
|
||||
$this->handleError($e, $conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A connection has been closed by React
|
||||
* @param \React\Socket\ConnectionInterface $conn
|
||||
*/
|
||||
public function handleEnd($conn) {
|
||||
try {
|
||||
$this->app->onClose($conn->decor);
|
||||
} catch (\Exception $e) {
|
||||
$this->handleError($e, $conn);
|
||||
}
|
||||
|
||||
unset($conn->decor);
|
||||
}
|
||||
|
||||
/**
|
||||
* An error has occurred, let the listening application know
|
||||
* @param \Exception $e
|
||||
* @param \React\Socket\ConnectionInterface $conn
|
||||
*/
|
||||
public function handleError(\Exception $e, $conn) {
|
||||
$this->app->onError($conn->decor, $e);
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Server;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
class IpBlackList implements MessageComponentInterface {
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $_blacklist = array();
|
||||
|
||||
/**
|
||||
* @var \Ratchet\MessageComponentInterface
|
||||
*/
|
||||
protected $_decorating;
|
||||
|
||||
/**
|
||||
* @param \Ratchet\MessageComponentInterface $component
|
||||
*/
|
||||
public function __construct(MessageComponentInterface $component) {
|
||||
$this->_decorating = $component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an address to the blacklist that will not be allowed to connect to your application
|
||||
* @param string $ip IP address to block from connecting to your application
|
||||
* @return IpBlackList
|
||||
*/
|
||||
public function blockAddress($ip) {
|
||||
$this->_blacklist[$ip] = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unblock an address so they can access your application again
|
||||
* @param string $ip IP address to unblock from connecting to your application
|
||||
* @return IpBlackList
|
||||
*/
|
||||
public function unblockAddress($ip) {
|
||||
if (isset($this->_blacklist[$this->filterAddress($ip)])) {
|
||||
unset($this->_blacklist[$this->filterAddress($ip)]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $address
|
||||
* @return bool
|
||||
*/
|
||||
public function isBlocked($address) {
|
||||
return (isset($this->_blacklist[$this->filterAddress($address)]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of all the addresses blocked
|
||||
* @return array
|
||||
*/
|
||||
public function getBlockedAddresses() {
|
||||
return array_keys($this->_blacklist);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $address
|
||||
* @return string
|
||||
*/
|
||||
public function filterAddress($address) {
|
||||
if (strstr($address, ':') && substr_count($address, '.') == 3) {
|
||||
list($address, $port) = explode(':', $address);
|
||||
}
|
||||
|
||||
return $address;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function onOpen(ConnectionInterface $conn) {
|
||||
if ($this->isBlocked($conn->remoteAddress)) {
|
||||
return $conn->close();
|
||||
}
|
||||
|
||||
return $this->_decorating->onOpen($conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function onMessage(ConnectionInterface $from, $msg) {
|
||||
return $this->_decorating->onMessage($from, $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function onClose(ConnectionInterface $conn) {
|
||||
if (!$this->isBlocked($conn->remoteAddress)) {
|
||||
$this->_decorating->onClose($conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
if (!$this->isBlocked($conn->remoteAddress)) {
|
||||
$this->_decorating->onError($conn, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Session\Serialize;
|
||||
|
||||
interface HandlerInterface {
|
||||
/**
|
||||
* @param array
|
||||
* @return string
|
||||
*/
|
||||
function serialize(array $data);
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
function unserialize($raw);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Session\Serialize;
|
||||
|
||||
class PhpBinaryHandler implements HandlerInterface {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function serialize(array $data) {
|
||||
throw new \RuntimeException("Serialize PhpHandler:serialize code not written yet, write me!");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @link http://ca2.php.net/manual/en/function.session-decode.php#108037 Code from this comment on php.net
|
||||
*/
|
||||
public function unserialize($raw) {
|
||||
$returnData = array();
|
||||
$offset = 0;
|
||||
|
||||
while ($offset < strlen($raw)) {
|
||||
$num = ord($raw[$offset]);
|
||||
$offset += 1;
|
||||
$varname = substr($raw, $offset, $num);
|
||||
$offset += $num;
|
||||
$data = unserialize(substr($raw, $offset));
|
||||
|
||||
$returnData[$varname] = $data;
|
||||
$offset += strlen(serialize($data));
|
||||
}
|
||||
|
||||
return $returnData;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Session\Serialize;
|
||||
|
||||
class PhpHandler implements HandlerInterface {
|
||||
/**
|
||||
* Simply reverse behaviour of unserialize method.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function serialize(array $data) {
|
||||
$preSerialized = array();
|
||||
$serialized = '';
|
||||
|
||||
if (count($data)) {
|
||||
foreach ($data as $bucket => $bucketData) {
|
||||
$preSerialized[] = $bucket . '|' . serialize($bucketData);
|
||||
}
|
||||
$serialized = implode('', $preSerialized);
|
||||
}
|
||||
|
||||
return $serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @link http://ca2.php.net/manual/en/function.session-decode.php#108037 Code from this comment on php.net
|
||||
* @throws \UnexpectedValueException If there is a problem parsing the data
|
||||
*/
|
||||
public function unserialize($raw) {
|
||||
$returnData = array();
|
||||
$offset = 0;
|
||||
|
||||
while ($offset < strlen($raw)) {
|
||||
if (!strstr(substr($raw, $offset), "|")) {
|
||||
throw new \UnexpectedValueException("invalid data, remaining: " . substr($raw, $offset));
|
||||
}
|
||||
|
||||
$pos = strpos($raw, "|", $offset);
|
||||
$num = $pos - $offset;
|
||||
$varname = substr($raw, $offset, $num);
|
||||
$offset += $num + 1;
|
||||
$data = unserialize(substr($raw, $offset));
|
||||
|
||||
$returnData[$varname] = $data;
|
||||
$offset += strlen(serialize($data));
|
||||
}
|
||||
|
||||
return $returnData;
|
||||
}
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Session;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\Http\HttpServerInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Ratchet\Session\Storage\VirtualSessionStorage;
|
||||
use Ratchet\Session\Serialize\HandlerInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
||||
|
||||
/**
|
||||
* This component will allow access to session data from your website for each user connected
|
||||
* Symfony HttpFoundation is required for this component to work
|
||||
* 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)
|
||||
*/
|
||||
class SessionProvider implements HttpServerInterface {
|
||||
/**
|
||||
* @var \Ratchet\MessageComponentInterface
|
||||
*/
|
||||
protected $_app;
|
||||
|
||||
/**
|
||||
* Selected handler storage assigned by the developer
|
||||
* @var \SessionHandlerInterface
|
||||
*/
|
||||
protected $_handler;
|
||||
|
||||
/**
|
||||
* Null storage handler if no previous session was found
|
||||
* @var \SessionHandlerInterface
|
||||
*/
|
||||
protected $_null;
|
||||
|
||||
/**
|
||||
* @var \Ratchet\Session\Serialize\HandlerInterface
|
||||
*/
|
||||
protected $_serializer;
|
||||
|
||||
/**
|
||||
* @param \Ratchet\Http\HttpServerInterface $app
|
||||
* @param \SessionHandlerInterface $handler
|
||||
* @param array $options
|
||||
* @param \Ratchet\Session\Serialize\HandlerInterface $serializer
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function __construct(HttpServerInterface $app, \SessionHandlerInterface $handler, array $options = array(), HandlerInterface $serializer = null) {
|
||||
$this->_app = $app;
|
||||
$this->_handler = $handler;
|
||||
$this->_null = new NullSessionHandler;
|
||||
|
||||
ini_set('session.auto_start', 0);
|
||||
ini_set('session.cache_limiter', '');
|
||||
ini_set('session.use_cookies', 0);
|
||||
|
||||
$this->setOptions($options);
|
||||
|
||||
if (null === $serializer) {
|
||||
$serialClass = __NAMESPACE__ . "\\Serialize\\{$this->toClassCase(ini_get('session.serialize_handler'))}Handler"; // awesome/terrible hack, eh?
|
||||
if (!class_exists($serialClass)) {
|
||||
throw new \RuntimeException('Unable to parse session serialize handler');
|
||||
}
|
||||
|
||||
$serializer = new $serialClass;
|
||||
}
|
||||
|
||||
$this->_serializer = $serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||
$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;
|
||||
$id = '';
|
||||
} else {
|
||||
$saveHandler = $this->_handler;
|
||||
}
|
||||
|
||||
$conn->Session = new Session(new VirtualSessionStorage($saveHandler, $id, $this->_serializer));
|
||||
|
||||
if (ini_get('session.auto_start')) {
|
||||
$conn->Session->start();
|
||||
}
|
||||
|
||||
return $this->_app->onOpen($conn, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function onMessage(ConnectionInterface $from, $msg) {
|
||||
return $this->_app->onMessage($from, $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function onClose(ConnectionInterface $conn) {
|
||||
// "close" session for Connection
|
||||
|
||||
return $this->_app->onClose($conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
return $this->_app->onError($conn, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all the php session. ini options
|
||||
* © Symfony
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
protected function setOptions(array $options) {
|
||||
$all = array(
|
||||
'auto_start', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
|
||||
'cookie_lifetime', 'cookie_path', 'cookie_secure',
|
||||
'entropy_file', 'entropy_length', 'gc_divisor',
|
||||
'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
|
||||
'hash_function', 'name', 'referer_check',
|
||||
'serialize_handler', 'use_cookies',
|
||||
'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
|
||||
'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
|
||||
'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags'
|
||||
);
|
||||
|
||||
foreach ($all as $key) {
|
||||
if (!array_key_exists($key, $options)) {
|
||||
$options[$key] = ini_get("session.{$key}");
|
||||
} else {
|
||||
ini_set("session.{$key}", $options[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $langDef Input to convert
|
||||
* @return string
|
||||
*/
|
||||
protected function toClassCase($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;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Session\Storage\Proxy;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||
|
||||
class VirtualProxy extends SessionHandlerProxy {
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $_sessionId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $_sessionName;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(\SessionHandlerInterface $handler) {
|
||||
parent::__construct($handler);
|
||||
|
||||
$this->saveHandlerName = 'user';
|
||||
$this->_sessionName = ini_get('session.name');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId() {
|
||||
return $this->_sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setId($id) {
|
||||
$this->_sessionId = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->_sessionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT CALL THIS METHOD
|
||||
* @internal
|
||||
*/
|
||||
public function setName($name) {
|
||||
throw new \RuntimeException("Can not change session name in VirtualProxy");
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Session\Storage;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
||||
use Ratchet\Session\Storage\Proxy\VirtualProxy;
|
||||
use Ratchet\Session\Serialize\HandlerInterface;
|
||||
|
||||
class VirtualSessionStorage extends NativeSessionStorage {
|
||||
/**
|
||||
* @var \Ratchet\Session\Serialize\HandlerInterface
|
||||
*/
|
||||
protected $_serializer;
|
||||
|
||||
/**
|
||||
* @param \SessionHandlerInterface $handler
|
||||
* @param string $sessionId The ID of the session to retrieve
|
||||
* @param \Ratchet\Session\Serialize\HandlerInterface $serializer
|
||||
*/
|
||||
public function __construct(\SessionHandlerInterface $handler, $sessionId, HandlerInterface $serializer) {
|
||||
$this->setSaveHandler($handler);
|
||||
$this->saveHandler->setId($sessionId);
|
||||
$this->_serializer = $serializer;
|
||||
$this->setMetadataBag(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function start() {
|
||||
if ($this->started && !$this->closed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// You have to call Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::open() to use
|
||||
// pdo_sqlite (and possible pdo_*) as session storage, if you are using a DSN string instead of a \PDO object
|
||||
// in the constructor. The method arguments are filled with the values, which are also used by the symfony
|
||||
// framework in this case. This must not be the best choice, but it works.
|
||||
$this->saveHandler->open(session_save_path(), session_name());
|
||||
|
||||
$rawData = $this->saveHandler->read($this->saveHandler->getId());
|
||||
$sessionData = $this->_serializer->unserialize($rawData);
|
||||
|
||||
$this->loadSession($sessionData);
|
||||
|
||||
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
|
||||
$this->saveHandler->setActive(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function regenerate($destroy = false, $lifetime = null) {
|
||||
// .. ?
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save() {
|
||||
// get the data from the bags?
|
||||
// serialize the data
|
||||
// save the data using the saveHandler
|
||||
// $this->saveHandler->write($this->saveHandler->getId(),
|
||||
|
||||
if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) {
|
||||
$this->saveHandler->setActive(false);
|
||||
}
|
||||
|
||||
$this->closed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSaveHandler($saveHandler = null) {
|
||||
if (!($saveHandler instanceof \SessionHandlerInterface)) {
|
||||
throw new \InvalidArgumentException('Handler must be instance of SessionHandlerInterface');
|
||||
}
|
||||
|
||||
if (!($saveHandler instanceof VirtualProxy)) {
|
||||
$saveHandler = new VirtualProxy($saveHandler);
|
||||
}
|
||||
|
||||
$this->saveHandler = $saveHandler;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
|
||||
class Exception extends \Exception {
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
|
||||
class JsonException extends Exception {
|
||||
public function __construct() {
|
||||
$code = json_last_error();
|
||||
|
||||
switch ($code) {
|
||||
case JSON_ERROR_DEPTH:
|
||||
$msg = 'Maximum stack depth exceeded';
|
||||
break;
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
$msg = 'Underflow or the modes mismatch';
|
||||
break;
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
$msg = 'Unexpected control character found';
|
||||
break;
|
||||
case JSON_ERROR_SYNTAX:
|
||||
$msg = 'Syntax error, malformed JSON';
|
||||
break;
|
||||
case JSON_ERROR_UTF8:
|
||||
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
||||
break;
|
||||
default:
|
||||
$msg = 'Unknown error';
|
||||
break;
|
||||
}
|
||||
|
||||
parent::__construct($msg, $code);
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\WebSocket\WsServerInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* WebSocket Application Messaging Protocol
|
||||
*
|
||||
* @link http://wamp.ws/spec
|
||||
* @link https://github.com/oberstet/autobahn-js
|
||||
*
|
||||
* +--------------+----+------------------+
|
||||
* | Message Type | ID | DIRECTION |
|
||||
* |--------------+----+------------------+
|
||||
* | WELCOME | 0 | Server-to-Client |
|
||||
* | PREFIX | 1 | Bi-Directional |
|
||||
* | CALL | 2 | Client-to-Server |
|
||||
* | CALL RESULT | 3 | Server-to-Client |
|
||||
* | CALL ERROR | 4 | Server-to-Client |
|
||||
* | SUBSCRIBE | 5 | Client-to-Server |
|
||||
* | UNSUBSCRIBE | 6 | Client-to-Server |
|
||||
* | PUBLISH | 7 | Client-to-Server |
|
||||
* | EVENT | 8 | Server-to-Client |
|
||||
* +--------------+----+------------------+
|
||||
*/
|
||||
class ServerProtocol implements MessageComponentInterface, WsServerInterface {
|
||||
const MSG_WELCOME = 0;
|
||||
const MSG_PREFIX = 1;
|
||||
const MSG_CALL = 2;
|
||||
const MSG_CALL_RESULT = 3;
|
||||
const MSG_CALL_ERROR = 4;
|
||||
const MSG_SUBSCRIBE = 5;
|
||||
const MSG_UNSUBSCRIBE = 6;
|
||||
const MSG_PUBLISH = 7;
|
||||
const MSG_EVENT = 8;
|
||||
|
||||
/**
|
||||
* @var WampServerInterface
|
||||
*/
|
||||
protected $_decorating;
|
||||
|
||||
/**
|
||||
* @var \SplObjectStorage
|
||||
*/
|
||||
protected $connections;
|
||||
|
||||
/**
|
||||
* @param WampServerInterface $serverComponent An class to propagate calls through
|
||||
*/
|
||||
public function __construct(WampServerInterface $serverComponent) {
|
||||
$this->_decorating = $serverComponent;
|
||||
$this->connections = new \SplObjectStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSubProtocols() {
|
||||
if ($this->_decorating instanceof WsServerInterface) {
|
||||
$subs = $this->_decorating->getSubProtocols();
|
||||
$subs[] = 'wamp';
|
||||
|
||||
return $subs;
|
||||
}
|
||||
|
||||
return ['wamp'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn) {
|
||||
$decor = new WampConnection($conn);
|
||||
$this->connections->attach($conn, $decor);
|
||||
|
||||
$this->_decorating->onOpen($decor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @throws \Ratchet\Wamp\Exception
|
||||
* @throws \Ratchet\Wamp\JsonException
|
||||
*/
|
||||
public function onMessage(ConnectionInterface $from, $msg) {
|
||||
$from = $this->connections[$from];
|
||||
|
||||
if (null === ($json = @json_decode($msg, true))) {
|
||||
throw new JsonException;
|
||||
}
|
||||
|
||||
if (!is_array($json) || $json !== array_values($json)) {
|
||||
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]) {
|
||||
case static::MSG_PREFIX:
|
||||
$from->WAMP->prefixes[$json[1]] = $json[2];
|
||||
break;
|
||||
|
||||
case static::MSG_CALL:
|
||||
array_shift($json);
|
||||
$callID = array_shift($json);
|
||||
$procURI = array_shift($json);
|
||||
|
||||
if (count($json) == 1 && is_array($json[0])) {
|
||||
$json = $json[0];
|
||||
}
|
||||
|
||||
$this->_decorating->onCall($from, $callID, $from->getUri($procURI), $json);
|
||||
break;
|
||||
|
||||
case static::MSG_SUBSCRIBE:
|
||||
$this->_decorating->onSubscribe($from, $from->getUri($json[1]));
|
||||
break;
|
||||
|
||||
case static::MSG_UNSUBSCRIBE:
|
||||
$this->_decorating->onUnSubscribe($from, $from->getUri($json[1]));
|
||||
break;
|
||||
|
||||
case static::MSG_PUBLISH:
|
||||
$exclude = (array_key_exists(3, $json) ? $json[3] : null);
|
||||
if (!is_array($exclude)) {
|
||||
if (true === (boolean)$exclude) {
|
||||
$exclude = [$from->WAMP->sessionId];
|
||||
} else {
|
||||
$exclude = [];
|
||||
}
|
||||
}
|
||||
|
||||
$eligible = (array_key_exists(4, $json) ? $json[4] : []);
|
||||
|
||||
$this->_decorating->onPublish($from, $from->getUri($json[1]), $json[2], $exclude, $eligible);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception('Invalid WAMP message type');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
$decor = $this->connections[$conn];
|
||||
$this->connections->detach($conn);
|
||||
|
||||
$this->_decorating->onClose($decor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
return $this->_decorating->onError($this->connections[$conn], $e);
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* A topic/channel containing connections that have subscribed to it
|
||||
*/
|
||||
class Topic implements \IteratorAggregate, \Countable {
|
||||
private $id;
|
||||
|
||||
private $subscribers;
|
||||
|
||||
/**
|
||||
* @param string $topicId Unique ID for this object
|
||||
*/
|
||||
public function __construct($topicId) {
|
||||
$this->id = $topicId;
|
||||
$this->subscribers = new \SplObjectStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to all the connections in this topic
|
||||
* @param string|array $msg Payload to publish
|
||||
* @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)
|
||||
* @return Topic The same Topic object to chain
|
||||
*/
|
||||
public function broadcast($msg, array $exclude = array(), array $eligible = array()) {
|
||||
$useEligible = (bool)count($eligible);
|
||||
foreach ($this->subscribers as $client) {
|
||||
if (in_array($client->WAMP->sessionId, $exclude)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($useEligible && !in_array($client->WAMP->sessionId, $eligible)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$client->event($this->id, $msg);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WampConnection $conn
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(ConnectionInterface $conn) {
|
||||
return $this->subscribers->contains($conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WampConnection $conn
|
||||
* @return Topic
|
||||
*/
|
||||
public function add(ConnectionInterface $conn) {
|
||||
$this->subscribers->attach($conn);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WampConnection $conn
|
||||
* @return Topic
|
||||
*/
|
||||
public function remove(ConnectionInterface $conn) {
|
||||
if ($this->subscribers->contains($conn)) {
|
||||
$this->subscribers->detach($conn);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator() {
|
||||
return $this->subscribers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count() {
|
||||
return $this->subscribers->count();
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\WebSocket\WsServerInterface;
|
||||
|
||||
class TopicManager implements WsServerInterface, WampServerInterface {
|
||||
/**
|
||||
* @var WampServerInterface
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $topicLookup = array();
|
||||
|
||||
public function __construct(WampServerInterface $app) {
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn) {
|
||||
$conn->WAMP->subscriptions = new \SplObjectStorage;
|
||||
$this->app->onOpen($conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {
|
||||
$this->app->onCall($conn, $id, $this->getTopic($topic), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onSubscribe(ConnectionInterface $conn, $topic) {
|
||||
$topicObj = $this->getTopic($topic);
|
||||
|
||||
if ($conn->WAMP->subscriptions->contains($topicObj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->topicLookup[$topic]->add($conn);
|
||||
$conn->WAMP->subscriptions->attach($topicObj);
|
||||
$this->app->onSubscribe($conn, $topicObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onUnsubscribe(ConnectionInterface $conn, $topic) {
|
||||
$topicObj = $this->getTopic($topic);
|
||||
|
||||
if (!$conn->WAMP->subscriptions->contains($topicObj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->cleanTopic($topicObj, $conn);
|
||||
|
||||
$this->app->onUnsubscribe($conn, $topicObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
|
||||
$this->app->onPublish($conn, $this->getTopic($topic), $event, $exclude, $eligible);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
$this->app->onClose($conn);
|
||||
|
||||
foreach ($this->topicLookup as $topic) {
|
||||
$this->cleanTopic($topic, $conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
$this->app->onError($conn, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSubProtocols() {
|
||||
if ($this->app instanceof WsServerInterface) {
|
||||
return $this->app->getSubProtocols();
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @return Topic
|
||||
*/
|
||||
protected function getTopic($topic) {
|
||||
if (!array_key_exists($topic, $this->topicLookup)) {
|
||||
$this->topicLookup[$topic] = new Topic($topic);
|
||||
}
|
||||
|
||||
return $this->topicLookup[$topic];
|
||||
}
|
||||
|
||||
protected function cleanTopic(Topic $topic, ConnectionInterface $conn) {
|
||||
if ($conn->WAMP->subscriptions->contains($topic)) {
|
||||
$conn->WAMP->subscriptions->detach($topic);
|
||||
}
|
||||
|
||||
$this->topicLookup[$topic->getId()]->remove($conn);
|
||||
|
||||
if (0 === $topic->count()) {
|
||||
unset($this->topicLookup[$topic->getId()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\AbstractConnectionDecorator;
|
||||
use Ratchet\Wamp\ServerProtocol as WAMP;
|
||||
|
||||
/**
|
||||
* A ConnectionInterface object wrapper that is passed to your WAMP application
|
||||
* representing a client. Methods on this Connection are therefore different.
|
||||
* @property \stdClass $WAMP
|
||||
*/
|
||||
class WampConnection extends AbstractConnectionDecorator {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(ConnectionInterface $conn) {
|
||||
parent::__construct($conn);
|
||||
|
||||
$this->WAMP = new \StdClass;
|
||||
$this->WAMP->sessionId = str_replace('.', '', uniqid(mt_rand(), true));
|
||||
$this->WAMP->prefixes = array();
|
||||
|
||||
$this->send(json_encode(array(WAMP::MSG_WELCOME, $this->WAMP->sessionId, 1, \Ratchet\VERSION)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Successfully respond to a call made by the client
|
||||
* @param string $id The unique ID given by the client to respond to
|
||||
* @param array $data an object or array
|
||||
* @return WampConnection
|
||||
*/
|
||||
public function callResult($id, $data = array()) {
|
||||
return $this->send(json_encode(array(WAMP::MSG_CALL_RESULT, $id, $data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond with an error to a client call
|
||||
* @param string $id The unique ID given by the client to respond to
|
||||
* @param string $errorUri The URI given to identify the specific error
|
||||
* @param string $desc A developer-oriented description of the error
|
||||
* @param string $details An optional human readable detail message to send back
|
||||
* @return WampConnection
|
||||
*/
|
||||
public function callError($id, $errorUri, $desc = '', $details = null) {
|
||||
if ($errorUri instanceof Topic) {
|
||||
$errorUri = (string)$errorUri;
|
||||
}
|
||||
|
||||
$data = array(WAMP::MSG_CALL_ERROR, $id, $errorUri, $desc);
|
||||
|
||||
if (null !== $details) {
|
||||
$data[] = $details;
|
||||
}
|
||||
|
||||
return $this->send(json_encode($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $topic The topic to broadcast to
|
||||
* @param mixed $msg Data to send with the event. Anything that is json'able
|
||||
* @return WampConnection
|
||||
*/
|
||||
public function event($topic, $msg) {
|
||||
return $this->send(json_encode(array(WAMP::MSG_EVENT, (string)$topic, $msg)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $curie
|
||||
* @param string $uri
|
||||
* @return WampConnection
|
||||
*/
|
||||
public function prefix($curie, $uri) {
|
||||
$this->WAMP->prefixes[$curie] = (string)$uri;
|
||||
|
||||
return $this->send(json_encode(array(WAMP::MSG_PREFIX, $curie, (string)$uri)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full request URI from the connection object if a prefix has been established for it
|
||||
* @param string $uri
|
||||
* @return string
|
||||
*/
|
||||
public function getUri($uri) {
|
||||
$curieSeperator = ':';
|
||||
|
||||
if (preg_match('/http(s*)\:\/\//', $uri) == false) {
|
||||
if (strpos($uri, $curieSeperator) !== false) {
|
||||
list($prefix, $action) = explode($curieSeperator, $uri);
|
||||
|
||||
if(isset($this->WAMP->prefixes[$prefix]) === true){
|
||||
return $this->WAMP->prefixes[$prefix] . '#' . $action;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function send($data) {
|
||||
$this->getConnection()->send($data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close($opt = null) {
|
||||
$this->getConnection()->close($opt);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\WebSocket\WsServerInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* Enable support for the official WAMP sub-protocol in your application
|
||||
* WAMP allows for Pub/Sub and RPC
|
||||
* @link http://wamp.ws The WAMP specification
|
||||
* @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
|
||||
*/
|
||||
class WampServer implements MessageComponentInterface, WsServerInterface {
|
||||
/**
|
||||
* @var ServerProtocol
|
||||
*/
|
||||
protected $wampProtocol;
|
||||
|
||||
/**
|
||||
* This class just makes it 1 step easier to use Topic objects in WAMP
|
||||
* If you're looking at the source code, look in the __construct of this
|
||||
* class and use that to make your application instead of using this
|
||||
*/
|
||||
public function __construct(WampServerInterface $app) {
|
||||
$this->wampProtocol = new ServerProtocol(new TopicManager($app));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn) {
|
||||
$this->wampProtocol->onOpen($conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onMessage(ConnectionInterface $conn, $msg) {
|
||||
try {
|
||||
$this->wampProtocol->onMessage($conn, $msg);
|
||||
} catch (Exception $we) {
|
||||
$conn->close(1007);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
$this->wampProtocol->onClose($conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
$this->wampProtocol->onError($conn, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSubProtocols() {
|
||||
return $this->wampProtocol->getSubProtocols();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
use Ratchet\ComponentInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* An extension of Ratchet\ComponentInterface to server a WAMP application
|
||||
* onMessage is replaced by various types of messages for this protocol (pub/sub or rpc)
|
||||
*/
|
||||
interface WampServerInterface extends ComponentInterface {
|
||||
/**
|
||||
* An RPC call has been received
|
||||
* @param \Ratchet\ConnectionInterface $conn
|
||||
* @param string $id The unique ID of the RPC, required to respond to
|
||||
* @param string|Topic $topic The topic to execute the call against
|
||||
* @param array $params Call parameters received from the client
|
||||
*/
|
||||
function onCall(ConnectionInterface $conn, $id, $topic, array $params);
|
||||
|
||||
/**
|
||||
* A request to subscribe to a topic has been made
|
||||
* @param \Ratchet\ConnectionInterface $conn
|
||||
* @param string|Topic $topic The topic to subscribe to
|
||||
*/
|
||||
function onSubscribe(ConnectionInterface $conn, $topic);
|
||||
|
||||
/**
|
||||
* A request to unsubscribe from a topic has been made
|
||||
* @param \Ratchet\ConnectionInterface $conn
|
||||
* @param string|Topic $topic The topic to unsubscribe from
|
||||
*/
|
||||
function onUnSubscribe(ConnectionInterface $conn, $topic);
|
||||
|
||||
/**
|
||||
* A client is attempting to publish content to a subscribed connections on a URI
|
||||
* @param \Ratchet\ConnectionInterface $conn
|
||||
* @param string|Topic $topic The topic the user has attempted to publish to
|
||||
* @param string $event Payload of the publish
|
||||
* @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)
|
||||
*/
|
||||
function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?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,8 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\WebSocket;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\RFC6455\Messaging\MessageInterface;
|
||||
|
||||
interface MessageCallableInterface {
|
||||
public function onMessage(ConnectionInterface $conn, MessageInterface $msg);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\WebSocket;
|
||||
use Ratchet\ComponentInterface;
|
||||
|
||||
interface MessageComponentInterface extends ComponentInterface, MessageCallableInterface {
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\WebSocket;
|
||||
use Ratchet\AbstractConnectionDecorator;
|
||||
use Ratchet\RFC6455\Messaging\DataInterface;
|
||||
use Ratchet\RFC6455\Messaging\Frame;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @property \StdClass $WebSocket
|
||||
*/
|
||||
class WsConnection extends AbstractConnectionDecorator {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function send($msg) {
|
||||
if (!$this->WebSocket->closing) {
|
||||
if (!($msg instanceof DataInterface)) {
|
||||
$msg = new Frame($msg);
|
||||
}
|
||||
|
||||
$this->getConnection()->send($msg->getContents());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|\Ratchet\RFC6455\Messaging\DataInterface
|
||||
*/
|
||||
public function close($code = 1000) {
|
||||
if ($this->WebSocket->closing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($code instanceof DataInterface) {
|
||||
$this->send($code);
|
||||
} else {
|
||||
$this->send(new Frame(pack('n', $code), true, Frame::OP_CLOSE));
|
||||
}
|
||||
|
||||
$this->getConnection()->close();
|
||||
|
||||
$this->WebSocket->closing = true;
|
||||
}
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\WebSocket;
|
||||
use Ratchet\ComponentInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\MessageComponentInterface as DataComponentInterface;
|
||||
use Ratchet\Http\HttpServerInterface;
|
||||
use Ratchet\Http\CloseResponseTrait;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Ratchet\RFC6455\Messaging\MessageInterface;
|
||||
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\Message;
|
||||
|
||||
/**
|
||||
* The adapter to handle WebSocket requests/responses
|
||||
* This is a mediator between the Server and your application to handle real-time messaging through a web browser
|
||||
* @link http://ca.php.net/manual/en/ref.http.php
|
||||
* @link http://dev.w3.org/html5/websockets/
|
||||
*/
|
||||
class WsServer implements HttpServerInterface {
|
||||
use CloseResponseTrait;
|
||||
|
||||
/**
|
||||
* Decorated component
|
||||
* @var \Ratchet\ComponentInterface
|
||||
*/
|
||||
private $delegate;
|
||||
|
||||
/**
|
||||
* @var \SplObjectStorage
|
||||
*/
|
||||
protected $connections;
|
||||
|
||||
/**
|
||||
* @var \Ratchet\RFC6455\Messaging\CloseFrameChecker
|
||||
*/
|
||||
private $closeFrameChecker;
|
||||
|
||||
/**
|
||||
* @var \Ratchet\RFC6455\Handshake\ServerNegotiator
|
||||
*/
|
||||
private $handshakeNegotiator;
|
||||
|
||||
/**
|
||||
* @var \Closure
|
||||
*/
|
||||
private $ueFlowFactory;
|
||||
|
||||
/**
|
||||
* @var \Closure
|
||||
*/
|
||||
private $pongReceiver;
|
||||
|
||||
/**
|
||||
* @var \Closure
|
||||
*/
|
||||
private $msgCb;
|
||||
|
||||
/**
|
||||
* @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->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;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||
if (null === $request) {
|
||||
throw new \UnexpectedValueException('$request can not be null');
|
||||
}
|
||||
|
||||
$conn->httpRequest = $request;
|
||||
|
||||
$conn->WebSocket = new \StdClass;
|
||||
$conn->WebSocket->closing = false;
|
||||
|
||||
$response = $this->handshakeNegotiator->handshake($request)->withHeader('X-Powered-By', \Ratchet\VERSION);
|
||||
|
||||
$conn->send(Message::toString($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onMessage(ConnectionInterface $from, $msg) {
|
||||
if ($from->WebSocket->closing) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->connections[$from]->buffer->onData($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
if ($this->connections->contains($conn)) {
|
||||
$context = $this->connections[$conn];
|
||||
$this->connections->detach($conn);
|
||||
|
||||
$this->delegate->onClose($context->connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
if ($this->connections->contains($conn)) {
|
||||
$this->delegate->onError($this->connections[$conn]->connection, $e);
|
||||
} else {
|
||||
$conn->close();
|
||||
}
|
||||
}
|
||||
|
||||
public function onControlFrame(FrameInterface $frame, WsConnection $conn) {
|
||||
switch ($frame->getOpCode()) {
|
||||
case Frame::OP_CLOSE:
|
||||
$conn->close($frame);
|
||||
break;
|
||||
case Frame::OP_PING:
|
||||
$conn->send(new Frame($frame->getPayload(), true, Frame::OP_PONG));
|
||||
break;
|
||||
case Frame::OP_PONG:
|
||||
$pongReceiver = $this->pongReceiver;
|
||||
$pongReceiver($frame, $conn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function setStrictSubProtocolCheck($enable) {
|
||||
$this->handshakeNegotiator->setStrictSubProtocolCheck($enable);
|
||||
}
|
||||
|
||||
public function enableKeepAlive(LoopInterface $loop, $interval = 30) {
|
||||
$lastPing = new Frame(uniqid(), true, Frame::OP_PING);
|
||||
$pingedConnections = new \SplObjectStorage;
|
||||
$splClearer = new \SplObjectStorage;
|
||||
|
||||
$this->pongReceiver = function(FrameInterface $frame, $wsConn) use ($pingedConnections, &$lastPing) {
|
||||
if ($frame->getPayload() === $lastPing->getPayload()) {
|
||||
$pingedConnections->detach($wsConn);
|
||||
}
|
||||
};
|
||||
|
||||
$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);
|
||||
|
||||
foreach ($this->connections as $key => $conn) {
|
||||
$wsConn = $this->connections[$conn]->connection;
|
||||
|
||||
$wsConn->send($lastPing);
|
||||
$pingedConnections->attach($wsConn);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\WebSocket;
|
||||
|
||||
/**
|
||||
* WebSocket Server Interface
|
||||
*/
|
||||
interface WsServerInterface {
|
||||
/**
|
||||
* If any component in a stack supports a WebSocket sub-protocol return each supported in an array
|
||||
* @return array
|
||||
* @todo This method may be removed in future version (note that will not break code, just make some code obsolete)
|
||||
*/
|
||||
function getSubProtocols();
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
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;
|
||||
$impl = sprintf('React\EventLoop\%sLoop', $argc > 2 ? $argv[2] : 'StreamSelect');
|
||||
|
||||
$loop = new $impl;
|
||||
$sock = new React\Socket\Server('0.0.0.0:' . $port, $loop);
|
||||
|
||||
$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->run();
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"options": {"failByDrop": false}
|
||||
, "outdir": "reports/ab"
|
||||
|
||||
, "servers": [
|
||||
{"agent": "Ratchet/0.4 libevent", "url": "ws://localhost:8001", "options": {"version": 18}}
|
||||
, {"agent": "Ratchet/0.4 libev", "url": "ws://localhost:8004", "options": {"version": 18}}
|
||||
, {"agent": "Ratchet/0.4 streams", "url": "ws://localhost:8002", "options": {"version": 18}}
|
||||
, {"agent": "AutobahnTestSuite/0.5.9", "url": "ws://localhost:8000", "options": {"version": 18}}
|
||||
]
|
||||
|
||||
, "cases": ["*"]
|
||||
, "exclude-cases": []
|
||||
, "exclude-agent-cases": {}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"options": {"failByDrop": false}
|
||||
, "outdir": "reports/profile"
|
||||
|
||||
, "servers": [
|
||||
{"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}}
|
||||
]
|
||||
|
||||
, "cases": ["9.7.4"]
|
||||
, "exclude-cases": ["1.2.*", "2.3", "2.4", "2.6", "9.2.*", "9.4.*", "9.6.*", "9.8.*"]
|
||||
, "exclude-agent-cases": {}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"options": {"failByDrop": false}
|
||||
, "outdir": "reports/rfc"
|
||||
|
||||
, "servers": [
|
||||
{"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}}
|
||||
]
|
||||
|
||||
, "cases": ["*"]
|
||||
, "exclude-cases": []
|
||||
, "exclude-agent-cases": {}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
<?php
|
||||
|
||||
$loader = require __DIR__ . '/../vendor/autoload.php';
|
||||
$loader->addPsr4('Ratchet\\', __DIR__ . '/helpers/Ratchet');
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet;
|
||||
|
||||
abstract class AbstractMessageComponentTestCase extends \PHPUnit_Framework_TestCase {
|
||||
protected $_app;
|
||||
protected $_serv;
|
||||
protected $_conn;
|
||||
|
||||
abstract public function getConnectionClassString();
|
||||
abstract public function getDecoratorClassString();
|
||||
abstract public function getComponentClassString();
|
||||
|
||||
public function setUp() {
|
||||
$this->_app = $this->getMock($this->getComponentClassString());
|
||||
$decorator = $this->getDecoratorClassString();
|
||||
$this->_serv = new $decorator($this->_app);
|
||||
$this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||
|
||||
$this->doOpen($this->_conn);
|
||||
}
|
||||
|
||||
protected function doOpen($conn) {
|
||||
$this->_serv->onOpen($conn);
|
||||
}
|
||||
|
||||
public function isExpectedConnection() {
|
||||
return new \PHPUnit_Framework_Constraint_IsInstanceOf($this->getConnectionClassString());
|
||||
}
|
||||
|
||||
public function testOpen() {
|
||||
$this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection());
|
||||
$this->doOpen($this->getMock('\Ratchet\ConnectionInterface'));
|
||||
}
|
||||
|
||||
public function testOnClose() {
|
||||
$this->_app->expects($this->once())->method('onClose')->with($this->isExpectedConnection());
|
||||
$this->_serv->onClose($this->_conn);
|
||||
}
|
||||
|
||||
public function testOnError() {
|
||||
$e = new \Exception('Whoops!');
|
||||
$this->_app->expects($this->once())->method('onError')->with($this->isExpectedConnection(), $e);
|
||||
$this->_serv->onError($this->_conn, $e);
|
||||
}
|
||||
|
||||
public function passthroughMessageTest($value) {
|
||||
$this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $value);
|
||||
$this->_serv->onMessage($this->_conn, $value);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Mock;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\WebSocket\WsServerInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
class Component implements MessageComponentInterface, WsServerInterface {
|
||||
public $last = array();
|
||||
|
||||
public $protocols = array();
|
||||
|
||||
public function __construct(ComponentInterface $app = null) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
|
||||
public function onOpen(ConnectionInterface $conn) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
|
||||
public function onMessage(ConnectionInterface $from, $msg) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
|
||||
public function getSubProtocols() {
|
||||
return $this->protocols;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Mock;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
class Connection implements ConnectionInterface {
|
||||
public $last = array(
|
||||
'send' => ''
|
||||
, 'close' => false
|
||||
);
|
||||
|
||||
public $remoteAddress = '127.0.0.1';
|
||||
|
||||
public function send($data) {
|
||||
$this->last[__FUNCTION__] = $data;
|
||||
}
|
||||
|
||||
public function close() {
|
||||
$this->last[__FUNCTION__] = true;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Mock;
|
||||
use Ratchet\AbstractConnectionDecorator;
|
||||
|
||||
class ConnectionDecorator extends AbstractConnectionDecorator {
|
||||
public $last = array(
|
||||
'write' => ''
|
||||
, 'end' => false
|
||||
);
|
||||
|
||||
public function send($data) {
|
||||
$this->last[__FUNCTION__] = $data;
|
||||
|
||||
$this->getConnection()->send($data);
|
||||
}
|
||||
|
||||
public function close() {
|
||||
$this->last[__FUNCTION__] = true;
|
||||
|
||||
$this->getConnection()->close();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Mock;
|
||||
use Ratchet\Wamp\WampServerInterface;
|
||||
use Ratchet\WebSocket\WsServerInterface;
|
||||
use Ratchet\ConnectionInterface;
|
||||
|
||||
class WampComponent implements WampServerInterface, WsServerInterface {
|
||||
public $last = array();
|
||||
|
||||
public $protocols = array();
|
||||
|
||||
public function getSubProtocols() {
|
||||
return $this->protocols;
|
||||
}
|
||||
|
||||
public function onCall(ConnectionInterface $conn, $id, $procURI, array $params) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
|
||||
public function onSubscribe(ConnectionInterface $conn, $topic) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
|
||||
public function onUnSubscribe(ConnectionInterface $conn, $topic) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
|
||||
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
|
||||
public function onOpen(ConnectionInterface $conn) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
$this->last[__FUNCTION__] = func_get_args();
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\WebSocket\WsServerInterface;
|
||||
use Ratchet\Wamp\WampServerInterface;
|
||||
|
||||
class NullComponent implements MessageComponentInterface, WsServerInterface, WampServerInterface {
|
||||
public function onOpen(ConnectionInterface $conn) {}
|
||||
|
||||
public function onMessage(ConnectionInterface $conn, $msg) {}
|
||||
|
||||
public function onClose(ConnectionInterface $conn) {}
|
||||
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {}
|
||||
|
||||
public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {}
|
||||
|
||||
public function onSubscribe(ConnectionInterface $conn, $topic) {}
|
||||
|
||||
public function onUnSubscribe(ConnectionInterface $conn, $topic) {}
|
||||
|
||||
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude = array(), array $eligible = array()) {}
|
||||
|
||||
public function getSubProtocols() {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp\Stub;
|
||||
use Ratchet\WebSocket\WsServerInterface;
|
||||
use Ratchet\Wamp\WampServerInterface;
|
||||
|
||||
interface WsWampServerInterface extends WsServerInterface, WampServerInterface {
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\WebSocket\Stub;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\WebSocket\WsServerInterface;
|
||||
|
||||
interface WsMessageComponentInterface extends MessageComponentInterface, WsServerInterface {
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet;
|
||||
use Ratchet\Mock\ConnectionDecorator;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\AbstractConnectionDecorator
|
||||
* @covers Ratchet\ConnectionInterface
|
||||
*/
|
||||
class AbstractConnectionDecoratorTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $mock;
|
||||
protected $l1;
|
||||
protected $l2;
|
||||
|
||||
public function setUp() {
|
||||
$this->mock = $this->getMock('\Ratchet\ConnectionInterface');
|
||||
$this->l1 = new ConnectionDecorator($this->mock);
|
||||
$this->l2 = new ConnectionDecorator($this->l1);
|
||||
}
|
||||
|
||||
public function testGet() {
|
||||
$var = 'hello';
|
||||
$val = 'world';
|
||||
|
||||
$this->mock->$var = $val;
|
||||
|
||||
$this->assertEquals($val, $this->l1->$var);
|
||||
$this->assertEquals($val, $this->l2->$var);
|
||||
}
|
||||
|
||||
public function testSet() {
|
||||
$var = 'Chris';
|
||||
$val = 'Boden';
|
||||
|
||||
$this->l1->$var = $val;
|
||||
|
||||
$this->assertEquals($val, $this->mock->$var);
|
||||
}
|
||||
|
||||
public function testSetLevel2() {
|
||||
$var = 'Try';
|
||||
$val = 'Again';
|
||||
|
||||
$this->l2->$var = $val;
|
||||
|
||||
$this->assertEquals($val, $this->mock->$var);
|
||||
}
|
||||
|
||||
public function testIsSetTrue() {
|
||||
$var = 'PHP';
|
||||
$val = 'Ratchet';
|
||||
|
||||
$this->mock->$var = $val;
|
||||
|
||||
$this->assertTrue(isset($this->l1->$var));
|
||||
$this->assertTrue(isset($this->l2->$var));
|
||||
}
|
||||
|
||||
public function testIsSetFalse() {
|
||||
$var = 'herp';
|
||||
$val = 'derp';
|
||||
|
||||
$this->assertFalse(isset($this->l1->$var));
|
||||
$this->assertFalse(isset($this->l2->$var));
|
||||
}
|
||||
|
||||
public function testUnset() {
|
||||
$var = 'Flying';
|
||||
$val = 'Monkey';
|
||||
|
||||
$this->mock->$var = $val;
|
||||
unset($this->l1->$var);
|
||||
|
||||
$this->assertFalse(isset($this->mock->$var));
|
||||
}
|
||||
|
||||
public function testUnsetLevel2() {
|
||||
$var = 'Flying';
|
||||
$val = 'Monkey';
|
||||
|
||||
$this->mock->$var = $val;
|
||||
unset($this->l2->$var);
|
||||
|
||||
$this->assertFalse(isset($this->mock->$var));
|
||||
}
|
||||
|
||||
public function testGetConnection() {
|
||||
$class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator');
|
||||
$method = $class->getMethod('getConnection');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$conn = $method->invokeArgs($this->l1, array());
|
||||
|
||||
$this->assertSame($this->mock, $conn);
|
||||
}
|
||||
|
||||
public function testGetConnectionLevel2() {
|
||||
$class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator');
|
||||
$method = $class->getMethod('getConnection');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$conn = $method->invokeArgs($this->l2, array());
|
||||
|
||||
$this->assertSame($this->l1, $conn);
|
||||
}
|
||||
|
||||
public function testWrapperCanStoreSelfInDecorator() {
|
||||
$this->mock->decorator = $this->l1;
|
||||
|
||||
$this->assertSame($this->l1, $this->l2->decorator);
|
||||
}
|
||||
|
||||
public function testDecoratorRecursion() {
|
||||
$this->mock->decorator = new \stdClass;
|
||||
$this->mock->decorator->conn = $this->l1;
|
||||
|
||||
$this->assertSame($this->l1, $this->mock->decorator->conn);
|
||||
$this->assertSame($this->l1, $this->l1->decorator->conn);
|
||||
$this->assertSame($this->l1, $this->l2->decorator->conn);
|
||||
}
|
||||
|
||||
public function testDecoratorRecursionLevel2() {
|
||||
$this->mock->decorator = new \stdClass;
|
||||
$this->mock->decorator->conn = $this->l2;
|
||||
|
||||
$this->assertSame($this->l2, $this->mock->decorator->conn);
|
||||
$this->assertSame($this->l2, $this->l1->decorator->conn);
|
||||
$this->assertSame($this->l2, $this->l2->decorator->conn);
|
||||
|
||||
// just for fun
|
||||
$this->assertSame($this->l2, $this->l2->decorator->conn->decorator->conn->decorator->conn);
|
||||
}
|
||||
|
||||
public function testWarningGettingNothing() {
|
||||
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||
$var = $this->mock->nonExistant;
|
||||
}
|
||||
|
||||
public function testWarningGettingNothingLevel1() {
|
||||
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||
$var = $this->l1->nonExistant;
|
||||
}
|
||||
|
||||
public function testWarningGettingNothingLevel2() {
|
||||
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||
$var = $this->l2->nonExistant;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Http;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Http\HttpRequestParser
|
||||
*/
|
||||
class HttpRequestParserTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $parser;
|
||||
|
||||
public function setUp() {
|
||||
$this->parser = new HttpRequestParser;
|
||||
}
|
||||
|
||||
public function headersProvider() {
|
||||
return array(
|
||||
array(false, "GET / HTTP/1.1\r\nHost: socketo.me\r\n")
|
||||
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n")
|
||||
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n1")
|
||||
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖")
|
||||
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖\r\n\r\n")
|
||||
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie\r\n")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider headersProvider
|
||||
*/
|
||||
public function testIsEom($expected, $message) {
|
||||
$this->assertEquals($expected, $this->parser->isEom($message));
|
||||
}
|
||||
|
||||
public function testBufferOverflowResponse() {
|
||||
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||
|
||||
$this->parser->maxSize = 20;
|
||||
|
||||
$this->assertNull($this->parser->onMessage($conn, "GET / HTTP/1.1\r\n"));
|
||||
|
||||
$this->setExpectedException('OverflowException');
|
||||
|
||||
$this->parser->onMessage($conn, "Header-Is: Too Big");
|
||||
}
|
||||
|
||||
public function testReturnTypeIsRequest() {
|
||||
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||
$return = $this->parser->onMessage($conn, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n");
|
||||
|
||||
$this->assertInstanceOf('\Psr\Http\Message\RequestInterface', $return);
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Http;
|
||||
use Ratchet\AbstractMessageComponentTestCase;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Http\HttpServer
|
||||
*/
|
||||
class HttpServerTest extends AbstractMessageComponentTestCase {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->_conn->httpHeadersReceived = true;
|
||||
}
|
||||
|
||||
public function getConnectionClassString() {
|
||||
return '\Ratchet\ConnectionInterface';
|
||||
}
|
||||
|
||||
public function getDecoratorClassString() {
|
||||
return '\Ratchet\Http\HttpServer';
|
||||
}
|
||||
|
||||
public function getComponentClassString() {
|
||||
return '\Ratchet\Http\HttpServerInterface';
|
||||
}
|
||||
|
||||
public function testOpen() {
|
||||
$headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n";
|
||||
|
||||
$this->_conn->httpHeadersReceived = false;
|
||||
$this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection());
|
||||
$this->_serv->onMessage($this->_conn, $headers);
|
||||
}
|
||||
|
||||
public function testOnMessageAfterHeaders() {
|
||||
$headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n";
|
||||
$this->_conn->httpHeadersReceived = false;
|
||||
$this->_serv->onMessage($this->_conn, $headers);
|
||||
|
||||
$message = "Hello World!";
|
||||
$this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $message);
|
||||
$this->_serv->onMessage($this->_conn, $message);
|
||||
}
|
||||
|
||||
public function testBufferOverflow() {
|
||||
$this->_conn->expects($this->once())->method('close');
|
||||
$this->_conn->httpHeadersReceived = false;
|
||||
|
||||
$this->_serv->onMessage($this->_conn, str_repeat('a', 5000));
|
||||
}
|
||||
|
||||
public function testCloseIfNotEstablished() {
|
||||
$this->_conn->httpHeadersReceived = false;
|
||||
$this->_conn->expects($this->once())->method('close');
|
||||
$this->_serv->onError($this->_conn, new \Exception('Whoops!'));
|
||||
}
|
||||
|
||||
public function testBufferHeaders() {
|
||||
$this->_conn->httpHeadersReceived = false;
|
||||
$this->_app->expects($this->never())->method('onOpen');
|
||||
$this->_app->expects($this->never())->method('onMessage');
|
||||
|
||||
$this->_serv->onMessage($this->_conn, "GET / HTTP/1.1");
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Http;
|
||||
use Ratchet\AbstractMessageComponentTestCase;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Http\OriginCheck
|
||||
*/
|
||||
class OriginCheckTest extends AbstractMessageComponentTestCase {
|
||||
protected $_reqStub;
|
||||
|
||||
public function setUp() {
|
||||
$this->_reqStub = $this->getMock('Psr\Http\Message\RequestInterface');
|
||||
$this->_reqStub->expects($this->any())->method('getHeader')->will($this->returnValue(['localhost']));
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$this->_serv->allowedOrigins[] = 'localhost';
|
||||
}
|
||||
|
||||
protected function doOpen($conn) {
|
||||
$this->_serv->onOpen($conn, $this->_reqStub);
|
||||
}
|
||||
|
||||
public function getConnectionClassString() {
|
||||
return '\Ratchet\ConnectionInterface';
|
||||
}
|
||||
|
||||
public function getDecoratorClassString() {
|
||||
return '\Ratchet\Http\OriginCheck';
|
||||
}
|
||||
|
||||
public function getComponentClassString() {
|
||||
return '\Ratchet\Http\HttpServerInterface';
|
||||
}
|
||||
|
||||
public function testCloseOnNonMatchingOrigin() {
|
||||
$this->_serv->allowedOrigins = ['socketo.me'];
|
||||
$this->_conn->expects($this->once())->method('close');
|
||||
|
||||
$this->_serv->onOpen($this->_conn, $this->_reqStub);
|
||||
}
|
||||
|
||||
public function testOnMessage() {
|
||||
$this->passthroughMessageTest('Hello World!');
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Http;
|
||||
use Ratchet\WebSocket\WsServerInterface;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
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
|
||||
*/
|
||||
class RouterTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $_router;
|
||||
protected $_matcher;
|
||||
protected $_conn;
|
||||
protected $_uri;
|
||||
protected $_req;
|
||||
|
||||
public function setUp() {
|
||||
$this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||
$this->_uri = $this->getMock('Psr\Http\Message\UriInterface');
|
||||
$this->_req = $this->getMock('\Psr\Http\Message\RequestInterface');
|
||||
$this->_req
|
||||
->expects($this->any())
|
||||
->method('getUri')
|
||||
->will($this->returnValue($this->_uri));
|
||||
$this->_matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
|
||||
$this->_matcher
|
||||
->expects($this->any())
|
||||
->method('getContext')
|
||||
->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext')));
|
||||
$this->_router = new Router($this->_matcher);
|
||||
|
||||
$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() {
|
||||
$this->_conn->expects($this->once())->method('close');
|
||||
|
||||
$nope = new ResourceNotFoundException;
|
||||
$this->_matcher->expects($this->any())->method('match')->will($this->throwException($nope));
|
||||
|
||||
$this->_router->onOpen($this->_conn, $this->_req);
|
||||
}
|
||||
|
||||
public function testNullRequest() {
|
||||
$this->setExpectedException('\UnexpectedValueException');
|
||||
$this->_router->onOpen($this->_conn);
|
||||
}
|
||||
|
||||
public function testControllerIsMessageComponentInterface() {
|
||||
$this->setExpectedException('\UnexpectedValueException');
|
||||
$this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => new \StdClass)));
|
||||
$this->_router->onOpen($this->_conn, $this->_req);
|
||||
}
|
||||
|
||||
public function testControllerOnOpen() {
|
||||
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||
$this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => $controller)));
|
||||
$this->_router->onOpen($this->_conn, $this->_req);
|
||||
|
||||
$expectedConn = new \PHPUnit_Framework_Constraint_IsInstanceOf('\Ratchet\ConnectionInterface');
|
||||
$controller->expects($this->once())->method('onOpen')->with($expectedConn, $this->_req);
|
||||
|
||||
$this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => $controller)));
|
||||
$this->_router->onOpen($this->_conn, $this->_req);
|
||||
}
|
||||
|
||||
public function testControllerOnMessageBubbles() {
|
||||
$message = "The greatest trick the Devil ever pulled was convincing the world he didn't exist";
|
||||
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||
$controller->expects($this->once())->method('onMessage')->with($this->_conn, $message);
|
||||
|
||||
$this->_conn->controller = $controller;
|
||||
|
||||
$this->_router->onMessage($this->_conn, $message);
|
||||
}
|
||||
|
||||
public function testControllerOnCloseBubbles() {
|
||||
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||
$controller->expects($this->once())->method('onClose')->with($this->_conn);
|
||||
|
||||
$this->_conn->controller = $controller;
|
||||
|
||||
$this->_router->onClose($this->_conn);
|
||||
}
|
||||
|
||||
public function testControllerOnErrorBubbles() {
|
||||
$e= new \Exception('One cannot be betrayed if one has no exceptions');
|
||||
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||
$controller->expects($this->once())->method('onError')->with($this->_conn, $e);
|
||||
|
||||
$this->_conn->controller = $controller;
|
||||
|
||||
$this->_router->onError($this->_conn, $e);
|
||||
}
|
||||
|
||||
public function testRouterGeneratesRouteParameters() {
|
||||
/** @var $controller WsServerInterface */
|
||||
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||
/** @var $matcher UrlMatcherInterface */
|
||||
$this->_matcher->expects($this->any())->method('match')->will(
|
||||
$this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux'])
|
||||
);
|
||||
$conn = $this->getMock('Ratchet\Mock\Connection');
|
||||
|
||||
$router = new Router($this->_matcher);
|
||||
|
||||
$router->onOpen($conn, $this->_req);
|
||||
|
||||
$this->assertEquals('foo=bar&baz=qux', $this->_req->getUri()->getQuery());
|
||||
}
|
||||
|
||||
public function testQueryParams() {
|
||||
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||
$this->_matcher->expects($this->any())->method('match')->will(
|
||||
$this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux'])
|
||||
);
|
||||
|
||||
$conn = $this->getMock('Ratchet\Mock\Connection');
|
||||
$request = $this->getMock('Psr\Http\Message\RequestInterface');
|
||||
$uri = new \GuzzleHttp\Psr7\Uri('ws://doesnt.matter/endpoint?hello=world&foo=nope');
|
||||
|
||||
$request->expects($this->any())->method('getUri')->will($this->returnCallback(function() use (&$uri) {
|
||||
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->onOpen($conn, $request);
|
||||
|
||||
$this->assertEquals('foo=nope&baz=qux&hello=world', $request->getUri()->getQuery());
|
||||
$this->assertEquals('ws', $request->getUri()->getScheme());
|
||||
$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');
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Server;
|
||||
use Ratchet\Server\EchoServer;
|
||||
|
||||
class EchoServerTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $_conn;
|
||||
protected $_comp;
|
||||
|
||||
public function setUp() {
|
||||
$this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||
$this->_comp = new EchoServer;
|
||||
}
|
||||
|
||||
public function testMessageEchod() {
|
||||
$message = 'Tillsonburg, my back still aches when I hear that word.';
|
||||
$this->_conn->expects($this->once())->method('send')->with($message);
|
||||
$this->_comp->onMessage($this->_conn, $message);
|
||||
}
|
||||
|
||||
public function testErrorClosesConnection() {
|
||||
ob_start();
|
||||
$this->_conn->expects($this->once())->method('close');
|
||||
$this->_comp->onError($this->_conn, new \Exception);
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Application\Server;
|
||||
use Ratchet\Server\FlashPolicy;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Server\FlashPolicy
|
||||
*/
|
||||
class FlashPolicyTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
protected $_policy;
|
||||
|
||||
public function setUp() {
|
||||
$this->_policy = new FlashPolicy();
|
||||
}
|
||||
|
||||
public function testPolicyRender() {
|
||||
$this->_policy->setSiteControl('all');
|
||||
$this->_policy->addAllowedAccess('example.com', '*');
|
||||
$this->_policy->addAllowedAccess('dev.example.com', '*');
|
||||
|
||||
$this->assertInstanceOf('SimpleXMLElement', $this->_policy->renderPolicy());
|
||||
}
|
||||
|
||||
public function testInvalidPolicyReader() {
|
||||
$this->setExpectedException('UnexpectedValueException');
|
||||
$this->_policy->renderPolicy();
|
||||
}
|
||||
|
||||
public function testInvalidDomainPolicyReader() {
|
||||
$this->setExpectedException('UnexpectedValueException');
|
||||
$this->_policy->setSiteControl('all');
|
||||
$this->_policy->addAllowedAccess('dev.example.*', '*');
|
||||
$this->_policy->renderPolicy();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider siteControl
|
||||
*/
|
||||
public function testSiteControlValidation($accept, $permittedCrossDomainPolicies) {
|
||||
$this->assertEquals($accept, $this->_policy->validateSiteControl($permittedCrossDomainPolicies));
|
||||
}
|
||||
|
||||
public static function siteControl() {
|
||||
return array(
|
||||
array(true, 'all')
|
||||
, array(true, 'none')
|
||||
, array(true, 'master-only')
|
||||
, array(false, 'by-content-type')
|
||||
, array(false, 'by-ftp-filename')
|
||||
, array(false, '')
|
||||
, array(false, 'all ')
|
||||
, array(false, 'asdf')
|
||||
, array(false, '@893830')
|
||||
, array(false, '*')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider URI
|
||||
*/
|
||||
public function testDomainValidation($accept, $domain) {
|
||||
$this->assertEquals($accept, $this->_policy->validateDomain($domain));
|
||||
}
|
||||
|
||||
public static function URI() {
|
||||
return array(
|
||||
array(true, '*')
|
||||
, array(true, 'example.com')
|
||||
, array(true, 'exam-ple.com')
|
||||
, array(true, '*.example.com')
|
||||
, array(true, 'www.example.com')
|
||||
, array(true, 'dev.dev.example.com')
|
||||
, array(true, 'http://example.com')
|
||||
, array(true, 'https://example.com')
|
||||
, array(true, 'http://*.example.com')
|
||||
, array(false, 'exam*ple.com')
|
||||
, array(true, '127.0.255.1')
|
||||
, array(true, 'localhost')
|
||||
, array(false, 'www.example.*')
|
||||
, array(false, 'www.exa*le.com')
|
||||
, array(false, 'www.example.*com')
|
||||
, array(false, '*.example.*')
|
||||
, array(false, 'gasldf*$#a0sdf0a8sdf')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ports
|
||||
*/
|
||||
public function testPortValidation($accept, $ports) {
|
||||
$this->assertEquals($accept, $this->_policy->validatePorts($ports));
|
||||
}
|
||||
|
||||
public static function ports() {
|
||||
return array(
|
||||
array(true, '*')
|
||||
, array(true, '80')
|
||||
, array(true, '80,443')
|
||||
, array(true, '507,516-523')
|
||||
, array(true, '507,516-523,333')
|
||||
, array(true, '507,516-523,507,516-523')
|
||||
, array(false, '516-')
|
||||
, array(true, '516-523,11')
|
||||
, array(false, '516,-523,11')
|
||||
, array(false, 'example')
|
||||
, array(false, 'asdf,123')
|
||||
, array(false, '--')
|
||||
, array(false, ',,,')
|
||||
, array(false, '838*')
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddAllowedAccessOnlyAcceptsValidPorts() {
|
||||
$this->setExpectedException('UnexpectedValueException');
|
||||
|
||||
$this->_policy->addAllowedAccess('*', 'nope');
|
||||
}
|
||||
|
||||
public function testSetSiteControlThrowsException() {
|
||||
$this->setExpectedException('UnexpectedValueException');
|
||||
|
||||
$this->_policy->setSiteControl('nope');
|
||||
}
|
||||
|
||||
public function testErrorClosesConnection() {
|
||||
$conn = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||
$conn->expects($this->once())->method('close');
|
||||
|
||||
$this->_policy->onError($conn, new \Exception);
|
||||
}
|
||||
|
||||
public function testOnMessageSendsString() {
|
||||
$this->_policy->addAllowedAccess('*', '*');
|
||||
|
||||
$conn = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||
$conn->expects($this->once())->method('send')->with($this->isType('string'));
|
||||
|
||||
$this->_policy->onMessage($conn, ' ');
|
||||
}
|
||||
|
||||
public function testOnOpenExists() {
|
||||
$this->assertTrue(method_exists($this->_policy, 'onOpen'));
|
||||
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||
$this->_policy->onOpen($conn);
|
||||
}
|
||||
|
||||
public function testOnCloseExists() {
|
||||
$this->assertTrue(method_exists($this->_policy, 'onClose'));
|
||||
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||
$this->_policy->onClose($conn);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Application\Server;
|
||||
use Ratchet\Server\IoConnection;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Server\IoConnection
|
||||
*/
|
||||
class IoConnectionTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $sock;
|
||||
protected $conn;
|
||||
|
||||
public function setUp() {
|
||||
$this->sock = $this->getMock('\\React\\Socket\\ConnectionInterface');
|
||||
$this->conn = new IoConnection($this->sock);
|
||||
}
|
||||
|
||||
public function testCloseBubbles() {
|
||||
$this->sock->expects($this->once())->method('end');
|
||||
$this->conn->close();
|
||||
}
|
||||
|
||||
public function testSendBubbles() {
|
||||
$msg = '6 hour rides are productive';
|
||||
|
||||
$this->sock->expects($this->once())->method('write')->with($msg);
|
||||
$this->conn->send($msg);
|
||||
}
|
||||
|
||||
public function testSendReturnsSelf() {
|
||||
$this->assertSame($this->conn, $this->conn->send('fluent interface'));
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Server;
|
||||
use Ratchet\Server\IoServer;
|
||||
use React\EventLoop\StreamSelectLoop;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\Socket\Server;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Server\IoServer
|
||||
*/
|
||||
class IoServerTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $server;
|
||||
|
||||
protected $app;
|
||||
|
||||
protected $port;
|
||||
|
||||
protected $reactor;
|
||||
|
||||
protected function tickLoop(LoopInterface $loop) {
|
||||
$loop->futureTick(function () use ($loop) {
|
||||
$loop->stop();
|
||||
});
|
||||
|
||||
$loop->run();
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
$this->app = $this->getMock('\\Ratchet\\MessageComponentInterface');
|
||||
|
||||
$loop = new StreamSelectLoop;
|
||||
$this->reactor = new Server(0, $loop);
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
public function testOnOpen() {
|
||||
$this->app->expects($this->once())->method('onOpen')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'));
|
||||
|
||||
$client = stream_socket_client("tcp://localhost:{$this->port}");
|
||||
|
||||
$this->tickLoop($this->server->loop);
|
||||
|
||||
//$this->assertTrue(is_string($this->app->last['onOpen'][0]->remoteAddress));
|
||||
//$this->assertTrue(is_int($this->app->last['onOpen'][0]->resourceId));
|
||||
}
|
||||
|
||||
public function testOnData() {
|
||||
$msg = 'Hello World!';
|
||||
|
||||
$this->app->expects($this->once())->method('onMessage')->with(
|
||||
$this->isInstanceOf('\\Ratchet\\ConnectionInterface')
|
||||
, $msg
|
||||
);
|
||||
|
||||
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||
socket_set_option($client, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||
socket_set_option($client, SOL_SOCKET, SO_SNDBUF, 4096);
|
||||
socket_set_block($client);
|
||||
socket_connect($client, 'localhost', $this->port);
|
||||
|
||||
$this->tickLoop($this->server->loop);
|
||||
|
||||
socket_write($client, $msg);
|
||||
$this->tickLoop($this->server->loop);
|
||||
|
||||
socket_shutdown($client, 1);
|
||||
socket_shutdown($client, 0);
|
||||
socket_close($client);
|
||||
|
||||
$this->tickLoop($this->server->loop);
|
||||
}
|
||||
|
||||
public function testOnClose() {
|
||||
$this->app->expects($this->once())->method('onClose')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'));
|
||||
|
||||
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||
socket_set_option($client, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||
socket_set_option($client, SOL_SOCKET, SO_SNDBUF, 4096);
|
||||
socket_set_block($client);
|
||||
socket_connect($client, 'localhost', $this->port);
|
||||
|
||||
$this->tickLoop($this->server->loop);
|
||||
|
||||
socket_shutdown($client, 1);
|
||||
socket_shutdown($client, 0);
|
||||
socket_close($client);
|
||||
|
||||
$this->tickLoop($this->server->loop);
|
||||
}
|
||||
|
||||
public function testFactory() {
|
||||
$this->assertInstanceOf('\\Ratchet\\Server\\IoServer', IoServer::factory($this->app, 0));
|
||||
}
|
||||
|
||||
public function testNoLoopProvidedError() {
|
||||
$this->setExpectedException('RuntimeException');
|
||||
|
||||
$io = new IoServer($this->app, $this->reactor);
|
||||
$io->run();
|
||||
}
|
||||
|
||||
public function testOnErrorPassesException() {
|
||||
$conn = $this->getMock('\\React\\Socket\\ConnectionInterface');
|
||||
$conn->decor = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||
$err = new \Exception("Nope");
|
||||
|
||||
$this->app->expects($this->once())->method('onError')->with($conn->decor, $err);
|
||||
|
||||
$this->server->handleError($err, $conn);
|
||||
}
|
||||
|
||||
public function onErrorCalledWhenExceptionThrown() {
|
||||
$this->markTestIncomplete("Need to learn how to throw an exception from a mock");
|
||||
|
||||
$conn = $this->getMock('\\React\\Socket\\ConnectionInterface');
|
||||
$this->server->handleConnect($conn);
|
||||
|
||||
$e = new \Exception;
|
||||
$this->app->expects($this->once())->method('onMessage')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'), 'f')->will($e);
|
||||
$this->app->expects($this->once())->method('onError')->with($this->instanceOf('\\Ratchet\\ConnectionInterface', $e));
|
||||
|
||||
$this->server->handleData('f', $conn);
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Server;
|
||||
use Ratchet\Server\IpBlackList;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Server\IpBlackList
|
||||
*/
|
||||
class IpBlackListTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $blocker;
|
||||
protected $mock;
|
||||
|
||||
public function setUp() {
|
||||
$this->mock = $this->getMock('\\Ratchet\\MessageComponentInterface');
|
||||
$this->blocker = new IpBlackList($this->mock);
|
||||
}
|
||||
|
||||
public function testOnOpen() {
|
||||
$this->mock->expects($this->exactly(3))->method('onOpen');
|
||||
|
||||
$conn1 = $this->newConn();
|
||||
$conn2 = $this->newConn();
|
||||
$conn3 = $this->newConn();
|
||||
|
||||
$this->blocker->onOpen($conn1);
|
||||
$this->blocker->onOpen($conn3);
|
||||
$this->blocker->onOpen($conn2);
|
||||
}
|
||||
|
||||
public function testBlockDoesNotTriggerOnOpen() {
|
||||
$conn = $this->newConn();
|
||||
|
||||
$this->blocker->blockAddress($conn->remoteAddress);
|
||||
|
||||
$this->mock->expects($this->never())->method('onOpen');
|
||||
|
||||
$ret = $this->blocker->onOpen($conn);
|
||||
}
|
||||
|
||||
public function testBlockDoesNotTriggerOnClose() {
|
||||
$conn = $this->newConn();
|
||||
|
||||
$this->blocker->blockAddress($conn->remoteAddress);
|
||||
|
||||
$this->mock->expects($this->never())->method('onClose');
|
||||
|
||||
$ret = $this->blocker->onOpen($conn);
|
||||
}
|
||||
|
||||
public function testOnMessageDecoration() {
|
||||
$conn = $this->newConn();
|
||||
$msg = 'Hello not being blocked';
|
||||
|
||||
$this->mock->expects($this->once())->method('onMessage')->with($conn, $msg);
|
||||
|
||||
$this->blocker->onMessage($conn, $msg);
|
||||
}
|
||||
|
||||
public function testOnCloseDecoration() {
|
||||
$conn = $this->newConn();
|
||||
|
||||
$this->mock->expects($this->once())->method('onClose')->with($conn);
|
||||
|
||||
$this->blocker->onClose($conn);
|
||||
}
|
||||
|
||||
public function testBlockClosesConnection() {
|
||||
$conn = $this->newConn();
|
||||
$this->blocker->blockAddress($conn->remoteAddress);
|
||||
|
||||
$conn->expects($this->once())->method('close');
|
||||
|
||||
$this->blocker->onOpen($conn);
|
||||
}
|
||||
|
||||
public function testAddAndRemoveWithFluentInterfaces() {
|
||||
$blockOne = '127.0.0.1';
|
||||
$blockTwo = '192.168.1.1';
|
||||
$unblock = '75.119.207.140';
|
||||
|
||||
$this->blocker
|
||||
->blockAddress($unblock)
|
||||
->blockAddress($blockOne)
|
||||
->unblockAddress($unblock)
|
||||
->blockAddress($blockTwo)
|
||||
;
|
||||
|
||||
$this->assertEquals(array($blockOne, $blockTwo), $this->blocker->getBlockedAddresses());
|
||||
}
|
||||
|
||||
public function testDecoratorPassesErrors() {
|
||||
$conn = $this->newConn();
|
||||
$e = new \Exception('I threw an error');
|
||||
|
||||
$this->mock->expects($this->once())->method('onError')->with($conn, $e);
|
||||
|
||||
$this->blocker->onError($conn, $e);
|
||||
}
|
||||
|
||||
public function addressProvider() {
|
||||
return array(
|
||||
array('127.0.0.1', '127.0.0.1')
|
||||
, array('localhost', 'localhost')
|
||||
, array('fe80::1%lo0', 'fe80::1%lo0')
|
||||
, array('127.0.0.1', '127.0.0.1:6392')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider addressProvider
|
||||
*/
|
||||
public function testFilterAddress($expected, $input) {
|
||||
$this->assertEquals($expected, $this->blocker->filterAddress($input));
|
||||
}
|
||||
|
||||
public function testUnblockingSilentlyFails() {
|
||||
$this->assertInstanceOf('\\Ratchet\\Server\\IpBlackList', $this->blocker->unblockAddress('localhost'));
|
||||
}
|
||||
|
||||
protected function newConn() {
|
||||
$conn = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||
$conn->remoteAddress = '127.0.0.1';
|
||||
|
||||
return $conn;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Session\Serialize;
|
||||
use Ratchet\Session\Serialize\PhpHandler;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Session\Serialize\PhpHandler
|
||||
*/
|
||||
class PhpHandlerTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $_handler;
|
||||
|
||||
public function setUp() {
|
||||
$this->_handler = new PhpHandler;
|
||||
}
|
||||
|
||||
public function serializedProvider() {
|
||||
return array(
|
||||
array(
|
||||
'_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}'
|
||||
, array(
|
||||
'_sf2_attributes' => array(
|
||||
'hello' => 'world'
|
||||
, 'last' => 1332872102
|
||||
)
|
||||
, '_sf2_flashes' => array()
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider serializedProvider
|
||||
*/
|
||||
public function testUnserialize($in, $expected) {
|
||||
$this->assertEquals($expected, $this->_handler->unserialize($in));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider serializedProvider
|
||||
*/
|
||||
public function testSerialize($serialized, $original) {
|
||||
$this->assertEquals($serialized, $this->_handler->serialize($original));
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Session;
|
||||
use Ratchet\AbstractMessageComponentTestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Session\SessionProvider
|
||||
* @covers Ratchet\Session\Storage\VirtualSessionStorage
|
||||
* @covers Ratchet\Session\Storage\Proxy\VirtualProxy
|
||||
*/
|
||||
class SessionProviderTest extends AbstractMessageComponentTestCase {
|
||||
public function setUp() {
|
||||
return $this->markTestIncomplete('Test needs to be updated for ini_set issue in PHP 7.2');
|
||||
|
||||
if (!class_exists('Symfony\Component\HttpFoundation\Session\Session')) {
|
||||
return $this->markTestSkipped('Dependency of Symfony HttpFoundation failed');
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
$this->_serv = new SessionProvider($this->_app, new NullSessionHandler);
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
ini_set('session.serialize_handler', 'php');
|
||||
}
|
||||
|
||||
public function getConnectionClassString() {
|
||||
return '\Ratchet\ConnectionInterface';
|
||||
}
|
||||
|
||||
public function getDecoratorClassString() {
|
||||
return '\Ratchet\NullComponent';
|
||||
}
|
||||
|
||||
public function getComponentClassString() {
|
||||
return '\Ratchet\Http\HttpServerInterface';
|
||||
}
|
||||
|
||||
public function classCaseProvider() {
|
||||
return array(
|
||||
array('php', 'Php')
|
||||
, array('php_binary', 'PhpBinary')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider classCaseProvider
|
||||
*/
|
||||
public function testToClassCase($in, $out) {
|
||||
$ref = new \ReflectionClass('\\Ratchet\\Session\\SessionProvider');
|
||||
$method = $ref->getMethod('toClassCase');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$component = new SessionProvider($this->getMock($this->getComponentClassString()), $this->getMock('\SessionHandlerInterface'));
|
||||
$this->assertEquals($out, $method->invokeArgs($component, array($in)));
|
||||
}
|
||||
|
||||
/**
|
||||
* I think I have severely butchered this test...it's not so much of a unit test as it is a full-fledged component test
|
||||
*/
|
||||
public function testConnectionValueFromPdo() {
|
||||
if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
|
||||
return $this->markTestSkipped('Session test requires PDO and pdo_sqlite');
|
||||
}
|
||||
|
||||
$sessionId = md5('testSession');
|
||||
|
||||
$dbOptions = array(
|
||||
'db_table' => 'sessions'
|
||||
, 'db_id_col' => 'sess_id'
|
||||
, 'db_data_col' => 'sess_data'
|
||||
, 'db_time_col' => 'sess_time'
|
||||
, 'db_lifetime_col' => 'sess_lifetime'
|
||||
);
|
||||
|
||||
$pdo = new \PDO("sqlite::memory:");
|
||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->exec(vsprintf("CREATE TABLE %s (%s TEXT NOT NULL PRIMARY KEY, %s BLOB NOT NULL, %s INTEGER NOT NULL, %s INTEGER)", $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:{}');
|
||||
|
||||
$component = new SessionProvider($this->getMock($this->getComponentClassString()), $pdoHandler, array('auto_start' => 1));
|
||||
$connection = $this->getMock('Ratchet\\ConnectionInterface');
|
||||
|
||||
$headers = $this->getMock('Psr\Http\Message\RequestInterface');
|
||||
$headers->expects($this->once())->method('getHeader')->will($this->returnValue([ini_get('session.name') . "={$sessionId};"]));
|
||||
|
||||
$component->onOpen($connection, $headers);
|
||||
|
||||
$this->assertEquals('world', $connection->Session->get('hello'));
|
||||
}
|
||||
|
||||
protected function newConn() {
|
||||
$conn = $this->getMock('Ratchet\ConnectionInterface');
|
||||
|
||||
$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));
|
||||
|
||||
return $conn;
|
||||
}
|
||||
|
||||
public function testOnMessageDecorator() {
|
||||
$message = "Database calls are usually blocking :(";
|
||||
$this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $message);
|
||||
$this->_serv->onMessage($this->_conn, $message);
|
||||
}
|
||||
|
||||
public function testRejectInvalidSeralizers() {
|
||||
if (!function_exists('wddx_serialize_value')) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
ini_set('session.serialize_handler', 'wddx');
|
||||
$this->setExpectedException('\RuntimeException');
|
||||
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,53 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Session\Storage;
|
||||
use Ratchet\Session\Serialize\PhpHandler;
|
||||
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
||||
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
|
||||
|
||||
class VirtualSessionStoragePDOTest extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @var VirtualSessionStorage
|
||||
*/
|
||||
protected $_virtualSessionStorage;
|
||||
|
||||
protected $_pathToDB;
|
||||
|
||||
public function setUp() {
|
||||
if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
|
||||
return $this->markTestSkipped('Session test requires PDO and pdo_sqlite');
|
||||
}
|
||||
|
||||
$schema = <<<SQL
|
||||
CREATE TABLE `sessions` (
|
||||
`sess_id` VARBINARY(128) NOT NULL PRIMARY KEY,
|
||||
`sess_data` BLOB NOT NULL,
|
||||
`sess_time` INTEGER UNSIGNED NOT NULL,
|
||||
`sess_lifetime` MEDIUMINT NOT NULL
|
||||
);
|
||||
SQL;
|
||||
$this->_pathToDB = tempnam(sys_get_temp_dir(), 'SQ3');;
|
||||
$dsn = 'sqlite:' . $this->_pathToDB;
|
||||
|
||||
$pdo = new \PDO($dsn);
|
||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->exec($schema);
|
||||
$pdo = null;
|
||||
|
||||
$sessionHandler = new PdoSessionHandler($dsn);
|
||||
$serializer = new PhpHandler();
|
||||
$this->_virtualSessionStorage = new VirtualSessionStorage($sessionHandler, 'foobar', $serializer);
|
||||
$this->_virtualSessionStorage->registerBag(new FlashBag());
|
||||
$this->_virtualSessionStorage->registerBag(new AttributeBag());
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
unlink($this->_pathToDB);
|
||||
}
|
||||
|
||||
public function testStartWithDSN() {
|
||||
$this->_virtualSessionStorage->start();
|
||||
|
||||
$this->assertTrue($this->_virtualSessionStorage->isStarted());
|
||||
}
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
use Ratchet\Mock\Connection;
|
||||
use Ratchet\Mock\WampComponent as TestComponent;
|
||||
|
||||
/**
|
||||
* @covers \Ratchet\Wamp\ServerProtocol
|
||||
* @covers \Ratchet\Wamp\WampServerInterface
|
||||
* @covers \Ratchet\Wamp\WampConnection
|
||||
*/
|
||||
class ServerProtocolTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $_comp;
|
||||
|
||||
protected $_app;
|
||||
|
||||
public function setUp() {
|
||||
$this->_app = new TestComponent;
|
||||
$this->_comp = new ServerProtocol($this->_app);
|
||||
}
|
||||
|
||||
protected function newConn() {
|
||||
return new Connection;
|
||||
}
|
||||
|
||||
public function invalidMessageProvider() {
|
||||
return [
|
||||
[0]
|
||||
, [3]
|
||||
, [4]
|
||||
, [8]
|
||||
, [9]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidMessageProvider
|
||||
*/
|
||||
public function testInvalidMessages($type) {
|
||||
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||
|
||||
$conn = $this->newConn();
|
||||
$this->_comp->onOpen($conn);
|
||||
$this->_comp->onMessage($conn, json_encode([$type]));
|
||||
}
|
||||
|
||||
public function testWelcomeMessage() {
|
||||
$conn = $this->newConn();
|
||||
|
||||
$this->_comp->onOpen($conn);
|
||||
|
||||
$message = $conn->last['send'];
|
||||
$json = json_decode($message);
|
||||
|
||||
$this->assertEquals(4, count($json));
|
||||
$this->assertEquals(0, $json[0]);
|
||||
$this->assertTrue(is_string($json[1]));
|
||||
$this->assertEquals(1, $json[2]);
|
||||
}
|
||||
|
||||
public function testSubscribe() {
|
||||
$uri = 'http://example.com';
|
||||
$clientMessage = array(5, $uri);
|
||||
|
||||
$conn = $this->newConn();
|
||||
|
||||
$this->_comp->onOpen($conn);
|
||||
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||
|
||||
$this->assertEquals($uri, $this->_app->last['onSubscribe'][1]);
|
||||
}
|
||||
|
||||
public function testUnSubscribe() {
|
||||
$uri = 'http://example.com/endpoint';
|
||||
$clientMessage = array(6, $uri);
|
||||
|
||||
$conn = $this->newConn();
|
||||
|
||||
$this->_comp->onOpen($conn);
|
||||
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||
|
||||
$this->assertEquals($uri, $this->_app->last['onUnSubscribe'][1]);
|
||||
}
|
||||
|
||||
public function callProvider() {
|
||||
return [
|
||||
[2, 'a', 'b']
|
||||
, [2, ['a', 'b']]
|
||||
, [1, 'one']
|
||||
, [3, 'one', 'two', 'three']
|
||||
, [3, ['un', 'deux', 'trois']]
|
||||
, [2, 'hi', ['hello', 'world']]
|
||||
, [2, ['hello', 'world'], 'hi']
|
||||
, [2, ['hello' => 'world', 'herp' => 'derp']]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider callProvider
|
||||
*/
|
||||
public function testCall() {
|
||||
$args = func_get_args();
|
||||
$paramNum = array_shift($args);
|
||||
|
||||
$uri = 'http://example.com/endpoint/' . rand(1, 100);
|
||||
$id = uniqid('', false);
|
||||
$clientMessage = array_merge(array(2, $id, $uri), $args);
|
||||
|
||||
$conn = $this->newConn();
|
||||
|
||||
$this->_comp->onOpen($conn);
|
||||
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||
|
||||
$this->assertEquals($id, $this->_app->last['onCall'][1]);
|
||||
$this->assertEquals($uri, $this->_app->last['onCall'][2]);
|
||||
|
||||
$this->assertEquals($paramNum, count($this->_app->last['onCall'][3]));
|
||||
}
|
||||
|
||||
public function testPublish() {
|
||||
$conn = $this->newConn();
|
||||
|
||||
$topic = 'pubsubhubbub';
|
||||
$event = 'Here I am, publishing data';
|
||||
|
||||
$clientMessage = array(7, $topic, $event);
|
||||
|
||||
$this->_comp->onOpen($conn);
|
||||
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||
|
||||
$this->assertEquals($topic, $this->_app->last['onPublish'][1]);
|
||||
$this->assertEquals($event, $this->_app->last['onPublish'][2]);
|
||||
$this->assertEquals(array(), $this->_app->last['onPublish'][3]);
|
||||
$this->assertEquals(array(), $this->_app->last['onPublish'][4]);
|
||||
}
|
||||
|
||||
public function testPublishAndExcludeMe() {
|
||||
$conn = $this->newConn();
|
||||
|
||||
$this->_comp->onOpen($conn);
|
||||
$this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', true)));
|
||||
|
||||
$this->assertEquals($conn->WAMP->sessionId, $this->_app->last['onPublish'][3][0]);
|
||||
}
|
||||
|
||||
public function testPublishAndEligible() {
|
||||
$conn = $this->newConn();
|
||||
|
||||
$buddy = uniqid('', false);
|
||||
$friend = uniqid('', false);
|
||||
|
||||
$this->_comp->onOpen($conn);
|
||||
$this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', false, array($buddy, $friend))));
|
||||
|
||||
$this->assertEquals(array(), $this->_app->last['onPublish'][3]);
|
||||
$this->assertEquals(2, count($this->_app->last['onPublish'][4]));
|
||||
}
|
||||
|
||||
public function eventProvider() {
|
||||
return array(
|
||||
array('http://example.com', array('one', 'two'))
|
||||
, array('curie', array(array('hello' => 'world', 'herp' => 'derp')))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider eventProvider
|
||||
*/
|
||||
public function testEvent($topic, $payload) {
|
||||
$conn = new WampConnection($this->newConn());
|
||||
$conn->event($topic, $payload);
|
||||
|
||||
$eventString = $conn->last['send'];
|
||||
|
||||
$this->assertSame(array(8, $topic, $payload), json_decode($eventString, true));
|
||||
}
|
||||
|
||||
public function testOnClosePropagation() {
|
||||
$conn = new Connection;
|
||||
|
||||
$this->_comp->onOpen($conn);
|
||||
$this->_comp->onClose($conn);
|
||||
|
||||
$class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection');
|
||||
$method = $class->getMethod('getConnection');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$check = $method->invokeArgs($this->_app->last['onClose'][0], array());
|
||||
|
||||
$this->assertSame($conn, $check);
|
||||
}
|
||||
|
||||
public function testOnErrorPropagation() {
|
||||
$conn = new Connection;
|
||||
|
||||
$e = new \Exception('Nope');
|
||||
|
||||
$this->_comp->onOpen($conn);
|
||||
$this->_comp->onError($conn, $e);
|
||||
|
||||
$class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection');
|
||||
$method = $class->getMethod('getConnection');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$check = $method->invokeArgs($this->_app->last['onError'][0], array());
|
||||
|
||||
$this->assertSame($conn, $check);
|
||||
$this->assertSame($e, $this->_app->last['onError'][1]);
|
||||
}
|
||||
|
||||
public function testPrefix() {
|
||||
$conn = new WampConnection($this->newConn());
|
||||
$this->_comp->onOpen($conn);
|
||||
|
||||
$prefix = 'incoming';
|
||||
$fullURI = "http://example.com/$prefix";
|
||||
$method = 'call';
|
||||
|
||||
$this->_comp->onMessage($conn, json_encode(array(1, $prefix, $fullURI)));
|
||||
|
||||
$this->assertEquals($fullURI, $conn->WAMP->prefixes[$prefix]);
|
||||
$this->assertEquals("$fullURI#$method", $conn->getUri("$prefix:$method"));
|
||||
}
|
||||
|
||||
public function testMessageMustBeJson() {
|
||||
$this->setExpectedException('\\Ratchet\\Wamp\\JsonException');
|
||||
|
||||
$conn = new Connection;
|
||||
|
||||
$this->_comp->onOpen($conn);
|
||||
$this->_comp->onMessage($conn, 'Hello World!');
|
||||
}
|
||||
|
||||
public function testGetSubProtocolsReturnsArray() {
|
||||
$this->assertTrue(is_array($this->_comp->getSubProtocols()));
|
||||
}
|
||||
|
||||
public function testGetSubProtocolsGetFromApp() {
|
||||
$this->_app->protocols = array('hello', 'world');
|
||||
|
||||
$this->assertGreaterThanOrEqual(3, count($this->_comp->getSubProtocols()));
|
||||
}
|
||||
|
||||
public function testWampOnMessageApp() {
|
||||
$app = $this->getMock('\\Ratchet\\Wamp\\WampServerInterface');
|
||||
$wamp = new ServerProtocol($app);
|
||||
|
||||
$this->assertContains('wamp', $wamp->getSubProtocols());
|
||||
}
|
||||
|
||||
public function badFormatProvider() {
|
||||
return array(
|
||||
array(json_encode(true))
|
||||
, array('{"valid":"json", "invalid": "message"}')
|
||||
, array('{"0": "fail", "hello": "world"}')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider badFormatProvider
|
||||
*/
|
||||
public function testValidJsonButInvalidProtocol($message) {
|
||||
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||
|
||||
$conn = $this->newConn();
|
||||
$this->_comp->onOpen($conn);
|
||||
$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']));
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Wamp\TopicManager
|
||||
*/
|
||||
class TopicManagerTest extends \PHPUnit_Framework_TestCase {
|
||||
private $mock;
|
||||
|
||||
/**
|
||||
* @var \Ratchet\Wamp\TopicManager
|
||||
*/
|
||||
private $mngr;
|
||||
|
||||
/**
|
||||
* @var \Ratchet\ConnectionInterface
|
||||
*/
|
||||
private $conn;
|
||||
|
||||
public function setUp() {
|
||||
$this->conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||
$this->mock = $this->getMock('\Ratchet\Wamp\WampServerInterface');
|
||||
$this->mngr = new TopicManager($this->mock);
|
||||
|
||||
$this->conn->WAMP = new \StdClass;
|
||||
$this->mngr->onOpen($this->conn);
|
||||
}
|
||||
|
||||
public function testGetTopicReturnsTopicObject() {
|
||||
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||
$method = $class->getMethod('getTopic');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$topic = $method->invokeArgs($this->mngr, array('The Topic'));
|
||||
|
||||
$this->assertInstanceOf('Ratchet\Wamp\Topic', $topic);
|
||||
}
|
||||
|
||||
public function testGetTopicCreatesTopicWithSameName() {
|
||||
$name = 'The Topic';
|
||||
|
||||
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||
$method = $class->getMethod('getTopic');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||
|
||||
$this->assertEquals($name, $topic->getId());
|
||||
}
|
||||
|
||||
public function testGetTopicReturnsSameObject() {
|
||||
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||
$method = $class->getMethod('getTopic');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$topic = $method->invokeArgs($this->mngr, array('No copy'));
|
||||
$again = $method->invokeArgs($this->mngr, array('No copy'));
|
||||
|
||||
$this->assertSame($topic, $again);
|
||||
}
|
||||
|
||||
public function testOnOpen() {
|
||||
$this->mock->expects($this->once())->method('onOpen');
|
||||
$this->mngr->onOpen($this->conn);
|
||||
}
|
||||
|
||||
public function testOnCall() {
|
||||
$id = uniqid();
|
||||
|
||||
$this->mock->expects($this->once())->method('onCall')->with(
|
||||
$this->conn
|
||||
, $id
|
||||
, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||
, array()
|
||||
);
|
||||
|
||||
$this->mngr->onCall($this->conn, $id, 'new topic', array());
|
||||
}
|
||||
|
||||
public function testOnSubscribeCreatesTopicObject() {
|
||||
$this->mock->expects($this->once())->method('onSubscribe')->with(
|
||||
$this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||
);
|
||||
|
||||
$this->mngr->onSubscribe($this->conn, 'new topic');
|
||||
}
|
||||
|
||||
public function testTopicIsInConnectionOnSubscribe() {
|
||||
$name = 'New Topic';
|
||||
|
||||
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||
$method = $class->getMethod('getTopic');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||
|
||||
$this->mngr->onSubscribe($this->conn, $name);
|
||||
|
||||
$this->assertTrue($this->conn->WAMP->subscriptions->contains($topic));
|
||||
}
|
||||
|
||||
public function testDoubleSubscriptionFiresOnce() {
|
||||
$this->mock->expects($this->exactly(1))->method('onSubscribe');
|
||||
|
||||
$this->mngr->onSubscribe($this->conn, 'same topic');
|
||||
$this->mngr->onSubscribe($this->conn, 'same topic');
|
||||
}
|
||||
|
||||
public function testUnsubscribeEvent() {
|
||||
$name = 'in and out';
|
||||
$this->mock->expects($this->once())->method('onUnsubscribe')->with(
|
||||
$this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||
);
|
||||
|
||||
$this->mngr->onSubscribe($this->conn, $name);
|
||||
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||
}
|
||||
|
||||
public function testUnsubscribeFiresOnce() {
|
||||
$name = 'getting sleepy';
|
||||
$this->mock->expects($this->exactly(1))->method('onUnsubscribe');
|
||||
|
||||
$this->mngr->onSubscribe($this->conn, $name);
|
||||
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||
}
|
||||
|
||||
public function testUnsubscribeRemovesTopicFromConnection() {
|
||||
$name = 'Bye Bye Topic';
|
||||
|
||||
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||
$method = $class->getMethod('getTopic');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||
|
||||
$this->mngr->onSubscribe($this->conn, $name);
|
||||
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||
|
||||
$this->assertFalse($this->conn->WAMP->subscriptions->contains($topic));
|
||||
}
|
||||
|
||||
public function testOnPublishBubbles() {
|
||||
$msg = 'Cover all the code!';
|
||||
|
||||
$this->mock->expects($this->once())->method('onPublish')->with(
|
||||
$this->conn
|
||||
, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||
, $msg
|
||||
, $this->isType('array')
|
||||
, $this->isType('array')
|
||||
);
|
||||
|
||||
$this->mngr->onPublish($this->conn, 'topic coverage', $msg, array(), array());
|
||||
}
|
||||
|
||||
public function testOnCloseBubbles() {
|
||||
$this->mock->expects($this->once())->method('onClose')->with($this->conn);
|
||||
$this->mngr->onClose($this->conn);
|
||||
}
|
||||
|
||||
protected function topicProvider($name) {
|
||||
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||
$method = $class->getMethod('getTopic');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$attribute = $class->getProperty('topicLookup');
|
||||
$attribute->setAccessible(true);
|
||||
|
||||
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||
|
||||
return array($topic, $attribute);
|
||||
}
|
||||
|
||||
public function testConnIsRemovedFromTopicOnClose() {
|
||||
$name = 'State Testing';
|
||||
list($topic, $attribute) = $this->topicProvider($name);
|
||||
|
||||
$this->assertCount(1, $attribute->getValue($this->mngr));
|
||||
|
||||
$this->mngr->onSubscribe($this->conn, $name);
|
||||
$this->mngr->onClose($this->conn);
|
||||
|
||||
$this->assertFalse($topic->has($this->conn));
|
||||
}
|
||||
|
||||
public static function topicConnExpectationProvider() {
|
||||
return [
|
||||
[ 'onClose', 0]
|
||||
, ['onUnsubscribe', 0]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider topicConnExpectationProvider
|
||||
*/
|
||||
public function testTopicRetentionFromLeavingConnections($methodCall, $expectation) {
|
||||
$topicName = 'checkTopic';
|
||||
list($topic, $attribute) = $this->topicProvider($topicName);
|
||||
|
||||
$this->mngr->onSubscribe($this->conn, $topicName);
|
||||
call_user_func_array(array($this->mngr, $methodCall), array($this->conn, $topicName));
|
||||
|
||||
$this->assertCount($expectation, $attribute->getValue($this->mngr));
|
||||
}
|
||||
|
||||
public function testOnErrorBubbles() {
|
||||
$e = new \Exception('All work and no play makes Chris a dull boy');
|
||||
$this->mock->expects($this->once())->method('onError')->with($this->conn, $e);
|
||||
|
||||
$this->mngr->onError($this->conn, $e);
|
||||
}
|
||||
|
||||
public function testGetSubProtocolsReturnsArray() {
|
||||
$this->assertInternalType('array', $this->mngr->getSubProtocols());
|
||||
}
|
||||
|
||||
public function testGetSubProtocolsBubbles() {
|
||||
$subs = array('hello', 'world');
|
||||
$app = $this->getMock('Ratchet\Wamp\Stub\WsWampServerInterface');
|
||||
$app->expects($this->once())->method('getSubProtocols')->will($this->returnValue($subs));
|
||||
$mngr = new TopicManager($app);
|
||||
|
||||
$this->assertEquals($subs, $mngr->getSubProtocols());
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Wamp\Topic
|
||||
*/
|
||||
class TopicTest extends \PHPUnit_Framework_TestCase {
|
||||
public function testGetId() {
|
||||
$id = uniqid();
|
||||
$topic = new Topic($id);
|
||||
|
||||
$this->assertEquals($id, $topic->getId());
|
||||
}
|
||||
|
||||
public function testAddAndCount() {
|
||||
$topic = new Topic('merp');
|
||||
|
||||
$topic->add($this->newConn());
|
||||
$topic->add($this->newConn());
|
||||
$topic->add($this->newConn());
|
||||
|
||||
$this->assertEquals(3, count($topic));
|
||||
}
|
||||
|
||||
public function testRemove() {
|
||||
$topic = new Topic('boop');
|
||||
$tracked = $this->newConn();
|
||||
|
||||
$topic->add($this->newConn());
|
||||
$topic->add($tracked);
|
||||
$topic->add($this->newConn());
|
||||
|
||||
$topic->remove($tracked);
|
||||
|
||||
$this->assertEquals(2, count($topic));
|
||||
}
|
||||
|
||||
public function testBroadcast() {
|
||||
$msg = 'Hello World!';
|
||||
$name = 'Batman';
|
||||
$protocol = json_encode(array(8, $name, $msg));
|
||||
|
||||
$first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||
$second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||
|
||||
$first->expects($this->once())
|
||||
->method('send')
|
||||
->with($this->equalTo($protocol));
|
||||
|
||||
$second->expects($this->once())
|
||||
->method('send')
|
||||
->with($this->equalTo($protocol));
|
||||
|
||||
$topic = new Topic($name);
|
||||
$topic->add($first);
|
||||
$topic->add($second);
|
||||
|
||||
$topic->broadcast($msg);
|
||||
}
|
||||
|
||||
public function testBroadcastWithExclude() {
|
||||
$msg = 'Hello odd numbers';
|
||||
$name = 'Excluding';
|
||||
$protocol = json_encode(array(8, $name, $msg));
|
||||
|
||||
$first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||
$second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||
$third = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||
|
||||
$first->expects($this->once())
|
||||
->method('send')
|
||||
->with($this->equalTo($protocol));
|
||||
|
||||
$second->expects($this->never())->method('send');
|
||||
|
||||
$third->expects($this->once())
|
||||
->method('send')
|
||||
->with($this->equalTo($protocol));
|
||||
|
||||
$topic = new Topic($name);
|
||||
$topic->add($first);
|
||||
$topic->add($second);
|
||||
$topic->add($third);
|
||||
|
||||
$topic->broadcast($msg, array($second->WAMP->sessionId));
|
||||
}
|
||||
|
||||
public function testBroadcastWithEligible() {
|
||||
$msg = 'Hello white list';
|
||||
$name = 'Eligible';
|
||||
$protocol = json_encode(array(8, $name, $msg));
|
||||
|
||||
$first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||
$second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||
$third = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||
|
||||
$first->expects($this->once())
|
||||
->method('send')
|
||||
->with($this->equalTo($protocol));
|
||||
|
||||
$second->expects($this->never())->method('send');
|
||||
|
||||
$third->expects($this->once())
|
||||
->method('send')
|
||||
->with($this->equalTo($protocol));
|
||||
|
||||
$topic = new Topic($name);
|
||||
$topic->add($first);
|
||||
$topic->add($second);
|
||||
$topic->add($third);
|
||||
|
||||
$topic->broadcast($msg, array(), array($first->WAMP->sessionId, $third->WAMP->sessionId));
|
||||
}
|
||||
|
||||
public function testIterator() {
|
||||
$first = $this->newConn();
|
||||
$second = $this->newConn();
|
||||
$third = $this->newConn();
|
||||
|
||||
$topic = new Topic('Joker');
|
||||
$topic->add($first)->add($second)->add($third);
|
||||
|
||||
$check = array($first, $second, $third);
|
||||
|
||||
foreach ($topic as $mock) {
|
||||
$this->assertNotSame(false, array_search($mock, $check));
|
||||
}
|
||||
}
|
||||
|
||||
public function testToString() {
|
||||
$name = 'Bane';
|
||||
$topic = new Topic($name);
|
||||
|
||||
$this->assertEquals($name, (string)$topic);
|
||||
}
|
||||
|
||||
public function testDoesHave() {
|
||||
$conn = $this->newConn();
|
||||
$topic = new Topic('Two Face');
|
||||
$topic->add($conn);
|
||||
|
||||
$this->assertTrue($topic->has($conn));
|
||||
}
|
||||
|
||||
public function testDoesNotHave() {
|
||||
$conn = $this->newConn();
|
||||
$topic = new Topic('Alfred');
|
||||
|
||||
$this->assertFalse($topic->has($conn));
|
||||
}
|
||||
|
||||
public function testDoesNotHaveAfterRemove() {
|
||||
$conn = $this->newConn();
|
||||
$topic = new Topic('Ras');
|
||||
|
||||
$topic->add($conn)->remove($conn);
|
||||
|
||||
$this->assertFalse($topic->has($conn));
|
||||
}
|
||||
|
||||
protected function newConn() {
|
||||
return new WampConnection($this->getMock('\\Ratchet\\ConnectionInterface'));
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Wamp\WampConnection
|
||||
*/
|
||||
class WampConnectionTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $conn;
|
||||
protected $mock;
|
||||
|
||||
public function setUp() {
|
||||
$this->mock = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||
$this->conn = new WampConnection($this->mock);
|
||||
}
|
||||
|
||||
public function testCallResult() {
|
||||
$callId = uniqid();
|
||||
$data = array('hello' => 'world', 'herp' => 'derp');
|
||||
|
||||
$this->mock->expects($this->once())->method('send')->with(json_encode(array(3, $callId, $data)));
|
||||
|
||||
$this->conn->callResult($callId, $data);
|
||||
}
|
||||
|
||||
public function testCallError() {
|
||||
$callId = uniqid();
|
||||
$uri = 'http://example.com/end/point';
|
||||
|
||||
$this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, '')));
|
||||
|
||||
$this->conn->callError($callId, $uri);
|
||||
}
|
||||
|
||||
public function testCallErrorWithTopic() {
|
||||
$callId = uniqid();
|
||||
$uri = 'http://example.com/end/point';
|
||||
|
||||
$this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, '')));
|
||||
|
||||
$this->conn->callError($callId, new Topic($uri));
|
||||
}
|
||||
|
||||
public function testDetailedCallError() {
|
||||
$callId = uniqid();
|
||||
$uri = 'http://example.com/end/point';
|
||||
$desc = 'beep boop beep';
|
||||
$detail = 'Error: Too much awesome';
|
||||
|
||||
$this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, $desc, $detail)));
|
||||
|
||||
$this->conn->callError($callId, $uri, $desc, $detail);
|
||||
}
|
||||
|
||||
public function testPrefix() {
|
||||
$shortOut = 'outgoing';
|
||||
$longOut = 'http://example.com/outgoing';
|
||||
|
||||
$this->mock->expects($this->once())->method('send')->with(json_encode(array(1, $shortOut, $longOut)));
|
||||
|
||||
$this->conn->prefix($shortOut, $longOut);
|
||||
}
|
||||
|
||||
public function testGetUriWhenNoCurieGiven() {
|
||||
$uri = 'http://example.com/noshort';
|
||||
|
||||
$this->assertEquals($uri, $this->conn->getUri($uri));
|
||||
}
|
||||
|
||||
public function testClose() {
|
||||
$mock = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||
$conn = new WampConnection($mock);
|
||||
|
||||
$mock->expects($this->once())->method('close');
|
||||
|
||||
$conn->close();
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
use Ratchet\AbstractMessageComponentTestCase;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Wamp\WampServer
|
||||
*/
|
||||
class WampServerTest extends AbstractMessageComponentTestCase {
|
||||
public function getConnectionClassString() {
|
||||
return '\Ratchet\Wamp\WampConnection';
|
||||
}
|
||||
|
||||
public function getDecoratorClassString() {
|
||||
return 'Ratchet\Wamp\WampServer';
|
||||
}
|
||||
|
||||
public function getComponentClassString() {
|
||||
return '\Ratchet\Wamp\WampServerInterface';
|
||||
}
|
||||
|
||||
public function testOnMessageToEvent() {
|
||||
$published = 'Client published this message';
|
||||
|
||||
$this->_app->expects($this->once())->method('onPublish')->with(
|
||||
$this->isExpectedConnection()
|
||||
, new \PHPUnit_Framework_Constraint_IsInstanceOf('\Ratchet\Wamp\Topic')
|
||||
, $published
|
||||
, array()
|
||||
, array()
|
||||
);
|
||||
|
||||
$this->_serv->onMessage($this->_conn, json_encode(array(7, 'topic', $published)));
|
||||
}
|
||||
|
||||
public function testGetSubProtocols() {
|
||||
// todo: could expand on this
|
||||
$this->assertInternalType('array', $this->_serv->getSubProtocols());
|
||||
}
|
||||
|
||||
public function testConnectionClosesOnInvalidJson() {
|
||||
$this->_conn->expects($this->once())->method('close');
|
||||
$this->_serv->onMessage($this->_conn, 'invalid json');
|
||||
}
|
||||
|
||||
public function testConnectionClosesOnProtocolError() {
|
||||
$this->_conn->expects($this->once())->method('close');
|
||||
$this->_serv->onMessage($this->_conn, json_encode(array('valid' => 'json', 'invalid' => 'protocol')));
|
||||
}
|
||||
}
|
||||
1115
digital_doctor/vendor/composer/installed.json
vendored
1115
digital_doctor/vendor/composer/installed.json
vendored
File diff suppressed because it is too large
Load Diff
151
digital_doctor/vendor/composer/installed.php
vendored
151
digital_doctor/vendor/composer/installed.php
vendored
@@ -5,20 +5,11 @@
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => 'e499661572c48662d059211606f96a9eb68c35dc',
|
||||
'reference' => '09ae48941229f26b04631d081e85a2f29bf91be2',
|
||||
'name' => 'topthink/think',
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'cboden/ratchet' => array(
|
||||
'pretty_version' => 'v0.4.4',
|
||||
'version' => '0.4.4.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../cboden/ratchet',
|
||||
'aliases' => array(),
|
||||
'reference' => '5012dc954541b40c5599d286fd40653f5716a38f',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'doing/phpqrcode' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
@@ -30,24 +21,6 @@
|
||||
'reference' => '600baab04cd4dfe2b307a321775ec5a66e330560',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'evenement/evenement' => array(
|
||||
'pretty_version' => 'v3.0.2',
|
||||
'version' => '3.0.2.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../evenement/evenement',
|
||||
'aliases' => array(),
|
||||
'reference' => '0a16b0d71ab13284339abb99d9d2bd813640efbc',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'fig/http-message-util' => array(
|
||||
'pretty_version' => '1.1.5',
|
||||
'version' => '1.1.5.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../fig/http-message-util',
|
||||
'aliases' => array(),
|
||||
'reference' => '9d94dc0154230ac39e5bf89398b324a86f63f765',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'guzzlehttp/guzzle' => array(
|
||||
'pretty_version' => '7.8.1',
|
||||
'version' => '7.8.1.0',
|
||||
@@ -136,12 +109,12 @@
|
||||
),
|
||||
),
|
||||
'psr/http-factory' => array(
|
||||
'pretty_version' => '1.1.0',
|
||||
'version' => '1.1.0.0',
|
||||
'pretty_version' => '1.0.2',
|
||||
'version' => '1.0.2.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/http-factory',
|
||||
'aliases' => array(),
|
||||
'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
|
||||
'reference' => 'e616d01114759c4c489f93b099585439f795fe35',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/http-message' => array(
|
||||
@@ -183,121 +156,13 @@
|
||||
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ratchet/rfc6455' => array(
|
||||
'pretty_version' => 'v0.3.1',
|
||||
'version' => '0.3.1.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ratchet/rfc6455',
|
||||
'aliases' => array(),
|
||||
'reference' => '7c964514e93456a52a99a20fcfa0de242a43ccdb',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/cache' => array(
|
||||
'pretty_version' => 'v1.2.0',
|
||||
'version' => '1.2.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/cache',
|
||||
'aliases' => array(),
|
||||
'reference' => 'd47c472b64aa5608225f47965a484b75c7817d5b',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/dns' => array(
|
||||
'pretty_version' => 'v1.13.0',
|
||||
'version' => '1.13.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/dns',
|
||||
'aliases' => array(),
|
||||
'reference' => 'eb8ae001b5a455665c89c1df97f6fb682f8fb0f5',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/event-loop' => array(
|
||||
'pretty_version' => 'v1.5.0',
|
||||
'version' => '1.5.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/event-loop',
|
||||
'aliases' => array(),
|
||||
'reference' => 'bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/http' => array(
|
||||
'pretty_version' => 'v1.10.0',
|
||||
'version' => '1.10.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/http',
|
||||
'aliases' => array(),
|
||||
'reference' => '8111281ee57f22b7194f5dba225e609ba7ce4d20',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/promise' => array(
|
||||
'pretty_version' => 'v3.2.0',
|
||||
'version' => '3.2.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/promise',
|
||||
'aliases' => array(),
|
||||
'reference' => '8a164643313c71354582dc850b42b33fa12a4b63',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/socket' => array(
|
||||
'pretty_version' => 'v1.15.0',
|
||||
'version' => '1.15.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/socket',
|
||||
'aliases' => array(),
|
||||
'reference' => '216d3aec0b87f04a40ca04f481e6af01bdd1d038',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/stream' => array(
|
||||
'pretty_version' => 'v1.4.0',
|
||||
'version' => '1.4.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/stream',
|
||||
'aliases' => array(),
|
||||
'reference' => '1e5b0acb8fe55143b5b426817155190eb6f5b18d',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/deprecation-contracts' => array(
|
||||
'pretty_version' => 'v2.5.3',
|
||||
'version' => '2.5.3.0',
|
||||
'pretty_version' => 'v2.5.2',
|
||||
'version' => '2.5.2.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
|
||||
'aliases' => array(),
|
||||
'reference' => '80d075412b557d41002320b96a096ca65aa2c98d',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/http-foundation' => array(
|
||||
'pretty_version' => 'v5.4.40',
|
||||
'version' => '5.4.40.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/http-foundation',
|
||||
'aliases' => array(),
|
||||
'reference' => 'cf4893ca4eca3fac4ae06da1590afdbbb4217847',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-mbstring' => array(
|
||||
'pretty_version' => 'v1.30.0',
|
||||
'version' => '1.30.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||
'aliases' => array(),
|
||||
'reference' => 'fd22ab50000ef01661e2a31d850ebaa297f8e03c',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php80' => array(
|
||||
'pretty_version' => 'v1.30.0',
|
||||
'version' => '1.30.0.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||
'aliases' => array(),
|
||||
'reference' => '77fa7995ac1b21ab60769b7323d600a991a90433',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/routing' => array(
|
||||
'pretty_version' => 'v5.4.40',
|
||||
'version' => '5.4.40.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/routing',
|
||||
'aliases' => array(),
|
||||
'reference' => '6df1dd8b306649303267a760699cf04cf39b1f7b',
|
||||
'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'textalk/websocket' => array(
|
||||
@@ -324,7 +189,7 @@
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => 'e499661572c48662d059211606f96a9eb68c35dc',
|
||||
'reference' => '09ae48941229f26b04631d081e85a2f29bf91be2',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'topthink/think-installer' => array(
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/.github export-ignore
|
||||
/doc export-ignore
|
||||
/examples export-ignore
|
||||
/tests export-ignore
|
||||
/.gitignore export-ignore
|
||||
/CHANGELOG.md export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2011 Igor Wiedler
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,64 +0,0 @@
|
||||
# Événement
|
||||
|
||||
Événement is a very simple event dispatching library for PHP.
|
||||
|
||||
It has the same design goals as [Silex](https://silex.symfony.com/) and
|
||||
[Pimple](https://github.com/silexphp/Pimple), to empower the user while staying concise
|
||||
and simple.
|
||||
|
||||
It is very strongly inspired by the [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) API found in
|
||||
[node.js](http://nodejs.org).
|
||||
|
||||

|
||||
[](https://packagist.org/packages/evenement/evenement)
|
||||
[](https://packagist.org/packages/evenement/evenement/stats)
|
||||
[](https://packagist.org/packages/evenement/evenement)
|
||||
|
||||
## Fetch
|
||||
|
||||
The recommended way to install Événement is [through composer](http://getcomposer.org). By running the following command:
|
||||
|
||||
$ composer require evenement/evenement
|
||||
|
||||
## Usage
|
||||
|
||||
### Creating an Emitter
|
||||
|
||||
```php
|
||||
<?php
|
||||
$emitter = new Evenement\EventEmitter();
|
||||
```
|
||||
|
||||
### Adding Listeners
|
||||
|
||||
```php
|
||||
<?php
|
||||
$emitter->on('user.created', function (User $user) use ($logger) {
|
||||
$logger->log(sprintf("User '%s' was created.", $user->getLogin()));
|
||||
});
|
||||
```
|
||||
|
||||
### Removing Listeners
|
||||
|
||||
```php
|
||||
<?php
|
||||
$emitter->removeListener('user.created', function (User $user) use ($logger) {
|
||||
$logger->log(sprintf("User '%s' was created.", $user->getLogin()));
|
||||
});
|
||||
```
|
||||
|
||||
### Emitting Events
|
||||
|
||||
```php
|
||||
<?php
|
||||
$emitter->emit('user.created', [$user]);
|
||||
```
|
||||
|
||||
Tests
|
||||
-----
|
||||
|
||||
$ ./vendor/bin/phpunit
|
||||
|
||||
License
|
||||
-------
|
||||
MIT, see LICENSE.
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "evenement/evenement",
|
||||
"description": "Événement is a very simple event dispatching library for PHP",
|
||||
"keywords": ["event-dispatcher", "event-emitter"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Igor Wiedler",
|
||||
"email": "igor@wiedler.ch"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9 || ^6"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Evenement\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Evenement\\Tests\\": "tests/"
|
||||
},
|
||||
"files": ["tests/functions.php"]
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of Evenement.
|
||||
*
|
||||
* (c) Igor Wiedler <igor@wiedler.ch>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evenement;
|
||||
|
||||
class EventEmitter implements EventEmitterInterface
|
||||
{
|
||||
use EventEmitterTrait;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of Evenement.
|
||||
*
|
||||
* (c) Igor Wiedler <igor@wiedler.ch>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evenement;
|
||||
|
||||
interface EventEmitterInterface
|
||||
{
|
||||
public function on($event, callable $listener);
|
||||
public function once($event, callable $listener);
|
||||
public function removeListener($event, callable $listener);
|
||||
public function removeAllListeners($event = null);
|
||||
public function listeners($event = null);
|
||||
public function emit($event, array $arguments = []);
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of Evenement.
|
||||
*
|
||||
* (c) Igor Wiedler <igor@wiedler.ch>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evenement;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function count;
|
||||
use function array_keys;
|
||||
use function array_merge;
|
||||
use function array_search;
|
||||
use function array_unique;
|
||||
use function array_values;
|
||||
|
||||
trait EventEmitterTrait
|
||||
{
|
||||
protected $listeners = [];
|
||||
protected $onceListeners = [];
|
||||
|
||||
public function on($event, callable $listener)
|
||||
{
|
||||
if ($event === null) {
|
||||
throw new InvalidArgumentException('event name must not be null');
|
||||
}
|
||||
|
||||
if (!isset($this->listeners[$event])) {
|
||||
$this->listeners[$event] = [];
|
||||
}
|
||||
|
||||
$this->listeners[$event][] = $listener;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function once($event, callable $listener)
|
||||
{
|
||||
if ($event === null) {
|
||||
throw new InvalidArgumentException('event name must not be null');
|
||||
}
|
||||
|
||||
if (!isset($this->onceListeners[$event])) {
|
||||
$this->onceListeners[$event] = [];
|
||||
}
|
||||
|
||||
$this->onceListeners[$event][] = $listener;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeListener($event, callable $listener)
|
||||
{
|
||||
if ($event === null) {
|
||||
throw new InvalidArgumentException('event name must not be null');
|
||||
}
|
||||
|
||||
if (isset($this->listeners[$event])) {
|
||||
$index = array_search($listener, $this->listeners[$event], true);
|
||||
if (false !== $index) {
|
||||
unset($this->listeners[$event][$index]);
|
||||
if (count($this->listeners[$event]) === 0) {
|
||||
unset($this->listeners[$event]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->onceListeners[$event])) {
|
||||
$index = array_search($listener, $this->onceListeners[$event], true);
|
||||
if (false !== $index) {
|
||||
unset($this->onceListeners[$event][$index]);
|
||||
if (count($this->onceListeners[$event]) === 0) {
|
||||
unset($this->onceListeners[$event]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function removeAllListeners($event = null)
|
||||
{
|
||||
if ($event !== null) {
|
||||
unset($this->listeners[$event]);
|
||||
} else {
|
||||
$this->listeners = [];
|
||||
}
|
||||
|
||||
if ($event !== null) {
|
||||
unset($this->onceListeners[$event]);
|
||||
} else {
|
||||
$this->onceListeners = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function listeners($event = null): array
|
||||
{
|
||||
if ($event === null) {
|
||||
$events = [];
|
||||
$eventNames = array_unique(
|
||||
array_merge(
|
||||
array_keys($this->listeners),
|
||||
array_keys($this->onceListeners)
|
||||
)
|
||||
);
|
||||
foreach ($eventNames as $eventName) {
|
||||
$events[$eventName] = array_merge(
|
||||
isset($this->listeners[$eventName]) ? $this->listeners[$eventName] : [],
|
||||
isset($this->onceListeners[$eventName]) ? $this->onceListeners[$eventName] : []
|
||||
);
|
||||
}
|
||||
return $events;
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
isset($this->listeners[$event]) ? $this->listeners[$event] : [],
|
||||
isset($this->onceListeners[$event]) ? $this->onceListeners[$event] : []
|
||||
);
|
||||
}
|
||||
|
||||
public function emit($event, array $arguments = [])
|
||||
{
|
||||
if ($event === null) {
|
||||
throw new InvalidArgumentException('event name must not be null');
|
||||
}
|
||||
|
||||
$listeners = [];
|
||||
if (isset($this->listeners[$event])) {
|
||||
$listeners = array_values($this->listeners[$event]);
|
||||
}
|
||||
|
||||
$onceListeners = [];
|
||||
if (isset($this->onceListeners[$event])) {
|
||||
$onceListeners = array_values($this->onceListeners[$event]);
|
||||
}
|
||||
|
||||
if(empty($listeners) === false) {
|
||||
foreach ($listeners as $listener) {
|
||||
$listener(...$arguments);
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($onceListeners) === false) {
|
||||
unset($this->onceListeners[$event]);
|
||||
foreach ($onceListeners as $listener) {
|
||||
$listener(...$arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
vendor/
|
||||
@@ -1,147 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file, in reverse chronological order by release.
|
||||
|
||||
## 1.1.5 - 2020-11-24
|
||||
|
||||
### Added
|
||||
|
||||
- [#19](https://github.com/php-fig/http-message-util/pull/19) adds support for PHP 8.
|
||||
|
||||
### Changed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nothing.
|
||||
|
||||
## 1.1.4 - 2020-02-05
|
||||
|
||||
### Added
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Changed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- [#15](https://github.com/php-fig/http-message-util/pull/15) removes the dependency on psr/http-message, as it is not technically necessary for usage of this package.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nothing.
|
||||
|
||||
## 1.1.3 - 2018-11-19
|
||||
|
||||
### Added
|
||||
|
||||
- [#10](https://github.com/php-fig/http-message-util/pull/10) adds the constants `StatusCodeInterface::STATUS_EARLY_HINTS` (103) and
|
||||
`StatusCodeInterface::STATUS_TOO_EARLY` (425).
|
||||
|
||||
### Changed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nothing.
|
||||
|
||||
## 1.1.2 - 2017-02-09
|
||||
|
||||
### Added
|
||||
|
||||
- [#4](https://github.com/php-fig/http-message-util/pull/4) adds the constant
|
||||
`StatusCodeInterface::STATUS_MISDIRECTED_REQUEST` (421).
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nothing.
|
||||
|
||||
## 1.1.1 - 2017-02-06
|
||||
|
||||
### Added
|
||||
|
||||
- [#3](https://github.com/php-fig/http-message-util/pull/3) adds the constant
|
||||
`StatusCodeInterface::STATUS_IM_A_TEAPOT` (418).
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nothing.
|
||||
|
||||
## 1.1.0 - 2016-09-19
|
||||
|
||||
### Added
|
||||
|
||||
- [#1](https://github.com/php-fig/http-message-util/pull/1) adds
|
||||
`Fig\Http\Message\StatusCodeInterface`, with constants named after common
|
||||
status reason phrases, with values indicating the status codes themselves.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nothing.
|
||||
|
||||
## 1.0.0 - 2017-08-05
|
||||
|
||||
### Added
|
||||
|
||||
- Adds `Fig\Http\Message\RequestMethodInterface`, with constants covering the
|
||||
most common HTTP request methods as specified by the IETF.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nothing.
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2016 PHP Framework Interoperability Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,17 +0,0 @@
|
||||
# PSR Http Message Util
|
||||
|
||||
This repository holds utility classes and constants to facilitate common
|
||||
operations of [PSR-7](https://www.php-fig.org/psr/psr-7/); the primary purpose is
|
||||
to provide constants for referring to request methods, response status codes and
|
||||
messages, and potentially common headers.
|
||||
|
||||
Implementation of PSR-7 interfaces is **not** within the scope of this package.
|
||||
|
||||
## Installation
|
||||
|
||||
Install by adding the package as a [Composer](https://getcomposer.org)
|
||||
requirement:
|
||||
|
||||
```bash
|
||||
$ composer require fig/http-message-util
|
||||
```
|
||||
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"name": "fig/http-message-util",
|
||||
"description": "Utility classes and constants for use with PSR-7 (psr/http-message)",
|
||||
"keywords": ["psr", "psr-7", "http", "http-message", "request", "response"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^5.3 || ^7.0 || ^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/http-message": "The package containing the PSR-7 interfaces"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Fig\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fig\Http\Message;
|
||||
|
||||
/**
|
||||
* Defines constants for common HTTP request methods.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* class RequestFactory implements RequestMethodInterface
|
||||
* {
|
||||
* public static function factory(
|
||||
* $uri = '/',
|
||||
* $method = self::METHOD_GET,
|
||||
* $data = []
|
||||
* ) {
|
||||
* }
|
||||
* }
|
||||
* </code>
|
||||
*/
|
||||
interface RequestMethodInterface
|
||||
{
|
||||
const METHOD_HEAD = 'HEAD';
|
||||
const METHOD_GET = 'GET';
|
||||
const METHOD_POST = 'POST';
|
||||
const METHOD_PUT = 'PUT';
|
||||
const METHOD_PATCH = 'PATCH';
|
||||
const METHOD_DELETE = 'DELETE';
|
||||
const METHOD_PURGE = 'PURGE';
|
||||
const METHOD_OPTIONS = 'OPTIONS';
|
||||
const METHOD_TRACE = 'TRACE';
|
||||
const METHOD_CONNECT = 'CONNECT';
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fig\Http\Message;
|
||||
|
||||
/**
|
||||
* Defines constants for common HTTP status code.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2295#section-8.1
|
||||
* @see https://tools.ietf.org/html/rfc2324#section-2.3
|
||||
* @see https://tools.ietf.org/html/rfc2518#section-9.7
|
||||
* @see https://tools.ietf.org/html/rfc2774#section-7
|
||||
* @see https://tools.ietf.org/html/rfc3229#section-10.4
|
||||
* @see https://tools.ietf.org/html/rfc4918#section-11
|
||||
* @see https://tools.ietf.org/html/rfc5842#section-7.1
|
||||
* @see https://tools.ietf.org/html/rfc5842#section-7.2
|
||||
* @see https://tools.ietf.org/html/rfc6585#section-3
|
||||
* @see https://tools.ietf.org/html/rfc6585#section-4
|
||||
* @see https://tools.ietf.org/html/rfc6585#section-5
|
||||
* @see https://tools.ietf.org/html/rfc6585#section-6
|
||||
* @see https://tools.ietf.org/html/rfc7231#section-6
|
||||
* @see https://tools.ietf.org/html/rfc7238#section-3
|
||||
* @see https://tools.ietf.org/html/rfc7725#section-3
|
||||
* @see https://tools.ietf.org/html/rfc7540#section-9.1.2
|
||||
* @see https://tools.ietf.org/html/rfc8297#section-2
|
||||
* @see https://tools.ietf.org/html/rfc8470#section-7
|
||||
* Usage:
|
||||
*
|
||||
* <code>
|
||||
* class ResponseFactory implements StatusCodeInterface
|
||||
* {
|
||||
* public function createResponse($code = self::STATUS_OK)
|
||||
* {
|
||||
* }
|
||||
* }
|
||||
* </code>
|
||||
*/
|
||||
interface StatusCodeInterface
|
||||
{
|
||||
// Informational 1xx
|
||||
const STATUS_CONTINUE = 100;
|
||||
const STATUS_SWITCHING_PROTOCOLS = 101;
|
||||
const STATUS_PROCESSING = 102;
|
||||
const STATUS_EARLY_HINTS = 103;
|
||||
// Successful 2xx
|
||||
const STATUS_OK = 200;
|
||||
const STATUS_CREATED = 201;
|
||||
const STATUS_ACCEPTED = 202;
|
||||
const STATUS_NON_AUTHORITATIVE_INFORMATION = 203;
|
||||
const STATUS_NO_CONTENT = 204;
|
||||
const STATUS_RESET_CONTENT = 205;
|
||||
const STATUS_PARTIAL_CONTENT = 206;
|
||||
const STATUS_MULTI_STATUS = 207;
|
||||
const STATUS_ALREADY_REPORTED = 208;
|
||||
const STATUS_IM_USED = 226;
|
||||
// Redirection 3xx
|
||||
const STATUS_MULTIPLE_CHOICES = 300;
|
||||
const STATUS_MOVED_PERMANENTLY = 301;
|
||||
const STATUS_FOUND = 302;
|
||||
const STATUS_SEE_OTHER = 303;
|
||||
const STATUS_NOT_MODIFIED = 304;
|
||||
const STATUS_USE_PROXY = 305;
|
||||
const STATUS_RESERVED = 306;
|
||||
const STATUS_TEMPORARY_REDIRECT = 307;
|
||||
const STATUS_PERMANENT_REDIRECT = 308;
|
||||
// Client Errors 4xx
|
||||
const STATUS_BAD_REQUEST = 400;
|
||||
const STATUS_UNAUTHORIZED = 401;
|
||||
const STATUS_PAYMENT_REQUIRED = 402;
|
||||
const STATUS_FORBIDDEN = 403;
|
||||
const STATUS_NOT_FOUND = 404;
|
||||
const STATUS_METHOD_NOT_ALLOWED = 405;
|
||||
const STATUS_NOT_ACCEPTABLE = 406;
|
||||
const STATUS_PROXY_AUTHENTICATION_REQUIRED = 407;
|
||||
const STATUS_REQUEST_TIMEOUT = 408;
|
||||
const STATUS_CONFLICT = 409;
|
||||
const STATUS_GONE = 410;
|
||||
const STATUS_LENGTH_REQUIRED = 411;
|
||||
const STATUS_PRECONDITION_FAILED = 412;
|
||||
const STATUS_PAYLOAD_TOO_LARGE = 413;
|
||||
const STATUS_URI_TOO_LONG = 414;
|
||||
const STATUS_UNSUPPORTED_MEDIA_TYPE = 415;
|
||||
const STATUS_RANGE_NOT_SATISFIABLE = 416;
|
||||
const STATUS_EXPECTATION_FAILED = 417;
|
||||
const STATUS_IM_A_TEAPOT = 418;
|
||||
const STATUS_MISDIRECTED_REQUEST = 421;
|
||||
const STATUS_UNPROCESSABLE_ENTITY = 422;
|
||||
const STATUS_LOCKED = 423;
|
||||
const STATUS_FAILED_DEPENDENCY = 424;
|
||||
const STATUS_TOO_EARLY = 425;
|
||||
const STATUS_UPGRADE_REQUIRED = 426;
|
||||
const STATUS_PRECONDITION_REQUIRED = 428;
|
||||
const STATUS_TOO_MANY_REQUESTS = 429;
|
||||
const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
|
||||
const STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
|
||||
// Server Errors 5xx
|
||||
const STATUS_INTERNAL_SERVER_ERROR = 500;
|
||||
const STATUS_NOT_IMPLEMENTED = 501;
|
||||
const STATUS_BAD_GATEWAY = 502;
|
||||
const STATUS_SERVICE_UNAVAILABLE = 503;
|
||||
const STATUS_GATEWAY_TIMEOUT = 504;
|
||||
const STATUS_VERSION_NOT_SUPPORTED = 505;
|
||||
const STATUS_VARIANT_ALSO_NEGOTIATES = 506;
|
||||
const STATUS_INSUFFICIENT_STORAGE = 507;
|
||||
const STATUS_LOOP_DETECTED = 508;
|
||||
const STATUS_NOT_EXTENDED = 510;
|
||||
const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
|
||||
"description": "Common interfaces for PSR-7 HTTP message factories",
|
||||
"keywords": [
|
||||
"psr",
|
||||
"psr-7",
|
||||
@@ -18,11 +18,8 @@
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-factory"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"php": ">=7.0.0",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user