diff --git a/src/FreeDSx/Socket/SocketServerOptions.php b/src/FreeDSx/Socket/SocketServerOptions.php index 1079e4c..6113c91 100644 --- a/src/FreeDSx/Socket/SocketServerOptions.php +++ b/src/FreeDSx/Socket/SocketServerOptions.php @@ -30,6 +30,8 @@ final class SocketServerOptions implements SocketOptionsInterface private ?int $backlog = null; + private bool $tcpNoDelay = false; + public function __construct() { $this->setSslValidateCert(false); @@ -112,6 +114,21 @@ public function getBacklog(): ?int return $this->backlog; } + /** + * Disable Nagle's algorithm on accepted TCP connections. + */ + public function setTcpNoDelay(bool $tcpNoDelay): self + { + $this->tcpNoDelay = $tcpNoDelay; + + return $this; + } + + public function isTcpNoDelay(): bool + { + return $this->tcpNoDelay; + } + /** * The socket-level stream context options, omitting anything left at its default so the kernel decides. * @@ -130,6 +147,9 @@ public function toStreamContextSocketOptions(): array if ($this->backlog !== null) { $opts['backlog'] = $this->backlog; } + if ($this->tcpNoDelay) { + $opts['tcp_nodelay'] = true; + } return $opts; } diff --git a/tests/unit/SocketServerOptionsTest.php b/tests/unit/SocketServerOptionsTest.php index 8c093ac..7c809f4 100644 --- a/tests/unit/SocketServerOptionsTest.php +++ b/tests/unit/SocketServerOptionsTest.php @@ -38,6 +38,7 @@ public function test_it_defaults_the_socket_options_to_off(): void self::assertFalse($this->subject->isReusePort()); self::assertFalse($this->subject->isReuseAddress()); self::assertNull($this->subject->getBacklog()); + self::assertFalse($this->subject->isTcpNoDelay()); } public function test_it_emits_the_port_reuse_option(): void @@ -70,18 +71,30 @@ public function test_it_emits_the_backlog_option(): void ); } + public function test_it_emits_the_tcp_no_delay_option(): void + { + $this->subject->setTcpNoDelay(true); + + self::assertSame( + ['tcp_nodelay' => true], + $this->subject->toStreamContextSocketOptions(), + ); + } + public function test_it_emits_every_option_that_was_set(): void { $this->subject ->setReusePort(true) ->setReuseAddress(true) - ->setBacklog(128); + ->setBacklog(128) + ->setTcpNoDelay(true); self::assertSame( [ 'so_reuseport' => true, 'so_reuseaddr' => true, 'backlog' => 128, + 'tcp_nodelay' => true, ], $this->subject->toStreamContextSocketOptions(), ); @@ -93,7 +106,9 @@ public function test_it_omits_an_option_that_was_switched_back_off(): void ->setReusePort(true) ->setReusePort(false) ->setBacklog(64) - ->setBacklog(null); + ->setBacklog(null) + ->setTcpNoDelay(true) + ->setTcpNoDelay(false); self::assertSame( [], diff --git a/tests/unit/SocketServerTest.php b/tests/unit/SocketServerTest.php index e2eb744..3eefe68 100644 --- a/tests/unit/SocketServerTest.php +++ b/tests/unit/SocketServerTest.php @@ -117,6 +117,31 @@ public function test_it_should_construct_a_unix_based_socket_server(): void self::assertFalse($this->subject->isConnected()); } + public function test_it_should_still_serve_a_unix_client_when_tcp_no_delay_is_set(): void + { + $this->requireUnixTransport(); + + $this->subject = SocketServer::bindUnix( + $this->testSocket, + (new SocketServerOptions())->setTcpNoDelay(true), + ); + $client = Socket::unix($this->testSocket); + + try { + $accepted = $this->subject->accept(1.0); + self::assertNotNull($accepted); + + $client->write('foo'); + + self::assertSame( + 'foo', + $accepted->read(), + ); + } finally { + $client->close(); + } + } + public function test_it_should_receive_data(): void { $this->subject = SocketServer::bindUdp('0.0.0.0', 33389);