initial 2.0.0-a2 commit

This commit is contained in:
jerry
2020-09-29 15:07:43 +08:00
parent 1510e2f0d0
commit f91d24aaaa
59 changed files with 2271 additions and 1475 deletions

View File

@@ -0,0 +1,51 @@
<?php
namespace ZM\Utils;
use Swoole\Coroutine;
class CoroutinePool
{
private static $cids = [];
private static $default_size = 30;
private static $sizes = [];
private static $yields = [];
public static function go(callable $func, $name = "default") {
if (!isset(self::$cids[$name])) self::$cids[$name] = [];
if (count(self::$cids[$name]) >= (self::$sizes[$name] ?? self::$default_size)) {
self::$yields[] = Coroutine::getCid();
Coroutine::suspend();
}
go(function () use ($func, $name) {
self::$cids[$name][] = Coroutine::getCid();
//Console::debug("正在执行协程,当前协程池中有 " . count(self::$cids[$name]) . " 个正在运行的协程: ".implode(", ", self::$cids[$name]));
$func();
self::checkCids($name);
});
}
public static function defaultSize(int $size) {
self::$default_size = $size;
}
public static function setSize($name, int $size) {
self::$sizes[$name] = $size;
}
private static function checkCids($name) {
if (in_array(Coroutine::getCid(), self::$cids[$name])) {
$a = array_search(Coroutine::getCid(), self::$cids[$name]);
array_splice(self::$cids[$name], $a, 1);
$r = array_shift(self::$yields);
if ($r !== null) {
Coroutine::resume($r);
}
}
}
}

117
src/ZM/Utils/HttpUtil.php Normal file
View File

@@ -0,0 +1,117 @@
<?php
namespace ZM\Utils;
use Co;
use ZM\Config\ZMConfig;
use ZM\Console\Console;
use ZM\Event\EventManager;
use ZM\Http\Response;
class HttpUtil
{
public static function parseUri($request, $response, $uri, &$node, &$params) {
$uri = explode("/", $uri);
$uri = array_diff($uri, ["..", "", "."]);
$node = EventManager::$req_mapping;
$params = [];
while (true) {
$r = array_shift($uri);
if ($r === null) break;
if (($cnt = count($node["son"] ?? [])) == 1) {
if (isset($node["param_route"])) {
foreach ($node["son"] as $k => $v) {
if ($v["id"] == $node["param_route"]) {
$node = $v;
$params[mb_substr($v["name"], 1, -1)] = $r;
continue 2;
}
}
} elseif ($node["son"][0]["name"] == $r) {
$node = $node["son"][0];
continue;
}
} elseif ($cnt >= 1) {
if (isset($node["param_route"])) {
foreach ($node["son"] as $k => $v) {
if ($v["id"] == $node["param_route"]) {
$node = $v;
$params[mb_substr($v["name"], 1, -1)] = $r;
continue 2;
}
}
}
foreach ($node["son"] as $k => $v) {
if ($v["name"] == $r) {
$node = $v;
continue 2;
}
}
}
if (ZMConfig::get("global", "static_file_server")["status"]) {
HttpUtil::handleStaticPage($request->server["request_uri"], $response);
return true;
}
}
return false;
}
public static function getHttpCodePage(int $http_code) {
if (isset(ZMConfig::get("global", "http_default_code_page")[$http_code])) {
return Co::readFile(DataProvider::getResourceFolder() . "html/" . ZMConfig::get("global", "http_default_code_page")[$http_code]);
} else return null;
}
/**
* @param $uri
* @param Response|\Swoole\Http\Response $response
* @param array $settings
* @return bool
*/
public static function handleStaticPage($uri, $response, $settings = []) {
$base_dir = $settings["document_root"] ?? ZMConfig::get("global", "static_file_server")["document_root"];
$base_index = $settings["document_index"] ?? ZMConfig::get("global", "static_file_server")["document_index"];
$path = realpath($base_dir . urldecode($uri));
if ($path !== false) {
if (is_dir($path)) $path = $path . '/';
$work = realpath($base_dir) . '/';
if (strpos($path, $work) !== 0) {
Console::info("[403] " . $uri);
self::responseCodePage($response, 403);
return true;
}
if (is_dir($path)) {
if(mb_substr($uri, -1, 1) != "/") {
Console::info("[302] " . $uri);
$response->redirect($uri."/", 302);
return true;
}
foreach ($base_index as $vp) {
if (is_file($path . "/" . $vp)) {
Console::info("[200] " . $uri);
$exp = strtolower(pathinfo($path . $vp)['extension'] ?? "unknown");
$response->setHeader("Content-Type", ZMConfig::get("file_header")[$exp] ?? "application/octet-stream");
$response->end(file_get_contents($path . $vp));
return true;
}
}
} elseif (is_file($path)) {
Console::info("[200] " . $uri);
$exp = strtolower(pathinfo($path)['extension'] ?? "unknown");
$response->setHeader("Content-Type", ZMConfig::get("file_header")[$exp] ?? "application/octet-stream");
$response->end(file_get_contents($path));
return true;
}
}
Console::info("[404] " . $uri);
self::responseCodePage($response, 404);
return true;
}
public static function responseCodePage($response, $code) {
$response->status($code);
$response->end(self::getHttpCodePage($code));
}
}

