Skip to content
This repository was archived by the owner on Feb 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"require": {
"php": ">=8.0",
"ext-swoole": "*",
"utopia-php/framework": "0.33.*"
Comment thread
levivannoort marked this conversation as resolved.
"utopia-php/framework": "0.33.x"
},
"require-dev": {
"phpunit/phpunit": "^9.3",
Expand Down
76 changes: 61 additions & 15 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3'

services:
php:
image: swoole-dev
Expand Down
54 changes: 49 additions & 5 deletions src/Swoole/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ class Request extends UtopiaRequest
*/
protected SwooleRequest $swoole;

/**
* List of trusted proxy header names to check for client IP address
*
* @var array<string>
*/
protected array $trustedIpHeaders = [];
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Request constructor.
*/
Expand Down Expand Up @@ -64,18 +71,55 @@ public function setServer(string $key, string $value): static
return $this;
}

/**
* Set trusted ip headers
*
* WARNING: Only set these headers if your application is behind a trusted proxy.
* Trusting these headers when accepting direct client connections is a security risk.
*
* @param array<string> $headers List of header names to trust (e.g., ['x-forwarded-for', 'x-real-ip'])
* @return static
*/
public function setTrustedIpHeaders(array $headers): static
{
$normalized = array_map('strtolower', $headers);
$trimmed = array_map('trim', $normalized);
$this->trustedIpHeaders = array_filter($trimmed);

return $this;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Get IP
*
* Returns users IP address.
* Support HTTP_X_FORWARDED_FOR header usually return
* from different proxy servers or PHP default REMOTE_ADDR
* Extracts the client's IP address from trusted headers or falls back to the remote address.
* Prioritizes headers like X-Forwarded-For when behind proxies or load balancers,
* defaulting to REMOTE_ADDR when trusted headers are unavailable.
*
* @return string The validated client IP address or '0.0.0.0' if unavailable
*/
public function getIP(): string
{
$ips = explode(',', $this->getHeader('x-forwarded-for', $this->getServer('remote_addr') ?? '0.0.0.0'));
$remoteAddr = $this->getServer('REMOTE_ADDR') ?? '0.0.0.0';

foreach ($this->trustedIpHeaders as $header) {
$headerValue = $this->getHeader($header);

if (empty($headerValue)) {
continue;
}

// Leftmost IP address is the address of the originating client
$ips = explode(',', $headerValue);
$ip = trim($ips[0]);

// Validate IP format (supports both IPv4 and IPv6)
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
}

return trim($ips[0] ?? '');
return $remoteAddr;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@
->inject('request')
->inject('response')
->action(function (Request $request, Response $response) {
$response->addHeader('Set-Cookie', 'key1=value1');
$response->addHeader('Set-Cookie', 'key2=value2');
$response->addHeader('Set-Cookie', 'key1=value1', override: false);
$response->addHeader('Set-Cookie', 'key2=value2', override: false);
$response->send('OK');
});

Expand Down