update skills

This commit is contained in:
2026-03-17 16:53:22 -07:00
parent 0b0783ef8e
commit f9a530667e
389 changed files with 54512 additions and 1 deletions

View File

@@ -0,0 +1,127 @@
# Workers VPC Connectivity
Connect Cloudflare Workers to private networks and internal infrastructure using TCP Sockets.
## Overview
Workers VPC connectivity enables outbound TCP connections from Workers to private resources in AWS, Azure, GCP, on-premises datacenters, or any private network. This is achieved through the **TCP Sockets API** (`cloudflare:sockets`), which provides low-level network access for custom protocols and services.
**Key capabilities:**
- Direct TCP connections to private IPs and hostnames
- TLS/StartTLS support for encrypted connections
- Integration with Cloudflare Tunnel for secure private network access
- Full control over wire protocols (database protocols, SSH, MQTT, custom TCP)
**Note:** This reference documents the TCP Sockets API. For the newer Workers VPC Services product (HTTP-only service bindings with built-in SSRF protection), refer to separate documentation when available. VPC Services is currently in beta (2025+).
## Quick Decision: Which Technology?
Need private network connectivity from Workers?
| Requirement | Use | Why |
|------------|-----|-----|
| HTTP/HTTPS APIs in private network | VPC Services (beta, separate docs) | SSRF-safe, declarative bindings |
| PostgreSQL/MySQL databases | [Hyperdrive](../hyperdrive/) | Connection pooling, caching, optimized |
| Custom TCP protocols (SSH, MQTT, proprietary) | **TCP Sockets (this doc)** | Full protocol control |
| Simple HTTP with lowest latency | TCP Sockets + [Smart Placement](../smart-placement/) | Manual optimization |
| Expose on-prem to internet (inbound) | [Cloudflare Tunnel](../tunnel/) | Not Worker-specific |
## When to Use TCP Sockets
**Use TCP Sockets when you need:**
- ✅ Direct control over wire protocols (e.g., Postgres wire protocol, SSH, Redis RESP)
- ✅ Non-HTTP protocols (MQTT, SMTP, custom binary protocols)
- ✅ StartTLS or custom TLS negotiation
- ✅ Streaming binary data over TCP
**Don't use TCP Sockets when:**
- ❌ You just need HTTP/HTTPS (use `fetch()` or VPC Services)
- ❌ You need PostgreSQL/MySQL (use Hyperdrive for pooling)
- ❌ You need WebSocket (use native Workers WebSocket)
## Quick Start
```typescript
import { connect } from 'cloudflare:sockets';
export default {
async fetch(req: Request): Promise<Response> {
// Connect to private service
const socket = connect(
{ hostname: "db.internal.company.net", port: 5432 },
{ secureTransport: "on" }
);
try {
await socket.opened; // Wait for connection
const writer = socket.writable.getWriter();
await writer.write(new TextEncoder().encode("QUERY\r\n"));
await writer.close();
const reader = socket.readable.getReader();
const { value } = await reader.read();
return new Response(value);
} finally {
await socket.close();
}
}
};
```
## Architecture Pattern: Workers + Tunnel
Most private network connectivity combines TCP Sockets with Cloudflare Tunnel:
```
┌─────────┐ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Worker │────▶│ TCP Socket │────▶│ Tunnel │────▶│ Private │
│ │ │ (this API) │ │ (cloudflared)│ │ Network │
└─────────┘ └─────────────┘ └──────────────┘ └─────────────┘
```
1. Worker opens TCP socket to Tunnel hostname
2. Tunnel endpoint routes to private IP
3. Response flows back through Tunnel to Worker
See [configuration.md](./configuration.md) for Tunnel setup details.
## Reading Order
1. **Start here (README.md)** - Overview and decision guide
2. **[api.md](./api.md)** - Socket interface, types, methods
3. **[configuration.md](./configuration.md)** - Wrangler setup, Tunnel integration
4. **[patterns.md](./patterns.md)** - Real-world examples (databases, protocols, error handling)
5. **[gotchas.md](./gotchas.md)** - Limits, blocked ports, common errors
## Key Limits
| Limit | Value |
|-------|-------|
| Max concurrent sockets per request | 6 |
| Blocked destinations | Cloudflare IPs, localhost, port 25 |
| Scope requirement | Must create in handler (not global) |
See [gotchas.md](./gotchas.md) for complete limits and troubleshooting.
## Best Practices
1. **Always close sockets** - Use try/finally blocks
2. **Validate destinations** - Prevent SSRF by allowlisting hosts
3. **Use Hyperdrive for databases** - Better performance than raw TCP
4. **Prefer fetch() for HTTP** - Only use TCP when necessary
5. **Combine with Smart Placement** - Reduce latency to private networks
## Related Technologies
- **[Hyperdrive](../hyperdrive/)** - PostgreSQL/MySQL with connection pooling
- **[Cloudflare Tunnel](../tunnel/)** - Secure private network access
- **[Smart Placement](../smart-placement/)** - Auto-locate Workers near backends
- **VPC Services (beta)** - HTTP-only service bindings with SSRF protection (separate docs)
## Reference
- [TCP Sockets API Documentation](https://developers.cloudflare.com/workers/runtime-apis/tcp-sockets/)
- [Connect to databases guide](https://developers.cloudflare.com/workers/tutorials/connect-to-postgres/)
- [Cloudflare Tunnel setup](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/)

View File

@@ -0,0 +1,202 @@
# TCP Sockets API Reference
Complete API reference for the Cloudflare Workers TCP Sockets API (`cloudflare:sockets`).
## Core Function: `connect()`
```typescript
function connect(
address: SocketAddress,
options?: SocketOptions
): Socket
```
Creates an outbound TCP connection to the specified address.
### Parameters
#### `SocketAddress`
```typescript
interface SocketAddress {
hostname: string; // DNS hostname or IP address
port: number; // TCP port (1-65535, excluding blocked ports)
}
```
| Field | Type | Description | Example |
|-------|------|-------------|---------|
| `hostname` | `string` | Target hostname or IP | `"db.internal.net"`, `"10.0.1.50"` |
| `port` | `number` | TCP port number | `5432`, `443`, `22` |
DNS names are resolved at connection time. IPv4, IPv6, and private IPs (10.x, 172.16.x, 192.168.x) supported.
#### `SocketOptions`
```typescript
interface SocketOptions {
secureTransport?: "off" | "on" | "starttls";
allowHalfOpen?: boolean;
}
```
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `secureTransport` | `"off" \| "on" \| "starttls"` | `"off"` | TLS mode |
| `allowHalfOpen` | `boolean` | `false` | Allow half-closed connections |
**`secureTransport` modes:**
| Mode | Behavior | Use Case |
|------|----------|----------|
| `"off"` | Plain TCP, no encryption | Testing, internal trusted networks |
| `"on"` | Immediate TLS handshake | HTTPS, secure databases, SSH |
| `"starttls"` | Start plain, upgrade later with `startTls()` | Postgres, SMTP, IMAP |
**`allowHalfOpen`:** When `false` (default), closing read stream auto-closes write stream. When `true`, streams are independent.
### Returns
A `Socket` object with readable/writable streams.
## Socket Interface
```typescript
interface Socket {
// Streams
readable: ReadableStream<Uint8Array>;
writable: WritableStream<Uint8Array>;
// Connection state
opened: Promise<SocketInfo>;
closed: Promise<void>;
// Methods
close(): Promise<void>;
startTls(): Socket;
}
```
### Properties
#### `readable: ReadableStream<Uint8Array>`
Stream for reading data from the socket. Use `getReader()` to consume data.
```typescript
const reader = socket.readable.getReader();
const { done, value } = await reader.read(); // Read one chunk
```
#### `writable: WritableStream<Uint8Array>`
Stream for writing data to the socket. Use `getWriter()` to send data.
```typescript
const writer = socket.writable.getWriter();
await writer.write(new TextEncoder().encode("HELLO\r\n"));
await writer.close();
```
#### `opened: Promise<SocketInfo>`
Promise that resolves when connection succeeds, rejects on failure.
```typescript
interface SocketInfo {
remoteAddress?: string; // May be undefined
localAddress?: string; // May be undefined
}
try {
const info = await socket.opened;
} catch (error) {
// Connection failed
}
```
#### `closed: Promise<void>`
Promise that resolves when socket is fully closed (both directions).
### Methods
#### `close(): Promise<void>`
Closes the socket gracefully, waiting for pending writes to complete.
```typescript
const socket = connect({ hostname: "api.internal", port: 443 });
try {
// Use socket
} finally {
await socket.close(); // Always call in finally block
}
```
#### `startTls(): Socket`
Upgrades connection to TLS. Only available when `secureTransport: "starttls"` was specified.
```typescript
const socket = connect(
{ hostname: "db.internal", port: 5432 },
{ secureTransport: "starttls" }
);
// Send protocol-specific StartTLS command
const writer = socket.writable.getWriter();
await writer.write(new TextEncoder().encode("STARTTLS\r\n"));
// Upgrade to TLS - use returned socket, not original
const secureSocket = socket.startTls();
const secureWriter = secureSocket.writable.getWriter();
```
## Complete Example
```typescript
import { connect } from 'cloudflare:sockets';
export default {
async fetch(req: Request): Promise<Response> {
const socket = connect({ hostname: "echo.example.com", port: 7 }, { secureTransport: "on" });
try {
await socket.opened;
const writer = socket.writable.getWriter();
await writer.write(new TextEncoder().encode("Hello, TCP!\n"));
await writer.close();
const reader = socket.readable.getReader();
const { value } = await reader.read();
return new Response(value);
} finally {
await socket.close();
}
}
};
```
See [patterns.md](./patterns.md) for multi-chunk reading, error handling, and protocol implementations.
## Quick Reference
| Task | Code |
|------|------|
| Import | `import { connect } from 'cloudflare:sockets';` |
| Connect | `connect({ hostname: "host", port: 443 })` |
| With TLS | `connect(addr, { secureTransport: "on" })` |
| StartTLS | `socket.startTls()` after handshake |
| Write | `await writer.write(data); await writer.close();` |
| Read | `const { value } = await reader.read();` |
| Error handling | `try { await socket.opened; } catch { }` |
| Always close | `try { } finally { await socket.close(); }` |
## See Also
- [patterns.md](./patterns.md) - Real-world protocol implementations
- [configuration.md](./configuration.md) - Wrangler setup and environment variables
- [gotchas.md](./gotchas.md) - Limits and error handling

View File

@@ -0,0 +1,147 @@
# Configuration
Setup and configuration for TCP Sockets in Cloudflare Workers.
## Wrangler Configuration
### Basic Setup
TCP Sockets are available by default in Workers runtime. No special configuration required in `wrangler.jsonc`:
```jsonc
{
"name": "private-network-worker",
"main": "src/index.ts",
"compatibility_date": "2025-01-01"
}
```
### Environment Variables
Store connection details as env vars:
```jsonc
{
"vars": { "DB_HOST": "10.0.1.50", "DB_PORT": "5432" }
}
```
```typescript
interface Env { DB_HOST: string; DB_PORT: string; }
export default {
async fetch(req: Request, env: Env): Promise<Response> {
const socket = connect({ hostname: env.DB_HOST, port: parseInt(env.DB_PORT) });
}
};
```
### Per-Environment Configuration
```jsonc
{
"vars": { "DB_HOST": "localhost" },
"env": {
"staging": { "vars": { "DB_HOST": "staging-db.internal.net" } },
"production": { "vars": { "DB_HOST": "prod-db.internal.net" } }
}
}
```
Deploy: `wrangler deploy --env staging` or `wrangler deploy --env production`
## Integration with Cloudflare Tunnel
To connect Workers to private networks, combine TCP Sockets with Cloudflare Tunnel:
```
Worker (TCP Socket) → Tunnel hostname → cloudflared → Private Network
```
### Quick Setup
1. **Install cloudflared** on a server inside your private network
2. **Create tunnel**: `cloudflared tunnel create my-private-network`
3. **Configure routing** in `config.yml`:
```yaml
tunnel: <TUNNEL_ID>
credentials-file: /path/to/<TUNNEL_ID>.json
ingress:
- hostname: db.internal.example.com
service: tcp://10.0.1.50:5432
- service: http_status:404 # Required catch-all
```
4. **Run tunnel**: `cloudflared tunnel run my-private-network`
5. **Connect from Worker**:
```typescript
const socket = connect(
{ hostname: "db.internal.example.com", port: 5432 }, // Tunnel hostname
{ secureTransport: "on" }
);
```
For detailed Tunnel setup, see [Tunnel configuration reference](../tunnel/configuration.md).
## Smart Placement Integration
Reduce latency by auto-placing Workers near backends:
```jsonc
{ "placement": { "mode": "smart" } }
```
Workers automatically relocate closer to TCP socket destinations after observing connection latency. See [Smart Placement reference](../smart-placement/).
## Secrets Management
Store sensitive credentials as secrets (not in wrangler.jsonc):
```bash
wrangler secret put DB_PASSWORD # Enter value when prompted
```
Access in Worker via `env.DB_PASSWORD`. Use in protocol handshake or authentication.
## Local Development
Test with `wrangler dev`. Note: Local mode may not access private networks. Use public endpoints or mock servers for development:
```typescript
const config = process.env.NODE_ENV === 'dev'
? { hostname: 'localhost', port: 5432 } // Mock
: { hostname: 'db.internal.example.com', port: 5432 }; // Production
```
## Connection String Patterns
Parse connection strings to extract host and port:
```typescript
function parseConnectionString(connStr: string): SocketAddress {
const url = new URL(connStr); // e.g., "postgres://10.0.1.50:5432/mydb"
return { hostname: url.hostname, port: parseInt(url.port) || 5432 };
}
```
## Hyperdrive Integration
For PostgreSQL/MySQL, prefer Hyperdrive over raw TCP sockets (includes connection pooling):
```jsonc
{ "hyperdrive": [{ "binding": "DB", "id": "<HYPERDRIVE_ID>" }] }
```
See [Hyperdrive reference](../hyperdrive/) for complete setup.
## Compatibility
TCP Sockets available in all modern Workers. Use current date: `"compatibility_date": "2025-01-01"`. No special flags required.
## Related Configuration
- **[Tunnel Configuration](../tunnel/configuration.md)** - Detailed cloudflared setup
- **[Smart Placement](../smart-placement/configuration.md)** - Placement mode options
- **[Hyperdrive](../hyperdrive/configuration.md)** - Database connection pooling setup

View File

@@ -0,0 +1,167 @@
# Gotchas and Troubleshooting
Common pitfalls, limitations, and solutions for TCP Sockets in Cloudflare Workers.
## Platform Limits
### Connection Limits
| Limit | Value |
|-------|-------|
| Max concurrent sockets per request | 6 (hard limit) |
| Socket lifetime | Request duration |
| Connection timeout | Platform-dependent, no setting |
**Problem:** Exceeding 6 connections throws error
**Solution:** Process in batches of 6
```typescript
for (let i = 0; i < hosts.length; i += 6) {
const batch = hosts.slice(i, i + 6).map(h => connect({ hostname: h, port: 443 }));
await Promise.all(batch.map(async s => { /* use */ await s.close(); }));
}
```
### Blocked Destinations
Cloudflare IPs (1.1.1.1), localhost (127.0.0.1), port 25 (SMTP), Worker's own URL blocked for security.
**Solution:** Use public IPs or Tunnel hostnames: `connect({ hostname: "db.internal.company.net", port: 5432 })`
### Scope Requirements
**Problem:** Sockets created in global scope fail
**Cause:** Sockets tied to request lifecycle
**Solution:** Create inside handler: `export default { async fetch() { const socket = connect(...); } }`
## Common Errors
### Error: "proxy request failed"
**Causes:** Blocked destination (Cloudflare IP, localhost, port 25), DNS failure, network unreachable
**Solution:** Validate destinations, use Tunnel hostnames, catch errors with try/catch
### Error: "TCP Loop detected"
**Cause:** Worker connecting to itself
**Solution:** Connect to external service, not Worker's own hostname
### Error: "Port 25 prohibited"
**Cause:** SMTP port blocked
**Solution:** Use Email Workers API for email
### Error: "socket is not open"
**Cause:** Read/write after close
**Solution:** Always use try/finally to ensure proper closure order
### Error: Connection timeout
**Cause:** No built-in timeout
**Solution:** Use `Promise.race()`:
```typescript
const socket = connect(addr, opts);
const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5000));
await Promise.race([socket.opened, timeout]);
```
## TLS/SSL Issues
### StartTLS Timing
**Problem:** Calling `startTls()` too early
**Solution:** Send protocol-specific STARTTLS command, wait for server OK, then call `socket.startTls()`
### Certificate Validation
**Problem:** Self-signed certs fail
**Solution:** Use proper certs or Tunnel (handles TLS termination)
## Performance Issues
### Not Using Connection Pooling
**Problem:** New connection overhead per request
**Solution:** Use [Hyperdrive](../hyperdrive/) for databases (built-in pooling)
### Not Using Smart Placement
**Problem:** High latency to backend
**Solution:** Enable: `{ "placement": { "mode": "smart" } }` in wrangler.jsonc
### Forgetting to Close Sockets
**Problem:** Resource leaks
**Solution:** Always use try/finally:
```typescript
const socket = connect({ hostname: "api.internal", port: 443 });
try {
// Use socket
} finally {
await socket.close();
}
```
## Data Handling Issues
### Assuming Single Read Gets All Data
**Problem:** Only reading once may miss chunked data
**Solution:** Loop `reader.read()` until `done === true` (see patterns.md)
### Text Encoding Issues
**Problem:** Using wrong encoding
**Solution:** Specify encoding: `new TextDecoder('iso-8859-1').decode(data)`
## Security Issues
### SSRF Vulnerability
**Problem:** User-controlled destinations allow access to internal services
**Solution:** Validate against strict allowlist:
```typescript
const ALLOWED = ['api1.internal.net', 'api2.internal.net'];
const host = new URL(req.url).searchParams.get('host');
if (!host || !ALLOWED.includes(host)) return new Response('Forbidden', { status: 403 });
```
## When to Use Alternatives
| Use Case | Alternative | Reason |
|----------|-------------|--------|
| PostgreSQL/MySQL | [Hyperdrive](../hyperdrive/) | Connection pooling, caching |
| HTTP/HTTPS | `fetch()` | Simpler, built-in |
| HTTP with SSRF protection | VPC Services (beta 2025+) | Declarative bindings |
## Debugging Tips
1. **Log connection details:** `const info = await socket.opened; console.log(info.remoteAddress);`
2. **Test with public services first:** Use tcpbin.com:4242 echo server
3. **Verify Tunnel:** `cloudflared tunnel info <name>` and `cloudflared tunnel route ip list`
## Related
- [Hyperdrive](../hyperdrive/) - Database connections
- [Smart Placement](../smart-placement/) - Latency optimization
- [Tunnel Troubleshooting](../tunnel/gotchas.md)

View File

@@ -0,0 +1,209 @@
# Common Patterns
Real-world patterns and examples for TCP Sockets in Cloudflare Workers.
```typescript
import { connect } from 'cloudflare:sockets';
```
## Basic Patterns
### Simple Request-Response
```typescript
const socket = connect({ hostname: "echo.example.com", port: 7 }, { secureTransport: "on" });
try {
await socket.opened;
const writer = socket.writable.getWriter();
await writer.write(new TextEncoder().encode("Hello\n"));
await writer.close();
const reader = socket.readable.getReader();
const { value } = await reader.read();
return new Response(value);
} finally {
await socket.close();
}
```
### Reading All Data
```typescript
async function readAll(socket: Socket): Promise<Uint8Array> {
const reader = socket.readable.getReader();
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
const total = chunks.reduce((sum, c) => sum + c.length, 0);
const result = new Uint8Array(total);
let offset = 0;
for (const chunk of chunks) { result.set(chunk, offset); offset += chunk.length; }
return result;
}
```
### Streaming Response
```typescript
// Stream socket data directly to HTTP response
const socket = connect({ hostname: "stream.internal", port: 9000 }, { secureTransport: "on" });
const writer = socket.writable.getWriter();
await writer.write(new TextEncoder().encode("STREAM\n"));
await writer.close();
return new Response(socket.readable);
```
## Protocol Examples
### Redis RESP
```typescript
// Send: *2\r\n$3\r\nGET\r\n$<keylen>\r\n<key>\r\n
// Recv: $<len>\r\n<data>\r\n or $-1\r\n for null
const socket = connect({ hostname: "redis.internal", port: 6379 });
const writer = socket.writable.getWriter();
await writer.write(new TextEncoder().encode(`*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n`));
```
### PostgreSQL
**Use [Hyperdrive](../hyperdrive/) for production.** Raw Postgres protocol is complex (startup, auth, query messages).
### MQTT
```typescript
const socket = connect({ hostname: "mqtt.broker", port: 1883 });
const writer = socket.writable.getWriter();
// CONNECT: 0x10 <len> 0x00 0x04 "MQTT" 0x04 <flags> ...
// PUBLISH: 0x30 <len> <topic_len> <topic> <message>
```
## Error Handling Patterns
### Retry with Backoff
```typescript
async function connectWithRetry(addr: SocketAddress, opts: SocketOptions, maxRetries = 3): Promise<Socket> {
for (let i = 1; i <= maxRetries; i++) {
try {
const socket = connect(addr, opts);
await socket.opened;
return socket;
} catch (error) {
if (i === maxRetries) throw error;
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i - 1))); // Exponential backoff
}
}
throw new Error('Unreachable');
}
```
### Timeout
```typescript
async function connectWithTimeout(addr: SocketAddress, opts: SocketOptions, ms = 5000): Promise<Socket> {
const socket = connect(addr, opts);
const timeout = new Promise<never>((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms));
await Promise.race([socket.opened, timeout]);
return socket;
}
```
### Fallback
```typescript
async function connectWithFallback(primary: string, fallback: string, port: number): Promise<Socket> {
try {
const socket = connect({ hostname: primary, port }, { secureTransport: "on" });
await socket.opened;
return socket;
} catch {
return connect({ hostname: fallback, port }, { secureTransport: "on" });
}
}
```
## Security Patterns
### Destination Allowlist (Prevent SSRF)
```typescript
const ALLOWED_HOSTS = ['db.internal.company.net', 'api.internal.company.net', /^10\.0\.1\.\d+$/];
function isAllowed(hostname: string): boolean {
return ALLOWED_HOSTS.some(p => p instanceof RegExp ? p.test(hostname) : p === hostname);
}
export default {
async fetch(req: Request): Promise<Response> {
const target = new URL(req.url).searchParams.get('host');
if (!target || !isAllowed(target)) return new Response('Forbidden', { status: 403 });
const socket = connect({ hostname: target, port: 443 });
// Use socket...
}
};
```
### Connection Pooling
```typescript
class SocketPool {
private pool = new Map<string, Socket[]>();
async acquire(hostname: string, port: number): Promise<Socket> {
const key = `${hostname}:${port}`;
const sockets = this.pool.get(key) || [];
if (sockets.length > 0) return sockets.pop()!;
const socket = connect({ hostname, port }, { secureTransport: "on" });
await socket.opened;
return socket;
}
release(hostname: string, port: number, socket: Socket): void {
const key = `${hostname}:${port}`;
const sockets = this.pool.get(key) || [];
if (sockets.length < 3) { sockets.push(socket); this.pool.set(key, sockets); }
else socket.close();
}
}
```
## Multi-Protocol Gateway
```typescript
interface Protocol { name: string; defaultPort: number; test(host: string, port: number): Promise<string>; }
const PROTOCOLS: Record<string, Protocol> = {
redis: {
name: 'redis',
defaultPort: 6379,
async test(host, port) {
const socket = connect({ hostname: host, port });
try {
const writer = socket.writable.getWriter();
await writer.write(new TextEncoder().encode('*1\r\n$4\r\nPING\r\n'));
writer.releaseLock();
const reader = socket.readable.getReader();
const { value } = await reader.read();
return new TextDecoder().decode(value || new Uint8Array());
} finally { await socket.close(); }
}
}
};
export default {
async fetch(req: Request): Promise<Response> {
const url = new URL(req.url);
const proto = url.pathname.slice(1); // /redis
const host = url.searchParams.get('host');
if (!host || !PROTOCOLS[proto]) return new Response('Invalid', { status: 400 });
const result = await PROTOCOLS[proto].test(host, parseInt(url.searchParams.get('port') || '') || PROTOCOLS[proto].defaultPort);
return new Response(result);
}
};
```