You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
High-performance, protocol-agnostic proxy built on Swoole for blazing fast connection management across HTTP, TCP, and SMTP protocols.
4
4
5
-
## 🚀 Performance First
5
+
## Performance First
6
6
7
7
-**Swoole coroutines**: Handle 100,000+ concurrent connections per server
8
8
-**Connection pooling**: Reuse connections to backend services
@@ -11,16 +11,17 @@ High-performance, protocol-agnostic proxy built on Swoole for blazing fast conne
11
11
-**Async I/O**: Non-blocking operations throughout
12
12
-**Memory efficient**: Shared memory tables for state management
13
13
14
-
## 🎯 Features
14
+
## Features
15
15
16
16
- Protocol-agnostic connection management
17
17
- Cold-start detection and triggering
18
18
- Automatic connection queueing during cold-starts
19
19
- Health checking and circuit breakers
20
20
- Built-in telemetry and metrics
21
+
- SSRF validation for security
21
22
- Support for HTTP, TCP (PostgreSQL/MySQL), and SMTP
22
23
23
-
## 📦 Installation
24
+
## Installation
24
25
25
26
### Using Composer
26
27
@@ -38,37 +39,95 @@ docker-compose up -d
38
39
39
40
See [DOCKER.md](DOCKER.md) for detailed Docker setup and configuration.
40
41
41
-
## 🏃 Quick Start
42
+
## Quick Start
42
43
43
-
The protocol-proxy uses the **Adapter Pattern** - similar to [utopia-php/database](https://github.com/utopia-php/database), [utopia-php/messaging](https://github.com/utopia-php/messaging), and [utopia-php/storage](https://github.com/utopia-php/storage).
44
+
The protocol-proxy uses the **Resolver Pattern** - a platform-agnostic interface for resolving resource identifiers to backend endpoints.
44
45
45
-
### HTTP Proxy (Basic)
46
+
### Implementing a Resolver
47
+
48
+
All servers require a `Resolver` implementation that maps resource IDs (hostnames, database IDs, domains) to backend endpoints:
46
49
47
50
```php
48
51
<?php
49
-
require 'vendor/autoload.php';
52
+
use Utopia\Proxy\Resolver;
53
+
use Utopia\Proxy\Resolver\Result;
54
+
use Utopia\Proxy\Resolver\Exception;
55
+
56
+
class MyResolver implements Resolver
57
+
{
58
+
public function resolve(string $resourceId): Result
59
+
{
60
+
// Map resource ID to backend endpoint
61
+
$backends = [
62
+
'api.example.com' => 'localhost:3000',
63
+
'app.example.com' => 'localhost:3001',
64
+
];
65
+
66
+
if (!isset($backends[$resourceId])) {
67
+
throw new Exception(
68
+
"No backend for: {$resourceId}",
69
+
Exception::NOT_FOUND
70
+
);
71
+
}
72
+
73
+
return new Result(endpoint: $backends[$resourceId]);
74
+
}
75
+
76
+
public function onConnect(string $resourceId, array $metadata = []): void
77
+
{
78
+
// Called when a connection is established
79
+
}
80
+
81
+
public function onDisconnect(string $resourceId, array $metadata = []): void
82
+
{
83
+
// Called when a connection is closed
84
+
}
85
+
86
+
public function trackActivity(string $resourceId, array $metadata = []): void
87
+
{
88
+
// Track activity for cold-start detection
89
+
}
90
+
91
+
public function invalidateCache(string $resourceId): void
92
+
{
93
+
// Invalidate cached resolution data
94
+
}
95
+
96
+
public function getStats(): array
97
+
{
98
+
return ['resolver' => 'custom'];
99
+
}
100
+
}
101
+
```
50
102
51
-
use Utopia\Platform\Action;
52
-
use Utopia\Proxy\Adapter\HTTP\Swoole as HTTPAdapter;
53
-
use Utopia\Proxy\Server\HTTP\Swoole as HTTPServer;
54
-
use Utopia\Proxy\Service\HTTP as HTTPService;
103
+
### HTTP Proxy
55
104
56
-
$adapter = new HTTPAdapter();
57
-
$service = $adapter->getService() ?? new HTTPService();
105
+
```php
106
+
<?php
107
+
require 'vendor/autoload.php';
58
108
59
-
// Required: Provide backend resolution logic
60
-
$service->addAction('resolve', (new class extends Action {})
61
-
->callback(function (string $hostname) use ($backend): string {
62
-
return $backend->getEndpoint($hostname);
63
-
}));
109
+
use Utopia\Proxy\Resolver;
110
+
use Utopia\Proxy\Resolver\Result;
111
+
use Utopia\Proxy\Server\HTTP\Swoole as HTTPServer;
64
112
65
-
$adapter->setService($service);
113
+
// Create resolver (inline example)
114
+
$resolver = new class implements Resolver {
115
+
public function resolve(string $resourceId): Result
116
+
{
117
+
return new Result(endpoint: 'backend:8080');
118
+
}
119
+
public function onConnect(string $resourceId, array $metadata = []): void {}
120
+
public function onDisconnect(string $resourceId, array $metadata = []): void {}
121
+
public function trackActivity(string $resourceId, array $metadata = []): void {}
122
+
public function invalidateCache(string $resourceId): void {}
123
+
public function getStats(): array { return []; }
124
+
};
66
125
67
126
$server = new HTTPServer(
127
+
$resolver,
68
128
host: '0.0.0.0',
69
129
port: 80,
70
-
workers: swoole_cpu_num() * 2,
71
-
config: ['adapter' => $adapter]
130
+
workers: swoole_cpu_num() * 2
72
131
);
73
132
74
133
$server->start();
@@ -80,9 +139,25 @@ $server->start();
80
139
<?php
81
140
require 'vendor/autoload.php';
82
141
142
+
use Utopia\Proxy\Resolver;
143
+
use Utopia\Proxy\Resolver\Result;
83
144
use Utopia\Proxy\Server\TCP\Swoole as TCPServer;
84
145
146
+
$resolver = new class implements Resolver {
147
+
public function resolve(string $resourceId): Result
148
+
{
149
+
// resourceId is the database name from connection
150
+
return new Result(endpoint: 'postgres:5432');
151
+
}
152
+
public function onConnect(string $resourceId, array $metadata = []): void {}
153
+
public function onDisconnect(string $resourceId, array $metadata = []): void {}
154
+
public function trackActivity(string $resourceId, array $metadata = []): void {}
155
+
public function invalidateCache(string $resourceId): void {}
156
+
public function getStats(): array { return []; }
157
+
};
158
+
85
159
$server = new TCPServer(
160
+
$resolver,
86
161
host: '0.0.0.0',
87
162
ports: [5432, 3306], // PostgreSQL, MySQL
88
163
workers: swoole_cpu_num() * 2
@@ -97,9 +172,25 @@ $server->start();
97
172
<?php
98
173
require 'vendor/autoload.php';
99
174
175
+
use Utopia\Proxy\Resolver;
176
+
use Utopia\Proxy\Resolver\Result;
100
177
use Utopia\Proxy\Server\SMTP\Swoole as SMTPServer;
101
178
179
+
$resolver = new class implements Resolver {
180
+
public function resolve(string $resourceId): Result
181
+
{
182
+
// resourceId is the domain from EHLO/HELO
183
+
return new Result(endpoint: 'mailserver:25');
184
+
}
185
+
public function onConnect(string $resourceId, array $metadata = []): void {}
186
+
public function onDisconnect(string $resourceId, array $metadata = []): void {}
187
+
public function trackActivity(string $resourceId, array $metadata = []): void {}
188
+
public function invalidateCache(string $resourceId): void {}
189
+
public function getStats(): array { return []; }
190
+
};
191
+
102
192
$server = new SMTPServer(
193
+
$resolver,
103
194
host: '0.0.0.0',
104
195
port: 25,
105
196
workers: swoole_cpu_num() * 2
@@ -108,38 +199,36 @@ $server = new SMTPServer(
108
199
$server->start();
109
200
```
110
201
111
-
## 🔧 Configuration
202
+
## Configuration
112
203
113
204
```php
114
205
<?php
115
206
$config = [
116
-
'host' => '0.0.0.0',
117
-
'port' => 80,
118
-
'workers' => 16,
119
-
120
207
// Performance tuning
121
-
'max_connections' => 100000,
122
-
'max_coroutine' => 100000,
123
-
'socket_buffer_size' => 2 * 1024 * 1024, // 2MB
124
-
'buffer_output_size' => 2 * 1024 * 1024, // 2MB
125
-
126
-
// Routing cache
127
-
'cache_ttl' => 1, // 1 second
128
-
129
-
// Database connection (for cache and resolution actions)
$server = new HTTPServer($resolver, '0.0.0.0', 80, 16, $config);
140
229
```
141
230
142
-
## ✅ Testing
231
+
## Testing
143
232
144
233
```bash
145
234
composer test
@@ -157,9 +246,9 @@ Coverage (requires Xdebug or PCOV):
157
246
vendor/bin/phpunit --coverage-text
158
247
```
159
248
160
-
## 🎨 Architecture
249
+
## Architecture
161
250
162
-
The protocol-proxy follows the **Adapter Pattern**used throughout utopia-php libraries (Database, Messaging, Storage), providing a clean and extensible architecture for protocol-specific implementations.
251
+
The protocol-proxy uses the **Resolver Pattern**for platform-agnostic backend resolution, combined with protocol-specific adapters for optimized handling.
0 commit comments