diff --git a/README.md b/README.md index 53d8dfff..12ed7776 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ For the code of the current stable 0.4.x release, checkout the * [ServerInterface](#serverinterface) * [connection event](#connection-event) * [error event](#error-event) - * [listen()](#listen) * [getPort()](#getport) * [close()](#close) * [Server](#server) @@ -37,7 +36,7 @@ Here is a server that closes the connection if you send it anything: ```php $loop = React\EventLoop\Factory::create(); -$socket = new React\Socket\Server($loop); +$socket = new React\Socket\Server(8080, $loop); $socket->on('connection', function (ConnectionInterface $conn) { $conn->write("Hello " . $conn->getRemoteAddress() . "!\n"); $conn->write("Welcome to this amazing server!\n"); @@ -47,7 +46,6 @@ $socket->on('connection', function (ConnectionInterface $conn) { $conn->close(); }); }); -$socket->listen(1337); $loop->run(); ``` @@ -62,7 +60,7 @@ For anything more complex, consider using the ```php $loop = React\EventLoop\Factory::create(); -$client = stream_socket_client('tcp://127.0.0.1:1337'); +$client = stream_socket_client('tcp://127.0.0.1:8080'); $conn = new React\Stream\Stream($client, $loop); $conn->pipe(new React\Stream\Stream(STDOUT, $loop)); $conn->write("Hello World!\n"); @@ -116,32 +114,10 @@ $server->on('error', function (Exception $e) { Note that this is not a fatal error event, i.e. the server keeps listening for new connections even after this event. -#### listen() - -The `listen(int $port, string $host = '127.0.0.1'): void` method can be used to -start listening on the given address. - -This starts accepting new incoming connections on the given address. -See also the [connection event](#connection-event) for more details. - -```php -$server->listen(8080); -``` - -By default, the server will listen on the localhost address and will not be -reachable from the outside. -You can change the host the socket is listening on through a second parameter -provided to the listen method: - -```php -$socket->listen(1337, '192.168.0.1'); -``` - -This method MUST NOT be called more than once on the same instance. #### getPort() -The `getPort(): int` method can be used to +The `getPort(): ?int` method can be used to return the port this server is currently listening on. ```php @@ -149,8 +125,8 @@ $port = $server->getPort(); echo 'Server listening on port ' . $port . PHP_EOL; ``` -This method MUST NOT be called before calling [`listen()`](#listen). -This method MUST NOT be called after calling [`close()`](#close). +It will return the port number or `NULL` if it is unknown (not applicable to +this server socket or already closed). #### close() @@ -164,8 +140,7 @@ echo 'Shutting down server socket' . PHP_EOL; $server->close(); ``` -This method MUST NOT be called before calling [`listen()`](#listen). -This method MUST NOT be called after calling [`close()`](#close). +Calling this method more than once on the same instance is a NO-OP. ### Server @@ -173,22 +148,27 @@ The `Server` class implements the [`ServerInterface`](#serverinterface) and is responsible for accepting plaintext TCP/IP connections. ```php -$server = new Server($loop); +$server = new Server(8080, $loop); +``` + +By default, the server will listen on the localhost address and will not be +reachable from the outside. +You can change the host the socket is listening on through a first parameter +provided to the constructor: -$server->listen(8080); +```php +$server = new Server('192.168.0.1:8080', $loop); ``` Optionally, you can specify [socket context options](http://php.net/manual/en/context.socket.php) for the underlying stream socket resource like this: ```php -$server = new Server($loop, array( +$server = new Server('[::1]:8080', $loop, array( 'backlog' => 200, 'so_reuseport' => true, 'ipv6_v6only' => true )); - -$server->listen(8080, '::1'); ``` > Note that available [socket context options](http://php.net/manual/en/context.socket.php), @@ -226,13 +206,10 @@ which in its most basic form may look something like this if you're using a PEM encoded certificate file: ```php -$server = new Server($loop); - +$server = new Server(8000, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => 'server.pem' )); - -$server->listen(8000); ``` > Note that the certificate file will not be loaded on instantiation but when an @@ -244,6 +221,7 @@ If your private key is encrypted with a passphrase, you have to specify it like this: ```php +$server = new Server(8000, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => 'server.pem', 'passphrase' => 'secret' diff --git a/examples/01-echo.php b/examples/01-echo.php index a1955190..744aea2d 100644 --- a/examples/01-echo.php +++ b/examples/01-echo.php @@ -20,7 +20,7 @@ $loop = Factory::create(); -$server = new Server($loop); +$server = new Server(isset($argv[1]) ? $argv[1] : 0, $loop); // secure TLS mode if certificate is given as second parameter if (isset($argv[2])) { @@ -29,8 +29,6 @@ )); } -$server->listen(isset($argv[1]) ? $argv[1] : 0); - $server->on('connection', function (ConnectionInterface $conn) use ($loop) { echo '[connected]' . PHP_EOL; $conn->pipe($conn); diff --git a/examples/02-chat-server.php b/examples/02-chat-server.php index 52e0d0c2..beb159d9 100644 --- a/examples/02-chat-server.php +++ b/examples/02-chat-server.php @@ -20,7 +20,7 @@ $loop = Factory::create(); -$server = new Server($loop); +$server = new Server(isset($argv[1]) ? $argv[1] : 0, $loop); // secure TLS mode if certificate is given as second parameter if (isset($argv[2])) { @@ -29,8 +29,6 @@ )); } -$server->listen(isset($argv[1]) ? $argv[1] : 0, '0.0.0.0'); - $clients = array(); $server->on('connection', function (ConnectionInterface $client) use (&$clients) { diff --git a/examples/03-benchmark.php b/examples/03-benchmark.php index 6619944d..712c1e07 100644 --- a/examples/03-benchmark.php +++ b/examples/03-benchmark.php @@ -25,7 +25,7 @@ $loop = Factory::create(); -$server = new Server($loop); +$server = new Server(isset($argv[1]) ? $argv[1] : 0, $loop); // secure TLS mode if certificate is given as second parameter if (isset($argv[2])) { @@ -34,8 +34,6 @@ )); } -$server->listen(isset($argv[1]) ? $argv[1] : 0); - $server->on('connection', function (ConnectionInterface $conn) use ($loop) { echo '[connected]' . PHP_EOL; diff --git a/src/Connection.php b/src/Connection.php index 7b562237..a1215c32 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -16,12 +16,14 @@ class Connection extends Stream implements ConnectionInterface { public function handleClose() { - if (is_resource($this->stream)) { - // http://chat.stackoverflow.com/transcript/message/7727858#7727858 - stream_socket_shutdown($this->stream, STREAM_SHUT_RDWR); - stream_set_blocking($this->stream, false); - fclose($this->stream); + if (!is_resource($this->stream)) { + return; } + + // http://chat.stackoverflow.com/transcript/message/7727858#7727858 + stream_socket_shutdown($this->stream, STREAM_SHUT_RDWR); + stream_set_blocking($this->stream, false); + fclose($this->stream); } public function getRemoteAddress() diff --git a/src/SecureServer.php b/src/SecureServer.php index 17607d27..5fcc4e20 100644 --- a/src/SecureServer.php +++ b/src/SecureServer.php @@ -39,20 +39,24 @@ class SecureServer extends EventEmitter implements ServerInterface { private $tcp; - private $context; - private $loop; private $encryption; public function __construct(Server $tcp, LoopInterface $loop, array $context) { + if (!is_resource($tcp->master)) { + throw new ConnectionException('TCP server already shut down'); + } + // default to empty passphrase to surpress blocking passphrase prompt $context += array( 'passphrase' => '' ); + foreach ($context as $name => $value) { + stream_context_set_option($tcp->master, 'ssl', $name, $value); + } + $this->tcp = $tcp; - $this->context = $context; - $this->loop = $loop; $this->encryption = new StreamEncryption($loop); $that = $this; @@ -64,15 +68,6 @@ public function __construct(Server $tcp, LoopInterface $loop, array $context) }); } - public function listen($port, $host = '127.0.0.1') - { - $this->tcp->listen($port, $host); - - foreach ($this->context as $name => $value) { - stream_context_set_option($this->tcp->master, 'ssl', $name, $value); - } - } - public function getPort() { return $this->tcp->getPort(); diff --git a/src/Server.php b/src/Server.php index c4503043..fd7341a6 100644 --- a/src/Server.php +++ b/src/Server.php @@ -4,11 +4,16 @@ use Evenement\EventEmitter; use React\EventLoop\LoopInterface; +use InvalidArgumentException; /** * The `Server` class implements the `ServerInterface` and * is responsible for accepting plaintext TCP/IP connections. * + * ```php + * $server = new Server(8080, $loop); + * ``` + * * Whenever a client connects, it will emit a `connection` event with a connection * instance implementing `ConnectionInterface`: * @@ -33,28 +38,36 @@ class Server extends EventEmitter implements ServerInterface { public $master; private $loop; - private $context; /** - * Creates a plaintext TCP/IP server. + * Creates a plaintext TCP/IP socket server and starts listening on the given address + * + * This starts accepting new incoming connections on the given address. + * See also the `connection event` documented in the `ServerInterface` + * for more details. * * ```php - * $server = new Server($loop); + * $server = new Server(8080, $loop); + * ``` + * + * By default, the server will listen on the localhost address and will not be + * reachable from the outside. + * You can change the host the socket is listening on through the first parameter + * provided to the constructor. * - * $server->listen(8080); + * ```php + * $server = new Server('192.168.0.1:8080', $loop); * ``` * * Optionally, you can specify [socket context options](http://php.net/manual/en/context.socket.php) * for the underlying stream socket resource like this: * * ```php - * $server = new Server($loop, array( + * $server = new Server('[::1]:8080', $loop, array( * 'backlog' => 200, * 'so_reuseport' => true, * 'ipv6_v6only' => true * )); - * - * $server->listen(8080, '::1'); * ``` * * Note that available [socket context options](http://php.net/manual/en/context.socket.php), @@ -62,31 +75,50 @@ class Server extends EventEmitter implements ServerInterface * and/or PHP version. * Passing unknown context options has no effect. * + * @param string $uri * @param LoopInterface $loop - * @param array $context + * @param array $context + * @throws InvalidArgumentException if the listening address is invalid + * @throws ConnectionException if listening on this address fails (already in use etc.) */ - public function __construct(LoopInterface $loop, array $context = array()) + public function __construct($uri, LoopInterface $loop, array $context = array()) { $this->loop = $loop; - $this->context = $context; - } - public function listen($port, $host = '127.0.0.1') - { - if (strpos($host, ':') !== false) { - // enclose IPv6 addresses in square brackets before appending port - $host = '[' . $host . ']'; + // a single port has been given => assume localhost + if ((string)(int)$uri === (string)$uri) { + $uri = '127.0.0.1:' . $uri; + } + + // assume default scheme if none has been given + if (strpos($uri, '://') === false) { + $uri = 'tcp://' . $uri; + } + + // parse_url() does not accept null ports (random port assignment) => manually remove + if (substr($uri, -2) === ':0') { + $parts = parse_url(substr($uri, 0, -2)); + if ($parts) { + $parts['port'] = 0; + } + } else { + $parts = parse_url($uri); + } + + // ensure URI contains TCP scheme, host and port + if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') { + throw new InvalidArgumentException('Invalid URI "' . $uri . '" given'); } $this->master = @stream_socket_server( - "tcp://$host:$port", + $uri, $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, - stream_context_create(array('socket' => $this->context)) + stream_context_create(array('socket' => $context)) ); if (false === $this->master) { - $message = "Could not bind to tcp://$host:$port: $errstr"; + $message = "Could not bind to $uri: $errstr"; throw new ConnectionException($message, $errno); } stream_set_blocking($this->master, 0); @@ -115,6 +147,10 @@ public function handleConnection($socket) public function getPort() { + if (!is_resource($this->master)) { + return null; + } + $name = stream_socket_get_name($this->master, false); return (int) substr(strrchr($name, ':'), 1); @@ -122,6 +158,10 @@ public function getPort() public function close() { + if (!is_resource($this->master)) { + return; + } + $this->loop->removeStream($this->master); fclose($this->master); $this->removeAllListeners(); diff --git a/src/ServerInterface.php b/src/ServerInterface.php index 3fe135ee..c562f18b 100644 --- a/src/ServerInterface.php +++ b/src/ServerInterface.php @@ -48,33 +48,10 @@ */ interface ServerInterface extends EventEmitterInterface { - /** - * Starts listening on the given address - * - * This starts accepting new incoming connections on the given address. - * See also the `connection event` above for more details. - * - * By default, the server will listen on the localhost address and will not be - * reachable from the outside. - * You can change the host the socket is listening on through a second parameter - * provided to the listen method. - * - * This method MUST NOT be called more than once on the same instance. - * - * @param int $port port to listen on - * @param string $host optional host to listen on, defaults to localhost IP - * @return void - * @throws Exception if listening on this address fails (invalid or already in use etc.) - */ - public function listen($port, $host = '127.0.0.1'); - /** * Returns the port this server is currently listening on * - * This method MUST NOT be called before calling listen(). - * This method MUST NOT be called after calling close(). - * - * @return int the port number + * @return ?int the port number or NULL if it is unknown (not applicable to this server socket or already closed) */ public function getPort(); @@ -83,8 +60,7 @@ public function getPort(); * * This will stop listening for new incoming connections on this socket. * - * This method MUST NOT be called before calling listen(). - * This method MUST NOT be called after calling close(). + * Calling this method more than once on the same instance is a NO-OP. * * @return void */ diff --git a/tests/FunctionalSecureServerTest.php b/tests/FunctionalSecureServerTest.php index 72e19499..dff5723f 100644 --- a/tests/FunctionalSecureServerTest.php +++ b/tests/FunctionalSecureServerTest.php @@ -4,12 +4,12 @@ use React\EventLoop\Factory; use React\SocketClient\TcpConnector; -use React\Socket\Server; use Clue\React\Block; use React\Socket\SecureServer; use React\SocketClient\SecureConnector; use React\Stream\Stream; use React\Socket\ConnectionInterface; +use React\Socket\Server; class FunctionalSecureServerTest extends TestCase { @@ -26,12 +26,11 @@ public function testEmitsConnectionForNewConnection() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost.pem' )); $server->on('connection', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $connector = new SecureConnector(new TcpConnector($loop), $loop, array( @@ -46,12 +45,11 @@ public function testWritesDataToConnection() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost.pem' )); $server->on('connection', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $server->on('connection', function (ConnectionInterface $conn) { @@ -75,15 +73,13 @@ public function testWritesDataInMultipleChunksToConnection() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost.pem' )); $server->on('connection', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); - $server->on('connection', function (ConnectionInterface $conn) { $conn->write(str_repeat('*', 400000)); }); @@ -110,12 +106,11 @@ public function testEmitsDataFromConnection() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost.pem' )); $server->on('connection', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $once = $this->expectCallableOnceWith('foo'); @@ -140,12 +135,11 @@ public function testEmitsDataInMultipleChunksFromConnection() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost.pem' )); $server->on('connection', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $received = 0; @@ -174,12 +168,11 @@ public function testPipesDataBackInMultipleChunksFromConnection() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost.pem' )); $server->on('connection', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $server->on('connection', function (ConnectionInterface $conn) use (&$received) { @@ -210,13 +203,12 @@ public function testEmitsConnectionForNewConnectionWithEncryptedCertificate() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem', 'passphrase' => 'swordfish' )); $server->on('connection', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $connector = new SecureConnector(new TcpConnector($loop), $loop, array( @@ -231,13 +223,12 @@ public function testEmitsErrorForServerWithInvalidCertificate() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => 'invalid.pem' )); $server->on('connection', $this->expectCallableNever()); $server->on('error', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $connector = new SecureConnector(new TcpConnector($loop), $loop, array( @@ -253,13 +244,12 @@ public function testEmitsErrorForServerWithEncryptedCertificateMissingPassphrase { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem' )); $server->on('connection', $this->expectCallableNever()); $server->on('error', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $connector = new SecureConnector(new TcpConnector($loop), $loop, array( @@ -275,14 +265,13 @@ public function testEmitsErrorForServerWithEncryptedCertificateWithInvalidPassph { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost_swordfish.pem', 'passphrase' => 'nope' )); $server->on('connection', $this->expectCallableNever()); $server->on('error', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $connector = new SecureConnector(new TcpConnector($loop), $loop, array( @@ -298,13 +287,12 @@ public function testEmitsErrorForConnectionWithPeerVerification() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost.pem' )); $server->on('connection', $this->expectCallableNever()); $server->on('error', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $connector = new SecureConnector(new TcpConnector($loop), $loop, array( @@ -320,13 +308,12 @@ public function testEmitsErrorIfConnectionIsCancelled() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost.pem' )); $server->on('connection', $this->expectCallableNever()); $server->on('error', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $connector = new SecureConnector(new TcpConnector($loop), $loop, array( @@ -343,13 +330,12 @@ public function testEmitsNothingIfConnectionIsIdle() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost.pem' )); $server->on('connection', $this->expectCallableNever()); $server->on('error', $this->expectCallableNever()); - $server->listen(0); $port = $server->getPort(); $connector = new TcpConnector($loop); @@ -363,13 +349,12 @@ public function testEmitsErrorIfConnectionIsNotSecureHandshake() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server = new SecureServer($server, $loop, array( 'local_cert' => __DIR__ . '/../examples/localhost.pem' )); $server->on('connection', $this->expectCallableNever()); $server->on('error', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $connector = new TcpConnector($loop); @@ -381,4 +366,17 @@ public function testEmitsErrorIfConnectionIsNotSecureHandshake() Block\sleep(self::TIMEOUT, $loop); } + + /** + * @expectedException React\Socket\ConnectionException + */ + public function testFailsToCreateSecureServerOnClosedSocket() + { + $loop = Factory::create(); + + $server = new Server(0, $loop); + $server->close(); + + new SecureServer($server, $loop, array()); + } } diff --git a/tests/FunctionalServerTest.php b/tests/FunctionalServerTest.php index 51eac721..96ac11bf 100644 --- a/tests/FunctionalServerTest.php +++ b/tests/FunctionalServerTest.php @@ -15,9 +15,8 @@ public function testEmitsConnectionForNewConnection() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server->on('connection', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $connector = new TcpConnector($loop); @@ -32,12 +31,11 @@ public function testEmitsConnectionWithRemoteIp() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $peer = null; $server->on('connection', function (ConnectionInterface $conn) use (&$peer) { $peer = $conn->getRemoteAddress(); }); - $server->listen(0); $port = $server->getPort(); $connector = new TcpConnector($loop); @@ -54,14 +52,13 @@ public function testEmitsConnectionWithRemoteIpAfterConnectionIsClosedByPeer() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $peer = null; $server->on('connection', function (ConnectionInterface $conn) use (&$peer) { $conn->on('close', function () use ($conn, &$peer) { $peer = $conn->getRemoteAddress(); }); }); - $server->listen(0); $port = $server->getPort(); $connector = new TcpConnector($loop); @@ -79,13 +76,12 @@ public function testEmitsConnectionWithRemoteNullAddressAfterConnectionIsClosedL { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $peer = null; $server->on('connection', function (ConnectionInterface $conn) use (&$peer) { $conn->close(); $peer = $conn->getRemoteAddress(); }); - $server->listen(0); $port = $server->getPort(); $connector = new TcpConnector($loop); @@ -102,9 +98,8 @@ public function testEmitsConnectionEvenIfConnectionIsCancelled() { $loop = Factory::create(); - $server = new Server($loop); + $server = new Server(0, $loop); $server->on('connection', $this->expectCallableOnce()); - $server->listen(0); $port = $server->getPort(); $connector = new TcpConnector($loop); @@ -120,13 +115,13 @@ public function testEmitsConnectionForNewIpv6Connection() { $loop = Factory::create(); - $server = new Server($loop); - $server->on('connection', $this->expectCallableOnce()); try { - $server->listen(0, '::1'); + $server = new Server('[::1]:0', $loop); } catch (ConnectionException $e) { $this->markTestSkipped('Unable to start IPv6 server socket (not available on your platform?)'); } + + $server->on('connection', $this->expectCallableOnce()); $port = $server->getPort(); $connector = new TcpConnector($loop); @@ -141,16 +136,16 @@ public function testEmitsConnectionWithRemoteIpv6() { $loop = Factory::create(); - $server = new Server($loop); - $peer = null; - $server->on('connection', function (ConnectionInterface $conn) use (&$peer) { - $peer = $conn->getRemoteAddress(); - }); try { - $server->listen(0, '::1'); + $server = new Server('[::1]:0', $loop); } catch (ConnectionException $e) { $this->markTestSkipped('Unable to start IPv6 server socket (not available on your platform?)'); } + + $peer = null; + $server->on('connection', function (ConnectionInterface $conn) use (&$peer) { + $peer = $conn->getRemoteAddress(); + }); $port = $server->getPort(); $connector = new TcpConnector($loop); @@ -172,12 +167,10 @@ public function testAppliesContextOptionsToSocketStreamResource() $loop = Factory::create(); - $server = new Server($loop, array( + $server = new Server(0, $loop, array( 'backlog' => 4 )); - $server->listen(0); - $all = stream_context_get_options($server->master); $this->assertEquals(array('socket' => array('backlog' => 4)), $all); diff --git a/tests/SecureServerTest.php b/tests/SecureServerTest.php index 2b8f2395..df1843e3 100644 --- a/tests/SecureServerTest.php +++ b/tests/SecureServerTest.php @@ -17,6 +17,7 @@ public function testGetPortWillBePassedThroughToTcpServer() { $tcp = $this->getMockBuilder('React\Socket\Server')->disableOriginalConstructor()->getMock(); $tcp->expects($this->once())->method('getPort')->willReturn(1234); + $tcp->master = stream_socket_server('tcp://localhost:0'); $loop = $this->getMock('React\EventLoop\LoopInterface'); @@ -29,6 +30,7 @@ public function testCloseWillBePassedThroughToTcpServer() { $tcp = $this->getMockBuilder('React\Socket\Server')->disableOriginalConstructor()->getMock(); $tcp->expects($this->once())->method('close'); + $tcp->master = stream_socket_server('tcp://localhost:0'); $loop = $this->getMock('React\EventLoop\LoopInterface'); diff --git a/tests/ServerTest.php b/tests/ServerTest.php index aefe3a9f..167b2622 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -19,14 +19,12 @@ private function createLoop() /** * @covers React\Socket\Server::__construct - * @covers React\Socket\Server::listen * @covers React\Socket\Server::getPort */ public function setUp() { $this->loop = $this->createLoop(); - $this->server = new Server($this->loop); - $this->server->listen(0); + $this->server = new Server(0, $this->loop); $this->port = $this->server->getPort(); } @@ -141,6 +139,18 @@ public function testLoopWillEndWhenServerIsClosed() $this->loop->run(); } + public function testCloseTwiceIsNoOp() + { + $this->server->close(); + $this->server->close(); + } + + public function testGetPortAfterCloseReturnsNull() + { + $this->server->close(); + $this->assertNull($this->server->getPort()); + } + public function testLoopWillEndWhenServerIsClosedAfterSingleConnection() { $client = stream_socket_client('tcp://localhost:' . $this->port); @@ -231,8 +241,7 @@ public function testConnectionDoesEndWhenClientCloses() */ public function testListenOnBusyPortThrows() { - $another = new Server($this->loop); - $another->listen($this->port); + $another = new Server($this->port, $this->loop); } /** diff --git a/tests/Stub/ServerStub.php b/tests/Stub/ServerStub.php index 2bc2f5aa..d385db00 100644 --- a/tests/Stub/ServerStub.php +++ b/tests/Stub/ServerStub.php @@ -7,10 +7,6 @@ class ServerStub extends EventEmitter implements ServerInterface { - public function listen($port, $host = '127.0.0.1') - { - } - public function getPort() { return 80;