Compare commits
No commits in common. "d61c5656c5f814a38ef357c2f3372ea8ccc46467" and "8a317800ef6ae1bd6c013d9ba93bd1e1888c31b7" have entirely different histories.
d61c5656c5
...
8a317800ef
11
README.md
11
README.md
@ -1,22 +1,19 @@
|
|||||||
# socks5-proxy
|
# socks5-proxy
|
||||||
Socks5 proxy written in PHP based on [workerman](https://github.com/walkor/Workerman). Now with username/password authentication according to RFC 1929.
|
Socks5 proxy written in PHP based on [workerman](https://github.com/walkor/Workerman).
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
1. ```git clone https://github.com/walkor/php-socks5```
|
1. ```git clone https://github.com/walkor/php-socks5```
|
||||||
|
|
||||||
2. ```composer install```
|
2. ```composer install```
|
||||||
|
|
||||||
## Config
|
|
||||||
Edit file ```config.php```
|
|
||||||
|
|
||||||
## Start
|
## Start
|
||||||
```php start.php start -d```
|
php start.php start -d
|
||||||
|
|
||||||
## Stop
|
## Stop
|
||||||
```php start.php stop```
|
php start.php stop
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
```php start.php status```
|
php start.php status
|
||||||
|
|
||||||
## Other links
|
## Other links
|
||||||
https://github.com/walkor/shadowsocks-php
|
https://github.com/walkor/shadowsocks-php
|
||||||
|
82
composer.lock
generated
82
composer.lock
generated
@ -1,82 +0,0 @@
|
|||||||
{
|
|
||||||
"_readme": [
|
|
||||||
"This file locks the dependencies of your project to a known state",
|
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
|
||||||
"This file is @generated automatically"
|
|
||||||
],
|
|
||||||
"content-hash": "7d5fe279e64858f72c1db8f82722e08b",
|
|
||||||
"packages": [
|
|
||||||
{
|
|
||||||
"name": "workerman/workerman",
|
|
||||||
"version": "v4.1.15",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/walkor/workerman.git",
|
|
||||||
"reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/walkor/workerman/zipball/afc8242fc769ab7cf22eb4ac22b97cb59d465e4e",
|
|
||||||
"reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=7.0"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"ext-event": "For better performance. "
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Workerman\\": "./"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "walkor",
|
|
||||||
"email": "walkor@workerman.net",
|
|
||||||
"homepage": "http://www.workerman.net",
|
|
||||||
"role": "Developer"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
|
|
||||||
"homepage": "http://www.workerman.net",
|
|
||||||
"keywords": [
|
|
||||||
"asynchronous",
|
|
||||||
"event-loop"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"email": "walkor@workerman.net",
|
|
||||||
"forum": "http://wenda.workerman.net/",
|
|
||||||
"issues": "https://github.com/walkor/workerman/issues",
|
|
||||||
"source": "https://github.com/walkor/workerman",
|
|
||||||
"wiki": "http://doc.workerman.net/"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://opencollective.com/workerman",
|
|
||||||
"type": "open_collective"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://www.patreon.com/walkor",
|
|
||||||
"type": "patreon"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2024-02-19T02:10:39+00:00"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"packages-dev": [],
|
|
||||||
"aliases": [],
|
|
||||||
"minimum-stability": "stable",
|
|
||||||
"stability-flags": [],
|
|
||||||
"prefer-stable": false,
|
|
||||||
"prefer-lowest": false,
|
|
||||||
"platform": [],
|
|
||||||
"platform-dev": [],
|
|
||||||
"plugin-api-version": "2.6.0"
|
|
||||||
}
|
|
17
config.php
17
config.php
@ -1,14 +1,3 @@
|
|||||||
<?php
|
$AUTH_ENABLED = 0;
|
||||||
|
$USERNAME = 'changeme';
|
||||||
$config = [
|
$PASSWORD = '1234';
|
||||||
"auth" => [
|
|
||||||
METHOD_NO_AUTH => true,
|
|
||||||
METHOD_USER_PASS => function ($request) {
|
|
||||||
return $request['user'] == 'user' && $request['pass'] == 'pass';
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"log_level" => LOG_DEBUG,
|
|
||||||
"tcp_port" => 20501,
|
|
||||||
"udp_port" => 0,
|
|
||||||
"wanIP" => '10.88.0.4',
|
|
||||||
];
|
|
||||||
|
611
start.php
611
start.php
@ -1,27 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of https://github.com/walkor/php-socks5.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
ini_set("memory_limit", "512M");
|
|
||||||
|
|
||||||
use \Workerman\Worker;
|
use \Workerman\Worker;
|
||||||
use \Workerman\Timer;
|
use \Workerman\WebServer;
|
||||||
|
use \Workerman\Connection\TcpConnection;
|
||||||
use \Workerman\Connection\AsyncTcpConnection;
|
use \Workerman\Connection\AsyncTcpConnection;
|
||||||
use \Workerman\Connection\AsyncUdpConnection;
|
|
||||||
|
|
||||||
// 自动加载类
|
// 自动加载类
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
require_once __DIR__ . '/config.php';
|
||||||
|
|
||||||
define('STAGE_INIT', 0);
|
define('STAGE_INIT', 0);
|
||||||
define('STAGE_AUTH', 1);
|
define('STAGE_AUTH', 1);
|
||||||
define('STAGE_ADDR', 2);
|
define('STAGE_ADDR', 2);
|
||||||
@ -35,16 +22,6 @@ define('CMD_CONNECT', 1);
|
|||||||
define('CMD_BIND', 2);
|
define('CMD_BIND', 2);
|
||||||
define('CMD_UDP_ASSOCIATE', 3);
|
define('CMD_UDP_ASSOCIATE', 3);
|
||||||
|
|
||||||
define('ERR_GENERAL', 1);
|
|
||||||
define('ERR_NOT_ALLOW', 2);
|
|
||||||
define('ERR_NETWORK', 3);
|
|
||||||
define('ERR_HOST', 4);
|
|
||||||
define('ERR_REFUSE', 5);
|
|
||||||
define('ERR_TTL_EXPIRED', 6);
|
|
||||||
define('ERR_UNKNOW_COMMAND', 7);
|
|
||||||
define('ERR_UNKNOW_ADDR_TYPE', 8);
|
|
||||||
define('ERR_UNKNOW', 9);
|
|
||||||
|
|
||||||
define('ADDRTYPE_IPV4', 1);
|
define('ADDRTYPE_IPV4', 1);
|
||||||
define('ADDRTYPE_IPV6', 4);
|
define('ADDRTYPE_IPV6', 4);
|
||||||
define('ADDRTYPE_HOST', 3);
|
define('ADDRTYPE_HOST', 3);
|
||||||
@ -53,515 +30,131 @@ define('METHOD_NO_AUTH', 0);
|
|||||||
define('METHOD_GSSAPI', 1);
|
define('METHOD_GSSAPI', 1);
|
||||||
define('METHOD_USER_PASS', 2);
|
define('METHOD_USER_PASS', 2);
|
||||||
|
|
||||||
require_once __DIR__ . '/config.php';
|
$worker = new Worker('tcp://0.0.0.0:1080');
|
||||||
|
$worker->onConnect = function($connection)
|
||||||
if (count($config['auth']) == 0) {
|
{
|
||||||
$config['auth'] = [METHOD_NO_AUTH => true];
|
|
||||||
}
|
|
||||||
|
|
||||||
$worker = new Worker('tcp://0.0.0.0:' . $config['tcp_port']);
|
|
||||||
$worker->onConnect = function ($connection) {
|
|
||||||
$connection->stage = STAGE_INIT;
|
$connection->stage = STAGE_INIT;
|
||||||
$connection->auth_type = NULL;
|
|
||||||
};
|
};
|
||||||
$worker->onMessage = function ($connection, $buffer) {
|
$worker->onMessage = function($connection, $buffer)
|
||||||
global $config;
|
{
|
||||||
logger(LOG_DEBUG, "recv:" . bin2hex($buffer));
|
switch($connection->stage)
|
||||||
switch ($connection->stage) {
|
{
|
||||||
// 初始化环节
|
|
||||||
case STAGE_INIT:
|
case STAGE_INIT:
|
||||||
$request = [];
|
if ($AUTH_ENABLED)
|
||||||
// 当前偏移量
|
{
|
||||||
$offset = 0;
|
$methodslen = ord($buffer[1]);
|
||||||
|
$methods = array();
|
||||||
// 检测buffer长度
|
for ($i = 0; $i < strlen($buffer)-3; $i++)
|
||||||
if (strlen($buffer) < 2) {
|
{
|
||||||
logger(LOG_ERR, "init socks5 failed. buffer too short.");
|
array_push($methods, ord($buffer[$i+3]));
|
||||||
$connection->send("\x05\xff");
|
}
|
||||||
$connection->stage = STAGE_DESTROYED;
|
if (in_array(METHOD_USER_PASS, $methods))
|
||||||
$connection->close();
|
{
|
||||||
return;
|
$connection->send("\x05\x02");
|
||||||
}
|
$connection->stage = STAGE_AUTH;
|
||||||
|
|
||||||
// Socks5 版本
|
|
||||||
$request['ver'] = ord($buffer[$offset]);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
// 认证方法数量
|
|
||||||
$request['method_count'] = ord($buffer[$offset]);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
if (strlen($buffer) < 2 + $request['method_count']) {
|
|
||||||
logger(LOG_ERR, "init authentic failed. buffer too short.");
|
|
||||||
$connection->send("\x05\xff");
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
$connection->close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 客户端支持的认证方法
|
|
||||||
$request['methods'] = [];
|
|
||||||
for ($i = 1; $i <= $request['method_count']; $i++) {
|
|
||||||
$request['methods'][] = ord($buffer[$offset]);
|
|
||||||
$offset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($config['auth'] as $k => $v) {
|
|
||||||
if (in_array($k, $request['methods'])) {
|
|
||||||
|
|
||||||
logger(LOG_INFO, "auth client via method $k");
|
|
||||||
logger(LOG_DEBUG, "send:" . bin2hex("\x05" . chr($k)));
|
|
||||||
|
|
||||||
$connection->send("\x05" . chr($k));
|
|
||||||
if ($k == 0) {
|
|
||||||
$connection->stage = STAGE_ADDR;
|
|
||||||
} else {
|
|
||||||
$connection->stage = STAGE_AUTH;
|
|
||||||
}
|
|
||||||
$connection->auth_type = $k;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
echo "client does not support user/pass auth\n";
|
||||||
if ($connection->stage != STAGE_AUTH) {
|
|
||||||
logger(LOG_ERR, "client has no matched auth methods");
|
|
||||||
logger(LOG_DEBUG, "send:" . bin2hex("\x05\xff"));
|
|
||||||
$connection->send("\x05\xff");
|
$connection->send("\x05\xff");
|
||||||
$connection->stage = STAGE_DESTROYED;
|
$connection->stage = STAGE_DESTROYED;
|
||||||
$connection->close();
|
$connection->close();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
$connection->send("\x05\x00");
|
||||||
|
$connection->stage = STAGE_ADDR;
|
||||||
return;
|
return;
|
||||||
// 认证环节
|
|
||||||
case STAGE_AUTH:
|
case STAGE_AUTH:
|
||||||
|
$userlen = ord($buffer[1]);
|
||||||
$request = [];
|
$user = substr($buffer, 2, $userlen);
|
||||||
// 当前偏移量
|
$passlen = ord($buffer[2 + $userlen]);
|
||||||
$offset = 0;
|
$pass = substr($buffer, 3 + $userlen, $passlen);
|
||||||
|
if ($user == $USERNAME && $pass == $PASSWORD)
|
||||||
if (strlen($buffer) < 5) {
|
{
|
||||||
logger(LOG_ERR, "auth failed. buffer too short.");
|
$connection->send("\x05\x00");
|
||||||
$connection->send("\x01\x01");
|
$connection->stage = STAGE_ADDR;
|
||||||
$connection->stage = STAGE_DESTROYED;
|
return;
|
||||||
|
}
|
||||||
|
echo "auth failed\n";
|
||||||
|
$connection->send("\x05\x01");
|
||||||
|
$connection->stage = STAGE_DESTROYED;
|
||||||
|
$connection->close();
|
||||||
|
return;
|
||||||
|
case STAGE_ADDR:
|
||||||
|
$cmd = ord($buffer[1]);
|
||||||
|
if($cmd != CMD_CONNECT)
|
||||||
|
{
|
||||||
|
echo "bad cmd $cmd\n";
|
||||||
$connection->close();
|
$connection->close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$header_data = parse_socket5_header($buffer);
|
||||||
// var_dump($connection->auth_type);
|
if(!$header_data)
|
||||||
switch ($connection->auth_type) {
|
{
|
||||||
case METHOD_USER_PASS:
|
$connection->close();
|
||||||
// 子协议 协商 版本
|
|
||||||
$request['sub_ver'] = ord($buffer[$offset]);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
// 用户名
|
|
||||||
$request['user_len'] = ord($buffer[$offset]);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
if (strlen($buffer) < 2 + $request['user_len'] + 2) {
|
|
||||||
logger(LOG_ERR, "auth username failed. buffer too short.");
|
|
||||||
$connection->send("\x01\x01");
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
$connection->close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$request['user'] = substr($buffer, $offset, $request['user_len']);
|
|
||||||
$offset += $request['user_len'];
|
|
||||||
|
|
||||||
// 密码
|
|
||||||
$request['pass_len'] = ord($buffer[$offset]);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
|
|
||||||
if (strlen($buffer) < 2 + $request['user_len'] + 1 + $request['pass_len']) {
|
|
||||||
logger(LOG_ERR, "auth password failed. buffer too short.");
|
|
||||||
$connection->send("\x01\x01");
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
$connection->close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$request['pass'] = substr($buffer, $offset, $request['pass_len']);
|
|
||||||
$offset += $request['pass_len'];
|
|
||||||
|
|
||||||
if ($config["auth"][METHOD_USER_PASS]($request)) {
|
|
||||||
logger(LOG_INFO, "auth ok");
|
|
||||||
$connection->send("\x01\x00");
|
|
||||||
$connection->stage = STAGE_ADDR;
|
|
||||||
} else {
|
|
||||||
logger(LOG_INFO, "auth failed");
|
|
||||||
$connection->send("\x01\x01");
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
$connection->close();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
logger(LOG_ERR, "unsupport auth type");
|
|
||||||
$connection->send("\x01\x01");
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
$connection->close();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case STAGE_ADDR:
|
|
||||||
$request = [];
|
|
||||||
// 当前偏移量
|
|
||||||
$offset = 0;
|
|
||||||
|
|
||||||
if (strlen($buffer) < 4) {
|
|
||||||
logger(LOG_ERR, "connect init failed. buffer too short.");
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
|
|
||||||
$response = [];
|
|
||||||
$response['ver'] = 5;
|
|
||||||
$response['rep'] = ERR_GENERAL;
|
|
||||||
$response['rsv'] = 0;
|
|
||||||
$response['addr_type'] = ADDRTYPE_IPV4;
|
|
||||||
$response['bind_addr'] = '0.0.0.0';
|
|
||||||
$response['bind_port'] = 0;
|
|
||||||
|
|
||||||
$connection->close(packResponse($response));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$connection->stage = STAGE_CONNECTING;
|
||||||
// Socks 版本
|
$remote_connection = new AsyncTcpConnection('tcp://'.$header_data[1].':'.$header_data[2]);
|
||||||
$request['ver'] = ord($buffer[$offset]);
|
$remote_connection->onConnect = function($remote_connection)use($connection)
|
||||||
$offset += 1;
|
{
|
||||||
|
$connection->state = STAGE_STREAM;
|
||||||
// 命令
|
$connection->send("\x05\x00\x00\x01\x00\x00\x00\x00\x10\x10");
|
||||||
$request['command'] = ord($buffer[$offset]);
|
$connection->pipe($remote_connection);
|
||||||
$offset += 1;
|
$remote_connection->pipe($connection);
|
||||||
|
};
|
||||||
// RSV
|
$remote_connection->connect();
|
||||||
$request['rsv'] = ord($buffer[$offset]);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
// AddressType
|
|
||||||
$request['addr_type'] = ord($buffer[$offset]);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
// DestAddr
|
|
||||||
switch ($request['addr_type']) {
|
|
||||||
case ADDRTYPE_IPV4:
|
|
||||||
|
|
||||||
if (strlen($buffer) < 4 + 4) {
|
|
||||||
logger(LOG_ERR, "connect init failed.[ADDRTYPE_IPV4] buffer too short.");
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
|
|
||||||
$response = [];
|
|
||||||
$response['ver'] = 5;
|
|
||||||
$response['rep'] = ERR_GENERAL;
|
|
||||||
$response['rsv'] = 0;
|
|
||||||
$response['addr_type'] = ADDRTYPE_IPV4;
|
|
||||||
$response['bind_addr'] = '0.0.0.0';
|
|
||||||
$response['bind_port'] = 0;
|
|
||||||
|
|
||||||
$connection->close(packResponse($response));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$tmp = substr($buffer, $offset, 4);
|
|
||||||
$ip = 0;
|
|
||||||
for ($i = 0; $i < 4; $i++) {
|
|
||||||
// var_dump(ord($tmp[$i]));
|
|
||||||
$ip += ord($tmp[$i]) * pow(256, 3 - $i);
|
|
||||||
}
|
|
||||||
$request['dest_addr'] = long2ip($ip);
|
|
||||||
$offset += 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ADDRTYPE_HOST:
|
|
||||||
$request['host_len'] = ord($buffer[$offset]);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
if (strlen($buffer) < 4 + 1 + $request['host_len']) {
|
|
||||||
logger(LOG_ERR, "connect init failed.[ADDRTYPE_HOST] buffer too short.");
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
|
|
||||||
$response = [];
|
|
||||||
$response['ver'] = 5;
|
|
||||||
$response['rep'] = ERR_GENERAL;
|
|
||||||
$response['rsv'] = 0;
|
|
||||||
$response['addr_type'] = ADDRTYPE_IPV4;
|
|
||||||
$response['bind_addr'] = '0.0.0.0';
|
|
||||||
$response['bind_port'] = 0;
|
|
||||||
|
|
||||||
$connection->close(packResponse($response));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$request['dest_addr'] = substr($buffer, $offset, $request['host_len']);
|
|
||||||
$offset += $request['host_len'];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ADDRTYPE_IPV6:
|
|
||||||
default:
|
|
||||||
logger(LOG_ERR, "unsupport ipv6. [ADDRTYPE_IPV6].");
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
|
|
||||||
$response = [];
|
|
||||||
$response['ver'] = 5;
|
|
||||||
$response['rep'] = ERR_UNKNOW_ADDR_TYPE;
|
|
||||||
$response['rsv'] = 0;
|
|
||||||
$response['addr_type'] = ADDRTYPE_IPV4;
|
|
||||||
$response['bind_addr'] = '0.0.0.0';
|
|
||||||
$response['bind_port'] = 0;
|
|
||||||
|
|
||||||
$connection->close(packResponse($response));
|
|
||||||
return;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DestPort
|
|
||||||
|
|
||||||
if (strlen($buffer) < $offset + 2) {
|
|
||||||
logger(LOG_ERR, "connect init failed.[port] buffer too short.");
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
|
|
||||||
$response = [];
|
|
||||||
$response['ver'] = 5;
|
|
||||||
$response['rep'] = ERR_GENERAL;
|
|
||||||
$response['rsv'] = 0;
|
|
||||||
$response['addr_type'] = ADDRTYPE_IPV4;
|
|
||||||
$response['bind_addr'] = '0.0.0.0';
|
|
||||||
$response['bind_port'] = 0;
|
|
||||||
|
|
||||||
$connection->close(packResponse($response));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$portData = unpack("n", substr($buffer, $offset, 2));
|
|
||||||
$request['dest_port'] = $portData[1];
|
|
||||||
$offset += 2;
|
|
||||||
|
|
||||||
// var_dump($request);
|
|
||||||
switch ($request['command']) {
|
|
||||||
case CMD_CONNECT:
|
|
||||||
logger(LOG_DEBUG, 'tcp://' . $request['dest_addr'] . ':' . $request['dest_port']);
|
|
||||||
if ($request['addr_type'] == ADDRTYPE_HOST) {
|
|
||||||
if (!filter_var($request['dest_addr'], FILTER_VALIDATE_IP)) {
|
|
||||||
logger(LOG_DEBUG, 'resolve DNS ' . $request['dest_addr']);
|
|
||||||
$connection->stage = STAGE_DNS;
|
|
||||||
$addr = dns_get_record($request['dest_addr'], DNS_A);
|
|
||||||
$addr = $addr ? array_pop($addr) : null;
|
|
||||||
logger(LOG_DEBUG, 'DNS resolved ' . $request['dest_addr'] . ' => ' . $addr['ip']);
|
|
||||||
} else {
|
|
||||||
$addr['ip'] = $request['dest_addr'];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$addr['ip'] = $request['dest_addr'];
|
|
||||||
}
|
|
||||||
if ($addr) {
|
|
||||||
$connection->stage = STAGE_CONNECTING;
|
|
||||||
$remote_connection = new AsyncTcpConnection('tcp://' . $addr['ip'] . ':' . $request['dest_port']);
|
|
||||||
$remote_connection->onConnect = function ($remote_connection) use ($connection, $request) {
|
|
||||||
$connection->state = STAGE_STREAM;
|
|
||||||
$response = [];
|
|
||||||
$response['ver'] = 5;
|
|
||||||
$response['rep'] = 0;
|
|
||||||
$response['rsv'] = 0;
|
|
||||||
$response['addr_type'] = $request['addr_type'];
|
|
||||||
$response['bind_addr'] = '0.0.0.0';
|
|
||||||
$response['bind_port'] = 18512;
|
|
||||||
|
|
||||||
$connection->send(packResponse($response));
|
|
||||||
$connection->pipe($remote_connection);
|
|
||||||
$remote_connection->pipe($connection);
|
|
||||||
logger(LOG_DEBUG, 'tcp://' . $request['dest_addr'] . ':' . $request['dest_port'] . ' [OK]');
|
|
||||||
};
|
|
||||||
$remote_connection->connect();
|
|
||||||
} else {
|
|
||||||
logger(LOG_DEBUG, 'DNS resolve failed.');
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
|
|
||||||
$response = [];
|
|
||||||
$response['ver'] = 5;
|
|
||||||
$response['rep'] = ERR_HOST;
|
|
||||||
$response['rsv'] = 0;
|
|
||||||
$response['addr_type'] = ADDRTYPE_IPV4;
|
|
||||||
$response['bind_addr'] = '0.0.0.0';
|
|
||||||
$response['bind_port'] = 0;
|
|
||||||
|
|
||||||
$connection->close(packResponse($response));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CMD_UDP_ASSOCIATE:
|
|
||||||
$connection->stage = STAGE_UDP_ASSOC;
|
|
||||||
var_dump("CMD_UDP_ASSOCIATE " . $config['udp_port']);
|
|
||||||
if ($config['udp_port'] == 0) {
|
|
||||||
$connection->udpWorker = new Worker('udp://0.0.0.0:0');
|
|
||||||
$connection->udpWorker->incId = 0;
|
|
||||||
$connection->udpWorker->onMessage = function ($udp_connection, $data) use ($connection) {
|
|
||||||
udpWorkerOnMessage($udp_connection, $data, $connection->udpWorker);
|
|
||||||
};
|
|
||||||
$connection->udpWorker->listen();
|
|
||||||
$listenInfo = stream_socket_get_name($connection->udpWorker->getMainSocket(), false);
|
|
||||||
list($bind_addr, $bind_port) = explode(":", $listenInfo);
|
|
||||||
} else {
|
|
||||||
$bind_port = $config['udp_port'];
|
|
||||||
}
|
|
||||||
$bind_addr = $config['wanIP'];
|
|
||||||
|
|
||||||
$response['ver'] = 5;
|
|
||||||
$response['rep'] = 0;
|
|
||||||
$response['rsv'] = 0;
|
|
||||||
$response['addr_type'] = ADDRTYPE_IPV4;
|
|
||||||
$response['bind_addr'] = $bind_addr;
|
|
||||||
$response['bind_port'] = $bind_port;
|
|
||||||
|
|
||||||
logger(LOG_DEBUG, 'send:' . bin2hex(packResponse($response)));
|
|
||||||
$connection->send(packResponse($response));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
logger(LOG_ERR, "connect init failed. unknow command.");
|
|
||||||
$connection->stage = STAGE_DESTROYED;
|
|
||||||
|
|
||||||
$response = [];
|
|
||||||
$response['ver'] = 5;
|
|
||||||
$response['rep'] = ERR_UNKNOW_COMMAND;
|
|
||||||
$response['rsv'] = 0;
|
|
||||||
$response['addr_type'] = ADDRTYPE_IPV4;
|
|
||||||
$response['bind_addr'] = '0.0.0.0';
|
|
||||||
$response['bind_port'] = 0;
|
|
||||||
|
|
||||||
$connection->close(packResponse($response));
|
|
||||||
return;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
$worker->onClose = function ($connection) {
|
|
||||||
logger(LOG_INFO, "client closed.");
|
|
||||||
};
|
|
||||||
|
|
||||||
function udpWorkerOnMessage($udp_connection, $data, &$worker)
|
|
||||||
|
function parse_socket5_header($buffer)
|
||||||
{
|
{
|
||||||
|
$addr_type = ord($buffer[3]);
|
||||||
logger(LOG_DEBUG, 'send:' . bin2hex($data));
|
switch($addr_type)
|
||||||
$request = [];
|
{
|
||||||
$offset = 0;
|
|
||||||
|
|
||||||
$request['rsv'] = substr($data, $offset, 2);
|
|
||||||
$offset += 2;
|
|
||||||
|
|
||||||
$request['frag'] = ord($data[$offset]);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
$request['addr_type'] = ord($data[$offset]);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
switch ($request['addr_type']) {
|
|
||||||
case ADDRTYPE_IPV4:
|
case ADDRTYPE_IPV4:
|
||||||
$tmp = substr($data, $offset, 4);
|
if(strlen($buffer) < 10)
|
||||||
$ip = 0;
|
{
|
||||||
for ($i = 0; $i < 4; $i++) {
|
echo bin2hex($buffer)."\n";
|
||||||
$ip += ord($tmp[$i]) * pow(256, 3 - $i);
|
|
||||||
}
|
|
||||||
$request['dest_addr'] = long2ip($ip);
|
|
||||||
$offset += 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ADDRTYPE_HOST:
|
|
||||||
$request['host_len'] = ord($data[$offset]);
|
|
||||||
$offset += 1;
|
|
||||||
|
|
||||||
$request['dest_addr'] = substr($data, $offset, $request['host_len']);
|
|
||||||
$offset += $request['host_len'];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ADDRTYPE_IPV6:
|
|
||||||
if (strlen($data) < 22) {
|
|
||||||
echo "buffer too short\n";
|
echo "buffer too short\n";
|
||||||
$error = true;
|
return false;
|
||||||
break;
|
}
|
||||||
|
$dest_addr = ord($buffer[4]).'.'.ord($buffer[5]).'.'.ord($buffer[6]).'.'.ord($buffer[7]);
|
||||||
|
$port_data = unpack('n', substr($buffer, -2));
|
||||||
|
$dest_port = $port_data[1];
|
||||||
|
$header_length = 10;
|
||||||
|
break;
|
||||||
|
case ADDRTYPE_HOST:
|
||||||
|
$addrlen = ord($buffer[4]);
|
||||||
|
if(strlen($buffer) < $addrlen + 5)
|
||||||
|
{
|
||||||
|
echo $buffer."\n";
|
||||||
|
echo bin2hex($buffer)."\n";
|
||||||
|
echo "buffer too short\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$dest_addr = substr($buffer, 5, $addrlen);
|
||||||
|
$port_data = unpack('n', substr($buffer, -2));
|
||||||
|
$dest_port = $port_data[1];
|
||||||
|
$header_length = $addrlen + 7;
|
||||||
|
break;
|
||||||
|
case ADDRTYPE_IPV6:
|
||||||
|
if(strlen($buffer) < 22)
|
||||||
|
{
|
||||||
|
echo "buffer too short\n";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
echo "todo ipv6\n";
|
echo "todo ipv6\n";
|
||||||
$error = true;
|
return false;
|
||||||
default:
|
default:
|
||||||
echo "unsupported addrtype {$request['addr_type']}\n";
|
echo "unsupported addrtype $addr_type\n";
|
||||||
$error = true;
|
return false;
|
||||||
}
|
}
|
||||||
|
return array($addr_type, $dest_addr, $dest_port, $header_length);
|
||||||
$portData = unpack("n", substr($data, $offset, 2));
|
|
||||||
$request['dest_port'] = $portData[1];
|
|
||||||
$offset += 2;
|
|
||||||
// var_dump($request['dest_addr']);
|
|
||||||
if ($request['addr_type'] == ADDRTYPE_HOST) {
|
|
||||||
logger(LOG_DEBUG, '解析DNS');
|
|
||||||
$addr = dns_get_record($request['dest_addr'], DNS_A);
|
|
||||||
$addr = $addr ? array_pop($addr) : null;
|
|
||||||
logger(LOG_DEBUG, 'DNS 解析完成' . $addr['ip']);
|
|
||||||
} else {
|
|
||||||
$addr['ip'] = $request['dest_addr'];
|
|
||||||
}
|
|
||||||
// var_dump($request);
|
|
||||||
|
|
||||||
// var_dump($udp_connection);
|
|
||||||
|
|
||||||
$remote_connection = new AsyncUdpConnection('udp://' . $addr['ip'] . ':' . $request['dest_port']);
|
|
||||||
$remote_connection->id = $worker->incId++;
|
|
||||||
$remote_connection->udp_connection = $udp_connection;
|
|
||||||
$remote_connection->onConnect = function ($remote_connection) use ($data, $offset) {
|
|
||||||
$remote_connection->send(substr($data, $offset));
|
|
||||||
};
|
|
||||||
$remote_connection->onMessage = function ($remote_connection, $recv) use ($data, $offset, $udp_connection, $worker) {
|
|
||||||
$udp_connection->close(substr($data, 0, $offset) . $recv);
|
|
||||||
$remote_connection->close();
|
|
||||||
unset($worker->udpConnections[$remote_connection->id]);
|
|
||||||
};
|
|
||||||
$remote_connection->deadTime = time() + 3;
|
|
||||||
$remote_connection->connect();
|
|
||||||
$worker->udpConnections[$remote_connection->id] = $remote_connection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$udpWorker = new Worker('udp://0.0.0.0:1080');
|
|
||||||
$udpWorker->incId = 0;
|
|
||||||
$udpWorker->onWorkerStart = function ($worker) {
|
|
||||||
$worker->udpConnections = [];
|
|
||||||
Timer::add(1, function () use ($worker) {
|
|
||||||
foreach ($worker->udpConnections as $id => $remote_connection) {
|
|
||||||
if ($remote_connection->deadTime < time()) {
|
|
||||||
$remote_connection->close();
|
|
||||||
$remote_connection->udp_connection->close();
|
|
||||||
unset($worker->udpConnections[$id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$udpWorker->onMessage = 'udpWorkerOnMessage';
|
|
||||||
|
|
||||||
function packResponse($response)
|
|
||||||
{
|
|
||||||
$data = "";
|
|
||||||
$data .= chr($response['ver']);
|
|
||||||
$data .= chr($response['rep']);
|
|
||||||
$data .= chr($response['rsv']);
|
|
||||||
$data .= chr($response['addr_type']);
|
|
||||||
|
|
||||||
switch ($response['addr_type']) {
|
|
||||||
case ADDRTYPE_IPV4:
|
|
||||||
$tmp = explode('.', $response['bind_addr']);
|
|
||||||
foreach ($tmp as $block) {
|
|
||||||
$data .= chr($block);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ADDRTYPE_HOST:
|
|
||||||
$host_len = strlen($response['bind_addr']);
|
|
||||||
$data .= chr($host_len);
|
|
||||||
$data .= $response['bind_addr'];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data .= pack("n", $response['bind_port']);
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function logger($level, $str)
|
|
||||||
{
|
|
||||||
global $config;
|
|
||||||
if ($config['log_level'] >= $level) {
|
|
||||||
echo $str . "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 如果不是在根目录启动,则运行runAll方法
|
// 如果不是在根目录启动,则运行runAll方法
|
||||||
if (!defined('GLOBAL_START')) {
|
if(!defined('GLOBAL_START'))
|
||||||
|
{
|
||||||
Worker::runAll();
|
Worker::runAll();
|
||||||
}
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
----------------------------------------------------------------------------------
|
|
||||||
Node Name Upload Speed Download Speed Latency Packet Loss
|
|
||||||
[0;33m Speedtest.net [0;32m774.65 Mbit/s [0;31m648.17 Mbit/s [0;36m(*)94.45 ms [0mNot available
|
|
||||||
[0;33m Zhenjiang 5G CT [0;32m644.54 Mbit/s [0;31m30.92 Mbit/s [0;36m46.07 ms [0mNot available
|
|
||||||
[0;33m Nanjing 5G CT [0;32m24.85 Mbit/s [0;31m744.16 Mbit/s [0;36m41.06 ms [0m13.0%
|
|
||||||
[0;33m Hangzhou CT [0;32m521.82 Mbit/s [0;31m991.68 Mbit/s [0;36m38.51 ms [0mNot available
|
|
||||||
[0;33m Shanghai 5G CU [0;32m941.08 Mbit/s [0;31m1159.90 Mbit/s [0;36m82.42 ms [0m0.0%
|
|
||||||
[0;33m TianJin 5G CU [0;32m1.46 Mbit/s [0;31m10.20 Mbit/s [0;36m199.76 ms [0mNot available
|
|
||||||
[0;33m Beijing CM [0;32m918.89 Mbit/s [0;31m1135.99 Mbit/s [0;36m73.82 ms [0mNot available
|
|
||||||
[0;33m Chengdu BN [0;32m896.37 Mbit/s [0;31m26.72 Mbit/s [0;36m75.30 ms [0mNot available
|
|
||||||
----------------------------------------------------------------------------------
|
|
1095
superbench.sh
1095
superbench.sh
File diff suppressed because it is too large
Load Diff
25
vendor/autoload.php
vendored
25
vendor/autoload.php
vendored
@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload.php @generated by Composer
|
|
||||||
|
|
||||||
if (PHP_VERSION_ID < 50600) {
|
|
||||||
if (!headers_sent()) {
|
|
||||||
header('HTTP/1.1 500 Internal Server Error');
|
|
||||||
}
|
|
||||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
|
||||||
if (!ini_get('display_errors')) {
|
|
||||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
|
||||||
fwrite(STDERR, $err);
|
|
||||||
} elseif (!headers_sent()) {
|
|
||||||
echo $err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trigger_error(
|
|
||||||
$err,
|
|
||||||
E_USER_ERROR
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
require_once __DIR__ . '/composer/autoload_real.php';
|
|
||||||
|
|
||||||
return ComposerAutoloaderInit7d5fe279e64858f72c1db8f82722e08b::getLoader();
|
|
579
vendor/composer/ClassLoader.php
vendored
579
vendor/composer/ClassLoader.php
vendored
@ -1,579 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Composer.
|
|
||||||
*
|
|
||||||
* (c) Nils Adermann <naderman@naderman.de>
|
|
||||||
* Jordi Boggiano <j.boggiano@seld.be>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Composer\Autoload;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
|
||||||
*
|
|
||||||
* $loader = new \Composer\Autoload\ClassLoader();
|
|
||||||
*
|
|
||||||
* // register classes with namespaces
|
|
||||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
|
||||||
* $loader->add('Symfony', __DIR__.'/framework');
|
|
||||||
*
|
|
||||||
* // activate the autoloader
|
|
||||||
* $loader->register();
|
|
||||||
*
|
|
||||||
* // to enable searching the include path (eg. for PEAR packages)
|
|
||||||
* $loader->setUseIncludePath(true);
|
|
||||||
*
|
|
||||||
* In this example, if you try to use a class in the Symfony\Component
|
|
||||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
|
||||||
* the autoloader will first look for the class under the component/
|
|
||||||
* directory, and it will then fallback to the framework/ directory if not
|
|
||||||
* found before giving up.
|
|
||||||
*
|
|
||||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
|
||||||
*
|
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
|
||||||
* @see https://www.php-fig.org/psr/psr-0/
|
|
||||||
* @see https://www.php-fig.org/psr/psr-4/
|
|
||||||
*/
|
|
||||||
class ClassLoader
|
|
||||||
{
|
|
||||||
/** @var \Closure(string):void */
|
|
||||||
private static $includeFile;
|
|
||||||
|
|
||||||
/** @var string|null */
|
|
||||||
private $vendorDir;
|
|
||||||
|
|
||||||
// PSR-4
|
|
||||||
/**
|
|
||||||
* @var array<string, array<string, int>>
|
|
||||||
*/
|
|
||||||
private $prefixLengthsPsr4 = array();
|
|
||||||
/**
|
|
||||||
* @var array<string, list<string>>
|
|
||||||
*/
|
|
||||||
private $prefixDirsPsr4 = array();
|
|
||||||
/**
|
|
||||||
* @var list<string>
|
|
||||||
*/
|
|
||||||
private $fallbackDirsPsr4 = array();
|
|
||||||
|
|
||||||
// PSR-0
|
|
||||||
/**
|
|
||||||
* List of PSR-0 prefixes
|
|
||||||
*
|
|
||||||
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
|
||||||
*
|
|
||||||
* @var array<string, array<string, list<string>>>
|
|
||||||
*/
|
|
||||||
private $prefixesPsr0 = array();
|
|
||||||
/**
|
|
||||||
* @var list<string>
|
|
||||||
*/
|
|
||||||
private $fallbackDirsPsr0 = array();
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
private $useIncludePath = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array<string, string>
|
|
||||||
*/
|
|
||||||
private $classMap = array();
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
private $classMapAuthoritative = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array<string, bool>
|
|
||||||
*/
|
|
||||||
private $missingClasses = array();
|
|
||||||
|
|
||||||
/** @var string|null */
|
|
||||||
private $apcuPrefix;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array<string, self>
|
|
||||||
*/
|
|
||||||
private static $registeredLoaders = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|null $vendorDir
|
|
||||||
*/
|
|
||||||
public function __construct($vendorDir = null)
|
|
||||||
{
|
|
||||||
$this->vendorDir = $vendorDir;
|
|
||||||
self::initializeIncludeClosure();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, list<string>>
|
|
||||||
*/
|
|
||||||
public function getPrefixes()
|
|
||||||
{
|
|
||||||
if (!empty($this->prefixesPsr0)) {
|
|
||||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, list<string>>
|
|
||||||
*/
|
|
||||||
public function getPrefixesPsr4()
|
|
||||||
{
|
|
||||||
return $this->prefixDirsPsr4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return list<string>
|
|
||||||
*/
|
|
||||||
public function getFallbackDirs()
|
|
||||||
{
|
|
||||||
return $this->fallbackDirsPsr0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return list<string>
|
|
||||||
*/
|
|
||||||
public function getFallbackDirsPsr4()
|
|
||||||
{
|
|
||||||
return $this->fallbackDirsPsr4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, string> Array of classname => path
|
|
||||||
*/
|
|
||||||
public function getClassMap()
|
|
||||||
{
|
|
||||||
return $this->classMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, string> $classMap Class to filename map
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addClassMap(array $classMap)
|
|
||||||
{
|
|
||||||
if ($this->classMap) {
|
|
||||||
$this->classMap = array_merge($this->classMap, $classMap);
|
|
||||||
} else {
|
|
||||||
$this->classMap = $classMap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a set of PSR-0 directories for a given prefix, either
|
|
||||||
* appending or prepending to the ones previously set for this prefix.
|
|
||||||
*
|
|
||||||
* @param string $prefix The prefix
|
|
||||||
* @param list<string>|string $paths The PSR-0 root directories
|
|
||||||
* @param bool $prepend Whether to prepend the directories
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function add($prefix, $paths, $prepend = false)
|
|
||||||
{
|
|
||||||
$paths = (array) $paths;
|
|
||||||
if (!$prefix) {
|
|
||||||
if ($prepend) {
|
|
||||||
$this->fallbackDirsPsr0 = array_merge(
|
|
||||||
$paths,
|
|
||||||
$this->fallbackDirsPsr0
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->fallbackDirsPsr0 = array_merge(
|
|
||||||
$this->fallbackDirsPsr0,
|
|
||||||
$paths
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$first = $prefix[0];
|
|
||||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
|
||||||
$this->prefixesPsr0[$first][$prefix] = $paths;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($prepend) {
|
|
||||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
|
||||||
$paths,
|
|
||||||
$this->prefixesPsr0[$first][$prefix]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
|
||||||
$this->prefixesPsr0[$first][$prefix],
|
|
||||||
$paths
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a set of PSR-4 directories for a given namespace, either
|
|
||||||
* appending or prepending to the ones previously set for this namespace.
|
|
||||||
*
|
|
||||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
|
||||||
* @param list<string>|string $paths The PSR-4 base directories
|
|
||||||
* @param bool $prepend Whether to prepend the directories
|
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addPsr4($prefix, $paths, $prepend = false)
|
|
||||||
{
|
|
||||||
$paths = (array) $paths;
|
|
||||||
if (!$prefix) {
|
|
||||||
// Register directories for the root namespace.
|
|
||||||
if ($prepend) {
|
|
||||||
$this->fallbackDirsPsr4 = array_merge(
|
|
||||||
$paths,
|
|
||||||
$this->fallbackDirsPsr4
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->fallbackDirsPsr4 = array_merge(
|
|
||||||
$this->fallbackDirsPsr4,
|
|
||||||
$paths
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
|
||||||
// Register directories for a new namespace.
|
|
||||||
$length = strlen($prefix);
|
|
||||||
if ('\\' !== $prefix[$length - 1]) {
|
|
||||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
|
||||||
}
|
|
||||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
|
||||||
$this->prefixDirsPsr4[$prefix] = $paths;
|
|
||||||
} elseif ($prepend) {
|
|
||||||
// Prepend directories for an already registered namespace.
|
|
||||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
|
||||||
$paths,
|
|
||||||
$this->prefixDirsPsr4[$prefix]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Append directories for an already registered namespace.
|
|
||||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
|
||||||
$this->prefixDirsPsr4[$prefix],
|
|
||||||
$paths
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a set of PSR-0 directories for a given prefix,
|
|
||||||
* replacing any others previously set for this prefix.
|
|
||||||
*
|
|
||||||
* @param string $prefix The prefix
|
|
||||||
* @param list<string>|string $paths The PSR-0 base directories
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function set($prefix, $paths)
|
|
||||||
{
|
|
||||||
if (!$prefix) {
|
|
||||||
$this->fallbackDirsPsr0 = (array) $paths;
|
|
||||||
} else {
|
|
||||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a set of PSR-4 directories for a given namespace,
|
|
||||||
* replacing any others previously set for this namespace.
|
|
||||||
*
|
|
||||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
|
||||||
* @param list<string>|string $paths The PSR-4 base directories
|
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setPsr4($prefix, $paths)
|
|
||||||
{
|
|
||||||
if (!$prefix) {
|
|
||||||
$this->fallbackDirsPsr4 = (array) $paths;
|
|
||||||
} else {
|
|
||||||
$length = strlen($prefix);
|
|
||||||
if ('\\' !== $prefix[$length - 1]) {
|
|
||||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
|
||||||
}
|
|
||||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
|
||||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns on searching the include path for class files.
|
|
||||||
*
|
|
||||||
* @param bool $useIncludePath
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setUseIncludePath($useIncludePath)
|
|
||||||
{
|
|
||||||
$this->useIncludePath = $useIncludePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can be used to check if the autoloader uses the include path to check
|
|
||||||
* for classes.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function getUseIncludePath()
|
|
||||||
{
|
|
||||||
return $this->useIncludePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns off searching the prefix and fallback directories for classes
|
|
||||||
* that have not been registered with the class map.
|
|
||||||
*
|
|
||||||
* @param bool $classMapAuthoritative
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
|
||||||
{
|
|
||||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should class lookup fail if not found in the current class map?
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isClassMapAuthoritative()
|
|
||||||
{
|
|
||||||
return $this->classMapAuthoritative;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
|
||||||
*
|
|
||||||
* @param string|null $apcuPrefix
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setApcuPrefix($apcuPrefix)
|
|
||||||
{
|
|
||||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
public function getApcuPrefix()
|
|
||||||
{
|
|
||||||
return $this->apcuPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers this instance as an autoloader.
|
|
||||||
*
|
|
||||||
* @param bool $prepend Whether to prepend the autoloader or not
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function register($prepend = false)
|
|
||||||
{
|
|
||||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
|
||||||
|
|
||||||
if (null === $this->vendorDir) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($prepend) {
|
|
||||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
|
||||||
} else {
|
|
||||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
|
||||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters this instance as an autoloader.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function unregister()
|
|
||||||
{
|
|
||||||
spl_autoload_unregister(array($this, 'loadClass'));
|
|
||||||
|
|
||||||
if (null !== $this->vendorDir) {
|
|
||||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the given class or interface.
|
|
||||||
*
|
|
||||||
* @param string $class The name of the class
|
|
||||||
* @return true|null True if loaded, null otherwise
|
|
||||||
*/
|
|
||||||
public function loadClass($class)
|
|
||||||
{
|
|
||||||
if ($file = $this->findFile($class)) {
|
|
||||||
$includeFile = self::$includeFile;
|
|
||||||
$includeFile($file);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the path to the file where the class is defined.
|
|
||||||
*
|
|
||||||
* @param string $class The name of the class
|
|
||||||
*
|
|
||||||
* @return string|false The path if found, false otherwise
|
|
||||||
*/
|
|
||||||
public function findFile($class)
|
|
||||||
{
|
|
||||||
// class map lookup
|
|
||||||
if (isset($this->classMap[$class])) {
|
|
||||||
return $this->classMap[$class];
|
|
||||||
}
|
|
||||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (null !== $this->apcuPrefix) {
|
|
||||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
|
||||||
if ($hit) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$file = $this->findFileWithExtension($class, '.php');
|
|
||||||
|
|
||||||
// Search for Hack files if we are running on HHVM
|
|
||||||
if (false === $file && defined('HHVM_VERSION')) {
|
|
||||||
$file = $this->findFileWithExtension($class, '.hh');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null !== $this->apcuPrefix) {
|
|
||||||
apcu_add($this->apcuPrefix.$class, $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false === $file) {
|
|
||||||
// Remember that this class does not exist.
|
|
||||||
$this->missingClasses[$class] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
|
||||||
*
|
|
||||||
* @return array<string, self>
|
|
||||||
*/
|
|
||||||
public static function getRegisteredLoaders()
|
|
||||||
{
|
|
||||||
return self::$registeredLoaders;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $class
|
|
||||||
* @param string $ext
|
|
||||||
* @return string|false
|
|
||||||
*/
|
|
||||||
private function findFileWithExtension($class, $ext)
|
|
||||||
{
|
|
||||||
// PSR-4 lookup
|
|
||||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
|
||||||
|
|
||||||
$first = $class[0];
|
|
||||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
|
||||||
$subPath = $class;
|
|
||||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
|
||||||
$subPath = substr($subPath, 0, $lastPos);
|
|
||||||
$search = $subPath . '\\';
|
|
||||||
if (isset($this->prefixDirsPsr4[$search])) {
|
|
||||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
|
||||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
|
||||||
if (file_exists($file = $dir . $pathEnd)) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSR-4 fallback dirs
|
|
||||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
|
||||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSR-0 lookup
|
|
||||||
if (false !== $pos = strrpos($class, '\\')) {
|
|
||||||
// namespaced class name
|
|
||||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
|
||||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
|
||||||
} else {
|
|
||||||
// PEAR-like class name
|
|
||||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->prefixesPsr0[$first])) {
|
|
||||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
|
||||||
if (0 === strpos($class, $prefix)) {
|
|
||||||
foreach ($dirs as $dir) {
|
|
||||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSR-0 fallback dirs
|
|
||||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
|
||||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSR-0 include paths.
|
|
||||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private static function initializeIncludeClosure()
|
|
||||||
{
|
|
||||||
if (self::$includeFile !== null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scope isolated include.
|
|
||||||
*
|
|
||||||
* Prevents access to $this/self from included files.
|
|
||||||
*
|
|
||||||
* @param string $file
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
self::$includeFile = \Closure::bind(static function($file) {
|
|
||||||
include $file;
|
|
||||||
}, null, null);
|
|
||||||
}
|
|
||||||
}
|
|
359
vendor/composer/InstalledVersions.php
vendored
359
vendor/composer/InstalledVersions.php
vendored
@ -1,359 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Composer.
|
|
||||||
*
|
|
||||||
* (c) Nils Adermann <naderman@naderman.de>
|
|
||||||
* Jordi Boggiano <j.boggiano@seld.be>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Composer;
|
|
||||||
|
|
||||||
use Composer\Autoload\ClassLoader;
|
|
||||||
use Composer\Semver\VersionParser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is copied in every Composer installed project and available to all
|
|
||||||
*
|
|
||||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
|
||||||
*
|
|
||||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
|
||||||
*
|
|
||||||
* @final
|
|
||||||
*/
|
|
||||||
class InstalledVersions
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var mixed[]|null
|
|
||||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
|
||||||
*/
|
|
||||||
private static $installed;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool|null
|
|
||||||
*/
|
|
||||||
private static $canGetVendors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array[]
|
|
||||||
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
|
||||||
*/
|
|
||||||
private static $installedByVendor = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
|
||||||
*
|
|
||||||
* @return string[]
|
|
||||||
* @psalm-return list<string>
|
|
||||||
*/
|
|
||||||
public static function getInstalledPackages()
|
|
||||||
{
|
|
||||||
$packages = array();
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
$packages[] = array_keys($installed['versions']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1 === \count($packages)) {
|
|
||||||
return $packages[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of all package names with a specific type e.g. 'library'
|
|
||||||
*
|
|
||||||
* @param string $type
|
|
||||||
* @return string[]
|
|
||||||
* @psalm-return list<string>
|
|
||||||
*/
|
|
||||||
public static function getInstalledPackagesByType($type)
|
|
||||||
{
|
|
||||||
$packagesByType = array();
|
|
||||||
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
foreach ($installed['versions'] as $name => $package) {
|
|
||||||
if (isset($package['type']) && $package['type'] === $type) {
|
|
||||||
$packagesByType[] = $name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $packagesByType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the given package is installed
|
|
||||||
*
|
|
||||||
* This also returns true if the package name is provided or replaced by another package
|
|
||||||
*
|
|
||||||
* @param string $packageName
|
|
||||||
* @param bool $includeDevRequirements
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (isset($installed['versions'][$packageName])) {
|
|
||||||
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the given package satisfies a version constraint
|
|
||||||
*
|
|
||||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
|
||||||
*
|
|
||||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
|
||||||
*
|
|
||||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
|
||||||
* @param string $packageName
|
|
||||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
|
||||||
{
|
|
||||||
$constraint = $parser->parseConstraints((string) $constraint);
|
|
||||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
|
||||||
|
|
||||||
return $provided->matches($constraint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
|
||||||
*
|
|
||||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
|
||||||
* whether a given version of a package is installed, and not just whether it exists
|
|
||||||
*
|
|
||||||
* @param string $packageName
|
|
||||||
* @return string Version constraint usable with composer/semver
|
|
||||||
*/
|
|
||||||
public static function getVersionRanges($packageName)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (!isset($installed['versions'][$packageName])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ranges = array();
|
|
||||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
|
||||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
|
||||||
}
|
|
||||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
|
||||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
|
||||||
}
|
|
||||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
|
||||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
|
||||||
}
|
|
||||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
|
||||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode(' || ', $ranges);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $packageName
|
|
||||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
|
||||||
*/
|
|
||||||
public static function getVersion($packageName)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (!isset($installed['versions'][$packageName])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $installed['versions'][$packageName]['version'];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $packageName
|
|
||||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
|
||||||
*/
|
|
||||||
public static function getPrettyVersion($packageName)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (!isset($installed['versions'][$packageName])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $installed['versions'][$packageName]['pretty_version'];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $packageName
|
|
||||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
|
||||||
*/
|
|
||||||
public static function getReference($packageName)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (!isset($installed['versions'][$packageName])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $installed['versions'][$packageName]['reference'];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $packageName
|
|
||||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
|
||||||
*/
|
|
||||||
public static function getInstallPath($packageName)
|
|
||||||
{
|
|
||||||
foreach (self::getInstalled() as $installed) {
|
|
||||||
if (!isset($installed['versions'][$packageName])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
|
||||||
*/
|
|
||||||
public static function getRootPackage()
|
|
||||||
{
|
|
||||||
$installed = self::getInstalled();
|
|
||||||
|
|
||||||
return $installed[0]['root'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the raw installed.php data for custom implementations
|
|
||||||
*
|
|
||||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
|
||||||
* @return array[]
|
|
||||||
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
|
||||||
*/
|
|
||||||
public static function getRawData()
|
|
||||||
{
|
|
||||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
|
||||||
|
|
||||||
if (null === self::$installed) {
|
|
||||||
// only require the installed.php file if this file is loaded from its dumped location,
|
|
||||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
|
||||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
|
||||||
self::$installed = include __DIR__ . '/installed.php';
|
|
||||||
} else {
|
|
||||||
self::$installed = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$installed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
|
||||||
*
|
|
||||||
* @return array[]
|
|
||||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
|
||||||
*/
|
|
||||||
public static function getAllRawData()
|
|
||||||
{
|
|
||||||
return self::getInstalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lets you reload the static array from another file
|
|
||||||
*
|
|
||||||
* This is only useful for complex integrations in which a project needs to use
|
|
||||||
* this class but then also needs to execute another project's autoloader in process,
|
|
||||||
* and wants to ensure both projects have access to their version of installed.php.
|
|
||||||
*
|
|
||||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
|
||||||
* the data it needs from this class, then call reload() with
|
|
||||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
|
||||||
* the project in which it runs can then also use this class safely, without
|
|
||||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
|
||||||
*
|
|
||||||
* @param array[] $data A vendor/composer/installed.php data set
|
|
||||||
* @return void
|
|
||||||
*
|
|
||||||
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
|
||||||
*/
|
|
||||||
public static function reload($data)
|
|
||||||
{
|
|
||||||
self::$installed = $data;
|
|
||||||
self::$installedByVendor = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array[]
|
|
||||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
|
||||||
*/
|
|
||||||
private static function getInstalled()
|
|
||||||
{
|
|
||||||
if (null === self::$canGetVendors) {
|
|
||||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
|
||||||
}
|
|
||||||
|
|
||||||
$installed = array();
|
|
||||||
|
|
||||||
if (self::$canGetVendors) {
|
|
||||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
|
||||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
|
||||||
$installed[] = self::$installedByVendor[$vendorDir];
|
|
||||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
|
||||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
|
||||||
$required = require $vendorDir.'/composer/installed.php';
|
|
||||||
$installed[] = self::$installedByVendor[$vendorDir] = $required;
|
|
||||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
|
||||||
self::$installed = $installed[count($installed) - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null === self::$installed) {
|
|
||||||
// only require the installed.php file if this file is loaded from its dumped location,
|
|
||||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
|
||||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
|
||||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
|
||||||
$required = require __DIR__ . '/installed.php';
|
|
||||||
self::$installed = $required;
|
|
||||||
} else {
|
|
||||||
self::$installed = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self::$installed !== array()) {
|
|
||||||
$installed[] = self::$installed;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $installed;
|
|
||||||
}
|
|
||||||
}
|
|
21
vendor/composer/LICENSE
vendored
21
vendor/composer/LICENSE
vendored
@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
10
vendor/composer/autoload_classmap.php
vendored
10
vendor/composer/autoload_classmap.php
vendored
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload_classmap.php @generated by Composer
|
|
||||||
|
|
||||||
$vendorDir = dirname(__DIR__);
|
|
||||||
$baseDir = dirname($vendorDir);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
|
||||||
);
|
|
9
vendor/composer/autoload_namespaces.php
vendored
9
vendor/composer/autoload_namespaces.php
vendored
@ -1,9 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload_namespaces.php @generated by Composer
|
|
||||||
|
|
||||||
$vendorDir = dirname(__DIR__);
|
|
||||||
$baseDir = dirname($vendorDir);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
);
|
|
10
vendor/composer/autoload_psr4.php
vendored
10
vendor/composer/autoload_psr4.php
vendored
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload_psr4.php @generated by Composer
|
|
||||||
|
|
||||||
$vendorDir = dirname(__DIR__);
|
|
||||||
$baseDir = dirname($vendorDir);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'Workerman\\' => array($vendorDir . '/workerman/workerman'),
|
|
||||||
);
|
|
38
vendor/composer/autoload_real.php
vendored
38
vendor/composer/autoload_real.php
vendored
@ -1,38 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload_real.php @generated by Composer
|
|
||||||
|
|
||||||
class ComposerAutoloaderInit7d5fe279e64858f72c1db8f82722e08b
|
|
||||||
{
|
|
||||||
private static $loader;
|
|
||||||
|
|
||||||
public static function loadClassLoader($class)
|
|
||||||
{
|
|
||||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
|
||||||
require __DIR__ . '/ClassLoader.php';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return \Composer\Autoload\ClassLoader
|
|
||||||
*/
|
|
||||||
public static function getLoader()
|
|
||||||
{
|
|
||||||
if (null !== self::$loader) {
|
|
||||||
return self::$loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
require __DIR__ . '/platform_check.php';
|
|
||||||
|
|
||||||
spl_autoload_register(array('ComposerAutoloaderInit7d5fe279e64858f72c1db8f82722e08b', 'loadClassLoader'), true, true);
|
|
||||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
|
||||||
spl_autoload_unregister(array('ComposerAutoloaderInit7d5fe279e64858f72c1db8f82722e08b', 'loadClassLoader'));
|
|
||||||
|
|
||||||
require __DIR__ . '/autoload_static.php';
|
|
||||||
call_user_func(\Composer\Autoload\ComposerStaticInit7d5fe279e64858f72c1db8f82722e08b::getInitializer($loader));
|
|
||||||
|
|
||||||
$loader->register(true);
|
|
||||||
|
|
||||||
return $loader;
|
|
||||||
}
|
|
||||||
}
|
|
36
vendor/composer/autoload_static.php
vendored
36
vendor/composer/autoload_static.php
vendored
@ -1,36 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// autoload_static.php @generated by Composer
|
|
||||||
|
|
||||||
namespace Composer\Autoload;
|
|
||||||
|
|
||||||
class ComposerStaticInit7d5fe279e64858f72c1db8f82722e08b
|
|
||||||
{
|
|
||||||
public static $prefixLengthsPsr4 = array (
|
|
||||||
'W' =>
|
|
||||||
array (
|
|
||||||
'Workerman\\' => 10,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
public static $prefixDirsPsr4 = array (
|
|
||||||
'Workerman\\' =>
|
|
||||||
array (
|
|
||||||
0 => __DIR__ . '/..' . '/workerman/workerman',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
public static $classMap = array (
|
|
||||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
|
||||||
);
|
|
||||||
|
|
||||||
public static function getInitializer(ClassLoader $loader)
|
|
||||||
{
|
|
||||||
return \Closure::bind(function () use ($loader) {
|
|
||||||
$loader->prefixLengthsPsr4 = ComposerStaticInit7d5fe279e64858f72c1db8f82722e08b::$prefixLengthsPsr4;
|
|
||||||
$loader->prefixDirsPsr4 = ComposerStaticInit7d5fe279e64858f72c1db8f82722e08b::$prefixDirsPsr4;
|
|
||||||
$loader->classMap = ComposerStaticInit7d5fe279e64858f72c1db8f82722e08b::$classMap;
|
|
||||||
|
|
||||||
}, null, ClassLoader::class);
|
|
||||||
}
|
|
||||||
}
|
|
72
vendor/composer/installed.json
vendored
72
vendor/composer/installed.json
vendored
@ -1,72 +0,0 @@
|
|||||||
{
|
|
||||||
"packages": [
|
|
||||||
{
|
|
||||||
"name": "workerman/workerman",
|
|
||||||
"version": "v4.1.15",
|
|
||||||
"version_normalized": "4.1.15.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/walkor/workerman.git",
|
|
||||||
"reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/walkor/workerman/zipball/afc8242fc769ab7cf22eb4ac22b97cb59d465e4e",
|
|
||||||
"reference": "afc8242fc769ab7cf22eb4ac22b97cb59d465e4e",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=7.0"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"ext-event": "For better performance. "
|
|
||||||
},
|
|
||||||
"time": "2024-02-19T02:10:39+00:00",
|
|
||||||
"type": "library",
|
|
||||||
"installation-source": "dist",
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Workerman\\": "./"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "walkor",
|
|
||||||
"email": "walkor@workerman.net",
|
|
||||||
"homepage": "http://www.workerman.net",
|
|
||||||
"role": "Developer"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
|
|
||||||
"homepage": "http://www.workerman.net",
|
|
||||||
"keywords": [
|
|
||||||
"asynchronous",
|
|
||||||
"event-loop"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"email": "walkor@workerman.net",
|
|
||||||
"forum": "http://wenda.workerman.net/",
|
|
||||||
"issues": "https://github.com/walkor/workerman/issues",
|
|
||||||
"source": "https://github.com/walkor/workerman",
|
|
||||||
"wiki": "http://doc.workerman.net/"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://opencollective.com/workerman",
|
|
||||||
"type": "open_collective"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://www.patreon.com/walkor",
|
|
||||||
"type": "patreon"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"install-path": "../workerman/workerman"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"dev-package-names": []
|
|
||||||
}
|
|
32
vendor/composer/installed.php
vendored
32
vendor/composer/installed.php
vendored
@ -1,32 +0,0 @@
|
|||||||
<?php return array(
|
|
||||||
'root' => array(
|
|
||||||
'name' => 'workerman/phptty',
|
|
||||||
'pretty_version' => 'dev-master',
|
|
||||||
'version' => 'dev-master',
|
|
||||||
'reference' => '663e8e8fcf7891b61411bcbeb9515dde8cea924f',
|
|
||||||
'type' => 'project',
|
|
||||||
'install_path' => __DIR__ . '/../../',
|
|
||||||
'aliases' => array(),
|
|
||||||
'dev' => true,
|
|
||||||
),
|
|
||||||
'versions' => array(
|
|
||||||
'workerman/phptty' => array(
|
|
||||||
'pretty_version' => 'dev-master',
|
|
||||||
'version' => 'dev-master',
|
|
||||||
'reference' => '663e8e8fcf7891b61411bcbeb9515dde8cea924f',
|
|
||||||
'type' => 'project',
|
|
||||||
'install_path' => __DIR__ . '/../../',
|
|
||||||
'aliases' => array(),
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
'workerman/workerman' => array(
|
|
||||||
'pretty_version' => 'v4.1.15',
|
|
||||||
'version' => '4.1.15.0',
|
|
||||||
'reference' => 'afc8242fc769ab7cf22eb4ac22b97cb59d465e4e',
|
|
||||||
'type' => 'library',
|
|
||||||
'install_path' => __DIR__ . '/../workerman/workerman',
|
|
||||||
'aliases' => array(),
|
|
||||||
'dev_requirement' => false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
26
vendor/composer/platform_check.php
vendored
26
vendor/composer/platform_check.php
vendored
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// platform_check.php @generated by Composer
|
|
||||||
|
|
||||||
$issues = array();
|
|
||||||
|
|
||||||
if (!(PHP_VERSION_ID >= 70000)) {
|
|
||||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.0.0". You are running ' . PHP_VERSION . '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($issues) {
|
|
||||||
if (!headers_sent()) {
|
|
||||||
header('HTTP/1.1 500 Internal Server Error');
|
|
||||||
}
|
|
||||||
if (!ini_get('display_errors')) {
|
|
||||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
|
||||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
|
||||||
} elseif (!headers_sent()) {
|
|
||||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trigger_error(
|
|
||||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
|
||||||
E_USER_ERROR
|
|
||||||
);
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
6911
|
|
100
vendor/workerman/workerman.log
vendored
100
vendor/workerman/workerman.log
vendored
@ -1,100 +0,0 @@
|
|||||||
2024-06-16 06:02:02 pid:6880 Workerman[start.php] start in DEBUG mode
|
|
||||||
2024-06-16 06:02:03 pid:6880 Workerman[start.php] stopping ...
|
|
||||||
2024-06-16 06:02:03 pid:6880 Workerman[start.php] has been stopped
|
|
||||||
2024-06-16 06:02:06 pid:6909 Workerman[start.php] start in DAEMON mode
|
|
||||||
2024-07-26 02:40:35 pid:6913 ArgumentCountError: Too few arguments to function udpWorkerOnMessage(), 2 passed in /www/php-socks5/vendor/workerman/workerman/Worker.php on line 2630 and exactly 3 expected in /www/php-socks5/start.php:435
|
|
||||||
Stack trace:
|
|
||||||
#0 /www/php-socks5/vendor/workerman/workerman/Worker.php(2630): udpWorkerOnMessage()
|
|
||||||
#1 [internal function]: Workerman\Worker->acceptUdpConnection()
|
|
||||||
#2 /www/php-socks5/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
|
|
||||||
#3 /www/php-socks5/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
|
|
||||||
#4 /www/php-socks5/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
|
|
||||||
#5 /www/php-socks5/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
|
|
||||||
#6 /www/php-socks5/vendor/workerman/workerman/Worker.php(560): Workerman\Worker::forkWorkers()
|
|
||||||
#7 /www/php-socks5/start.php(566): Workerman\Worker::runAll()
|
|
||||||
#8 {main}
|
|
||||||
2024-07-26 02:40:35 pid:6911 worker[none:6913] exit with status 64000
|
|
||||||
2024-08-22 04:41:53 pid:30460 ArgumentCountError: Too few arguments to function udpWorkerOnMessage(), 2 passed in /www/php-socks5/vendor/workerman/workerman/Worker.php on line 2630 and exactly 3 expected in /www/php-socks5/start.php:435
|
|
||||||
Stack trace:
|
|
||||||
#0 /www/php-socks5/vendor/workerman/workerman/Worker.php(2630): udpWorkerOnMessage()
|
|
||||||
#1 [internal function]: Workerman\Worker->acceptUdpConnection()
|
|
||||||
#2 /www/php-socks5/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
|
|
||||||
#3 /www/php-socks5/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
|
|
||||||
#4 /www/php-socks5/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
|
|
||||||
#5 /www/php-socks5/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
|
|
||||||
#6 /www/php-socks5/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
|
|
||||||
#7 /www/php-socks5/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
|
|
||||||
#8 /www/php-socks5/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
|
|
||||||
#9 /www/php-socks5/start.php(566): Workerman\Worker::runAll()
|
|
||||||
#10 {main}
|
|
||||||
2024-08-22 04:41:53 pid:6911 worker[none:30460] exit with status 64000
|
|
||||||
2024-10-16 09:13:08 pid:7425 ArgumentCountError: Too few arguments to function udpWorkerOnMessage(), 2 passed in /www/php-socks5/vendor/workerman/workerman/Worker.php on line 2630 and exactly 3 expected in /www/php-socks5/start.php:435
|
|
||||||
Stack trace:
|
|
||||||
#0 /www/php-socks5/vendor/workerman/workerman/Worker.php(2630): udpWorkerOnMessage()
|
|
||||||
#1 [internal function]: Workerman\Worker->acceptUdpConnection()
|
|
||||||
#2 /www/php-socks5/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
|
|
||||||
#3 /www/php-socks5/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
|
|
||||||
#4 /www/php-socks5/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
|
|
||||||
#5 /www/php-socks5/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
|
|
||||||
#6 /www/php-socks5/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
|
|
||||||
#7 /www/php-socks5/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
|
|
||||||
#8 /www/php-socks5/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
|
|
||||||
#9 /www/php-socks5/start.php(566): Workerman\Worker::runAll()
|
|
||||||
#10 {main}
|
|
||||||
2024-10-16 09:13:08 pid:6911 worker[none:7425] exit with status 64000
|
|
||||||
2024-11-01 23:30:00 pid:11414 ArgumentCountError: Too few arguments to function udpWorkerOnMessage(), 2 passed in /www/php-socks5/vendor/workerman/workerman/Worker.php on line 2630 and exactly 3 expected in /www/php-socks5/start.php:435
|
|
||||||
Stack trace:
|
|
||||||
#0 /www/php-socks5/vendor/workerman/workerman/Worker.php(2630): udpWorkerOnMessage()
|
|
||||||
#1 [internal function]: Workerman\Worker->acceptUdpConnection()
|
|
||||||
#2 /www/php-socks5/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
|
|
||||||
#3 /www/php-socks5/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
|
|
||||||
#4 /www/php-socks5/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
|
|
||||||
#5 /www/php-socks5/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
|
|
||||||
#6 /www/php-socks5/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
|
|
||||||
#7 /www/php-socks5/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
|
|
||||||
#8 /www/php-socks5/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
|
|
||||||
#9 /www/php-socks5/start.php(566): Workerman\Worker::runAll()
|
|
||||||
#10 {main}
|
|
||||||
2024-11-01 23:30:00 pid:6911 worker[none:11414] exit with status 64000
|
|
||||||
2024-11-03 03:46:28 pid:8250 ArgumentCountError: Too few arguments to function udpWorkerOnMessage(), 2 passed in /www/php-socks5/vendor/workerman/workerman/Worker.php on line 2630 and exactly 3 expected in /www/php-socks5/start.php:435
|
|
||||||
Stack trace:
|
|
||||||
#0 /www/php-socks5/vendor/workerman/workerman/Worker.php(2630): udpWorkerOnMessage()
|
|
||||||
#1 [internal function]: Workerman\Worker->acceptUdpConnection()
|
|
||||||
#2 /www/php-socks5/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
|
|
||||||
#3 /www/php-socks5/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
|
|
||||||
#4 /www/php-socks5/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
|
|
||||||
#5 /www/php-socks5/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
|
|
||||||
#6 /www/php-socks5/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
|
|
||||||
#7 /www/php-socks5/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
|
|
||||||
#8 /www/php-socks5/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
|
|
||||||
#9 /www/php-socks5/start.php(566): Workerman\Worker::runAll()
|
|
||||||
#10 {main}
|
|
||||||
2024-11-03 03:46:28 pid:6911 worker[none:8250] exit with status 64000
|
|
||||||
2024-11-03 13:04:45 pid:12375 ArgumentCountError: Too few arguments to function udpWorkerOnMessage(), 2 passed in /www/php-socks5/vendor/workerman/workerman/Worker.php on line 2630 and exactly 3 expected in /www/php-socks5/start.php:435
|
|
||||||
Stack trace:
|
|
||||||
#0 /www/php-socks5/vendor/workerman/workerman/Worker.php(2630): udpWorkerOnMessage()
|
|
||||||
#1 [internal function]: Workerman\Worker->acceptUdpConnection()
|
|
||||||
#2 /www/php-socks5/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
|
|
||||||
#3 /www/php-socks5/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
|
|
||||||
#4 /www/php-socks5/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
|
|
||||||
#5 /www/php-socks5/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
|
|
||||||
#6 /www/php-socks5/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
|
|
||||||
#7 /www/php-socks5/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
|
|
||||||
#8 /www/php-socks5/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
|
|
||||||
#9 /www/php-socks5/start.php(566): Workerman\Worker::runAll()
|
|
||||||
#10 {main}
|
|
||||||
2024-11-03 13:04:45 pid:6911 worker[none:12375] exit with status 64000
|
|
||||||
2024-11-25 02:25:35 pid:22517 ArgumentCountError: Too few arguments to function udpWorkerOnMessage(), 2 passed in /www/php-socks5/vendor/workerman/workerman/Worker.php on line 2630 and exactly 3 expected in /www/php-socks5/start.php:435
|
|
||||||
Stack trace:
|
|
||||||
#0 /www/php-socks5/vendor/workerman/workerman/Worker.php(2630): udpWorkerOnMessage()
|
|
||||||
#1 [internal function]: Workerman\Worker->acceptUdpConnection()
|
|
||||||
#2 /www/php-socks5/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
|
|
||||||
#3 /www/php-socks5/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
|
|
||||||
#4 /www/php-socks5/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
|
|
||||||
#5 /www/php-socks5/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
|
|
||||||
#6 /www/php-socks5/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
|
|
||||||
#7 /www/php-socks5/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
|
|
||||||
#8 /www/php-socks5/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
|
|
||||||
#9 /www/php-socks5/start.php(566): Workerman\Worker::runAll()
|
|
||||||
#10 {main}
|
|
||||||
2024-11-25 02:25:35 pid:6911 worker[none:22517] exit with status 64000
|
|
@ -1,4 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
open_collective: workerman
|
|
||||||
patreon: walkor
|
|
6
vendor/workerman/workerman/.gitignore
vendored
6
vendor/workerman/workerman/.gitignore
vendored
@ -1,6 +0,0 @@
|
|||||||
logs
|
|
||||||
.buildpath
|
|
||||||
.project
|
|
||||||
.settings
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
69
vendor/workerman/workerman/Autoloader.php
vendored
69
vendor/workerman/workerman/Autoloader.php
vendored
@ -1,69 +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 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 Workerman;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Autoload.
|
|
||||||
*/
|
|
||||||
class Autoloader
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Autoload root path.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected static $_autoloadRootPath = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set autoload root path.
|
|
||||||
*
|
|
||||||
* @param string $root_path
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function setRootPath($root_path)
|
|
||||||
{
|
|
||||||
self::$_autoloadRootPath = $root_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load files by namespace.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public static function loadByNamespace($name)
|
|
||||||
{
|
|
||||||
$class_path = \str_replace('\\', \DIRECTORY_SEPARATOR, $name);
|
|
||||||
if (\strpos($name, 'Workerman\\') === 0) {
|
|
||||||
$class_file = __DIR__ . \substr($class_path, \strlen('Workerman')) . '.php';
|
|
||||||
} else {
|
|
||||||
if (self::$_autoloadRootPath) {
|
|
||||||
$class_file = self::$_autoloadRootPath . \DIRECTORY_SEPARATOR . $class_path . '.php';
|
|
||||||
}
|
|
||||||
if (empty($class_file) || !\is_file($class_file)) {
|
|
||||||
$class_file = __DIR__ . \DIRECTORY_SEPARATOR . '..' . \DIRECTORY_SEPARATOR . "$class_path.php";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (\is_file($class_file)) {
|
|
||||||
require_once($class_file);
|
|
||||||
if (\class_exists($name, false)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
\spl_autoload_register('\Workerman\Autoloader::loadByNamespace');
|
|
@ -1,378 +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 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 Workerman\Connection;
|
|
||||||
|
|
||||||
use StdClass;
|
|
||||||
use Workerman\Events\EventInterface;
|
|
||||||
use Workerman\Lib\Timer;
|
|
||||||
use Workerman\Worker;
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AsyncTcpConnection.
|
|
||||||
*/
|
|
||||||
class AsyncTcpConnection extends TcpConnection
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Emitted when socket connection is successfully established.
|
|
||||||
*
|
|
||||||
* @var callable|null
|
|
||||||
*/
|
|
||||||
public $onConnect = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transport layer protocol.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $transport = 'tcp';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_status = self::STATUS_INITIAL;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote host.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_remoteHost = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote port.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_remotePort = 80;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect start time.
|
|
||||||
*
|
|
||||||
* @var float
|
|
||||||
*/
|
|
||||||
protected $_connectStartTime = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote URI.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_remoteURI = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Context option.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_contextOption = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reconnect timer.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_reconnectTimer = null;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PHP built-in protocols.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected static $_builtinTransports = array(
|
|
||||||
'tcp' => 'tcp',
|
|
||||||
'udp' => 'udp',
|
|
||||||
'unix' => 'unix',
|
|
||||||
'ssl' => 'ssl',
|
|
||||||
'sslv2' => 'sslv2',
|
|
||||||
'sslv3' => 'sslv3',
|
|
||||||
'tls' => 'tls'
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct.
|
|
||||||
*
|
|
||||||
* @param string $remote_address
|
|
||||||
* @param array $context_option
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function __construct($remote_address, array $context_option = array())
|
|
||||||
{
|
|
||||||
$address_info = \parse_url($remote_address);
|
|
||||||
if (!$address_info) {
|
|
||||||
list($scheme, $this->_remoteAddress) = \explode(':', $remote_address, 2);
|
|
||||||
if('unix' === strtolower($scheme)) {
|
|
||||||
$this->_remoteAddress = substr($remote_address, strpos($remote_address, '/') + 2);
|
|
||||||
}
|
|
||||||
if (!$this->_remoteAddress) {
|
|
||||||
Worker::safeEcho(new \Exception('bad remote_address'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!isset($address_info['port'])) {
|
|
||||||
$address_info['port'] = 0;
|
|
||||||
}
|
|
||||||
if (!isset($address_info['path'])) {
|
|
||||||
$address_info['path'] = '/';
|
|
||||||
}
|
|
||||||
if (!isset($address_info['query'])) {
|
|
||||||
$address_info['query'] = '';
|
|
||||||
} else {
|
|
||||||
$address_info['query'] = '?' . $address_info['query'];
|
|
||||||
}
|
|
||||||
$this->_remoteHost = $address_info['host'];
|
|
||||||
$this->_remotePort = $address_info['port'];
|
|
||||||
$this->_remoteURI = "{$address_info['path']}{$address_info['query']}";
|
|
||||||
$scheme = isset($address_info['scheme']) ? $address_info['scheme'] : 'tcp';
|
|
||||||
$this->_remoteAddress = 'unix' === strtolower($scheme)
|
|
||||||
? substr($remote_address, strpos($remote_address, '/') + 2)
|
|
||||||
: $this->_remoteHost . ':' . $this->_remotePort;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->id = $this->_id = self::$_idRecorder++;
|
|
||||||
if(\PHP_INT_MAX === self::$_idRecorder){
|
|
||||||
self::$_idRecorder = 0;
|
|
||||||
}
|
|
||||||
// Check application layer protocol class.
|
|
||||||
if (!isset(self::$_builtinTransports[$scheme])) {
|
|
||||||
$scheme = \ucfirst($scheme);
|
|
||||||
$this->protocol = '\\Protocols\\' . $scheme;
|
|
||||||
if (!\class_exists($this->protocol)) {
|
|
||||||
$this->protocol = "\\Workerman\\Protocols\\$scheme";
|
|
||||||
if (!\class_exists($this->protocol)) {
|
|
||||||
throw new Exception("class \\Protocols\\$scheme not exist");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->transport = self::$_builtinTransports[$scheme];
|
|
||||||
}
|
|
||||||
|
|
||||||
// For statistics.
|
|
||||||
++self::$statistics['connection_count'];
|
|
||||||
$this->maxSendBufferSize = self::$defaultMaxSendBufferSize;
|
|
||||||
$this->maxPackageSize = self::$defaultMaxPackageSize;
|
|
||||||
$this->_contextOption = $context_option;
|
|
||||||
$this->context = new StdClass;
|
|
||||||
static::$connections[$this->_id] = $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do connect.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function connect()
|
|
||||||
{
|
|
||||||
if ($this->_status !== self::STATUS_INITIAL && $this->_status !== self::STATUS_CLOSING &&
|
|
||||||
$this->_status !== self::STATUS_CLOSED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->_status = self::STATUS_CONNECTING;
|
|
||||||
$this->_connectStartTime = \microtime(true);
|
|
||||||
if ($this->transport !== 'unix') {
|
|
||||||
if (!$this->_remotePort) {
|
|
||||||
$this->_remotePort = $this->transport === 'ssl' ? 443 : 80;
|
|
||||||
$this->_remoteAddress = $this->_remoteHost.':'.$this->_remotePort;
|
|
||||||
}
|
|
||||||
// Open socket connection asynchronously.
|
|
||||||
if ($this->_contextOption) {
|
|
||||||
$context = \stream_context_create($this->_contextOption);
|
|
||||||
$this->_socket = \stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}",
|
|
||||||
$errno, $errstr, 0, \STREAM_CLIENT_ASYNC_CONNECT, $context);
|
|
||||||
} else {
|
|
||||||
$this->_socket = \stream_socket_client("tcp://{$this->_remoteHost}:{$this->_remotePort}",
|
|
||||||
$errno, $errstr, 0, \STREAM_CLIENT_ASYNC_CONNECT);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->_socket = \stream_socket_client("{$this->transport}://{$this->_remoteAddress}", $errno, $errstr, 0,
|
|
||||||
\STREAM_CLIENT_ASYNC_CONNECT);
|
|
||||||
}
|
|
||||||
// If failed attempt to emit onError callback.
|
|
||||||
if (!$this->_socket || !\is_resource($this->_socket)) {
|
|
||||||
$this->emitError(\WORKERMAN_CONNECT_FAIL, $errstr);
|
|
||||||
if ($this->_status === self::STATUS_CLOSING) {
|
|
||||||
$this->destroy();
|
|
||||||
}
|
|
||||||
if ($this->_status === self::STATUS_CLOSED) {
|
|
||||||
$this->onConnect = null;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Add socket to global event loop waiting connection is successfully established or faild.
|
|
||||||
Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'checkConnection'));
|
|
||||||
// For windows.
|
|
||||||
if(\DIRECTORY_SEPARATOR === '\\') {
|
|
||||||
Worker::$globalEvent->add($this->_socket, EventInterface::EV_EXCEPT, array($this, 'checkConnection'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reconnect.
|
|
||||||
*
|
|
||||||
* @param int $after
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function reconnect($after = 0)
|
|
||||||
{
|
|
||||||
$this->_status = self::STATUS_INITIAL;
|
|
||||||
static::$connections[$this->_id] = $this;
|
|
||||||
if ($this->_reconnectTimer) {
|
|
||||||
Timer::del($this->_reconnectTimer);
|
|
||||||
}
|
|
||||||
if ($after > 0) {
|
|
||||||
$this->_reconnectTimer = Timer::add($after, array($this, 'connect'), null, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CancelReconnect.
|
|
||||||
*/
|
|
||||||
public function cancelReconnect()
|
|
||||||
{
|
|
||||||
if ($this->_reconnectTimer) {
|
|
||||||
Timer::del($this->_reconnectTimer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get remote address.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getRemoteHost()
|
|
||||||
{
|
|
||||||
return $this->_remoteHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get remote URI.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getRemoteURI()
|
|
||||||
{
|
|
||||||
return $this->_remoteURI;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to emit onError callback.
|
|
||||||
*
|
|
||||||
* @param int $code
|
|
||||||
* @param string $msg
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function emitError($code, $msg)
|
|
||||||
{
|
|
||||||
$this->_status = self::STATUS_CLOSING;
|
|
||||||
if ($this->onError) {
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onError, $this, $code, $msg);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check connection is successfully established or faild.
|
|
||||||
*
|
|
||||||
* @param resource $socket
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function checkConnection()
|
|
||||||
{
|
|
||||||
// Remove EV_EXPECT for windows.
|
|
||||||
if(\DIRECTORY_SEPARATOR === '\\') {
|
|
||||||
Worker::$globalEvent->del($this->_socket, EventInterface::EV_EXCEPT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove write listener.
|
|
||||||
Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
|
|
||||||
|
|
||||||
if ($this->_status !== self::STATUS_CONNECTING) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check socket state.
|
|
||||||
if ($address = \stream_socket_get_name($this->_socket, true)) {
|
|
||||||
// Nonblocking.
|
|
||||||
\stream_set_blocking($this->_socket, false);
|
|
||||||
// Compatible with hhvm
|
|
||||||
if (\function_exists('stream_set_read_buffer')) {
|
|
||||||
\stream_set_read_buffer($this->_socket, 0);
|
|
||||||
}
|
|
||||||
// Try to open keepalive for tcp and disable Nagle algorithm.
|
|
||||||
if (\function_exists('socket_import_stream') && $this->transport === 'tcp') {
|
|
||||||
$raw_socket = \socket_import_stream($this->_socket);
|
|
||||||
\socket_set_option($raw_socket, \SOL_SOCKET, \SO_KEEPALIVE, 1);
|
|
||||||
\socket_set_option($raw_socket, \SOL_TCP, \TCP_NODELAY, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SSL handshake.
|
|
||||||
if ($this->transport === 'ssl') {
|
|
||||||
$this->_sslHandshakeCompleted = $this->doSslHandshake($this->_socket);
|
|
||||||
if ($this->_sslHandshakeCompleted === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// There are some data waiting to send.
|
|
||||||
if ($this->_sendBuffer) {
|
|
||||||
Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register a listener waiting read event.
|
|
||||||
Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
|
|
||||||
|
|
||||||
$this->_status = self::STATUS_ESTABLISHED;
|
|
||||||
$this->_remoteAddress = $address;
|
|
||||||
|
|
||||||
// Try to emit onConnect callback.
|
|
||||||
if ($this->onConnect) {
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onConnect, $this);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Try to emit protocol::onConnect
|
|
||||||
if ($this->protocol && \method_exists($this->protocol, 'onConnect')) {
|
|
||||||
try {
|
|
||||||
\call_user_func(array($this->protocol, 'onConnect'), $this);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Connection failed.
|
|
||||||
$this->emitError(\WORKERMAN_CONNECT_FAIL, 'connect ' . $this->_remoteAddress . ' fail after ' . round(\microtime(true) - $this->_connectStartTime, 4) . ' seconds');
|
|
||||||
if ($this->_status === self::STATUS_CLOSING) {
|
|
||||||
$this->destroy();
|
|
||||||
}
|
|
||||||
if ($this->_status === self::STATUS_CLOSED) {
|
|
||||||
$this->onConnect = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,203 +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 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 Workerman\Connection;
|
|
||||||
|
|
||||||
use Workerman\Events\EventInterface;
|
|
||||||
use Workerman\Worker;
|
|
||||||
use \Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AsyncUdpConnection.
|
|
||||||
*/
|
|
||||||
class AsyncUdpConnection extends UdpConnection
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Emitted when socket connection is successfully established.
|
|
||||||
*
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $onConnect = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emitted when socket connection closed.
|
|
||||||
*
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $onClose = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connected or not.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $connected = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Context option.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_contextOption = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct.
|
|
||||||
*
|
|
||||||
* @param string $remote_address
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function __construct($remote_address, $context_option = null)
|
|
||||||
{
|
|
||||||
// Get the application layer communication protocol and listening address.
|
|
||||||
list($scheme, $address) = \explode(':', $remote_address, 2);
|
|
||||||
// Check application layer protocol class.
|
|
||||||
if ($scheme !== 'udp') {
|
|
||||||
$scheme = \ucfirst($scheme);
|
|
||||||
$this->protocol = '\\Protocols\\' . $scheme;
|
|
||||||
if (!\class_exists($this->protocol)) {
|
|
||||||
$this->protocol = "\\Workerman\\Protocols\\$scheme";
|
|
||||||
if (!\class_exists($this->protocol)) {
|
|
||||||
throw new Exception("class \\Protocols\\$scheme not exist");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_remoteAddress = \substr($address, 2);
|
|
||||||
$this->_contextOption = $context_option;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For udp package.
|
|
||||||
*
|
|
||||||
* @param resource $socket
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function baseRead($socket)
|
|
||||||
{
|
|
||||||
$recv_buffer = \stream_socket_recvfrom($socket, Worker::MAX_UDP_PACKAGE_SIZE, 0, $remote_address);
|
|
||||||
if (false === $recv_buffer || empty($remote_address)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->onMessage) {
|
|
||||||
if ($this->protocol) {
|
|
||||||
$parser = $this->protocol;
|
|
||||||
$recv_buffer = $parser::decode($recv_buffer, $this);
|
|
||||||
}
|
|
||||||
++ConnectionInterface::$statistics['total_request'];
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onMessage, $this, $recv_buffer);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends data on the connection.
|
|
||||||
*
|
|
||||||
* @param string $send_buffer
|
|
||||||
* @param bool $raw
|
|
||||||
* @return void|boolean
|
|
||||||
*/
|
|
||||||
public function send($send_buffer, $raw = false)
|
|
||||||
{
|
|
||||||
if (false === $raw && $this->protocol) {
|
|
||||||
$parser = $this->protocol;
|
|
||||||
$send_buffer = $parser::encode($send_buffer, $this);
|
|
||||||
if ($send_buffer === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->connected === false) {
|
|
||||||
$this->connect();
|
|
||||||
}
|
|
||||||
return \strlen($send_buffer) === \stream_socket_sendto($this->_socket, $send_buffer, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close connection.
|
|
||||||
*
|
|
||||||
* @param mixed $data
|
|
||||||
* @param bool $raw
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function close($data = null, $raw = false)
|
|
||||||
{
|
|
||||||
if ($data !== null) {
|
|
||||||
$this->send($data, $raw);
|
|
||||||
}
|
|
||||||
Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
|
|
||||||
\fclose($this->_socket);
|
|
||||||
$this->connected = false;
|
|
||||||
// Try to emit onClose callback.
|
|
||||||
if ($this->onClose) {
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onClose, $this);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->onConnect = $this->onMessage = $this->onClose = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function connect()
|
|
||||||
{
|
|
||||||
if ($this->connected === true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($this->_contextOption) {
|
|
||||||
$context = \stream_context_create($this->_contextOption);
|
|
||||||
$this->_socket = \stream_socket_client("udp://{$this->_remoteAddress}", $errno, $errmsg,
|
|
||||||
30, \STREAM_CLIENT_CONNECT, $context);
|
|
||||||
} else {
|
|
||||||
$this->_socket = \stream_socket_client("udp://{$this->_remoteAddress}", $errno, $errmsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->_socket) {
|
|
||||||
Worker::safeEcho(new \Exception($errmsg));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
\stream_set_blocking($this->_socket, false);
|
|
||||||
|
|
||||||
if ($this->onMessage) {
|
|
||||||
Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
|
|
||||||
}
|
|
||||||
$this->connected = true;
|
|
||||||
// Try to emit onConnect callback.
|
|
||||||
if ($this->onConnect) {
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onConnect, $this);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,126 +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 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 Workerman\Connection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ConnectionInterface.
|
|
||||||
*/
|
|
||||||
#[\AllowDynamicProperties]
|
|
||||||
abstract class ConnectionInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Statistics for status command.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public static $statistics = array(
|
|
||||||
'connection_count' => 0,
|
|
||||||
'total_request' => 0,
|
|
||||||
'throw_exception' => 0,
|
|
||||||
'send_fail' => 0,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emitted when data is received.
|
|
||||||
*
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $onMessage = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emitted when the other end of the socket sends a FIN packet.
|
|
||||||
*
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $onClose = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emitted when an error occurs with connection.
|
|
||||||
*
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $onError = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends data on the connection.
|
|
||||||
*
|
|
||||||
* @param mixed $send_buffer
|
|
||||||
* @return void|boolean
|
|
||||||
*/
|
|
||||||
abstract public function send($send_buffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get remote IP.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
abstract public function getRemoteIp();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get remote port.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
abstract public function getRemotePort();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get remote address.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
abstract public function getRemoteAddress();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get local IP.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
abstract public function getLocalIp();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get local port.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
abstract public function getLocalPort();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get local address.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
abstract public function getLocalAddress();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is ipv4.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
abstract public function isIPv4();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is ipv6.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
abstract public function isIPv6();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close connection.
|
|
||||||
*
|
|
||||||
* @param string|null $data
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
abstract public function close($data = null);
|
|
||||||
}
|
|
@ -1,982 +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 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 Workerman\Connection;
|
|
||||||
|
|
||||||
use Workerman\Events\EventInterface;
|
|
||||||
use Workerman\Worker;
|
|
||||||
use \Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TcpConnection.
|
|
||||||
*/
|
|
||||||
class TcpConnection extends ConnectionInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Read buffer size.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const READ_BUFFER_SIZE = 65535;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status initial.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const STATUS_INITIAL = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status connecting.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const STATUS_CONNECTING = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status connection established.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const STATUS_ESTABLISHED = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status closing.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const STATUS_CLOSING = 4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status closed.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const STATUS_CLOSED = 8;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emitted when data is received.
|
|
||||||
*
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $onMessage = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emitted when the other end of the socket sends a FIN packet.
|
|
||||||
*
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $onClose = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emitted when an error occurs with connection.
|
|
||||||
*
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $onError = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emitted when the send buffer becomes full.
|
|
||||||
*
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $onBufferFull = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emitted when the send buffer becomes empty.
|
|
||||||
*
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $onBufferDrain = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Application layer protocol.
|
|
||||||
* The format is like this Workerman\\Protocols\\Http.
|
|
||||||
*
|
|
||||||
* @var \Workerman\Protocols\ProtocolInterface
|
|
||||||
*/
|
|
||||||
public $protocol = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transport (tcp/udp/unix/ssl).
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $transport = 'tcp';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Which worker belong to.
|
|
||||||
*
|
|
||||||
* @var Worker
|
|
||||||
*/
|
|
||||||
public $worker = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bytes read.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $bytesRead = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bytes written.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $bytesWritten = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connection->id.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $id = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A copy of $worker->id which used to clean up the connection in worker->connections
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_id = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the maximum send buffer size for the current connection.
|
|
||||||
* OnBufferFull callback will be emited When the send buffer is full.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $maxSendBufferSize = 1048576;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Context.
|
|
||||||
*
|
|
||||||
* @var object|null
|
|
||||||
*/
|
|
||||||
public $context = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default send buffer size.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public static $defaultMaxSendBufferSize = 1048576;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the maximum acceptable packet size for the current connection.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $maxPackageSize = 1048576;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default maximum acceptable packet size.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public static $defaultMaxPackageSize = 10485760;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Id recorder.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected static $_idRecorder = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Socket
|
|
||||||
*
|
|
||||||
* @var resource
|
|
||||||
*/
|
|
||||||
protected $_socket = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send buffer.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_sendBuffer = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receive buffer.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_recvBuffer = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current package length.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_currentPackageLength = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connection status.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_status = self::STATUS_ESTABLISHED;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote address.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_remoteAddress = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is paused.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $_isPaused = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SSL handshake completed or not.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $_sslHandshakeCompleted = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All connection instances.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public static $connections = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status to string.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public static $_statusToString = array(
|
|
||||||
self::STATUS_INITIAL => 'INITIAL',
|
|
||||||
self::STATUS_CONNECTING => 'CONNECTING',
|
|
||||||
self::STATUS_ESTABLISHED => 'ESTABLISHED',
|
|
||||||
self::STATUS_CLOSING => 'CLOSING',
|
|
||||||
self::STATUS_CLOSED => 'CLOSED',
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct.
|
|
||||||
*
|
|
||||||
* @param resource $socket
|
|
||||||
* @param string $remote_address
|
|
||||||
*/
|
|
||||||
public function __construct($socket, $remote_address = '')
|
|
||||||
{
|
|
||||||
++self::$statistics['connection_count'];
|
|
||||||
$this->id = $this->_id = self::$_idRecorder++;
|
|
||||||
if(self::$_idRecorder === \PHP_INT_MAX){
|
|
||||||
self::$_idRecorder = 0;
|
|
||||||
}
|
|
||||||
$this->_socket = $socket;
|
|
||||||
\stream_set_blocking($this->_socket, 0);
|
|
||||||
// Compatible with hhvm
|
|
||||||
if (\function_exists('stream_set_read_buffer')) {
|
|
||||||
\stream_set_read_buffer($this->_socket, 0);
|
|
||||||
}
|
|
||||||
Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
|
|
||||||
$this->maxSendBufferSize = self::$defaultMaxSendBufferSize;
|
|
||||||
$this->maxPackageSize = self::$defaultMaxPackageSize;
|
|
||||||
$this->_remoteAddress = $remote_address;
|
|
||||||
static::$connections[$this->id] = $this;
|
|
||||||
$this->context = new \stdClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get status.
|
|
||||||
*
|
|
||||||
* @param bool $raw_output
|
|
||||||
*
|
|
||||||
* @return int|string
|
|
||||||
*/
|
|
||||||
public function getStatus($raw_output = true)
|
|
||||||
{
|
|
||||||
if ($raw_output) {
|
|
||||||
return $this->_status;
|
|
||||||
}
|
|
||||||
return self::$_statusToString[$this->_status];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends data on the connection.
|
|
||||||
*
|
|
||||||
* @param mixed $send_buffer
|
|
||||||
* @param bool $raw
|
|
||||||
* @return bool|null
|
|
||||||
*/
|
|
||||||
public function send($send_buffer, $raw = false)
|
|
||||||
{
|
|
||||||
if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to call protocol::encode($send_buffer) before sending.
|
|
||||||
if (false === $raw && $this->protocol !== null) {
|
|
||||||
$parser = $this->protocol;
|
|
||||||
$send_buffer = $parser::encode($send_buffer, $this);
|
|
||||||
if ($send_buffer === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->_status !== self::STATUS_ESTABLISHED ||
|
|
||||||
($this->transport === 'ssl' && $this->_sslHandshakeCompleted !== true)
|
|
||||||
) {
|
|
||||||
if ($this->_sendBuffer && $this->bufferIsFull()) {
|
|
||||||
++self::$statistics['send_fail'];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->_sendBuffer .= $send_buffer;
|
|
||||||
$this->checkBufferWillFull();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to send data directly.
|
|
||||||
if ($this->_sendBuffer === '') {
|
|
||||||
if ($this->transport === 'ssl') {
|
|
||||||
Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
|
|
||||||
$this->_sendBuffer = $send_buffer;
|
|
||||||
$this->checkBufferWillFull();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$len = 0;
|
|
||||||
try {
|
|
||||||
$len = @\fwrite($this->_socket, $send_buffer);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::log($e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::log($e);
|
|
||||||
}
|
|
||||||
// send successful.
|
|
||||||
if ($len === \strlen($send_buffer)) {
|
|
||||||
$this->bytesWritten += $len;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Send only part of the data.
|
|
||||||
if ($len > 0) {
|
|
||||||
$this->_sendBuffer = \substr($send_buffer, $len);
|
|
||||||
$this->bytesWritten += $len;
|
|
||||||
} else {
|
|
||||||
// Connection closed?
|
|
||||||
if (!\is_resource($this->_socket) || \feof($this->_socket)) {
|
|
||||||
++self::$statistics['send_fail'];
|
|
||||||
if ($this->onError) {
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onError, $this, \WORKERMAN_SEND_FAIL, 'client closed');
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->destroy();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->_sendBuffer = $send_buffer;
|
|
||||||
}
|
|
||||||
Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
|
|
||||||
// Check if the send buffer will be full.
|
|
||||||
$this->checkBufferWillFull();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->bufferIsFull()) {
|
|
||||||
++self::$statistics['send_fail'];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_sendBuffer .= $send_buffer;
|
|
||||||
// Check if the send buffer is full.
|
|
||||||
$this->checkBufferWillFull();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get remote IP.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getRemoteIp()
|
|
||||||
{
|
|
||||||
$pos = \strrpos($this->_remoteAddress, ':');
|
|
||||||
if ($pos) {
|
|
||||||
return (string) \substr($this->_remoteAddress, 0, $pos);
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get remote port.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getRemotePort()
|
|
||||||
{
|
|
||||||
if ($this->_remoteAddress) {
|
|
||||||
return (int) \substr(\strrchr($this->_remoteAddress, ':'), 1);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get remote address.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getRemoteAddress()
|
|
||||||
{
|
|
||||||
return $this->_remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get local IP.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getLocalIp()
|
|
||||||
{
|
|
||||||
$address = $this->getLocalAddress();
|
|
||||||
$pos = \strrpos($address, ':');
|
|
||||||
if (!$pos) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return \substr($address, 0, $pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get local port.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getLocalPort()
|
|
||||||
{
|
|
||||||
$address = $this->getLocalAddress();
|
|
||||||
$pos = \strrpos($address, ':');
|
|
||||||
if (!$pos) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return (int)\substr(\strrchr($address, ':'), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get local address.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getLocalAddress()
|
|
||||||
{
|
|
||||||
if (!\is_resource($this->_socket)) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return (string)@\stream_socket_get_name($this->_socket, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get send buffer queue size.
|
|
||||||
*
|
|
||||||
* @return integer
|
|
||||||
*/
|
|
||||||
public function getSendBufferQueueSize()
|
|
||||||
{
|
|
||||||
return \strlen($this->_sendBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get recv buffer queue size.
|
|
||||||
*
|
|
||||||
* @return integer
|
|
||||||
*/
|
|
||||||
public function getRecvBufferQueueSize()
|
|
||||||
{
|
|
||||||
return \strlen($this->_recvBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is ipv4.
|
|
||||||
*
|
|
||||||
* return bool.
|
|
||||||
*/
|
|
||||||
public function isIpV4()
|
|
||||||
{
|
|
||||||
if ($this->transport === 'unix') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return \strpos($this->getRemoteIp(), ':') === false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is ipv6.
|
|
||||||
*
|
|
||||||
* return bool.
|
|
||||||
*/
|
|
||||||
public function isIpV6()
|
|
||||||
{
|
|
||||||
if ($this->transport === 'unix') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return \strpos($this->getRemoteIp(), ':') !== false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses the reading of data. That is onMessage will not be emitted. Useful to throttle back an upload.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function pauseRecv()
|
|
||||||
{
|
|
||||||
Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
|
|
||||||
$this->_isPaused = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resumes reading after a call to pauseRecv.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function resumeRecv()
|
|
||||||
{
|
|
||||||
if ($this->_isPaused === true) {
|
|
||||||
Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
|
|
||||||
$this->_isPaused = false;
|
|
||||||
$this->baseRead($this->_socket, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base read handler.
|
|
||||||
*
|
|
||||||
* @param resource $socket
|
|
||||||
* @param bool $check_eof
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function baseRead($socket, $check_eof = true)
|
|
||||||
{
|
|
||||||
// SSL handshake.
|
|
||||||
if ($this->transport === 'ssl' && $this->_sslHandshakeCompleted !== true) {
|
|
||||||
if ($this->doSslHandshake($socket)) {
|
|
||||||
$this->_sslHandshakeCompleted = true;
|
|
||||||
if ($this->_sendBuffer) {
|
|
||||||
Worker::$globalEvent->add($socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$buffer = '';
|
|
||||||
try {
|
|
||||||
$buffer = @\fread($socket, self::READ_BUFFER_SIZE);
|
|
||||||
} catch (\Exception $e) {} catch (\Error $e) {}
|
|
||||||
|
|
||||||
// Check connection closed.
|
|
||||||
if ($buffer === '' || $buffer === false) {
|
|
||||||
if ($check_eof && (\feof($socket) || !\is_resource($socket) || $buffer === false)) {
|
|
||||||
$this->destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->bytesRead += \strlen($buffer);
|
|
||||||
$this->_recvBuffer .= $buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the application layer protocol has been set up.
|
|
||||||
if ($this->protocol !== null) {
|
|
||||||
$parser = $this->protocol;
|
|
||||||
while ($this->_recvBuffer !== '' && !$this->_isPaused) {
|
|
||||||
// The current packet length is known.
|
|
||||||
if ($this->_currentPackageLength) {
|
|
||||||
// Data is not enough for a package.
|
|
||||||
if ($this->_currentPackageLength > \strlen($this->_recvBuffer)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Get current package length.
|
|
||||||
try {
|
|
||||||
$this->_currentPackageLength = $parser::input($this->_recvBuffer, $this);
|
|
||||||
} catch (\Exception $e) {} catch (\Error $e) {}
|
|
||||||
// The packet length is unknown.
|
|
||||||
if ($this->_currentPackageLength === 0) {
|
|
||||||
break;
|
|
||||||
} elseif ($this->_currentPackageLength > 0 && $this->_currentPackageLength <= $this->maxPackageSize) {
|
|
||||||
// Data is not enough for a package.
|
|
||||||
if ($this->_currentPackageLength > \strlen($this->_recvBuffer)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} // Wrong package.
|
|
||||||
else {
|
|
||||||
Worker::safeEcho('Error package. package_length=' . \var_export($this->_currentPackageLength, true));
|
|
||||||
$this->destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The data is enough for a packet.
|
|
||||||
++self::$statistics['total_request'];
|
|
||||||
// The current packet length is equal to the length of the buffer.
|
|
||||||
if (\strlen($this->_recvBuffer) === $this->_currentPackageLength) {
|
|
||||||
$one_request_buffer = $this->_recvBuffer;
|
|
||||||
$this->_recvBuffer = '';
|
|
||||||
} else {
|
|
||||||
// Get a full package from the buffer.
|
|
||||||
$one_request_buffer = \substr($this->_recvBuffer, 0, $this->_currentPackageLength);
|
|
||||||
// Remove the current package from the receive buffer.
|
|
||||||
$this->_recvBuffer = \substr($this->_recvBuffer, $this->_currentPackageLength);
|
|
||||||
}
|
|
||||||
// Reset the current packet length to 0.
|
|
||||||
$this->_currentPackageLength = 0;
|
|
||||||
if (!$this->onMessage) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Decode request buffer before Emitting onMessage callback.
|
|
||||||
\call_user_func($this->onMessage, $this, $parser::decode($one_request_buffer, $this));
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->_recvBuffer === '' || $this->_isPaused) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Applications protocol is not set.
|
|
||||||
++self::$statistics['total_request'];
|
|
||||||
if (!$this->onMessage) {
|
|
||||||
$this->_recvBuffer = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onMessage, $this, $this->_recvBuffer);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
// Clean receive buffer.
|
|
||||||
$this->_recvBuffer = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base write handler.
|
|
||||||
*
|
|
||||||
* @return void|bool
|
|
||||||
*/
|
|
||||||
public function baseWrite()
|
|
||||||
{
|
|
||||||
\set_error_handler(function(){});
|
|
||||||
if ($this->transport === 'ssl') {
|
|
||||||
$len = @\fwrite($this->_socket, $this->_sendBuffer, 8192);
|
|
||||||
} else {
|
|
||||||
$len = @\fwrite($this->_socket, $this->_sendBuffer);
|
|
||||||
}
|
|
||||||
\restore_error_handler();
|
|
||||||
if ($len === \strlen($this->_sendBuffer)) {
|
|
||||||
$this->bytesWritten += $len;
|
|
||||||
Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
|
|
||||||
$this->_sendBuffer = '';
|
|
||||||
// Try to emit onBufferDrain callback when the send buffer becomes empty.
|
|
||||||
if ($this->onBufferDrain) {
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onBufferDrain, $this);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->_status === self::STATUS_CLOSING) {
|
|
||||||
$this->destroy();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ($len > 0) {
|
|
||||||
$this->bytesWritten += $len;
|
|
||||||
$this->_sendBuffer = \substr($this->_sendBuffer, $len);
|
|
||||||
} else {
|
|
||||||
++self::$statistics['send_fail'];
|
|
||||||
$this->destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SSL handshake.
|
|
||||||
*
|
|
||||||
* @param resource $socket
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function doSslHandshake($socket){
|
|
||||||
if (\feof($socket)) {
|
|
||||||
$this->destroy();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$async = $this instanceof AsyncTcpConnection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We disabled ssl3 because https://blog.qualys.com/ssllabs/2014/10/15/ssl-3-is-dead-killed-by-the-poodle-attack.
|
|
||||||
* You can enable ssl3 by the codes below.
|
|
||||||
*/
|
|
||||||
/*if($async){
|
|
||||||
$type = STREAM_CRYPTO_METHOD_SSLv2_CLIENT | STREAM_CRYPTO_METHOD_SSLv23_CLIENT | STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
|
|
||||||
}else{
|
|
||||||
$type = STREAM_CRYPTO_METHOD_SSLv2_SERVER | STREAM_CRYPTO_METHOD_SSLv23_SERVER | STREAM_CRYPTO_METHOD_SSLv3_SERVER;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if($async){
|
|
||||||
$type = \STREAM_CRYPTO_METHOD_SSLv2_CLIENT | \STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
|
|
||||||
}else{
|
|
||||||
$type = \STREAM_CRYPTO_METHOD_SSLv2_SERVER | \STREAM_CRYPTO_METHOD_SSLv23_SERVER;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hidden error.
|
|
||||||
\set_error_handler(function($errno, $errstr, $file){
|
|
||||||
if (!Worker::$daemonize) {
|
|
||||||
Worker::safeEcho("SSL handshake error: $errstr \n");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$ret = \stream_socket_enable_crypto($socket, true, $type);
|
|
||||||
\restore_error_handler();
|
|
||||||
// Negotiation has failed.
|
|
||||||
if (false === $ret) {
|
|
||||||
$this->destroy();
|
|
||||||
return false;
|
|
||||||
} elseif (0 === $ret) {
|
|
||||||
// There isn't enough data and should try again.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (isset($this->onSslHandshake)) {
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onSslHandshake, $this);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method pulls all the data out of a readable stream, and writes it to the supplied destination.
|
|
||||||
*
|
|
||||||
* @param self $dest
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function pipe(self $dest)
|
|
||||||
{
|
|
||||||
$source = $this;
|
|
||||||
$this->onMessage = function ($source, $data) use ($dest) {
|
|
||||||
$dest->send($data);
|
|
||||||
};
|
|
||||||
$this->onClose = function ($source) use ($dest) {
|
|
||||||
$dest->close();
|
|
||||||
};
|
|
||||||
$dest->onBufferFull = function ($dest) use ($source) {
|
|
||||||
$source->pauseRecv();
|
|
||||||
};
|
|
||||||
$dest->onBufferDrain = function ($dest) use ($source) {
|
|
||||||
$source->resumeRecv();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove $length of data from receive buffer.
|
|
||||||
*
|
|
||||||
* @param int $length
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function consumeRecvBuffer($length)
|
|
||||||
{
|
|
||||||
$this->_recvBuffer = \substr($this->_recvBuffer, $length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close connection.
|
|
||||||
*
|
|
||||||
* @param mixed $data
|
|
||||||
* @param bool $raw
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function close($data = null, $raw = false)
|
|
||||||
{
|
|
||||||
if($this->_status === self::STATUS_CONNECTING){
|
|
||||||
$this->destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->_status === self::STATUS_CLOSING || $this->_status === self::STATUS_CLOSED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($data !== null) {
|
|
||||||
$this->send($data, $raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_status = self::STATUS_CLOSING;
|
|
||||||
|
|
||||||
if ($this->_sendBuffer === '') {
|
|
||||||
$this->destroy();
|
|
||||||
} else {
|
|
||||||
$this->pauseRecv();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the real socket.
|
|
||||||
*
|
|
||||||
* @return resource
|
|
||||||
*/
|
|
||||||
public function getSocket()
|
|
||||||
{
|
|
||||||
return $this->_socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the send buffer will be full.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function checkBufferWillFull()
|
|
||||||
{
|
|
||||||
if ($this->maxSendBufferSize <= \strlen($this->_sendBuffer)) {
|
|
||||||
if ($this->onBufferFull) {
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onBufferFull, $this);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether send buffer is full.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function bufferIsFull()
|
|
||||||
{
|
|
||||||
// Buffer has been marked as full but still has data to send then the packet is discarded.
|
|
||||||
if ($this->maxSendBufferSize <= \strlen($this->_sendBuffer)) {
|
|
||||||
if ($this->onError) {
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onError, $this, \WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether send buffer is Empty.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function bufferIsEmpty()
|
|
||||||
{
|
|
||||||
return empty($this->_sendBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy connection.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function destroy()
|
|
||||||
{
|
|
||||||
// Avoid repeated calls.
|
|
||||||
if ($this->_status === self::STATUS_CLOSED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Remove event listener.
|
|
||||||
Worker::$globalEvent->del($this->_socket, EventInterface::EV_READ);
|
|
||||||
Worker::$globalEvent->del($this->_socket, EventInterface::EV_WRITE);
|
|
||||||
|
|
||||||
// Close socket.
|
|
||||||
try {
|
|
||||||
@\fclose($this->_socket);
|
|
||||||
} catch (\Exception $e) {} catch (\Error $e) {}
|
|
||||||
|
|
||||||
$this->_status = self::STATUS_CLOSED;
|
|
||||||
// Try to emit onClose callback.
|
|
||||||
if ($this->onClose) {
|
|
||||||
try {
|
|
||||||
\call_user_func($this->onClose, $this);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Try to emit protocol::onClose
|
|
||||||
if ($this->protocol && \method_exists($this->protocol, 'onClose')) {
|
|
||||||
try {
|
|
||||||
\call_user_func(array($this->protocol, 'onClose'), $this);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->_sendBuffer = $this->_recvBuffer = '';
|
|
||||||
$this->_currentPackageLength = 0;
|
|
||||||
$this->_isPaused = $this->_sslHandshakeCompleted = false;
|
|
||||||
if ($this->_status === self::STATUS_CLOSED) {
|
|
||||||
// Cleaning up the callback to avoid memory leaks.
|
|
||||||
$this->onMessage = $this->onClose = $this->onError = $this->onBufferFull = $this->onBufferDrain = null;
|
|
||||||
// Remove from worker->connections.
|
|
||||||
if ($this->worker) {
|
|
||||||
unset($this->worker->connections[$this->_id]);
|
|
||||||
}
|
|
||||||
unset(static::$connections[$this->_id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destruct.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __destruct()
|
|
||||||
{
|
|
||||||
static $mod;
|
|
||||||
self::$statistics['connection_count']--;
|
|
||||||
if (Worker::getGracefulStop()) {
|
|
||||||
if (!isset($mod)) {
|
|
||||||
$mod = \ceil((self::$statistics['connection_count'] + 1) / 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 === self::$statistics['connection_count'] % $mod) {
|
|
||||||
Worker::log('worker[' . \posix_getpid() . '] remains ' . self::$statistics['connection_count'] . ' connection(s)');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 === self::$statistics['connection_count']) {
|
|
||||||
Worker::stopAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,208 +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 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 Workerman\Connection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UdpConnection.
|
|
||||||
*/
|
|
||||||
class UdpConnection extends ConnectionInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Application layer protocol.
|
|
||||||
* The format is like this Workerman\\Protocols\\Http.
|
|
||||||
*
|
|
||||||
* @var \Workerman\Protocols\ProtocolInterface
|
|
||||||
*/
|
|
||||||
public $protocol = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transport layer protocol.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $transport = 'udp';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Udp socket.
|
|
||||||
*
|
|
||||||
* @var resource
|
|
||||||
*/
|
|
||||||
protected $_socket = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote address.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_remoteAddress = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct.
|
|
||||||
*
|
|
||||||
* @param resource $socket
|
|
||||||
* @param string $remote_address
|
|
||||||
*/
|
|
||||||
public function __construct($socket, $remote_address)
|
|
||||||
{
|
|
||||||
$this->_socket = $socket;
|
|
||||||
$this->_remoteAddress = $remote_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends data on the connection.
|
|
||||||
*
|
|
||||||
* @param string $send_buffer
|
|
||||||
* @param bool $raw
|
|
||||||
* @return void|boolean
|
|
||||||
*/
|
|
||||||
public function send($send_buffer, $raw = false)
|
|
||||||
{
|
|
||||||
if (false === $raw && $this->protocol) {
|
|
||||||
$parser = $this->protocol;
|
|
||||||
$send_buffer = $parser::encode($send_buffer, $this);
|
|
||||||
if ($send_buffer === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return \strlen($send_buffer) === \stream_socket_sendto($this->_socket, $send_buffer, 0, $this->isIpV6() ? '[' . $this->getRemoteIp() . ']:' . $this->getRemotePort() : $this->_remoteAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get remote IP.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getRemoteIp()
|
|
||||||
{
|
|
||||||
$pos = \strrpos($this->_remoteAddress, ':');
|
|
||||||
if ($pos) {
|
|
||||||
return \trim(\substr($this->_remoteAddress, 0, $pos), '[]');
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get remote port.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getRemotePort()
|
|
||||||
{
|
|
||||||
if ($this->_remoteAddress) {
|
|
||||||
return (int)\substr(\strrchr($this->_remoteAddress, ':'), 1);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get remote address.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getRemoteAddress()
|
|
||||||
{
|
|
||||||
return $this->_remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get local IP.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getLocalIp()
|
|
||||||
{
|
|
||||||
$address = $this->getLocalAddress();
|
|
||||||
$pos = \strrpos($address, ':');
|
|
||||||
if (!$pos) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return \substr($address, 0, $pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get local port.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getLocalPort()
|
|
||||||
{
|
|
||||||
$address = $this->getLocalAddress();
|
|
||||||
$pos = \strrpos($address, ':');
|
|
||||||
if (!$pos) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return (int)\substr(\strrchr($address, ':'), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get local address.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getLocalAddress()
|
|
||||||
{
|
|
||||||
return (string)@\stream_socket_get_name($this->_socket, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is ipv4.
|
|
||||||
*
|
|
||||||
* @return bool.
|
|
||||||
*/
|
|
||||||
public function isIpV4()
|
|
||||||
{
|
|
||||||
if ($this->transport === 'unix') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return \strpos($this->getRemoteIp(), ':') === false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is ipv6.
|
|
||||||
*
|
|
||||||
* @return bool.
|
|
||||||
*/
|
|
||||||
public function isIpV6()
|
|
||||||
{
|
|
||||||
if ($this->transport === 'unix') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return \strpos($this->getRemoteIp(), ':') !== false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close connection.
|
|
||||||
*
|
|
||||||
* @param mixed $data
|
|
||||||
* @param bool $raw
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function close($data = null, $raw = false)
|
|
||||||
{
|
|
||||||
if ($data !== null) {
|
|
||||||
$this->send($data, $raw);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the real socket.
|
|
||||||
*
|
|
||||||
* @return resource
|
|
||||||
*/
|
|
||||||
public function getSocket()
|
|
||||||
{
|
|
||||||
return $this->_socket;
|
|
||||||
}
|
|
||||||
}
|
|
189
vendor/workerman/workerman/Events/Ev.php
vendored
189
vendor/workerman/workerman/Events/Ev.php
vendored
@ -1,189 +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 有个鬼<42765633@qq.com>
|
|
||||||
* @link http://www.workerman.net/
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
|
||||||
*/
|
|
||||||
namespace Workerman\Events;
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
use \EvWatcher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ev eventloop
|
|
||||||
*/
|
|
||||||
class Ev implements EventInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* All listeners for read/write event.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_allEvents = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event listeners of signal.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_eventSignal = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All timer event listeners.
|
|
||||||
* [func, args, event, flag, time_interval]
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_eventTimer = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timer id.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected static $_timerId = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a timer.
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function add($fd, $flag, $func, $args = null)
|
|
||||||
{
|
|
||||||
$callback = function ($event, $socket) use ($fd, $func) {
|
|
||||||
try {
|
|
||||||
\call_user_func($func, $fd);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
switch ($flag) {
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
$event = new \EvSignal($fd, $callback);
|
|
||||||
$this->_eventSignal[$fd] = $event;
|
|
||||||
return true;
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE:
|
|
||||||
$repeat = $flag === self::EV_TIMER_ONCE ? 0 : $fd;
|
|
||||||
$param = array($func, (array)$args, $flag, $fd, self::$_timerId);
|
|
||||||
$event = new \EvTimer($fd, $repeat, array($this, 'timerCallback'), $param);
|
|
||||||
$this->_eventTimer[self::$_timerId] = $event;
|
|
||||||
return self::$_timerId++;
|
|
||||||
default :
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
$real_flag = $flag === self::EV_READ ? \Ev::READ : \Ev::WRITE;
|
|
||||||
$event = new \EvIo($fd, $real_flag, $callback);
|
|
||||||
$this->_allEvents[$fd_key][$flag] = $event;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a timer.
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function del($fd, $flag)
|
|
||||||
{
|
|
||||||
switch ($flag) {
|
|
||||||
case self::EV_READ:
|
|
||||||
case self::EV_WRITE:
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
if (isset($this->_allEvents[$fd_key][$flag])) {
|
|
||||||
$this->_allEvents[$fd_key][$flag]->stop();
|
|
||||||
unset($this->_allEvents[$fd_key][$flag]);
|
|
||||||
}
|
|
||||||
if (empty($this->_allEvents[$fd_key])) {
|
|
||||||
unset($this->_allEvents[$fd_key]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
if (isset($this->_eventSignal[$fd_key])) {
|
|
||||||
$this->_eventSignal[$fd_key]->stop();
|
|
||||||
unset($this->_eventSignal[$fd_key]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE:
|
|
||||||
if (isset($this->_eventTimer[$fd])) {
|
|
||||||
$this->_eventTimer[$fd]->stop();
|
|
||||||
unset($this->_eventTimer[$fd]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timer callback.
|
|
||||||
*
|
|
||||||
* @param EvWatcher $event
|
|
||||||
*/
|
|
||||||
public function timerCallback(EvWatcher $event)
|
|
||||||
{
|
|
||||||
$param = $event->data;
|
|
||||||
$timer_id = $param[4];
|
|
||||||
if ($param[2] === self::EV_TIMER_ONCE) {
|
|
||||||
$this->_eventTimer[$timer_id]->stop();
|
|
||||||
unset($this->_eventTimer[$timer_id]);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
\call_user_func_array($param[0], $param[1]);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all timers.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function clearAllTimer()
|
|
||||||
{
|
|
||||||
foreach ($this->_eventTimer as $event) {
|
|
||||||
$event->stop();
|
|
||||||
}
|
|
||||||
$this->_eventTimer = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main loop.
|
|
||||||
*
|
|
||||||
* @see EventInterface::loop()
|
|
||||||
*/
|
|
||||||
public function loop()
|
|
||||||
{
|
|
||||||
\Ev::run();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy loop.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function destroy()
|
|
||||||
{
|
|
||||||
\Ev::stop(\Ev::BREAK_ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get timer count.
|
|
||||||
*
|
|
||||||
* @return integer
|
|
||||||
*/
|
|
||||||
public function getTimerCount()
|
|
||||||
{
|
|
||||||
return \count($this->_eventTimer);
|
|
||||||
}
|
|
||||||
}
|
|
215
vendor/workerman/workerman/Events/Event.php
vendored
215
vendor/workerman/workerman/Events/Event.php
vendored
@ -1,215 +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 有个鬼<42765633@qq.com>
|
|
||||||
* @copyright 有个鬼<42765633@qq.com>
|
|
||||||
* @link http://www.workerman.net/
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
|
||||||
*/
|
|
||||||
namespace Workerman\Events;
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* libevent eventloop
|
|
||||||
*/
|
|
||||||
class Event implements EventInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Event base.
|
|
||||||
* @var object
|
|
||||||
*/
|
|
||||||
protected $_eventBase = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All listeners for read/write event.
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_allEvents = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event listeners of signal.
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_eventSignal = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All timer event listeners.
|
|
||||||
* [func, args, event, flag, time_interval]
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_eventTimer = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timer id.
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected static $_timerId = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* construct
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
if (\class_exists('\\\\EventBase', false)) {
|
|
||||||
$class_name = '\\\\EventBase';
|
|
||||||
} else {
|
|
||||||
$class_name = '\EventBase';
|
|
||||||
}
|
|
||||||
$this->_eventBase = new $class_name();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see EventInterface::add()
|
|
||||||
*/
|
|
||||||
public function add($fd, $flag, $func, $args=array())
|
|
||||||
{
|
|
||||||
if (\class_exists('\\\\Event', false)) {
|
|
||||||
$class_name = '\\\\Event';
|
|
||||||
} else {
|
|
||||||
$class_name = '\Event';
|
|
||||||
}
|
|
||||||
switch ($flag) {
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
$event = $class_name::signal($this->_eventBase, $fd, $func);
|
|
||||||
if (!$event||!$event->add()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->_eventSignal[$fd_key] = $event;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE:
|
|
||||||
|
|
||||||
$param = array($func, (array)$args, $flag, $fd, self::$_timerId);
|
|
||||||
$event = new $class_name($this->_eventBase, -1, $class_name::TIMEOUT|$class_name::PERSIST, array($this, "timerCallback"), $param);
|
|
||||||
if (!$event||!$event->addTimer($fd)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->_eventTimer[self::$_timerId] = $event;
|
|
||||||
return self::$_timerId++;
|
|
||||||
|
|
||||||
default :
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
$real_flag = $flag === self::EV_READ ? $class_name::READ | $class_name::PERSIST : $class_name::WRITE | $class_name::PERSIST;
|
|
||||||
$event = new $class_name($this->_eventBase, $fd, $real_flag, $func, $fd);
|
|
||||||
if (!$event||!$event->add()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->_allEvents[$fd_key][$flag] = $event;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see Events\EventInterface::del()
|
|
||||||
*/
|
|
||||||
public function del($fd, $flag)
|
|
||||||
{
|
|
||||||
switch ($flag) {
|
|
||||||
|
|
||||||
case self::EV_READ:
|
|
||||||
case self::EV_WRITE:
|
|
||||||
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
if (isset($this->_allEvents[$fd_key][$flag])) {
|
|
||||||
$this->_allEvents[$fd_key][$flag]->del();
|
|
||||||
unset($this->_allEvents[$fd_key][$flag]);
|
|
||||||
}
|
|
||||||
if (empty($this->_allEvents[$fd_key])) {
|
|
||||||
unset($this->_allEvents[$fd_key]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
if (isset($this->_eventSignal[$fd_key])) {
|
|
||||||
$this->_eventSignal[$fd_key]->del();
|
|
||||||
unset($this->_eventSignal[$fd_key]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE:
|
|
||||||
if (isset($this->_eventTimer[$fd])) {
|
|
||||||
$this->_eventTimer[$fd]->del();
|
|
||||||
unset($this->_eventTimer[$fd]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timer callback.
|
|
||||||
* @param int|null $fd
|
|
||||||
* @param int $what
|
|
||||||
* @param int $timer_id
|
|
||||||
*/
|
|
||||||
public function timerCallback($fd, $what, $param)
|
|
||||||
{
|
|
||||||
$timer_id = $param[4];
|
|
||||||
|
|
||||||
if ($param[2] === self::EV_TIMER_ONCE) {
|
|
||||||
$this->_eventTimer[$timer_id]->del();
|
|
||||||
unset($this->_eventTimer[$timer_id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
\call_user_func_array($param[0], $param[1]);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see Events\EventInterface::clearAllTimer()
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function clearAllTimer()
|
|
||||||
{
|
|
||||||
foreach ($this->_eventTimer as $event) {
|
|
||||||
$event->del();
|
|
||||||
}
|
|
||||||
$this->_eventTimer = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see EventInterface::loop()
|
|
||||||
*/
|
|
||||||
public function loop()
|
|
||||||
{
|
|
||||||
$this->_eventBase->loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy loop.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function destroy()
|
|
||||||
{
|
|
||||||
$this->_eventBase->exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get timer count.
|
|
||||||
*
|
|
||||||
* @return integer
|
|
||||||
*/
|
|
||||||
public function getTimerCount()
|
|
||||||
{
|
|
||||||
return \count($this->_eventTimer);
|
|
||||||
}
|
|
||||||
}
|
|
107
vendor/workerman/workerman/Events/EventInterface.php
vendored
107
vendor/workerman/workerman/Events/EventInterface.php
vendored
@ -1,107 +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 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 Workerman\Events;
|
|
||||||
|
|
||||||
interface EventInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Read event.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const EV_READ = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write event.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const EV_WRITE = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Except event
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const EV_EXCEPT = 3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signal event.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const EV_SIGNAL = 4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timer event.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const EV_TIMER = 8;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timer once event.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
const EV_TIMER_ONCE = 16;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add event listener to event loop.
|
|
||||||
*
|
|
||||||
* @param mixed $fd
|
|
||||||
* @param int $flag
|
|
||||||
* @param callable $func
|
|
||||||
* @param array $args
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function add($fd, $flag, $func, $args = array());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove event listener from event loop.
|
|
||||||
*
|
|
||||||
* @param mixed $fd
|
|
||||||
* @param int $flag
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function del($fd, $flag);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all timers.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function clearAllTimer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main loop.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function loop();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy loop.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function destroy();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Timer count.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getTimerCount();
|
|
||||||
}
|
|
225
vendor/workerman/workerman/Events/Libevent.php
vendored
225
vendor/workerman/workerman/Events/Libevent.php
vendored
@ -1,225 +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 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 Workerman\Events;
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* libevent eventloop
|
|
||||||
*/
|
|
||||||
class Libevent implements EventInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Event base.
|
|
||||||
*
|
|
||||||
* @var resource
|
|
||||||
*/
|
|
||||||
protected $_eventBase = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All listeners for read/write event.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_allEvents = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event listeners of signal.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_eventSignal = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All timer event listeners.
|
|
||||||
* [func, args, event, flag, time_interval]
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_eventTimer = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* construct
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->_eventBase = \event_base_new();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function add($fd, $flag, $func, $args = array())
|
|
||||||
{
|
|
||||||
switch ($flag) {
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
$real_flag = \EV_SIGNAL | \EV_PERSIST;
|
|
||||||
$this->_eventSignal[$fd_key] = \event_new();
|
|
||||||
if (!\event_set($this->_eventSignal[$fd_key], $fd, $real_flag, $func, null)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!\event_base_set($this->_eventSignal[$fd_key], $this->_eventBase)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!\event_add($this->_eventSignal[$fd_key])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE:
|
|
||||||
$event = \event_new();
|
|
||||||
$timer_id = (int)$event;
|
|
||||||
if (!\event_set($event, 0, \EV_TIMEOUT, array($this, 'timerCallback'), $timer_id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!\event_base_set($event, $this->_eventBase)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$time_interval = $fd * 1000000;
|
|
||||||
if (!\event_add($event, $time_interval)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->_eventTimer[$timer_id] = array($func, (array)$args, $event, $flag, $time_interval);
|
|
||||||
return $timer_id;
|
|
||||||
|
|
||||||
default :
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
$real_flag = $flag === self::EV_READ ? \EV_READ | \EV_PERSIST : \EV_WRITE | \EV_PERSIST;
|
|
||||||
|
|
||||||
$event = \event_new();
|
|
||||||
|
|
||||||
if (!\event_set($event, $fd, $real_flag, $func, null)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!\event_base_set($event, $this->_eventBase)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!\event_add($event)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_allEvents[$fd_key][$flag] = $event;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function del($fd, $flag)
|
|
||||||
{
|
|
||||||
switch ($flag) {
|
|
||||||
case self::EV_READ:
|
|
||||||
case self::EV_WRITE:
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
if (isset($this->_allEvents[$fd_key][$flag])) {
|
|
||||||
\event_del($this->_allEvents[$fd_key][$flag]);
|
|
||||||
unset($this->_allEvents[$fd_key][$flag]);
|
|
||||||
}
|
|
||||||
if (empty($this->_allEvents[$fd_key])) {
|
|
||||||
unset($this->_allEvents[$fd_key]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
if (isset($this->_eventSignal[$fd_key])) {
|
|
||||||
\event_del($this->_eventSignal[$fd_key]);
|
|
||||||
unset($this->_eventSignal[$fd_key]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE:
|
|
||||||
// 这里 fd 为timerid
|
|
||||||
if (isset($this->_eventTimer[$fd])) {
|
|
||||||
\event_del($this->_eventTimer[$fd][2]);
|
|
||||||
unset($this->_eventTimer[$fd]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timer callback.
|
|
||||||
*
|
|
||||||
* @param mixed $_null1
|
|
||||||
* @param int $_null2
|
|
||||||
* @param mixed $timer_id
|
|
||||||
*/
|
|
||||||
protected function timerCallback($_null1, $_null2, $timer_id)
|
|
||||||
{
|
|
||||||
if ($this->_eventTimer[$timer_id][3] === self::EV_TIMER) {
|
|
||||||
\event_add($this->_eventTimer[$timer_id][2], $this->_eventTimer[$timer_id][4]);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
\call_user_func_array($this->_eventTimer[$timer_id][0], $this->_eventTimer[$timer_id][1]);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
if (isset($this->_eventTimer[$timer_id]) && $this->_eventTimer[$timer_id][3] === self::EV_TIMER_ONCE) {
|
|
||||||
$this->del($timer_id, self::EV_TIMER_ONCE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function clearAllTimer()
|
|
||||||
{
|
|
||||||
foreach ($this->_eventTimer as $task_data) {
|
|
||||||
\event_del($task_data[2]);
|
|
||||||
}
|
|
||||||
$this->_eventTimer = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function loop()
|
|
||||||
{
|
|
||||||
\event_base_loop($this->_eventBase);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy loop.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function destroy()
|
|
||||||
{
|
|
||||||
foreach ($this->_eventSignal as $event) {
|
|
||||||
\event_del($event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get timer count.
|
|
||||||
*
|
|
||||||
* @return integer
|
|
||||||
*/
|
|
||||||
public function getTimerCount()
|
|
||||||
{
|
|
||||||
return \count($this->_eventTimer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
264
vendor/workerman/workerman/Events/React/Base.php
vendored
264
vendor/workerman/workerman/Events/React/Base.php
vendored
@ -1,264 +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 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 Workerman\Events\React;
|
|
||||||
|
|
||||||
use Workerman\Events\EventInterface;
|
|
||||||
use React\EventLoop\TimerInterface;
|
|
||||||
use React\EventLoop\LoopInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class StreamSelectLoop
|
|
||||||
* @package Workerman\Events\React
|
|
||||||
*/
|
|
||||||
class Base implements LoopInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_timerIdMap = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_timerIdIndex = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_signalHandlerMap = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var LoopInterface
|
|
||||||
*/
|
|
||||||
protected $_eventLoop = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base constructor.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->_eventLoop = new \React\EventLoop\StreamSelectLoop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add event listener to event loop.
|
|
||||||
*
|
|
||||||
* @param int $fd
|
|
||||||
* @param int $flag
|
|
||||||
* @param callable $func
|
|
||||||
* @param array $args
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function add($fd, $flag, $func, array $args = array())
|
|
||||||
{
|
|
||||||
$args = (array)$args;
|
|
||||||
switch ($flag) {
|
|
||||||
case EventInterface::EV_READ:
|
|
||||||
return $this->addReadStream($fd, $func);
|
|
||||||
case EventInterface::EV_WRITE:
|
|
||||||
return $this->addWriteStream($fd, $func);
|
|
||||||
case EventInterface::EV_SIGNAL:
|
|
||||||
if (isset($this->_signalHandlerMap[$fd])) {
|
|
||||||
$this->removeSignal($fd, $this->_signalHandlerMap[$fd]);
|
|
||||||
}
|
|
||||||
$this->_signalHandlerMap[$fd] = $func;
|
|
||||||
return $this->addSignal($fd, $func);
|
|
||||||
case EventInterface::EV_TIMER:
|
|
||||||
$timer_obj = $this->addPeriodicTimer($fd, function() use ($func, $args) {
|
|
||||||
\call_user_func_array($func, $args);
|
|
||||||
});
|
|
||||||
$this->_timerIdMap[++$this->_timerIdIndex] = $timer_obj;
|
|
||||||
return $this->_timerIdIndex;
|
|
||||||
case EventInterface::EV_TIMER_ONCE:
|
|
||||||
$index = ++$this->_timerIdIndex;
|
|
||||||
$timer_obj = $this->addTimer($fd, function() use ($func, $args, $index) {
|
|
||||||
$this->del($index,EventInterface::EV_TIMER_ONCE);
|
|
||||||
\call_user_func_array($func, $args);
|
|
||||||
});
|
|
||||||
$this->_timerIdMap[$index] = $timer_obj;
|
|
||||||
return $this->_timerIdIndex;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove event listener from event loop.
|
|
||||||
*
|
|
||||||
* @param mixed $fd
|
|
||||||
* @param int $flag
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function del($fd, $flag)
|
|
||||||
{
|
|
||||||
switch ($flag) {
|
|
||||||
case EventInterface::EV_READ:
|
|
||||||
return $this->removeReadStream($fd);
|
|
||||||
case EventInterface::EV_WRITE:
|
|
||||||
return $this->removeWriteStream($fd);
|
|
||||||
case EventInterface::EV_SIGNAL:
|
|
||||||
if (!isset($this->_eventLoop[$fd])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$func = $this->_eventLoop[$fd];
|
|
||||||
unset($this->_eventLoop[$fd]);
|
|
||||||
return $this->removeSignal($fd, $func);
|
|
||||||
|
|
||||||
case EventInterface::EV_TIMER:
|
|
||||||
case EventInterface::EV_TIMER_ONCE:
|
|
||||||
if (isset($this->_timerIdMap[$fd])){
|
|
||||||
$timer_obj = $this->_timerIdMap[$fd];
|
|
||||||
unset($this->_timerIdMap[$fd]);
|
|
||||||
$this->cancelTimer($timer_obj);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main loop.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function loop()
|
|
||||||
{
|
|
||||||
$this->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy loop.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function destroy()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get timer count.
|
|
||||||
*
|
|
||||||
* @return integer
|
|
||||||
*/
|
|
||||||
public function getTimerCount()
|
|
||||||
{
|
|
||||||
return \count($this->_timerIdMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param resource $stream
|
|
||||||
* @param callable $listener
|
|
||||||
*/
|
|
||||||
public function addReadStream($stream, $listener)
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->addReadStream($stream, $listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param resource $stream
|
|
||||||
* @param callable $listener
|
|
||||||
*/
|
|
||||||
public function addWriteStream($stream, $listener)
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->addWriteStream($stream, $listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param resource $stream
|
|
||||||
*/
|
|
||||||
public function removeReadStream($stream)
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->removeReadStream($stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param resource $stream
|
|
||||||
*/
|
|
||||||
public function removeWriteStream($stream)
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->removeWriteStream($stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param float|int $interval
|
|
||||||
* @param callable $callback
|
|
||||||
* @return \React\EventLoop\Timer\Timer|TimerInterface
|
|
||||||
*/
|
|
||||||
public function addTimer($interval, $callback)
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->addTimer($interval, $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param float|int $interval
|
|
||||||
* @param callable $callback
|
|
||||||
* @return \React\EventLoop\Timer\Timer|TimerInterface
|
|
||||||
*/
|
|
||||||
public function addPeriodicTimer($interval, $callback)
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->addPeriodicTimer($interval, $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param TimerInterface $timer
|
|
||||||
*/
|
|
||||||
public function cancelTimer(TimerInterface $timer)
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->cancelTimer($timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param callable $listener
|
|
||||||
*/
|
|
||||||
public function futureTick($listener)
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->futureTick($listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $signal
|
|
||||||
* @param callable $listener
|
|
||||||
*/
|
|
||||||
public function addSignal($signal, $listener)
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->addSignal($signal, $listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $signal
|
|
||||||
* @param callable $listener
|
|
||||||
*/
|
|
||||||
public function removeSignal($signal, $listener)
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->removeSignal($signal, $listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run.
|
|
||||||
*/
|
|
||||||
public function run()
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop.
|
|
||||||
*/
|
|
||||||
public function stop()
|
|
||||||
{
|
|
||||||
return $this->_eventLoop->stop();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +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 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 Workerman\Events\React;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ExtEventLoop
|
|
||||||
* @package Workerman\Events\React
|
|
||||||
*/
|
|
||||||
class ExtEventLoop extends Base
|
|
||||||
{
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->_eventLoop = new \React\EventLoop\ExtEventLoop();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +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 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 Workerman\Events\React;
|
|
||||||
use Workerman\Events\EventInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ExtLibEventLoop
|
|
||||||
* @package Workerman\Events\React
|
|
||||||
*/
|
|
||||||
class ExtLibEventLoop extends Base
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->_eventLoop = new \React\EventLoop\ExtLibeventLoop();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +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 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 Workerman\Events\React;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class StreamSelectLoop
|
|
||||||
* @package Workerman\Events\React
|
|
||||||
*/
|
|
||||||
class StreamSelectLoop extends Base
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->_eventLoop = new \React\EventLoop\StreamSelectLoop();
|
|
||||||
}
|
|
||||||
}
|
|
357
vendor/workerman/workerman/Events/Select.php
vendored
357
vendor/workerman/workerman/Events/Select.php
vendored
@ -1,357 +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 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 Workerman\Events;
|
|
||||||
|
|
||||||
use Throwable;
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* select eventloop
|
|
||||||
*/
|
|
||||||
class Select implements EventInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* All listeners for read/write event.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $_allEvents = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event listeners of signal.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $_signalEvents = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fds waiting for read event.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_readFds = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fds waiting for write event.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_writeFds = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fds waiting for except event.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_exceptFds = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timer scheduler.
|
|
||||||
* {['data':timer_id, 'priority':run_timestamp], ..}
|
|
||||||
*
|
|
||||||
* @var \SplPriorityQueue
|
|
||||||
*/
|
|
||||||
protected $_scheduler = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All timer event listeners.
|
|
||||||
* [[func, args, flag, timer_interval], ..]
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_eventTimer = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timer id.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_timerId = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select timeout.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_selectTimeout = 100000000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Paired socket channels
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $channel = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
// Init SplPriorityQueue.
|
|
||||||
$this->_scheduler = new \SplPriorityQueue();
|
|
||||||
$this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function add($fd, $flag, $func, $args = array())
|
|
||||||
{
|
|
||||||
switch ($flag) {
|
|
||||||
case self::EV_READ:
|
|
||||||
case self::EV_WRITE:
|
|
||||||
$count = $flag === self::EV_READ ? \count($this->_readFds) : \count($this->_writeFds);
|
|
||||||
if ($count >= 1024) {
|
|
||||||
echo "Warning: system call select exceeded the maximum number of connections 1024, please install event/libevent extension for more connections.\n";
|
|
||||||
} else if (\DIRECTORY_SEPARATOR !== '/' && $count >= 256) {
|
|
||||||
echo "Warning: system call select exceeded the maximum number of connections 256.\n";
|
|
||||||
}
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
$this->_allEvents[$fd_key][$flag] = array($func, $fd);
|
|
||||||
if ($flag === self::EV_READ) {
|
|
||||||
$this->_readFds[$fd_key] = $fd;
|
|
||||||
} else {
|
|
||||||
$this->_writeFds[$fd_key] = $fd;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::EV_EXCEPT:
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
$this->_allEvents[$fd_key][$flag] = array($func, $fd);
|
|
||||||
$this->_exceptFds[$fd_key] = $fd;
|
|
||||||
break;
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
// Windows not support signal.
|
|
||||||
if(\DIRECTORY_SEPARATOR !== '/') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
$this->_signalEvents[$fd_key][$flag] = array($func, $fd);
|
|
||||||
\pcntl_signal($fd, array($this, 'signalHandler'));
|
|
||||||
break;
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE:
|
|
||||||
$timer_id = $this->_timerId++;
|
|
||||||
$run_time = \microtime(true) + $fd;
|
|
||||||
$this->_scheduler->insert($timer_id, -$run_time);
|
|
||||||
$this->_eventTimer[$timer_id] = array($func, (array)$args, $flag, $fd);
|
|
||||||
$select_timeout = ($run_time - \microtime(true)) * 1000000;
|
|
||||||
$select_timeout = $select_timeout <= 0 ? 1 : $select_timeout;
|
|
||||||
if( $this->_selectTimeout > $select_timeout ){
|
|
||||||
$this->_selectTimeout = (int) $select_timeout;
|
|
||||||
}
|
|
||||||
return $timer_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signal handler.
|
|
||||||
*
|
|
||||||
* @param int $signal
|
|
||||||
*/
|
|
||||||
public function signalHandler($signal)
|
|
||||||
{
|
|
||||||
\call_user_func_array($this->_signalEvents[$signal][self::EV_SIGNAL][0], array($signal));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function del($fd, $flag)
|
|
||||||
{
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
switch ($flag) {
|
|
||||||
case self::EV_READ:
|
|
||||||
unset($this->_allEvents[$fd_key][$flag], $this->_readFds[$fd_key]);
|
|
||||||
if (empty($this->_allEvents[$fd_key])) {
|
|
||||||
unset($this->_allEvents[$fd_key]);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case self::EV_WRITE:
|
|
||||||
unset($this->_allEvents[$fd_key][$flag], $this->_writeFds[$fd_key]);
|
|
||||||
if (empty($this->_allEvents[$fd_key])) {
|
|
||||||
unset($this->_allEvents[$fd_key]);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case self::EV_EXCEPT:
|
|
||||||
unset($this->_allEvents[$fd_key][$flag], $this->_exceptFds[$fd_key]);
|
|
||||||
if(empty($this->_allEvents[$fd_key]))
|
|
||||||
{
|
|
||||||
unset($this->_allEvents[$fd_key]);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
if(\DIRECTORY_SEPARATOR !== '/') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
unset($this->_signalEvents[$fd_key]);
|
|
||||||
\pcntl_signal($fd, SIG_IGN);
|
|
||||||
break;
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE;
|
|
||||||
unset($this->_eventTimer[$fd_key]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tick for timer.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function tick()
|
|
||||||
{
|
|
||||||
$tasks_to_insert = [];
|
|
||||||
while (!$this->_scheduler->isEmpty()) {
|
|
||||||
$scheduler_data = $this->_scheduler->top();
|
|
||||||
$timer_id = $scheduler_data['data'];
|
|
||||||
$next_run_time = -$scheduler_data['priority'];
|
|
||||||
$time_now = \microtime(true);
|
|
||||||
$this->_selectTimeout = (int) (($next_run_time - $time_now) * 1000000);
|
|
||||||
if ($this->_selectTimeout <= 0) {
|
|
||||||
$this->_scheduler->extract();
|
|
||||||
|
|
||||||
if (!isset($this->_eventTimer[$timer_id])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// [func, args, flag, timer_interval]
|
|
||||||
$task_data = $this->_eventTimer[$timer_id];
|
|
||||||
if ($task_data[2] === self::EV_TIMER) {
|
|
||||||
$next_run_time = $time_now + $task_data[3];
|
|
||||||
$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);
|
|
||||||
}
|
|
||||||
if (isset($this->_eventTimer[$timer_id]) && $task_data[2] === self::EV_TIMER_ONCE) {
|
|
||||||
$this->del($timer_id, self::EV_TIMER_ONCE);
|
|
||||||
}
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function clearAllTimer()
|
|
||||||
{
|
|
||||||
$this->_scheduler = new \SplPriorityQueue();
|
|
||||||
$this->_scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
|
|
||||||
$this->_eventTimer = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function loop()
|
|
||||||
{
|
|
||||||
while (1) {
|
|
||||||
if(\DIRECTORY_SEPARATOR === '/') {
|
|
||||||
// Calls signal handlers for pending signals
|
|
||||||
\pcntl_signal_dispatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
$read = $this->_readFds;
|
|
||||||
$write = $this->_writeFds;
|
|
||||||
$except = $this->_exceptFds;
|
|
||||||
$ret = false;
|
|
||||||
|
|
||||||
if ($read || $write || $except) {
|
|
||||||
// Waiting read/write/signal/timeout events.
|
|
||||||
try {
|
|
||||||
$ret = @stream_select($read, $write, $except, 0, $this->_selectTimeout);
|
|
||||||
} catch (\Exception $e) {} catch (\Error $e) {}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$this->_selectTimeout >= 1 && usleep($this->_selectTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->_scheduler->isEmpty()) {
|
|
||||||
$this->tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$ret) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($read) {
|
|
||||||
foreach ($read as $fd) {
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
if (isset($this->_allEvents[$fd_key][self::EV_READ])) {
|
|
||||||
\call_user_func_array($this->_allEvents[$fd_key][self::EV_READ][0],
|
|
||||||
array($this->_allEvents[$fd_key][self::EV_READ][1]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($write) {
|
|
||||||
foreach ($write as $fd) {
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
if (isset($this->_allEvents[$fd_key][self::EV_WRITE])) {
|
|
||||||
\call_user_func_array($this->_allEvents[$fd_key][self::EV_WRITE][0],
|
|
||||||
array($this->_allEvents[$fd_key][self::EV_WRITE][1]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($except) {
|
|
||||||
foreach($except as $fd) {
|
|
||||||
$fd_key = (int) $fd;
|
|
||||||
if(isset($this->_allEvents[$fd_key][self::EV_EXCEPT])) {
|
|
||||||
\call_user_func_array($this->_allEvents[$fd_key][self::EV_EXCEPT][0],
|
|
||||||
array($this->_allEvents[$fd_key][self::EV_EXCEPT][1]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy loop.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function destroy()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get timer count.
|
|
||||||
*
|
|
||||||
* @return integer
|
|
||||||
*/
|
|
||||||
public function getTimerCount()
|
|
||||||
{
|
|
||||||
return \count($this->_eventTimer);
|
|
||||||
}
|
|
||||||
}
|
|
230
vendor/workerman/workerman/Events/Swoole.php
vendored
230
vendor/workerman/workerman/Events/Swoole.php
vendored
@ -1,230 +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 Ares<aresrr#qq.com>
|
|
||||||
* @link http://www.workerman.net/
|
|
||||||
* @link https://github.com/ares333/Workerman
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
|
||||||
*/
|
|
||||||
namespace Workerman\Events;
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
use Swoole\Event;
|
|
||||||
use Swoole\Timer;
|
|
||||||
|
|
||||||
class Swoole implements EventInterface
|
|
||||||
{
|
|
||||||
|
|
||||||
protected $_timer = array();
|
|
||||||
|
|
||||||
protected $_timerOnceMap = array();
|
|
||||||
|
|
||||||
protected $mapId = 0;
|
|
||||||
|
|
||||||
protected $_fd = array();
|
|
||||||
|
|
||||||
// milisecond
|
|
||||||
public static $signalDispatchInterval = 500;
|
|
||||||
|
|
||||||
protected $_hasSignal = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* {@inheritdoc}
|
|
||||||
*
|
|
||||||
* @see \Workerman\Events\EventInterface::add()
|
|
||||||
*/
|
|
||||||
public function add($fd, $flag, $func, $args = array())
|
|
||||||
{
|
|
||||||
switch ($flag) {
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
$res = \pcntl_signal($fd, $func, false);
|
|
||||||
if (! $this->_hasSignal && $res) {
|
|
||||||
Timer::tick(static::$signalDispatchInterval,
|
|
||||||
function () {
|
|
||||||
\pcntl_signal_dispatch();
|
|
||||||
});
|
|
||||||
$this->_hasSignal = true;
|
|
||||||
}
|
|
||||||
return $res;
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE:
|
|
||||||
$method = self::EV_TIMER === $flag ? 'tick' : 'after';
|
|
||||||
if ($this->mapId > \PHP_INT_MAX) {
|
|
||||||
$this->mapId = 0;
|
|
||||||
}
|
|
||||||
$mapId = $this->mapId++;
|
|
||||||
$t = (int)($fd * 1000);
|
|
||||||
if ($t < 1) {
|
|
||||||
$t = 1;
|
|
||||||
}
|
|
||||||
$timer_id = Timer::$method($t,
|
|
||||||
function ($timer_id = null) use ($func, $args, $mapId) {
|
|
||||||
try {
|
|
||||||
\call_user_func_array($func, (array)$args);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
// EV_TIMER_ONCE
|
|
||||||
if (! isset($timer_id)) {
|
|
||||||
// may be deleted in $func
|
|
||||||
if (\array_key_exists($mapId, $this->_timerOnceMap)) {
|
|
||||||
$timer_id = $this->_timerOnceMap[$mapId];
|
|
||||||
unset($this->_timer[$timer_id],
|
|
||||||
$this->_timerOnceMap[$mapId]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if ($flag === self::EV_TIMER_ONCE) {
|
|
||||||
$this->_timerOnceMap[$mapId] = $timer_id;
|
|
||||||
$this->_timer[$timer_id] = $mapId;
|
|
||||||
} else {
|
|
||||||
$this->_timer[$timer_id] = null;
|
|
||||||
}
|
|
||||||
return $timer_id;
|
|
||||||
case self::EV_READ:
|
|
||||||
case self::EV_WRITE:
|
|
||||||
$fd_key = (int) $fd;
|
|
||||||
if (! isset($this->_fd[$fd_key])) {
|
|
||||||
if ($flag === self::EV_READ) {
|
|
||||||
$res = Event::add($fd, $func, null, SWOOLE_EVENT_READ);
|
|
||||||
$fd_type = SWOOLE_EVENT_READ;
|
|
||||||
} else {
|
|
||||||
$res = Event::add($fd, null, $func, SWOOLE_EVENT_WRITE);
|
|
||||||
$fd_type = SWOOLE_EVENT_WRITE;
|
|
||||||
}
|
|
||||||
if ($res) {
|
|
||||||
$this->_fd[$fd_key] = $fd_type;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$fd_val = $this->_fd[$fd_key];
|
|
||||||
$res = true;
|
|
||||||
if ($flag === self::EV_READ) {
|
|
||||||
if (($fd_val & SWOOLE_EVENT_READ) !== SWOOLE_EVENT_READ) {
|
|
||||||
$res = Event::set($fd, $func, null,
|
|
||||||
SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);
|
|
||||||
$this->_fd[$fd_key] |= SWOOLE_EVENT_READ;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (($fd_val & SWOOLE_EVENT_WRITE) !== SWOOLE_EVENT_WRITE) {
|
|
||||||
$res = Event::set($fd, null, $func,
|
|
||||||
SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);
|
|
||||||
$this->_fd[$fd_key] |= SWOOLE_EVENT_WRITE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* {@inheritdoc}
|
|
||||||
*
|
|
||||||
* @see \Workerman\Events\EventInterface::del()
|
|
||||||
*/
|
|
||||||
public function del($fd, $flag)
|
|
||||||
{
|
|
||||||
switch ($flag) {
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
return \pcntl_signal($fd, SIG_IGN, false);
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE:
|
|
||||||
// already remove in EV_TIMER_ONCE callback.
|
|
||||||
if (! \array_key_exists($fd, $this->_timer)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$res = Timer::clear($fd);
|
|
||||||
if ($res) {
|
|
||||||
$mapId = $this->_timer[$fd];
|
|
||||||
if (isset($mapId)) {
|
|
||||||
unset($this->_timerOnceMap[$mapId]);
|
|
||||||
}
|
|
||||||
unset($this->_timer[$fd]);
|
|
||||||
}
|
|
||||||
return $res;
|
|
||||||
case self::EV_READ:
|
|
||||||
case self::EV_WRITE:
|
|
||||||
$fd_key = (int) $fd;
|
|
||||||
if (isset($this->_fd[$fd_key])) {
|
|
||||||
$fd_val = $this->_fd[$fd_key];
|
|
||||||
if ($flag === self::EV_READ) {
|
|
||||||
$flag_remove = ~ SWOOLE_EVENT_READ;
|
|
||||||
} else {
|
|
||||||
$flag_remove = ~ SWOOLE_EVENT_WRITE;
|
|
||||||
}
|
|
||||||
$fd_val &= $flag_remove;
|
|
||||||
if (0 === $fd_val) {
|
|
||||||
$res = Event::del($fd);
|
|
||||||
if ($res) {
|
|
||||||
unset($this->_fd[$fd_key]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$res = Event::set($fd, null, null, $fd_val);
|
|
||||||
if ($res) {
|
|
||||||
$this->_fd[$fd_key] = $fd_val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$res = true;
|
|
||||||
}
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* {@inheritdoc}
|
|
||||||
*
|
|
||||||
* @see \Workerman\Events\EventInterface::clearAllTimer()
|
|
||||||
*/
|
|
||||||
public function clearAllTimer()
|
|
||||||
{
|
|
||||||
foreach (array_keys($this->_timer) as $v) {
|
|
||||||
Timer::clear($v);
|
|
||||||
}
|
|
||||||
$this->_timer = array();
|
|
||||||
$this->_timerOnceMap = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* {@inheritdoc}
|
|
||||||
*
|
|
||||||
* @see \Workerman\Events\EventInterface::loop()
|
|
||||||
*/
|
|
||||||
public function loop()
|
|
||||||
{
|
|
||||||
Event::wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* {@inheritdoc}
|
|
||||||
*
|
|
||||||
* @see \Workerman\Events\EventInterface::destroy()
|
|
||||||
*/
|
|
||||||
public function destroy()
|
|
||||||
{
|
|
||||||
Event::exit();
|
|
||||||
posix_kill(posix_getpid(), SIGINT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* {@inheritdoc}
|
|
||||||
*
|
|
||||||
* @see \Workerman\Events\EventInterface::getTimerCount()
|
|
||||||
*/
|
|
||||||
public function getTimerCount()
|
|
||||||
{
|
|
||||||
return \count($this->_timer);
|
|
||||||
}
|
|
||||||
}
|
|
260
vendor/workerman/workerman/Events/Uv.php
vendored
260
vendor/workerman/workerman/Events/Uv.php
vendored
@ -1,260 +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\Events;
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* libuv eventloop
|
|
||||||
*/
|
|
||||||
class Uv implements EventInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Event Loop.
|
|
||||||
* @var object
|
|
||||||
*/
|
|
||||||
protected $_eventLoop = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All listeners for read/write event.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_allEvents = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event listeners of signal.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_eventSignal = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All timer event listeners.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_eventTimer = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timer id.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected static $_timerId = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Constructor
|
|
||||||
*
|
|
||||||
* @param object $loop
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(\UVLoop $loop = null)
|
|
||||||
{
|
|
||||||
if(!extension_loaded('uv'))
|
|
||||||
{
|
|
||||||
throw new \Exception(__CLASS__ . ' requires the UV extension, but detected it has NOT been installed yet.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(empty($loop) || !$loop instanceof \UVLoop)
|
|
||||||
{
|
|
||||||
$this->_eventLoop = \uv_default_loop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_eventLoop = $loop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Add a timer
|
|
||||||
*
|
|
||||||
* @param resource $fd
|
|
||||||
* @param int $flag
|
|
||||||
* @param callback $func
|
|
||||||
* @param mixed $args
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function add($fd, $flag, $func, $args = null)
|
|
||||||
{
|
|
||||||
switch ($flag)
|
|
||||||
{
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
$signalCallback = function($watcher, $socket)use($func, $fd){
|
|
||||||
try {
|
|
||||||
\call_user_func($func, $fd);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
$signalWatcher = \uv_signal_init();
|
|
||||||
\uv_signal_start($signalWatcher, $signalCallback, $fd);
|
|
||||||
$this->_eventSignal[$fd] = $signalWatcher;
|
|
||||||
return true;
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE:
|
|
||||||
$repeat = $flag === self::EV_TIMER_ONCE ? 0 : (int)($fd * 1000);
|
|
||||||
$param = array($func, (array)$args, $flag, $fd, self::$_timerId);
|
|
||||||
$timerWatcher = \uv_timer_init();
|
|
||||||
\uv_timer_start($timerWatcher, ($flag === self::EV_TIMER_ONCE ? (int)($fd * 1000) :1), $repeat, function($watcher)use($param){
|
|
||||||
call_user_func_array([$this, 'timerCallback'], [$param]);
|
|
||||||
});
|
|
||||||
$this->_eventTimer[self::$_timerId] = $timerWatcher;
|
|
||||||
return self::$_timerId++;
|
|
||||||
case self::EV_READ:
|
|
||||||
case self::EV_WRITE:
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
$ioCallback = function($watcher, $status, $events, $fd)use($func){
|
|
||||||
try {
|
|
||||||
\call_user_func($func, $fd);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
$ioWatcher = \uv_poll_init($this->_eventLoop, $fd);
|
|
||||||
$real_flag = $flag === self::EV_READ ? \Uv::READABLE : \Uv::WRITABLE;
|
|
||||||
\uv_poll_start($ioWatcher, $real_flag, $ioCallback);
|
|
||||||
$this->_allEvents[$fd_key][$flag] = $ioWatcher;
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Remove a timer
|
|
||||||
*
|
|
||||||
* @param resource $fd
|
|
||||||
* @param int $flag
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function del($fd, $flag)
|
|
||||||
{
|
|
||||||
switch ($flag)
|
|
||||||
{
|
|
||||||
case self::EV_READ:
|
|
||||||
case self::EV_WRITE:
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
if (isset($this->_allEvents[$fd_key][$flag])) {
|
|
||||||
$watcher = $this->_allEvents[$fd_key][$flag];
|
|
||||||
\uv_is_active($watcher) && \uv_poll_stop($watcher);
|
|
||||||
unset($this->_allEvents[$fd_key][$flag]);
|
|
||||||
}
|
|
||||||
if (empty($this->_allEvents[$fd_key])) {
|
|
||||||
unset($this->_allEvents[$fd_key]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::EV_SIGNAL:
|
|
||||||
$fd_key = (int)$fd;
|
|
||||||
if (isset($this->_eventSignal[$fd_key])) {
|
|
||||||
$watcher = $this->_eventSignal[$fd_key];
|
|
||||||
\uv_is_active($watcher) && \uv_signal_stop($watcher);
|
|
||||||
unset($this->_eventSignal[$fd_key]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::EV_TIMER:
|
|
||||||
case self::EV_TIMER_ONCE:
|
|
||||||
if (isset($this->_eventTimer[$fd])) {
|
|
||||||
$watcher = $this->_eventTimer[$fd];
|
|
||||||
\uv_is_active($watcher) && \uv_timer_stop($watcher);
|
|
||||||
unset($this->_eventTimer[$fd]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Timer callback
|
|
||||||
*
|
|
||||||
* @param array $input
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function timerCallback($input)
|
|
||||||
{
|
|
||||||
if(!is_array($input)) return;
|
|
||||||
|
|
||||||
$timer_id = $input[4];
|
|
||||||
|
|
||||||
if ($input[2] === self::EV_TIMER_ONCE)
|
|
||||||
{
|
|
||||||
$watcher = $this->_eventTimer[$timer_id];
|
|
||||||
\uv_is_active($watcher) && \uv_timer_stop($watcher);
|
|
||||||
unset($this->_eventTimer[$timer_id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
\call_user_func_array($input[0], $input[1]);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Remove all timers
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function clearAllTimer()
|
|
||||||
{
|
|
||||||
if(!is_array($this->_eventTimer)) return;
|
|
||||||
|
|
||||||
foreach($this->_eventTimer as $watcher)
|
|
||||||
{
|
|
||||||
\uv_is_active($watcher) && \uv_timer_stop($watcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_eventTimer = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Start loop
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function loop()
|
|
||||||
{
|
|
||||||
\Uv_run();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destroy loop
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function destroy()
|
|
||||||
{
|
|
||||||
!empty($this->_eventLoop) && \uv_loop_delete($this->_eventLoop);
|
|
||||||
$this->_allEvents = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get timer count
|
|
||||||
*
|
|
||||||
* @return integer
|
|
||||||
*/
|
|
||||||
public function getTimerCount()
|
|
||||||
{
|
|
||||||
return \count($this->_eventTimer);
|
|
||||||
}
|
|
||||||
}
|
|
44
vendor/workerman/workerman/Lib/Constants.php
vendored
44
vendor/workerman/workerman/Lib/Constants.php
vendored
@ -1,44 +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 walkor<walkor@workerman.net>
|
|
||||||
* @copyright walkor<walkor@workerman.net>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
|
||||||
*
|
|
||||||
* @link http://www.workerman.net/
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Pcre.jit is not stable, temporarily disabled.
|
|
||||||
ini_set('pcre.jit', 0);
|
|
||||||
|
|
||||||
// For onError callback.
|
|
||||||
const WORKERMAN_CONNECT_FAIL = 1;
|
|
||||||
// For onError callback.
|
|
||||||
const WORKERMAN_SEND_FAIL = 2;
|
|
||||||
|
|
||||||
// Define OS Type
|
|
||||||
const OS_TYPE_LINUX = 'linux';
|
|
||||||
const OS_TYPE_WINDOWS = 'windows';
|
|
||||||
|
|
||||||
// Compatible with php7
|
|
||||||
if (!class_exists('Error')) {
|
|
||||||
class Error extends Exception
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!interface_exists('SessionHandlerInterface')) {
|
|
||||||
interface SessionHandlerInterface {
|
|
||||||
public function close();
|
|
||||||
public function destroy($session_id);
|
|
||||||
public function gc($maxlifetime);
|
|
||||||
public function open($save_path ,$session_name);
|
|
||||||
public function read($session_id);
|
|
||||||
public function write($session_id , $session_data);
|
|
||||||
}
|
|
||||||
}
|
|
22
vendor/workerman/workerman/Lib/Timer.php
vendored
22
vendor/workerman/workerman/Lib/Timer.php
vendored
@ -1,22 +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 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 Workerman\Lib;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do not use Workerman\Lib\Timer.
|
|
||||||
* Please use Workerman\Timer.
|
|
||||||
* This class is only used for compatibility with workerman 3.*
|
|
||||||
* @package Workerman\Lib
|
|
||||||
*/
|
|
||||||
class Timer extends \Workerman\Timer {}
|
|
21
vendor/workerman/workerman/MIT-LICENSE.txt
vendored
21
vendor/workerman/workerman/MIT-LICENSE.txt
vendored
@ -1,21 +0,0 @@
|
|||||||
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.
|
|
61
vendor/workerman/workerman/Protocols/Frame.php
vendored
61
vendor/workerman/workerman/Protocols/Frame.php
vendored
@ -1,61 +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 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 Workerman\Protocols;
|
|
||||||
|
|
||||||
use Workerman\Connection\TcpConnection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frame Protocol.
|
|
||||||
*/
|
|
||||||
class Frame
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Check the integrity of the package.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @param TcpConnection $connection
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public static function input($buffer, TcpConnection $connection)
|
|
||||||
{
|
|
||||||
if (\strlen($buffer) < 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$unpack_data = \unpack('Ntotal_length', $buffer);
|
|
||||||
return $unpack_data['total_length'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function decode($buffer)
|
|
||||||
{
|
|
||||||
return \substr($buffer, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function encode($buffer)
|
|
||||||
{
|
|
||||||
$total_length = 4 + \strlen($buffer);
|
|
||||||
return \pack('N', $total_length) . $buffer;
|
|
||||||
}
|
|
||||||
}
|
|
323
vendor/workerman/workerman/Protocols/Http.php
vendored
323
vendor/workerman/workerman/Protocols/Http.php
vendored
@ -1,323 +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 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 Workerman\Protocols;
|
|
||||||
|
|
||||||
use Workerman\Connection\TcpConnection;
|
|
||||||
use Workerman\Protocols\Http\Request;
|
|
||||||
use Workerman\Protocols\Http\Response;
|
|
||||||
use Workerman\Protocols\Http\Session;
|
|
||||||
use Workerman\Protocols\Websocket;
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class Http.
|
|
||||||
* @package Workerman\Protocols
|
|
||||||
*/
|
|
||||||
class Http
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Request class name.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected static $_requestClass = 'Workerman\Protocols\Http\Request';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload tmp dir.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected static $_uploadTmpDir = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open cache.
|
|
||||||
*
|
|
||||||
* @var bool.
|
|
||||||
*/
|
|
||||||
protected static $_enableCache = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get or set session name.
|
|
||||||
*
|
|
||||||
* @param string|null $name
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function sessionName($name = null)
|
|
||||||
{
|
|
||||||
if ($name !== null && $name !== '') {
|
|
||||||
Session::$name = (string)$name;
|
|
||||||
}
|
|
||||||
return Session::$name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get or set the request class name.
|
|
||||||
*
|
|
||||||
* @param string|null $class_name
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function requestClass($class_name = null)
|
|
||||||
{
|
|
||||||
if ($class_name) {
|
|
||||||
static::$_requestClass = $class_name;
|
|
||||||
}
|
|
||||||
return static::$_requestClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable or disable Cache.
|
|
||||||
*
|
|
||||||
* @param mixed $value
|
|
||||||
*/
|
|
||||||
public static function enableCache($value)
|
|
||||||
{
|
|
||||||
static::$_enableCache = (bool)$value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the integrity of the package.
|
|
||||||
*
|
|
||||||
* @param string $recv_buffer
|
|
||||||
* @param TcpConnection $connection
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public static function input($recv_buffer, TcpConnection $connection)
|
|
||||||
{
|
|
||||||
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 (\strlen($recv_buffer) >= 16384) {
|
|
||||||
$connection->close("HTTP/1.1 413 Request Entity Too Large\r\n\r\n", true);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$length = $crlf_pos + 4;
|
|
||||||
$method = \strstr($recv_buffer, ' ', true);
|
|
||||||
|
|
||||||
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);
|
|
||||||
if ($pos = \strpos($header, "\r\nContent-Length: ")) {
|
|
||||||
$length = $length + (int)\substr($header, $pos + 18, 10);
|
|
||||||
$has_content_length = true;
|
|
||||||
} else if (\preg_match("/\r\ncontent-length: ?(\d+)/i", $header, $match)) {
|
|
||||||
$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 ($has_content_length) {
|
|
||||||
if ($length > $connection->maxPackageSize) {
|
|
||||||
$connection->close("HTTP/1.1 413 Request Entity Too Large\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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Http decode.
|
|
||||||
*
|
|
||||||
* @param string $recv_buffer
|
|
||||||
* @param TcpConnection $connection
|
|
||||||
* @return \Workerman\Protocols\Http\Request
|
|
||||||
*/
|
|
||||||
public static function decode($recv_buffer, TcpConnection $connection)
|
|
||||||
{
|
|
||||||
static $requests = array();
|
|
||||||
$cacheable = static::$_enableCache && !isset($recv_buffer[512]);
|
|
||||||
if (true === $cacheable && isset($requests[$recv_buffer])) {
|
|
||||||
$request = $requests[$recv_buffer];
|
|
||||||
$request->connection = $connection;
|
|
||||||
$connection->__request = $request;
|
|
||||||
$request->properties = array();
|
|
||||||
return $request;
|
|
||||||
}
|
|
||||||
$request = new static::$_requestClass($recv_buffer);
|
|
||||||
$request->connection = $connection;
|
|
||||||
$connection->__request = $request;
|
|
||||||
if (true === $cacheable) {
|
|
||||||
$requests[$recv_buffer] = $request;
|
|
||||||
if (\count($requests) > 512) {
|
|
||||||
unset($requests[key($requests)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $request;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Http encode.
|
|
||||||
*
|
|
||||||
* @param string|Response $response
|
|
||||||
* @param TcpConnection $connection
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function encode($response, TcpConnection $connection)
|
|
||||||
{
|
|
||||||
if (isset($connection->__request)) {
|
|
||||||
$connection->__request->session = null;
|
|
||||||
$connection->__request->connection = null;
|
|
||||||
$connection->__request = null;
|
|
||||||
}
|
|
||||||
if (!\is_object($response)) {
|
|
||||||
$ext_header = '';
|
|
||||||
if (isset($connection->__header)) {
|
|
||||||
foreach ($connection->__header as $name => $value) {
|
|
||||||
if (\is_array($value)) {
|
|
||||||
foreach ($value as $item) {
|
|
||||||
$ext_header = "$name: $item\r\n";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$ext_header = "$name: $value\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unset($connection->__header);
|
|
||||||
}
|
|
||||||
$body_len = \strlen((string)$response);
|
|
||||||
return "HTTP/1.1 200 OK\r\nServer: workerman\r\n{$ext_header}Connection: keep-alive\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: $body_len\r\n\r\n$response";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($connection->__header)) {
|
|
||||||
$response->withHeaders($connection->__header);
|
|
||||||
unset($connection->__header);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($response->file)) {
|
|
||||||
$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(
|
|
||||||
'Content-Length' => $body_len,
|
|
||||||
'Accept-Ranges' => 'bytes',
|
|
||||||
));
|
|
||||||
if ($offset || $length) {
|
|
||||||
$offset_end = $offset + $body_len - 1;
|
|
||||||
$response->header('Content-Range', "bytes $offset-$offset_end/$file_size");
|
|
||||||
}
|
|
||||||
if ($body_len < 2 * 1024 * 1024) {
|
|
||||||
$connection->send((string)$response . file_get_contents($file, false, null, $offset, $body_len), true);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
$handler = \fopen($file, 'r');
|
|
||||||
if (false === $handler) {
|
|
||||||
$connection->close(new Response(403, null, '403 Forbidden'));
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
$connection->send((string)$response, true);
|
|
||||||
static::sendStream($connection, $handler, $offset, $length);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (string)$response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send remainder of a stream to client.
|
|
||||||
*
|
|
||||||
* @param TcpConnection $connection
|
|
||||||
* @param resource $handler
|
|
||||||
* @param int $offset
|
|
||||||
* @param int $length
|
|
||||||
*/
|
|
||||||
protected static function sendStream(TcpConnection $connection, $handler, $offset = 0, $length = 0)
|
|
||||||
{
|
|
||||||
$connection->bufferFull = false;
|
|
||||||
if ($offset !== 0) {
|
|
||||||
\fseek($handler, $offset);
|
|
||||||
}
|
|
||||||
$offset_end = $offset + $length;
|
|
||||||
// Read file content from disk piece by piece and send to client.
|
|
||||||
$do_write = function () use ($connection, $handler, $length, $offset_end) {
|
|
||||||
// Send buffer not full.
|
|
||||||
while ($connection->bufferFull === false) {
|
|
||||||
// Read from disk.
|
|
||||||
$size = 1024 * 1024;
|
|
||||||
if ($length !== 0) {
|
|
||||||
$tell = \ftell($handler);
|
|
||||||
$remain_size = $offset_end - $tell;
|
|
||||||
if ($remain_size <= 0) {
|
|
||||||
fclose($handler);
|
|
||||||
$connection->onBufferDrain = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$size = $remain_size > $size ? $size : $remain_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
$buffer = \fread($handler, $size);
|
|
||||||
// Read eof.
|
|
||||||
if ($buffer === '' || $buffer === false) {
|
|
||||||
fclose($handler);
|
|
||||||
$connection->onBufferDrain = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$connection->send($buffer, true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Send buffer full.
|
|
||||||
$connection->onBufferFull = function ($connection) {
|
|
||||||
$connection->bufferFull = true;
|
|
||||||
};
|
|
||||||
// Send buffer drain.
|
|
||||||
$connection->onBufferDrain = function ($connection) use ($do_write) {
|
|
||||||
$connection->bufferFull = false;
|
|
||||||
$do_write();
|
|
||||||
};
|
|
||||||
$do_write();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set or get uploadTmpDir.
|
|
||||||
*
|
|
||||||
* @return bool|string
|
|
||||||
*/
|
|
||||||
public static function uploadTmpDir($dir = null)
|
|
||||||
{
|
|
||||||
if (null !== $dir) {
|
|
||||||
static::$_uploadTmpDir = $dir;
|
|
||||||
}
|
|
||||||
if (static::$_uploadTmpDir === '') {
|
|
||||||
if ($upload_tmp_dir = \ini_get('upload_tmp_dir')) {
|
|
||||||
static::$_uploadTmpDir = $upload_tmp_dir;
|
|
||||||
} else if ($upload_tmp_dir = \sys_get_temp_dir()) {
|
|
||||||
static::$_uploadTmpDir = $upload_tmp_dir;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return static::$_uploadTmpDir;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +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 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 Workerman\Protocols\Http;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class Chunk
|
|
||||||
* @package Workerman\Protocols\Http
|
|
||||||
*/
|
|
||||||
class Chunk
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Chunk buffer.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_buffer = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Chunk constructor.
|
|
||||||
* @param string $buffer
|
|
||||||
*/
|
|
||||||
public function __construct($buffer)
|
|
||||||
{
|
|
||||||
$this->_buffer = $buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __toString
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
return \dechex(\strlen($this->_buffer))."\r\n$this->_buffer\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,694 +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 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 Workerman\Protocols\Http;
|
|
||||||
|
|
||||||
use Workerman\Connection\TcpConnection;
|
|
||||||
use Workerman\Protocols\Http\Session;
|
|
||||||
use Workerman\Protocols\Http;
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class Request
|
|
||||||
* @package Workerman\Protocols\Http
|
|
||||||
*/
|
|
||||||
class Request
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Connection.
|
|
||||||
*
|
|
||||||
* @var TcpConnection
|
|
||||||
*/
|
|
||||||
public $connection = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session instance.
|
|
||||||
*
|
|
||||||
* @var Session
|
|
||||||
*/
|
|
||||||
public $session = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Properties.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $properties = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public static $maxFileUploads = 1024;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Http buffer.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_buffer = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request data.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_data = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable cache.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected static $_enableCache = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is safe.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $_isSafe = true;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request constructor.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
*/
|
|
||||||
public function __construct($buffer)
|
|
||||||
{
|
|
||||||
$this->_buffer = $buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* $_GET.
|
|
||||||
*
|
|
||||||
* @param string|null $name
|
|
||||||
* @param mixed|null $default
|
|
||||||
* @return mixed|null
|
|
||||||
*/
|
|
||||||
public function get($name = null, $default = null)
|
|
||||||
{
|
|
||||||
if (!isset($this->_data['get'])) {
|
|
||||||
$this->parseGet();
|
|
||||||
}
|
|
||||||
if (null === $name) {
|
|
||||||
return $this->_data['get'];
|
|
||||||
}
|
|
||||||
return isset($this->_data['get'][$name]) ? $this->_data['get'][$name] : $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* $_POST.
|
|
||||||
*
|
|
||||||
* @param string|null $name
|
|
||||||
* @param mixed|null $default
|
|
||||||
* @return mixed|null
|
|
||||||
*/
|
|
||||||
public function post($name = null, $default = null)
|
|
||||||
{
|
|
||||||
if (!isset($this->_data['post'])) {
|
|
||||||
$this->parsePost();
|
|
||||||
}
|
|
||||||
if (null === $name) {
|
|
||||||
return $this->_data['post'];
|
|
||||||
}
|
|
||||||
return isset($this->_data['post'][$name]) ? $this->_data['post'][$name] : $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get header item by name.
|
|
||||||
*
|
|
||||||
* @param string|null $name
|
|
||||||
* @param mixed|null $default
|
|
||||||
* @return array|string|null
|
|
||||||
*/
|
|
||||||
public function header($name = null, $default = null)
|
|
||||||
{
|
|
||||||
if (!isset($this->_data['headers'])) {
|
|
||||||
$this->parseHeaders();
|
|
||||||
}
|
|
||||||
if (null === $name) {
|
|
||||||
return $this->_data['headers'];
|
|
||||||
}
|
|
||||||
$name = \strtolower($name);
|
|
||||||
return isset($this->_data['headers'][$name]) ? $this->_data['headers'][$name] : $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get cookie item by name.
|
|
||||||
*
|
|
||||||
* @param string|null $name
|
|
||||||
* @param mixed|null $default
|
|
||||||
* @return array|string|null
|
|
||||||
*/
|
|
||||||
public function cookie($name = null, $default = null)
|
|
||||||
{
|
|
||||||
if (!isset($this->_data['cookie'])) {
|
|
||||||
$this->_data['cookie'] = array();
|
|
||||||
\parse_str(\preg_replace('/; ?/', '&', $this->header('cookie', '')), $this->_data['cookie']);
|
|
||||||
}
|
|
||||||
if ($name === null) {
|
|
||||||
return $this->_data['cookie'];
|
|
||||||
}
|
|
||||||
return isset($this->_data['cookie'][$name]) ? $this->_data['cookie'][$name] : $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get upload files.
|
|
||||||
*
|
|
||||||
* @param string|null $name
|
|
||||||
* @return array|null
|
|
||||||
*/
|
|
||||||
public function file($name = null)
|
|
||||||
{
|
|
||||||
if (!isset($this->_data['files'])) {
|
|
||||||
$this->parsePost();
|
|
||||||
}
|
|
||||||
if (null === $name) {
|
|
||||||
return $this->_data['files'];
|
|
||||||
}
|
|
||||||
return isset($this->_data['files'][$name]) ? $this->_data['files'][$name] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get method.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function method()
|
|
||||||
{
|
|
||||||
if (!isset($this->_data['method'])) {
|
|
||||||
$this->parseHeadFirstLine();
|
|
||||||
}
|
|
||||||
return $this->_data['method'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get http protocol version.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function protocolVersion()
|
|
||||||
{
|
|
||||||
if (!isset($this->_data['protocolVersion'])) {
|
|
||||||
$this->parseProtocolVersion();
|
|
||||||
}
|
|
||||||
return $this->_data['protocolVersion'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get host.
|
|
||||||
*
|
|
||||||
* @param bool $without_port
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function host($without_port = false)
|
|
||||||
{
|
|
||||||
$host = $this->header('host');
|
|
||||||
if ($host && $without_port) {
|
|
||||||
return preg_replace('/:\d{1,5}$/', '', $host);
|
|
||||||
}
|
|
||||||
return $host;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get uri.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function uri()
|
|
||||||
{
|
|
||||||
if (!isset($this->_data['uri'])) {
|
|
||||||
$this->parseHeadFirstLine();
|
|
||||||
}
|
|
||||||
return $this->_data['uri'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get path.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function path()
|
|
||||||
{
|
|
||||||
if (!isset($this->_data['path'])) {
|
|
||||||
$this->_data['path'] = (string)\parse_url($this->uri(), PHP_URL_PATH);
|
|
||||||
}
|
|
||||||
return $this->_data['path'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get query string.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function queryString()
|
|
||||||
{
|
|
||||||
if (!isset($this->_data['query_string'])) {
|
|
||||||
$this->_data['query_string'] = (string)\parse_url($this->uri(), PHP_URL_QUERY);
|
|
||||||
}
|
|
||||||
return $this->_data['query_string'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get session.
|
|
||||||
*
|
|
||||||
* @return bool|\Workerman\Protocols\Http\Session
|
|
||||||
*/
|
|
||||||
public function session()
|
|
||||||
{
|
|
||||||
if ($this->session === null) {
|
|
||||||
$session_id = $this->sessionId();
|
|
||||||
if ($session_id === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->session = new Session($session_id);
|
|
||||||
}
|
|
||||||
return $this->session;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get/Set session id.
|
|
||||||
*
|
|
||||||
* @param $session_id
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function sessionId($session_id = null)
|
|
||||||
{
|
|
||||||
if ($session_id) {
|
|
||||||
unset($this->sid);
|
|
||||||
}
|
|
||||||
if (!isset($this->sid)) {
|
|
||||||
$session_name = Session::$name;
|
|
||||||
$sid = $session_id ? '' : $this->cookie($session_name);
|
|
||||||
if ($sid === '' || $sid === null) {
|
|
||||||
if ($this->connection === null) {
|
|
||||||
Worker::safeEcho('Request->session() fail, header already send');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$sid = $session_id ? $session_id : static::createSessionId();
|
|
||||||
$cookie_params = Session::getCookieParams();
|
|
||||||
$this->connection->__header['Set-Cookie'] = array($session_name . '=' . $sid
|
|
||||||
. (empty($cookie_params['domain']) ? '' : '; Domain=' . $cookie_params['domain'])
|
|
||||||
. (empty($cookie_params['lifetime']) ? '' : '; Max-Age=' . $cookie_params['lifetime'])
|
|
||||||
. (empty($cookie_params['path']) ? '' : '; Path=' . $cookie_params['path'])
|
|
||||||
. (empty($cookie_params['samesite']) ? '' : '; SameSite=' . $cookie_params['samesite'])
|
|
||||||
. (!$cookie_params['secure'] ? '' : '; Secure')
|
|
||||||
. (!$cookie_params['httponly'] ? '' : '; HttpOnly'));
|
|
||||||
}
|
|
||||||
$this->sid = $sid;
|
|
||||||
}
|
|
||||||
return $this->sid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get http raw head.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function rawHead()
|
|
||||||
{
|
|
||||||
if (!isset($this->_data['head'])) {
|
|
||||||
$this->_data['head'] = \strstr($this->_buffer, "\r\n\r\n", true);
|
|
||||||
}
|
|
||||||
return $this->_data['head'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get http raw body.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function rawBody()
|
|
||||||
{
|
|
||||||
return \substr($this->_buffer, \strpos($this->_buffer, "\r\n\r\n") + 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get raw buffer.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function rawBuffer()
|
|
||||||
{
|
|
||||||
return $this->_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable or disable cache.
|
|
||||||
*
|
|
||||||
* @param mixed $value
|
|
||||||
*/
|
|
||||||
public static function enableCache($value)
|
|
||||||
{
|
|
||||||
static::$_enableCache = (bool)$value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse first line of http header buffer.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function parseHeadFirstLine()
|
|
||||||
{
|
|
||||||
$first_line = \strstr($this->_buffer, "\r\n", true);
|
|
||||||
$tmp = \explode(' ', $first_line, 3);
|
|
||||||
$this->_data['method'] = $tmp[0];
|
|
||||||
$this->_data['uri'] = isset($tmp[1]) ? $tmp[1] : '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse protocol version.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function parseProtocolVersion()
|
|
||||||
{
|
|
||||||
$first_line = \strstr($this->_buffer, "\r\n", true);
|
|
||||||
$protoco_version = substr(\strstr($first_line, 'HTTP/'), 5);
|
|
||||||
$this->_data['protocolVersion'] = $protoco_version ? $protoco_version : '1.0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse headers.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function parseHeaders()
|
|
||||||
{
|
|
||||||
static $cache = [];
|
|
||||||
$this->_data['headers'] = array();
|
|
||||||
$raw_head = $this->rawHead();
|
|
||||||
$end_line_position = \strpos($raw_head, "\r\n");
|
|
||||||
if ($end_line_position === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$head_buffer = \substr($raw_head, $end_line_position + 2);
|
|
||||||
$cacheable = static::$_enableCache && !isset($head_buffer[2048]);
|
|
||||||
if ($cacheable && isset($cache[$head_buffer])) {
|
|
||||||
$this->_data['headers'] = $cache[$head_buffer];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$head_data = \explode("\r\n", $head_buffer);
|
|
||||||
foreach ($head_data as $content) {
|
|
||||||
if (false !== \strpos($content, ':')) {
|
|
||||||
list($key, $value) = \explode(':', $content, 2);
|
|
||||||
$key = \strtolower($key);
|
|
||||||
$value = \ltrim($value);
|
|
||||||
} else {
|
|
||||||
$key = \strtolower($content);
|
|
||||||
$value = '';
|
|
||||||
}
|
|
||||||
if (isset($this->_data['headers'][$key])) {
|
|
||||||
$this->_data['headers'][$key] = "{$this->_data['headers'][$key]},$value";
|
|
||||||
} else {
|
|
||||||
$this->_data['headers'][$key] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($cacheable) {
|
|
||||||
$cache[$head_buffer] = $this->_data['headers'];
|
|
||||||
if (\count($cache) > 128) {
|
|
||||||
unset($cache[key($cache)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse head.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function parseGet()
|
|
||||||
{
|
|
||||||
static $cache = [];
|
|
||||||
$query_string = $this->queryString();
|
|
||||||
$this->_data['get'] = array();
|
|
||||||
if ($query_string === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$cacheable = static::$_enableCache && !isset($query_string[1024]);
|
|
||||||
if ($cacheable && isset($cache[$query_string])) {
|
|
||||||
$this->_data['get'] = $cache[$query_string];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
\parse_str($query_string, $this->_data['get']);
|
|
||||||
if ($cacheable) {
|
|
||||||
$cache[$query_string] = $this->_data['get'];
|
|
||||||
if (\count($cache) > 256) {
|
|
||||||
unset($cache[key($cache)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse post.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function parsePost()
|
|
||||||
{
|
|
||||||
static $cache = [];
|
|
||||||
$this->_data['post'] = $this->_data['files'] = array();
|
|
||||||
$content_type = $this->header('content-type', '');
|
|
||||||
if (\preg_match('/boundary="?(\S+)"?/', $content_type, $match)) {
|
|
||||||
$http_post_boundary = '--' . $match[1];
|
|
||||||
$this->parseUploadFiles($http_post_boundary);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$body_buffer = $this->rawBody();
|
|
||||||
if ($body_buffer === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$cacheable = static::$_enableCache && !isset($body_buffer[1024]);
|
|
||||||
if ($cacheable && isset($cache[$body_buffer])) {
|
|
||||||
$this->_data['post'] = $cache[$body_buffer];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (\preg_match('/\bjson\b/i', $content_type)) {
|
|
||||||
$this->_data['post'] = (array) json_decode($body_buffer, true);
|
|
||||||
} else {
|
|
||||||
\parse_str($body_buffer, $this->_data['post']);
|
|
||||||
}
|
|
||||||
if ($cacheable) {
|
|
||||||
$cache[$body_buffer] = $this->_data['post'];
|
|
||||||
if (\count($cache) > 256) {
|
|
||||||
unset($cache[key($cache)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse upload files.
|
|
||||||
*
|
|
||||||
* @param string $http_post_boundary
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function parseUploadFiles($http_post_boundary)
|
|
||||||
{
|
|
||||||
$http_post_boundary = \trim($http_post_boundary, '"');
|
|
||||||
$buffer = $this->_buffer;
|
|
||||||
$post_encode_string = '';
|
|
||||||
$files_encode_string = '';
|
|
||||||
$files = [];
|
|
||||||
$boday_position = strpos($buffer, "\r\n\r\n") + 4;
|
|
||||||
$offset = $boday_position + strlen($http_post_boundary) + 2;
|
|
||||||
$max_count = static::$maxFileUploads;
|
|
||||||
while ($max_count-- > 0 && $offset) {
|
|
||||||
$offset = $this->parseUploadFile($http_post_boundary, $offset, $post_encode_string, $files_encode_string, $files);
|
|
||||||
}
|
|
||||||
if ($post_encode_string) {
|
|
||||||
parse_str($post_encode_string, $this->_data['post']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($files_encode_string) {
|
|
||||||
parse_str($files_encode_string, $this->_data['files']);
|
|
||||||
\array_walk_recursive($this->_data['files'], function (&$value) use ($files) {
|
|
||||||
$value = $files[$value];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $boundary
|
|
||||||
* @param $section_start_offset
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
protected function parseUploadFile($boundary, $section_start_offset, &$post_encode_string, &$files_encode_str, &$files)
|
|
||||||
{
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
$content_lines_end_offset = \strpos($this->_buffer, "\r\n\r\n", $section_start_offset);
|
|
||||||
if (!$content_lines_end_offset || $content_lines_end_offset + 4 > $section_end_offset) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$content_lines_str = \substr($this->_buffer, $section_start_offset, $content_lines_end_offset - $section_start_offset);
|
|
||||||
$content_lines = \explode("\r\n", trim($content_lines_str . "\r\n"));
|
|
||||||
$boundary_value = \substr($this->_buffer, $content_lines_end_offset + 4, $section_end_offset - $content_lines_end_offset - 4);
|
|
||||||
$upload_key = false;
|
|
||||||
foreach ($content_lines as $content_line) {
|
|
||||||
if (!\strpos($content_line, ': ')) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
list($key, $value) = \explode(': ', $content_line);
|
|
||||||
switch (strtolower($key)) {
|
|
||||||
case "content-disposition":
|
|
||||||
// Is file data.
|
|
||||||
if (\preg_match('/name="(.*?)"; filename="(.*?)"/i', $value, $match)) {
|
|
||||||
$error = 0;
|
|
||||||
$tmp_file = '';
|
|
||||||
$file_name = $match[2];
|
|
||||||
$size = \strlen($boundary_value);
|
|
||||||
$tmp_upload_dir = HTTP::uploadTmpDir();
|
|
||||||
if (!$tmp_upload_dir) {
|
|
||||||
$error = UPLOAD_ERR_NO_TMP_DIR;
|
|
||||||
} else if ($boundary_value === '' && $file_name === '') {
|
|
||||||
$error = UPLOAD_ERR_NO_FILE;
|
|
||||||
} else {
|
|
||||||
$tmp_file = \tempnam($tmp_upload_dir, 'workerman.upload.');
|
|
||||||
if ($tmp_file === false || false === \file_put_contents($tmp_file, $boundary_value)) {
|
|
||||||
$error = UPLOAD_ERR_CANT_WRITE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$upload_key = $match[1];
|
|
||||||
// Parse upload files.
|
|
||||||
$file = [
|
|
||||||
'name' => $file_name,
|
|
||||||
'tmp_name' => $tmp_file,
|
|
||||||
'size' => $size,
|
|
||||||
'error' => $error,
|
|
||||||
'type' => '',
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
} // Is post field.
|
|
||||||
else {
|
|
||||||
// Parse $_POST.
|
|
||||||
if (\preg_match('/name="(.*?)"$/', $value, $match)) {
|
|
||||||
$k = $match[1];
|
|
||||||
$post_encode_string .= \urlencode($k) . "=" . \urlencode($boundary_value) . '&';
|
|
||||||
}
|
|
||||||
return $section_end_offset + \strlen($boundary) + 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "content-type":
|
|
||||||
$file['type'] = \trim($value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($upload_key === false) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$files_encode_str .= \urlencode($upload_key) . '=' . \count($files) . '&';
|
|
||||||
$files[] = $file;
|
|
||||||
|
|
||||||
return $section_end_offset + \strlen($boundary) + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create session id.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected static function createSessionId()
|
|
||||||
{
|
|
||||||
return \bin2hex(\pack('d', \microtime(true)) . random_bytes(8));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param mixed $value
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __set($name, $value)
|
|
||||||
{
|
|
||||||
$this->properties[$name] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return mixed|null
|
|
||||||
*/
|
|
||||||
public function __get($name)
|
|
||||||
{
|
|
||||||
return isset($this->properties[$name]) ? $this->properties[$name] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Isset.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function __isset($name)
|
|
||||||
{
|
|
||||||
return isset($this->properties[$name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unset.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __unset($name)
|
|
||||||
{
|
|
||||||
unset($this->properties[$name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __toString.
|
|
||||||
*/
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
return $this->_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __wakeup.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __wakeup()
|
|
||||||
{
|
|
||||||
$this->_isSafe = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __destruct.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __destruct()
|
|
||||||
{
|
|
||||||
if (isset($this->_data['files']) && $this->_isSafe) {
|
|
||||||
\clearstatcache();
|
|
||||||
\array_walk_recursive($this->_data['files'], function($value, $key){
|
|
||||||
if ($key === 'tmp_name') {
|
|
||||||
if (\is_file($value)) {
|
|
||||||
\unlink($value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,458 +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 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 Workerman\Protocols\Http;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class Response
|
|
||||||
* @package Workerman\Protocols\Http
|
|
||||||
*/
|
|
||||||
class Response
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Header data.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_header = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Http status.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $_status = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Http reason.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_reason = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Http version.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_version = '1.1';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Http body.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $_body = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send file info
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $file = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mine type map.
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected static $_mimeTypeMap = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Phrases.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected static $_phrases = array(
|
|
||||||
100 => 'Continue',
|
|
||||||
101 => 'Switching Protocols',
|
|
||||||
102 => 'Processing',
|
|
||||||
200 => 'OK',
|
|
||||||
201 => 'Created',
|
|
||||||
202 => 'Accepted',
|
|
||||||
203 => 'Non-Authoritative Information',
|
|
||||||
204 => 'No Content',
|
|
||||||
205 => 'Reset Content',
|
|
||||||
206 => 'Partial Content',
|
|
||||||
207 => 'Multi-status',
|
|
||||||
208 => 'Already Reported',
|
|
||||||
300 => 'Multiple Choices',
|
|
||||||
301 => 'Moved Permanently',
|
|
||||||
302 => 'Found',
|
|
||||||
303 => 'See Other',
|
|
||||||
304 => 'Not Modified',
|
|
||||||
305 => 'Use Proxy',
|
|
||||||
306 => 'Switch Proxy',
|
|
||||||
307 => 'Temporary Redirect',
|
|
||||||
400 => 'Bad Request',
|
|
||||||
401 => 'Unauthorized',
|
|
||||||
402 => 'Payment Required',
|
|
||||||
403 => 'Forbidden',
|
|
||||||
404 => 'Not Found',
|
|
||||||
405 => 'Method Not Allowed',
|
|
||||||
406 => 'Not Acceptable',
|
|
||||||
407 => 'Proxy Authentication Required',
|
|
||||||
408 => 'Request Time-out',
|
|
||||||
409 => 'Conflict',
|
|
||||||
410 => 'Gone',
|
|
||||||
411 => 'Length Required',
|
|
||||||
412 => 'Precondition Failed',
|
|
||||||
413 => 'Request Entity Too Large',
|
|
||||||
414 => 'Request-URI Too Large',
|
|
||||||
415 => 'Unsupported Media Type',
|
|
||||||
416 => 'Requested range not satisfiable',
|
|
||||||
417 => 'Expectation Failed',
|
|
||||||
418 => 'I\'m a teapot',
|
|
||||||
422 => 'Unprocessable Entity',
|
|
||||||
423 => 'Locked',
|
|
||||||
424 => 'Failed Dependency',
|
|
||||||
425 => 'Unordered Collection',
|
|
||||||
426 => 'Upgrade Required',
|
|
||||||
428 => 'Precondition Required',
|
|
||||||
429 => 'Too Many Requests',
|
|
||||||
431 => 'Request Header Fields Too Large',
|
|
||||||
451 => 'Unavailable For Legal Reasons',
|
|
||||||
500 => 'Internal Server Error',
|
|
||||||
501 => 'Not Implemented',
|
|
||||||
502 => 'Bad Gateway',
|
|
||||||
503 => 'Service Unavailable',
|
|
||||||
504 => 'Gateway Time-out',
|
|
||||||
505 => 'HTTP Version not supported',
|
|
||||||
506 => 'Variant Also Negotiates',
|
|
||||||
507 => 'Insufficient Storage',
|
|
||||||
508 => 'Loop Detected',
|
|
||||||
511 => 'Network Authentication Required',
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function init() {
|
|
||||||
static::initMimeTypeMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response constructor.
|
|
||||||
*
|
|
||||||
* @param int $status
|
|
||||||
* @param array $headers
|
|
||||||
* @param string $body
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
$status = 200,
|
|
||||||
$headers = array(),
|
|
||||||
$body = ''
|
|
||||||
) {
|
|
||||||
$this->_status = $status;
|
|
||||||
$this->_header = $headers;
|
|
||||||
$this->_body = (string)$body;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set header.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param string $value
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function header($name, $value) {
|
|
||||||
$this->_header[$name] = $value;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set header.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param string $value
|
|
||||||
* @return Response
|
|
||||||
*/
|
|
||||||
public function withHeader($name, $value) {
|
|
||||||
return $this->header($name, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set headers.
|
|
||||||
*
|
|
||||||
* @param array $headers
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function withHeaders($headers) {
|
|
||||||
$this->_header = \array_merge_recursive($this->_header, $headers);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove header.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function withoutHeader($name) {
|
|
||||||
unset($this->_header[$name]);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get header.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return null|array|string
|
|
||||||
*/
|
|
||||||
public function getHeader($name) {
|
|
||||||
if (!isset($this->_header[$name])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return $this->_header[$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get headers.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getHeaders() {
|
|
||||||
return $this->_header;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set status.
|
|
||||||
*
|
|
||||||
* @param int $code
|
|
||||||
* @param string|null $reason_phrase
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function withStatus($code, $reason_phrase = null) {
|
|
||||||
$this->_status = $code;
|
|
||||||
$this->_reason = $reason_phrase;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get status code.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getStatusCode() {
|
|
||||||
return $this->_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get reason phrase.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getReasonPhrase() {
|
|
||||||
return $this->_reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set protocol version.
|
|
||||||
*
|
|
||||||
* @param int $version
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function withProtocolVersion($version) {
|
|
||||||
$this->_version = $version;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set http body.
|
|
||||||
*
|
|
||||||
* @param string $body
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function withBody($body) {
|
|
||||||
$this->_body = $body;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get http raw body.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function rawBody() {
|
|
||||||
return $this->_body;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send file.
|
|
||||||
*
|
|
||||||
* @param string $file
|
|
||||||
* @param int $offset
|
|
||||||
* @param int $length
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function withFile($file, $offset = 0, $length = 0) {
|
|
||||||
if (!\is_file($file)) {
|
|
||||||
return $this->withStatus(404)->withBody('<h3>404 Not Found</h3>');
|
|
||||||
}
|
|
||||||
$this->file = array('file' => $file, 'offset' => $offset, 'length' => $length);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set cookie.
|
|
||||||
*
|
|
||||||
* @param $name
|
|
||||||
* @param string $value
|
|
||||||
* @param int $max_age
|
|
||||||
* @param string $path
|
|
||||||
* @param string $domain
|
|
||||||
* @param bool $secure
|
|
||||||
* @param bool $http_only
|
|
||||||
* @param string $same_site
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function cookie($name, $value = '', $max_age = null, $path = '', $domain = '', $secure = false, $http_only = false, $same_site = '')
|
|
||||||
{
|
|
||||||
$this->_header['Set-Cookie'][] = $name . '=' . \rawurlencode($value)
|
|
||||||
. (empty($domain) ? '' : '; Domain=' . $domain)
|
|
||||||
. ($max_age === null ? '' : '; Max-Age=' . $max_age)
|
|
||||||
. (empty($path) ? '' : '; Path=' . $path)
|
|
||||||
. (!$secure ? '' : '; Secure')
|
|
||||||
. (!$http_only ? '' : '; HttpOnly')
|
|
||||||
. (empty($same_site) ? '' : '; SameSite=' . $same_site);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create header for file.
|
|
||||||
*
|
|
||||||
* @param array $file_info
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function createHeadForFile($file_info)
|
|
||||||
{
|
|
||||||
$file = $file_info['file'];
|
|
||||||
$reason = $this->_reason ? $this->_reason : static::$_phrases[$this->_status];
|
|
||||||
$head = "HTTP/{$this->_version} {$this->_status} $reason\r\n";
|
|
||||||
$headers = $this->_header;
|
|
||||||
if (!isset($headers['Server'])) {
|
|
||||||
$head .= "Server: workerman\r\n";
|
|
||||||
}
|
|
||||||
foreach ($headers as $name => $value) {
|
|
||||||
if (\is_array($value)) {
|
|
||||||
foreach ($value as $item) {
|
|
||||||
$head .= "$name: $item\r\n";
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$head .= "$name: $value\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($headers['Connection'])) {
|
|
||||||
$head .= "Connection: keep-alive\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$file_info = \pathinfo($file);
|
|
||||||
$extension = isset($file_info['extension']) ? $file_info['extension'] : '';
|
|
||||||
$base_name = isset($file_info['basename']) ? $file_info['basename'] : 'unknown';
|
|
||||||
if (!isset($headers['Content-Type'])) {
|
|
||||||
if (isset(self::$_mimeTypeMap[$extension])) {
|
|
||||||
$head .= "Content-Type: " . self::$_mimeTypeMap[$extension] . "\r\n";
|
|
||||||
} else {
|
|
||||||
$head .= "Content-Type: application/octet-stream\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($headers['Content-Disposition']) && !isset(self::$_mimeTypeMap[$extension])) {
|
|
||||||
$head .= "Content-Disposition: attachment; filename=\"$base_name\"\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($headers['Last-Modified'])) {
|
|
||||||
if ($mtime = \filemtime($file)) {
|
|
||||||
$head .= 'Last-Modified: '. \gmdate('D, d M Y H:i:s', $mtime) . ' GMT' . "\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "{$head}\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __toString.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
if (isset($this->file)) {
|
|
||||||
return $this->createHeadForFile($this->file);
|
|
||||||
}
|
|
||||||
|
|
||||||
$reason = $this->_reason ? $this->_reason : static::$_phrases[$this->_status];
|
|
||||||
$body_len = \strlen($this->_body);
|
|
||||||
if (empty($this->_header)) {
|
|
||||||
return "HTTP/{$this->_version} {$this->_status} $reason\r\nServer: workerman\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: $body_len\r\nConnection: keep-alive\r\n\r\n{$this->_body}";
|
|
||||||
}
|
|
||||||
|
|
||||||
$head = "HTTP/{$this->_version} {$this->_status} $reason\r\n";
|
|
||||||
$headers = $this->_header;
|
|
||||||
if (!isset($headers['Server'])) {
|
|
||||||
$head .= "Server: workerman\r\n";
|
|
||||||
}
|
|
||||||
foreach ($headers as $name => $value) {
|
|
||||||
if (\is_array($value)) {
|
|
||||||
foreach ($value as $item) {
|
|
||||||
$head .= "$name: $item\r\n";
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$head .= "$name: $value\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($headers['Connection'])) {
|
|
||||||
$head .= "Connection: keep-alive\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($headers['Content-Type'])) {
|
|
||||||
$head .= "Content-Type: text/html;charset=utf-8\r\n";
|
|
||||||
} else if ($headers['Content-Type'] === 'text/event-stream') {
|
|
||||||
return $head . $this->_body;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($headers['Transfer-Encoding'])) {
|
|
||||||
$head .= "Content-Length: $body_len\r\n\r\n";
|
|
||||||
} else {
|
|
||||||
return $body_len ? "$head\r\n" . dechex($body_len) . "\r\n{$this->_body}\r\n" : "$head\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// The whole http package
|
|
||||||
return $head . $this->_body;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init mime map.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function initMimeTypeMap()
|
|
||||||
{
|
|
||||||
$mime_file = __DIR__ . '/mime.types';
|
|
||||||
$items = \file($mime_file, \FILE_IGNORE_NEW_LINES | \FILE_SKIP_EMPTY_LINES);
|
|
||||||
foreach ($items as $content) {
|
|
||||||
if (\preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match)) {
|
|
||||||
$mime_type = $match[1];
|
|
||||||
$extension_var = $match[2];
|
|
||||||
$extension_array = \explode(' ', \substr($extension_var, 0, -1));
|
|
||||||
foreach ($extension_array as $file_extension) {
|
|
||||||
static::$_mimeTypeMap[$file_extension] = $mime_type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response::init();
|
|
@ -1,64 +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 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 Workerman\Protocols\Http;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ServerSentEvents
|
|
||||||
* @package Workerman\Protocols\Http
|
|
||||||
*/
|
|
||||||
class ServerSentEvents
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Data.
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_data = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ServerSentEvents constructor.
|
|
||||||
* $data for example ['event'=>'ping', 'data' => 'some thing', 'id' => 1000, 'retry' => 5000]
|
|
||||||
* @param array $data
|
|
||||||
*/
|
|
||||||
public function __construct(array $data)
|
|
||||||
{
|
|
||||||
$this->_data = $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __toString.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
$buffer = '';
|
|
||||||
$data = $this->_data;
|
|
||||||
if (isset($data[''])) {
|
|
||||||
$buffer = ": {$data['']}\n";
|
|
||||||
}
|
|
||||||
if (isset($data['event'])) {
|
|
||||||
$buffer .= "event: {$data['event']}\n";
|
|
||||||
}
|
|
||||||
if (isset($data['id'])) {
|
|
||||||
$buffer .= "id: {$data['id']}\n";
|
|
||||||
}
|
|
||||||
if (isset($data['retry'])) {
|
|
||||||
$buffer .= "retry: {$data['retry']}\n";
|
|
||||||
}
|
|
||||||
if (isset($data['data'])) {
|
|
||||||
$buffer .= 'data: ' . str_replace("\n", "\ndata: ", $data['data']) . "\n";
|
|
||||||
}
|
|
||||||
return $buffer . "\n";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,461 +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 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 Workerman\Protocols\Http;
|
|
||||||
|
|
||||||
use Workerman\Protocols\Http\Session\SessionHandlerInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class Session
|
|
||||||
* @package Workerman\Protocols\Http
|
|
||||||
*/
|
|
||||||
class Session
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Session andler class which implements SessionHandlerInterface.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected static $_handlerClass = 'Workerman\Protocols\Http\Session\FileSessionHandler';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parameters of __constructor for session handler class.
|
|
||||||
*
|
|
||||||
* @var null
|
|
||||||
*/
|
|
||||||
protected static $_handlerConfig = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session name.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public static $name = 'PHPSID';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto update timestamp.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
public static $autoUpdateTimestamp = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session lifetime.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public static $lifetime = 1440;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cookie lifetime.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public static $cookieLifetime = 1440;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session cookie path.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public static $cookiePath = '/';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session cookie domain.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public static $domain = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTTPS only cookies.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
public static $secure = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTTP access only.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
public static $httpOnly = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Same-site cookies.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public static $sameSite = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gc probability.
|
|
||||||
*
|
|
||||||
* @var int[]
|
|
||||||
*/
|
|
||||||
public static $gcProbability = [1, 1000];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session handler instance.
|
|
||||||
*
|
|
||||||
* @var SessionHandlerInterface
|
|
||||||
*/
|
|
||||||
protected static $_handler = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session data.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_data = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session changed and need to save.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $_needSave = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session id.
|
|
||||||
*
|
|
||||||
* @var null
|
|
||||||
*/
|
|
||||||
protected $_sessionId = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is safe.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $_isSafe = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session constructor.
|
|
||||||
*
|
|
||||||
* @param string $session_id
|
|
||||||
*/
|
|
||||||
public function __construct($session_id)
|
|
||||||
{
|
|
||||||
static::checkSessionId($session_id);
|
|
||||||
if (static::$_handler === null) {
|
|
||||||
static::initHandler();
|
|
||||||
}
|
|
||||||
$this->_sessionId = $session_id;
|
|
||||||
if ($data = static::$_handler->read($session_id)) {
|
|
||||||
$this->_data = \unserialize($data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get session id.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getId()
|
|
||||||
{
|
|
||||||
return $this->_sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get session.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param mixed|null $default
|
|
||||||
* @return mixed|null
|
|
||||||
*/
|
|
||||||
public function get($name, $default = null)
|
|
||||||
{
|
|
||||||
return isset($this->_data[$name]) ? $this->_data[$name] : $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store data in the session.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param mixed $value
|
|
||||||
*/
|
|
||||||
public function set($name, $value)
|
|
||||||
{
|
|
||||||
$this->_data[$name] = $value;
|
|
||||||
$this->_needSave = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an item from the session.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
*/
|
|
||||||
public function delete($name)
|
|
||||||
{
|
|
||||||
unset($this->_data[$name]);
|
|
||||||
$this->_needSave = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve and delete an item from the session.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param mixed|null $default
|
|
||||||
* @return mixed|null
|
|
||||||
*/
|
|
||||||
public function pull($name, $default = null)
|
|
||||||
{
|
|
||||||
$value = $this->get($name, $default);
|
|
||||||
$this->delete($name);
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store data in the session.
|
|
||||||
*
|
|
||||||
* @param string|array $key
|
|
||||||
* @param mixed|null $value
|
|
||||||
*/
|
|
||||||
public function put($key, $value = null)
|
|
||||||
{
|
|
||||||
if (!\is_array($key)) {
|
|
||||||
$this->set($key, $value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($key as $k => $v) {
|
|
||||||
$this->_data[$k] = $v;
|
|
||||||
}
|
|
||||||
$this->_needSave = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a piece of data from the session.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
*/
|
|
||||||
public function forget($name)
|
|
||||||
{
|
|
||||||
if (\is_scalar($name)) {
|
|
||||||
$this->delete($name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (\is_array($name)) {
|
|
||||||
foreach ($name as $key) {
|
|
||||||
unset($this->_data[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->_needSave = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve all the data in the session.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function all()
|
|
||||||
{
|
|
||||||
return $this->_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all data from the session.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function flush()
|
|
||||||
{
|
|
||||||
$this->_needSave = true;
|
|
||||||
$this->_data = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determining If An Item Exists In The Session.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function has($name)
|
|
||||||
{
|
|
||||||
return isset($this->_data[$name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To determine if an item is present in the session, even if its value is null.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function exists($name)
|
|
||||||
{
|
|
||||||
return \array_key_exists($name, $this->_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save session to store.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function save()
|
|
||||||
{
|
|
||||||
if ($this->_needSave) {
|
|
||||||
if (empty($this->_data)) {
|
|
||||||
static::$_handler->destroy($this->_sessionId);
|
|
||||||
} else {
|
|
||||||
static::$_handler->write($this->_sessionId, \serialize($this->_data));
|
|
||||||
}
|
|
||||||
} elseif (static::$autoUpdateTimestamp) {
|
|
||||||
static::refresh();
|
|
||||||
}
|
|
||||||
$this->_needSave = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh session expire time.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function refresh()
|
|
||||||
{
|
|
||||||
static::$_handler->updateTimestamp($this->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function init()
|
|
||||||
{
|
|
||||||
if (($gc_probability = (int)\ini_get('session.gc_probability')) && ($gc_divisor = (int)\ini_get('session.gc_divisor'))) {
|
|
||||||
static::$gcProbability = [$gc_probability, $gc_divisor];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($gc_max_life_time = \ini_get('session.gc_maxlifetime')) {
|
|
||||||
self::$lifetime = (int)$gc_max_life_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
$session_cookie_params = \session_get_cookie_params();
|
|
||||||
static::$cookieLifetime = $session_cookie_params['lifetime'];
|
|
||||||
static::$cookiePath = $session_cookie_params['path'];
|
|
||||||
static::$domain = $session_cookie_params['domain'];
|
|
||||||
static::$secure = $session_cookie_params['secure'];
|
|
||||||
static::$httpOnly = $session_cookie_params['httponly'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set session handler class.
|
|
||||||
*
|
|
||||||
* @param mixed|null $class_name
|
|
||||||
* @param mixed|null $config
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function handlerClass($class_name = null, $config = null)
|
|
||||||
{
|
|
||||||
if ($class_name) {
|
|
||||||
static::$_handlerClass = $class_name;
|
|
||||||
}
|
|
||||||
if ($config) {
|
|
||||||
static::$_handlerConfig = $config;
|
|
||||||
}
|
|
||||||
return static::$_handlerClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get cookie params.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function getCookieParams()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'lifetime' => static::$cookieLifetime,
|
|
||||||
'path' => static::$cookiePath,
|
|
||||||
'domain' => static::$domain,
|
|
||||||
'secure' => static::$secure,
|
|
||||||
'httponly' => static::$httpOnly,
|
|
||||||
'samesite' => static::$sameSite,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init handler.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected static function initHandler()
|
|
||||||
{
|
|
||||||
if (static::$_handlerConfig === null) {
|
|
||||||
static::$_handler = new static::$_handlerClass();
|
|
||||||
} else {
|
|
||||||
static::$_handler = new static::$_handlerClass(static::$_handlerConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GC sessions.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function gc()
|
|
||||||
{
|
|
||||||
static::$_handler->gc(static::$lifetime);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __wakeup.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __wakeup()
|
|
||||||
{
|
|
||||||
$this->_isSafe = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __destruct.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __destruct()
|
|
||||||
{
|
|
||||||
if (!$this->_isSafe) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->save();
|
|
||||||
if (\random_int(1, static::$gcProbability[1]) <= static::$gcProbability[0]) {
|
|
||||||
$this->gc();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check session id.
|
|
||||||
*
|
|
||||||
* @param string $session_id
|
|
||||||
*/
|
|
||||||
protected static function checkSessionId($session_id)
|
|
||||||
{
|
|
||||||
if (!\preg_match('/^[a-zA-Z0-9"]+$/', $session_id)) {
|
|
||||||
throw new SessionException("session_id $session_id is invalid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class SessionException
|
|
||||||
* @package Workerman\Protocols\Http
|
|
||||||
*/
|
|
||||||
class SessionException extends \RuntimeException
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init session.
|
|
||||||
Session::init();
|
|
@ -1,183 +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 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 Workerman\Protocols\Http\Session;
|
|
||||||
|
|
||||||
use Workerman\Protocols\Http\Session;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class FileSessionHandler
|
|
||||||
* @package Workerman\Protocols\Http\Session
|
|
||||||
*/
|
|
||||||
class FileSessionHandler implements SessionHandlerInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Session save path.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected static $_sessionSavePath = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session file prefix.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected static $_sessionFilePrefix = 'session_';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init.
|
|
||||||
*/
|
|
||||||
public static function init() {
|
|
||||||
$save_path = @\session_save_path();
|
|
||||||
if (!$save_path || \strpos($save_path, 'tcp://') === 0) {
|
|
||||||
$save_path = \sys_get_temp_dir();
|
|
||||||
}
|
|
||||||
static::sessionSavePath($save_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FileSessionHandler constructor.
|
|
||||||
* @param array $config
|
|
||||||
*/
|
|
||||||
public function __construct($config = array()) {
|
|
||||||
if (isset($config['save_path'])) {
|
|
||||||
static::sessionSavePath($config['save_path']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function open($save_path, $name)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function read($session_id)
|
|
||||||
{
|
|
||||||
$session_file = static::sessionFile($session_id);
|
|
||||||
\clearstatcache();
|
|
||||||
if (\is_file($session_file)) {
|
|
||||||
if (\time() - \filemtime($session_file) > Session::$lifetime) {
|
|
||||||
\unlink($session_file);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
$data = \file_get_contents($session_file);
|
|
||||||
return $data ? $data : '';
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function write($session_id, $session_data)
|
|
||||||
{
|
|
||||||
$temp_file = static::$_sessionSavePath . uniqid(bin2hex(random_bytes(8)), true);
|
|
||||||
if (!\file_put_contents($temp_file, $session_data)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return \rename($temp_file, static::sessionFile($session_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update sesstion modify time.
|
|
||||||
*
|
|
||||||
* @see https://www.php.net/manual/en/class.sessionupdatetimestamphandlerinterface.php
|
|
||||||
* @see https://www.php.net/manual/zh/function.touch.php
|
|
||||||
*
|
|
||||||
* @param string $id Session id.
|
|
||||||
* @param string $data Session Data.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function updateTimestamp($id, $data = "")
|
|
||||||
{
|
|
||||||
$session_file = static::sessionFile($id);
|
|
||||||
if (!file_exists($session_file)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// set file modify time to current time
|
|
||||||
$set_modify_time = \touch($session_file);
|
|
||||||
// clear file stat cache
|
|
||||||
\clearstatcache();
|
|
||||||
return $set_modify_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function close()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function destroy($session_id)
|
|
||||||
{
|
|
||||||
$session_file = static::sessionFile($session_id);
|
|
||||||
if (\is_file($session_file)) {
|
|
||||||
\unlink($session_file);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function gc($maxlifetime) {
|
|
||||||
$time_now = \time();
|
|
||||||
foreach (\glob(static::$_sessionSavePath . static::$_sessionFilePrefix . '*') as $file) {
|
|
||||||
if(\is_file($file) && $time_now - \filemtime($file) > $maxlifetime) {
|
|
||||||
\unlink($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get session file path.
|
|
||||||
*
|
|
||||||
* @param string $session_id
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected static function sessionFile($session_id) {
|
|
||||||
return static::$_sessionSavePath.static::$_sessionFilePrefix.$session_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get or set session file path.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function sessionSavePath($path) {
|
|
||||||
if ($path) {
|
|
||||||
if ($path[\strlen($path)-1] !== DIRECTORY_SEPARATOR) {
|
|
||||||
$path .= DIRECTORY_SEPARATOR;
|
|
||||||
}
|
|
||||||
static::$_sessionSavePath = $path;
|
|
||||||
if (!\is_dir($path)) {
|
|
||||||
\mkdir($path, 0777, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSessionHandler::init();
|
|
@ -1,46 +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 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 Workerman\Protocols\Http\Session;
|
|
||||||
|
|
||||||
use Workerman\Protocols\Http\Session;
|
|
||||||
|
|
||||||
class RedisClusterSessionHandler extends RedisSessionHandler
|
|
||||||
{
|
|
||||||
public function __construct($config)
|
|
||||||
{
|
|
||||||
$timeout = isset($config['timeout']) ? $config['timeout'] : 2;
|
|
||||||
$read_timeout = isset($config['read_timeout']) ? $config['read_timeout'] : $timeout;
|
|
||||||
$persistent = isset($config['persistent']) ? $config['persistent'] : false;
|
|
||||||
$auth = isset($config['auth']) ? $config['auth'] : '';
|
|
||||||
if ($auth) {
|
|
||||||
$this->_redis = new \RedisCluster(null, $config['host'], $timeout, $read_timeout, $persistent, $auth);
|
|
||||||
} else {
|
|
||||||
$this->_redis = new \RedisCluster(null, $config['host'], $timeout, $read_timeout, $persistent);
|
|
||||||
}
|
|
||||||
if (empty($config['prefix'])) {
|
|
||||||
$config['prefix'] = 'redis_session_';
|
|
||||||
}
|
|
||||||
$this->_redis->setOption(\Redis::OPT_PREFIX, $config['prefix']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function read($session_id)
|
|
||||||
{
|
|
||||||
return $this->_redis->get($session_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,154 +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 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 Workerman\Protocols\Http\Session;
|
|
||||||
|
|
||||||
use Workerman\Protocols\Http\Session;
|
|
||||||
use Workerman\Timer;
|
|
||||||
use RedisException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class RedisSessionHandler
|
|
||||||
* @package Workerman\Protocols\Http\Session
|
|
||||||
*/
|
|
||||||
class RedisSessionHandler implements SessionHandlerInterface
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Redis
|
|
||||||
*/
|
|
||||||
protected $_redis;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $_config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RedisSessionHandler constructor.
|
|
||||||
* @param array $config = [
|
|
||||||
* 'host' => '127.0.0.1',
|
|
||||||
* 'port' => 6379,
|
|
||||||
* 'timeout' => 2,
|
|
||||||
* 'auth' => '******',
|
|
||||||
* 'database' => 2,
|
|
||||||
* 'prefix' => 'redis_session_',
|
|
||||||
* 'ping' => 55,
|
|
||||||
* ]
|
|
||||||
*/
|
|
||||||
public function __construct($config)
|
|
||||||
{
|
|
||||||
if (false === extension_loaded('redis')) {
|
|
||||||
throw new \RuntimeException('Please install redis extension.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($config['timeout'])) {
|
|
||||||
$config['timeout'] = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_config = $config;
|
|
||||||
|
|
||||||
$this->connect();
|
|
||||||
|
|
||||||
Timer::add(!empty($config['ping']) ? $config['ping'] : 55, function () {
|
|
||||||
$this->_redis->get('ping');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function connect()
|
|
||||||
{
|
|
||||||
$config = $this->_config;
|
|
||||||
|
|
||||||
$this->_redis = new \Redis();
|
|
||||||
if (false === $this->_redis->connect($config['host'], $config['port'], $config['timeout'])) {
|
|
||||||
throw new \RuntimeException("Redis connect {$config['host']}:{$config['port']} fail.");
|
|
||||||
}
|
|
||||||
if (!empty($config['auth'])) {
|
|
||||||
$this->_redis->auth($config['auth']);
|
|
||||||
}
|
|
||||||
if (!empty($config['database'])) {
|
|
||||||
$this->_redis->select($config['database']);
|
|
||||||
}
|
|
||||||
if (empty($config['prefix'])) {
|
|
||||||
$config['prefix'] = 'redis_session_';
|
|
||||||
}
|
|
||||||
$this->_redis->setOption(\Redis::OPT_PREFIX, $config['prefix']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function open($save_path, $name)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function read($session_id)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return $this->_redis->get($session_id);
|
|
||||||
} catch (RedisException $e) {
|
|
||||||
$msg = strtolower($e->getMessage());
|
|
||||||
if ($msg === 'connection lost' || strpos($msg, 'went away')) {
|
|
||||||
$this->connect();
|
|
||||||
return $this->_redis->get($session_id);
|
|
||||||
}
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function write($session_id, $session_data)
|
|
||||||
{
|
|
||||||
return true === $this->_redis->setex($session_id, Session::$lifetime, $session_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function updateTimestamp($id, $data = "")
|
|
||||||
{
|
|
||||||
return true === $this->_redis->expire($id, Session::$lifetime);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function destroy($session_id)
|
|
||||||
{
|
|
||||||
$this->_redis->del($session_id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function close()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function gc($maxlifetime)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,114 +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 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 Workerman\Protocols\Http\Session;
|
|
||||||
|
|
||||||
interface SessionHandlerInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Close the session
|
|
||||||
* @link http://php.net/manual/en/sessionhandlerinterface.close.php
|
|
||||||
* @return bool <p>
|
|
||||||
* The return value (usually TRUE on success, FALSE on failure).
|
|
||||||
* Note this value is returned internally to PHP for processing.
|
|
||||||
* </p>
|
|
||||||
* @since 5.4.0
|
|
||||||
*/
|
|
||||||
public function close();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy a session
|
|
||||||
* @link http://php.net/manual/en/sessionhandlerinterface.destroy.php
|
|
||||||
* @param string $session_id The session ID being destroyed.
|
|
||||||
* @return bool <p>
|
|
||||||
* The return value (usually TRUE on success, FALSE on failure).
|
|
||||||
* Note this value is returned internally to PHP for processing.
|
|
||||||
* </p>
|
|
||||||
* @since 5.4.0
|
|
||||||
*/
|
|
||||||
public function destroy($session_id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleanup old sessions
|
|
||||||
* @link http://php.net/manual/en/sessionhandlerinterface.gc.php
|
|
||||||
* @param int $maxlifetime <p>
|
|
||||||
* Sessions that have not updated for
|
|
||||||
* the last maxlifetime seconds will be removed.
|
|
||||||
* </p>
|
|
||||||
* @return bool <p>
|
|
||||||
* The return value (usually TRUE on success, FALSE on failure).
|
|
||||||
* Note this value is returned internally to PHP for processing.
|
|
||||||
* </p>
|
|
||||||
* @since 5.4.0
|
|
||||||
*/
|
|
||||||
public function gc($maxlifetime);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize session
|
|
||||||
* @link http://php.net/manual/en/sessionhandlerinterface.open.php
|
|
||||||
* @param string $save_path The path where to store/retrieve the session.
|
|
||||||
* @param string $name The session name.
|
|
||||||
* @return bool <p>
|
|
||||||
* The return value (usually TRUE on success, FALSE on failure).
|
|
||||||
* Note this value is returned internally to PHP for processing.
|
|
||||||
* </p>
|
|
||||||
* @since 5.4.0
|
|
||||||
*/
|
|
||||||
public function open($save_path, $name);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read session data
|
|
||||||
* @link http://php.net/manual/en/sessionhandlerinterface.read.php
|
|
||||||
* @param string $session_id The session id to read data for.
|
|
||||||
* @return string <p>
|
|
||||||
* Returns an encoded string of the read data.
|
|
||||||
* If nothing was read, it must return an empty string.
|
|
||||||
* Note this value is returned internally to PHP for processing.
|
|
||||||
* </p>
|
|
||||||
* @since 5.4.0
|
|
||||||
*/
|
|
||||||
public function read($session_id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write session data
|
|
||||||
* @link http://php.net/manual/en/sessionhandlerinterface.write.php
|
|
||||||
* @param string $session_id The session id.
|
|
||||||
* @param string $session_data <p>
|
|
||||||
* The encoded session data. This data is the
|
|
||||||
* result of the PHP internally encoding
|
|
||||||
* the $_SESSION superglobal to a serialized
|
|
||||||
* string and passing it as this parameter.
|
|
||||||
* Please note sessions use an alternative serialization method.
|
|
||||||
* </p>
|
|
||||||
* @return bool <p>
|
|
||||||
* The return value (usually TRUE on success, FALSE on failure).
|
|
||||||
* Note this value is returned internally to PHP for processing.
|
|
||||||
* </p>
|
|
||||||
* @since 5.4.0
|
|
||||||
*/
|
|
||||||
public function write($session_id, $session_data);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update sesstion modify time.
|
|
||||||
*
|
|
||||||
* @see https://www.php.net/manual/en/class.sessionupdatetimestamphandlerinterface.php
|
|
||||||
*
|
|
||||||
* @param string $id Session id.
|
|
||||||
* @param string $data Session Data.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function updateTimestamp($id, $data = "");
|
|
||||||
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
|
|
||||||
types {
|
|
||||||
text/html html htm shtml;
|
|
||||||
text/css css;
|
|
||||||
text/xml xml;
|
|
||||||
image/gif gif;
|
|
||||||
image/jpeg jpeg jpg;
|
|
||||||
application/javascript js;
|
|
||||||
application/atom+xml atom;
|
|
||||||
application/rss+xml rss;
|
|
||||||
|
|
||||||
text/mathml mml;
|
|
||||||
text/plain txt;
|
|
||||||
text/vnd.sun.j2me.app-descriptor jad;
|
|
||||||
text/vnd.wap.wml wml;
|
|
||||||
text/x-component htc;
|
|
||||||
|
|
||||||
image/png png;
|
|
||||||
image/tiff tif tiff;
|
|
||||||
image/vnd.wap.wbmp wbmp;
|
|
||||||
image/x-icon ico;
|
|
||||||
image/x-jng jng;
|
|
||||||
image/x-ms-bmp bmp;
|
|
||||||
image/svg+xml svg svgz;
|
|
||||||
image/webp webp;
|
|
||||||
|
|
||||||
application/font-woff woff;
|
|
||||||
application/java-archive jar war ear;
|
|
||||||
application/json json;
|
|
||||||
application/mac-binhex40 hqx;
|
|
||||||
application/msword doc;
|
|
||||||
application/pdf pdf;
|
|
||||||
application/postscript ps eps ai;
|
|
||||||
application/rtf rtf;
|
|
||||||
application/vnd.apple.mpegurl m3u8;
|
|
||||||
application/vnd.ms-excel xls;
|
|
||||||
application/vnd.ms-fontobject eot;
|
|
||||||
application/vnd.ms-powerpoint ppt;
|
|
||||||
application/vnd.wap.wmlc wmlc;
|
|
||||||
application/vnd.google-earth.kml+xml kml;
|
|
||||||
application/vnd.google-earth.kmz kmz;
|
|
||||||
application/x-7z-compressed 7z;
|
|
||||||
application/x-cocoa cco;
|
|
||||||
application/x-java-archive-diff jardiff;
|
|
||||||
application/x-java-jnlp-file jnlp;
|
|
||||||
application/x-makeself run;
|
|
||||||
application/x-perl pl pm;
|
|
||||||
application/x-pilot prc pdb;
|
|
||||||
application/x-rar-compressed rar;
|
|
||||||
application/x-redhat-package-manager rpm;
|
|
||||||
application/x-sea sea;
|
|
||||||
application/x-shockwave-flash swf;
|
|
||||||
application/x-stuffit sit;
|
|
||||||
application/x-tcl tcl tk;
|
|
||||||
application/x-x509-ca-cert der pem crt;
|
|
||||||
application/x-xpinstall xpi;
|
|
||||||
application/xhtml+xml xhtml;
|
|
||||||
application/xspf+xml xspf;
|
|
||||||
application/zip zip;
|
|
||||||
|
|
||||||
application/octet-stream bin exe dll;
|
|
||||||
application/octet-stream deb;
|
|
||||||
application/octet-stream dmg;
|
|
||||||
application/octet-stream iso img;
|
|
||||||
application/octet-stream msi msp msm;
|
|
||||||
|
|
||||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
|
|
||||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
|
|
||||||
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
|
|
||||||
|
|
||||||
audio/midi mid midi kar;
|
|
||||||
audio/mpeg mp3;
|
|
||||||
audio/ogg ogg;
|
|
||||||
audio/x-m4a m4a;
|
|
||||||
audio/x-realaudio ra;
|
|
||||||
|
|
||||||
video/3gpp 3gpp 3gp;
|
|
||||||
video/mp2t ts;
|
|
||||||
video/mp4 mp4;
|
|
||||||
video/mpeg mpeg mpg;
|
|
||||||
video/quicktime mov;
|
|
||||||
video/webm webm;
|
|
||||||
video/x-flv flv;
|
|
||||||
video/x-m4v m4v;
|
|
||||||
video/x-mng mng;
|
|
||||||
video/x-ms-asf asx asf;
|
|
||||||
video/x-ms-wmv wmv;
|
|
||||||
video/x-msvideo avi;
|
|
||||||
font/ttf ttf;
|
|
||||||
}
|
|
@ -1,52 +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 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 Workerman\Protocols;
|
|
||||||
|
|
||||||
use Workerman\Connection\ConnectionInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Protocol interface
|
|
||||||
*/
|
|
||||||
interface ProtocolInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Check the integrity of the package.
|
|
||||||
* Please return the length of package.
|
|
||||||
* If length is unknow please return 0 that mean wating more data.
|
|
||||||
* If the package has something wrong please return false the connection will be closed.
|
|
||||||
*
|
|
||||||
* @param string $recv_buffer
|
|
||||||
* @param ConnectionInterface $connection
|
|
||||||
* @return int|false
|
|
||||||
*/
|
|
||||||
public static function input($recv_buffer, ConnectionInterface $connection);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode package and emit onMessage($message) callback, $message is the result that decode returned.
|
|
||||||
*
|
|
||||||
* @param string $recv_buffer
|
|
||||||
* @param ConnectionInterface $connection
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public static function decode($recv_buffer, ConnectionInterface $connection);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode package brefore sending to client.
|
|
||||||
*
|
|
||||||
* @param mixed $data
|
|
||||||
* @param ConnectionInterface $connection
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function encode($data, ConnectionInterface $connection);
|
|
||||||
}
|
|
70
vendor/workerman/workerman/Protocols/Text.php
vendored
70
vendor/workerman/workerman/Protocols/Text.php
vendored
@ -1,70 +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 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 Workerman\Protocols;
|
|
||||||
|
|
||||||
use Workerman\Connection\ConnectionInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Text Protocol.
|
|
||||||
*/
|
|
||||||
class Text
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Check the integrity of the package.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @param ConnectionInterface $connection
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public static function input($buffer, ConnectionInterface $connection)
|
|
||||||
{
|
|
||||||
// Judge whether the package length exceeds the limit.
|
|
||||||
if (isset($connection->maxPackageSize) && \strlen($buffer) >= $connection->maxPackageSize) {
|
|
||||||
$connection->close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Find the position of "\n".
|
|
||||||
$pos = \strpos($buffer, "\n");
|
|
||||||
// No "\n", packet length is unknown, continue to wait for the data so return 0.
|
|
||||||
if ($pos === false) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Return the current package length.
|
|
||||||
return $pos + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function encode($buffer)
|
|
||||||
{
|
|
||||||
// Add "\n"
|
|
||||||
return $buffer . "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function decode($buffer)
|
|
||||||
{
|
|
||||||
// Remove "\n"
|
|
||||||
return \rtrim($buffer, "\r\n");
|
|
||||||
}
|
|
||||||
}
|
|
564
vendor/workerman/workerman/Protocols/Websocket.php
vendored
564
vendor/workerman/workerman/Protocols/Websocket.php
vendored
@ -1,564 +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 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 Workerman\Protocols;
|
|
||||||
|
|
||||||
use Workerman\Connection\ConnectionInterface;
|
|
||||||
use Workerman\Connection\TcpConnection;
|
|
||||||
use Workerman\Protocols\Http\Request;
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebSocket protocol.
|
|
||||||
*/
|
|
||||||
class Websocket implements \Workerman\Protocols\ProtocolInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Websocket blob type.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
const BINARY_TYPE_BLOB = "\x81";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Websocket blob type.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
const BINARY_TYPE_BLOB_DEFLATE = "\xc1";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Websocket arraybuffer type.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
const BINARY_TYPE_ARRAYBUFFER = "\x82";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Websocket arraybuffer type.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
const BINARY_TYPE_ARRAYBUFFER_DEFLATE = "\xc2";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the integrity of the package.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @param ConnectionInterface $connection
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public static function input($buffer, ConnectionInterface $connection)
|
|
||||||
{
|
|
||||||
// Receive length.
|
|
||||||
$recv_len = \strlen($buffer);
|
|
||||||
// We need more data.
|
|
||||||
if ($recv_len < 6) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has not yet completed the handshake.
|
|
||||||
if (empty($connection->context->websocketHandshake)) {
|
|
||||||
return static::dealHandshake($buffer, $connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buffer websocket frame data.
|
|
||||||
if ($connection->context->websocketCurrentFrameLength) {
|
|
||||||
// We need more frame data.
|
|
||||||
if ($connection->context->websocketCurrentFrameLength > $recv_len) {
|
|
||||||
// Return 0, because it is not clear the full packet length, waiting for the frame of fin=1.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$first_byte = \ord($buffer[0]);
|
|
||||||
$second_byte = \ord($buffer[1]);
|
|
||||||
$data_len = $second_byte & 127;
|
|
||||||
$is_fin_frame = $first_byte >> 7;
|
|
||||||
$masked = $second_byte >> 7;
|
|
||||||
|
|
||||||
if (!$masked) {
|
|
||||||
Worker::safeEcho("frame not masked so close the connection\n");
|
|
||||||
$connection->close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$opcode = $first_byte & 0xf;
|
|
||||||
switch ($opcode) {
|
|
||||||
case 0x0:
|
|
||||||
break;
|
|
||||||
// Blob type.
|
|
||||||
case 0x1:
|
|
||||||
break;
|
|
||||||
// Arraybuffer type.
|
|
||||||
case 0x2:
|
|
||||||
break;
|
|
||||||
// Close package.
|
|
||||||
case 0x8:
|
|
||||||
// Try to emit onWebSocketClose callback.
|
|
||||||
$close_cb = $connection->onWebSocketClose ?? $connection->worker->onWebSocketClose ?? false;
|
|
||||||
if ($close_cb) {
|
|
||||||
try {
|
|
||||||
$close_cb($connection);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
} // Close connection.
|
|
||||||
else {
|
|
||||||
$connection->close("\x88\x02\x03\xe8", true);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
// Ping package.
|
|
||||||
case 0x9:
|
|
||||||
break;
|
|
||||||
// Pong package.
|
|
||||||
case 0xa:
|
|
||||||
break;
|
|
||||||
// Wrong opcode.
|
|
||||||
default :
|
|
||||||
Worker::safeEcho("error opcode $opcode and close websocket connection. Buffer:" . bin2hex($buffer) . "\n");
|
|
||||||
$connection->close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate packet length.
|
|
||||||
$head_len = 6;
|
|
||||||
if ($data_len === 126) {
|
|
||||||
$head_len = 8;
|
|
||||||
if ($head_len > $recv_len) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$pack = \unpack('nn/ntotal_len', $buffer);
|
|
||||||
$data_len = $pack['total_len'];
|
|
||||||
} else {
|
|
||||||
if ($data_len === 127) {
|
|
||||||
$head_len = 14;
|
|
||||||
if ($head_len > $recv_len) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$arr = \unpack('n/N2c', $buffer);
|
|
||||||
$data_len = $arr['c1'] * 4294967296 + $arr['c2'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$current_frame_length = $head_len + $data_len;
|
|
||||||
|
|
||||||
$total_package_size = \strlen($connection->context->websocketDataBuffer) + $current_frame_length;
|
|
||||||
if ($total_package_size > $connection->maxPackageSize) {
|
|
||||||
Worker::safeEcho("error package. package_length=$total_package_size\n");
|
|
||||||
$connection->close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($is_fin_frame) {
|
|
||||||
if ($opcode === 0x9) {
|
|
||||||
if ($recv_len >= $current_frame_length) {
|
|
||||||
$ping_data = static::decode(\substr($buffer, 0, $current_frame_length), $connection);
|
|
||||||
$connection->consumeRecvBuffer($current_frame_length);
|
|
||||||
$tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
|
|
||||||
$connection->websocketType = "\x8a";
|
|
||||||
$ping_cb = $connection->onWebSocketPing ?? $connection->worker->onWebSocketPing ?? false;
|
|
||||||
if ($ping_cb) {
|
|
||||||
try {
|
|
||||||
$ping_cb($connection, $ping_data);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$connection->send($ping_data);
|
|
||||||
}
|
|
||||||
$connection->websocketType = $tmp_connection_type;
|
|
||||||
if ($recv_len > $current_frame_length) {
|
|
||||||
return static::input(\substr($buffer, $current_frame_length), $connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
} else if ($opcode === 0xa) {
|
|
||||||
if ($recv_len >= $current_frame_length) {
|
|
||||||
$pong_data = static::decode(\substr($buffer, 0, $current_frame_length), $connection);
|
|
||||||
$connection->consumeRecvBuffer($current_frame_length);
|
|
||||||
$tmp_connection_type = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
|
|
||||||
$connection->websocketType = "\x8a";
|
|
||||||
// Try to emit onWebSocketPong callback.
|
|
||||||
$pong_cb = $connection->onWebSocketPong ?? $connection->worker->onWebSocketPong ?? false;
|
|
||||||
if ($pong_cb) {
|
|
||||||
try {
|
|
||||||
$pong_cb($connection, $pong_data);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$connection->websocketType = $tmp_connection_type;
|
|
||||||
if ($recv_len > $current_frame_length) {
|
|
||||||
return static::input(\substr($buffer, $current_frame_length), $connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return $current_frame_length;
|
|
||||||
} else {
|
|
||||||
$connection->context->websocketCurrentFrameLength = $current_frame_length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Received just a frame length data.
|
|
||||||
if ($connection->context->websocketCurrentFrameLength === $recv_len) {
|
|
||||||
static::decode($buffer, $connection);
|
|
||||||
$connection->consumeRecvBuffer($connection->context->websocketCurrentFrameLength);
|
|
||||||
$connection->context->websocketCurrentFrameLength = 0;
|
|
||||||
return 0;
|
|
||||||
} // The length of the received data is greater than the length of a frame.
|
|
||||||
elseif ($connection->context->websocketCurrentFrameLength < $recv_len) {
|
|
||||||
static::decode(\substr($buffer, 0, $connection->context->websocketCurrentFrameLength), $connection);
|
|
||||||
$connection->consumeRecvBuffer($connection->context->websocketCurrentFrameLength);
|
|
||||||
$current_frame_length = $connection->context->websocketCurrentFrameLength;
|
|
||||||
$connection->context->websocketCurrentFrameLength = 0;
|
|
||||||
// Continue to read next frame.
|
|
||||||
return static::input(\substr($buffer, $current_frame_length), $connection);
|
|
||||||
} // The length of the received data is less than the length of a frame.
|
|
||||||
else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Websocket encode.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @param ConnectionInterface $connection
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function encode($buffer, ConnectionInterface $connection)
|
|
||||||
{
|
|
||||||
if (!is_scalar($buffer)) {
|
|
||||||
throw new \Exception("You can't send(" . \gettype($buffer) . ") to client, you need to convert it to a string. ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($connection->websocketType)) {
|
|
||||||
$connection->websocketType = static::BINARY_TYPE_BLOB;
|
|
||||||
}
|
|
||||||
|
|
||||||
// permessage-deflate
|
|
||||||
if (\ord($connection->websocketType) & 64) {
|
|
||||||
$buffer = static::deflate($connection, $buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
$first_byte = $connection->websocketType;
|
|
||||||
$len = \strlen($buffer);
|
|
||||||
|
|
||||||
if ($len <= 125) {
|
|
||||||
$encode_buffer = $first_byte . \chr($len) . $buffer;
|
|
||||||
} else {
|
|
||||||
if ($len <= 65535) {
|
|
||||||
$encode_buffer = $first_byte . \chr(126) . \pack("n", $len) . $buffer;
|
|
||||||
} else {
|
|
||||||
$encode_buffer = $first_byte . \chr(127) . \pack("xxxxN", $len) . $buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handshake not completed so temporary buffer websocket data waiting for send.
|
|
||||||
if (empty($connection->context->websocketHandshake)) {
|
|
||||||
if (empty($connection->context->tmpWebsocketData)) {
|
|
||||||
$connection->context->tmpWebsocketData = '';
|
|
||||||
}
|
|
||||||
// If buffer has already full then discard the current package.
|
|
||||||
if (\strlen($connection->context->tmpWebsocketData) > $connection->maxSendBufferSize) {
|
|
||||||
if ($connection->onError) {
|
|
||||||
try {
|
|
||||||
($connection->onError)($connection, WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
$connection->context->tmpWebsocketData .= $encode_buffer;
|
|
||||||
// Check buffer is full.
|
|
||||||
if ($connection->maxSendBufferSize <= \strlen($connection->context->tmpWebsocketData)) {
|
|
||||||
if ($connection->onBufferFull) {
|
|
||||||
try {
|
|
||||||
($connection->onBufferFull)($connection);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Return empty string.
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $encode_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Websocket decode.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @param ConnectionInterface $connection
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function decode($buffer, ConnectionInterface $connection)
|
|
||||||
{
|
|
||||||
$first_byte = \ord($buffer[0]);
|
|
||||||
$second_byte = \ord($buffer[1]);
|
|
||||||
$len = $second_byte & 127;
|
|
||||||
$is_fin_frame = $first_byte >> 7;
|
|
||||||
$rsv1 = 64 === ($first_byte & 64);
|
|
||||||
|
|
||||||
if ($len === 126) {
|
|
||||||
$masks = \substr($buffer, 4, 4);
|
|
||||||
$data = \substr($buffer, 8);
|
|
||||||
} else {
|
|
||||||
if ($len === 127) {
|
|
||||||
$masks = \substr($buffer, 10, 4);
|
|
||||||
$data = \substr($buffer, 14);
|
|
||||||
} else {
|
|
||||||
$masks = \substr($buffer, 2, 4);
|
|
||||||
$data = \substr($buffer, 6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$dataLength = \strlen($data);
|
|
||||||
$masks = \str_repeat($masks, \floor($dataLength / 4)) . \substr($masks, 0, $dataLength % 4);
|
|
||||||
$decoded = $data ^ $masks;
|
|
||||||
if ($connection->context->websocketCurrentFrameLength) {
|
|
||||||
$connection->context->websocketDataBuffer .= $decoded;
|
|
||||||
if ($rsv1) {
|
|
||||||
return static::inflate($connection, $connection->context->websocketDataBuffer, $is_fin_frame);
|
|
||||||
}
|
|
||||||
return $connection->context->websocketDataBuffer;
|
|
||||||
} else {
|
|
||||||
if ($connection->context->websocketDataBuffer !== '') {
|
|
||||||
$decoded = $connection->context->websocketDataBuffer . $decoded;
|
|
||||||
$connection->context->websocketDataBuffer = '';
|
|
||||||
}
|
|
||||||
if ($rsv1) {
|
|
||||||
return static::inflate($connection, $decoded, $is_fin_frame);
|
|
||||||
}
|
|
||||||
return $decoded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inflate.
|
|
||||||
*
|
|
||||||
* @param $connection
|
|
||||||
* @param $buffer
|
|
||||||
* @param $is_fin_frame
|
|
||||||
* @return false|string
|
|
||||||
*/
|
|
||||||
protected static function inflate($connection, $buffer, $is_fin_frame)
|
|
||||||
{
|
|
||||||
if (!isset($connection->context->inflator)) {
|
|
||||||
$connection->context->inflator = \inflate_init(
|
|
||||||
\ZLIB_ENCODING_RAW,
|
|
||||||
[
|
|
||||||
'level' => -1,
|
|
||||||
'memory' => 8,
|
|
||||||
'window' => 9,
|
|
||||||
'strategy' => \ZLIB_DEFAULT_STRATEGY
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ($is_fin_frame) {
|
|
||||||
$buffer .= "\x00\x00\xff\xff";
|
|
||||||
}
|
|
||||||
return \inflate_add($connection->context->inflator, $buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deflate.
|
|
||||||
*
|
|
||||||
* @param $connection
|
|
||||||
* @param $buffer
|
|
||||||
* @return false|string
|
|
||||||
*/
|
|
||||||
protected static function deflate($connection, $buffer)
|
|
||||||
{
|
|
||||||
if (!isset($connection->context->deflator)) {
|
|
||||||
$connection->context->deflator = \deflate_init(
|
|
||||||
\ZLIB_ENCODING_RAW,
|
|
||||||
[
|
|
||||||
'level' => -1,
|
|
||||||
'memory' => 8,
|
|
||||||
'window' => 9,
|
|
||||||
'strategy' => \ZLIB_DEFAULT_STRATEGY
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return \substr(\deflate_add($connection->context->deflator, $buffer), 0, -4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Websocket handshake.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @param TcpConnection $connection
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public static function dealHandshake($buffer, $connection)
|
|
||||||
{
|
|
||||||
// HTTP protocol.
|
|
||||||
if (0 === \strpos($buffer, 'GET')) {
|
|
||||||
// Find \r\n\r\n.
|
|
||||||
$header_end_pos = \strpos($buffer, "\r\n\r\n");
|
|
||||||
if (!$header_end_pos) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$header_length = $header_end_pos + 4;
|
|
||||||
|
|
||||||
// Get Sec-WebSocket-Key.
|
|
||||||
$Sec_WebSocket_Key = '';
|
|
||||||
if (\preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match)) {
|
|
||||||
$Sec_WebSocket_Key = $match[1];
|
|
||||||
} else {
|
|
||||||
$connection->close("HTTP/1.1 200 WebSocket\r\nServer: workerman/" . Worker::VERSION . "\r\n\r\n<div style=\"text-align:center\"><h1>WebSocket</h1><hr>workerman/" . Worker::VERSION . "</div>",
|
|
||||||
true);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Calculation websocket key.
|
|
||||||
$new_key = \base64_encode(\sha1($Sec_WebSocket_Key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
|
|
||||||
// Handshake response data.
|
|
||||||
$handshake_message = "HTTP/1.1 101 Switching Protocols\r\n"
|
|
||||||
. "Upgrade: websocket\r\n"
|
|
||||||
. "Sec-WebSocket-Version: 13\r\n"
|
|
||||||
. "Connection: Upgrade\r\n"
|
|
||||||
. "Sec-WebSocket-Accept: " . $new_key . "\r\n";
|
|
||||||
|
|
||||||
// Websocket data buffer.
|
|
||||||
$connection->context->websocketDataBuffer = '';
|
|
||||||
// Current websocket frame length.
|
|
||||||
$connection->context->websocketCurrentFrameLength = 0;
|
|
||||||
// Current websocket frame data.
|
|
||||||
$connection->context->websocketCurrentFrameBuffer = '';
|
|
||||||
// Consume handshake data.
|
|
||||||
$connection->consumeRecvBuffer($header_length);
|
|
||||||
|
|
||||||
// Try to emit onWebSocketConnect callback.
|
|
||||||
$on_websocket_connect = $connection->onWebSocketConnect ?? $connection->worker->onWebSocketConnect ?? false;
|
|
||||||
if ($on_websocket_connect) {
|
|
||||||
static::parseHttpHeader($buffer);
|
|
||||||
try {
|
|
||||||
\call_user_func($on_websocket_connect, $connection, $buffer);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
} catch (\Error $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
if (!empty($_SESSION) && \class_exists('\GatewayWorker\Lib\Context')) {
|
|
||||||
$connection->session = \GatewayWorker\Lib\Context::sessionEncode($_SESSION);
|
|
||||||
}
|
|
||||||
$_GET = $_SERVER = $_SESSION = $_COOKIE = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
// blob or arraybuffer
|
|
||||||
if (empty($connection->websocketType)) {
|
|
||||||
$connection->websocketType = static::BINARY_TYPE_BLOB;
|
|
||||||
}
|
|
||||||
|
|
||||||
$has_server_header = false;
|
|
||||||
|
|
||||||
if (isset($connection->headers)) {
|
|
||||||
if (\is_array($connection->headers)) {
|
|
||||||
foreach ($connection->headers as $header) {
|
|
||||||
if (\stripos($header, 'Server:') === 0) {
|
|
||||||
$has_server_header = true;
|
|
||||||
}
|
|
||||||
$handshake_message .= "$header\r\n";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (\stripos($connection->headers, 'Server:') !== false) {
|
|
||||||
$has_server_header = true;
|
|
||||||
}
|
|
||||||
$handshake_message .= "$connection->headers\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!$has_server_header) {
|
|
||||||
$handshake_message .= "Server: workerman/" . Worker::VERSION . "\r\n";
|
|
||||||
}
|
|
||||||
$handshake_message .= "\r\n";
|
|
||||||
// Send handshake response.
|
|
||||||
$connection->send($handshake_message, true);
|
|
||||||
// Mark handshake complete..
|
|
||||||
$connection->context->websocketHandshake = true;
|
|
||||||
|
|
||||||
// There are data waiting to be sent.
|
|
||||||
if (!empty($connection->context->tmpWebsocketData)) {
|
|
||||||
$connection->send($connection->context->tmpWebsocketData, true);
|
|
||||||
$connection->context->tmpWebsocketData = '';
|
|
||||||
}
|
|
||||||
if (\strlen($buffer) > $header_length) {
|
|
||||||
return static::input(\substr($buffer, $header_length), $connection);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Bad websocket handshake request.
|
|
||||||
$connection->close("HTTP/1.1 200 WebSocket\r\nServer: workerman/" . Worker::VERSION . "\r\n\r\n<div style=\"text-align:center\"><h1>WebSocket</h1><hr>workerman/" . Worker::VERSION . "</div>",
|
|
||||||
true);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse http header.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected static function parseHttpHeader($buffer)
|
|
||||||
{
|
|
||||||
// Parse headers.
|
|
||||||
list($http_header, ) = \explode("\r\n\r\n", $buffer, 2);
|
|
||||||
$header_data = \explode("\r\n", $http_header);
|
|
||||||
|
|
||||||
if ($_SERVER) {
|
|
||||||
$_SERVER = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = \explode(' ',
|
|
||||||
$header_data[0]);
|
|
||||||
|
|
||||||
unset($header_data[0]);
|
|
||||||
foreach ($header_data as $content) {
|
|
||||||
// \r\n\r\n
|
|
||||||
if (empty($content)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
list($key, $value) = \explode(':', $content, 2);
|
|
||||||
$key = \str_replace('-', '_', \strtoupper($key));
|
|
||||||
$value = \trim($value);
|
|
||||||
$_SERVER['HTTP_' . $key] = $value;
|
|
||||||
switch ($key) {
|
|
||||||
// HTTP_HOST
|
|
||||||
case 'HOST':
|
|
||||||
$tmp = \explode(':', $value);
|
|
||||||
$_SERVER['SERVER_NAME'] = $tmp[0];
|
|
||||||
if (isset($tmp[1])) {
|
|
||||||
$_SERVER['SERVER_PORT'] = $tmp[1];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
// cookie
|
|
||||||
case 'COOKIE':
|
|
||||||
\parse_str(\str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// QUERY_STRING
|
|
||||||
$_SERVER['QUERY_STRING'] = \parse_url($_SERVER['REQUEST_URI'], \PHP_URL_QUERY);
|
|
||||||
if ($_SERVER['QUERY_STRING']) {
|
|
||||||
// $GET
|
|
||||||
\parse_str($_SERVER['QUERY_STRING'], $_GET);
|
|
||||||
} else {
|
|
||||||
$_SERVER['QUERY_STRING'] = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
432
vendor/workerman/workerman/Protocols/Ws.php
vendored
432
vendor/workerman/workerman/Protocols/Ws.php
vendored
@ -1,432 +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 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 Workerman\Protocols;
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
use Workerman\Timer;
|
|
||||||
use Workerman\Connection\TcpConnection;
|
|
||||||
use Workerman\Connection\ConnectionInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Websocket protocol for client.
|
|
||||||
*/
|
|
||||||
class Ws
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Websocket blob type.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
const BINARY_TYPE_BLOB = "\x81";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Websocket arraybuffer type.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
const BINARY_TYPE_ARRAYBUFFER = "\x82";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the integrity of the package.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @param ConnectionInterface $connection
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public static function input($buffer, ConnectionInterface $connection)
|
|
||||||
{
|
|
||||||
if (empty($connection->context->handshakeStep)) {
|
|
||||||
Worker::safeEcho("recv data before handshake. Buffer:" . \bin2hex($buffer) . "\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Recv handshake response
|
|
||||||
if ($connection->context->handshakeStep === 1) {
|
|
||||||
return self::dealHandshake($buffer, $connection);
|
|
||||||
}
|
|
||||||
$recvLen = \strlen($buffer);
|
|
||||||
if ($recvLen < 2) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Buffer websocket frame data.
|
|
||||||
if ($connection->context->websocketCurrentFrameLength) {
|
|
||||||
// We need more frame data.
|
|
||||||
if ($connection->context->websocketCurrentFrameLength > $recvLen) {
|
|
||||||
// Return 0, because it is not clear the full packet length, waiting for the frame of fin=1.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
$firstbyte = \ord($buffer[0]);
|
|
||||||
$secondbyte = \ord($buffer[1]);
|
|
||||||
$dataLen = $secondbyte & 127;
|
|
||||||
$isFinFrame = $firstbyte >> 7;
|
|
||||||
$masked = $secondbyte >> 7;
|
|
||||||
|
|
||||||
if ($masked) {
|
|
||||||
Worker::safeEcho("frame masked so close the connection\n");
|
|
||||||
$connection->close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$opcode = $firstbyte & 0xf;
|
|
||||||
|
|
||||||
switch ($opcode) {
|
|
||||||
case 0x0:
|
|
||||||
// Blob type.
|
|
||||||
case 0x1:
|
|
||||||
// Arraybuffer type.
|
|
||||||
case 0x2:
|
|
||||||
// Ping package.
|
|
||||||
case 0x9:
|
|
||||||
// Pong package.
|
|
||||||
case 0xa:
|
|
||||||
break;
|
|
||||||
// Close package.
|
|
||||||
case 0x8:
|
|
||||||
// Try to emit onWebSocketClose callback.
|
|
||||||
if (isset($connection->onWebSocketClose)) {
|
|
||||||
try {
|
|
||||||
($connection->onWebSocketClose)($connection);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
} // Close connection.
|
|
||||||
else {
|
|
||||||
$connection->close();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
// Wrong opcode.
|
|
||||||
default :
|
|
||||||
Worker::safeEcho("error opcode $opcode and close websocket connection. Buffer:" . $buffer . "\n");
|
|
||||||
$connection->close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Calculate packet length.
|
|
||||||
if ($dataLen === 126) {
|
|
||||||
if (\strlen($buffer) < 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$pack = \unpack('nn/ntotal_len', $buffer);
|
|
||||||
$currentFrameLength = $pack['total_len'] + 4;
|
|
||||||
} else if ($dataLen === 127) {
|
|
||||||
if (\strlen($buffer) < 10) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$arr = \unpack('n/N2c', $buffer);
|
|
||||||
$currentFrameLength = $arr['c1'] * 4294967296 + $arr['c2'] + 10;
|
|
||||||
} else {
|
|
||||||
$currentFrameLength = $dataLen + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
$totalPackageSize = \strlen($connection->context->websocketDataBuffer) + $currentFrameLength;
|
|
||||||
if ($totalPackageSize > $connection->maxPackageSize) {
|
|
||||||
Worker::safeEcho("error package. package_length=$totalPackageSize\n");
|
|
||||||
$connection->close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($isFinFrame) {
|
|
||||||
if ($opcode === 0x9) {
|
|
||||||
if ($recvLen >= $currentFrameLength) {
|
|
||||||
$pingData = static::decode(\substr($buffer, 0, $currentFrameLength), $connection);
|
|
||||||
$connection->consumeRecvBuffer($currentFrameLength);
|
|
||||||
$tmpConnectionType = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
|
|
||||||
$connection->websocketType = "\x8a";
|
|
||||||
if (isset($connection->onWebSocketPing)) {
|
|
||||||
try {
|
|
||||||
($connection->onWebSocketPing)($connection, $pingData);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$connection->send($pingData);
|
|
||||||
}
|
|
||||||
$connection->websocketType = $tmpConnectionType;
|
|
||||||
if ($recvLen > $currentFrameLength) {
|
|
||||||
return static::input(\substr($buffer, $currentFrameLength), $connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
} else if ($opcode === 0xa) {
|
|
||||||
if ($recvLen >= $currentFrameLength) {
|
|
||||||
$pongData = static::decode(\substr($buffer, 0, $currentFrameLength), $connection);
|
|
||||||
$connection->consumeRecvBuffer($currentFrameLength);
|
|
||||||
$tmpConnectionType = isset($connection->websocketType) ? $connection->websocketType : static::BINARY_TYPE_BLOB;
|
|
||||||
$connection->websocketType = "\x8a";
|
|
||||||
// Try to emit onWebSocketPong callback.
|
|
||||||
if (isset($connection->onWebSocketPong)) {
|
|
||||||
try {
|
|
||||||
($connection->onWebSocketPong)($connection, $pongData);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$connection->websocketType = $tmpConnectionType;
|
|
||||||
if ($recvLen > $currentFrameLength) {
|
|
||||||
return static::input(\substr($buffer, $currentFrameLength), $connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return $currentFrameLength;
|
|
||||||
} else {
|
|
||||||
$connection->context->websocketCurrentFrameLength = $currentFrameLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Received just a frame length data.
|
|
||||||
if ($connection->context->websocketCurrentFrameLength === $recvLen) {
|
|
||||||
self::decode($buffer, $connection);
|
|
||||||
$connection->consumeRecvBuffer($connection->context->websocketCurrentFrameLength);
|
|
||||||
$connection->context->websocketCurrentFrameLength = 0;
|
|
||||||
return 0;
|
|
||||||
} // The length of the received data is greater than the length of a frame.
|
|
||||||
elseif ($connection->context->websocketCurrentFrameLength < $recvLen) {
|
|
||||||
self::decode(\substr($buffer, 0, $connection->context->websocketCurrentFrameLength), $connection);
|
|
||||||
$connection->consumeRecvBuffer($connection->context->websocketCurrentFrameLength);
|
|
||||||
$currentFrameLength = $connection->context->websocketCurrentFrameLength;
|
|
||||||
$connection->context->websocketCurrentFrameLength = 0;
|
|
||||||
// Continue to read next frame.
|
|
||||||
return self::input(\substr($buffer, $currentFrameLength), $connection);
|
|
||||||
} // The length of the received data is less than the length of a frame.
|
|
||||||
else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Websocket encode.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @param ConnectionInterface $connection
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function encode($payload, ConnectionInterface $connection)
|
|
||||||
{
|
|
||||||
if (empty($connection->websocketType)) {
|
|
||||||
$connection->websocketType = self::BINARY_TYPE_BLOB;
|
|
||||||
}
|
|
||||||
$payload = (string)$payload;
|
|
||||||
if (empty($connection->context->handshakeStep)) {
|
|
||||||
static::sendHandshake($connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
$maskKey = "\x00\x00\x00\x00";
|
|
||||||
$length = \strlen($payload);
|
|
||||||
|
|
||||||
if (strlen($payload) < 126) {
|
|
||||||
$head = chr(0x80 | $length);
|
|
||||||
} elseif ($length < 0xFFFF) {
|
|
||||||
$head = chr(0x80 | 126) . pack("n", $length);
|
|
||||||
} else {
|
|
||||||
$head = chr(0x80 | 127) . pack("N", 0) . pack("N", $length);
|
|
||||||
}
|
|
||||||
|
|
||||||
$frame = $connection->websocketType . $head . $maskKey;
|
|
||||||
// append payload to frame:
|
|
||||||
$maskKey = \str_repeat($maskKey, \floor($length / 4)) . \substr($maskKey, 0, $length % 4);
|
|
||||||
$frame .= $payload ^ $maskKey;
|
|
||||||
if ($connection->context->handshakeStep === 1) {
|
|
||||||
// If buffer has already full then discard the current package.
|
|
||||||
if (\strlen($connection->context->tmpWebsocketData) > $connection->maxSendBufferSize) {
|
|
||||||
if ($connection->onError) {
|
|
||||||
try {
|
|
||||||
($connection->onError)($connection, WORKERMAN_SEND_FAIL, 'send buffer full and drop package');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
$connection->context->tmpWebsocketData = $connection->context->tmpWebsocketData . $frame;
|
|
||||||
// Check buffer is full.
|
|
||||||
if ($connection->maxSendBufferSize <= \strlen($connection->context->tmpWebsocketData)) {
|
|
||||||
if ($connection->onBufferFull) {
|
|
||||||
try {
|
|
||||||
($connection->onBufferFull)($connection);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return $frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Websocket decode.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @param ConnectionInterface $connection
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function decode($bytes, ConnectionInterface $connection)
|
|
||||||
{
|
|
||||||
$dataLength = \ord($bytes[1]);
|
|
||||||
|
|
||||||
if ($dataLength === 126) {
|
|
||||||
$decodedData = \substr($bytes, 4);
|
|
||||||
} else if ($dataLength === 127) {
|
|
||||||
$decodedData = \substr($bytes, 10);
|
|
||||||
} else {
|
|
||||||
$decodedData = \substr($bytes, 2);
|
|
||||||
}
|
|
||||||
if ($connection->context->websocketCurrentFrameLength) {
|
|
||||||
$connection->context->websocketDataBuffer .= $decodedData;
|
|
||||||
return $connection->context->websocketDataBuffer;
|
|
||||||
} else {
|
|
||||||
if ($connection->context->websocketDataBuffer !== '') {
|
|
||||||
$decodedData = $connection->context->websocketDataBuffer . $decodedData;
|
|
||||||
$connection->context->websocketDataBuffer = '';
|
|
||||||
}
|
|
||||||
return $decodedData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send websocket handshake data.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function onConnect($connection)
|
|
||||||
{
|
|
||||||
static::sendHandshake($connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clean
|
|
||||||
*
|
|
||||||
* @param TcpConnection $connection
|
|
||||||
*/
|
|
||||||
public static function onClose($connection)
|
|
||||||
{
|
|
||||||
$connection->context->handshakeStep = null;
|
|
||||||
$connection->context->websocketCurrentFrameLength = 0;
|
|
||||||
$connection->context->tmpWebsocketData = '';
|
|
||||||
$connection->context->websocketDataBuffer = '';
|
|
||||||
if (!empty($connection->context->websocketPingTimer)) {
|
|
||||||
Timer::del($connection->context->websocketPingTimer);
|
|
||||||
$connection->context->websocketPingTimer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send websocket handshake.
|
|
||||||
*
|
|
||||||
* @param TcpConnection $connection
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function sendHandshake(ConnectionInterface $connection)
|
|
||||||
{
|
|
||||||
if (!empty($connection->context->handshakeStep)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Get Host.
|
|
||||||
$port = $connection->getRemotePort();
|
|
||||||
$host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port;
|
|
||||||
// Handshake header.
|
|
||||||
$connection->context->websocketSecKey = \base64_encode(random_bytes(16));
|
|
||||||
$userHeader = $connection->headers ?? null;
|
|
||||||
$userHeaderStr = '';
|
|
||||||
if (!empty($userHeader)) {
|
|
||||||
if (\is_array($userHeader)) {
|
|
||||||
foreach ($userHeader as $k => $v) {
|
|
||||||
$userHeaderStr .= "$k: $v\r\n";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$userHeaderStr .= $userHeader;
|
|
||||||
}
|
|
||||||
$userHeaderStr = "\r\n" . \trim($userHeaderStr);
|
|
||||||
}
|
|
||||||
$header = 'GET ' . $connection->getRemoteURI() . " HTTP/1.1\r\n" .
|
|
||||||
(!\preg_match("/\nHost:/i", $userHeaderStr) ? "Host: $host\r\n" : '') .
|
|
||||||
"Connection: Upgrade\r\n" .
|
|
||||||
"Upgrade: websocket\r\n" .
|
|
||||||
(isset($connection->websocketOrigin) ? "Origin: " . $connection->websocketOrigin . "\r\n" : '') .
|
|
||||||
(isset($connection->websocketClientProtocol) ? "Sec-WebSocket-Protocol: " . $connection->websocketClientProtocol . "\r\n" : '') .
|
|
||||||
"Sec-WebSocket-Version: 13\r\n" .
|
|
||||||
"Sec-WebSocket-Key: " . $connection->context->websocketSecKey . $userHeaderStr . "\r\n\r\n";
|
|
||||||
$connection->send($header, true);
|
|
||||||
$connection->context->handshakeStep = 1;
|
|
||||||
$connection->context->websocketCurrentFrameLength = 0;
|
|
||||||
$connection->context->websocketDataBuffer = '';
|
|
||||||
$connection->context->tmpWebsocketData = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Websocket handshake.
|
|
||||||
*
|
|
||||||
* @param string $buffer
|
|
||||||
* @param TcpConnection $connection
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public static function dealHandshake($buffer, ConnectionInterface $connection)
|
|
||||||
{
|
|
||||||
$pos = \strpos($buffer, "\r\n\r\n");
|
|
||||||
if ($pos) {
|
|
||||||
//checking Sec-WebSocket-Accept
|
|
||||||
if (\preg_match("/Sec-WebSocket-Accept: *(.*?)\r\n/i", $buffer, $match)) {
|
|
||||||
if ($match[1] !== \base64_encode(\sha1($connection->context->websocketSecKey . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true))) {
|
|
||||||
Worker::safeEcho("Sec-WebSocket-Accept not match. Header:\n" . \substr($buffer, 0, $pos) . "\n");
|
|
||||||
$connection->close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Worker::safeEcho("Sec-WebSocket-Accept not found. Header:\n" . \substr($buffer, 0, $pos) . "\n");
|
|
||||||
$connection->close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handshake complete
|
|
||||||
|
|
||||||
// Get WebSocket subprotocol (if specified by server)
|
|
||||||
if (\preg_match("/Sec-WebSocket-Protocol: *(.*?)\r\n/i", $buffer, $match)) {
|
|
||||||
$connection->websocketServerProtocol = \trim($match[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$connection->context->handshakeStep = 2;
|
|
||||||
$handshakeResponseLength = $pos + 4;
|
|
||||||
// Try to emit onWebSocketConnect callback.
|
|
||||||
if (isset($connection->onWebSocketConnect)) {
|
|
||||||
try {
|
|
||||||
($connection->onWebSocketConnect)($connection, \substr($buffer, 0, $handshakeResponseLength));
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Worker::stopAll(250, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Headbeat.
|
|
||||||
if (!empty($connection->websocketPingInterval)) {
|
|
||||||
$connection->context->websocketPingTimer = Timer::add($connection->websocketPingInterval, function () use ($connection) {
|
|
||||||
if (false === $connection->send(\pack('H*', '898000000000'), true)) {
|
|
||||||
Timer::del($connection->context->websocketPingTimer);
|
|
||||||
$connection->context->websocketPingTimer = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$connection->consumeRecvBuffer($handshakeResponseLength);
|
|
||||||
if (!empty($connection->context->tmpWebsocketData)) {
|
|
||||||
$connection->send($connection->context->tmpWebsocketData, true);
|
|
||||||
$connection->context->tmpWebsocketData = '';
|
|
||||||
}
|
|
||||||
if (\strlen($buffer) > $handshakeResponseLength) {
|
|
||||||
return self::input(\substr($buffer, $handshakeResponseLength), $connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
342
vendor/workerman/workerman/README.md
vendored
342
vendor/workerman/workerman/README.md
vendored
@ -1,342 +0,0 @@
|
|||||||
# Workerman
|
|
||||||
[![Gitter](https://badges.gitter.im/walkor/Workerman.svg)](https://gitter.im/walkor/Workerman?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge)
|
|
||||||
[![Latest Stable Version](https://poser.pugx.org/workerman/workerman/v/stable)](https://packagist.org/packages/workerman/workerman)
|
|
||||||
[![Total Downloads](https://poser.pugx.org/workerman/workerman/downloads)](https://packagist.org/packages/workerman/workerman)
|
|
||||||
[![Monthly Downloads](https://poser.pugx.org/workerman/workerman/d/monthly)](https://packagist.org/packages/workerman/workerman)
|
|
||||||
[![Daily Downloads](https://poser.pugx.org/workerman/workerman/d/daily)](https://packagist.org/packages/workerman/workerman)
|
|
||||||
[![License](https://poser.pugx.org/workerman/workerman/license)](https://packagist.org/packages/workerman/workerman)
|
|
||||||
|
|
||||||
## What is it
|
|
||||||
Workerman is an asynchronous event-driven PHP framework with high performance to build fast and scalable network applications.
|
|
||||||
Workerman supports HTTP, Websocket, SSL and other custom protocols.
|
|
||||||
Workerman supports event extension.
|
|
||||||
|
|
||||||
## Requires
|
|
||||||
PHP 7.0 or Higher
|
|
||||||
A POSIX compatible operating system (Linux, OSX, BSD)
|
|
||||||
POSIX and PCNTL extensions required
|
|
||||||
Event extension recommended for better performance
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```
|
|
||||||
composer require workerman/workerman
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
### A websocket server
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
// Create a Websocket server
|
|
||||||
$ws_worker = new Worker('websocket://0.0.0.0:2346');
|
|
||||||
|
|
||||||
// Emitted when new connection come
|
|
||||||
$ws_worker->onConnect = function ($connection) {
|
|
||||||
echo "New connection\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
// Emitted when data received
|
|
||||||
$ws_worker->onMessage = function ($connection, $data) {
|
|
||||||
// Send hello $data
|
|
||||||
$connection->send('Hello ' . $data);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Emitted when connection closed
|
|
||||||
$ws_worker->onClose = function ($connection) {
|
|
||||||
echo "Connection closed\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
// Run worker
|
|
||||||
Worker::runAll();
|
|
||||||
```
|
|
||||||
|
|
||||||
### An http server
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
// #### http worker ####
|
|
||||||
$http_worker = new Worker('http://0.0.0.0:2345');
|
|
||||||
|
|
||||||
// 4 processes
|
|
||||||
$http_worker->count = 4;
|
|
||||||
|
|
||||||
// Emitted when data received
|
|
||||||
$http_worker->onMessage = function ($connection, $request) {
|
|
||||||
//$request->get();
|
|
||||||
//$request->post();
|
|
||||||
//$request->header();
|
|
||||||
//$request->cookie();
|
|
||||||
//$request->session();
|
|
||||||
//$request->uri();
|
|
||||||
//$request->path();
|
|
||||||
//$request->method();
|
|
||||||
|
|
||||||
// Send data to client
|
|
||||||
$connection->send("Hello World");
|
|
||||||
};
|
|
||||||
|
|
||||||
// Run all workers
|
|
||||||
Worker::runAll();
|
|
||||||
```
|
|
||||||
|
|
||||||
### A tcp server
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
// #### create socket and listen 1234 port ####
|
|
||||||
$tcp_worker = new Worker('tcp://0.0.0.0:1234');
|
|
||||||
|
|
||||||
// 4 processes
|
|
||||||
$tcp_worker->count = 4;
|
|
||||||
|
|
||||||
// Emitted when new connection come
|
|
||||||
$tcp_worker->onConnect = function ($connection) {
|
|
||||||
echo "New Connection\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
// Emitted when data received
|
|
||||||
$tcp_worker->onMessage = function ($connection, $data) {
|
|
||||||
// Send data to client
|
|
||||||
$connection->send("Hello $data \n");
|
|
||||||
};
|
|
||||||
|
|
||||||
// Emitted when connection is closed
|
|
||||||
$tcp_worker->onClose = function ($connection) {
|
|
||||||
echo "Connection closed\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
Worker::runAll();
|
|
||||||
```
|
|
||||||
|
|
||||||
### A udp server
|
|
||||||
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
$worker = new Worker('udp://0.0.0.0:1234');
|
|
||||||
|
|
||||||
// 4 processes
|
|
||||||
$tcp_worker->count = 4;
|
|
||||||
|
|
||||||
// Emitted when data received
|
|
||||||
$worker->onMessage = function($connection, $data)
|
|
||||||
{
|
|
||||||
$connection->send($data);
|
|
||||||
};
|
|
||||||
|
|
||||||
Worker::runAll();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Enable SSL
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
// SSL context.
|
|
||||||
$context = array(
|
|
||||||
'ssl' => array(
|
|
||||||
'local_cert' => '/your/path/of/server.pem',
|
|
||||||
'local_pk' => '/your/path/of/server.key',
|
|
||||||
'verify_peer' => false,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create a Websocket server with ssl context.
|
|
||||||
$ws_worker = new Worker('websocket://0.0.0.0:2346', $context);
|
|
||||||
|
|
||||||
// Enable SSL. WebSocket+SSL means that Secure WebSocket (wss://).
|
|
||||||
// The similar approaches for Https etc.
|
|
||||||
$ws_worker->transport = 'ssl';
|
|
||||||
|
|
||||||
$ws_worker->onMessage = function ($connection, $data) {
|
|
||||||
// Send hello $data
|
|
||||||
$connection->send('Hello ' . $data);
|
|
||||||
};
|
|
||||||
|
|
||||||
Worker::runAll();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom protocol
|
|
||||||
Protocols/MyTextProtocol.php
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Protocols;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User defined protocol
|
|
||||||
* Format Text+"\n"
|
|
||||||
*/
|
|
||||||
class MyTextProtocol
|
|
||||||
{
|
|
||||||
public static function input($recv_buffer)
|
|
||||||
{
|
|
||||||
// Find the position of the first occurrence of "\n"
|
|
||||||
$pos = strpos($recv_buffer, "\n");
|
|
||||||
|
|
||||||
// Not a complete package. Return 0 because the length of package can not be calculated
|
|
||||||
if ($pos === false) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return length of the package
|
|
||||||
return $pos+1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function decode($recv_buffer)
|
|
||||||
{
|
|
||||||
return trim($recv_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function encode($data)
|
|
||||||
{
|
|
||||||
return $data . "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
// #### MyTextProtocol worker ####
|
|
||||||
$text_worker = new Worker('MyTextProtocol://0.0.0.0:5678');
|
|
||||||
|
|
||||||
$text_worker->onConnect = function ($connection) {
|
|
||||||
echo "New connection\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
$text_worker->onMessage = function ($connection, $data) {
|
|
||||||
// Send data to client
|
|
||||||
$connection->send("Hello world\n");
|
|
||||||
};
|
|
||||||
|
|
||||||
$text_worker->onClose = function ($connection) {
|
|
||||||
echo "Connection closed\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
// Run all workers
|
|
||||||
Worker::runAll();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Timer
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
use Workerman\Timer;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
$task = new Worker();
|
|
||||||
$task->onWorkerStart = function ($task) {
|
|
||||||
// 2.5 seconds
|
|
||||||
$time_interval = 2.5;
|
|
||||||
$timer_id = Timer::add($time_interval, function () {
|
|
||||||
echo "Timer run\n";
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Run all workers
|
|
||||||
Worker::runAll();
|
|
||||||
```
|
|
||||||
|
|
||||||
### AsyncTcpConnection (tcp/ws/text/frame etc...)
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
use Workerman\Worker;
|
|
||||||
use Workerman\Connection\AsyncTcpConnection;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
$worker = new Worker();
|
|
||||||
$worker->onWorkerStart = function () {
|
|
||||||
// Websocket protocol for client.
|
|
||||||
$ws_connection = new AsyncTcpConnection('ws://echo.websocket.org:80');
|
|
||||||
$ws_connection->onConnect = function ($connection) {
|
|
||||||
$connection->send('Hello');
|
|
||||||
};
|
|
||||||
$ws_connection->onMessage = function ($connection, $data) {
|
|
||||||
echo "Recv: $data\n";
|
|
||||||
};
|
|
||||||
$ws_connection->onError = function ($connection, $code, $msg) {
|
|
||||||
echo "Error: $msg\n";
|
|
||||||
};
|
|
||||||
$ws_connection->onClose = function ($connection) {
|
|
||||||
echo "Connection closed\n";
|
|
||||||
};
|
|
||||||
$ws_connection->connect();
|
|
||||||
};
|
|
||||||
|
|
||||||
Worker::runAll();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Available commands
|
|
||||||
```php start.php start ```
|
|
||||||
```php start.php start -d ```
|
|
||||||
![workerman start](http://www.workerman.net/img/workerman-start.png)
|
|
||||||
```php start.php status ```
|
|
||||||
![workerman satus](http://www.workerman.net/img/workerman-status.png?a=123)
|
|
||||||
```php start.php connections```
|
|
||||||
```php start.php stop ```
|
|
||||||
```php start.php restart ```
|
|
||||||
```php start.php reload ```
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
中文主页:[http://www.workerman.net](https://www.workerman.net)
|
|
||||||
|
|
||||||
中文文档: [https://www.workerman.net/doc/workerman](https://www.workerman.net/doc/workerman)
|
|
||||||
|
|
||||||
Documentation:[https://github.com/walkor/workerman-manual](https://github.com/walkor/workerman-manual/blob/master/english/SUMMARY.md)
|
|
||||||
|
|
||||||
# Benchmarks
|
|
||||||
https://www.techempower.com/benchmarks/#section=data-r20&hw=ph&test=db&l=yyku7z-e7&a=2
|
|
||||||
![image](https://user-images.githubusercontent.com/6073368/146704320-1559fe97-aa67-4ee3-95d6-61e341b3c93b.png)
|
|
||||||
|
|
||||||
## Sponsors
|
|
||||||
[opencollective.com/walkor](https://opencollective.com/walkor)
|
|
||||||
|
|
||||||
[patreon.com/walkor](https://patreon.com/walkor)
|
|
||||||
|
|
||||||
## Donate
|
|
||||||
|
|
||||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UQGGS9UB35WWG"><img src="http://donate.workerman.net/img/donate.png"></a>
|
|
||||||
|
|
||||||
## Other links with workerman
|
|
||||||
|
|
||||||
[webman](https://github.com/walkor/webman)
|
|
||||||
[PHPSocket.IO](https://github.com/walkor/phpsocket.io)
|
|
||||||
[php-socks5](https://github.com/walkor/php-socks5)
|
|
||||||
[php-http-proxy](https://github.com/walkor/php-http-proxy)
|
|
||||||
|
|
||||||
## LICENSE
|
|
||||||
|
|
||||||
Workerman is released under the [MIT license](https://github.com/walkor/workerman/blob/master/MIT-LICENSE.txt).
|
|
220
vendor/workerman/workerman/Timer.php
vendored
220
vendor/workerman/workerman/Timer.php
vendored
@ -1,220 +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 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 Workerman;
|
|
||||||
|
|
||||||
use Workerman\Events\EventInterface;
|
|
||||||
use Workerman\Worker;
|
|
||||||
use \Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timer.
|
|
||||||
*
|
|
||||||
* example:
|
|
||||||
* Workerman\Timer::add($time_interval, callback, array($arg1, $arg2..));
|
|
||||||
*/
|
|
||||||
class Timer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Tasks that based on ALARM signal.
|
|
||||||
* [
|
|
||||||
* run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
|
|
||||||
* run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
|
|
||||||
* ..
|
|
||||||
* ]
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected static $_tasks = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* event
|
|
||||||
*
|
|
||||||
* @var EventInterface
|
|
||||||
*/
|
|
||||||
protected static $_event = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* timer id
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected static $_timerId = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* timer status
|
|
||||||
* [
|
|
||||||
* timer_id1 => bool,
|
|
||||||
* timer_id2 => bool,
|
|
||||||
* ....................,
|
|
||||||
* ]
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected static $_status = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init.
|
|
||||||
*
|
|
||||||
* @param EventInterface $event
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function init($event = null)
|
|
||||||
{
|
|
||||||
if ($event) {
|
|
||||||
self::$_event = $event;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (\function_exists('pcntl_signal')) {
|
|
||||||
\pcntl_signal(\SIGALRM, array('\Workerman\Lib\Timer', 'signalHandle'), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ALARM signal handler.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function signalHandle()
|
|
||||||
{
|
|
||||||
if (!self::$_event) {
|
|
||||||
\pcntl_alarm(1);
|
|
||||||
self::tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a timer.
|
|
||||||
*
|
|
||||||
* @param float $time_interval
|
|
||||||
* @param callable $func
|
|
||||||
* @param mixed $args
|
|
||||||
* @param bool $persistent
|
|
||||||
* @return int|bool
|
|
||||||
*/
|
|
||||||
public static function add($time_interval, $func, $args = array(), $persistent = true)
|
|
||||||
{
|
|
||||||
if ($time_interval <= 0) {
|
|
||||||
Worker::safeEcho(new Exception("bad time_interval"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($args === null) {
|
|
||||||
$args = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self::$_event) {
|
|
||||||
return self::$_event->add($time_interval,
|
|
||||||
$persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not workerman runtime just return.
|
|
||||||
if (!Worker::getAllWorkers()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!\is_callable($func)) {
|
|
||||||
Worker::safeEcho(new Exception("not callable"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty(self::$_tasks)) {
|
|
||||||
\pcntl_alarm(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$run_time = \time() + $time_interval;
|
|
||||||
if (!isset(self::$_tasks[$run_time])) {
|
|
||||||
self::$_tasks[$run_time] = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$_timerId = self::$_timerId == \PHP_INT_MAX ? 1 : ++self::$_timerId;
|
|
||||||
self::$_status[self::$_timerId] = true;
|
|
||||||
self::$_tasks[$run_time][self::$_timerId] = array($func, (array)$args, $persistent, $time_interval);
|
|
||||||
|
|
||||||
return self::$_timerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tick.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function tick()
|
|
||||||
{
|
|
||||||
if (empty(self::$_tasks)) {
|
|
||||||
\pcntl_alarm(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$time_now = \time();
|
|
||||||
foreach (self::$_tasks as $run_time => $task_data) {
|
|
||||||
if ($time_now >= $run_time) {
|
|
||||||
foreach ($task_data as $index => $one_task) {
|
|
||||||
$task_func = $one_task[0];
|
|
||||||
$task_args = $one_task[1];
|
|
||||||
$persistent = $one_task[2];
|
|
||||||
$time_interval = $one_task[3];
|
|
||||||
try {
|
|
||||||
\call_user_func_array($task_func, $task_args);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Worker::safeEcho($e);
|
|
||||||
}
|
|
||||||
if($persistent && !empty(self::$_status[$index])) {
|
|
||||||
$new_run_time = \time() + $time_interval;
|
|
||||||
if(!isset(self::$_tasks[$new_run_time])) self::$_tasks[$new_run_time] = array();
|
|
||||||
self::$_tasks[$new_run_time][$index] = array($task_func, (array)$task_args, $persistent, $time_interval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unset(self::$_tasks[$run_time]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a timer.
|
|
||||||
*
|
|
||||||
* @param mixed $timer_id
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function del($timer_id)
|
|
||||||
{
|
|
||||||
if (self::$_event) {
|
|
||||||
return self::$_event->del($timer_id, EventInterface::EV_TIMER);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(self::$_tasks as $run_time => $task_data)
|
|
||||||
{
|
|
||||||
if(array_key_exists($timer_id, $task_data)) unset(self::$_tasks[$run_time][$timer_id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(array_key_exists($timer_id, self::$_status)) unset(self::$_status[$timer_id]);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all timers.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function delAll()
|
|
||||||
{
|
|
||||||
self::$_tasks = self::$_status = array();
|
|
||||||
if (\function_exists('pcntl_alarm')) {
|
|
||||||
\pcntl_alarm(0);
|
|
||||||
}
|
|
||||||
if (self::$_event) {
|
|
||||||
self::$_event->clearAllTimer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
2672
vendor/workerman/workerman/Worker.php
vendored
2672
vendor/workerman/workerman/Worker.php
vendored
File diff suppressed because it is too large
Load Diff
38
vendor/workerman/workerman/composer.json
vendored
38
vendor/workerman/workerman/composer.json
vendored
@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "workerman/workerman",
|
|
||||||
"type": "library",
|
|
||||||
"keywords": [
|
|
||||||
"event-loop",
|
|
||||||
"asynchronous"
|
|
||||||
],
|
|
||||||
"homepage": "http://www.workerman.net",
|
|
||||||
"license": "MIT",
|
|
||||||
"description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "walkor",
|
|
||||||
"email": "walkor@workerman.net",
|
|
||||||
"homepage": "http://www.workerman.net",
|
|
||||||
"role": "Developer"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"email": "walkor@workerman.net",
|
|
||||||
"issues": "https://github.com/walkor/workerman/issues",
|
|
||||||
"forum": "http://wenda.workerman.net/",
|
|
||||||
"wiki": "http://doc.workerman.net/",
|
|
||||||
"source": "https://github.com/walkor/workerman"
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=7.0"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"ext-event": "For better performance. "
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Workerman\\": "./"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"minimum-stability": "dev"
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user