Configuration fixes, makefile tweaks

* The PHAR now gets tagged with version and buildtime
* WebSocket support can now be disabled
This commit is contained in:
Chris 2024-03-12 01:13:19 +01:00
parent da450b510a
commit b3476881e1
7 changed files with 77 additions and 33 deletions

View File

@ -13,6 +13,7 @@ phar: ## Build .phar using pharlite
git clone $(REPODIR) $(BUILDDIR) && \ git clone $(REPODIR) $(BUILDDIR) && \
cd $(BUILDDIR) && \ cd $(BUILDDIR) && \
composer install --no-dev && \ composer install --no-dev && \
echo "<?php return [ 'version' => '$(shell git describe --tags)', 'buildtime' => '$(shell date)' ];" > src/meta && \
pharlite pharlite
mv $(BUILDDIR)/*.phar $(REPODIR) && \ mv $(BUILDDIR)/*.phar $(REPODIR) && \
rm -rf $(BUILDDIR) rm -rf $(BUILDDIR)

View File

@ -3,15 +3,25 @@
use NoccyLabs\Mercureact\Configuration; use NoccyLabs\Mercureact\Configuration;
use NoccyLabs\Mercureact\Daemon; use NoccyLabs\Mercureact\Daemon;
use PHPUnit\TextUI\Help;
require_once __DIR__."/../vendor/autoload.php"; file_exists(__DIR__."/../vendor") && require_once __DIR__."/../vendor/autoload.php";
file_exists(__DIR__."/../../../autoload.php") && require_once __DIR__."/../../../autoload.php";
if (file_exists(__DIR__."/../src/meta")) {
$meta = require_once(__DIR__."/../src/meta");
define("MERCUREACT_VERSION", $meta['version']??'0.0.0');
define("MERCUREACT_BUILDTIME", $meta['buildtime']);
} else {
define("MERCUREACT_VERSION", "DEV");
define("MERCUREACT_BUILDTIME", null);
}
$opts = getopt("c:C:h"); $opts = getopt("c:C:h");
if (isset($opts['h'])) { if (isset($opts['h'])) {
$info = "v".MERCUREACT_VERSION.(MERCUREACT_BUILDTIME?("\nBuilt on ".MERCUREACT_BUILDTIME):"");
fwrite(STDERR, <<<HELP fwrite(STDERR, <<<HELP
Mercureact Realtime SSE Daemon Mercureact Realtime SSE Daemon {$info}
(c) 2024, NoccyLabs - Distributed under GNU GPL v3 or later. (c) 2024, NoccyLabs - Distributed under GNU GPL v3 or later.
Options: Options:
@ -32,8 +42,9 @@ if (isset($opts['C'])) {
} }
file_put_contents($file, <<<DEFAULTS file_put_contents($file, <<<DEFAULTS
server: server:
public_url: https://example.com
address: 0.0.0.0:9000 address: 0.0.0.0:9000
public_url: https://example.com
websockets: false
cors: cors:
allow_origin: '*' allow_origin: '*'
csp: "default-src * 'self' http: 'unsafe-eval' 'unsafe-inline'; connect-src * 'self'" csp: "default-src * 'self' http: 'unsafe-eval' 'unsafe-inline'; connect-src * 'self'"
@ -41,9 +52,6 @@ if (isset($opts['C'])) {
cert: foo.pem cert: foo.pem
key: foo.key key: foo.key
websocket:
enable: true
publish: publish:
overwrite_id: false overwrite_id: false
reject_duplicates: true reject_duplicates: true
@ -71,4 +79,4 @@ if (isset($opts['c'])) {
} }
$daemon = new Daemon($config); $daemon = new Daemon($config);
$daemon->start(); $daemon->start();

View File

@ -2,3 +2,6 @@
This is alpha quality software. Expect, and please report, any bugs encountered. This is alpha quality software. Expect, and please report, any bugs encountered.
## Authentication
When using the query thing for authentication, keep in mind that the authentication details may be saved to logs. `mercureactd` itself doese not log more than the path by default, but any downstream proxies may not be as caring. This is part of the reason why the Mercure specification doesn't care much for WebSockets -- they are hard to secure.

View File

@ -2,7 +2,10 @@
# Please make a copy of me before editing # Please make a copy of me before editing
server: server:
# Listen address
address: 0.0.0.0:9000 address: 0.0.0.0:9000
# Enable websocket support (experimental)
websockets: false
# Setup CORS headers # Setup CORS headers
cors: cors:
@ -16,10 +19,6 @@ server:
cert: foo.pem cert: foo.pem
key: foo.key key: foo.key
# Enable websockets
websocket:
enable: true
publish: publish:
# Assign a UUID to published messages even if one is already set in the message # Assign a UUID to published messages even if one is already set in the message
overwrite_id: false overwrite_id: false

View File

@ -97,5 +97,16 @@ class Configuration
return $this; return $this;
} }
public function getEnableWebSockets(): bool
{
return (bool)($this->config['server.websockets']??false);
}
public function setEnableWebSockets(bool $enable): self
{
$this->config['server.websockets'] = $enable;
return $this;
}
} }

View File

@ -2,7 +2,10 @@
namespace NoccyLabs\Mercureact; namespace NoccyLabs\Mercureact;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use NoccyLabs\Mercureact\Http\Server; use NoccyLabs\Mercureact\Http\Server;
use Psr\Log\LoggerInterface;
use React\EventLoop\Loop; use React\EventLoop\Loop;
use React\EventLoop\LoopInterface; use React\EventLoop\LoopInterface;
use React\Socket\SocketServer; use React\Socket\SocketServer;
@ -15,19 +18,33 @@ class Daemon
private Server $server; private Server $server;
private LoggerInterface $logger;
public function __construct(Configuration $config, ?LoopInterface $loop=null) public function __construct(Configuration $config, ?LoopInterface $loop=null)
{ {
$this->config = $config; $this->config = $config;
$this->loop = $loop??Loop::get(); $this->loop = $loop??Loop::get();
$this->logger = $this->createLogger();
}
private function createLogger(): Logger
{
$handlers = [
new StreamHandler(STDOUT)
];
$logger = new Logger("main", $handlers);
return $logger;
} }
public function start(): void public function start(): void
{ {
$this->server = new Server($this->config, $this->loop); $this->server = new Server($this->config, $this->logger, $this->loop);
$this->logger->info("NoccyLabs Mercureact Daemon v".MERCUREACT_VERSION);
$listenAddress = $this->config->getListenAddress(); $listenAddress = $this->config->getListenAddress();
if (!$listenAddress) { if (!$listenAddress) {
fwrite(STDERR, "Warning: Empty listening address. You won't make it far.\n"); $this->logger->warning("Empty listening address. You won't make it far.");
return; return;
} }
$socket = new SocketServer("tcp://".$listenAddress); $socket = new SocketServer("tcp://".$listenAddress);

View File

@ -13,9 +13,11 @@ use NoccyLabs\Mercureact\Http\Middleware\ResponseMiddleware;
use NoccyLabs\Mercureact\Http\Middleware\SecurityMiddleware; use NoccyLabs\Mercureact\Http\Middleware\SecurityMiddleware;
use NoccyLabs\Mercureact\Http\Middleware\WebSocketHandler; use NoccyLabs\Mercureact\Http\Middleware\WebSocketHandler;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use React\EventLoop\Loop; use React\EventLoop\Loop;
use React\EventLoop\LoopInterface; use React\EventLoop\LoopInterface;
use React\Http\HttpServer; use React\Http\HttpServer;
use React\Socket\SecureServer;
use React\Socket\ServerInterface; use React\Socket\ServerInterface;
use SplObjectStorage; use SplObjectStorage;
@ -45,13 +47,13 @@ class Server
* *
* *
*/ */
public function __construct(Configuration $config, ?LoopInterface $loop=null) public function __construct(Configuration $config, ?LoggerInterface $logger, ?LoopInterface $loop=null)
{ {
$this->loop = $loop??Loop::get(); $this->loop = $loop??Loop::get();
$this->config = $config; $this->config = $config;
$this->logger = $this->createLogger(); $this->logger = $logger ?? new NullLogger();
$this->topicManager = new TopicManager(); $this->topicManager = new TopicManager();
$this->loop->addPeriodicTimer(30, function () { $this->loop->addPeriodicTimer(30, function () {
@ -70,17 +72,11 @@ class Server
*/ */
public function listen(ServerInterface $socket): void public function listen(ServerInterface $socket): void
{ {
$this->logger->info("Listening on ".$socket->getAddress()."\n");
$this->server->listen($socket); $this->server->listen($socket);
} $this->logger->info(sprintf(
"Listening on %s",
private function createLogger(): Logger str_replace("tcp://",($socket instanceof SecureServer?"https://":"http://"),$socket->getAddress())
{ ));
$handlers = [
new StreamHandler(STDOUT)
];
$logger = new Logger("main", $handlers);
return $logger;
} }
/** /**
@ -89,7 +85,7 @@ class Server
*/ */
private function createHttpServer(): HttpServer private function createHttpServer(): HttpServer
{ {
return new HttpServer( $stack = [
$this->responseMiddleware = new ResponseMiddleware( $this->responseMiddleware = new ResponseMiddleware(
config: $this->config, config: $this->config,
logger: $this->logger->withName("http"), logger: $this->logger->withName("http"),
@ -97,11 +93,18 @@ class Server
$this->securityMiddleware = new SecurityMiddleware( $this->securityMiddleware = new SecurityMiddleware(
config: $this->config config: $this->config
), ),
$this->webSocketHandler = new WebSocketHandler( ];
config: $this->config, if ($this->config->getEnableWebSockets()) {
webSocketClients: $this->webSocketClients, $stack = [ ...$stack,
topicManager: $this->topicManager $this->webSocketHandler = new WebSocketHandler(
), config: $this->config,
webSocketClients: $this->webSocketClients,
topicManager: $this->topicManager
),
];
$this->logger->warning("The WebSocket support is incomplete and insecure, but enabling it as requested.");
}
$stack = [ ...$stack,
$this->mercureHandler = new MercureHandler( $this->mercureHandler = new MercureHandler(
config: $this->config, config: $this->config,
topicManager: $this->topicManager topicManager: $this->topicManager
@ -111,7 +114,9 @@ class Server
topicManager: $this->topicManager topicManager: $this->topicManager
), ),
$this->notFoundHandler = new NotFoundHandler() $this->notFoundHandler = new NotFoundHandler()
); ];
return new HttpServer(...$stack);
} }