Merge pull request #836 from SAP/dependabot/npm_and_yarn/tsx-4.0.0
[e-mobility-charging-stations-simulator.git] / src / charging-station / ui-server / AbstractUIServer.ts
CommitLineData
01f4001e 1import { type IncomingMessage, Server, type ServerResponse } from 'node:http';
a6080904 2import { type Http2Server, createServer } from 'node:http2';
daa6505e
JB
3
4import type { WebSocket } from 'ws';
fe94fce0 5
4c3c0d59
JB
6import type { AbstractUIService } from './ui-services/AbstractUIService';
7import { UIServiceFactory } from './ui-services/UIServiceFactory';
7b5dbe91 8import { BaseError } from '../../exception';
eb3abc4f 9import {
a6080904 10 ApplicationProtocolVersion,
eb3abc4f 11 AuthenticationType,
268a74bb 12 type ChargingStationData,
e0b0ee21
JB
13 type ProcedureName,
14 type ProtocolRequest,
15 type ProtocolResponse,
f130b8e6 16 ProtocolVersion,
e0b0ee21
JB
17 type RequestPayload,
18 type ResponsePayload,
268a74bb
JB
19 type UIServerConfiguration,
20} from '../../types';
8114d10e 21
fe94fce0 22export abstract class AbstractUIServer {
32de5a57 23 public readonly chargingStations: Map<string, ChargingStationData>;
a6080904 24 protected readonly httpServer: Server | Http2Server;
976d11ec 25 protected readonly responseHandlers: Map<string, ServerResponse | WebSocket>;
5e3cb728 26 protected readonly uiServices: Map<ProtocolVersion, AbstractUIService>;
fe94fce0 27
eb3abc4f 28 public constructor(protected readonly uiServerConfiguration: UIServerConfiguration) {
32de5a57 29 this.chargingStations = new Map<string, ChargingStationData>();
a6080904
JB
30 switch (this.uiServerConfiguration.version) {
31 case ApplicationProtocolVersion.VERSION_11:
32 this.httpServer = new Server();
33 break;
34 case ApplicationProtocolVersion.VERSION_20:
35 this.httpServer = createServer();
36 break;
37 default:
38 throw new BaseError(
39 `Unsupported application protocol version ${this.uiServerConfiguration.version}`,
40 );
41 }
94dc3080 42 this.responseHandlers = new Map<string, ServerResponse | WebSocket>();
fe94fce0
JB
43 this.uiServices = new Map<ProtocolVersion, AbstractUIService>();
44 }
45
852a4c5f
JB
46 public buildProtocolRequest(
47 id: string,
48 procedureName: ProcedureName,
5edd8ba0 49 requestPayload: RequestPayload,
5e3cb728
JB
50 ): ProtocolRequest {
51 return [id, procedureName, requestPayload];
852a4c5f
JB
52 }
53
5e3cb728
JB
54 public buildProtocolResponse(id: string, responsePayload: ResponsePayload): ProtocolResponse {
55 return [id, responsePayload];
852a4c5f
JB
56 }
57
daa6505e
JB
58 public stop(): void {
59 this.chargingStations.clear();
60 }
61
7c1395ab 62 public async sendInternalRequest(request: ProtocolRequest): Promise<ProtocolResponse> {
f130b8e6
JB
63 const protocolVersion = ProtocolVersion['0.0.1'];
64 this.registerProtocolVersionUIService(protocolVersion);
e1d9a0f4
JB
65 return this.uiServices
66 .get(protocolVersion)
67 ?.requestHandler(request) as Promise<ProtocolResponse>;
6bd808fd
JB
68 }
69
e64c6fa9
JB
70 public hasResponseHandler(id: string): boolean {
71 return this.responseHandlers.has(id);
1ca4a038
JB
72 }
73
a307349b
JB
74 protected startHttpServer(): void {
75 if (this.httpServer.listening === false) {
76 this.httpServer.listen(this.uiServerConfiguration.options);
77 }
78 }
79
143498c8
JB
80 protected registerProtocolVersionUIService(version: ProtocolVersion): void {
81 if (this.uiServices.has(version) === false) {
82 this.uiServices.set(version, UIServiceFactory.getUIServiceImplementation(version, this));
83 }
84 }
85
976d11ec
JB
86 protected authenticate(req: IncomingMessage, next: (err?: Error) => void): void {
87 if (this.isBasicAuthEnabled() === true) {
88 if (this.isValidBasicAuth(req) === false) {
7b5dbe91 89 next(new BaseError('Unauthorized'));
976d11ec
JB
90 }
91 next();
92 }
93 next();
94 }
95
96 private isBasicAuthEnabled(): boolean {
eb3abc4f
JB
97 return (
98 this.uiServerConfiguration.authentication?.enabled === true &&
99 this.uiServerConfiguration.authentication?.type === AuthenticationType.BASIC_AUTH
100 );
101 }
102
976d11ec 103 private isValidBasicAuth(req: IncomingMessage): boolean {
eb3abc4f
JB
104 const authorizationHeader = req.headers.authorization ?? '';
105 const authorizationToken = authorizationHeader.split(/\s+/).pop() ?? '';
106 const authentication = Buffer.from(authorizationToken, 'base64').toString();
107 const authenticationParts = authentication.split(/:/);
108 const username = authenticationParts.shift();
109 const password = authenticationParts.join(':');
110 return (
111 this.uiServerConfiguration.authentication?.username === username &&
112 this.uiServerConfiguration.authentication?.password === password
113 );
114 }
115
fe94fce0 116 public abstract start(): void;
5e3cb728
JB
117 public abstract sendRequest(request: ProtocolRequest): void;
118 public abstract sendResponse(response: ProtocolResponse): void;
119 public abstract logPrefix(
120 moduleName?: string,
121 methodName?: string,
5edd8ba0 122 prefixSuffix?: string,
5e3cb728 123 ): string;
fe94fce0 124}