273
274
private async _startServer(seed: TSeed): Promise<IInternalRuntime<TState>> {
276
>
const inFlight = new Set<IProxyInFlight>();
277
>
const httpModule = await import('http');
278
>
const server = httpModule.createServer();
279
>
280
>
await new Promise<void>((resolve, reject) => {
281
>
const onError = (err: Error) => reject(err);
282
>
server.once('error', onError);
283
>
server.listen(0, '127.0.0.1', () => {
284
>
server.removeListener('error', onError);
285
>
resolve();
286
>
});
287
>
});
288
>
289
>
const address = server.address();
290
>
if (!address || typeof address === 'string') {
291
server.close();
292
throw new Error(`${this.name} failed to bind: unexpected address ${String(address)}`);
293
}
295
>
this._logService.info(`[${this.name}] listening on ${baseUrl}`);
296
>
297
>
const runtime: IInternalRuntime<TState> = {
298
>
server,
299
>
baseUrl,
300
>
nonce,
301
>
inFlight,
302
>
refcount: 0,
303
>
state: this.createState(seed),
304
>
};
305
>
306
>
// Attach the request handler only after `runtime` is fully built.
307
>
// Node's single-threaded event loop guarantees no `request` event can
308
>
// be parsed and dispatched between `listen` resolving and this
309
>
// synchronous registration, so the handler can safely close over
310
>
// `runtime` as a `const`.
311
>
server.on('request', (req, res) => {
312
this.handleRequest(req, res, runtime).catch(err => {
313
// Last-resort safety net. Concrete proxies are expected to