diff options
Diffstat (limited to 'src/Server.php')
-rw-r--r-- | src/Server.php | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/src/Server.php b/src/Server.php new file mode 100644 index 0000000..46c7dca --- /dev/null +++ b/src/Server.php @@ -0,0 +1,111 @@ +<?php + +namespace GeminiFoundation; + +class Server +{ + protected string $hostname; + + protected array $certificate = [ + 'file' => null, + 'key' => null, + 'passphrase' => null, + ]; + + protected array $requestHandlers = []; + + /** + * @param array $certificate + * @param string $hostname + */ + public function __construct( + array $certificate, + string $hostname = 'localhost' + ) + { + $this->certificate = $certificate; + $this->hostname = $hostname; + } + + public function setCertificate(string $certificateFile, string $keyFile, string $passphrase = ''): static + { + $this->certificate = [ + 'file' => $certificateFile, + 'key' => $keyFile, + 'passphrase' => $passphrase, + ]; + + return $this; + } + + public function onRequest(RequestHandlerInterface|callable $callable): static + { + $this->requestHandlers[] = $callable; + + return $this; + } + + public function listen(int $port = 1965): void + { + $context = stream_context_create(options: [ + 'ssl' => [ + 'local_cert' => $this->certificate['file'], + 'local_pk' => $this->certificate['key'], + 'passphrase' => $this->certificate['passphrase'], + + 'allow_self_signed' => true, + 'verify_peer' => false, + ], + ]); + + $socket = stream_socket_server( + address: "tls://{$this->hostname}:{$port}", + context: $context + ); + + $connections = []; + + while (true) { + $connection = stream_socket_accept( + socket: $socket, + timeout: empty($connections) ? -1 : 0, + peer_name: $peer + ); + + if ($connection) { + $connections[$peer] = $connection; + } + + if (count($connections) == 0) { + continue; + } + + $streams = stream_select( + read: $connections, + write: $write, + except: $except, + seconds: 5 + ); + + if ($streams) { + foreach ($connections as $peer => $connection) { + if (feof($connection)) { + fclose($connection); + unset($connections[$peer]); + continue; + } + + $request = Request::fromResource($connection); + $response = new Response(); + foreach ($this->requestHandlers as $requestHandler) { + $response = $requestHandler($response, $request); + } + $response->send($connection); + + fclose($connection); + unset($connections[$peer]); + } + } + } + } +} |