diff --git a/README.md b/README.md index 7bfdb25..960223a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,27 @@ -### Prototype ChatApp +### [WIP] WebSocket server for [Pathfinder](https://github.com/exodus4d/pathfinder) -**Demo:** -- https://www.test.pathfinder-w.space/ +####Install: +1. Install [Composer](https://getcomposer.org/download/) +2. Install Composer dependencies from `composer.json` file: + - `php composer.phar install` (edit composer.phar path to your Composer installation) +3. Start WebSocket server `php cmd.php` -**Info:** +####Default Configuration +**Clients (WebBrowser) listen for connections** +- Host:`0.0.0.0.` (=> any client can connect) +- Port:`8020` +- URI:`127.0.0.1:8020` (Your WebServer (e.g. Nginx) should pass all WebSocket connections to this source) + +**TCP Socket connection (Internal use fore WebServer <=> WebSocket communication)** +- Host:`127.0.0.1` (=> Assumed WebServer and WebSocket Server running on the same machine) +- Port:`5555` +- URI: `tcp://127.0.0.1:5555` + +**[Optional]** +The default configuration should be fine for most installations. +You can change/overwrite the default **Host** and **Port** configuration by adding additional CLI parameters when starting the WebSocket server: + +`php cmd.php --pf_listen_host [CLIENTS_HOST] --pf_listen_port [CLIENTS_PORT] --pf_host [TCP_HOST] --pf_port [TCP_PORT]` + +####Info: - [*Ratchet*](http://socketo.me/) - "WebSockets for PHP" -- [*Web Sockets API*](https://www.w3.org/TR/2009/WD-websockets-20091222/) -- [*Web Workers*](https://www.w3.org/TR/workers/) - (SharedWorker) diff --git a/app/Main/Chat.php b/app/Main/Chat.php deleted file mode 100644 index ba07d4d..0000000 --- a/app/Main/Chat.php +++ /dev/null @@ -1,45 +0,0 @@ -clients = new \SplObjectStorage; - echo "__construct() \n"; - } - - public function onOpen(ConnectionInterface $conn) { - //store the new connection - $this->clients->attach($conn); - echo "NEW connection! ({$conn->resourceId})\n"; - } - - public function onMessage(ConnectionInterface $from, $msg) { - $numRecv = count($this->clients) - 1; - echo sprintf('Connection %d sending to %d other connection%s' . "\n" - , $from->resourceId, $numRecv, $numRecv == 1 ? '' : 's'); - - foreach ($this->clients as $client) { - // if ($from !== $client) { - $client->send($msg); - // } - } - } - - public function onClose(ConnectionInterface $conn) { - $this->clients->detach($conn); - - echo "Connection {$conn->resourceId} has disconnected\n"; - } - - public function onError(ConnectionInterface $conn, \Exception $e) { - echo "An error has occurred: {$e->getMessage()}\n"; - - $conn->close(); - } -} \ No newline at end of file diff --git a/app/Main/mapupdate.php b/app/Main/mapupdate.php new file mode 100644 index 0000000..1117a7f --- /dev/null +++ b/app/Main/mapupdate.php @@ -0,0 +1,494 @@ + tokens are unique and expire onSubscribe! + * @var + */ + + protected $characterAccessData; + /** + * access tokens for clients grouped by mapId + * -> tokens are unique and expire onSubscribe! + * @var + */ + protected $mapAccessData; + + /** + * connected characters + * @var + */ + protected $characters; + /** + * valid client connections subscribed to maps + * @var array + */ + protected $subscriptions; + + /** + * internal socket for response calls + * @var null | \React\ZMQ\SocketWrapper + */ + protected $internalSocket = null; + + public function __construct($socket) { + $this->internalSocket = $socket; + $this->characterAccessData = []; + $this->mapAccessData = []; + $this->characters = []; + $this->subscriptions = []; + + echo "__construct() \n"; + } + + public function onOpen(ConnectionInterface $conn) { + echo "NEW connection! ({$conn->resourceId})\n"; + } + + public function onMessage(ConnectionInterface $conn, $msg) { + $msg = json_decode($msg, true); + + if( + isset($msg['task']) && + isset($msg['load']) + ){ + $task = $msg['task']; + $load = $msg['load']; + + switch($task){ + case 'subscribe': + $this->subscribe($conn, $load); + break; + default: + break; + } + } + } + + public function onClose(ConnectionInterface $conn) { + $this->unSubscribeConnection($conn); + + echo "Connection {$conn->resourceId} has disconnected\n"; + } + + public function onError(ConnectionInterface $conn, \Exception $e) { + $this->unSubscribeConnection($conn); + $conn->close(); + + echo "An error has occurred: {$e->getMessage()}\n"; + } + + /** + * subscribes a connection to valid accessible maps + * @param ConnectionInterface $conn + * @param $subscribeData + */ + private function subscribe(ConnectionInterface $conn, $subscribeData){ + $characterId = (int)$subscribeData['id']; + $characterToken = $subscribeData['token']; + + if($characterId && $characterToken){ + + // check if character access token is valid (exists and not expired in $this->characterAccessData + if( $this->checkCharacterAccess($characterId, $characterToken) ){ + $this->characters[$characterId][$conn->resourceId] = $conn; + + // valid character -> check map access + foreach((array)$subscribeData['mapData'] as $data){ + $mapId = (int)$data['id']; + $mapToken = $data['token']; + + if($mapId && $mapToken){ + // check if token is valid (exists and not expired) in $this->mapAccessData + if( $this->checkMapAccess($characterId, $mapId, $mapToken) ){ + // valid map subscribe request + $this->subscriptions[$mapId][$characterId] = $characterId; + } + } + } + } + } + } + + /** + * subscribes an active connection from maps + * @param ConnectionInterface $conn + */ + private function unSubscribeConnection(ConnectionInterface $conn){ + $characterIds = $this->getCharacterIdsByConnection($conn); + + foreach($characterIds as $characterId){ + $this->unSubscribeCharacterId($characterId, $conn); + } + } + + /** + * unSubscribe a $characterId from ALL maps + * -> if $conn is set -> just unSub the $characterId from this $conn + * @param int $characterId + * @param null $conn + * @return bool + */ + private function unSubscribeCharacterId($characterId, $conn = null){ + if($characterId){ + + // unSub from $this->characters ------------------------------------------------------- + if($conn){ + // just unSub a specific connection (e.g. single browser window) + $resourceId = $conn->resourceId; + if( isset($this->characters[$characterId][$resourceId]) ){ + unset($this->characters[$characterId][$resourceId]); + + if( !count($this->characters[$characterId]) ){ + // no connection left for this character + unset($this->characters[$characterId]); + } + } + }else{ + // unSub ALL connections from a character (e.g. multiple browsers) + if( isset($this->characters[$characterId]) ){ + unset($this->characters[$characterId]); + } + } + + // unSub from $this->subscriptions ---------------------------------------------------- + foreach($this->subscriptions as $mapId => $characterIds){ + if(array_key_exists($characterId, $characterIds)){ + unset($this->subscriptions[$mapId][$characterId]); + + if( !count($this->subscriptions[$mapId]) ){ + // no characters left on this map + unset($this->subscriptions[$mapId]); + } + } + } + } + + return true; + } + + /** + * delete mapId from subscriptions and broadcast "delete msg" to clients + * @param string $task + * @param int $mapId + * @return int + */ + private function deleteMapId($task, $mapId){ + $connectionCount = $this->broadcastMapData($task, $mapId, $mapId); + + // remove map from subscriptions + if( isset($this->subscriptions[$mapId]) ){ + unset($this->subscriptions[$mapId]); + } + + return $connectionCount; + } + + /** + * @param ConnectionInterface $conn + * @return int[] + */ + private function getCharacterIdsByConnection(ConnectionInterface $conn){ + $characterIds = []; + $resourceId = $conn->resourceId; + + foreach($this->characters as $characterId => $resourceIDs){ + if( + array_key_exists($resourceId, $resourceIDs) && + !in_array($characterId, $characterIds) + ){ + $characterIds[] = $characterId; + } + } + return $characterIds; + } + + /** + * @param $mapId + * @return array + */ + private function getCharacterIdsByMapId($mapId){ + $characterIds = []; + + if( !empty($this->subscriptions[$mapId]) ){ + $characterIds = array_values( (array)$this->subscriptions[$mapId]); + } + + return $characterIds; + } + + /** + * get connection objects by characterIds + * @param int[] $characterIds + * @return \SplObjectStorage + */ + private function getConnectionsByCharacterIds($characterIds){ + $connections = new \SplObjectStorage; + + foreach($characterIds as $characterId){ + if($charConnections = (array)$this->characters[$characterId] ){ + foreach($charConnections as $conn){ + if( !$connections->contains($conn) ){ + $connections->attach($conn); + } + } + } + } + + return $connections; + } + + /** + * check character access against $this->characterAccessData whitelist + * @param $characterId + * @param $characterToken + * @return bool + */ + private function checkCharacterAccess($characterId, $characterToken){ + $access = false; + if( !empty($characterAccessData = (array)$this->characterAccessData[$characterId]) ){ + foreach($characterAccessData as $i => $data){ + $deleteToken = false; + // check expire for $this->characterAccessData -> check ALL characters and remove expired + if( ((int)$data['expire'] - time()) > 0 ){ + // still valid -> check token + if($characterToken === $data['token']){ + $access = true; + $deleteToken = true; + } + }else{ + // token expired + $deleteToken = true; + } + + if($deleteToken){ + unset($this->characterAccessData[$characterId][$i]); + // -> check if tokens for this charId is empty + if( empty($this->characterAccessData[$characterId]) ){ + unset($this->characterAccessData[$characterId]); + + } + } + } + } + return $access; + } + + /** + * check map access against $this->mapAccessData whitelist + * @param $characterId + * @param $mapId + * @param $mapToken + * @return bool + */ + private function checkMapAccess($characterId, $mapId, $mapToken){ + $access = false; + if( !empty($mapAccessData = (array)$this->mapAccessData[$mapId][$characterId]) ){ + foreach($mapAccessData as $i => $data){ + $deleteToken = false; + // check expire for $this->mapAccessData -> check ALL characters and remove expired + if( ((int)$data['expire'] - time()) > 0 ){ + // still valid -> check token + if($mapToken === $data['token']){ + $access = true; + $deleteToken = true; + } + }else{ + // token expired + $deleteToken = true; + } + + if($deleteToken){ + unset($this->mapAccessData[$mapId][$characterId][$i]); + // -> check if tokens for this charId is empty + if( empty($this->mapAccessData[$mapId][$characterId]) ){ + unset($this->mapAccessData[$mapId][$characterId]); + // -> check if map has no access tokens left for characters + if( empty($this->mapAccessData[$mapId]) ){ + unset($this->mapAccessData[$mapId]); + } + } + } + } + } + return $access; + } + + /** + * broadcast data ($load) to $connections + * @param ConnectionInterface[] | \SplObjectStorage $connections + * @param $task + * @param $load + * @param int[] $characterIds optional, recipients (e.g if multiple browser tabs are open) + */ + private function broadcastData($connections, $task, $load, $characterIds = []){ + $response = [ + 'task' => $task, + 'characterIds' => $characterIds, + 'load' => $load + ]; + + foreach($connections as $conn){ + $conn->send( json_encode($response) ); + } + } + + // custom calls =============================================================================== + + /** + * receive data from TCP socket (main App) + * -> send response back + * @param $data + */ + public function receiveData($data){ + $data = (array)json_decode($data, true); + $load = $data['load']; + $task = $data['task']; + $response = false; + + switch($data['task']){ + case 'characterLogout': + $response = $this->unSubscribeCharacterId($load); + break; + case 'mapConnectionAccess': + $response = $this->setConnectionAccess($load); + break; + case 'mapAccess': + $response = $this->setAccess($task, $load); + break; + case 'mapUpdate': + $response = $this->broadcastMapUpdate($task, $load); + break; + case 'mapDeleted': + $response = $this->deleteMapId($task, $load); + break; + case 'healthCheck': + $response = 1; + break; + } + + $this->internalSocket->send($response); + } + + /** + * @param string $task + * @param array $mapData + * @return int + */ + private function broadcastMapUpdate($task, $mapData){ + $mapId = (int)$mapData['config']['id']; + + return $this->broadcastMapData($task, $mapId, $mapData); + } + + /** + * send map data to ALL connected clients + * @param string $task + * @param int $mapId + * @param mixed $load + * @return int + */ + private function broadcastMapData($task, $mapId, $load){ + $characterIds = $this->getCharacterIdsByMapId($mapId); + $connections = $this->getConnectionsByCharacterIds($characterIds); + + $this->broadcastData($connections, $task, $load, $characterIds); + return count($connections); + } + + /** + * set/update map access for allowed characterIds + * @param string $task + * @param array $accessData + * @return int count of connected characters + */ + private function setAccess($task, $accessData){ + $NewMapCharacterIds = []; + + if($mapId = (int)$accessData['id']){ + $characterIds = (array)$accessData['characterIds']; + $currentMapCharacterIds = array_values((array)$this->subscriptions[$mapId]); + + // check all charactersIds that have access... ---------------------------------------- + foreach($characterIds as $characterId){ + // ... for it least ONE active connection ... + if( !empty($this->characters[$characterId]) ){ + // ... add characterId to new subscriptions for a map + $NewMapCharacterIds[$characterId] = $characterId; + } + } + + // broadcast "map delete" to no longer valid characters ------------------------------- + $removedMapCharacterIds = array_diff($currentMapCharacterIds, array_values($NewMapCharacterIds) ); + $removedMapCharacterConnections = $this->getConnectionsByCharacterIds($removedMapCharacterIds); + $this->broadcastData($removedMapCharacterConnections, $task, $mapId, $removedMapCharacterIds); + + // update map subscriptions ----------------------------------------------------------- + if( !empty($NewMapCharacterIds) ){ + // set new characters that have map access (overwrites existing subscriptions for that map) + $this->subscriptions[$mapId] = $NewMapCharacterIds; + }else{ + // no characters (left) on this map + unset($this->subscriptions[$mapId]); + } + } + return count($NewMapCharacterIds); + } + + /** + * set map access data (whitelist) tokens for map access + * @param $connectionAccessData + * @return bool + */ + private function setConnectionAccess($connectionAccessData) { + $response = false; + $characterId = (int)$connectionAccessData['id']; + $characterToken = $connectionAccessData['token']; + + if( + $characterId && + $characterToken + ){ + // expire time for character and map tokens + $expireTime = time() + $this->mapAccessExpireSeconds; + + // tokens for character access + $this->characterAccessData[$characterId][] = [ + 'token' => $characterToken, + 'expire' => $expireTime + ]; + + foreach((array)$connectionAccessData['mapData'] as $mapData){ + $mapId = (int)$mapData['id']; + + $this->mapAccessData[$mapId][$characterId][] = [ + 'token' => $mapData['token'], + 'expire' => $expireTime + ]; + } + + $response = 'OK'; + } + + return $response; + } +} \ No newline at end of file diff --git a/app/WebSockets.php b/app/WebSockets.php index 9c27f55..3972c15 100644 --- a/app/WebSockets.php +++ b/app/WebSockets.php @@ -12,23 +12,51 @@ use Ratchet\Server\IoServer; use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; +use React; + class WebSockets { - function __construct(){ - $this->startChat(); + protected $dns; + protected $wsListenPort; + protected $wsListenHost; + + function __construct($dns, $wsListenPort, $wsListenHost){ + $this->dns = $dns; + $this->wsListenPort = (int)$wsListenPort; + $this->wsListenHost = $wsListenHost; + + $this->startMapSocket(); } - private function startChat(){ - $server = IoServer::factory( + private function startMapSocket(){ + $loop = React\EventLoop\Factory::create(); + + // Listen for the web server to make a ZeroMQ push after an ajax request + $context = new React\ZMQ\Context($loop); + + $pull = $context->getSocket(\ZMQ::SOCKET_REP); + // Binding to 127.0.0.1 means, the only client that can connect is itself + $pull->bind( $this->dns ); + + // main app -> inject socket for response + $mapUpdate = new Main\MapUpdate($pull); + // "onMessage" listener + $pull->on('message', [$mapUpdate, 'receiveData']); + + // Set up our WebSocket server for clients wanting real-time updates + $webSock = new React\Socket\Server($loop); + // Binding to 0.0.0.0 means remotes can connect (Web Clients) + $webSock->listen($this->wsListenPort, $this->wsListenHost); + new IoServer( new HttpServer( new WsServer( - new Main\Chat() + $mapUpdate ) ), - 8020 + $webSock ); - $server->run(); + $loop->run(); } } \ No newline at end of file diff --git a/cmd.php b/cmd.php index f01c96a..7d1c44b 100644 --- a/cmd.php +++ b/cmd.php @@ -3,4 +3,34 @@ require 'vendor/autoload.php'; use Exodus4D\Socket; -new Socket\WebSockets(); \ No newline at end of file +if(PHP_SAPI === 'cli'){ + // optional CLI params + $options = getopt('', [ + 'pf_listen_host:', + 'pf_listen_port:', + 'pf_host:', + 'pf_port:' + ]); + + /** + * WebSocket connection (for WebClients => Browser) + * default WebSocket URI: ws://127.0.0.1:8020 + * + * pf_client_ip '0.0.0.0' <-- any client can connect + * pf_ws_port 8020 <-- any client can connect + */ + $wsListenHost = (!empty($options['pf_listen_host'])) ? $options['pf_listen_host'] : '0.0.0.0' ; + $wsListenPort = (!empty($options['pf_listen_port'])) ? (int)$options['pf_listen_port'] : 8020 ; + + $host = (!empty($options['pf_host'])) ? $options['pf_host'] : '127.0.0.1' ; + $port = (!empty($options['pf_port'])) ? (int)$options['pf_port'] : 5555 ; + + $dns = 'tcp://' . $host . ':' . $port; + + new Socket\WebSockets($dns, $wsListenPort, $wsListenHost); +}else{ + echo "Script need to be called by CLI!"; +} + + + diff --git a/composer.json b/composer.json index 423d2dc..b774875 100644 --- a/composer.json +++ b/composer.json @@ -1,10 +1,23 @@ { - "require": { - "cboden/ratchet": "dev-master" - }, + "name": "exodus4d/pathfinder_websocket", + "description": "WebSocket extension for 'Pathfinder'", + "minimum-stability": "stable", + "license": "MIT", + "authors": [ + { + "name": "Mark Friedrich", + "email": "pathfinder@exodus4d.de" + } + ], "autoload": { "psr-4": { "Exodus4D\\Socket\\": "app/" } + }, + "require": { + "php-64bit": ">=7.0", + "ext-zmq": "1.1.*", + "cboden/ratchet": "0.3.*", + "react/zmq": "0.3.*" } } \ No newline at end of file diff --git a/css/style.css b/css/style.css deleted file mode 100644 index fd0079b..0000000 --- a/css/style.css +++ /dev/null @@ -1,72 +0,0 @@ -.hidden { - display: none; -} - -#wrapper { - width: 800px; - margin: 0 auto; -} - -#leave-room { - margin-bottom: 10px; - float: right; -} - -#user-container { - width: 500px; - margin: 0 auto; - text-align: center; -} - -#main-container { - width: 500px; - margin: 0 auto; -} - -#messages { - height: 300px; - width: 500px; - border: 1px solid #ccc; - padding: 20px; - text-align: left; - overflow-y: scroll; -} - -#msg-container { - padding: 20px; -} - -#msg { - width: 400px; -} - -.user { - font-weight: bold; -} - -.msg { - margin-bottom: 10px; - overflow: hidden; -} - -.time { - float: right; - color: #939393; - font-size: 13px; -} - -.details { - margin-top: 20px; -} - -#socket-status, -#notification-status { - font-size: 40px; -} -.red{ - color: red; -} - -.green{ - color: green; -} \ No newline at end of file diff --git a/index.html b/index.html deleted file mode 100644 index 46b38a6..0000000 --- a/index.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - Chat - - - - - - - -
- Status: - Notification: - -
- - - -
- - - -
- - - - - - - - \ No newline at end of file diff --git a/js/app.js b/js/app.js deleted file mode 100644 index c4cad57..0000000 --- a/js/app.js +++ /dev/null @@ -1,162 +0,0 @@ -var domain = location.host; -var workerProtocol = (window.location.protocol === 'https:') ? 'wss:' : 'ws:'; -var workerUri = workerProtocol + '//' + domain + '/ws/chat'; - -window.onload = function (){ - - var msgWorker = this.msgWorker; - - var worker = new SharedWorker('js/worker/chat.js', 'worker_name'); - - worker.port.addEventListener('message', function(e){ - let load = e.data; - load.__proto__ = msgWorker.prototype; - - switch(load.command){ - case 'ws:open': - // WebSocket in SharedWorker is open - setSocketStatus(true); - initChat(); - break; - case 'ws:send': - updateMessages(load.data()); - break; - case 'ws:closed': - setSocketStatus(false); - break; - } - - // show webSocket info - console.info(load.socket); - }, false); - - worker.onerror = function(e){ - console.error('SharedWorker onerror:'); - }; - - worker.port.start(); - - var msgWorkerInit = new msgWorker('ws:init'); - msgWorkerInit.data({ - uri: workerUri - }); - worker.port.postMessage(msgWorkerInit); - - - // Chat init ================================================================================== - var user; - var messages = []; - - var messages_template = Handlebars.compile($('#messages-template').html()); - - var initChat = function(){ - - $('#join-chat').click(function(){ - user = $('#user').val(); - $('#user-container').addClass('hidden'); - $('#main-container').removeClass('hidden'); - - var msgWorkerSend = new msgWorker('ws:send'); - msgWorkerSend.data({ - 'user': user, - 'text': user + ' entered the room', - 'time': moment().format('hh:mm a') - }); - - worker.port.postMessage(msgWorkerSend); - - $('#user').val(''); - }); - - $('#send-msg').click(function(){ - var text = $('#msg').val(); - - var msgWorkerSend = new msgWorker('ws:send'); - msgWorkerSend.data({ - 'user': user, - 'text': text, - 'time': moment().format('hh:mm a') - }); - - worker.port.postMessage(msgWorkerSend); - - $('#msg').val(''); - }); - - $('#leave-room').click(function(){ - var msgWorkerSend = new msgWorker('ws:send'); - msgWorkerSend.data({ - 'user': user, - 'text': user + ' has left the room', - 'time': moment().format('hh:mm a') - }); - - worker.port.postMessage(msgWorkerSend); - - $('#messages').html(''); - messages = []; - - $('#main-container').addClass('hidden'); - $('#user-container').removeClass('hidden'); - - - }); - }; - - var setSocketStatus = function(status){ - $('#socket-status').toggleClass('red', !status).toggleClass('green', status); - }; - - var updateMessages = function(msg){ - messages.push(msg); - var messages_html = messages_template({'messages': messages}); - $('#messages').html(messages_html); - $("#messages").animate({ scrollTop: $('#messages')[0].scrollHeight}, 1000); - }; - - // Notification init ========================================================================== - - var updateNotification = function(status){ - $('#notification-status').toggleClass('red', !status).toggleClass('green', status); - }; - - var notifyMe = function(){ - var msgWorkerNotify = new msgWorker('ws:notify'); - - if (Notification.permission === 'granted'){ - msgWorkerNotify.data({ - status: true - }); - worker.port.postMessage(msgWorkerNotify); - - updateNotification(true); - }else{ - Notification.requestPermission(function (permission) { - msgWorkerNotify.data({ - status: permission === 'granted' - }); - worker.port.postMessage(msgWorkerNotify); - - updateNotification(permission === 'granted'); - }); - } - }; - - $('#toggle-notification').on('click', notifyMe); - - // ============================================================================================ - /* - window.onbeforeunload = function() { - var msgWorkerClose = new msgWorker('ws:close'); - worker.port.postMessage(msgWorkerClose); - - //console.log('test close'); - //worker.port.close(); - - return 'sdf'; - }; - */ -}; - - - diff --git a/js/worker/chat.js b/js/worker/chat.js deleted file mode 100644 index 6e3c509..0000000 --- a/js/worker/chat.js +++ /dev/null @@ -1,90 +0,0 @@ -'use strict'; -self.importScripts('message.js'); - -var socket = null; -var ports = []; -var notifications = false; - -var initSocket = function(uri){ - var msgWorkerOpen = new msgWorker('ws:open'); - - if(socket === null){ - socket = new WebSocket(uri); - - socket.onopen = function(e){ - msgWorkerOpen.socket = this; - - ports[ports.length - 1].postMessage(msgWorkerOpen); - - socket.onmessage = function(e){ - let load = JSON.parse(e.data); - - let msgWorkerSend = new msgWorker('ws:send'); - msgWorkerSend.socket = this; - - msgWorkerSend.data(load); - - for (let i = 0; i < ports.length; i++) { - ports[i].postMessage(msgWorkerSend); - } - - if(notifications){ - new Notification('Message: ' + load.text); - } - }; - - socket.onclose = function(){ - let msgWorkerWsClosed = new msgWorker('ws:closed'); - msgWorkerWsClosed.socket = this; - - console.log(socket.readyState); - for (let i = 0; i < ports.length; i++) { - ports[i].postMessage(msgWorkerWsClosed); - } - }; - - socket.onerror = function(){ - console.error('ws: onerror()'); - }; - } - }else{ - // socket still open - ports[ports.length - 1].postMessage(msgWorkerOpen); - } -}; - -self.addEventListener('connect', function (event){ - let port = event.ports[0]; - ports.push(port); - - port.addEventListener('message', function (e){ - let load = e.data; - load.__proto__ = msgWorker.prototype; - - switch(load.command){ - case 'ws:init': - initSocket(load.data().uri); - break; - case 'ws:send': - socket.send(JSON.stringify(load.data())); - break; - case 'ws:close': - closeSocket(); - break; - case 'ws:notify': - notifications = load.data().status; - break; - } - }, false); - - port.start(); -}, false); - - -// Util ================================================================ -var closeSocket = function(){ - // only close if active - if(socket.readyState === socket.OPEN){ - socket.close(); - } -}; diff --git a/js/worker/message.js b/js/worker/message.js deleted file mode 100644 index 34ab7c4..0000000 --- a/js/worker/message.js +++ /dev/null @@ -1,36 +0,0 @@ -var msgWorker = class MessageWorker { - - constructor(cmd){ - // message properties - this.cmd = cmd; - this.msgBody = null; - - // webSocket props - this.ws = { - url: undefined, - readyState: undefined, - }; - } - - set socket(socket){ - this.ws.url = socket.url; - this.ws.readyState = socket.readyState; - } - - get socket(){ - return this.ws; - } - - get command(){ - return this.cmd; - } - - - data(data) { - if(data){ - this.msgBody = data; - } - - return this.msgBody; - } -};