X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2Fui-server%2FAbstractUIServer.ts;h=133389d60b3cf18e01ad04922332121bbf0d37da;hb=b3d7d65476a4ab586b3ccd188f0bfbe8452aba0e;hp=9a19d39d9a6d29550c614b937f14fd18bcb514e3;hpb=5e3cb7281de2b6fa8b61a453f964c2f213fefa80;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ui-server/AbstractUIServer.ts b/src/charging-station/ui-server/AbstractUIServer.ts index 9a19d39d..133389d6 100644 --- a/src/charging-station/ui-server/AbstractUIServer.ts +++ b/src/charging-station/ui-server/AbstractUIServer.ts @@ -1,32 +1,52 @@ -import type { Server as HttpServer } from 'http'; +import { type IncomingMessage, Server, type ServerResponse } from 'node:http'; +import { type Http2Server, createServer } from 'node:http2'; -import type WebSocket from 'ws'; +import type { WebSocket } from 'ws'; -import type { ChargingStationData } from '../../types/ChargingStationWorker'; -import type { - ProcedureName, - ProtocolRequest, - ProtocolResponse, +import type { AbstractUIService } from './ui-services/AbstractUIService'; +import { UIServiceFactory } from './ui-services/UIServiceFactory'; +import { BaseError } from '../../exception'; +import { + ApplicationProtocolVersion, + AuthenticationType, + type ChargingStationData, + type ProcedureName, + type ProtocolRequest, + type ProtocolResponse, ProtocolVersion, - RequestPayload, - ResponsePayload, -} from '../../types/UIProtocol'; -import type AbstractUIService from './ui-services/AbstractUIService'; + type RequestPayload, + type ResponsePayload, + type UIServerConfiguration, +} from '../../types'; export abstract class AbstractUIServer { public readonly chargingStations: Map; - protected server: WebSocket.Server | HttpServer; + protected readonly httpServer: Server | Http2Server; + protected readonly responseHandlers: Map; protected readonly uiServices: Map; - public constructor() { + public constructor(protected readonly uiServerConfiguration: UIServerConfiguration) { this.chargingStations = new Map(); + switch (this.uiServerConfiguration.version) { + case ApplicationProtocolVersion.VERSION_11: + this.httpServer = new Server(); + break; + case ApplicationProtocolVersion.VERSION_20: + this.httpServer = createServer(); + break; + default: + throw new BaseError( + `Unsupported application protocol version ${this.uiServerConfiguration.version}`, + ); + } + this.responseHandlers = new Map(); this.uiServices = new Map(); } public buildProtocolRequest( id: string, procedureName: ProcedureName, - requestPayload: RequestPayload + requestPayload: RequestPayload, ): ProtocolRequest { return [id, procedureName, requestPayload]; } @@ -35,13 +55,70 @@ export abstract class AbstractUIServer { return [id, responsePayload]; } + public stop(): void { + this.chargingStations.clear(); + } + + public async sendInternalRequest(request: ProtocolRequest): Promise { + const protocolVersion = ProtocolVersion['0.0.1']; + this.registerProtocolVersionUIService(protocolVersion); + return this.uiServices + .get(protocolVersion) + ?.requestHandler(request) as Promise; + } + + public hasResponseHandler(id: string): boolean { + return this.responseHandlers.has(id); + } + + protected startHttpServer(): void { + if (this.httpServer.listening === false) { + this.httpServer.listen(this.uiServerConfiguration.options); + } + } + + protected registerProtocolVersionUIService(version: ProtocolVersion): void { + if (this.uiServices.has(version) === false) { + this.uiServices.set(version, UIServiceFactory.getUIServiceImplementation(version, this)); + } + } + + protected authenticate(req: IncomingMessage, next: (err?: Error) => void): void { + if (this.isBasicAuthEnabled() === true) { + if (this.isValidBasicAuth(req) === false) { + next(new BaseError('Unauthorized')); + } + next(); + } + next(); + } + + private isBasicAuthEnabled(): boolean { + return ( + this.uiServerConfiguration.authentication?.enabled === true && + this.uiServerConfiguration.authentication?.type === AuthenticationType.BASIC_AUTH + ); + } + + private isValidBasicAuth(req: IncomingMessage): boolean { + const authorizationHeader = req.headers.authorization ?? ''; + const authorizationToken = authorizationHeader.split(/\s+/).pop() ?? ''; + const authentication = Buffer.from(authorizationToken, 'base64').toString(); + const authenticationParts = authentication.split(/:/); + const username = authenticationParts.shift(); + const password = authenticationParts.join(':'); + return ( + this.uiServerConfiguration.authentication?.username === username && + this.uiServerConfiguration.authentication?.password === password + ); + } + public abstract start(): void; - public abstract stop(): void; public abstract sendRequest(request: ProtocolRequest): void; public abstract sendResponse(response: ProtocolResponse): void; public abstract logPrefix( moduleName?: string, methodName?: string, - prefixSuffix?: string + prefixSuffix?: string, ): string; }