fix:更新已知bug,优化代码
This commit is contained in:
4
vendor/workerman/gateway-worker/.github/FUNDING.yml
vendored
Normal file
4
vendor/workerman/gateway-worker/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
open_collective: walkor
|
||||
patreon: walkor
|
||||
21
vendor/workerman/gateway-worker/MIT-LICENSE.txt
vendored
Normal file
21
vendor/workerman/gateway-worker/MIT-LICENSE.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2009-2015 walkor<walkor@workerman.net> and contributors (see https://github.com/walkor/workerman/contributors)
|
||||
|
||||
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.
|
||||
38
vendor/workerman/gateway-worker/README.md
vendored
Normal file
38
vendor/workerman/gateway-worker/README.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
GatewayWorker
|
||||
=================
|
||||
|
||||
GatewayWorker基于[Workerman](https://github.com/walkor/Workerman)开发的一个项目框架,用于快速开发长连接应用,例如app推送服务端、即时IM服务端、游戏服务端、物联网、智能家居等等。
|
||||
|
||||
GatewayWorker使用经典的Gateway和Worker进程模型。Gateway进程负责维持客户端连接,并转发客户端的数据给Worker进程处理;Worker进程负责处理实际的业务逻辑,并将结果推送给对应的客户端。Gateway服务和Worker服务可以分开部署在不同的服务器上,实现分布式集群。
|
||||
|
||||
GatewayWorker提供非常方便的API,可以全局广播数据、可以向某个群体广播数据、也可以向某个特定客户端推送数据。配合Workerman的定时器,也可以定时推送数据。
|
||||
|
||||
快速开始
|
||||
======
|
||||
开发者可以从一个简单的demo开始(demo中包含了GatewayWorker内核,以及start_gateway.php start_business.php等启动入口文件)<br>
|
||||
[点击这里下载demo](http://www.workerman.net/download/GatewayWorker.zip)。<br>
|
||||
demo说明见源码readme。
|
||||
|
||||
手册
|
||||
=======
|
||||
http://www.workerman.net/gatewaydoc/
|
||||
|
||||
安装内核
|
||||
=======
|
||||
|
||||
只安装GatewayWorker内核文件(不包含start_gateway.php start_businessworker.php等启动入口文件)
|
||||
```
|
||||
composer require workerman/gateway-worker
|
||||
```
|
||||
|
||||
使用GatewayWorker开发的项目
|
||||
=======
|
||||
## [tadpole](http://kedou.workerman.net/)
|
||||
[Live demo](http://kedou.workerman.net/)
|
||||
[Source code](https://github.com/walkor/workerman)
|
||||

|
||||
|
||||
## [chat room](http://chat.workerman.net/)
|
||||
[Live demo](http://chat.workerman.net/)
|
||||
[Source code](https://github.com/walkor/workerman-chat)
|
||||

|
||||
12
vendor/workerman/gateway-worker/composer.json
vendored
Normal file
12
vendor/workerman/gateway-worker/composer.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name" : "workerman/gateway-worker",
|
||||
"keywords": ["distributed","communication"],
|
||||
"homepage": "http://www.workerman.net",
|
||||
"license" : "MIT",
|
||||
"require": {
|
||||
"workerman/workerman" : "^4.0.30"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {"GatewayWorker\\": "./src"}
|
||||
}
|
||||
}
|
||||
565
vendor/workerman/gateway-worker/src/BusinessWorker.php
vendored
Normal file
565
vendor/workerman/gateway-worker/src/BusinessWorker.php
vendored
Normal file
@@ -0,0 +1,565 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of workerman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace GatewayWorker;
|
||||
|
||||
use Workerman\Connection\TcpConnection;
|
||||
|
||||
use Workerman\Worker;
|
||||
use Workerman\Timer;
|
||||
use Workerman\Connection\AsyncTcpConnection;
|
||||
use GatewayWorker\Protocols\GatewayProtocol;
|
||||
use GatewayWorker\Lib\Context;
|
||||
|
||||
/**
|
||||
*
|
||||
* BusinessWorker 用于处理Gateway转发来的数据
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
*
|
||||
*/
|
||||
class BusinessWorker extends Worker
|
||||
{
|
||||
/**
|
||||
* 保存与 gateway 的连接 connection 对象
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $gatewayConnections = array();
|
||||
|
||||
/**
|
||||
* 注册中心地址
|
||||
*
|
||||
* @var string|array
|
||||
*/
|
||||
public $registerAddress = '127.0.0.1:1236';
|
||||
|
||||
/**
|
||||
* 事件处理类,默认是 Event 类
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $eventHandler = 'Events';
|
||||
|
||||
/**
|
||||
* 业务超时时间,可用来定位程序卡在哪里
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $processTimeout = 30;
|
||||
|
||||
/**
|
||||
* 业务超时时间,可用来定位程序卡在哪里
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
public $processTimeoutHandler = '\\Workerman\\Worker::log';
|
||||
|
||||
/**
|
||||
* 秘钥
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $secretKey = '';
|
||||
|
||||
/**
|
||||
* businessWorker进程将消息转发给gateway进程的发送缓冲区大小
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $sendToGatewayBufferSize = 10240000;
|
||||
|
||||
/**
|
||||
* 保存用户设置的 worker 启动回调
|
||||
*
|
||||
* @var callback
|
||||
*/
|
||||
protected $_onWorkerStart = null;
|
||||
|
||||
/**
|
||||
* 保存用户设置的 workerReload 回调
|
||||
*
|
||||
* @var callback
|
||||
*/
|
||||
protected $_onWorkerReload = null;
|
||||
|
||||
/**
|
||||
* 保存用户设置的 workerStop 回调
|
||||
*
|
||||
* @var callback
|
||||
*/
|
||||
protected $_onWorkerStop= null;
|
||||
|
||||
/**
|
||||
* 到注册中心的连接
|
||||
*
|
||||
* @var AsyncTcpConnection
|
||||
*/
|
||||
protected $_registerConnection = null;
|
||||
|
||||
/**
|
||||
* 处于连接状态的 gateway 通讯地址
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_connectingGatewayAddresses = array();
|
||||
|
||||
/**
|
||||
* 所有 geteway 内部通讯地址
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_gatewayAddresses = array();
|
||||
|
||||
/**
|
||||
* 等待连接个 gateway 地址
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_waitingConnectGatewayAddresses = array();
|
||||
|
||||
/**
|
||||
* Event::onConnect 回调
|
||||
*
|
||||
* @var callback
|
||||
*/
|
||||
protected $_eventOnConnect = null;
|
||||
|
||||
/**
|
||||
* Event::onMessage 回调
|
||||
*
|
||||
* @var callback
|
||||
*/
|
||||
protected $_eventOnMessage = null;
|
||||
|
||||
/**
|
||||
* Event::onClose 回调
|
||||
*
|
||||
* @var callback
|
||||
*/
|
||||
protected $_eventOnClose = null;
|
||||
|
||||
/**
|
||||
* websocket回调
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
protected $_eventOnWebSocketConnect = null;
|
||||
|
||||
/**
|
||||
* SESSION 版本缓存
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_sessionVersion = array();
|
||||
|
||||
/**
|
||||
* 用于保持长连接的心跳时间间隔
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const PERSISTENCE_CONNECTION_PING_INTERVAL = 25;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param string $socket_name
|
||||
* @param array $context_option
|
||||
*/
|
||||
public function __construct($socket_name = '', $context_option = array())
|
||||
{
|
||||
parent::__construct($socket_name, $context_option);
|
||||
$backrace = debug_backtrace();
|
||||
$this->_autoloadRootPath = dirname($backrace[0]['file']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->_onWorkerStart = $this->onWorkerStart;
|
||||
$this->_onWorkerReload = $this->onWorkerReload;
|
||||
$this->_onWorkerStop = $this->onWorkerStop;
|
||||
$this->onWorkerStop = array($this, 'onWorkerStop');
|
||||
$this->onWorkerStart = array($this, 'onWorkerStart');
|
||||
$this->onWorkerReload = array($this, 'onWorkerReload');
|
||||
parent::run();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当进程启动时一些初始化工作
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function onWorkerStart()
|
||||
{
|
||||
if (function_exists('opcache_reset')) {
|
||||
opcache_reset();
|
||||
}
|
||||
|
||||
if (!class_exists('\Protocols\GatewayProtocol')) {
|
||||
class_alias('GatewayWorker\Protocols\GatewayProtocol', 'Protocols\GatewayProtocol');
|
||||
}
|
||||
|
||||
if (!is_array($this->registerAddress)) {
|
||||
$this->registerAddress = array($this->registerAddress);
|
||||
}
|
||||
$this->connectToRegister();
|
||||
|
||||
\GatewayWorker\Lib\Gateway::setBusinessWorker($this);
|
||||
\GatewayWorker\Lib\Gateway::$secretKey = $this->secretKey;
|
||||
if ($this->_onWorkerStart) {
|
||||
call_user_func($this->_onWorkerStart, $this);
|
||||
}
|
||||
|
||||
if (is_callable($this->eventHandler . '::onWorkerStart')) {
|
||||
call_user_func($this->eventHandler . '::onWorkerStart', $this);
|
||||
}
|
||||
|
||||
if (function_exists('pcntl_signal')) {
|
||||
// 业务超时信号处理
|
||||
pcntl_signal(SIGALRM, array($this, 'timeoutHandler'), false);
|
||||
} else {
|
||||
$this->processTimeout = 0;
|
||||
}
|
||||
|
||||
// 设置回调
|
||||
if (is_callable($this->eventHandler . '::onConnect')) {
|
||||
$this->_eventOnConnect = $this->eventHandler . '::onConnect';
|
||||
}
|
||||
|
||||
if (is_callable($this->eventHandler . '::onMessage')) {
|
||||
$this->_eventOnMessage = $this->eventHandler . '::onMessage';
|
||||
} else {
|
||||
echo "Waring: {$this->eventHandler}::onMessage is not callable\n";
|
||||
}
|
||||
|
||||
if (is_callable($this->eventHandler . '::onClose')) {
|
||||
$this->_eventOnClose = $this->eventHandler . '::onClose';
|
||||
}
|
||||
|
||||
if (is_callable($this->eventHandler . '::onWebSocketConnect')) {
|
||||
$this->_eventOnWebSocketConnect = $this->eventHandler . '::onWebSocketConnect';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* onWorkerReload 回调
|
||||
*
|
||||
* @param Worker $worker
|
||||
*/
|
||||
protected function onWorkerReload($worker)
|
||||
{
|
||||
// 防止进程立刻退出
|
||||
$worker->reloadable = false;
|
||||
// 延迟 0.05 秒退出,避免 BusinessWorker 瞬间全部退出导致没有可用的 BusinessWorker 进程
|
||||
Timer::add(0.05, array('Workerman\Worker', 'stopAll'));
|
||||
// 执行用户定义的 onWorkerReload 回调
|
||||
if ($this->_onWorkerReload) {
|
||||
call_user_func($this->_onWorkerReload, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当进程关闭时一些清理工作
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function onWorkerStop()
|
||||
{
|
||||
if ($this->_onWorkerStop) {
|
||||
call_user_func($this->_onWorkerStop, $this);
|
||||
}
|
||||
if (is_callable($this->eventHandler . '::onWorkerStop')) {
|
||||
call_user_func($this->eventHandler . '::onWorkerStop', $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接服务注册中心
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function connectToRegister()
|
||||
{
|
||||
foreach ($this->registerAddress as $register_address) {
|
||||
$register_connection = new AsyncTcpConnection("text://{$register_address}");
|
||||
$secret_key = $this->secretKey;
|
||||
$register_connection->onConnect = function () use ($register_connection, $secret_key, $register_address) {
|
||||
$register_connection->send('{"event":"worker_connect","secret_key":"' . $secret_key . '"}');
|
||||
// 如果Register服务器不在本地服务器,则需要保持心跳
|
||||
if (strpos($register_address, '127.0.0.1') !== 0) {
|
||||
$register_connection->ping_timer = Timer::add(self::PERSISTENCE_CONNECTION_PING_INTERVAL, function () use ($register_connection) {
|
||||
$register_connection->send('{"event":"ping"}');
|
||||
});
|
||||
}
|
||||
};
|
||||
$register_connection->onClose = function ($register_connection) {
|
||||
if(!empty($register_connection->ping_timer)) {
|
||||
Timer::del($register_connection->ping_timer);
|
||||
}
|
||||
$register_connection->reconnect(1);
|
||||
};
|
||||
$register_connection->onMessage = array($this, 'onRegisterConnectionMessage');
|
||||
$register_connection->connect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 当注册中心发来消息时
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onRegisterConnectionMessage($register_connection, $data)
|
||||
{
|
||||
$data = json_decode($data, true);
|
||||
if (!isset($data['event'])) {
|
||||
echo "Received bad data from Register\n";
|
||||
return;
|
||||
}
|
||||
$event = $data['event'];
|
||||
switch ($event) {
|
||||
case 'broadcast_addresses':
|
||||
if (!is_array($data['addresses'])) {
|
||||
echo "Received bad data from Register. Addresses empty\n";
|
||||
return;
|
||||
}
|
||||
$addresses = $data['addresses'];
|
||||
$this->_gatewayAddresses = array();
|
||||
foreach ($addresses as $addr) {
|
||||
$this->_gatewayAddresses[$addr] = $addr;
|
||||
}
|
||||
$this->checkGatewayConnections($addresses);
|
||||
break;
|
||||
default:
|
||||
echo "Receive bad event:$event from Register.\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 gateway 转发来数据时
|
||||
*
|
||||
* @param TcpConnection $connection
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function onGatewayMessage($connection, $data)
|
||||
{
|
||||
$cmd = $data['cmd'];
|
||||
if ($cmd === GatewayProtocol::CMD_PING) {
|
||||
return;
|
||||
}
|
||||
// 上下文数据
|
||||
Context::$client_ip = $data['client_ip'];
|
||||
Context::$client_port = $data['client_port'];
|
||||
Context::$local_ip = $data['local_ip'];
|
||||
Context::$local_port = $data['local_port'];
|
||||
Context::$connection_id = $data['connection_id'];
|
||||
Context::$client_id = Context::addressToClientId($data['local_ip'], $data['local_port'],
|
||||
$data['connection_id']);
|
||||
// $_SERVER 变量
|
||||
$_SERVER = array(
|
||||
'REMOTE_ADDR' => long2ip($data['client_ip']),
|
||||
'REMOTE_PORT' => $data['client_port'],
|
||||
'GATEWAY_ADDR' => long2ip($data['local_ip']),
|
||||
'GATEWAY_PORT' => $data['gateway_port'],
|
||||
'GATEWAY_CLIENT_ID' => Context::$client_id,
|
||||
);
|
||||
// 检查session版本,如果是过期的session数据则拉取最新的数据
|
||||
if ($cmd !== GatewayProtocol::CMD_ON_CLOSE && isset($this->_sessionVersion[Context::$client_id]) && $this->_sessionVersion[Context::$client_id] !== crc32($data['ext_data'])) {
|
||||
$_SESSION = Context::$old_session = \GatewayWorker\Lib\Gateway::getSession(Context::$client_id);
|
||||
$this->_sessionVersion[Context::$client_id] = crc32($data['ext_data']);
|
||||
} else {
|
||||
if (!isset($this->_sessionVersion[Context::$client_id])) {
|
||||
$this->_sessionVersion[Context::$client_id] = crc32($data['ext_data']);
|
||||
}
|
||||
// 尝试解析 session
|
||||
if ($data['ext_data'] != '') {
|
||||
Context::$old_session = $_SESSION = Context::sessionDecode($data['ext_data']);
|
||||
} else {
|
||||
Context::$old_session = $_SESSION = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->processTimeout) {
|
||||
pcntl_alarm($this->processTimeout);
|
||||
}
|
||||
// 尝试执行 Event::onConnection、Event::onMessage、Event::onClose
|
||||
switch ($cmd) {
|
||||
case GatewayProtocol::CMD_ON_CONNECT:
|
||||
if ($this->_eventOnConnect) {
|
||||
call_user_func($this->_eventOnConnect, Context::$client_id);
|
||||
}
|
||||
break;
|
||||
case GatewayProtocol::CMD_ON_MESSAGE:
|
||||
if ($this->_eventOnMessage) {
|
||||
call_user_func($this->_eventOnMessage, Context::$client_id, $data['body']);
|
||||
}
|
||||
break;
|
||||
case GatewayProtocol::CMD_ON_CLOSE:
|
||||
unset($this->_sessionVersion[Context::$client_id]);
|
||||
if ($this->_eventOnClose) {
|
||||
call_user_func($this->_eventOnClose, Context::$client_id);
|
||||
}
|
||||
break;
|
||||
case GatewayProtocol::CMD_ON_WEBSOCKET_CONNECT:
|
||||
if ($this->_eventOnWebSocketConnect) {
|
||||
call_user_func($this->_eventOnWebSocketConnect, Context::$client_id, $data['body']);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ($this->processTimeout) {
|
||||
pcntl_alarm(0);
|
||||
}
|
||||
|
||||
// session 必须是数组
|
||||
if ($_SESSION !== null && !is_array($_SESSION)) {
|
||||
throw new \Exception('$_SESSION must be an array. But $_SESSION=' . var_export($_SESSION, true) . ' is not array.');
|
||||
}
|
||||
|
||||
// 判断 session 是否被更改
|
||||
if ($_SESSION !== Context::$old_session && $cmd !== GatewayProtocol::CMD_ON_CLOSE) {
|
||||
$session_str_now = $_SESSION !== null ? Context::sessionEncode($_SESSION) : '';
|
||||
\GatewayWorker\Lib\Gateway::setSocketSession(Context::$client_id, $session_str_now);
|
||||
$this->_sessionVersion[Context::$client_id] = crc32($session_str_now);
|
||||
}
|
||||
|
||||
Context::clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当与 Gateway 的连接断开时触发
|
||||
*
|
||||
* @param TcpConnection $connection
|
||||
* @return void
|
||||
*/
|
||||
public function onGatewayClose($connection)
|
||||
{
|
||||
$addr = $connection->remoteAddress;
|
||||
unset($this->gatewayConnections[$addr], $this->_connectingGatewayAddresses[$addr]);
|
||||
if (isset($this->_gatewayAddresses[$addr]) && !isset($this->_waitingConnectGatewayAddresses[$addr])) {
|
||||
Timer::add(1, array($this, 'tryToConnectGateway'), array($addr), false);
|
||||
$this->_waitingConnectGatewayAddresses[$addr] = $addr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试连接 Gateway 内部通讯地址
|
||||
*
|
||||
* @param string $addr
|
||||
*/
|
||||
public function tryToConnectGateway($addr)
|
||||
{
|
||||
if (!isset($this->gatewayConnections[$addr]) && !isset($this->_connectingGatewayAddresses[$addr]) && isset($this->_gatewayAddresses[$addr])) {
|
||||
$gateway_connection = new AsyncTcpConnection("GatewayProtocol://$addr");
|
||||
$gateway_connection->remoteAddress = $addr;
|
||||
$gateway_connection->onConnect = array($this, 'onConnectGateway');
|
||||
$gateway_connection->onMessage = array($this, 'onGatewayMessage');
|
||||
$gateway_connection->onClose = array($this, 'onGatewayClose');
|
||||
$gateway_connection->onError = array($this, 'onGatewayError');
|
||||
$gateway_connection->maxSendBufferSize = $this->sendToGatewayBufferSize;
|
||||
if (TcpConnection::$defaultMaxSendBufferSize == $gateway_connection->maxSendBufferSize) {
|
||||
$gateway_connection->maxSendBufferSize = 50 * 1024 * 1024;
|
||||
}
|
||||
$gateway_data = GatewayProtocol::$empty;
|
||||
$gateway_data['cmd'] = GatewayProtocol::CMD_WORKER_CONNECT;
|
||||
$gateway_data['body'] = json_encode(array(
|
||||
'worker_key' =>"{$this->name}:{$this->id}",
|
||||
'secret_key' => $this->secretKey,
|
||||
));
|
||||
$gateway_connection->send($gateway_data);
|
||||
$gateway_connection->connect();
|
||||
$this->_connectingGatewayAddresses[$addr] = $addr;
|
||||
}
|
||||
unset($this->_waitingConnectGatewayAddresses[$addr]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 gateway 的通信端口是否都已经连
|
||||
* 如果有未连接的端口,则尝试连接
|
||||
*
|
||||
* @param array $addresses_list
|
||||
*/
|
||||
public function checkGatewayConnections($addresses_list)
|
||||
{
|
||||
if (empty($addresses_list)) {
|
||||
return;
|
||||
}
|
||||
foreach ($addresses_list as $addr) {
|
||||
if (!isset($this->_waitingConnectGatewayAddresses[$addr])) {
|
||||
$this->tryToConnectGateway($addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当连接上 gateway 的通讯端口时触发
|
||||
* 将连接 connection 对象保存起来
|
||||
*
|
||||
* @param TcpConnection $connection
|
||||
* @return void
|
||||
*/
|
||||
public function onConnectGateway($connection)
|
||||
{
|
||||
$this->gatewayConnections[$connection->remoteAddress] = $connection;
|
||||
unset($this->_connectingGatewayAddresses[$connection->remoteAddress], $this->_waitingConnectGatewayAddresses[$connection->remoteAddress]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当与 gateway 的连接出现错误时触发
|
||||
*
|
||||
* @param TcpConnection $connection
|
||||
* @param int $error_no
|
||||
* @param string $error_msg
|
||||
*/
|
||||
public function onGatewayError($connection, $error_no, $error_msg)
|
||||
{
|
||||
echo "GatewayConnection Error : $error_no ,$error_msg\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有 Gateway 内部通讯地址
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAllGatewayAddresses()
|
||||
{
|
||||
return $this->_gatewayAddresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务超时回调
|
||||
*
|
||||
* @param int $signal
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function timeoutHandler($signal)
|
||||
{
|
||||
switch ($signal) {
|
||||
// 超时时钟
|
||||
case SIGALRM:
|
||||
// 超时异常
|
||||
$e = new \Exception("process_timeout", 506);
|
||||
$trace_str = $e->getTraceAsString();
|
||||
// 去掉第一行timeoutHandler的调用栈
|
||||
$trace_str = $e->getMessage() . ":\n" . substr($trace_str, strpos($trace_str, "\n") + 1) . "\n";
|
||||
// 开发者没有设置超时处理函数,或者超时处理函数返回空则执行退出
|
||||
if (!$this->processTimeoutHandler || !call_user_func($this->processTimeoutHandler, $trace_str, $e)) {
|
||||
Worker::stopAll();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
1063
vendor/workerman/gateway-worker/src/Gateway.php
vendored
Normal file
1063
vendor/workerman/gateway-worker/src/Gateway.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
136
vendor/workerman/gateway-worker/src/Lib/Context.php
vendored
Normal file
136
vendor/workerman/gateway-worker/src/Lib/Context.php
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of workerman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace GatewayWorker\Lib;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 上下文 包含当前用户 uid, 内部通信 local_ip local_port socket_id,以及客户端 client_ip client_port
|
||||
*/
|
||||
class Context
|
||||
{
|
||||
/**
|
||||
* 内部通讯 id
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $local_ip;
|
||||
|
||||
/**
|
||||
* 内部通讯端口
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $local_port;
|
||||
|
||||
/**
|
||||
* 客户端 ip
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $client_ip;
|
||||
|
||||
/**
|
||||
* 客户端端口
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $client_port;
|
||||
|
||||
/**
|
||||
* client_id
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $client_id;
|
||||
|
||||
/**
|
||||
* 连接 connection->id
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $connection_id;
|
||||
|
||||
/**
|
||||
* 旧的session
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $old_session;
|
||||
|
||||
/**
|
||||
* 编码 session
|
||||
*
|
||||
* @param mixed $session_data
|
||||
* @return string
|
||||
*/
|
||||
public static function sessionEncode($session_data = '')
|
||||
{
|
||||
if ($session_data !== '') {
|
||||
return serialize($session_data);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码 session
|
||||
*
|
||||
* @param string $session_buffer
|
||||
* @return mixed
|
||||
*/
|
||||
public static function sessionDecode($session_buffer)
|
||||
{
|
||||
return unserialize($session_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除上下文
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clear()
|
||||
{
|
||||
self::$local_ip = self::$local_port = self::$client_ip = self::$client_port =
|
||||
self::$client_id = self::$connection_id = self::$old_session = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通讯地址到 client_id 的转换
|
||||
*
|
||||
* @param int $local_ip
|
||||
* @param int $local_port
|
||||
* @param int $connection_id
|
||||
* @return string
|
||||
*/
|
||||
public static function addressToClientId($local_ip, $local_port, $connection_id)
|
||||
{
|
||||
return bin2hex(pack('NnN', $local_ip, $local_port, $connection_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* client_id 到通讯地址的转换
|
||||
*
|
||||
* @param string $client_id
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function clientIdToAddress($client_id)
|
||||
{
|
||||
if (strlen($client_id) !== 20) {
|
||||
echo new Exception("client_id $client_id is invalid");
|
||||
return false;
|
||||
}
|
||||
return unpack('Nlocal_ip/nlocal_port/Nconnection_id', pack('H*', $client_id));
|
||||
}
|
||||
}
|
||||
76
vendor/workerman/gateway-worker/src/Lib/Db.php
vendored
Normal file
76
vendor/workerman/gateway-worker/src/Lib/Db.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of workerman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace GatewayWorker\Lib;
|
||||
|
||||
use Config\Db as DbConfig;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 数据库类
|
||||
*/
|
||||
class Db
|
||||
{
|
||||
/**
|
||||
* 实例数组
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $instance = array();
|
||||
|
||||
/**
|
||||
* 获取实例
|
||||
*
|
||||
* @param string $config_name
|
||||
* @return DbConnection
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function instance($config_name)
|
||||
{
|
||||
if (!isset(DbConfig::$$config_name)) {
|
||||
echo "\\Config\\Db::$config_name not set\n";
|
||||
throw new Exception("\\Config\\Db::$config_name not set\n");
|
||||
}
|
||||
|
||||
if (empty(self::$instance[$config_name])) {
|
||||
$config = DbConfig::$$config_name;
|
||||
self::$instance[$config_name] = new DbConnection($config['host'], $config['port'],
|
||||
$config['user'], $config['password'], $config['dbname'],$config['charset']);
|
||||
}
|
||||
return self::$instance[$config_name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭数据库实例
|
||||
*
|
||||
* @param string $config_name
|
||||
*/
|
||||
public static function close($config_name)
|
||||
{
|
||||
if (isset(self::$instance[$config_name])) {
|
||||
self::$instance[$config_name]->closeConnection();
|
||||
self::$instance[$config_name] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭所有数据库实例
|
||||
*/
|
||||
public static function closeAll()
|
||||
{
|
||||
foreach (self::$instance as $connection) {
|
||||
$connection->closeConnection();
|
||||
}
|
||||
self::$instance = array();
|
||||
}
|
||||
}
|
||||
1979
vendor/workerman/gateway-worker/src/Lib/DbConnection.php
vendored
Normal file
1979
vendor/workerman/gateway-worker/src/Lib/DbConnection.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1380
vendor/workerman/gateway-worker/src/Lib/Gateway.php
vendored
Normal file
1380
vendor/workerman/gateway-worker/src/Lib/Gateway.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
216
vendor/workerman/gateway-worker/src/Protocols/GatewayProtocol.php
vendored
Normal file
216
vendor/workerman/gateway-worker/src/Protocols/GatewayProtocol.php
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of workerman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace GatewayWorker\Protocols;
|
||||
|
||||
/**
|
||||
* Gateway 与 Worker 间通讯的二进制协议
|
||||
*
|
||||
* struct GatewayProtocol
|
||||
* {
|
||||
* unsigned int pack_len,
|
||||
* unsigned char cmd,//命令字
|
||||
* unsigned int local_ip,
|
||||
* unsigned short local_port,
|
||||
* unsigned int client_ip,
|
||||
* unsigned short client_port,
|
||||
* unsigned int connection_id,
|
||||
* unsigned char flag,
|
||||
* unsigned short gateway_port,
|
||||
* unsigned int ext_len,
|
||||
* char[ext_len] ext_data,
|
||||
* char[pack_length-HEAD_LEN] body//包体
|
||||
* }
|
||||
* NCNnNnNCnN
|
||||
*/
|
||||
class GatewayProtocol
|
||||
{
|
||||
// 发给worker,gateway有一个新的连接
|
||||
const CMD_ON_CONNECT = 1;
|
||||
|
||||
// 发给worker的,客户端有消息
|
||||
const CMD_ON_MESSAGE = 3;
|
||||
|
||||
// 发给worker上的关闭链接事件
|
||||
const CMD_ON_CLOSE = 4;
|
||||
|
||||
// 发给gateway的向单个用户发送数据
|
||||
const CMD_SEND_TO_ONE = 5;
|
||||
|
||||
// 发给gateway的向所有用户发送数据
|
||||
const CMD_SEND_TO_ALL = 6;
|
||||
|
||||
// 发给gateway的踢出用户
|
||||
// 1、如果有待发消息,将在发送完后立即销毁用户连接
|
||||
// 2、如果无待发消息,将立即销毁用户连接
|
||||
const CMD_KICK = 7;
|
||||
|
||||
// 发给gateway的立即销毁用户连接
|
||||
const CMD_DESTROY = 8;
|
||||
|
||||
// 发给gateway,通知用户session更新
|
||||
const CMD_UPDATE_SESSION = 9;
|
||||
|
||||
// 获取在线状态
|
||||
const CMD_GET_ALL_CLIENT_SESSIONS = 10;
|
||||
|
||||
// 判断是否在线
|
||||
const CMD_IS_ONLINE = 11;
|
||||
|
||||
// client_id绑定到uid
|
||||
const CMD_BIND_UID = 12;
|
||||
|
||||
// 解绑
|
||||
const CMD_UNBIND_UID = 13;
|
||||
|
||||
// 向uid发送数据
|
||||
const CMD_SEND_TO_UID = 14;
|
||||
|
||||
// 根据uid获取绑定的clientid
|
||||
const CMD_GET_CLIENT_ID_BY_UID = 15;
|
||||
|
||||
// 加入组
|
||||
const CMD_JOIN_GROUP = 20;
|
||||
|
||||
// 离开组
|
||||
const CMD_LEAVE_GROUP = 21;
|
||||
|
||||
// 向组成员发消息
|
||||
const CMD_SEND_TO_GROUP = 22;
|
||||
|
||||
// 获取组成员
|
||||
const CMD_GET_CLIENT_SESSIONS_BY_GROUP = 23;
|
||||
|
||||
// 获取组在线连接数
|
||||
const CMD_GET_CLIENT_COUNT_BY_GROUP = 24;
|
||||
|
||||
// 按照条件查找
|
||||
const CMD_SELECT = 25;
|
||||
|
||||
// 获取在线的群组ID
|
||||
const CMD_GET_GROUP_ID_LIST = 26;
|
||||
|
||||
// 取消分组
|
||||
const CMD_UNGROUP = 27;
|
||||
|
||||
// worker连接gateway事件
|
||||
const CMD_WORKER_CONNECT = 200;
|
||||
|
||||
// 心跳
|
||||
const CMD_PING = 201;
|
||||
|
||||
// GatewayClient连接gateway事件
|
||||
const CMD_GATEWAY_CLIENT_CONNECT = 202;
|
||||
|
||||
// 根据client_id获取session
|
||||
const CMD_GET_SESSION_BY_CLIENT_ID = 203;
|
||||
|
||||
// 发给gateway,覆盖session
|
||||
const CMD_SET_SESSION = 204;
|
||||
|
||||
// 当websocket握手时触发,只有websocket协议支持此命令字
|
||||
const CMD_ON_WEBSOCKET_CONNECT = 205;
|
||||
|
||||
// 包体是标量
|
||||
const FLAG_BODY_IS_SCALAR = 0x01;
|
||||
|
||||
// 通知gateway在send时不调用协议encode方法,在广播组播时提升性能
|
||||
const FLAG_NOT_CALL_ENCODE = 0x02;
|
||||
|
||||
/**
|
||||
* 包头长度
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const HEAD_LEN = 28;
|
||||
|
||||
public static $empty = array(
|
||||
'cmd' => 0,
|
||||
'local_ip' => 0,
|
||||
'local_port' => 0,
|
||||
'client_ip' => 0,
|
||||
'client_port' => 0,
|
||||
'connection_id' => 0,
|
||||
'flag' => 0,
|
||||
'gateway_port' => 0,
|
||||
'ext_data' => '',
|
||||
'body' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
* 返回包长度
|
||||
*
|
||||
* @param string $buffer
|
||||
* @return int return current package length
|
||||
*/
|
||||
public static function input($buffer)
|
||||
{
|
||||
if (strlen($buffer) < self::HEAD_LEN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$data = unpack("Npack_len", $buffer);
|
||||
return $data['pack_len'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取整个包的 buffer
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
public static function encode($data)
|
||||
{
|
||||
$flag = (int)is_scalar($data['body']);
|
||||
if (!$flag) {
|
||||
$data['body'] = serialize($data['body']);
|
||||
}
|
||||
$data['flag'] |= $flag;
|
||||
$ext_len = strlen($data['ext_data']);
|
||||
$package_len = self::HEAD_LEN + $ext_len + strlen($data['body']);
|
||||
return pack("NCNnNnNCnN", $package_len,
|
||||
$data['cmd'], $data['local_ip'],
|
||||
$data['local_port'], $data['client_ip'],
|
||||
$data['client_port'], $data['connection_id'],
|
||||
$data['flag'], $data['gateway_port'],
|
||||
$ext_len) . $data['ext_data'] . $data['body'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从二进制数据转换为数组
|
||||
*
|
||||
* @param string $buffer
|
||||
* @return array
|
||||
*/
|
||||
public static function decode($buffer)
|
||||
{
|
||||
$data = unpack("Npack_len/Ccmd/Nlocal_ip/nlocal_port/Nclient_ip/nclient_port/Nconnection_id/Cflag/ngateway_port/Next_len",
|
||||
$buffer);
|
||||
if ($data['ext_len'] > 0) {
|
||||
$data['ext_data'] = substr($buffer, self::HEAD_LEN, $data['ext_len']);
|
||||
if ($data['flag'] & self::FLAG_BODY_IS_SCALAR) {
|
||||
$data['body'] = substr($buffer, self::HEAD_LEN + $data['ext_len']);
|
||||
} else {
|
||||
$data['body'] = unserialize(substr($buffer, self::HEAD_LEN + $data['ext_len']));
|
||||
}
|
||||
} else {
|
||||
$data['ext_data'] = '';
|
||||
if ($data['flag'] & self::FLAG_BODY_IS_SCALAR) {
|
||||
$data['body'] = substr($buffer, self::HEAD_LEN);
|
||||
} else {
|
||||
$data['body'] = unserialize(substr($buffer, self::HEAD_LEN));
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
193
vendor/workerman/gateway-worker/src/Register.php
vendored
Normal file
193
vendor/workerman/gateway-worker/src/Register.php
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of workerman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace GatewayWorker;
|
||||
|
||||
use Workerman\Worker;
|
||||
use Workerman\Timer;
|
||||
|
||||
/**
|
||||
*
|
||||
* 注册中心,用于注册 Gateway 和 BusinessWorker
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
*
|
||||
*/
|
||||
class Register extends Worker
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $name = 'Register';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $reloadable = false;
|
||||
|
||||
/**
|
||||
* 秘钥
|
||||
* @var string
|
||||
*/
|
||||
public $secretKey = '';
|
||||
|
||||
/**
|
||||
* 所有 gateway 的连接
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_gatewayConnections = array();
|
||||
|
||||
/**
|
||||
* 所有 worker 的连接
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_workerConnections = array();
|
||||
|
||||
/**
|
||||
* 进程启动时间
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_startTime = 0;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
// 设置 onMessage 连接回调
|
||||
$this->onConnect = array($this, 'onConnect');
|
||||
|
||||
// 设置 onMessage 回调
|
||||
$this->onMessage = array($this, 'onMessage');
|
||||
|
||||
// 设置 onClose 回调
|
||||
$this->onClose = array($this, 'onClose');
|
||||
|
||||
// 记录进程启动的时间
|
||||
$this->_startTime = time();
|
||||
|
||||
// 强制使用text协议
|
||||
$this->protocol = '\Workerman\Protocols\Text';
|
||||
|
||||
// reusePort
|
||||
$this->reusePort = false;
|
||||
|
||||
// 运行父方法
|
||||
parent::run();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置个定时器,将未及时发送验证的连接关闭
|
||||
*
|
||||
* @param \Workerman\Connection\ConnectionInterface $connection
|
||||
* @return void
|
||||
*/
|
||||
public function onConnect($connection)
|
||||
{
|
||||
$connection->timeout_timerid = Timer::add(10, function () use ($connection) {
|
||||
Worker::log("Register auth timeout (".$connection->getRemoteIp()."). See http://doc2.workerman.net/register-auth-timeout.html");
|
||||
$connection->close();
|
||||
}, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置消息回调
|
||||
*
|
||||
* @param \Workerman\Connection\ConnectionInterface $connection
|
||||
* @param string $buffer
|
||||
* @return void
|
||||
*/
|
||||
public function onMessage($connection, $buffer)
|
||||
{
|
||||
// 删除定时器
|
||||
Timer::del($connection->timeout_timerid);
|
||||
$data = @json_decode($buffer, true);
|
||||
if (empty($data['event'])) {
|
||||
$error = "Bad request for Register service. Request info(IP:".$connection->getRemoteIp().", Request Buffer:$buffer). See http://doc2.workerman.net/register-auth-timeout.html";
|
||||
Worker::log($error);
|
||||
return $connection->close($error);
|
||||
}
|
||||
$event = $data['event'];
|
||||
$secret_key = isset($data['secret_key']) ? $data['secret_key'] : '';
|
||||
// 开始验证
|
||||
switch ($event) {
|
||||
// 是 gateway 连接
|
||||
case 'gateway_connect':
|
||||
if (empty($data['address'])) {
|
||||
echo "address not found\n";
|
||||
return $connection->close();
|
||||
}
|
||||
if ($secret_key !== $this->secretKey) {
|
||||
Worker::log("Register: Key does not match ".var_export($secret_key, true)." !== ".var_export($this->secretKey, true));
|
||||
return $connection->close();
|
||||
}
|
||||
$this->_gatewayConnections[$connection->id] = $data['address'];
|
||||
$this->broadcastAddresses();
|
||||
break;
|
||||
// 是 worker 连接
|
||||
case 'worker_connect':
|
||||
if ($secret_key !== $this->secretKey) {
|
||||
Worker::log("Register: Key does not match ".var_export($secret_key, true)." !== ".var_export($this->secretKey, true));
|
||||
return $connection->close();
|
||||
}
|
||||
$this->_workerConnections[$connection->id] = $connection;
|
||||
$this->broadcastAddresses($connection);
|
||||
break;
|
||||
case 'ping':
|
||||
break;
|
||||
default:
|
||||
Worker::log("Register unknown event:$event IP: ".$connection->getRemoteIp()." Buffer:$buffer. See http://doc2.workerman.net/register-auth-timeout.html");
|
||||
$connection->close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接关闭时
|
||||
*
|
||||
* @param \Workerman\Connection\ConnectionInterface $connection
|
||||
*/
|
||||
public function onClose($connection)
|
||||
{
|
||||
if (isset($this->_gatewayConnections[$connection->id])) {
|
||||
unset($this->_gatewayConnections[$connection->id]);
|
||||
$this->broadcastAddresses();
|
||||
}
|
||||
if (isset($this->_workerConnections[$connection->id])) {
|
||||
unset($this->_workerConnections[$connection->id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向 BusinessWorker 广播 gateway 内部通讯地址
|
||||
*
|
||||
* @param \Workerman\Connection\ConnectionInterface $connection
|
||||
*/
|
||||
public function broadcastAddresses($connection = null)
|
||||
{
|
||||
$data = array(
|
||||
'event' => 'broadcast_addresses',
|
||||
'addresses' => array_unique(array_values($this->_gatewayConnections)),
|
||||
);
|
||||
$buffer = json_encode($data);
|
||||
if ($connection) {
|
||||
$connection->send($buffer);
|
||||
return;
|
||||
}
|
||||
foreach ($this->_workerConnections as $con) {
|
||||
$con->send($buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
vendor/workerman/redis-queue/composer.json
vendored
3
vendor/workerman/redis-queue/composer.json
vendored
@@ -6,7 +6,8 @@
|
||||
"description": "Message queue system written in PHP based on workerman and backed by Redis.",
|
||||
"require": {
|
||||
"php": ">=5.4",
|
||||
"workerman/redis" : "^1.0"
|
||||
"workerman/redis" : "^1.0",
|
||||
"workerman/workerman" : "^4.0.20"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {"Workerman\\RedisQueue\\": "./src"}
|
||||
|
||||
@@ -5,22 +5,22 @@
|
||||
"high performance",
|
||||
"http service"
|
||||
],
|
||||
"homepage": "http://www.workerman.net",
|
||||
"homepage": "https://www.workerman.net",
|
||||
"license": "MIT",
|
||||
"description": "High performance HTTP Service Framework.",
|
||||
"authors": [
|
||||
{
|
||||
"name": "walkor",
|
||||
"email": "walkor@workerman.net",
|
||||
"homepage": "http://www.workerman.net",
|
||||
"homepage": "https://www.workerman.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"email": "walkor@workerman.net",
|
||||
"issues": "https://github.com/walkor/webman/issues",
|
||||
"forum": "http://wenda.workerman.net/",
|
||||
"wiki": "http://doc.workerman.net/",
|
||||
"forum": "https://wenda.workerman.net/",
|
||||
"wiki": "https://doc.workerman.net/",
|
||||
"source": "https://github.com/walkor/webman-framework"
|
||||
},
|
||||
"require": {
|
||||
|
||||
182
vendor/workerman/webman-framework/src/App.php
vendored
182
vendor/workerman/webman-framework/src/App.php
vendored
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
@@ -18,6 +19,9 @@ use Closure;
|
||||
use FastRoute\Dispatcher;
|
||||
use Monolog\Logger;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use ReflectionFunction;
|
||||
use ReflectionFunctionAbstract;
|
||||
use ReflectionMethod;
|
||||
use Throwable;
|
||||
use Webman\Exception\ExceptionHandler;
|
||||
use Webman\Exception\ExceptionHandlerInterface;
|
||||
@@ -118,7 +122,8 @@ class App
|
||||
return static::send($connection, $callback($request), $request);
|
||||
}
|
||||
|
||||
if (static::unsafeUri($connection, $path, $request) ||
|
||||
if (
|
||||
static::unsafeUri($connection, $path, $request) ||
|
||||
static::findFile($connection, $path, $key, $request) ||
|
||||
static::findRoute($connection, $path, $key, $request)
|
||||
) {
|
||||
@@ -126,13 +131,13 @@ class App
|
||||
}
|
||||
|
||||
$controller_and_action = static::parseControllerAction($path);
|
||||
if (!$controller_and_action || Route::hasDisableDefaultRoute()) {
|
||||
$plugin = $controller_and_action['plugin'] ?? '';
|
||||
if (!$controller_and_action || Route::hasDisableDefaultRoute($plugin)) {
|
||||
$callback = static::getFallback();
|
||||
$request->app = $request->controller = $request->action = '';
|
||||
static::send($connection, $callback($request), $request);
|
||||
return null;
|
||||
}
|
||||
$plugin = $controller_and_action['plugin'];
|
||||
$app = $controller_and_action['app'];
|
||||
$controller = $controller_and_action['controller'];
|
||||
$action = $controller_and_action['action'];
|
||||
@@ -177,12 +182,14 @@ class App
|
||||
*/
|
||||
protected static function unsafeUri(TcpConnection $connection, string $path, $request)
|
||||
{
|
||||
if (\strpos($path, '..') !== false ||
|
||||
if (
|
||||
!$path ||
|
||||
\strpos($path, '..') !== false ||
|
||||
\strpos($path, "\\") !== false ||
|
||||
\strpos($path, "\0") !== false ||
|
||||
\strpos($path, '//') !== false || !$path) {
|
||||
\strpos($path, "\0") !== false
|
||||
) {
|
||||
$callback = static::getFallback();
|
||||
$request->app = $request->controller = $request->action = '';
|
||||
$request->plugin = $request->app = $request->controller = $request->action = '';
|
||||
static::send($connection, $callback($request), $request);
|
||||
return true;
|
||||
}
|
||||
@@ -196,7 +203,12 @@ class App
|
||||
{
|
||||
// when route, controller and action not found, try to use Route::fallback
|
||||
return Route::getFallback() ?: function () {
|
||||
return new Response(404, [], \file_get_contents(static::$_publicPath . '/404.html'));
|
||||
try {
|
||||
$notFoundContent = \file_get_contents(static::$_publicPath . '/404.html');
|
||||
} catch (Throwable $e) {
|
||||
$notFoundContent = '404 Not Found';
|
||||
}
|
||||
return new Response(404, [], $notFoundContent);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -233,7 +245,7 @@ class App
|
||||
/**
|
||||
* @param $app
|
||||
* @param $call
|
||||
* @param null $args
|
||||
* @param array|null $args
|
||||
* @param bool $with_global_middleware
|
||||
* @param RouteObject $route
|
||||
* @return callable
|
||||
@@ -253,18 +265,34 @@ class App
|
||||
foreach ($middlewares as $key => $item) {
|
||||
$middlewares[$key][0] = static::container($plugin)->get($item[0]);
|
||||
}
|
||||
$controller_reuse = static::config($plugin, 'app.controller_reuse', true);
|
||||
|
||||
$need_inject = static::isNeedInject($call, $args);
|
||||
if (\is_array($call) && \is_string($call[0])) {
|
||||
$controller_reuse = static::config($plugin, 'app.controller_reuse', true);
|
||||
if (!$controller_reuse) {
|
||||
$call = function ($request, ...$args) use ($call, $plugin) {
|
||||
$call[0] = static::container($plugin)->make($call[0]);
|
||||
return $call($request, ...$args);
|
||||
};
|
||||
if ($need_inject) {
|
||||
$call = function ($request, ...$args) use ($call, $plugin) {
|
||||
$call[0] = static::container($plugin)->make($call[0]);
|
||||
$reflector = static::getReflector($call);
|
||||
$args = static::resolveMethodDependencies($plugin, $request, $args, $reflector);
|
||||
return $call(...$args);
|
||||
};
|
||||
$need_inject = false;
|
||||
} else {
|
||||
$call = function ($request, ...$args) use ($call, $plugin) {
|
||||
$call[0] = static::container($plugin)->make($call[0]);
|
||||
return $call($request, ...$args);
|
||||
};
|
||||
}
|
||||
} else {
|
||||
$call[0] = static::container($plugin)->get($call[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($need_inject) {
|
||||
$call = static::resolveInject($plugin, $call);
|
||||
}
|
||||
|
||||
if ($middlewares) {
|
||||
$callback = \array_reduce($middlewares, function ($carry, $pipe) {
|
||||
return function ($request) use ($carry, $pipe) {
|
||||
@@ -300,6 +328,129 @@ class App
|
||||
return $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $plugin
|
||||
* @param array|Closure $call
|
||||
* @param null|array $args
|
||||
* @return Closure
|
||||
* @see Dependency injection through reflection information
|
||||
*/
|
||||
protected static function resolveInject(string $plugin, $call)
|
||||
{
|
||||
return function (Request $request, ...$args) use ($plugin, $call) {
|
||||
$reflector = static::getReflector($call);
|
||||
$args = static::resolveMethodDependencies($plugin, $request, $args, $reflector);
|
||||
return $call(...$args);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether inject is required
|
||||
*
|
||||
* @param $call
|
||||
* @param $args
|
||||
* @return bool
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected static function isNeedInject($call, $args)
|
||||
{
|
||||
$args = $args ?: [];
|
||||
$reflector = static::getReflector($call);
|
||||
$reflection_parameters = $reflector->getParameters();
|
||||
if (!$reflection_parameters) {
|
||||
return false;
|
||||
}
|
||||
$first_parameter = \current($reflection_parameters);
|
||||
unset($reflection_parameters[\key($reflection_parameters)]);
|
||||
$adapters_list = ['int', 'string', 'bool', 'array', 'object', 'float', 'mixed', 'resource'];
|
||||
foreach ($reflection_parameters as $parameter) {
|
||||
if ($parameter->hasType() && !\in_array($parameter->getType()->getName(), $adapters_list)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!$first_parameter->hasType()) {
|
||||
if (\count($args) <= count($reflection_parameters)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} elseif (!\is_a(static::$_request, $first_parameter->getType()->getName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reflector.
|
||||
*
|
||||
* @param $call
|
||||
* @return void
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected static function getReflector($call)
|
||||
{
|
||||
if ($call instanceof Closure) {
|
||||
return new ReflectionFunction($call);
|
||||
}
|
||||
return new ReflectionMethod($call[0], $call[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return dependent parameters
|
||||
*
|
||||
* @param string $plugin
|
||||
* @param Request $request
|
||||
* @param array $args
|
||||
* @param ReflectionFunctionAbstract $reflector
|
||||
* @return array
|
||||
*/
|
||||
protected static function resolveMethodDependencies(string $plugin, Request $request, array $args, ReflectionFunctionAbstract $reflector)
|
||||
{
|
||||
// Specification parameter information
|
||||
$args = \array_values($args);
|
||||
$parameters = [];
|
||||
// An array of reflection classes for loop parameters, with each $parameter representing a reflection object of parameters
|
||||
foreach ($reflector->getParameters() as $parameter) {
|
||||
// Parameter quota consumption
|
||||
if ($parameter->hasType()) {
|
||||
$name = $parameter->getType()->getName();
|
||||
switch ($name) {
|
||||
case 'int':
|
||||
case 'string':
|
||||
case 'bool':
|
||||
case 'array':
|
||||
case 'object':
|
||||
case 'float':
|
||||
case 'mixed':
|
||||
case 'resource':
|
||||
goto _else;
|
||||
default:
|
||||
if (\is_a(static::$_request, $name)) {
|
||||
//Inject Request
|
||||
$parameters[] = $request;
|
||||
} else {
|
||||
$parameters[] = static::container($plugin)->make($name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
_else:
|
||||
// The variable parameter
|
||||
if (null !== \key($args)) {
|
||||
$parameters[] = \current($args);
|
||||
} else {
|
||||
// Indicates whether the current parameter has a default value. If yes, return true
|
||||
$parameters[] = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null;
|
||||
}
|
||||
// Quota of consumption variables
|
||||
\next($args);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the result of parameters replacement
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $plugin
|
||||
* @return ContainerInterface
|
||||
@@ -310,7 +461,7 @@ class App
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Request
|
||||
* @return Request|\support\Request
|
||||
*/
|
||||
public static function request()
|
||||
{
|
||||
@@ -454,6 +605,7 @@ class App
|
||||
*/
|
||||
protected static function parseControllerAction(string $path)
|
||||
{
|
||||
$path = \str_replace('-', '', $path);
|
||||
$path_explode = \explode('/', trim($path, '/'));
|
||||
$is_plugin = isset($path_explode[1]) && $path_explode[0] === 'app';
|
||||
$config_prefix = $is_plugin ? "plugin.{$path_explode[1]}." : '';
|
||||
|
||||
@@ -73,6 +73,14 @@ class Config
|
||||
static::load($config_path, $exclude_file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function clear()
|
||||
{
|
||||
static::$_config = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,10 @@ class Container implements ContainerInterface
|
||||
* @var array
|
||||
*/
|
||||
protected $_instances = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $_definitions = [];
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
@@ -25,10 +29,14 @@ class Container implements ContainerInterface
|
||||
public function get(string $name)
|
||||
{
|
||||
if (!isset($this->_instances[$name])) {
|
||||
if (!\class_exists($name)) {
|
||||
throw new NotFoundException("Class '$name' not found");
|
||||
if (isset($this->_definitions[$name])) {
|
||||
$this->_instances[$name] = call_user_func($this->_definitions[$name], $this);
|
||||
} else {
|
||||
if (!\class_exists($name)) {
|
||||
throw new NotFoundException("Class '$name' not found");
|
||||
}
|
||||
$this->_instances[$name] = new $name();
|
||||
}
|
||||
$this->_instances[$name] = new $name();
|
||||
}
|
||||
return $this->_instances[$name];
|
||||
}
|
||||
@@ -39,7 +47,8 @@ class Container implements ContainerInterface
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return \array_key_exists($name, $this->_instances);
|
||||
return \array_key_exists($name, $this->_instances)
|
||||
|| array_key_exists($name, $this->_definitions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,4 +65,14 @@ class Container implements ContainerInterface
|
||||
return new $name(... array_values($constructor));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $definitions
|
||||
* @return $this
|
||||
*/
|
||||
public function addDefinitions(array $definitions)
|
||||
{
|
||||
$this->_definitions = array_merge($this->_definitions, $definitions);
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use Webman\Http\Response;
|
||||
|
||||
/**
|
||||
* Class Handler
|
||||
* @package Support\Exception
|
||||
* @package support\exception
|
||||
*/
|
||||
class ExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
|
||||
@@ -64,7 +64,7 @@ class UploadFile extends File
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUploadMineType()
|
||||
public function getUploadMimeType()
|
||||
{
|
||||
return $this->_uploadMimeType;
|
||||
}
|
||||
@@ -93,4 +93,12 @@ class UploadFile extends File
|
||||
return $this->_uploadErrorCode === UPLOAD_ERR_OK;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* @deprecated
|
||||
* @return string
|
||||
*/
|
||||
public function getUploadMineType()
|
||||
{
|
||||
return $this->_uploadMimeType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,12 @@ class Install
|
||||
mkdir($parent_dir, 0777, true);
|
||||
}
|
||||
}
|
||||
copy_dir(__DIR__ . "/$source", base_path() . "/$dest", true);
|
||||
$source_file = __DIR__ . "/$source";
|
||||
copy_dir($source_file, base_path() . "/$dest", true);
|
||||
echo "Create $dest\r\n";
|
||||
if (is_file($source_file)) {
|
||||
@unlink($source_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -358,7 +358,7 @@ class Route
|
||||
require_once $route_config_file;
|
||||
}
|
||||
if (!is_dir($plugin_config_path = $config_path . '/plugin')) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
$dir_iterator = new \RecursiveDirectoryIterator($plugin_config_path, \FilesystemIterator::FOLLOW_SYMLINKS);
|
||||
$iterator = new \RecursiveIteratorIterator($dir_iterator);
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
Support\App::run();
|
||||
@@ -19,11 +19,11 @@ class App
|
||||
ini_set('display_errors', 'on');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (class_exists(Dotenv::class) && file_exists(base_path() . '/.env')) {
|
||||
if (class_exists(Dotenv::class) && file_exists(run_path('.env'))) {
|
||||
if (method_exists(Dotenv::class, 'createUnsafeImmutable')) {
|
||||
Dotenv::createUnsafeImmutable(base_path())->load();
|
||||
Dotenv::createUnsafeImmutable(run_path())->load();
|
||||
} else {
|
||||
Dotenv::createMutable(base_path())->load();
|
||||
Dotenv::createMutable(run_path())->load();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ class App
|
||||
require_once \base_path() . '/support/bootstrap.php';
|
||||
$app = new \Webman\App(config('app.request_class', Request::class), Log::channel('default'), app_path(), public_path());
|
||||
$worker->onMessage = [$app, 'onMessage'];
|
||||
[$app, 'onWorkerStart']($worker);
|
||||
\call_user_func([$app, 'onWorkerStart'], $worker);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use Symfony\Component\Cache\Psr16Cache;
|
||||
|
||||
/**
|
||||
* Class Cache
|
||||
* @package Support\Bootstrap
|
||||
* @package support\bootstrap
|
||||
*
|
||||
* Strings methods
|
||||
* @method static mixed get($key, $default = null)
|
||||
|
||||
@@ -19,7 +19,7 @@ use Webman\Config;
|
||||
|
||||
/**
|
||||
* Class Container
|
||||
* @package Support
|
||||
* @package support
|
||||
* @method static mixed get($name)
|
||||
* @method static mixed make($name, array $parameters)
|
||||
* @method static bool has($name)
|
||||
|
||||
@@ -18,7 +18,7 @@ use Illuminate\Database\Capsule\Manager;
|
||||
|
||||
/**
|
||||
* Class Db
|
||||
* @package Support
|
||||
* @package support
|
||||
* @method static array select(string $query, $bindings = [], $useReadPdo = true)
|
||||
* @method static int insert(string $query, $bindings = [])
|
||||
* @method static int update(string $query, $bindings = [])
|
||||
|
||||
@@ -21,7 +21,7 @@ use Monolog\Logger;
|
||||
|
||||
/**
|
||||
* Class Log
|
||||
* @package Support
|
||||
* @package support
|
||||
*
|
||||
* @method static void log($level, $message, array $context = [])
|
||||
* @method static void debug($message, array $context = [])
|
||||
|
||||
@@ -23,7 +23,7 @@ use Workerman\Worker;
|
||||
|
||||
/**
|
||||
* Class Redis
|
||||
* @package Support
|
||||
* @package support
|
||||
*
|
||||
* Strings methods
|
||||
* @method static int append($key, $value)
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace support;
|
||||
|
||||
/**
|
||||
* Class Request
|
||||
* @package Support
|
||||
* @package support
|
||||
*/
|
||||
class Request extends \Webman\Http\Request
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace support;
|
||||
|
||||
/**
|
||||
* Class Response
|
||||
* @package Support
|
||||
* @package support
|
||||
*/
|
||||
class Response extends \Webman\Http\Response
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ use Webman\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class Translation
|
||||
* @package Support
|
||||
* @package support
|
||||
* @method static string trans(?string $id, array $parameters = [], string $domain = null, string $locale = null)
|
||||
* @method static void setLocale(string $locale)
|
||||
* @method static string getLocale()
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
use support\Log;
|
||||
use Webman\Bootstrap;
|
||||
use Webman\Config;
|
||||
use Webman\Route;
|
||||
use Webman\Middleware;
|
||||
use Webman\Util;
|
||||
|
||||
$worker = $worker ?? null;
|
||||
|
||||
if ($timezone = config('app.default_timezone')) {
|
||||
date_default_timezone_set($timezone);
|
||||
}
|
||||
|
||||
set_error_handler(function ($level, $message, $file = '', $line = 0) {
|
||||
if (error_reporting() & $level) {
|
||||
throw new ErrorException($message, 0, $level, $file, $line);
|
||||
}
|
||||
});
|
||||
|
||||
if ($worker) {
|
||||
register_shutdown_function(function ($start_time) {
|
||||
if (time() - $start_time <= 1) {
|
||||
sleep(1);
|
||||
}
|
||||
}, time());
|
||||
}
|
||||
|
||||
if (class_exists('Dotenv\Dotenv') && file_exists(base_path() . '/.env')) {
|
||||
if (method_exists('Dotenv\Dotenv', 'createUnsafeImmutable')) {
|
||||
Dotenv::createUnsafeImmutable(base_path())->load();
|
||||
} else {
|
||||
Dotenv::createMutable(base_path())->load();
|
||||
}
|
||||
}
|
||||
|
||||
Support\App::loadAllConfig(['route']);
|
||||
|
||||
foreach (config('autoload.files', []) as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($project['autoload']['files'] ?? [] as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
}
|
||||
foreach ($projects['autoload']['files'] ?? [] as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
}
|
||||
|
||||
Middleware::load(config('middleware', []), '');
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project) || $name === 'static') {
|
||||
continue;
|
||||
}
|
||||
Middleware::load($project['middleware'] ?? [], '');
|
||||
}
|
||||
Middleware::load($projects['middleware'] ?? [], $firm);
|
||||
if ($static_middlewares = config("plugin.$firm.static.middleware")) {
|
||||
Middleware::load(['__static__' => $static_middlewares], $firm);
|
||||
}
|
||||
}
|
||||
Middleware::load(['__static__' => config('static.middleware', [])], '');
|
||||
|
||||
foreach (config('bootstrap', []) as $class_name) {
|
||||
if (!class_exists($class_name)) {
|
||||
$log = "Warning: Class $class_name setting in config/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $class_name */
|
||||
$class_name::start($worker);
|
||||
}
|
||||
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($project['bootstrap'] ?? [] as $class_name) {
|
||||
if (!class_exists($class_name)) {
|
||||
$log = "Warning: Class $class_name setting in config/plugin/$firm/$name/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $class_name */
|
||||
$class_name::start($worker);
|
||||
}
|
||||
}
|
||||
foreach ($projects['bootstrap'] ?? [] as $class_name) {
|
||||
if (!class_exists($class_name)) {
|
||||
$log = "Warning: Class $class_name setting in plugin/$firm/config/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $class_name */
|
||||
$class_name::start($worker);
|
||||
}
|
||||
}
|
||||
|
||||
$directory = base_path() . '/plugin';
|
||||
$paths = [config_path()];
|
||||
foreach (Util::scanDir($directory) as $path) {
|
||||
if (is_dir($path = "$path/config")) {
|
||||
$paths[] = $path;
|
||||
}
|
||||
}
|
||||
Route::load($paths);
|
||||
|
||||
@@ -21,7 +21,7 @@ use Workerman\Worker;
|
||||
|
||||
/**
|
||||
* Class Session
|
||||
* @package Support
|
||||
* @package support
|
||||
*/
|
||||
class Session implements Bootstrap
|
||||
{
|
||||
|
||||
@@ -15,12 +15,23 @@
|
||||
namespace support\exception;
|
||||
|
||||
use Exception;
|
||||
use Webman\Http\Response;
|
||||
use Webman\Http\Request;
|
||||
|
||||
/**
|
||||
* Class BusinessException
|
||||
* @package Support\Exception
|
||||
* @package support\exception
|
||||
*/
|
||||
class BusinessException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
public function render(Request $request): ?Response
|
||||
{
|
||||
if ($request->expectsJson()) {
|
||||
$code = $this->getCode();
|
||||
$json = ['code' => $code ? $code : 500, 'msg' => $this->getMessage()];
|
||||
return new Response(200, ['Content-Type' => 'application/json'],
|
||||
\json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
}
|
||||
return new Response(200, [], $this->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use Webman\Http\Response;
|
||||
|
||||
/**
|
||||
* Class Handler
|
||||
* @package Support\Exception
|
||||
* @package support\exception
|
||||
*/
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
@@ -36,6 +36,11 @@ class Handler extends ExceptionHandler
|
||||
|
||||
public function render(Request $request, Throwable $exception): Response
|
||||
{
|
||||
if(($exception instanceof BusinessException) && ($response = $exception->render($request)))
|
||||
{
|
||||
return $response;
|
||||
}
|
||||
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,482 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\Translation;
|
||||
use support\Container;
|
||||
use support\view\Raw;
|
||||
use support\view\Blade;
|
||||
use support\view\ThinkPHP;
|
||||
use support\view\Twig;
|
||||
use Workerman\Worker;
|
||||
use Webman\App;
|
||||
use Webman\Config;
|
||||
use Webman\Route;
|
||||
|
||||
// Phar support.
|
||||
if (\is_phar()) {
|
||||
\define('BASE_PATH', dirname(__DIR__));
|
||||
} else {
|
||||
\define('BASE_PATH', realpath(__DIR__ . '/../'));
|
||||
}
|
||||
\define('WEBMAN_VERSION', '1.4');
|
||||
|
||||
/**
|
||||
* @param $return_phar
|
||||
* @return false|string
|
||||
*/
|
||||
function base_path(bool $return_phar = true)
|
||||
{
|
||||
static $real_path = '';
|
||||
if (!$real_path) {
|
||||
$real_path = \is_phar() ? \dirname(Phar::running(false)) : BASE_PATH;
|
||||
}
|
||||
return $return_phar ? BASE_PATH : $real_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function app_path()
|
||||
{
|
||||
return BASE_PATH . DIRECTORY_SEPARATOR . 'app';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function public_path()
|
||||
{
|
||||
static $path = '';
|
||||
if (!$path) {
|
||||
$path = \config('app.public_path', BASE_PATH . DIRECTORY_SEPARATOR . 'public');
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function config_path()
|
||||
{
|
||||
return BASE_PATH . DIRECTORY_SEPARATOR . 'config';
|
||||
}
|
||||
|
||||
/**
|
||||
* Phar support.
|
||||
* Compatible with the 'realpath' function in the phar file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function runtime_path()
|
||||
{
|
||||
static $path = '';
|
||||
if (!$path) {
|
||||
$path = \config('app.runtime_path', BASE_PATH . DIRECTORY_SEPARATOR . 'runtime');
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $status
|
||||
* @param array $headers
|
||||
* @param string $body
|
||||
* @return Response
|
||||
*/
|
||||
function response($body = '', $status = 200, $headers = [])
|
||||
{
|
||||
return new Response($status, $headers, $body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param int $options
|
||||
* @return Response
|
||||
*/
|
||||
function json($data, $options = JSON_UNESCAPED_UNICODE)
|
||||
{
|
||||
return new Response(200, ['Content-Type' => 'application/json'], \json_encode($data, $options));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $xml
|
||||
* @return Response
|
||||
*/
|
||||
function xml($xml)
|
||||
{
|
||||
if ($xml instanceof SimpleXMLElement) {
|
||||
$xml = $xml->asXML();
|
||||
}
|
||||
return new Response(200, ['Content-Type' => 'text/xml'], $xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param string $callback_name
|
||||
* @return Response
|
||||
*/
|
||||
function jsonp($data, $callback_name = 'callback')
|
||||
{
|
||||
if (!\is_scalar($data) && null !== $data) {
|
||||
$data = \json_encode($data);
|
||||
}
|
||||
return new Response(200, [], "$callback_name($data)");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $location
|
||||
* @param int $status
|
||||
* @param array $headers
|
||||
* @return Response
|
||||
*/
|
||||
function redirect(string $location, int $status = 302, array $headers = [])
|
||||
{
|
||||
$response = new Response($status, ['Location' => $location]);
|
||||
if (!empty($headers)) {
|
||||
$response->withHeaders($headers);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $template
|
||||
* @param array $vars
|
||||
* @param null $app
|
||||
* @return Response
|
||||
*/
|
||||
function view(string $template, array $vars = [], string $app = null)
|
||||
{
|
||||
$request = \request();
|
||||
$plugin = $request->plugin ?? '';
|
||||
$handler = \config($plugin ? "plugin.$plugin.view.handler" : 'view.handler');
|
||||
return new Response(200, [], $handler::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
function raw_view(string $template, array $vars = [], string $app = null)
|
||||
{
|
||||
return new Response(200, [], Raw::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
*/
|
||||
function blade_view(string $template, array $vars = [], string $app = null)
|
||||
{
|
||||
return new Response(200, [], Blade::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
*/
|
||||
function think_view(string $template, array $vars = [], string $app = null)
|
||||
{
|
||||
return new Response(200, [], ThinkPHP::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
*/
|
||||
function twig_view(string $template, array $vars = [], string $app = null)
|
||||
{
|
||||
return new Response(200, [], Twig::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Request
|
||||
*/
|
||||
function request()
|
||||
{
|
||||
return App::request();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $key
|
||||
* @param $default
|
||||
* @return array|mixed|null
|
||||
*/
|
||||
function config(string $key = null, $default = null)
|
||||
{
|
||||
return Config::get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param ...$parameters
|
||||
* @return string
|
||||
*/
|
||||
function route(string $name, ...$parameters)
|
||||
{
|
||||
$route = Route::getByName($name);
|
||||
if (!$route) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!$parameters) {
|
||||
return $route->url();
|
||||
}
|
||||
|
||||
if (\is_array(\current($parameters))) {
|
||||
$parameters = \current($parameters);
|
||||
}
|
||||
|
||||
return $route->url($parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
function session($key = null, $default = null)
|
||||
{
|
||||
$session = \request()->session();
|
||||
if (null === $key) {
|
||||
return $session;
|
||||
}
|
||||
if (\is_array($key)) {
|
||||
$session->put($key);
|
||||
return null;
|
||||
}
|
||||
if (\strpos($key, '.')) {
|
||||
$key_array = \explode('.', $key);
|
||||
$value = $session->all();
|
||||
foreach ($key_array as $index) {
|
||||
if (!isset($value[$index])) {
|
||||
return $default;
|
||||
}
|
||||
$value = $value[$index];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
return $session->get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param array $parameters
|
||||
* @param string|null $domain
|
||||
* @param string|null $locale
|
||||
* @return string
|
||||
*/
|
||||
function trans(string $id, array $parameters = [], string $domain = null, string $locale = null)
|
||||
{
|
||||
$res = Translation::trans($id, $parameters, $domain, $locale);
|
||||
return $res === '' ? $id : $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $locale
|
||||
* @return string
|
||||
*/
|
||||
function locale(string $locale = null)
|
||||
{
|
||||
if (!$locale) {
|
||||
return Translation::getLocale();
|
||||
}
|
||||
Translation::setLocale($locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* 404 not found
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
function not_found()
|
||||
{
|
||||
return new Response(404, [], \file_get_contents(public_path() . '/404.html'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy dir.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $dest
|
||||
* @param bool $overwrite
|
||||
* @return void
|
||||
*/
|
||||
function copy_dir(string $source, string $dest, bool $overwrite = false)
|
||||
{
|
||||
if (\is_dir($source)) {
|
||||
if (!is_dir($dest)) {
|
||||
\mkdir($dest);
|
||||
}
|
||||
$files = \scandir($source);
|
||||
foreach ($files as $file) {
|
||||
if ($file !== "." && $file !== "..") {
|
||||
\copy_dir("$source/$file", "$dest/$file");
|
||||
}
|
||||
}
|
||||
} else if (\file_exists($source) && ($overwrite || !\file_exists($dest))) {
|
||||
\copy($source, $dest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove dir.
|
||||
*
|
||||
* @param string $dir
|
||||
* @return bool
|
||||
*/
|
||||
function remove_dir(string $dir)
|
||||
{
|
||||
if (\is_link($dir) || \is_file($dir)) {
|
||||
return \unlink($dir);
|
||||
}
|
||||
$files = \array_diff(\scandir($dir), array('.', '..'));
|
||||
foreach ($files as $file) {
|
||||
(\is_dir("$dir/$file") && !\is_link($dir)) ? \remove_dir("$dir/$file") : \unlink("$dir/$file");
|
||||
}
|
||||
return \rmdir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $worker
|
||||
* @param $class
|
||||
*/
|
||||
function worker_bind($worker, $class)
|
||||
{
|
||||
$callback_map = [
|
||||
'onConnect',
|
||||
'onMessage',
|
||||
'onClose',
|
||||
'onError',
|
||||
'onBufferFull',
|
||||
'onBufferDrain',
|
||||
'onWorkerStop',
|
||||
'onWebSocketConnect'
|
||||
];
|
||||
foreach ($callback_map as $name) {
|
||||
if (\method_exists($class, $name)) {
|
||||
$worker->$name = [$class, $name];
|
||||
}
|
||||
}
|
||||
if (\method_exists($class, 'onWorkerStart')) {
|
||||
[$class, 'onWorkerStart']($worker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $process_name
|
||||
* @param $config
|
||||
* @return void
|
||||
*/
|
||||
function worker_start($process_name, $config)
|
||||
{
|
||||
$worker = new Worker($config['listen'] ?? null, $config['context'] ?? []);
|
||||
$property_map = [
|
||||
'count',
|
||||
'user',
|
||||
'group',
|
||||
'reloadable',
|
||||
'reusePort',
|
||||
'transport',
|
||||
'protocol',
|
||||
];
|
||||
$worker->name = $process_name;
|
||||
foreach ($property_map as $property) {
|
||||
if (isset($config[$property])) {
|
||||
$worker->$property = $config[$property];
|
||||
}
|
||||
}
|
||||
|
||||
$worker->onWorkerStart = function ($worker) use ($config) {
|
||||
require_once \base_path() . '/support/bootstrap.php';
|
||||
|
||||
foreach ($config['services'] ?? [] as $server) {
|
||||
if (!\class_exists($server['handler'])) {
|
||||
echo "process error: class {$server['handler']} not exists\r\n";
|
||||
continue;
|
||||
}
|
||||
$listen = new Worker($server['listen'] ?? null, $server['context'] ?? []);
|
||||
if (isset($server['listen'])) {
|
||||
echo "listen: {$server['listen']}\n";
|
||||
}
|
||||
$instance = Container::make($server['handler'], $server['constructor'] ?? []);
|
||||
\worker_bind($listen, $instance);
|
||||
$listen->listen();
|
||||
}
|
||||
|
||||
if (isset($config['handler'])) {
|
||||
if (!\class_exists($config['handler'])) {
|
||||
echo "process error: class {$config['handler']} not exists\r\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$instance = Container::make($config['handler'], $config['constructor'] ?? []);
|
||||
\worker_bind($worker, $instance);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Phar support.
|
||||
* Compatible with the 'realpath' function in the phar file.
|
||||
*
|
||||
* @param string $file_path
|
||||
* @return string
|
||||
*/
|
||||
function get_realpath(string $file_path): string
|
||||
{
|
||||
if (\strpos($file_path, 'phar://') === 0) {
|
||||
return $file_path;
|
||||
} else {
|
||||
return \realpath($file_path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
function is_phar()
|
||||
{
|
||||
return \class_exists(\Phar::class, false) && Phar::running();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
function cpu_count()
|
||||
{
|
||||
// Windows does not support the number of processes setting.
|
||||
if (\DIRECTORY_SEPARATOR === '\\') {
|
||||
return 1;
|
||||
}
|
||||
$count = 4;
|
||||
if (\is_callable('shell_exec')) {
|
||||
if (\strtolower(PHP_OS) === 'darwin') {
|
||||
$count = (int)\shell_exec('sysctl -n machdep.cpu.core_count');
|
||||
} else {
|
||||
$count = (int)\shell_exec('nproc');
|
||||
}
|
||||
}
|
||||
return $count > 0 ? $count : 4;
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace support\View;
|
||||
namespace support\view;
|
||||
|
||||
use Jenssegers\Blade\Blade as BladeView;
|
||||
use Webman\View;
|
||||
@@ -20,7 +20,7 @@ use Webman\View;
|
||||
/**
|
||||
* Class Blade
|
||||
* composer require jenssegers/blade
|
||||
* @package Support\View
|
||||
* @package support\view
|
||||
*/
|
||||
class Blade implements View
|
||||
{
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace support\View;
|
||||
namespace support\view;
|
||||
|
||||
use Webman\View;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class Raw
|
||||
* @package Support\View
|
||||
* @package support\view
|
||||
*/
|
||||
class Raw implements View
|
||||
{
|
||||
@@ -51,14 +51,14 @@ class Raw implements View
|
||||
$view_suffix = \config("{$config_prefix}view.options.view_suffix", 'html');
|
||||
$app = $app === null ? $request->app : $app;
|
||||
$base_view_path = $plugin ? \base_path() . "/plugin/$plugin/app" : \app_path();
|
||||
$view_path = $app === '' ? "$base_view_path/view/$template.$view_suffix" : "$base_view_path/$app/view/$template.$view_suffix";
|
||||
$__template_path__ = $app === '' ? "$base_view_path/view/$template.$view_suffix" : "$base_view_path/$app/view/$template.$view_suffix";
|
||||
|
||||
\extract(static::$_vars, \EXTR_SKIP);
|
||||
\extract($vars, \EXTR_SKIP);
|
||||
\extract(static::$_vars);
|
||||
\extract($vars);
|
||||
\ob_start();
|
||||
// Try to include php file.
|
||||
try {
|
||||
include $view_path;
|
||||
include $__template_path__;
|
||||
} catch (Throwable $e) {
|
||||
static::$_vars = [];
|
||||
\ob_end_clean();
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace support\View;
|
||||
namespace support\view;
|
||||
|
||||
use think\Template;
|
||||
use Webman\View;
|
||||
|
||||
/**
|
||||
* Class Blade
|
||||
* @package Support\View
|
||||
* @package support\view
|
||||
*/
|
||||
class ThinkPHP implements View
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace support\View;
|
||||
namespace support\view;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
@@ -20,7 +20,7 @@ use Webman\View;
|
||||
|
||||
/**
|
||||
* Class Blade
|
||||
* @package Support\View
|
||||
* @package support\view
|
||||
*/
|
||||
class Twig implements View
|
||||
{
|
||||
|
||||
116
vendor/workerman/webman-framework/src/windows.php
vendored
116
vendor/workerman/webman-framework/src/windows.php
vendored
@@ -1,116 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Start file for windows
|
||||
*/
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use process\Monitor;
|
||||
use support\App;
|
||||
use Dotenv\Dotenv;
|
||||
use Workerman\Worker;
|
||||
|
||||
ini_set('display_errors', 'on');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (class_exists('Dotenv\Dotenv') && file_exists(base_path() . '/.env')) {
|
||||
if (method_exists('Dotenv\Dotenv', 'createUnsafeImmutable')) {
|
||||
Dotenv::createUnsafeImmutable(base_path())->load();
|
||||
} else {
|
||||
Dotenv::createMutable(base_path())->load();
|
||||
}
|
||||
}
|
||||
|
||||
App::loadAllConfig(['route']);
|
||||
|
||||
$error_reporting = config('app.error_reporting');
|
||||
if (isset($error_reporting)) {
|
||||
error_reporting($error_reporting);
|
||||
}
|
||||
|
||||
$runtime_process_path = runtime_path() . DIRECTORY_SEPARATOR . '/windows';
|
||||
if (!is_dir($runtime_process_path)) {
|
||||
mkdir($runtime_process_path);
|
||||
}
|
||||
$process_files = [
|
||||
__DIR__ . DIRECTORY_SEPARATOR . 'start.php'
|
||||
];
|
||||
foreach (config('process', []) as $process_name => $config) {
|
||||
$process_files[] = write_process_file($runtime_process_path, $process_name, '');
|
||||
}
|
||||
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($project['process'] ?? [] as $process_name => $config) {
|
||||
$process_files[] = write_process_file($runtime_process_path, $process_name, "$firm.$name");
|
||||
}
|
||||
}
|
||||
foreach ($projects['process'] ?? [] as $process_name => $config) {
|
||||
$process_files[] = write_process_file($runtime_process_path, $process_name, $firm);
|
||||
}
|
||||
}
|
||||
|
||||
function write_process_file($runtime_process_path, $process_name, $firm)
|
||||
{
|
||||
$process_param = $firm ? "plugin.$firm.$process_name" : $process_name;
|
||||
$config_param = $firm ? "config('plugin.$firm.process')['$process_name']" : "config('process')['$process_name']";
|
||||
$file_content = <<<EOF
|
||||
<?php
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use Workerman\Worker;
|
||||
use Webman\Config;
|
||||
use support\App;
|
||||
|
||||
ini_set('display_errors', 'on');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (is_callable('opcache_reset')) {
|
||||
opcache_reset();
|
||||
}
|
||||
|
||||
App::loadAllConfig(['route']);
|
||||
|
||||
worker_start('$process_param', $config_param);
|
||||
|
||||
if (DIRECTORY_SEPARATOR != "/") {
|
||||
Worker::\$logFile = config('server')['log_file'] ?? Worker::\$logFile;
|
||||
}
|
||||
|
||||
Worker::runAll();
|
||||
|
||||
EOF;
|
||||
$process_file = $runtime_process_path . DIRECTORY_SEPARATOR . "start_$process_param.php";
|
||||
file_put_contents($process_file, $file_content);
|
||||
return $process_file;
|
||||
}
|
||||
|
||||
if ($monitor_config = config('process.monitor.constructor')) {
|
||||
$monitor = new Monitor(...array_values($monitor_config));
|
||||
}
|
||||
|
||||
function popen_processes($process_files)
|
||||
{
|
||||
$cmd = "php " . implode(' ', $process_files);
|
||||
$descriptorspec = [STDIN, STDOUT, STDOUT];
|
||||
$resource = proc_open($cmd, $descriptorspec, $pipes);
|
||||
if (!$resource) {
|
||||
exit("Can not execute $cmd\r\n");
|
||||
}
|
||||
return $resource;
|
||||
}
|
||||
|
||||
$resource = popen_processes($process_files);
|
||||
echo "\r\n";
|
||||
while (1) {
|
||||
sleep(1);
|
||||
if (!empty($monitor) && $monitor->checkAllFilesChange()) {
|
||||
$status = proc_get_status($resource);
|
||||
$pid = $status['pid'];
|
||||
shell_exec("taskkill /F /T /PID $pid");
|
||||
proc_close($resource);
|
||||
$resource = popen_processes($process_files);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ namespace Workerman\Connection;
|
||||
/**
|
||||
* ConnectionInterface.
|
||||
*/
|
||||
#[\AllowDynamicProperties]
|
||||
abstract class ConnectionInterface
|
||||
{
|
||||
/**
|
||||
|
||||
26
vendor/workerman/workerman/Events/Select.php
vendored
26
vendor/workerman/workerman/Events/Select.php
vendored
@@ -13,6 +13,9 @@
|
||||
*/
|
||||
namespace Workerman\Events;
|
||||
|
||||
use Throwable;
|
||||
use Workerman\Worker;
|
||||
|
||||
/**
|
||||
* select eventloop
|
||||
*/
|
||||
@@ -211,6 +214,7 @@ class Select implements EventInterface
|
||||
*/
|
||||
protected function tick()
|
||||
{
|
||||
$tasks_to_insert = [];
|
||||
while (!$this->_scheduler->isEmpty()) {
|
||||
$scheduler_data = $this->_scheduler->top();
|
||||
$timer_id = $scheduler_data['data'];
|
||||
@@ -228,14 +232,28 @@ class Select implements EventInterface
|
||||
$task_data = $this->_eventTimer[$timer_id];
|
||||
if ($task_data[2] === self::EV_TIMER) {
|
||||
$next_run_time = $time_now + $task_data[3];
|
||||
$this->_scheduler->insert($timer_id, -$next_run_time);
|
||||
$tasks_to_insert[] = [$timer_id, -$next_run_time];
|
||||
}
|
||||
try {
|
||||
\call_user_func_array($task_data[0], $task_data[1]);
|
||||
} catch (Throwable $e) {
|
||||
Worker::stopAll(250, $e);
|
||||
}
|
||||
\call_user_func_array($task_data[0], $task_data[1]);
|
||||
if (isset($this->_eventTimer[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) {
|
||||
$this->del($timer_id, self::EV_TIMER_ONCE);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach ($tasks_to_insert as $item) {
|
||||
$this->_scheduler->insert($item[0], $item[1]);
|
||||
}
|
||||
if (!$this->_scheduler->isEmpty()) {
|
||||
$scheduler_data = $this->_scheduler->top();
|
||||
$next_run_time = -$scheduler_data['priority'];
|
||||
$time_now = \microtime(true);
|
||||
$this->_selectTimeout = \max((int) (($next_run_time - $time_now) * 1000000), 0);
|
||||
return;
|
||||
}
|
||||
$this->_selectTimeout = 100000000;
|
||||
@@ -275,10 +293,8 @@ class Select implements EventInterface
|
||||
|
||||
} else {
|
||||
$this->_selectTimeout >= 1 && usleep($this->_selectTimeout);
|
||||
$ret = false;
|
||||
}
|
||||
|
||||
|
||||
if (!$this->_scheduler->isEmpty()) {
|
||||
$this->tick();
|
||||
}
|
||||
|
||||
@@ -1,584 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of workerman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author 爬山虎<blogdaren@163.com>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Workerman\Protocols\FastCGI;
|
||||
|
||||
use Workerman\Worker;
|
||||
use Workerman\Protocols\Fcgi;
|
||||
|
||||
class Request
|
||||
{
|
||||
/**
|
||||
* allowed request methods
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
const ALLOWED_REQUEST_METHODS = ['GET', 'POST', 'PUT', 'HEAD', 'DELETE'];
|
||||
|
||||
/**
|
||||
* allowed FastCGI roles
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
const ALLOWED_ROLES = [
|
||||
Fcgi::FCGI_RESPONDER,
|
||||
Fcgi::FCGI_AUTHORIZER,
|
||||
Fcgi::FCGI_FILTER,
|
||||
];
|
||||
|
||||
/**
|
||||
* allowed content type
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
const ALLOWED_CONTENT_TYPES = [
|
||||
self::MIME_URL_ENCODED_FORM_DATA,
|
||||
self::MIME_MULTI_PART_FORM_DATA,
|
||||
self::MIME_JSON_DATA,
|
||||
];
|
||||
|
||||
/**
|
||||
* the MIME type of url encoded form data
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const MIME_URL_ENCODED_FORM_DATA = 'application/x-www-form-urlencoded';
|
||||
|
||||
/**
|
||||
* the MIME type of multi part form data
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const MIME_MULTI_PART_FORM_DATA = 'multipart/form-data; boundary=__X_FASTCGI_CLIENT_BOUNDARY__';
|
||||
|
||||
/**
|
||||
* the MIME type of json data
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const MIME_JSON_DATA = 'application/json';
|
||||
|
||||
/**
|
||||
* FastCGI script to be executed
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $script = '';
|
||||
|
||||
/**
|
||||
* content MIME Type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $contentType = self::MIME_URL_ENCODED_FORM_DATA;
|
||||
|
||||
/**
|
||||
* content data
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $content = '';
|
||||
|
||||
/**
|
||||
* content length
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $contentLength = 0;
|
||||
|
||||
/**
|
||||
* request uri
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $requestUri = '';
|
||||
|
||||
/**
|
||||
* request method
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $requestMethod = 'GET';
|
||||
|
||||
/**
|
||||
* query string
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $queryString = '';
|
||||
|
||||
/**
|
||||
* gateway inteface
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $gatewayInterface = 'FastCGI/1.0';
|
||||
|
||||
/**
|
||||
* server software
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $serverSoftware = 'FastCGI-Client';
|
||||
|
||||
/**
|
||||
* server name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $serverName = 'localhost';
|
||||
|
||||
/**
|
||||
* request id
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $requestId = 0;
|
||||
|
||||
/**
|
||||
* proxy counter for request id
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
static protected $_idCounter = 1;
|
||||
|
||||
/**
|
||||
* custom params
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $customParams = [];
|
||||
|
||||
/**
|
||||
* indicates FastCGI server to keep connection alive or not after finishing one request
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $keepAlive = true;
|
||||
|
||||
/**
|
||||
* indicates FastCGI server to play the specific role
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $role = Fcgi::FCGI_RESPONDER;
|
||||
|
||||
/**
|
||||
* @brief __construct
|
||||
*
|
||||
* @param string $script
|
||||
* @param string|array $content
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($script = '', $content = '')
|
||||
{
|
||||
$this->setScript($script);
|
||||
$this->setContent($content);
|
||||
(self::$_idCounter >= (1 << 16)) && self::$_idCounter = 0;
|
||||
$this->requestId = self::$_idCounter++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get request id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRequestId()
|
||||
{
|
||||
return $this->requestId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set the role
|
||||
*
|
||||
* @param int $role
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setRole($role = Fcgi::FCGI_RESPONDER)
|
||||
{
|
||||
if(!is_int($role) || !in_array($role, static::ALLOWED_ROLES))
|
||||
{
|
||||
$role = Fcgi::FCGI_RESPONDER;
|
||||
}
|
||||
|
||||
$this->role = $role;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get the role
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRole()
|
||||
{
|
||||
return $this->role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set connection alive status
|
||||
*
|
||||
* @param boolean $status
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setKeepAlive($status = true)
|
||||
{
|
||||
$this->keepAlive = !is_bool($status) ? true : $status;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get connection alive status
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getKeepAlive()
|
||||
{
|
||||
return $this->keepAlive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get server software
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getServerSoftware()
|
||||
{
|
||||
return $this->serverSoftware;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set server software
|
||||
*
|
||||
* @param string $software
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setServerSoftware($software)
|
||||
{
|
||||
if(!empty($software) && \is_string($software))
|
||||
{
|
||||
$this->serverSoftware = $software;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get server name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getServerName()
|
||||
{
|
||||
return $this->serverName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set server name
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setServerName($name)
|
||||
{
|
||||
if(!empty($name) && \is_string($name))
|
||||
{
|
||||
$this->serverName = $name;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get content type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
return $this->contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set content type
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setContentType($type)
|
||||
{
|
||||
if(!\is_string($type) || !in_array($type, static::ALLOWED_CONTENT_TYPES))
|
||||
{
|
||||
$type = static::MIME_URL_ENCODED_FORM_DATA;
|
||||
}
|
||||
|
||||
$this->contentType = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set content
|
||||
*
|
||||
* @param string|array $content
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
if(\is_string($content) || \is_array($content))
|
||||
{
|
||||
$this->content = !\is_string($content) ? http_build_query($content) : $content;
|
||||
$this->contentLength = \strlen($this->content);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get content length
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getContentLength()
|
||||
{
|
||||
return $this->contentLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get gateway interface
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGatewayInterface()
|
||||
{
|
||||
return $this->gatewayInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set FastCGI script
|
||||
*
|
||||
* @param string $filename
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setScript($filename)
|
||||
{
|
||||
if(!empty($filename) && \is_string($filename))
|
||||
{
|
||||
$this->script = $filename;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get FastCGI script
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getScript()
|
||||
{
|
||||
return $this->script;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set custom params
|
||||
*
|
||||
* @param array $pair
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setCustomParams($pair)
|
||||
{
|
||||
if(!\is_array($pair)) return $this;
|
||||
|
||||
foreach($pair as $k => $v)
|
||||
{
|
||||
if(!\is_string($v)) continue;
|
||||
$this->customParams[$k] = $v;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief append custom params
|
||||
*
|
||||
* @param array $pair
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function appendCustomParams($pair)
|
||||
{
|
||||
if(\is_array($pair))
|
||||
{
|
||||
$this->customParams = \array_merge($this->customParams, $pair);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief reset custom params
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function resetCustomParams()
|
||||
{
|
||||
$this->customParams = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set query string
|
||||
*
|
||||
* @param string|array $string
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setQueryString($data = '')
|
||||
{
|
||||
if(\is_string($data) || \is_array($data))
|
||||
{
|
||||
$this->queryString = !\is_string($data) ? http_build_query($data) : $data;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get query string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryString()
|
||||
{
|
||||
return $this->queryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get custom params
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCustomParams()
|
||||
{
|
||||
return $this->customParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get all params
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParams()
|
||||
{
|
||||
return \array_merge($this->customParams, $this->getDefaultParams());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get default params
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDefaultParams()
|
||||
{
|
||||
return [
|
||||
'GATEWAY_INTERFACE' => $this->getGatewayInterface(),
|
||||
'SCRIPT_FILENAME' => $this->getScript(),
|
||||
'REQUEST_METHOD' => $this->getRequestMethod(),
|
||||
'REQUEST_URI' => $this->getRequestUri(),
|
||||
'QUERY_STRING' => $this->getQueryString(),
|
||||
'CONTENT_TYPE' => $this->getContentType(),
|
||||
'CONTENT_LENGTH' => $this->getContentLength(),
|
||||
'SERVER_NAME' => $this->getServerName(),
|
||||
'SERVER_SOFTWARE' => $this->getServerSoftware(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set request method
|
||||
*
|
||||
* @param string $method
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setRequestMethod($method = 'GET')
|
||||
{
|
||||
if(!\is_string($method) || !in_array(strtoupper($method), static::ALLOWED_REQUEST_METHODS))
|
||||
{
|
||||
$method = 'GET';
|
||||
}
|
||||
|
||||
$this->requestMethod = strtoupper($method);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get request method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRequestMethod()
|
||||
{
|
||||
return $this->requestMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get request uri
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRequestUri()
|
||||
{
|
||||
return $this->requestUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set request uri
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setRequestUri($uri)
|
||||
{
|
||||
if(\is_string($uri))
|
||||
{
|
||||
$this->requestUri = $uri;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of workerman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author 爬山虎<blogdaren@163.com>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace Workerman\Protocols\FastCGI;
|
||||
|
||||
class Response
|
||||
{
|
||||
/**
|
||||
* success status
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const STATUS_OK = 200;
|
||||
|
||||
/**
|
||||
* invalid status
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const STATUS_INVALID = -1;
|
||||
|
||||
/**
|
||||
* the request id from response
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_requestId;
|
||||
|
||||
/**
|
||||
* the stdout from response
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_stdout = '';
|
||||
|
||||
/**
|
||||
* the stderr from response
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_stderr = '';
|
||||
|
||||
/**
|
||||
* the origin header from response
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_header = '';
|
||||
|
||||
/**
|
||||
* the origin body from response
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_body = '';
|
||||
|
||||
/**
|
||||
* @brief __construct
|
||||
*
|
||||
* @param int $request_id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($request_id = 0)
|
||||
{
|
||||
$this->setRequestId($request_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set request id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function setRequestId($id = 0)
|
||||
{
|
||||
$this->_requestId = (\is_int($id) && $id > 0) ? $id : -1;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set stdout
|
||||
*
|
||||
* @param string $stdout
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setStdout($stdout = '')
|
||||
{
|
||||
if(\is_string($stdout))
|
||||
{
|
||||
$this->_stdout = $stdout;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get stdout
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStdout()
|
||||
{
|
||||
return $this->_stdout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set stderr
|
||||
*
|
||||
* @param string $stderr
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setStderr($stderr = '')
|
||||
{
|
||||
if(\is_string($stderr))
|
||||
{
|
||||
$this->_stderr = $stderr;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get stderr
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getStderr()
|
||||
{
|
||||
return $this->_stderr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get header
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHeader()
|
||||
{
|
||||
return $this->_header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get body
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBody()
|
||||
{
|
||||
return $this->_body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get request id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRequestId()
|
||||
{
|
||||
return $this->_requestId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief format response output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function formatOutput()
|
||||
{
|
||||
$status = static::STATUS_INVALID;
|
||||
$header = [];
|
||||
$body = '';
|
||||
$crlf_pos = \strpos($this->getStdout(), "\r\n\r\n");
|
||||
|
||||
if(false !== $crlf_pos)
|
||||
{
|
||||
$status = static::STATUS_OK;
|
||||
$head = \substr($this->getStdout(), 0, $crlf_pos);
|
||||
$body = \substr($this->getStdout(), $crlf_pos + 4);
|
||||
$this->_header = \substr($this->getStdout(), 0, $crlf_pos + 4);
|
||||
$this->_body = $body;
|
||||
$header_lines = \explode(PHP_EOL, $head);
|
||||
|
||||
foreach($header_lines as $line)
|
||||
{
|
||||
if(preg_match('/([\w-]+):\s*(.*)$/', $line, $matches))
|
||||
{
|
||||
$name = \trim($matches[1]);
|
||||
$value = \trim($matches[2]);
|
||||
|
||||
if('status' === strtolower($name))
|
||||
{
|
||||
$pos = strpos($value, ' ') ;
|
||||
$status = false !== $pos ? \substr($value, 0, $pos) : static::STATUS_OK;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!array_key_exists($name, $header))
|
||||
{
|
||||
$header[$name] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
!\is_array($header[$name]) && $header[$name] = [$header[$name]];
|
||||
$header[$name][] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output = [
|
||||
'requestId' => $this->getRequestId(),
|
||||
'status' => $status,
|
||||
'stderr' => $this->getStderr(),
|
||||
'header' => $header,
|
||||
'body' => $body,
|
||||
];
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
541
vendor/workerman/workerman/Protocols/Fcgi.php
vendored
541
vendor/workerman/workerman/Protocols/Fcgi.php
vendored
@@ -1,541 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of workerman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author 爬山虎<blogdaren@163.com>
|
||||
* @protocol http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Workerman\Protocols;
|
||||
|
||||
use Workerman\Worker;
|
||||
use Workerman\Connection\TcpConnection;
|
||||
use Workerman\Protocols\FastCGI\Request;
|
||||
use Workerman\Protocols\FastCGI\Response;
|
||||
|
||||
class Fcgi
|
||||
{
|
||||
/**
|
||||
* the version of fcgi protocol
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_VERSION_1 = 1;
|
||||
|
||||
/**
|
||||
* the fixed length of FCGI_Header: sizeof(FCGI_Header) === 8
|
||||
*
|
||||
* typedef struct {
|
||||
* unsigned char version;
|
||||
* unsigned char type;
|
||||
* unsigned char requestIdB1;
|
||||
* unsigned char requestIdB0;
|
||||
* unsigned char contentLengthB1;
|
||||
* unsigned char contentLengthB0;
|
||||
* unsigned char paddingLength;
|
||||
* unsigned char reserved;
|
||||
* } FCGI_Header;
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_HEADER_LEN = 8;
|
||||
|
||||
/**
|
||||
* the max length of payload
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_MAX_PAYLOAD_LEN = 65535;
|
||||
|
||||
/**
|
||||
* the reserved bit FCGI_Header
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const FCGI_RESERVED = '';
|
||||
|
||||
/**
|
||||
* the padding bit FCGI_Header
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const FCGI_PADDING = '';
|
||||
|
||||
/**
|
||||
* the record type of FCGI_BEGIN_REQUEST
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_BEGIN_REQUEST = 1;
|
||||
|
||||
/**
|
||||
* the record type of FCGI_ABORT_REQUEST
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_ABORT_REQUEST = 2;
|
||||
|
||||
/**
|
||||
* the record type of FCGI_END_REQUEST
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_END_REQUEST = 3;
|
||||
|
||||
/**
|
||||
* the record type of FCGI_PARAMS
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_PARAMS = 4;
|
||||
|
||||
/**
|
||||
* -------------------------------------
|
||||
* the pseudo record type of FCGI_PARAMS
|
||||
* -------------------------------------
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_PARAMS_END = 4 << 3;
|
||||
|
||||
/**
|
||||
* the record type of FCGI_STDIN
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_STDIN = 5;
|
||||
|
||||
/**
|
||||
* -------------------------------------
|
||||
* the pseudo record type of FCGI_STDIN
|
||||
* -------------------------------------
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_STDIN_END = 5 << 4;
|
||||
|
||||
/**
|
||||
* the record type of FCGI_STDOUT
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_STDOUT = 6;
|
||||
|
||||
/**
|
||||
* the record type of FCGI_STDERR
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_STDERR = 7;
|
||||
|
||||
/**
|
||||
* the record type of FCGI_DATA
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_DATA = 8;
|
||||
|
||||
/**
|
||||
* the record type of FCGI_GET_VALUES
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_GET_VALUES = 9;
|
||||
|
||||
/**
|
||||
* the record type of FCGI_GET_VALUES_RESULT
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_GET_VALUES_RESULT = 10;
|
||||
|
||||
/**
|
||||
* the record type of FCGI_UNKNOWN_TYPE
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_UNKNOWN_TYPE = 11;
|
||||
|
||||
/**
|
||||
* the role type of FCGI_RESPONDER
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_RESPONDER = 1;
|
||||
|
||||
/**
|
||||
* the role type of FCGI_AUTHORIZER
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_AUTHORIZER = 2;
|
||||
|
||||
/**
|
||||
* the role type of FCGI_FILTER
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_FILTER = 3;
|
||||
|
||||
/**
|
||||
* the protocol status of FCGI_REQUEST_COMPLETE
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_REQUEST_COMPLETE = 0;
|
||||
|
||||
/**
|
||||
* the protocol status of FCGI_CANT_MPX_CONN
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_CANT_MPX_CONN = 1;
|
||||
|
||||
/**
|
||||
* the protocol status of FCGI_OVERLOADED
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_OVERLOADED = 2;
|
||||
|
||||
/**
|
||||
* the protocol status of FCGI_UNKNOWN_ROLE
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const FCGI_UNKNOWN_ROLE = 3;
|
||||
|
||||
/**
|
||||
* the request object
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
static private $_request = NULL;
|
||||
|
||||
/**
|
||||
* check the integrity of the package
|
||||
*
|
||||
* @param string $buffer
|
||||
* @param TcpConnection $connection
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function input($buffer, TcpConnection $connection)
|
||||
{
|
||||
$recv_len = \strlen($buffer);
|
||||
|
||||
if($recv_len < static::FCGI_HEADER_LEN) return 0;
|
||||
|
||||
if(!isset($connection->packetLength)) $connection->packetLength = 0;
|
||||
|
||||
$data = \unpack("Cversion/Ctype/nrequestId/ncontentLength/CpaddingLength/Creserved", $buffer);
|
||||
if(false === $data) return 0;
|
||||
|
||||
$chunk_len = static::FCGI_HEADER_LEN + $data['contentLength'] + $data['paddingLength'];
|
||||
if($recv_len < $chunk_len) return 0;
|
||||
|
||||
if(static::FCGI_END_REQUEST != $data['type'])
|
||||
{
|
||||
$connection->packetLength += $chunk_len;
|
||||
$next_chunk_len = static::input(\substr($buffer, $chunk_len), $connection);
|
||||
|
||||
if(0 == $next_chunk_len)
|
||||
{
|
||||
//important!! don't forget to reset to zero byte!!
|
||||
$connection->packetLength = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$connection->packetLength += $chunk_len;
|
||||
}
|
||||
|
||||
//check package length exceeds the max package length or not
|
||||
if($connection->packetLength > $connection->maxPackageSize)
|
||||
{
|
||||
$msg = "Exception: recv error package. package_length = {$connection->packetLength} ";
|
||||
$msg .= "exceeds the limit {$connection->maxPackageSize}" . PHP_EOL;
|
||||
Worker::safeEcho($msg);
|
||||
$connection->close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $connection->packetLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief decode package
|
||||
*
|
||||
* @param string $buffer
|
||||
* @param TcpConnection $connection
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function decode($buffer, TcpConnection $connection)
|
||||
{
|
||||
$offset = 0;
|
||||
$stdout = $stderr = '';
|
||||
|
||||
do
|
||||
{
|
||||
$header_buffer = \substr($buffer, $offset, static::FCGI_HEADER_LEN);
|
||||
$data = \unpack("Cversion/Ctype/nrequestId/ncontentLength/CpaddingLength/Creserved", $header_buffer);
|
||||
|
||||
//we are not going to throw new \Exception("Failed to unpack header data from the binary buffer.");
|
||||
//but just break out of the loop to avoid bring much unnecessary TCP connections with TIME_WAIT status
|
||||
if(false === $data)
|
||||
{
|
||||
$stderr = "Failed to unpack header data from the binary buffer";
|
||||
Worker::safeEcho($stderr);
|
||||
$connection->close();
|
||||
break;
|
||||
}
|
||||
|
||||
$chunk_len = static::FCGI_HEADER_LEN + $data['contentLength'] + $data['paddingLength'];
|
||||
$body_buffer = \substr($buffer, $offset + static::FCGI_HEADER_LEN, $chunk_len - static::FCGI_HEADER_LEN);
|
||||
|
||||
|
||||
switch($data['type'])
|
||||
{
|
||||
case static::FCGI_STDOUT:
|
||||
$payload = \unpack("a{$data['contentLength']}contentData/a{$data['paddingLength']}paddingData", $body_buffer);
|
||||
$stdout .= $payload['contentData'];
|
||||
break;
|
||||
case static::FCGI_STDERR:
|
||||
$payload = \unpack("a{$data['contentLength']}contentData/a{$data['paddingLength']}paddingData", $body_buffer);
|
||||
$stderr .= $payload['contentData'];
|
||||
break;
|
||||
case static::FCGI_END_REQUEST:
|
||||
$payload = \unpack("NappStatus/CprotocolStatus/a3reserved", $body_buffer);
|
||||
$result = static::checkProtocolStatus($payload['protocolStatus']);
|
||||
|
||||
if(0 <> $result['code'])
|
||||
{
|
||||
$stderr = $result['msg'];
|
||||
Worker::safeEcho($stderr);
|
||||
$connection->close();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//not support yet
|
||||
$payload = '';
|
||||
break;
|
||||
}
|
||||
|
||||
$offset += $chunk_len;
|
||||
}while($offset < $connection->packetLength);
|
||||
|
||||
//important!! don't forget to reset to zero byte!!
|
||||
$connection->packetLength = 0;
|
||||
|
||||
//build response
|
||||
$response = new Response();
|
||||
$output = $response->setRequestId($data['requestId'] ?? -1)
|
||||
->setStdout($stdout)
|
||||
->setStderr($stderr)
|
||||
->formatOutput();
|
||||
|
||||
//trigger user callback as onResponse
|
||||
if(!empty($connection->onResponse) && is_callable($connection->onResponse))
|
||||
{
|
||||
try {
|
||||
\call_user_func($connection->onResponse, $connection, $response);
|
||||
} catch (\Exception $e) {
|
||||
$msg = "Exception: onResponse: " . $e->getMessage();
|
||||
Worker::safeEcho($msg);
|
||||
$connection->close();
|
||||
} catch (\Error $e) {
|
||||
$msg = "Exception: onResponse: " . $e->getMessage();
|
||||
Worker::safeEcho($msg);
|
||||
$connection->close();
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief encode package
|
||||
*
|
||||
* @param Request $request
|
||||
* @param TcpConnection $connection
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function encode(Request $request, TcpConnection $connection)
|
||||
{
|
||||
if(!$request instanceof Request) return '';
|
||||
|
||||
static::$_request = $request;
|
||||
|
||||
$packet = '';
|
||||
$packet .= static::createPacket(static::FCGI_BEGIN_REQUEST);
|
||||
$packet .= static::createPacket(static::FCGI_PARAMS);
|
||||
$packet .= static::createPacket(static::FCGI_PARAMS_END);
|
||||
$packet .= static::createPacket(static::FCGI_STDIN);
|
||||
$packet .= static::createPacket(static::FCGI_STDIN_END);
|
||||
|
||||
$connection->maxSendBufferSize = TcpConnection::$defaultMaxSendBufferSize * 10;
|
||||
$packet_len = \strlen($packet);
|
||||
|
||||
if($packet_len > $connection->maxSendBufferSize)
|
||||
{
|
||||
$msg = "Exception: send error package. package_length = {$packet_len} ";
|
||||
$msg .= "exceeds the limit {$connection->maxSendBufferSize}" . PHP_EOL;
|
||||
Worker::safeEcho($msg);
|
||||
$connection->close();
|
||||
return '';
|
||||
}
|
||||
|
||||
return $packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief pack payload
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
static private function packPayload($type = '')
|
||||
{
|
||||
$payload = '';
|
||||
|
||||
switch($type)
|
||||
{
|
||||
case static::FCGI_BEGIN_REQUEST:
|
||||
$payload = \pack(
|
||||
"nCa5",
|
||||
static::$_request->getRole(),
|
||||
static::$_request->getKeepAlive(),
|
||||
static::FCGI_RESERVED
|
||||
);
|
||||
break;
|
||||
case static::FCGI_PARAMS:
|
||||
case static::FCGI_PARAMS_END:
|
||||
$payload = '';
|
||||
$params = (static::FCGI_PARAMS == $type) ? static::$_request->getParams() : [];
|
||||
foreach($params as $name => $value)
|
||||
{
|
||||
$name_len = \strlen($name);
|
||||
$value_len = \strlen($value);
|
||||
$format = [
|
||||
$name_len > 127 ? 'N' : 'C',
|
||||
$value_len > 127 ? 'N' : 'C',
|
||||
"a{$name_len}",
|
||||
"a{$value_len}",
|
||||
];
|
||||
$format = implode ('', $format);
|
||||
$payload .= \pack(
|
||||
$format,
|
||||
$name_len > 127 ? ($name_len | 0x80000000) : $name_len,
|
||||
$value_len > 127 ? ($value_len | 0x80000000) : $value_len,
|
||||
$name,
|
||||
$value
|
||||
);
|
||||
}
|
||||
break;
|
||||
case static::FCGI_STDIN:
|
||||
case static::FCGI_ABORT_REQUEST:
|
||||
case static::FCGI_DATA:
|
||||
$payload = \pack("a" . static::$_request->getContentLength(), static::$_request->getContent());
|
||||
break;
|
||||
case static::FCGI_STDIN_END:
|
||||
$payload = '';
|
||||
break;
|
||||
case static::FCGI_UNKNOWN_TYPE:
|
||||
$payload = \pack("Ca7", static::FCGI_UNKNOWN_TYPE, static::FCGI_RESERVED);
|
||||
break;
|
||||
default:
|
||||
$payload = '';
|
||||
break;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief create request packet
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
static public function createPacket($type = '')
|
||||
{
|
||||
$packet = '';
|
||||
$offset = 0;
|
||||
$payload = static::packPayload($type);
|
||||
$total_len = \strlen($payload);
|
||||
|
||||
//don't forget to reset pseudo record type to normal
|
||||
$type == static::FCGI_PARAMS_END && $type = static::FCGI_PARAMS;
|
||||
$type == static::FCGI_STDIN_END && $type = static::FCGI_STDIN;
|
||||
|
||||
//maybe need to split payload into many chunks
|
||||
do
|
||||
{
|
||||
$chunk = \substr($payload, $offset, static::FCGI_MAX_PAYLOAD_LEN);
|
||||
$chunk_len = \strlen($chunk);
|
||||
$remainder = \abs($chunk_len % 8);
|
||||
$padding_len = $remainder > 0 ? 8 - $remainder : 0;
|
||||
|
||||
$header = \pack(
|
||||
"CCnnCC",
|
||||
static::FCGI_VERSION_1,
|
||||
$type,
|
||||
static::$_request->getRequestId(),
|
||||
$chunk_len,
|
||||
$padding_len,
|
||||
static::FCGI_RESERVED
|
||||
);
|
||||
|
||||
$padding = \pack("a{$padding_len}", static::FCGI_PADDING);
|
||||
$packet .= $header . $chunk . $padding;
|
||||
$offset += $chunk_len;
|
||||
}while($offset < $total_len);
|
||||
|
||||
return $packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief check the protocol status from FCGI_END_REQUEST body
|
||||
*
|
||||
* @param int $status
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
static public function checkProtocolStatus($status = 0)
|
||||
{
|
||||
switch($status)
|
||||
{
|
||||
case static::FCGI_REQUEST_COMPLETE:
|
||||
$msg = 'Accepted: request completed ok';
|
||||
break;
|
||||
case static::FCGI_CANT_MPX_CONN:
|
||||
$msg = 'Rejected: FastCGI server does not support concurrent processing';
|
||||
break;
|
||||
case static::FCGI_OVERLOADED:
|
||||
$msg = 'Rejected: FastCGI server run out of resources or reached the limit';
|
||||
break;
|
||||
case static::FCGI_UNKNOWN_ROLE:
|
||||
$msg = 'Rejected: FastCGI server not support the specified role';
|
||||
break;
|
||||
default:
|
||||
$msg = 'Rejected: FastCGI server does not know what happened';
|
||||
break;
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => $status,
|
||||
'msg' => $msg,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
49
vendor/workerman/workerman/Protocols/Http.php
vendored
49
vendor/workerman/workerman/Protocols/Http.php
vendored
@@ -94,60 +94,58 @@ class Http
|
||||
*/
|
||||
public static function input($recv_buffer, TcpConnection $connection)
|
||||
{
|
||||
static $input = array();
|
||||
static $input = [];
|
||||
if (!isset($recv_buffer[512]) && isset($input[$recv_buffer])) {
|
||||
return $input[$recv_buffer];
|
||||
}
|
||||
$crlf_pos = \strpos($recv_buffer, "\r\n\r\n");
|
||||
if (false === $crlf_pos) {
|
||||
// Judge whether the package length exceeds the limit.
|
||||
if ($recv_len = \strlen($recv_buffer) >= 16384) {
|
||||
if (\strlen($recv_buffer) >= 16384) {
|
||||
$connection->close("HTTP/1.1 413 Request Entity Too Large\r\n\r\n", true);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
$head_len = $crlf_pos + 4;
|
||||
$length = $crlf_pos + 4;
|
||||
$method = \strstr($recv_buffer, ' ', true);
|
||||
|
||||
if ($method === 'GET' || $method === 'OPTIONS' || $method === 'HEAD' || $method === 'DELETE') {
|
||||
if (!isset($recv_buffer[512])) {
|
||||
$input[$recv_buffer] = $head_len;
|
||||
if (\count($input) > 512) {
|
||||
unset($input[key($input)]);
|
||||
}
|
||||
}
|
||||
return $head_len;
|
||||
} else if ($method !== 'POST' && $method !== 'PUT' && $method !== 'PATCH') {
|
||||
if (!\in_array($method, ['GET', 'POST', 'OPTIONS', 'HEAD', 'DELETE', 'PUT', 'PATCH'])) {
|
||||
$connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$header = \substr($recv_buffer, 0, $crlf_pos);
|
||||
$length = false;
|
||||
if ($pos = \strpos($header, "\r\nContent-Length: ")) {
|
||||
$length = $head_len + (int)\substr($header, $pos + 18, 10);
|
||||
$length = $length + (int)\substr($header, $pos + 18, 10);
|
||||
$has_content_length = true;
|
||||
} else if (\preg_match("/\r\ncontent-length: ?(\d+)/i", $header, $match)) {
|
||||
$length = $head_len + $match[1];
|
||||
$length = $length + $match[1];
|
||||
$has_content_length = true;
|
||||
} else {
|
||||
$has_content_length = false;
|
||||
if (false !== stripos($header, "\r\nTransfer-Encoding:")) {
|
||||
$connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ($length !== false) {
|
||||
if (!isset($recv_buffer[512])) {
|
||||
$input[$recv_buffer] = $length;
|
||||
if (\count($input) > 512) {
|
||||
unset($input[key($input)]);
|
||||
}
|
||||
}
|
||||
if ($has_content_length) {
|
||||
if ($length > $connection->maxPackageSize) {
|
||||
$connection->close("HTTP/1.1 413 Request Entity Too Large\r\n\r\n", true);
|
||||
return 0;
|
||||
}
|
||||
return $length;
|
||||
}
|
||||
|
||||
$connection->close("HTTP/1.1 400 Bad Request\r\n\r\n", true);
|
||||
return 0;
|
||||
if (!isset($recv_buffer[512])) {
|
||||
$input[$recv_buffer] = $length;
|
||||
if (\count($input) > 512) {
|
||||
unset($input[key($input)]);
|
||||
}
|
||||
}
|
||||
|
||||
return $length;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,6 +219,7 @@ class Http
|
||||
$file = $response->file['file'];
|
||||
$offset = $response->file['offset'];
|
||||
$length = $response->file['length'];
|
||||
clearstatcache();
|
||||
$file_size = (int)\filesize($file);
|
||||
$body_len = $length > 0 ? $length : $file_size - $offset;
|
||||
$response->withHeaders(array(
|
||||
|
||||
@@ -520,6 +520,9 @@ class Request
|
||||
{
|
||||
$file = [];
|
||||
$boundary = "\r\n$boundary";
|
||||
if (\strlen($this->_buffer) < $section_start_offset) {
|
||||
return 0;
|
||||
}
|
||||
$section_end_offset = \strpos($this->_buffer, $boundary, $section_start_offset);
|
||||
if (!$section_end_offset) {
|
||||
return 0;
|
||||
@@ -596,7 +599,7 @@ class Request
|
||||
*/
|
||||
protected static function createSessionId()
|
||||
{
|
||||
return \bin2hex(\pack('d', \microtime(true)) . \pack('N', \mt_rand()));
|
||||
return \bin2hex(\pack('d', \microtime(true)) . random_bytes(8));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -410,7 +410,7 @@ class Session
|
||||
public function __destruct()
|
||||
{
|
||||
$this->save();
|
||||
if (\rand(1, static::$gcProbability[1]) <= static::$gcProbability[0]) {
|
||||
if (\random_int(1, static::$gcProbability[1]) <= static::$gcProbability[0]) {
|
||||
$this->gc();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ class FileSessionHandler implements SessionHandlerInterface
|
||||
*/
|
||||
public function write($session_id, $session_data)
|
||||
{
|
||||
$temp_file = static::$_sessionSavePath.uniqid(mt_rand(), true);
|
||||
$temp_file = static::$_sessionSavePath . uniqid(bin2hex(random_bytes(8)), true);
|
||||
if (!\file_put_contents($temp_file, $session_data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
2
vendor/workerman/workerman/Protocols/Ws.php
vendored
2
vendor/workerman/workerman/Protocols/Ws.php
vendored
@@ -355,7 +355,7 @@ class Ws
|
||||
$port = $connection->getRemotePort();
|
||||
$host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port;
|
||||
// Handshake header.
|
||||
$connection->websocketSecKey = \base64_encode(\md5(\mt_rand(), true));
|
||||
$connection->websocketSecKey = \base64_encode(random_bytes(16));
|
||||
$user_header = isset($connection->headers) ? $connection->headers :
|
||||
(isset($connection->wsHttpHeader) ? $connection->wsHttpHeader : null);
|
||||
$user_header_str = '';
|
||||
|
||||
48
vendor/workerman/workerman/Worker.php
vendored
48
vendor/workerman/workerman/Worker.php
vendored
@@ -26,6 +26,7 @@ use \Exception;
|
||||
* Worker class
|
||||
* A container for listening ports
|
||||
*/
|
||||
#[\AllowDynamicProperties]
|
||||
class Worker
|
||||
{
|
||||
/**
|
||||
@@ -33,7 +34,7 @@ class Worker
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '4.0.42';
|
||||
const VERSION = '4.1.4';
|
||||
|
||||
/**
|
||||
* Status starting.
|
||||
@@ -183,7 +184,7 @@ class Worker
|
||||
public $onBufferDrain = null;
|
||||
|
||||
/**
|
||||
* Emitted when worker processes stoped.
|
||||
* Emitted when worker processes stopped.
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
@@ -548,11 +549,13 @@ class Worker
|
||||
{
|
||||
static::checkSapiEnv();
|
||||
static::init();
|
||||
static::lock();
|
||||
static::parseCommand();
|
||||
static::daemonize();
|
||||
static::initWorkers();
|
||||
static::installSignal();
|
||||
static::saveMasterPid();
|
||||
static::lock(\LOCK_UN);
|
||||
static::displayUI();
|
||||
static::forkWorkers();
|
||||
static::resetStd();
|
||||
@@ -629,24 +632,25 @@ class Worker
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function lock()
|
||||
protected static function lock($flag = \LOCK_EX)
|
||||
{
|
||||
$fd = \fopen(static::$_startFile, 'r');
|
||||
if ($fd && !flock($fd, LOCK_EX)) {
|
||||
static::log('Workerman['.static::$_startFile.'] already running.');
|
||||
exit;
|
||||
static $fd;
|
||||
if (\DIRECTORY_SEPARATOR !== '/') {
|
||||
return;
|
||||
}
|
||||
$lock_file = static::$pidFile . '.lock';
|
||||
$fd = $fd ?: \fopen($lock_file, 'a+');
|
||||
if ($fd) {
|
||||
flock($fd, $flag);
|
||||
if ($flag === \LOCK_UN) {
|
||||
fclose($fd);
|
||||
$fd = null;
|
||||
clearstatcache();
|
||||
if (\is_file($lock_file)) {
|
||||
unlink($lock_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function unlock()
|
||||
{
|
||||
$fd = \fopen(static::$_startFile, 'r');
|
||||
$fd && flock($fd, \LOCK_UN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1693,11 +1697,9 @@ class Worker
|
||||
// onWorkerExit
|
||||
if ($worker->onWorkerExit) {
|
||||
try {
|
||||
call_user_func($worker->onWorkerExit, $worker, $status, $pid);
|
||||
} catch (\Exception $e) {
|
||||
static::log("worker[{$worker->name}] onWorkerExit $e");
|
||||
} catch (\Error $e) {
|
||||
static::log("worker[{$worker->name}] onWorkerExit $e");
|
||||
($worker->onWorkerExit)($worker, $status, $pid);
|
||||
} catch (\Throwable $exception) {
|
||||
static::log("worker[{$worker->name}] onWorkerExit $exception");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
vendor/workerman/workerman/composer.json
vendored
2
vendor/workerman/workerman/composer.json
vendored
@@ -24,7 +24,7 @@
|
||||
"source": "https://github.com/walkor/workerman"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4"
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-event": "For better performance. "
|
||||
|
||||
Reference in New Issue
Block a user