diff --git a/src/SocketHttpServer.php b/src/SocketHttpServer.php index 5f828ab7..22aa9592 100644 --- a/src/SocketHttpServer.php +++ b/src/SocketHttpServer.php @@ -365,6 +365,7 @@ private function handleClient( try { $driver->handleClient($client, $socket, $socket); } finally { + $client->close(); unset($this->drivers[$id]); } } catch (\Throwable $exception) { diff --git a/test/SocketHttpServerTest.php b/test/SocketHttpServerTest.php new file mode 100644 index 00000000..537c4b4e --- /dev/null +++ b/test/SocketHttpServerTest.php @@ -0,0 +1,104 @@ +client = $client; + // Return normally, as a driver does when the connection ends + // without throwing. + } + + public function getPendingRequestCount(): int + { + return 0; + } + + public function getApplicationLayerProtocols(): array + { + return []; + } + + public function stop(): void + { + } + }; + + $driverFactory = new class($driver) implements HttpDriverFactory { + public function __construct(private readonly HttpDriver $driver) + { + } + + public function createHttpDriver( + RequestHandler $requestHandler, + ErrorHandler $errorHandler, + Client $client, + ): HttpDriver { + return $this->driver; + } + + public function getApplicationLayerProtocols(): array + { + return []; + } + }; + + $server = new SocketHttpServer( + new NullLogger(), + new Socket\ResourceServerSocketFactory(), + new SocketClientFactory(new NullLogger()), + httpDriverFactory: $driverFactory, + ); + + $server->expose(Socket\SocketAddress\fromString('127.0.0.1:0')); + $server->start( + new ClosureRequestHandler(fn () => new Response(HttpStatus::OK)), + new DefaultErrorHandler(), + ); + + try { + $client = Socket\connect($server->getServers()[0]->getAddress()->toString()); + $client->write("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"); + delay(0.1); + $client->close(); + delay(0.1); + + self::assertNotNull($driver->client, 'Driver did not receive a client'); + self::assertTrue( + $driver->client->isClosed(), + 'Client was not closed after the driver returned; its socket and ' + . 'event-loop watcher leak for the lifetime of the process', + ); + } finally { + $server->stop(); + } + } +}