rhizome-node/src/http/index.ts
Lentil Hoffman b5c0c010a9
feat: Refactor Docker orchestrator and enhance test utilities
This commit includes a major refactoring of the Docker orchestrator implementation
along with improvements to the testing infrastructure:

- Refactored Docker orchestrator to handle dynamic port assignment
- Added comprehensive test utilities in docker-test-utils.ts
- Improved error handling and resource cleanup in test environments
- Enhanced NodeStatus interface with containerId and networkId
- Added support for different storage types in NodeConfig
- Fixed request port handling in TestOrchestrator
- Added proper cleanup method to NodeOrchestrator interface

The changes ensure more reliable container management and better test isolation
while maintaining backward compatibility with existing implementations.

BREAKING CHANGE: The NodeOrchestrator interface now requires a cleanup() method.
2025-06-19 16:58:07 -05:00

107 lines
2.8 KiB
TypeScript

import Debug from "debug";
import express from "express";
import {Server} from "http";
import {RhizomeNode} from "../node";
import {HttpApi} from "./api";
import {HttpHtml} from "./html";
const debug = Debug('rz:http-api');
export class HttpServer {
app = express();
httpHtml: HttpHtml;
httpApi: HttpApi;
server?: Server;
constructor(readonly rhizomeNode: RhizomeNode) {
this.httpHtml = new HttpHtml(this.rhizomeNode);
this.httpApi = new HttpApi(this.rhizomeNode);
this.app.use(express.json());
this.app.use('/html', this.httpHtml.router);
this.app.use('/api', this.httpApi.router);
}
/**
* Start the HTTP server
*/
start() {
const {httpAddr, httpPort} = this.rhizomeNode.config;
debug(`[${this.rhizomeNode.config.peerId}]`, `Starting HTTP server on ${httpAddr}:${httpPort}...`);
try {
this.httpHtml.start();
// Create the server
this.server = this.app.listen({
port: httpPort,
host: httpAddr,
exclusive: true
});
// Add error handler
this.server.on('error', (error) => {
debug(`[${this.rhizomeNode.config.peerId}]`, `HTTP server error:`, error);
});
// Add callback for logging
this.server.on('listening', () => {
const address = this.server?.address();
const actualPort = typeof address === 'string' ? httpPort : address?.port;
debug(`[${this.rhizomeNode.config.peerId}]`, `HTTP server bound to ${httpAddr}:${actualPort}`);
});
debug(`[${this.rhizomeNode.config.peerId}]`, 'HTTP server start initiated');
} catch (error) {
debug(`[${this.rhizomeNode.config.peerId}]`, 'Error starting HTTP server:', error);
throw error;
}
}
/**
* Start the HTTP server and return a promise that resolves when the server is listening
*/
async startAndWait(): Promise<void> {
// If server is already listening, resolve immediately
if (this.server?.listening) {
return Promise.resolve();
}
return new Promise<void>((resolve, reject) => {
const timeout = setTimeout(() => {
cleanup();
reject(new Error(`HTTP server failed to start within 10 seconds`));
}, 10000);
const onListening = () => {
cleanup();
resolve();
};
const onError = (error: Error) => {
cleanup();
reject(error);
};
const cleanup = () => {
clearTimeout(timeout);
this.server?.off('listening', onListening);
this.server?.off('error', onError);
};
// Start the server if not already started
if (!this.server) {
this.start();
}
// Add event listeners
this.server?.on('listening', onListening);
this.server?.on('error', onError);
});
}
async stop() {
this.server?.close();
this.httpHtml.stop();
}
}