281 lines
8.7 KiB
PHP
281 lines
8.7 KiB
PHP
<?php declare(strict_types=1);
|
|
|
|
namespace Orbit\Tests;
|
|
|
|
use Monolog\Logger;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
use Orbit\Cert;
|
|
use Orbit\Config;
|
|
use Orbit\Server;
|
|
use Orbit\Request;
|
|
use Orbit\Response;
|
|
|
|
final class ServerTest extends TestCase
|
|
{
|
|
public function makeTestLogger(): Logger
|
|
{
|
|
$logger = new Logger('test-orbit');
|
|
$logger->pushHandler(new \Monolog\Handler\TestHandler());
|
|
|
|
return $logger;
|
|
}
|
|
|
|
public function getTestLogRecords($logger): array
|
|
{
|
|
return $logger->getHandlers()[0]->getRecords();
|
|
}
|
|
|
|
public function getTestLogMessages($logger): array
|
|
{
|
|
$messages = [];
|
|
foreach ($this->getTestLogRecords($logger) as $message) {
|
|
$messages[] = $message['level_name'] . ": " . $message['message'];
|
|
}
|
|
|
|
return $messages;
|
|
}
|
|
|
|
public function testConstructFailsWithoutConfig(): void
|
|
{
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage("Missing required cert file");
|
|
|
|
$config = new Config();
|
|
$server = new Server($config);
|
|
}
|
|
|
|
public function testConstructAutoCert(): void
|
|
{
|
|
$config = new Config(true);
|
|
$server = new Server($config);
|
|
$this->assertInstanceOf(Server::class, $server);
|
|
$this->assertEquals(1, count($server->getLogger()->getHandlers()));
|
|
}
|
|
|
|
public function testConstructSetLogger(): void
|
|
{
|
|
$config = new Config(true);
|
|
$logger = new Logger('orbit-test');
|
|
$server = new Server($config, null, $logger);
|
|
|
|
$l = $server->getLogger();
|
|
$this->assertSame($l, $logger);
|
|
$this->assertEquals(0, count($l->getHandlers()));
|
|
}
|
|
|
|
public function testConstructSetCert(): void
|
|
{
|
|
$config = new Config(true);
|
|
$cert = new Cert($config);
|
|
$server = new Server($config, $cert);
|
|
|
|
$this->assertSame($cert, $server->cert);
|
|
}
|
|
|
|
public function testConstructSetsTimeout(): void
|
|
{
|
|
ini_set("default_socket_timeout", "32");
|
|
|
|
$server = new Server(new Config(true));
|
|
$this->assertEquals(32, $server->timeout);
|
|
}
|
|
|
|
public function testIntakeConnectionsMustBeResource(): void
|
|
{
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage("Stream server must be resource object");
|
|
|
|
$server = new Server(new Config(true));
|
|
|
|
$s = new \stdClass();
|
|
$result = $server->intakeConnections($s);
|
|
}
|
|
|
|
public function testValidateRequestMustContainHost(): void
|
|
{
|
|
$request = new Request('');
|
|
|
|
$server = new Server(new Config(true));
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertFalse($is_valid);
|
|
$this->assertSame(Response::STATUS_BAD_REQUEST, $response->status);
|
|
$this->assertStringContainsString("Bad request", $response->meta);
|
|
}
|
|
|
|
public function testValidateRequestMustHaveCorrectScheme(): void
|
|
{
|
|
$request = new Request('https://foo.bar/');
|
|
|
|
$server = new Server(new Config(true));
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertFalse($is_valid);
|
|
$this->assertSame(Response::STATUS_PROXY_REQUEST_REFUSED, $response->status);
|
|
$this->assertStringContainsString("unsupported scheme", $response->meta);
|
|
}
|
|
|
|
public function testValidateRequestMustHaveCorrectHost(): void
|
|
{
|
|
$request = new Request('gemini://superfly.com/');
|
|
|
|
$server = new Server(new Config(true));
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertFalse($is_valid);
|
|
$this->assertSame(Response::STATUS_PROXY_REQUEST_REFUSED, $response->status);
|
|
$this->assertStringContainsString("invalid host", $response->meta);
|
|
}
|
|
|
|
public function testValidateRequestMustHaveCorrectPort(): void
|
|
{
|
|
$request = new Request('gemini://localhost:8080/');
|
|
|
|
$server = new Server(new Config(true));
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertFalse($is_valid);
|
|
$this->assertSame(Response::STATUS_PROXY_REQUEST_REFUSED, $response->status);
|
|
$this->assertStringContainsString("invalid port", $response->meta);
|
|
}
|
|
|
|
public function testValidateRequestCanIncludePort(): void
|
|
{
|
|
$request = new Request('gemini://localhost:1965/');
|
|
|
|
$server = new Server(new Config(true));
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertTrue($is_valid);
|
|
$this->assertSame('', $response->status);
|
|
}
|
|
|
|
public function testValidateRequestMustNotBeMoreThanLimit(): void
|
|
{
|
|
$url = 'gemini://localhost/';
|
|
$request = new Request($url . str_repeat("x", 1025 - strlen($url)));
|
|
|
|
$server = new Server(new Config(true));
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertFalse($is_valid);
|
|
$this->assertSame(Response::STATUS_BAD_REQUEST, $response->status);
|
|
$this->assertStringContainsString("too long", $response->meta);
|
|
}
|
|
|
|
public function testValidateRequestCanBeAtLimit(): void
|
|
{
|
|
$url = 'gemini://localhost/';
|
|
$request = new Request($url . str_repeat("x", 1024 - strlen($url)));
|
|
|
|
$server = new Server(new Config(true));
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertTrue($is_valid);
|
|
$this->assertSame('', $response->status);
|
|
}
|
|
|
|
public function testValidateRequestMustNotContainNonUtf8Bytes(): void
|
|
{
|
|
$request = new Request('gemini://localhost/' . chr(240) . chr(159) . chr(144) . chr(152));
|
|
|
|
$server = new Server(new Config(true));
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertFalse($is_valid);
|
|
$this->assertSame(Response::STATUS_BAD_REQUEST, $response->status);
|
|
$this->assertStringContainsString("non-UTF8", $response->meta);
|
|
}
|
|
|
|
public function testValidateRequestLocalhostIsValid(): void
|
|
{
|
|
$request = new Request('gemini://localhost/');
|
|
|
|
$server = new Server(new Config(true));
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertTrue($is_valid);
|
|
$this->assertSame('', $response->status);
|
|
}
|
|
|
|
public function testValidateRequestLoopbackAddrIsValid(): void
|
|
{
|
|
$request = new Request('gemini://127.0.0.1/');
|
|
|
|
$server = new Server(new Config(true));
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertTrue($is_valid);
|
|
$this->assertSame('', $response->status);
|
|
}
|
|
|
|
public function testValidateRequestMatchesConfigHostname(): void
|
|
{
|
|
$request = new Request('gemini://dogdays.dev/');
|
|
|
|
$config = new Config(true);
|
|
$config->hostname = 'dogdays.dev';
|
|
$server = new Server($config);
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertTrue($is_valid);
|
|
$this->assertSame('', $response->status);
|
|
@unlink("certs/dogdays.dev.cert.pem");
|
|
@unlink("certs/dogdays.dev.key.pem");
|
|
}
|
|
|
|
public function testValidateRequestMatchesConfigPort(): void
|
|
{
|
|
$request = new Request('gemini://localhost:1972/');
|
|
|
|
$config = new Config(true);
|
|
$config->port = '1972';
|
|
$server = new Server($config);
|
|
[$is_valid, $response] = $server->validateRequest($request);
|
|
|
|
$this->assertTrue($is_valid);
|
|
$this->assertSame('', $response->status);
|
|
}
|
|
|
|
public function testOnWarningTimedOut(): void
|
|
{
|
|
$logger = $this->makeTestLogger();
|
|
$server = new Server(new Config(true), null, $logger);
|
|
|
|
$result = $server->onWarning(1, 'Connection timed out');
|
|
$this->assertTrue($result);
|
|
$messages = $this->getTestLogMessages($logger);
|
|
$this->assertNotContains('Error while accepting', $messages);
|
|
}
|
|
|
|
public function testOnWarningError(): void
|
|
{
|
|
$logger = $this->makeTestLogger();
|
|
$server = new Server(new Config(true), null, $logger);
|
|
|
|
$result = $server->onWarning(1, 'Something terrible happened');
|
|
$this->assertTrue($result);
|
|
$messages = $this->getTestLogMessages($logger);
|
|
$this->assertStringContainsString('Error while accepting connection', $messages[3]);
|
|
}
|
|
|
|
public function testGetListenAddress(): void
|
|
{
|
|
$config = new Config(true);
|
|
$config->host = '127.0.0.1';
|
|
$config->port = 1888;
|
|
$server = new Server($config);
|
|
|
|
$address = $server->getListenAddress();
|
|
|
|
$this->assertSame('tls://127.0.0.1:1888', $address);
|
|
}
|
|
|
|
public function tearDown(): void
|
|
{
|
|
@unlink("certs/localhost.cert.pem");
|
|
@unlink("certs/localhost.key.pem");
|
|
}
|
|
}
|