View File

@@ -5,74 +5,18 @@ namespace ZM\Utils;
use Exception;
use ZM\Config\ZMConfig;
use ZM\ConnectionManager\ConnectionObject;
use Swoole\Event;
use ZM\Console\Console;
use ZM\Store\ZMBuf;
use ZM\Annotation\Swoole\SwooleEventAt;
use ZM\Framework;
class Terminal
{
/**
* @var false|resource
*/
public static $console_proc = null;
public static $pipes = [];
static function listenConsole($terminal_id) {
if ($terminal_id === null) {
if (ZMBuf::$server->worker_id === 0) Console::info("ConsoleCommand disabled.");
return;
}
global $terminal_id;
global $port;
$port = ZMConfig::get("global", "port");
$vss = new SwooleEventAt();
$vss->type = "open";
$vss->level = 256;
$vss->rule = "connectType:terminal";
$vss->callback = function (?ConnectionObject $conn) use ($terminal_id) {
$req = ctx()->getRequest();
if ($conn->getName() != "terminal") return false;
Console::debug("Terminal fd: " . $conn->getFd());
ZMBuf::set("terminal_fd", $conn->getFd());
if (($req->header["x-terminal-id"] ?? "") != $terminal_id) {
ZMBuf::$server->close($conn->getFd());
return false;
}
return false;
};
ZMBuf::$events[SwooleEventAt::class][] = $vss;
$vss2 = new SwooleEventAt();
$vss2->type = "message";
$vss2->rule = "connectType:terminal";
$vss2->callback = function (?ConnectionObject $conn) {
if ($conn === null) return false;
if ($conn->getName() != "terminal") return false;
$cmd = ctx()->getFrame()->data;
self::executeCommand($cmd);
return false;
};
ZMBuf::$events[SwooleEventAt::class][] = $vss2;
if (ZMBuf::$server->worker_id === 0) {
go(function () {
global $terminal_id, $port;
$descriptorspec = array(
0 => STDIN,
1 => STDOUT,
2 => STDERR
);
self::$console_proc = proc_open('php -r \'$terminal_id = "' . $terminal_id . '";$port = ' . $port . ';require "' . __DIR__ . '/terminal_listener.php";\'', $descriptorspec, self::$pipes);
});
}
}
/**
* @param string $cmd
* @param $resource
* @return bool
*/
private static function executeCommand(string $cmd) {
public static function executeCommand(string $cmd, $resource) {
$it = explodeMsg($cmd);
switch ($it[0] ?? '') {
case 'logtest':
@@ -88,7 +32,13 @@ class Terminal
$class_name = $it[1];
$function_name = $it[2];
$class = new $class_name([]);
call_user_func_array([$class, $function_name], []);
$class->$function_name();
return true;
case 'psysh':
if (Framework::$argv["disable-coroutine"])
eval(\Psy\sh());
else
Console::error("Only \"--disable-coroutine\" mode can use psysh!!!");
return true;
case 'bc':
$code = base64_decode($it[1] ?? '', true);
@@ -104,18 +54,13 @@ class Terminal
Console::log($it[2], $it[1]);
return true;
case 'stop':
Event::del($resource);
ZMUtil::stop();
return false;
case 'reload':
case 'r':
ZMUtil::reload();
return false;
case 'save':
//$origin = ZMBuf::$atomics["info_level"]->get();
//ZMBuf::$atomics["info_level"]->set(3);
DataProvider::saveBuffer();
//ZMBuf::$atomics["info_level"]->set($origin);
return true;
case '':
return true;
default:

View File

@@ -1,144 +0,0 @@
<?php
namespace ZM\Utils;
use ZM\Console\Console;
use Swoole\Coroutine\Http\Client;
class ZMRequest
{
/**
* 使用Swoole协程客户端发起HTTP GET请求
* @param $url
* @param array $headers
* @param array $set
* @param bool $return_body
* @return bool|string|Client
* @version 1.1
* 返回请求后的body
* 如果请求失败或返回状态不是200则返回 false
*/
public static function get($url, $headers = [], $set = [], $return_body = true) {
/** @var Client $cli */
list($cli, $parse) = self::getNewClient($url);
if($cli === null) return false;
$cli->set($set == [] ? ['timeout' => 15.0] : $set);
$cli->setHeaders($headers);
$cli->get($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""));
if ($return_body) {
if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
$a = $cli->body;
$cli->close();
return $a;
} else {
$cli->close();
return $cli;
}
}
/**
* 使用Swoole协程客户端发起HTTP POST请求
* 返回请求后的body
* 如果请求失败或返回状态不是200则返回 false
* @param $url
* @param array $header
* @param $data
* @param array $set
* @param bool $return_body
* @return bool|string|Client
*/
public static function post($url, array $header, $data, $set = [], $return_body = true) {
/** @var Client $cli */
list($cli, $parse) = self::getNewClient($url);
if($cli === null) return false;
$cli->set($set == [] ? ['timeout' => 15.0] : $set);
$cli->setHeaders($header);
$cli->post($parse["path"] . (isset($parse["query"]) ? ("?" . $parse["query"]) : ""), $data);
if ($return_body) {
if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
$a = $cli->body;
$cli->close();
return $a;
} else {
$cli->close();
return $cli;
}
}
/**
* @param $url
* @param array $set
* @param array $header
* @return ZMWebSocket
* @since 1.5
*/
public static function websocket($url, $set = ['websocket_mask' => true], $header = []) {
return new ZMWebSocket($url, $set, $header);
}
/**
* @param $url
* @param array $attribute
* @param bool $return_body
* @return bool|string|Client
*/
public static function request($url, $attribute = [], $return_body = true) {
/** @var Client $cli */
list($cli, $parse) = self::getNewClient($url);
if($cli === null) return false;
$cli->set($attribute["set"] ?? ["timeout" => 15.0]);
$cli->setMethod($attribute["method"] ?? "GET");
$cli->setHeaders($attribute["headers"] ?? []);
if(isset($attribute["data"])) $cli->setData($attribute["data"]);
if(isset($attribute["file"])) {
foreach($attribute["file"] as $k => $v) {
$cli->addFile($v["path"], $v["name"], $v["mime_type"] ?? null, $v["filename"] ?? null, $v["offset"] ?? 0, $v["length"] ?? 0);
}
}
$cli->execute($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""));
if ($return_body) {
if ($cli->errCode != 0 || $cli->statusCode != 200) return false;
$a = $cli->body;
$cli->close();
return $a;
} else {
$cli->close();
return $cli;
}
}
/**
* @param $url
* @param null|bool $dst
* @return bool
*/
public static function downloadFile($url, $dst = null) {
/** @var Client $cli */
list($cli, $parse) = self::getNewClient($url);
if($cli === null) return false;
$cli->set(["timeout" => 60.0]);
$save_path = $dst === null ? "/tmp/_zm_".mt_rand(1000000, 9999999) : $dst;
$result = $cli->download($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""), $save_path);
if($result === false) return false;
elseif ($dst === null) return $save_path;
else return true;
}
/**
* @param $url
* @return bool|array
*/
private static function getNewClient($url) {
$parse = parse_url($url);
if (!isset($parse["host"])) {
Console::warning("ZMRequest: url must contains scheme such as \"http(s)://\"");
return false;
}
if(!isset($parse["path"])) $parse["path"] = "/";
$port = $parse["port"] ?? (($parse["scheme"] ?? "http") == "https" ? 443 : 80);
$cli = new Client($parse["host"], $port, ($parse["scheme"] ?? "http") == "https");
return [$cli, $parse];
}
}

View File

@@ -5,55 +5,45 @@ namespace ZM\Utils;
use Co;
use ZM\Config\ZMConfig;
use Exception;
use Swoole\Event;
use Swoole\Timer;
use ZM\Console\Console;
use ZM\API\CQ;
use ZM\Store\LightCache;
use ZM\Store\ZMBuf;
class ZMUtil
{
/**
* 检查workerStart是否运行结束
*/
public static function checkWait() {
if (ZMBuf::isset("wait_start")) {
ZMBuf::append("wait_start", Co::getCid());
Co::suspend();
public static function stop() {
Console::warning(Console::setColor("Stopping server...", "red"));
if (ZMBuf::$terminal !== null)
Event::del(ZMBuf::$terminal);
ZMBuf::atomic("stop_signal")->set(1);
try {
LightCache::set('stop', 'OK');
} catch (Exception $e) {
}
server()->shutdown();
server()->stop();
}
public static function stop($without_shutdown = false) {
Console::info(Console::setColor("Stopping server...", "red"));
foreach ((ZMBuf::$server->connections ?? []) as $v) {
ZMBuf::$server->close($v);
}
DataProvider::saveBuffer();
if (!$without_shutdown)
ZMBuf::$server->shutdown();
ZMBuf::$server->stop();
}
public static function getHttpCodePage(int $http_code) {
if (isset(ZMConfig::get("global", "http_default_code_page")[$http_code])) {
return Co::readFile(DataProvider::getResourceFolder() . "html/" . ZMConfig::get("global", "http_default_code_page")[$http_code]);
} else return null;
}
public static function reload() {
public static function reload($delay = 800) {
Console::info(Console::setColor("Reloading server...", "gold"));
usleep($delay * 1000);
foreach (ZMBuf::get("wait_api", []) as $k => $v) {
if ($v["result"] === null) Co::resume($v["coroutine"]);
}
foreach (ZMBuf::$server->connections as $v) {
ZMBuf::$server->close($v);
foreach (server()->connections as $v) {
server()->close($v);
}
DataProvider::saveBuffer();
ZMBuf::$server->reload();
Timer::clearAll();
server()->reload();
}
public static function getModInstance($class) {
if (!isset(ZMBuf::$instance[$class])) {
Console::debug("Class instance $class not exist, so I created it.");
//Console::debug("Class instance $class not exist, so I created it.");
return ZMBuf::$instance[$class] = new $class();
} else {
return ZMBuf::$instance[$class];

View File

@@ -1,106 +0,0 @@
<?php
namespace ZM\Utils;
use ZM\Console\Console;
use Swoole\Coroutine\Http\Client;
use Swoole\WebSocket\Frame;
/**
* Class ZMWebSocket
* @package ZM\Utils
* @since 1.5
*/
class ZMWebSocket
{
private $parse;
private $client;
public $is_available = false;
private $close_func;
private $message_func;
public function __construct($url, $set = ['websocket_mask' => true], $header = []) {
$this->parse = parse_url($url);
if (!isset($this->parse["host"])) {
Console::warning("ZMRequest: url must contains scheme such as \"ws(s)://\"");
return;
}
if (!isset($this->parse["path"])) $this->parse["path"] = "/";
$port = $this->parse["port"] ?? (($this->parse["scheme"] ?? "ws") == "wss" ? 443 : 80);
$this->client = new Client($this->parse["host"], $port, (($this->parse["scheme"] ?? "ws") == "wss" ? true : false));
$this->client->set($set);
if ($header != []) $this->client->setHeaders($header);
$this->is_available = true;
}
/**
* @return bool
*/
public function upgrade() {
if (!$this->is_available) return false;
$r = $this->client->upgrade($this->parse["path"] . (isset($this->parse["query"]) ? ("?" . $this->parse["query"]) : ""));
if ($r) {
go(function () {
while (true) {
$result = $this->client->recv(60);
if ($result === false) {
if ($this->client->connected === false) {
go(function () {
call_user_func($this->close_func, $this->client);
});
break;
}
} elseif ($result instanceof Frame) {
go(function () use ($result) {
$this->is_available = false;
call_user_func($this->message_func, $result, $this->client);
});
}
}
});
return true;
}
return false;
}
/**
* @param callable $callable
* @return $this
*/
public function onMessage(callable $callable) {
$this->message_func = $callable;
return $this;
}
/**
* @param callable $callable
* @return $this
*/
public function onClose(callable $callable) {
$this->close_func = $callable;
return $this;
}
}
if (!debug_backtrace()) {
go(function () {
require_once __DIR__ . "/../../Framework/Console.php";
$cli = new ZMWebSocket("ws://127.0.0.1:20001/");
if (!$cli->is_available) die("Error!\n");
$cli->onMessage(function (Frame $frame) {
var_dump($frame);
});
$cli->onClose(function () {
echo "Connection closed.\n";
});
if ($cli->upgrade()) {
echo "成功连接!\n";
} else {
echo "连接失败!\n";
}
});
}