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
73 changes: 73 additions & 0 deletions docs/api/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,76 @@ $app = new FrameworkX\App($container);

$app->run();
```

X supports running behind reverse proxies just fine. However, by default it will
see the IP address of the last proxy server as the client IP address (this will
often be `127.0.0.1`). You can get the original client IP address if you configure
your proxy server to forward the original client IP address in the `X-Forwarded-For`
(XFF) or `Forwarded` HTTP request header. If you want to use these trusted headers,
you may use a custom middleware to read the IP from this header before passing
it to the [`AccessLogHandler`](middleware.md#accessloghandler) like this:

=== "Using middleware instances"

```php title="public/index.php"
<?php

use Acme\Todo\TrustedProxyMiddleware;

require __DIR__ . '/../vendor/autoload.php';

$app = new FrameworkX\App(
new TrustedProxyMiddleware(),
new FrameworkX\AccessLogHandler(),
new FrameworkX\ErrorHandler()
);

// Register routes here, see routing…

$app->run();
```

=== "Using middleware names"

```php title="public/index.php"
<?php

use Acme\Todo\TrustedProxyMiddleware;

require __DIR__ . '/../vendor/autoload.php';

$app = new FrameworkX\App(
TrustedProxyMiddleware::class,
FrameworkX\AccessLogHandler::class,
FrameworkX\ErrorHandler::class
);

// Register routes here, see routing…

$app->run();
```

```php title="src/TrustedProxyMiddleware.php"
<?php

namespace Acme\Todo;

use Psr\Http\Message\ServerRequestInterface;

class TrustedProxyMiddleware
{
public function __invoke(ServerRequestInterface $request, callable $next)
{
// use 127.0.0.1 as trusted proxy to read from X-Forwarded-For (XFF)
$remote_addr = $request->getAttribute('remote_addr') ?? $request->getServerParams()['REMOTE_ADDR'] ?? null;
if ($remote_addr === '127.0.0.1' && $request->hasHeader('X-Forwarded-For')) {
$remote_addr = preg_replace('/,.*/', '', $request->getHeaderLine('X-Forwarded-For'));
$request = $request->withAttribute('remote_addr', $remote_addr);
}

return $next($request);
}
}
```

See also [middleware handling](middleware.md) for more details.
2 changes: 1 addition & 1 deletion src/AccessLogHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private function log(ServerRequestInterface $request, ResponseInterface $respons
}

$this->sapi->log(
($request->getServerParams()['REMOTE_ADDR'] ?? '-') . ' ' .
($request->getAttribute('remote_addr') ?? $request->getServerParams()['REMOTE_ADDR'] ?? '-') . ' ' .
'"' . $this->escape($method) . ' ' . $this->escape($request->getRequestTarget()) . ' HTTP/' . $request->getProtocolVersion() . '" ' .
$status . ' ' . $responseSize . ' ' . sprintf('%.3F', $time < 0 ? 0 : $time)
);
Expand Down
13 changes: 13 additions & 0 deletions tests/AccessLogHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,19 @@ public function testInvokeWithStreamingResponsePrintsNothingIfStreamIsPending()
$stream->write('hello');
}

public function testInvokeWithRemoteAddrAttributePrintsRequestLogWithIpFromAttribute()
{
$handler = new AccessLogHandler();

$request = new ServerRequest('GET', 'http://localhost:8080/users', [], '', '1.1', ['REMOTE_ADDR' => '127.0.0.1']);
$request = $request->withAttribute('remote_addr', '10.0.0.1');
$response = new Response(200, [], "Hello\n");

// 2021-01-29 12:22:01.717 10.0.0.1 "GET /users HTTP/1.1" 200 6 0.000\n
$this->expectOutputRegex("/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} 10\.0\.0\.1 \"GET \/users HTTP\/1\.1\" 200 6 0\.0\d\d" . PHP_EOL . "$/");
$handler($request, function () use ($response) { return $response; });
}

public function testInvokeWithoutRemoteAddressPrintsRequestLogWithDashAsPlaceholder()
{
$handler = new AccessLogHandler();
Expand Down