forked from oven-sh/bun
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathunix-domain-socket-proxy.ts
More file actions
136 lines (116 loc) · 3.83 KB
/
unix-domain-socket-proxy.ts
File metadata and controls
136 lines (116 loc) · 3.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import * as net from "node:net";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
/**
* A Unix domain socket proxy that forwards connections to a TCP host:port.
* This is useful for testing Unix socket connections when the actual service
* is running in a Docker container accessible only via TCP.
*/
export class UnixDomainSocketProxy {
private server: net.Server | null = null;
private socketPath: string;
private targetHost: string;
private targetPort: number;
private serviceName: string;
private connections: Set<net.Socket> = new Set();
constructor(serviceName: string, targetHost: string, targetPort: number) {
this.serviceName = serviceName;
this.targetHost = targetHost;
this.targetPort = targetPort;
this.socketPath = path.join(os.tmpdir(), `${serviceName}_proxy_${Date.now()}.sock`);
}
/**
* Get the Unix socket path for clients to connect to
*/
get path(): string {
return this.socketPath;
}
/**
* Start the proxy server
*/
async start(): Promise<void> {
// Clean up any existing socket file
try {
fs.unlinkSync(this.socketPath);
} catch {
// Ignore error if file doesn't exist
}
return new Promise((resolve, reject) => {
this.server = net.createServer(clientSocket => {
console.log(`${this.serviceName} connection received on unix socket`);
// Track this connection
this.connections.add(clientSocket);
// Create connection to the actual service container
const containerSocket = net.createConnection({
host: this.targetHost,
port: this.targetPort,
});
// Handle container connection
containerSocket.on("connect", () => {
console.log(`Connected to ${this.serviceName} container via proxy`);
});
containerSocket.on("error", err => {
console.error(`${this.serviceName} container connection error:`, err);
clientSocket.destroy();
});
containerSocket.on("close", () => {
clientSocket.end();
this.connections.delete(clientSocket);
});
// Handle client socket
clientSocket.on("data", data => {
containerSocket.write(data);
});
clientSocket.on("error", err => {
console.error(`${this.serviceName} client socket error:`, err);
containerSocket.destroy();
});
clientSocket.on("close", () => {
containerSocket.end();
this.connections.delete(clientSocket);
});
// Forward container responses back to client
containerSocket.on("data", data => {
clientSocket.write(data);
});
});
this.server.on("error", reject);
this.server.listen(this.socketPath, () => {
console.log(`Unix domain socket proxy for ${this.serviceName} listening on ${this.socketPath}`);
resolve();
});
});
}
/**
* Stop the proxy server and clean up
*/
stop(): void {
// Close all active connections
for (const connection of this.connections) {
connection.destroy();
}
this.connections.clear();
// Close the server
if (this.server) {
this.server.close();
this.server = null;
console.log(`Closed Unix socket proxy server for ${this.serviceName}`);
}
// Remove the socket file
try {
fs.unlinkSync(this.socketPath);
console.log(`Removed Unix socket file for ${this.serviceName}`);
} catch {
// Ignore error if file doesn't exist
}
}
/**
* Create and start a proxy instance
*/
static async create(serviceName: string, targetHost: string, targetPort: number): Promise<UnixDomainSocketProxy> {
const proxy = new UnixDomainSocketProxy(serviceName, targetHost, targetPort);
await proxy.start();
return proxy;
}
}