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"); } }