Skip to content

Commit

Permalink
Server setMaxConnections() config method
Browse files Browse the repository at this point in the history
  • Loading branch information
sirn-se committed Jul 2, 2024
1 parent 93bb170 commit 79bef39
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 5 deletions.
1 change: 1 addition & 0 deletions docs/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

* Server `getConnections()`, `getReadableConnections()`, `getWritableConnections()` (@sirn-se)
* `onHandshake(...)` listener (will deprecate `onConnect(...)`) (@sirn-se)
* Server `setMaxConnections(int|null)` (@sirn-se)

### `3.0.0`

Expand Down
2 changes: 2 additions & 0 deletions docs/Server.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ $server
->setTimeout(300)
// Specify frame size in bytes (default 4096 bytes)
->setFrameSize(1024)
// Set maximum number of allowed connections (default is unlimited)
->setMaxConnections(10)
;

echo "port: {$server->getPort()}\n";
Expand Down
9 changes: 7 additions & 2 deletions examples/echoserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* --ssl : Use SSL, default false
* --timeout <int> : Timeout in seconds, default 200 seconds
* --framesize <int> : Frame size in bytes, default 4096 bytes
* --connections <int> : Max number of connections, default unlimited
* --debug : Output log data (if logger is available)
*/

Expand All @@ -28,7 +29,7 @@
// Server options specified or default
$options = array_merge([
'port' => 80,
], getopt('', ['port:', 'ssl', 'timeout:', 'framesize:', 'debug']));
], getopt('', ['port:', 'ssl', 'timeout:', 'framesize:', 'connections:', 'debug']));

// Initiate server.
try {
Expand All @@ -51,6 +52,10 @@
$server->setFrameSize($options['framesize']);
echo "# Set frame size: {$options['framesize']}\n";
}
if (isset($options['connections'])) {
$server->setMaxConnections($options['connections']);
echo "# Set max connections: {$options['connections']}\n";
}

echo "# Listening on port {$server->getPort()}\n";
$server->onHandshake(function ($server, $connection, $request, $response) {
Expand Down Expand Up @@ -136,6 +141,6 @@
})->onError(function ($server, $connection, $exception) {
echo "> Error: {$exception->getMessage()}\n";
})->start();
} catch (Throwable $e) {
} catch (\Throwable $e) {
echo "# ERROR: {$e->getMessage()}\n";
}
11 changes: 9 additions & 2 deletions examples/random_server.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
*
* Console options:
* --port <int> : The port to listen to, default 80
* --ssl : Use SSL, default false
* --timeout <int> : Timeout in seconds, random default
* --framesize <int> : Frame size as bytes, random default
* --connections <int> : Max number of connections, default unlimited
* --debug : Output log data (if logger is available)
*/

Expand All @@ -38,7 +40,7 @@
'port' => 80,
'timeout' => rand(1, 60),
'framesize' => rand(1, 4096) * 8,
], getopt('', ['port:', 'ssl', 'timeout:', 'framesize:', 'debug']));
], getopt('', ['port:', 'ssl', 'timeout:', 'framesize:', 'connections:', 'debug']));

// Initiate server.
try {
Expand All @@ -47,6 +49,7 @@
->addMiddleware(new \WebSocket\Middleware\CloseHandler())
->addMiddleware(new \WebSocket\Middleware\PingResponder())
;
$server->setMaxConnections(1);

// If debug mode and logger is available
if (isset($options['debug']) && class_exists('WebSocket\Test\EchoLog')) {
Expand All @@ -61,6 +64,10 @@
$server->setFrameSize($options['framesize']);
echo "# Set frame size: {$options['framesize']}\n";
}
if (isset($options['connections'])) {
$server->setMaxConnections($options['connections']);
echo "# Set max connections: {$options['connections']}\n";
}

echo "# Listening on port {$server->getPort()}\n";
$server->onHandshake(function ($server, $connection, $request, $response) {
Expand Down Expand Up @@ -105,6 +112,6 @@
break;
}
})->start();
} catch (Throwable $e) {
} catch (\Throwable $e) {
echo "> ERROR: {$e->getMessage()}\n";
}
21 changes: 20 additions & 1 deletion src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class Server implements LoggerAwareInterface, Stringable
private bool $running = false;
private array $connections = [];
private array $middlewares = [];
private int|null $maxConnections = null;


