Skip to content

Commit

Permalink
Addressed initialization issues and patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
thehenrytsai committed Jul 5, 2024
1 parent 704cbb4 commit 9ea81e4
Show file tree
Hide file tree
Showing 17 changed files with 351 additions and 116 deletions.
71 changes: 67 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"readable-stream": "4.4.2",
"response-time": "2.3.2",
"uuid": "9.0.0",
"ws": "8.17.1"
"ws": "8.18.0"
},
"devDependencies": {
"@types/bytes": "3.1.1",
Expand Down
4 changes: 3 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export const config = {
* Postgres: 'postgres://root:dwn@localhost:5432/dwn'
* MySQL: 'mysql://root:dwn@localhost:3306/dwn'
*/
ttlCacheUrl: process.env.DWN_TTL_CACHE_URL || 'sqlite://',
ttlCacheUrl: process.env.DWN_TTL_CACHE_URL || 'mysql://root:dwn@localhost:3306/dwn',
// ttlCacheUrl: process.env.DWN_TTL_CACHE_URL || 'sqlite://',
// ttlCacheUrl: process.env.DWN_TTL_CACHE_URL || 'postgres://root:dwn@localhost:5432/dwn',

/**
* Used to populate the `version` and `sdkVersion` properties returned by the `/info` endpoint.
Expand Down
57 changes: 50 additions & 7 deletions src/dwn-server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { EventStream } from '@tbd54566975/dwn-sdk-js';
import { Dwn, EventEmitterStream } from '@tbd54566975/dwn-sdk-js';

import type { ProcessHandlers } from './process-handlers.js';
import type { Server } from 'http';
import log from 'loglevel';
import prefix from 'loglevel-plugin-prefix';
Expand All @@ -10,7 +11,7 @@ import { HttpServerShutdownHandler } from './lib/http-server-shutdown-handler.js

import { type DwnServerConfig, config as defaultConfig } from './config.js';
import { HttpApi } from './http-api.js';
import { setProcessHandlers } from './process-handlers.js';
import { setProcessHandlers, unsetProcessHandlers } from './process-handlers.js';
import { getDWNConfig } from './storage.js';
import { WsApi } from './ws-api.js';
import { RegistrationManager } from './registration/registration-manager.js';
Expand All @@ -20,7 +21,14 @@ export type DwnServerOptions = {
config?: DwnServerConfig;
};

export enum DwnServerState {
Stopped,
Started
}

export class DwnServer {
serverState = DwnServerState.Stopped;
processHandlers: ProcessHandlers;
dwn?: Dwn;
config: DwnServerConfig;
#httpServerShutdownHandler: HttpServerShutdownHandler;
Expand All @@ -40,9 +48,17 @@ export class DwnServer {
prefix.apply(log);
}

/**
* Starts the DWN server.
*/
async start(): Promise<void> {
if (this.serverState === DwnServerState.Started) {
return;
}

await this.#setupServer();
setProcessHandlers(this);
this.processHandlers = setProcessHandlers(this);
this.serverState = DwnServerState.Started;
}

/**
Expand Down Expand Up @@ -76,9 +92,8 @@ export class DwnServer {

this.#httpApi = await HttpApi.create(this.config, this.dwn, registrationManager);

await this.#httpApi.start(this.config.port, () => {
log.info(`HttpServer listening on port ${this.config.port}`);
});
await this.#httpApi.start(this.config.port);
log.info(`HttpServer listening on port ${this.config.port}`);

this.#httpServerShutdownHandler = new HttpServerShutdownHandler(
this.#httpApi.server,
Expand All @@ -91,8 +106,36 @@ export class DwnServer {
}
}

stop(callback: () => void): void {
this.#httpServerShutdownHandler.stop(callback);
/**
* Stops the DWN server.
*/
async stop(): Promise<void> {
if (this.serverState === DwnServerState.Stopped) {
return;
}

// F YEAH!
if (this.dwn['didResolver']['cache']['cache']) {
await this.dwn['didResolver']['cache']['cache']['close']();
}

await this.dwn.close();
await this.#httpApi.stop();

// close WebSocket server if it was initialized
if (this.#wsApi !== undefined) {
await this.#wsApi.close();
}

await new Promise<void>((resolve) => {
this.#httpServerShutdownHandler.stop(() => {
resolve();
});
});

unsetProcessHandlers(this.processHandlers);

this.serverState = DwnServerState.Stopped;
}

