Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/workers-vpc/patterns.md
1# Common Patterns23Real-world patterns and examples for TCP Sockets in Cloudflare Workers.45```typescript6import { connect } from 'cloudflare:sockets';7```89## Basic Patterns1011### Simple Request-Response1213```typescript14const socket = connect({ hostname: "echo.example.com", port: 7 }, { secureTransport: "on" });15try {16await socket.opened;17const writer = socket.writable.getWriter();18await writer.write(new TextEncoder().encode("Hello\n"));19await writer.close();2021const reader = socket.readable.getReader();22const { value } = await reader.read();23return new Response(value);24} finally {25await socket.close();26}27```2829### Reading All Data3031```typescript32async function readAll(socket: Socket): Promise<Uint8Array> {33const reader = socket.readable.getReader();34const chunks: Uint8Array[] = [];35while (true) {36const { done, value } = await reader.read();37if (done) break;38chunks.push(value);39}40const total = chunks.reduce((sum, c) => sum + c.length, 0);41const result = new Uint8Array(total);42let offset = 0;43for (const chunk of chunks) { result.set(chunk, offset); offset += chunk.length; }44return result;45}46```4748### Streaming Response4950```typescript51// Stream socket data directly to HTTP response52const socket = connect({ hostname: "stream.internal", port: 9000 }, { secureTransport: "on" });53const writer = socket.writable.getWriter();54await writer.write(new TextEncoder().encode("STREAM\n"));55await writer.close();56return new Response(socket.readable);57```5859## Protocol Examples6061### Redis RESP6263```typescript64// Send: *2\r\n$3\r\nGET\r\n$<keylen>\r\n<key>\r\n65// Recv: $<len>\r\n<data>\r\n or $-1\r\n for null66const socket = connect({ hostname: "redis.internal", port: 6379 });67const writer = socket.writable.getWriter();68await writer.write(new TextEncoder().encode(`*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n`));69```7071### PostgreSQL7273**Use [Hyperdrive](../hyperdrive/) for production.** Raw Postgres protocol is complex (startup, auth, query messages).7475### MQTT7677```typescript78const socket = connect({ hostname: "mqtt.broker", port: 1883 });79const writer = socket.writable.getWriter();80// CONNECT: 0x10 <len> 0x00 0x04 "MQTT" 0x04 <flags> ...81// PUBLISH: 0x30 <len> <topic_len> <topic> <message>82```8384## Error Handling Patterns8586### Retry with Backoff8788```typescript89async function connectWithRetry(addr: SocketAddress, opts: SocketOptions, maxRetries = 3): Promise<Socket> {90for (let i = 1; i <= maxRetries; i++) {91try {92const socket = connect(addr, opts);93await socket.opened;94return socket;95} catch (error) {96if (i === maxRetries) throw error;97await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i - 1))); // Exponential backoff98}99}100throw new Error('Unreachable');101}102```103104### Timeout105106```typescript107async function connectWithTimeout(addr: SocketAddress, opts: SocketOptions, ms = 5000): Promise<Socket> {108const socket = connect(addr, opts);109const timeout = new Promise<never>((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms));110await Promise.race([socket.opened, timeout]);111return socket;112}113```114115### Fallback116117```typescript118async function connectWithFallback(primary: string, fallback: string, port: number): Promise<Socket> {119try {120const socket = connect({ hostname: primary, port }, { secureTransport: "on" });121await socket.opened;122return socket;123} catch {124return connect({ hostname: fallback, port }, { secureTransport: "on" });125}126}127```128129## Security Patterns130131### Destination Allowlist (Prevent SSRF)132133```typescript134const ALLOWED_HOSTS = ['db.internal.company.net', 'api.internal.company.net', /^10\.0\.1\.\d+$/];135136function isAllowed(hostname: string): boolean {137return ALLOWED_HOSTS.some(p => p instanceof RegExp ? p.test(hostname) : p === hostname);138}139140export default {141async fetch(req: Request): Promise<Response> {142const target = new URL(req.url).searchParams.get('host');143if (!target || !isAllowed(target)) return new Response('Forbidden', { status: 403 });144const socket = connect({ hostname: target, port: 443 });145// Use socket...146}147};148```149150### Connection Pooling151152```typescript153class SocketPool {154private pool = new Map<string, Socket[]>();155156async acquire(hostname: string, port: number): Promise<Socket> {157const key = `${hostname}:${port}`;158const sockets = this.pool.get(key) || [];159if (sockets.length > 0) return sockets.pop()!;160const socket = connect({ hostname, port }, { secureTransport: "on" });161await socket.opened;162return socket;163}164165release(hostname: string, port: number, socket: Socket): void {166const key = `${hostname}:${port}`;167const sockets = this.pool.get(key) || [];168if (sockets.length < 3) { sockets.push(socket); this.pool.set(key, sockets); }169else socket.close();170}171}172```173174## Multi-Protocol Gateway175176```typescript177interface Protocol { name: string; defaultPort: number; test(host: string, port: number): Promise<string>; }178179const PROTOCOLS: Record<string, Protocol> = {180redis: {181name: 'redis',182defaultPort: 6379,183async test(host, port) {184const socket = connect({ hostname: host, port });185try {186const writer = socket.writable.getWriter();187await writer.write(new TextEncoder().encode('*1\r\n$4\r\nPING\r\n'));188writer.releaseLock();189const reader = socket.readable.getReader();190const { value } = await reader.read();191return new TextDecoder().decode(value || new Uint8Array());192} finally { await socket.close(); }193}194}195};196197export default {198async fetch(req: Request): Promise<Response> {199const url = new URL(req.url);200const proto = url.pathname.slice(1); // /redis201const host = url.searchParams.get('host');202if (!host || !PROTOCOLS[proto]) return new Response('Invalid', { status: 400 });203const result = await PROTOCOLS[proto].test(host, parseInt(url.searchParams.get('port') || '') || PROTOCOLS[proto].defaultPort);204return new Response(result);205}206};207```208209210