mirror of
https://github.com/Karaka-Management/phpOMS.git
synced 2026-01-11 09:48:40 +00:00
Supporting websockets
This commit is contained in:
parent
e02de0b5a4
commit
3d7ee2e690
|
|
@ -214,7 +214,7 @@ class FileLogger implements LoggerInterface
|
|||
* Interpolate context
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
* @param array $context
|
||||
* @param string $level
|
||||
*
|
||||
* @return string
|
||||
|
|
@ -292,7 +292,7 @@ class FileLogger implements LoggerInterface
|
|||
* System is unusable.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
* @param array $context
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
|
|
@ -312,7 +312,7 @@ class FileLogger implements LoggerInterface
|
|||
* trigger the SMS alerts and wake you up.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
|
|
@ -331,7 +331,7 @@ class FileLogger implements LoggerInterface
|
|||
* Example: Application component unavailable, unexpected exception.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
|
|
@ -349,7 +349,7 @@ class FileLogger implements LoggerInterface
|
|||
* be logged and monitored.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
|
|
@ -369,7 +369,7 @@ class FileLogger implements LoggerInterface
|
|||
* that are not necessarily wrong.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
|
|
@ -386,7 +386,7 @@ class FileLogger implements LoggerInterface
|
|||
* Normal but significant events.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
|
|
@ -405,7 +405,7 @@ class FileLogger implements LoggerInterface
|
|||
* Example: User logs in, SQL logs.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
|
|
@ -422,7 +422,7 @@ class FileLogger implements LoggerInterface
|
|||
* Detailed debug information.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
|
|
@ -440,7 +440,7 @@ class FileLogger implements LoggerInterface
|
|||
*
|
||||
* @param string $level
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
|
|
@ -610,4 +610,13 @@ class FileLogger implements LoggerInterface
|
|||
|
||||
return $log;
|
||||
}
|
||||
|
||||
public function console(string $text, bool $verbose)
|
||||
{
|
||||
$text = date('[Y-m-d H:i:s] ') . $text . "\r\n";
|
||||
|
||||
if ($verbose) {
|
||||
echo $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ namespace phpOMS\Module;
|
|||
|
||||
use phpOMS\ApplicationAbstract;
|
||||
use phpOMS\DataStorage\Database\DatabaseType;
|
||||
use phpOMS\Log\FileLogger;
|
||||
use phpOMS\Message\Http\Request;
|
||||
use phpOMS\System\FilePathException;
|
||||
use phpOMS\Utils\IO\Json\InvalidJsonException;
|
||||
|
|
@ -429,7 +430,15 @@ class ModuleManager
|
|||
{
|
||||
if (is_array($module)) {
|
||||
foreach ($module as $m) {
|
||||
$this->initModule($m);
|
||||
try {
|
||||
$this->initModule($m);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$this->app->logger->warning(FileLogger::MSG_FULL, [
|
||||
'message' => 'Trying to initialize ' . $m . ' without controller.',
|
||||
'line' => $e->getLine(),
|
||||
'file' => $e->getFile(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
} elseif (is_string($module) && realpath(self::MODULE_PATH . '/' . $module . '/Controller.php') !== false) {
|
||||
$this->running[$module] = ModuleFactory::getInstance($module, $this->app);
|
||||
|
|
|
|||
82
Socket/Client/ClientConnection.php
Normal file
82
Socket/Client/ClientConnection.php
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 7.0
|
||||
*
|
||||
* @category TBD
|
||||
* @package TBD
|
||||
* @author OMS Development Team <dev@oms.com>
|
||||
* @author Dennis Eichhorn <d.eichhorn@oms.com>
|
||||
* @copyright 2013 Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link http://orange-management.com
|
||||
*/
|
||||
namespace phpOMS\Socket\Client;
|
||||
|
||||
use phpOMS\Socket\CommandManager;
|
||||
use phpOMS\Socket\SocketAbstract;
|
||||
|
||||
/**
|
||||
* Client socket class.
|
||||
*
|
||||
* @category Framework
|
||||
* @package phpOMS\Socket\Client
|
||||
* @author OMS Development Team <dev@oms.com>
|
||||
* @author Dennis Eichhorn <d.eichhorn@oms.com>
|
||||
* @license OMS License 1.0
|
||||
* @link http://orange-management.com
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class ClientConnection
|
||||
{
|
||||
private $id = 0;
|
||||
private $socket = null;
|
||||
private $handshake = false;
|
||||
private $pid = null;
|
||||
private $connected = true;
|
||||
|
||||
public function __construct($id, $socket)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->socket = $socket;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getSocket() {
|
||||
return $this->socket;
|
||||
}
|
||||
|
||||
public function getHandshake() {
|
||||
return $this->handshake;
|
||||
}
|
||||
|
||||
public function getPid() {
|
||||
return $this->pid;
|
||||
}
|
||||
|
||||
public function isConnected() {
|
||||
return $this->connected;
|
||||
}
|
||||
|
||||
public function setSocket($socket) {
|
||||
$this->socket = $socket;
|
||||
}
|
||||
|
||||
public function setHandshake($handshake) {
|
||||
$this->handshake = $handshake;
|
||||
}
|
||||
|
||||
public function setPid($pid) {
|
||||
$this->pid = $pid;
|
||||
}
|
||||
|
||||
public function setConnected(bool $connected) {
|
||||
$this->connected = $connected;
|
||||
}
|
||||
}
|
||||
34
Socket/Client/NullClientConnection.php
Normal file
34
Socket/Client/NullClientConnection.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 7.0
|
||||
*
|
||||
* @category TBD
|
||||
* @package TBD
|
||||
* @author OMS Development Team <dev@oms.com>
|
||||
* @author Dennis Eichhorn <d.eichhorn@oms.com>
|
||||
* @copyright 2013 Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link http://orange-management.com
|
||||
*/
|
||||
namespace phpOMS\Socket\Client;
|
||||
|
||||
use phpOMS\Socket\CommandManager;
|
||||
use phpOMS\Socket\SocketAbstract;
|
||||
|
||||
/**
|
||||
* Client socket class.
|
||||
*
|
||||
* @category Framework
|
||||
* @package phpOMS\Socket\Client
|
||||
* @author OMS Development Team <dev@oms.com>
|
||||
* @author Dennis Eichhorn <d.eichhorn@oms.com>
|
||||
* @license OMS License 1.0
|
||||
* @link http://orange-management.com
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class NullClientConnection extends ClientConnection
|
||||
{
|
||||
}
|
||||
|
|
@ -69,21 +69,14 @@ class PacketManager
|
|||
* Handle package.
|
||||
*
|
||||
* @param string $data Package data
|
||||
* @param mixed $key Client Id
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @author Dennis Eichhorn <d.eichhorn@oms.com>
|
||||
*/
|
||||
public function handle(string $data, $key)
|
||||
public function handle(string $data, $client)
|
||||
{
|
||||
/*
|
||||
if (!empty($data)) {
|
||||
$data = explode(' ', $data);
|
||||
$this->commandManager->trigger($data[0], $key, $data);
|
||||
} else {
|
||||
$this->commandManager->trigger('empty', $key, $data);
|
||||
}*/
|
||||
echo $data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,40 @@
|
|||
|
||||
namespace phpOMS\Socket\Server;
|
||||
|
||||
use phpOMS\Socket\Client\ClientConnection;
|
||||
use phpOMS\Socket\Client\NullClientConnection;
|
||||
|
||||
class ClientManager
|
||||
{
|
||||
private $clients = [];
|
||||
|
||||
public function add(ClientConnection $client)
|
||||
{
|
||||
$this->clients[$client->getId()] = $client;
|
||||
}
|
||||
|
||||
public function get($id)
|
||||
{
|
||||
return $this->clients[$id] ?? new NullClientConnection(uniqid(), null);
|
||||
}
|
||||
|
||||
public function getBySocket($socket) {
|
||||
foreach($this->clients as $client) {
|
||||
if($client->getSocket() === $socket) {
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
|
||||
return new NullClientConnection(uniqid(), null);
|
||||
}
|
||||
|
||||
public function remove($id) {
|
||||
if(isset($this->clients[$id])) {
|
||||
unset($this->clients[$id]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
namespace phpOMS\Socket\Server;
|
||||
|
||||
use phpOMS\Socket\Client\ClientConnection;
|
||||
use phpOMS\Socket\CommandManager;
|
||||
use phpOMS\Socket\Packets\PacketManager;
|
||||
|
||||
|
|
@ -58,6 +59,10 @@ class Server extends SocketAbstract
|
|||
*/
|
||||
private $packetManager = null;
|
||||
|
||||
private $clientManager = null;
|
||||
|
||||
private $verbose = true;
|
||||
|
||||
/**
|
||||
* Socket application.
|
||||
*
|
||||
|
|
@ -72,6 +77,7 @@ class Server extends SocketAbstract
|
|||
|
||||
if ($connected) {
|
||||
fclose($connected);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
@ -89,6 +95,7 @@ class Server extends SocketAbstract
|
|||
public function __construct($app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->clientManager = new ClientManager();
|
||||
$this->packetManager = new PacketManager(new CommandManager(), new ClientManager());
|
||||
}
|
||||
|
||||
|
|
@ -97,7 +104,9 @@ class Server extends SocketAbstract
|
|||
*/
|
||||
public function create(string $ip, int $port)
|
||||
{
|
||||
$this->app->logger->console('Creating socket...', $this->verbose);
|
||||
parent::create($ip, $port);
|
||||
$this->app->logger->console('Binding socket...', $this->verbose);
|
||||
socket_bind($this->sock, $this->ip, $this->port);
|
||||
}
|
||||
|
||||
|
|
@ -116,15 +125,57 @@ class Server extends SocketAbstract
|
|||
$this->limit = $limit;
|
||||
}
|
||||
|
||||
public function handshake($client, $headers)
|
||||
{
|
||||
if (preg_match("/Sec-WebSocket-Version: (.*)\r\n/", $headers, $match)) {
|
||||
$version = $match[1];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($version == 13) {
|
||||
if (preg_match("/GET (.*) HTTP/", $headers, $match)) {
|
||||
$root = $match[1];
|
||||
}
|
||||
|
||||
if (preg_match("/Host: (.*)\r\n/", $headers, $match)) {
|
||||
$host = $match[1];
|
||||
}
|
||||
|
||||
if (preg_match("/Origin: (.*)\r\n/", $headers, $match)) {
|
||||
$origin = $match[1];
|
||||
}
|
||||
|
||||
if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match)) {
|
||||
$key = $match[1];
|
||||
}
|
||||
|
||||
$acceptKey = $key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
||||
$acceptKey = base64_encode(sha1($acceptKey, true));
|
||||
$upgrade = "HTTP/1.1 101 Switching Protocols\r\n" .
|
||||
"Upgrade: websocket\r\n" .
|
||||
"Connection: Upgrade\r\n" .
|
||||
"Sec-WebSocket-Accept: $acceptKey" .
|
||||
"\r\n\r\n";
|
||||
socket_write($client->getSocket(), $upgrade);
|
||||
$client->setHandshake(true);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->app->logger->console('Start listening...', $this->verbose);
|
||||
socket_listen($this->sock);
|
||||
socket_set_nonblock($this->sock);
|
||||
$this->conn[] = $this->sock;
|
||||
|
||||
$this->app->logger->console('Start running...', $this->verbose);
|
||||
while ($this->run) {
|
||||
$read = $this->conn;
|
||||
|
||||
|
|
@ -138,55 +189,54 @@ class Server extends SocketAbstract
|
|||
// socket_clear_error();
|
||||
}
|
||||
|
||||
if (in_array($this->sock, $read)) {
|
||||
$this->conn[] = $newc = socket_accept($this->sock);
|
||||
foreach ($read as $key => $socket) {
|
||||
if ($this->sock === $socket) {
|
||||
$newc = socket_accept($this->sock);
|
||||
$this->connectClient($newc);
|
||||
} else {
|
||||
$client = $this->clientManager->getBySocket($socket);
|
||||
$data = socket_read($socket, 1024);
|
||||
|
||||
// TODO: init account
|
||||
// This is only the initial connection no auth happens here
|
||||
// Here the server should request an authentication
|
||||
|
||||
$welcome = "Welcome to the server.\n";
|
||||
socket_write($newc, $welcome, strlen($welcome));
|
||||
socket_getpeername($newc, $ip);
|
||||
|
||||
unset($read[0]);
|
||||
}
|
||||
|
||||
foreach ($read as $key => $client) {
|
||||
$data = socket_read($client, 1024);
|
||||
|
||||
if ($this->clientReadError($key)) {
|
||||
continue;
|
||||
if (!$client->getHandshake()) {
|
||||
$this->app->logger->console('Doing handshake...', $this->verbose);
|
||||
if ($this->handshake($client, $data)) {
|
||||
$this->app->logger->console('Handshake succeeded.', $this->verbose);
|
||||
} else {
|
||||
$this->app->logger->console('Handshake failed.', $this->verbose);
|
||||
$this->disconnectClient($client);
|
||||
}
|
||||
} else {
|
||||
$this->packetManager->handle($this->unmask($data), $client);
|
||||
}
|
||||
}
|
||||
|
||||
$this->packetManager->handle(trim($data), $key);
|
||||
}
|
||||
}
|
||||
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client read error.
|
||||
*
|
||||
* @param mixed $key Client key
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @author Dennis Eichhorn <d.eichhorn@oms.com>
|
||||
*/
|
||||
private function clientReadError($key) : bool
|
||||
public function connectClient($socket)
|
||||
{
|
||||
if (socket_last_error() === 10054) {
|
||||
socket_clear_error();
|
||||
unset($this->conn[$key]);
|
||||
$this->conn = array_values($this->conn);
|
||||
$this->app->logger->console('Connecting client...', $this->verbose);
|
||||
$this->clientManager->add($client = new ClientConnection(uniqid(), $socket));
|
||||
$this->conn[$client->getId()] = $socket;
|
||||
$this->app->logger->console('Connected client.', $this->verbose);
|
||||
}
|
||||
|
||||
return true;
|
||||
public function disconnectClient($client)
|
||||
{
|
||||
$this->app->logger->console('Disconnecting client...', $this->verbose);
|
||||
$client->setConnected(false);
|
||||
$client->setHandshake(false);
|
||||
socket_shutdown($client->getSocket(), 2);
|
||||
socket_close($client->getSocket());
|
||||
|
||||
if (isset($this->conn[$client->getId()])) {
|
||||
unset($this->conn[$client->getId()]);
|
||||
}
|
||||
|
||||
return false;
|
||||
$this->clientManager->remove($client->getId());
|
||||
$this->app->logger->console('Disconnected client.', $this->verbose);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -205,20 +255,24 @@ class Server extends SocketAbstract
|
|||
parent::__destruct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect client.
|
||||
*
|
||||
* @param mixed $key Client key
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @author Dennis Eichhorn <d.eichhorn@oms.com>
|
||||
*/
|
||||
private function disconnect($key)
|
||||
private function unmask($payload)
|
||||
{
|
||||
if (isset($this->conn[$key])) {
|
||||
unset($this->conn[$key]);
|
||||
$length = ord($payload[1]) & 127;
|
||||
if ($length == 126) {
|
||||
$masks = substr($payload, 4, 4);
|
||||
$data = substr($payload, 8);
|
||||
} elseif ($length == 127) {
|
||||
$masks = substr($payload, 10, 4);
|
||||
$data = substr($payload, 14);
|
||||
} else {
|
||||
$masks = substr($payload, 2, 4);
|
||||
$data = substr($payload, 6);
|
||||
}
|
||||
$text = '';
|
||||
for ($i = 0; $i < strlen($data); ++$i) {
|
||||
$text .= $data[$i] ^ $masks[$i % 4];
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ abstract class SocketAbstract implements SocketInterface
|
|||
public function close()
|
||||
{
|
||||
if ($this->sock !== null) {
|
||||
socket_shutdown($this->sock, 2);
|
||||
socket_close($this->sock);
|
||||
$this->sock = null;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user