Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ public function createClient($address, $timeout = null)
$socket->connectTimeout($address, $timeout);
$socket->setBlocking(true);
}
}
catch (Exception $e) {
} catch (Exception $e) {
$socket->close();
throw $e;
}
Expand Down Expand Up @@ -59,8 +58,7 @@ public function createServer($address)
if ($socket->getType() === SOCK_STREAM) {
$socket->listen();
}
}
catch (Exception $e) {
} catch (Exception $e) {
$socket->close();
throw $e;
}
Expand Down
7 changes: 3 additions & 4 deletions src/Socket.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,9 @@ public function connectTimeout($address, $timeout)

// socket is already connected immediately?
return $this;
}
catch (Exception $e) {
// non-blocking connect() should be EINPROGRESS => otherwise re-throw
if ($e->getCode() !== SOCKET_EINPROGRESS) {
} catch (Exception $e) {
// non-blocking connect() should be EINPROGRESS (or EWOULDBLOCK on Windows) => otherwise re-throw
if ($e->getCode() !== SOCKET_EINPROGRESS && $e->getCode() !== SOCKET_EWOULDBLOCK) {
throw $e;
}

Expand Down
50 changes: 29 additions & 21 deletions tests/FactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ public function testCreateClientTcp4UnboundFails()
{
try {
$this->factory->createClient('tcp://localhost:2');
}
catch (Exception $e) {
} catch (Exception $e) {
if ($e->getCode() === SOCKET_ECONNREFUSED) {
return;
}
Expand Down Expand Up @@ -160,13 +159,18 @@ public function testCreateServerUdp4Random()
*/
public function testCreateServerUnix()
{
$path = '/tmp/randomfactorytests.sock';
if (DIRECTORY_SEPARATOR !== '\\') {
$path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'test-' . md5(microtime(true)) . '.sock';
} else {
// create test socket in local directory on Windows
$path = 'test-' . md5(microtime(true)) . '.sock';
}

$socket = $this->factory->createServer('unix://' . $path);

$this->assertInstanceOf('Socket\Raw\Socket', $socket);

unlink($path);
$this->assertTrue(unlink($path), 'Unable to remove temporary socket ' . $path);
}

/**
Expand All @@ -179,8 +183,7 @@ public function testCreateServerUnixFailInvalidPath()

try {
$this->factory->createServer('unix://' . $path);
}
catch (Exception $e) {
} catch (Exception $e) {
return;
}

Expand All @@ -192,7 +195,10 @@ public function testCreateServerUnixFailInvalidPath()
*/
public function testCreateServerUdg()
{
// skip if not unix
if (DIRECTORY_SEPARATOR === '\\') {
// https://blogs.msdn.microsoft.com/commandline/2017/12/19/af_unix-comes-to-windows/
$this->markTestSkipped('Unix datagram sockets not supported on Windows');
}

$path = '/tmp/randomfactorytests.sock';

Expand All @@ -207,8 +213,7 @@ public function testCreateServerIcmp4()
{
try {
$socket = $this->factory->createServer('icmp://0.0.0.0');
}
catch (Exception $e) {
} catch (Exception $e) {
if ($e->getCode() === SOCKET_EPERM) {
// skip if not root
return $this->markTestSkipped('No access to ICMPv4 socket (only root can do so)');
Expand All @@ -226,8 +231,7 @@ public function testCreateServerIcmp6()
{
try {
$socket = $this->factory->createServer('icmp6://[::1]');
}
catch (Exception $e) {
} catch (Exception $e) {
if ($e->getCode() === SOCKET_EPERM) {
// skip if not root
return $this->markTestSkipped('No access to ICMPv6 socket (only root can do so)');
Expand Down Expand Up @@ -287,6 +291,11 @@ public function testCreateUnix()
*/
public function testCreateUdg()
{
if (DIRECTORY_SEPARATOR === '\\') {
// https://blogs.msdn.microsoft.com/commandline/2017/12/19/af_unix-comes-to-windows/
$this->markTestSkipped('Unix datagram sockets not supported on Windows');
}

$socket = $this->factory->createUdg();

$this->assertInstanceOf('Socket\Raw\Socket', $socket);
Expand All @@ -296,8 +305,7 @@ public function testCreateIcmp4()
{
try {
$socket = $this->factory->createIcmp4();
}
catch (Exception $e) {
} catch (Exception $e) {
if ($e->getCode() === SOCKET_EPERM) {
// skip if not root
return $this->markTestSkipped('No access to ICMPv4 socket (only root can do so)');
Expand All @@ -315,8 +323,7 @@ public function testCreateIcmp6()
{
try {
$socket = $this->factory->createIcmp6();
}
catch (Exception $e) {
} catch (Exception $e) {
if ($e->getCode() === SOCKET_EPERM) {
// skip if not root
return $this->markTestSkipped('No access to ICMPv6 socket (only root can do so)');
Expand Down Expand Up @@ -353,8 +360,7 @@ public function testCreateInvalid()
{
try {
$this->factory->create(0, 1, 2);
}
catch (Exception $e) {
} catch (Exception $e) {
return;
}
$this->fail();
Expand All @@ -365,6 +371,10 @@ public function testCreateInvalid()
*/
public function testCreatePair()
{
if (DIRECTORY_SEPARATOR === '\\') {
$this->markTestSkipped('Unix socket pair not supported on Windows');
}

$sockets = $this->factory->createPair(AF_UNIX, SOCK_STREAM, 0);

$this->assertCount(2, $sockets);
Expand All @@ -379,8 +389,7 @@ public function testCreatePairInvalid()
{
try {
$this->factory->createPair(0, 1, 2);
}
catch (Exception $e) {
} catch (Exception $e) {
return;
}
$this->fail();
Expand Down Expand Up @@ -473,8 +482,7 @@ public function testCreateFromStringInvalid()
$address = 'invalid://whatever';
try {
$this->factory->createFromString($address, $scheme);
}
catch (Exception $e) {
} catch (Exception $e) {
return;
}
$this->fail('Creating socket for invalid scheme should fail');
Expand Down
31 changes: 21 additions & 10 deletions tests/SocketTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ public function testConnectFailUnbound()
try {
$this->factory->createClient('localhost:2');
$this->fail('Expected connection to fail');
}
catch (Exception $e) {
} catch (Exception $e) {
$this->assertEquals(SOCKET_ECONNREFUSED, $e->getCode());
}
}
Expand All @@ -83,10 +82,12 @@ public function testConnectAsyncGoogle()
try {
$this->assertSame($socket, $socket->connect('www.google.com:80'));
$this->fail('Calling connect() succeeded immediately');
}
catch (Exception $e) {
// non-blocking connect() should be EINPROGRESS
$this->assertEquals(SOCKET_EINPROGRESS, $e->getCode());
} catch (Exception $e) {
// non-blocking connect() should be EINPROGRESS or EWOULDBLOCK on Windows
$this->assertEquals(
DIRECTORY_SEPARATOR !== '\\' ? SOCKET_EINPROGRESS : SOCKET_EWOULDBLOCK,
$e->getCode()
);

// connection should be established within 5.0 seconds
$this->assertTrue($socket->selectWrite(5.0));
Expand All @@ -100,6 +101,10 @@ public function testConnectAsyncGoogle()

public function testConnectAsyncFailUnbound()
{
if (PHP_OS !== 'Linux') {
$this->markTestSkipped('Only Linux is known to refuse connections to unbound addresses');
}

$socket = $this->factory->createTcp4();

$this->assertInstanceOf('Socket\Raw\Socket', $socket);
Expand All @@ -109,8 +114,7 @@ public function testConnectAsyncFailUnbound()
try {
$this->assertSame($socket, $socket->connect('localhost:2'));
$this->fail('Calling connect() succeeded immediately');
}
catch (Exception $e) {
} catch (Exception $e) {
// non-blocking connect() should be EINPROGRESS
$this->assertEquals(SOCKET_EINPROGRESS, $e->getCode());

Expand Down Expand Up @@ -139,7 +143,8 @@ public function testSelectFloat()
$this->assertFalse($socket->selectRead(0.5));
$time = microtime(true) - $time;

$this->assertGreaterThan(0.5, $time);
// check timer interval, but add some grace time to cope with inaccurate timers
$this->assertGreaterThan(0.48, $time);
$this->assertLessThan(1.0, $time);
}

Expand Down Expand Up @@ -176,6 +181,10 @@ public function testConnectTimeoutFailTimeout()

public function testConnectTimeoutFailUnbound()
{
if (PHP_OS !== 'Linux') {
$this->markTestSkipped('Only Linux is known to refuse connections to unbound addresses');
}

$socket = $this->factory->createTcp4();

$this->setExpectedException('Socket\Raw\Exception', null, SOCKET_ECONNREFUSED);
Expand Down Expand Up @@ -225,7 +234,9 @@ public function testServerNonBlockingAcceptClient(Socket $server)
// create local client connected to the given server
$client = $this->factory->createClient($server->getSockName());

// client connected, so we can not accept() this socket
// client connected, so we should be able to accept() this socket immediately
// on Windows, there appears to be a race condition, so first wait for server to be ready
$server->selectRead(0.1);
$peer = $server->accept();

// peer should be writable right away
Expand Down