Skip to content

Commit 2856ece

Browse files
committed
Update docs
1 parent b52f618 commit 2856ece

3 files changed

Lines changed: 371 additions & 206 deletions

File tree

README.md

Lines changed: 184 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
High-performance, protocol-agnostic proxy built on Swoole for blazing fast connection management across HTTP, TCP, and SMTP protocols.
44

5-
## 🚀 Performance First
5+
## Performance First
66

77
- **Swoole coroutines**: Handle 100,000+ concurrent connections per server
88
- **Connection pooling**: Reuse connections to backend services
@@ -11,16 +11,17 @@ High-performance, protocol-agnostic proxy built on Swoole for blazing fast conne
1111
- **Async I/O**: Non-blocking operations throughout
1212
- **Memory efficient**: Shared memory tables for state management
1313

14-
## 🎯 Features
14+
## Features
1515

1616
- Protocol-agnostic connection management
1717
- Cold-start detection and triggering
1818
- Automatic connection queueing during cold-starts
1919
- Health checking and circuit breakers
2020
- Built-in telemetry and metrics
21+
- SSRF validation for security
2122
- Support for HTTP, TCP (PostgreSQL/MySQL), and SMTP
2223

23-
## 📦 Installation
24+
## Installation
2425

2526
### Using Composer
2627

@@ -38,37 +39,95 @@ docker-compose up -d
3839

3940
See [DOCKER.md](DOCKER.md) for detailed Docker setup and configuration.
4041

41-
## 🏃 Quick Start
42+
## Quick Start
4243

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.
4445

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:
4649

4750
```php
4851
<?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+
```
50102

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
55104

56-
$adapter = new HTTPAdapter();
57-
$service = $adapter->getService() ?? new HTTPService();
105+
```php
106+
<?php
107+
require 'vendor/autoload.php';
58108

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;
64112

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+
};
66125

67126
$server = new HTTPServer(
127+
$resolver,
68128
host: '0.0.0.0',
69129
port: 80,
70-
workers: swoole_cpu_num() * 2,
71-
config: ['adapter' => $adapter]
130+
workers: swoole_cpu_num() * 2
72131
);
73132

74133
$server->start();
@@ -80,9 +139,25 @@ $server->start();
80139
<?php
81140
require 'vendor/autoload.php';
82141

142+
use Utopia\Proxy\Resolver;
143+
use Utopia\Proxy\Resolver\Result;
83144
use Utopia\Proxy\Server\TCP\Swoole as TCPServer;
84145

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+
85159
$server = new TCPServer(
160+
$resolver,
86161
host: '0.0.0.0',
87162
ports: [5432, 3306], // PostgreSQL, MySQL
88163
workers: swoole_cpu_num() * 2
@@ -97,9 +172,25 @@ $server->start();
97172
<?php
98173
require 'vendor/autoload.php';
99174

175+
use Utopia\Proxy\Resolver;
176+
use Utopia\Proxy\Resolver\Result;
100177
use Utopia\Proxy\Server\SMTP\Swoole as SMTPServer;
101178

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+
102192
$server = new SMTPServer(
193+
$resolver,
103194
host: '0.0.0.0',
104195
port: 25,
105196
workers: swoole_cpu_num() * 2
@@ -108,38 +199,36 @@ $server = new SMTPServer(
108199
$server->start();
109200
```
110201

111-
## 🔧 Configuration
202+
## Configuration
112203

113204
```php
114205
<?php
115206
$config = [
116-
'host' => '0.0.0.0',
117-
'port' => 80,
118-
'workers' => 16,
119-
120207
// 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)
130-
'db_host' => 'localhost',
131-
'db_port' => 3306,
132-
'db_user' => 'appwrite',
133-
'db_pass' => 'password',
134-
'db_name' => 'appwrite',
135-
136-
// Redis cache
137-
'redis_host' => '127.0.0.1',
138-
'redis_port' => 6379,
208+
'max_connections' => 100_000,
209+
'max_coroutine' => 100_000,
210+
'socket_buffer_size' => 8 * 1024 * 1024, // 8MB
211+
'buffer_output_size' => 8 * 1024 * 1024, // 8MB
212+
'log_level' => SWOOLE_LOG_ERROR,
213+
214+
// HTTP-specific
215+
'backend_pool_size' => 2048,
216+
'telemetry_headers' => true,
217+
'fast_path' => true,
218+
'open_http2_protocol' => false,
219+
220+
// Cold-start settings
221+
'cold_start_timeout' => 30_000, // 30 seconds
222+
'health_check_interval' => 100, // 100ms
223+
224+
// Security
225+
'skip_validation' => false, // Enable SSRF protection
139226
];
227+
228+
$server = new HTTPServer($resolver, '0.0.0.0', 80, 16, $config);
140229
```
141230

142-
## Testing
231+
## Testing
143232

