(opts: StartProxyOptions)
| 191 | ]; |
| 192 | |
| 193 | export async function startProxyServer(opts: StartProxyOptions): Promise<ProxyServerHandle> { |
| 194 | const timeoutMs = opts.upstreamTimeoutMs ?? DEFAULT_UPSTREAM_TIMEOUT_MS; |
| 195 | const upgradeSockets = new Set<Duplex>(); |
| 196 | const httpServer: HttpServer = createHttpServer((req, res) => { |
| 197 | if (rejectIfNotLoopbackApi(req, res)) return; |
| 198 | forwardRequest(req, res, opts.upstreamHost, opts.upstreamPort, timeoutMs); |
| 199 | }); |
| 200 | |
| 201 | httpServer.on('upgrade', (req, clientSocket, head) => { |
| 202 | if (rejectUpgradeIfNotLoopback(req, clientSocket)) return; |
| 203 | proxyUpgrade(req, clientSocket, head, opts.upstreamHost, opts.upstreamPort, upgradeSockets); |
| 204 | }); |
| 205 | |
| 206 | await new Promise<void>((done, fail) => { |
| 207 | const onError = (err: Error) => fail(err); |
| 208 | httpServer.once('error', onError); |
| 209 | httpServer.listen(opts.listenPort, opts.host, () => { |
| 210 | httpServer.off('error', onError); |
| 211 | done(); |
| 212 | }); |
| 213 | }); |
| 214 | |
| 215 | const addr = httpServer.address(); |
| 216 | const port = typeof addr === 'object' && addr !== null ? addr.port : opts.listenPort; |
| 217 | |
| 218 | return { |
| 219 | httpServer, |
| 220 | port, |
| 221 | close: () => |
| 222 | new Promise<void>((done) => { |
| 223 | for (const sock of upgradeSockets) { |
| 224 | try { |
| 225 | sock.destroy(); |
| 226 | } catch {} |
| 227 | } |
| 228 | upgradeSockets.clear(); |
| 229 | httpServer.close(() => done()); |
| 230 | httpServer.closeIdleConnections(); |
| 231 | }), |
| 232 | }; |
| 233 | } |
| 234 | |
| 235 | interface ProxyRequestOptions { |
| 236 | upstreamHost: string; |
no test coverage detected