Skip to content

Commit 61d9d2c

Browse files
committed
chore: add httpserver
1 parent cc7f5d8 commit 61d9d2c

12 files changed

Lines changed: 488 additions & 442 deletions

File tree

composer.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@
3030
},
3131
"require": {
3232
"php": ">=8.2",
33+
"ext-swoole": "*",
3334
"utopia-php/di": "0.3.*",
34-
"utopia-php/validators": "0.2.*",
35-
"ext-swoole": "*"
35+
"utopia-php/servers": "0.3.*",
36+
"utopia-php/compression": "0.1.*",
37+
"utopia-php/validators": "0.2.*"
3638
},
3739
"config": {
3840
"allow-plugins": {

composer.lock

Lines changed: 102 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Http/Adapter/FPM/Request.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,26 @@ public function setServer(string $key, string $value): static
6868
*/
6969
public function getIP(): string
7070
{
71-
$ips = explode(',', $this->getHeader('HTTP_X_FORWARDED_FOR', $this->getServer('REMOTE_ADDR') ?? '0.0.0.0'));
71+
$remoteAddr = $this->getServer('REMOTE_ADDR') ?? '0.0.0.0';
7272

73-
return trim($ips[0] ?? '');
73+
foreach ($this->trustedIpHeaders as $header) {
74+
$headerValue = $this->getHeader($header);
75+
76+
if (empty($headerValue)) {
77+
continue;
78+
}
79+
80+
// Leftmost IP address is the address of the originating client
81+
$ips = \explode(',', $headerValue);
82+
$ip = \trim($ips[0]);
83+
84+
// Validate IP format (supports both IPv4 and IPv6)
85+
if (\filter_var($ip, FILTER_VALIDATE_IP)) {
86+
return $ip;
87+
}
88+
}
89+
90+
return $remoteAddr;
7491
}
7592

7693
/**
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
namespace Utopia\Http\Adapter\Swoole;
4+
5+
use Swoole\Coroutine;
6+
use Utopia\Http\Adapter;
7+
use Utopia\DI\Container;
8+
use Swoole\Http\Server as SwooleServer;
9+
use Swoole\Http\Request as SwooleRequest;
10+
use Swoole\Http\Response as SwooleResponse;
11+
12+
class HttpServer extends Adapter
13+
{
14+
protected SwooleServer $server;
15+
protected const REQUEST_CONTAINER_CONTEXT_KEY = '__utopia_http_request_container';
16+
protected Container $container;
17+
protected ?Container $requestContainer = null;
18+
protected bool $coroutines;
19+
20+
public function __construct(string $host, ?string $port = null, array $settings = [], ?Container $container = null, bool $coroutines = false)
21+
{
22+
$this->coroutines = $coroutines;
23+
$this->server = new SwooleServer($host, (int) $port);
24+
$this->server->set(\array_merge([
25+
'enable_coroutine' => $coroutines,
26+
], $settings));
27+
$this->container = $container ?? new Container();
28+
}
29+
30+
public function onRequest(callable $callback)
31+
{
32+
$this->server->on('request', function (SwooleRequest $request, SwooleResponse $response) use ($callback) {
33+
$handler = function () use ($request, $response, $callback) {
34+
$requestContainer = new Container($this->container);
35+
$requestContainer->set('swooleRequest', fn () => $request);
36+
$requestContainer->set('swooleResponse', fn () => $response);
37+
38+
if ($this->coroutines && Coroutine::getCid() !== -1) {
39+
Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] = $requestContainer;
40+
} else {
41+
$this->requestContainer = $requestContainer;
42+
}
43+
44+
\call_user_func($callback, new Request($request), new Response($response));
45+
};
46+
47+
if ($this->coroutines) {
48+
go($handler);
49+
} else {
50+
$handler();
51+
}
52+
});
53+
}
54+
55+
public function getContainer(): Container
56+
{
57+
if ($this->coroutines && Coroutine::getCid() !== -1) {
58+
return Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] ?? $this->container;
59+
}
60+
61+
return $this->requestContainer ?? $this->container;
62+
}
63+
64+
public function getServer(): SwooleServer
65+
{
66+
return $this->server;
67+
}
68+
69+
public function onStart(callable $callback)
70+
{
71+
$this->server->on('start', function () use ($callback) {
72+
if ($this->coroutines) {
73+
go(function () use ($callback) {
74+
\call_user_func($callback, $this);
75+
});
76+
} else {
77+
\call_user_func($callback, $this);
78+
}
79+
});
80+
}
81+
82+
public function start()
83+
{
84+
return $this->server->start();
85+
}
86+
}

src/Http/Adapter/Swoole/Request.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,26 @@ public function setServer(string $key, string $value): static
7373
*/
7474
public function getIP(): string
7575
{
76-
$ips = explode(',', $this->getHeader('x-forwarded-for', $this->getServer('remote_addr') ?? '0.0.0.0'));
76+
$remoteAddr = $this->getServer('remote_addr') ?? '0.0.0.0';
7777

78-
return trim($ips[0] ?? '');
78+
foreach ($this->trustedIpHeaders as $header) {
79+
$headerValue = $this->getHeader($header);
80+
81+
if (empty($headerValue)) {
82+
continue;
83+
}
84+
85+
// Leftmost IP address is the address of the originating client
86+
$ips = explode(',', $headerValue);
87+
$ip = trim($ips[0]);
88+
89+
// Validate IP format (supports both IPv4 and IPv6)
90+
if (filter_var($ip, FILTER_VALIDATE_IP)) {
91+
return $ip;
92+
}
93+
}
94+
95+
return $remoteAddr;
7996
}
8097

8198
/**

src/Http/Adapter/Swoole/Server.php

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,14 @@ class Server extends Adapter
1616
protected SwooleServer $server;
1717
protected const REQUEST_CONTAINER_CONTEXT_KEY = '__utopia_http_request_container';
1818
protected Container $container;
19-
protected ?Container $requestContainer = null;
2019

2120
public function __construct(string $host, ?string $port = null, array $settings = [], ?Container $container = null)
2221
{
2322
$this->server = new SwooleServer($host, $port);
24-
$this->server->set(\array_merge([
23+
$this->server->set(\array_merge($settings, [
2524
'enable_coroutine' => true,
2625
'http_parse_cookie' => false,
27-
], $settings));
26+
]));
2827
$this->container = $container ?? new Container();
2928
}
3029

@@ -35,11 +34,7 @@ public function onRequest(callable $callback)
3534
$requestContainer->set('swooleRequest', fn () => $request);
3635
$requestContainer->set('swooleResponse', fn () => $response);
3736

38-
if (Coroutine::getCid() !== -1) {
39-
Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] = $requestContainer;
40-
} else {
41-
$this->requestContainer = $requestContainer;
42-
}
37+
Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] = $requestContainer;
4338

4439
$utopiaRequest = new Request($request);
4540
$utopiaResponse = new Response($response);
@@ -50,11 +45,12 @@ public function onRequest(callable $callback)
5045

5146
public function getContainer(): Container
5247
{
53-
if (Coroutine::getCid() !== -1) {
54-
return Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] ?? $this->container;
55-
}
48+
return Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] ?? $this->container;
49+
}
5650

57-
return $this->requestContainer ?? $this->container;
51+
public function getServer(): SwooleServer
52+
{
53+
return $this->server;
5854
}
5955

6056
public function onStart(callable $callback)

0 commit comments

Comments
 (0)