144233
```bash
145234
composer test
@@ -157,9 +246,9 @@ Coverage (requires Xdebug or PCOV):
157246
vendor/bin/phpunit --coverage-text
158247
```
159248

160-
## 🎨 Architecture
249+
## Architecture
161250

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.
163252

164253
```
165254
┌─────────────────────────────────────────────────────────────────┐
@@ -183,65 +272,75 @@ The protocol-proxy follows the **Adapter Pattern** used throughout utopia-php li
183272
│ └─────────────┴─────────────┘ │
184273
│ │ │
185274
│ ┌────────▼────────┐ │
186-
│ │ Adapter │ │
187-
│ │ (Abstract) │ │
275+
│ │ Resolver │ │
276+
│ │ (Interface) │ │
188277
│ └────────┬────────┘ │
189278
│ │ │
190279
│ ┌───────────────┼───────────────┐ │
191280
│ │ │ │ │
192281
│ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │
193-
│ │ Cache │ │ Database│ │ Compute │ │
194-
│ │ Layer │ │ Pool │ │ API │ │
282+
│ │ Routing │ │Lifecycle│ │ Stats │ │
283+
│ │ Cache │ │ Hooks │ │ & Logs │ │
195284
│ └─────────┘ └─────────┘ └─────────┘ │
196285
│ │
197286
└─────────────────────────────────────────────────────────────────┘
198287
```
199288

200-
### Adapter Pattern
289+
### Resolver Interface
201290

202-
Following the design principles of utopia-php libraries:
291+
The `Resolver` interface is the core abstraction point:
203292

204-
- **Abstract Base**: `Adapter` class defines core proxy behavior
205-
- Connection handling and routing
206-
- Cold-start detection and triggering
207-
- Caching and performance optimization
293+
```php
294+
interface Resolver
295+
{
296+
// Map resource ID to backend endpoint
297+
public function resolve(string $resourceId): Result;
208298

209-
- **Protocol-Specific Adapters**:
210-
- `HTTP` - Routes HTTP requests based on hostname
211-
- `TCP` - Routes TCP connections (PostgreSQL/MySQL) based on SNI
212-
- `SMTP` - Routes SMTP connections based on email domain
299+
// Lifecycle hooks
300+
public function onConnect(string $resourceId, array $metadata = []): void;
301+
public function onDisconnect(string $resourceId, array $metadata = []): void;
213302

214-
This pattern enables:
215-
- Easy addition of new protocols
216-
- Protocol-specific optimizations
217-
- Consistent interface across all proxy types
218-
- Shared infrastructure (caching, pooling, metrics)
303+
// Activity tracking for cold-start detection
304+
public function trackActivity(string $resourceId, array $metadata = []): void;
219305

220-
## 📊 Performance Benchmarks
306+
// Cache management
307+
public function invalidateCache(string $resourceId): void;
221308

309+
// Statistics
310+
public function getStats(): array;
311+
}
222312
```
223-
HTTP Proxy:
224-
- Requests/sec: 250,000+
225-
- Latency p50: <1ms
226-
- Latency p99: <5ms
227-
- Connections: 100,000+ concurrent
228-
229-
TCP Proxy:
230-
- Connections/sec: 100,000+
231-
- Throughput: 10GB/s+
232-
- Latency: <1ms forwarding overhead
233-
234-
SMTP Proxy:
235-
- Messages/sec: 50,000+
236-
- Concurrent connections: 50,000+
313+
314+
### Resolution Result
315+
316+
The `Result` class contains the resolved backend endpoint:
317+
318+
```php
319+
new Result(
320+
endpoint: 'host:port', // Required: backend endpoint
321+
metadata: ['key' => 'val'], // Optional: additional data
322+
timeout: 30 // Optional: connection timeout override
323+
);
237324
```
238325

239-
## 🧪 Testing
326+
### Resolution Exceptions
240327

241-
```bash
242-
composer test
328+
Use `Resolver\Exception` with appropriate error codes:
329+
330+
```php
331+
throw new Exception('Not found', Exception::NOT_FOUND); // 404
332+
throw new Exception('Unavailable', Exception::UNAVAILABLE); // 503
333+
throw new Exception('Timeout', Exception::TIMEOUT); // 504
334+
throw new Exception('Forbidden', Exception::FORBIDDEN); // 403
335+
throw new Exception('Error', Exception::INTERNAL); // 500
243336
```
244337

245-
## 📝 License
338+
### Protocol-Specific Adapters
339+
340+
- **HTTP** - Routes requests based on `Host` header
341+
- **TCP** - Routes connections based on database name from PostgreSQL/MySQL protocol
342+
- **SMTP** - Routes connections based on domain from EHLO/HELO command
343+
344+
## License
246345

247346
BSD-3-Clause

0 commit comments

Comments
 (0)