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
23 changes: 23 additions & 0 deletions docs/best-practices/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,29 @@ You may be wondering how fast a pure PHP web server implementation could possibl
In fact, in benchmarks this setup outperforms any traditional PHP stack by orders of magnitude.
The answer: [Lightning fast!](https://framework-x.clue.engineering/#lightning-fast)

### Listen address

By default, X will listen on `http://127.0.0.1:8080`, i.e. you can connect to it on
the local port `8080`, but you can not connect to it from outside the system it's
running on. This is a common approach when running this behind a reverse proxy
such as nginx, HAproxy, etc. for TLS termination as discussed in the next chapter.

If you want to change the listen address, you can pass an IP and port
combination through the `X_LISTEN` environment variable like this:

```bash
$ X_LISTEN=127.0.0.1:8081 php app.php
```

While not usually recommended, you can also expose this to the public by using
the special `0.0.0.0` IPv4 address or `[::]` IPv6 address like this:

```bash
$ X_LISTEN=0.0.0.0:8080 php app.php
```

### More

If you're going to use this in production, we still recommend running this
behind a reverse proxy such as nginx, HAproxy, etc. for TLS termination
(HTTPS support).
Expand Down
7 changes: 6 additions & 1 deletion src/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,12 @@ private function runLoop()
return $response;
});

$socket = new SocketServer('127.0.0.1:8080', [], $this->loop);
$listen = \getenv('X_LISTEN');
if ($listen === false) {
$listen = '127.0.0.1:8080';
}

$socket = new SocketServer($listen, [], $this->loop);
$http->listen($socket);

$this->log('Listening on ' . \str_replace('tcp:', 'http:', $socket->getAddress()));
Expand Down
56 changes: 56 additions & 0 deletions tests/AppTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,62 @@ public function testRunWillRunGivenLoopInstanceAndReportListeningAddress()
$app->run();
}

public function testRunWillRunGivenLoopInstanceAndReportListeningAddressFromEnvironment()
{
$socket = @stream_socket_server('127.0.0.1:0');
$addr = stream_socket_get_name($socket, false);
fclose($socket);

putenv('X_LISTEN=' . $addr);
$loop = $this->createMock(LoopInterface::class);
$loop->expects($this->once())->method('run');
$app = new App($loop);

$this->expectOutputRegex('/' . preg_quote('Listening on http://' . $addr . PHP_EOL, '/') . '$/');
$app->run();
}

public function testRunWillRunGivenLoopInstanceAndReportListeningAddressFromEnvironmentWithRandomPort()
{
putenv('X_LISTEN=127.0.0.1:0');
$loop = $this->createMock(LoopInterface::class);
$loop->expects($this->once())->method('run');
$app = new App($loop);

$this->expectOutputRegex('/' . preg_quote('Listening on http://127.0.0.1:', '/') . '\d+' . PHP_EOL . '$/');
$app->run();
}

public function testRunAppWithEmptyAddressThrowsWithoutRunningLoop()
{
putenv('X_LISTEN=');
$loop = $this->createMock(LoopInterface::class);
$loop->expects($this->never())->method('run');
$app = new App($loop);

$this->expectException(\InvalidArgumentException::class);
$app->run();
}

public function testRunAppWithBusyPortThrowsWithoutRunningLoop()
{
$socket = @stream_socket_server('127.0.0.1:0');
$addr = stream_socket_get_name($socket, false);

if (@stream_socket_server($addr) !== false) {
$this->markTestSkipped('System does not prevent listening on same address twice');
}

putenv('X_LISTEN=' . $addr);
$loop = $this->createMock(LoopInterface::class);
$loop->expects($this->never())->method('run');
$app = new App($loop);

$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Failed to listen on');
$app->run();
}

public function testGetMethodAddsGetRouteOnRouter()
{
$app = new App();
Expand Down