CommunitySolidServer/src/server/HandlerServerConfigurator.ts
Joachim Van Herwegen 4223dcf8a4 feat: Split up server creation and request handling
This allows us to decouple the WebSocket listening from the HTTP configs,
making these features completely orthogonal.
2022-11-09 09:10:00 +01:00

69 lines
2.5 KiB
TypeScript

import type { Server, IncomingMessage, ServerResponse } from 'http';
import { getLoggerFor } from '../logging/LogUtil';
import { isError } from '../util/errors/ErrorUtil';
import { guardStream } from '../util/GuardedStream';
import type { HttpHandler } from './HttpHandler';
import { ServerConfigurator } from './ServerConfigurator';
/**
* A {@link ServerConfigurator} that attaches an {@link HttpHandler} to the `request` event of a {@link Server}.
* All incoming requests will be sent to the provided handler.
* Failsafes are added to make sure a valid response is sent in case something goes wrong.
*
* The `showStackTrace` parameter can be used to add stack traces to error outputs.
*/
export class HandlerServerConfigurator extends ServerConfigurator {
protected readonly logger = getLoggerFor(this);
protected readonly errorLogger = (error: Error): void => {
this.logger.error(`Request error: ${error.message}`);
};
/** The main HttpHandler */
private readonly handler: HttpHandler;
private readonly showStackTrace: boolean;
public constructor(handler: HttpHandler, showStackTrace = false) {
super();
this.handler = handler;
this.showStackTrace = showStackTrace;
}
public async handle(server: Server): Promise<void> {
server.on('request',
async(request: IncomingMessage, response: ServerResponse): Promise<void> => {
try {
this.logger.info(`Received ${request.method} request for ${request.url}`);
const guardedRequest = guardStream(request);
guardedRequest.on('error', this.errorLogger);
await this.handler.handleSafe({ request: guardedRequest, response });
} catch (error: unknown) {
const errMsg = this.createErrorMessage(error);
this.logger.error(errMsg);
if (response.headersSent) {
response.end();
} else {
response.setHeader('Content-Type', 'text/plain; charset=utf-8');
response.writeHead(500).end(errMsg);
}
} finally {
if (!response.headersSent) {
response.writeHead(404).end();
}
}
});
}
/**
* Creates a readable error message based on the error and the `showStackTrace` parameter.
*/
private createErrorMessage(error: unknown): string {
if (!isError(error)) {
return `Unknown error: ${error}.\n`;
}
if (this.showStackTrace && error.stack) {
return `${error.stack}\n`;
}
return `${error.name}: ${error.message}\n`;
}
}