get httpServer(): Server {
Expand Down
36 changes: 29 additions & 7 deletions src/http-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,10 +340,6 @@ export class HttpApi {
this.#setupWeb5ConnectServerRoutes();
}

#listen(port: number, callback?: () => void): void {
this.#server.listen(port, callback);
}

#setupRegistrationRoutes(): void {
if (this.#config.registrationProofOfWorkEnabled) {
this.#api.get('/registration/proof-of-work', async (_req: Request, res: Response) => {
Expand Down Expand Up @@ -458,8 +454,34 @@ export class HttpApi {
});
}

async start(port: number, callback?: () => void): Promise<http.Server> {
this.#listen(port, callback);
return this.#server;
/**
* Starts the HTTP API endpoint on the given port.
* @returns The HTTP server instance.
*/
async start(port: number): Promise<void> {
// promisify http.Server.listen() and await on it
await new Promise<void>((resolve) => {
this.#server.listen(port, () => {
resolve();
});
});
}

/**
* Stops the HTTP API endpoint.
*/
async stop(): Promise<void> {
// promisify http.Server.close() and await on it
await new Promise<void>((resolve, reject) => {
this.#server.close((err?: Error) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});

this.server.closeAllConnections();
}
}
68 changes: 49 additions & 19 deletions src/process-handlers.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,66 @@
import type { DwnServer } from './dwn-server.js';

export const gracefulShutdown = (dwnServer: DwnServer): void => {
dwnServer.stop(() => {
console.log('http server stopped.. exiting');
process.exit(0);
});
export const gracefulShutdown = async (dwnServer: DwnServer): Promise<void> => {
await dwnServer.stop();
console.log('http server stopped.. exiting');
process.exit(0);
};

export const setProcessHandlers = (dwnServer: DwnServer): void => {
process.on('unhandledRejection', (reason, promise) => {
export type ProcessHandlers = {
unhandledRejectionHandler: (reason: any, promise: Promise<any>) => void,
uncaughtExceptionHandler: (err: Error) => void,
sigintHandler: () => Promise<void>,
sigtermHandler: () => Promise<void>
};


export const setProcessHandlers = (dwnServer: DwnServer): ProcessHandlers => {
const unhandledRejectionHandler = (reason: any, promise: Promise<any>): void => {
console.error(
`Unhandled promise rejection. Reason: ${reason}. Promise: ${JSON.stringify(
promise,
)}`,
);
});
};

process.on('uncaughtException', (err) => {
const uncaughtExceptionHandler = (err: Error): void => {
console.error('Uncaught exception:', err.stack || err);
});
};

// triggered by ctrl+c with no traps in between
process.on('SIGINT', async () => {
const sigintHandler = async (): Promise<void> => {
console.log('exit signal received [SIGINT]. starting graceful shutdown');
await gracefulShutdown(dwnServer);
};

gracefulShutdown(dwnServer);
});

// triggered by docker, tiny etc.
process.on('SIGTERM', async () => {
const sigtermHandler = async (): Promise<void> => {
console.log('exit signal received [SIGTERM]. starting graceful shutdown');
await gracefulShutdown(dwnServer);
};

gracefulShutdown(dwnServer);
});
process.on('unhandledRejection', unhandledRejectionHandler);
process.on('uncaughtException', uncaughtExceptionHandler);
process.on('SIGINT', sigintHandler);
process.on('SIGTERM', sigtermHandler);

// Store handlers to be able to remove them later
return {
unhandledRejectionHandler,
uncaughtExceptionHandler,
sigintHandler,
sigtermHandler
};
};

export const unsetProcessHandlers = (handlers: ProcessHandlers): void => {
const {
unhandledRejectionHandler,
uncaughtExceptionHandler,
sigintHandler,
sigtermHandler
} = handlers;

process.off('unhandledRejection', unhandledRejectionHandler);
process.off('uncaughtException', uncaughtExceptionHandler);
process.off('SIGINT', sigintHandler);
process.off('SIGTERM', sigtermHandler);
};
4 changes: 4 additions & 0 deletions src/registration/registration-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export class RegistrationStore {

return result[0];
}

public async close(): Promise<void> {
this.db.destroy();
}
}

interface RegisteredTenants {
Expand Down
3 changes: 1 addition & 2 deletions src/ws-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ export class WsApi {
this.#wsServer.on('close', () => this.#connectionManager.closeAll());
}

start(): WebSocketServer {
start(): void {
this.#setupWebSocket();
return this.#wsServer;
}

async close(): Promise<void> {
Expand Down
Loading

0 comments on commit 9ea81e4

Please sign in to comment.