/* ---------- Magic methods ------------------------------------------------------------------------------------ */
Expand Down Expand Up @@ -157,7 +158,7 @@ public function getTimeout(): int
*/
public function setFrameSize(int $frameSize): self
{
if ($frameSize < 1) {
if ($frameSize < 3) {
throw new InvalidArgumentException("Invalid frameSize '{$frameSize}' provided");
}
$this->frameSize = $frameSize;
Expand Down Expand Up @@ -257,6 +258,20 @@ public function addMiddleware(MiddlewareInterface $middleware): self
return $this;
}

/**
* Set maximum number of connections allowed, null means unlimited.
* @param int|null $maxConnections
* @return self
*/
public function setMaxConnections(int|null $maxConnections): self
{
if ($maxConnections < 1) {
throw new InvalidArgumentException("Invalid maxConnections '{$maxConnections}' provided");
}
$this->maxConnections = $maxConnections;
return $this;
}


/* ---------- Messaging operations ----------------------------------------------------------------------------- */

Expand Down Expand Up @@ -417,6 +432,10 @@ protected function createSocketServer(): void
protected function acceptSocket(SocketServer $socket): void
{
try {
if (!is_null($this->maxConnections) && $this->getConnectionCount() >= $this->maxConnections) {
$this->logger->warning("[server] Denied connection, reached max {$this->maxConnections}");
return;
}
$stream = $socket->accept();
$name = $stream->getRemoteName();
$this->streams->attach($stream, $name);
Expand Down
9 changes: 9 additions & 0 deletions tests/suites/server/ConfigErrorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,13 @@ public function testInvalidFrameSize(): void
$server = new Server();
$server->setFrameSize(0);
}

public function testInvalidMaxConnections(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionCode(0);
$this->expectExceptionMessage("Invalid maxConnections '0' provided");
$server = new Server();
$server->setMaxConnections(0);
}
}
1 change: 1 addition & 0 deletions tests/suites/server/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public function testServerConfiguration(): void
});
$this->assertSame($server, $server->setTimeout(300));
$this->assertSame($server, $server->setFrameSize(64));
$this->assertSame($server, $server->setMaxConnections(64));
$this->assertSame($server, $server->addMiddleware(new Callback()));

$this->assertEquals('WebSocket\Server(ssl://0.0.0.0:9000)', "{$server}");
Expand Down
35 changes: 35 additions & 0 deletions tests/suites/server/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -658,4 +658,39 @@ public function testUnmaskedException(): void
$this->expectSocketStreamClose();
unset($server);
}

public function testMaxConnectionsOverflow(): void
{
$this->expectStreamFactory();
$server = new Server(8000);
$server->setStreamFactory(new StreamFactory());
$server->setMaxConnections(1);

$this->expectWsServerSetup(scheme: 'tcp', port: 8000);
$this->expectWsSelectConnections(['@server']);
$this->expectSocketServerAccept();
$this->expectSocketStream();
$this->expectSocketStreamGetMetadata();
$this->expectSocketStreamGetRemoteName()->setReturn(function () {
return 'fake-connection-1';
});
$this->expectStreamCollectionAttach();
$this->expectSocketStreamGetLocalName()->setReturn(function () {
return 'fake-connection-1';
});
$this->expectSocketStreamGetRemoteName();
$this->expectSocketStreamSetTimeout();
$this->expectWsServerPerformHandshake();
$this->expectSocketStreamIsConnected();
$this->expectWsSelectConnections(['@server'])->addAssert(function () use ($server) {
$server->stop();
});

$server->start();
$this->assertEquals(1, $server->getConnectionCount());

$this->expectSocketStreamIsConnected();
$this->expectSocketStreamClose();
unset($server);
}
}

0 comments on commit 79bef39

Please sign in to comment.