1 import { type IncomingMessage
, Server
, type ServerResponse
} from
'node:http';
2 import { type Http2Server
, createServer
} from
'node:http2';
4 import type { WebSocket
} from
'ws';
6 import type { AbstractUIService
} from
'./ui-services/AbstractUIService';
7 import { UIServiceFactory
} from
'./ui-services/UIServiceFactory';
8 import { BaseError
} from
'../../exception';
10 ApplicationProtocolVersion
,
12 type ChargingStationData
,
15 type ProtocolResponse
,
19 type UIServerConfiguration
,
22 export abstract class AbstractUIServer
{
23 public readonly chargingStations
: Map
<string, ChargingStationData
>;
24 protected readonly httpServer
: Server
| Http2Server
;
25 protected readonly responseHandlers
: Map
<string, ServerResponse
| WebSocket
>;
26 protected readonly uiServices
: Map
<ProtocolVersion
, AbstractUIService
>;
28 public constructor(protected readonly uiServerConfiguration
: UIServerConfiguration
) {
29 this.chargingStations
= new Map
<string, ChargingStationData
>();
30 switch (this.uiServerConfiguration
.version
) {
31 case ApplicationProtocolVersion
.VERSION_11
:
32 this.httpServer
= new Server();
34 case ApplicationProtocolVersion
.VERSION_20
:
35 this.httpServer
= createServer();
39 `Unsupported application protocol version ${this.uiServerConfiguration.version}`,
42 this.responseHandlers
= new Map
<string, ServerResponse
| WebSocket
>();
43 this.uiServices
= new Map
<ProtocolVersion
, AbstractUIService
>();
46 public buildProtocolRequest(
48 procedureName
: ProcedureName
,
49 requestPayload
: RequestPayload
,
51 return [id
, procedureName
, requestPayload
];
54 public buildProtocolResponse(id
: string, responsePayload
: ResponsePayload
): ProtocolResponse
{
55 return [id
, responsePayload
];
59 this.stopHttpServer();
60 this.chargingStations
.clear();
63 public async sendInternalRequest(request
: ProtocolRequest
): Promise
<ProtocolResponse
> {
64 const protocolVersion
= ProtocolVersion
['0.0.1'];
65 this.registerProtocolVersionUIService(protocolVersion
);
66 return this.uiServices
68 ?.requestHandler(request
) as Promise
<ProtocolResponse
>;
71 public hasResponseHandler(id
: string): boolean {
72 return this.responseHandlers
.has(id
);
75 protected startHttpServer(): void {
76 if (this.httpServer
.listening
=== false) {
77 this.httpServer
.listen(this.uiServerConfiguration
.options
);
81 protected registerProtocolVersionUIService(version
: ProtocolVersion
): void {
82 if (this.uiServices
.has(version
) === false) {
83 this.uiServices
.set(version
, UIServiceFactory
.getUIServiceImplementation(version
, this));
87 protected authenticate(req
: IncomingMessage
, next
: (err
?: Error) => void): void {
88 if (this.isBasicAuthEnabled() === true) {
89 if (this.isValidBasicAuth(req
) === false) {
90 next(new BaseError('Unauthorized'));
97 private stopHttpServer(): void {
98 if (this.httpServer
.listening
=== true) {
99 this.httpServer
.close();
103 private isBasicAuthEnabled(): boolean {
105 this.uiServerConfiguration
.authentication
?.enabled
=== true &&
106 this.uiServerConfiguration
.authentication
?.type === AuthenticationType
.BASIC_AUTH
110 private isValidBasicAuth(req
: IncomingMessage
): boolean {
111 const authorizationHeader
= req
.headers
.authorization
?? '';
112 const authorizationToken
= authorizationHeader
.split(/\s
+/).pop() ?? '';
113 const authentication
= Buffer
.from(authorizationToken
, 'base64').toString();
114 const authenticationParts
= authentication
.split(/:/);
115 const username
= authenticationParts
.shift();
116 const password
= authenticationParts
.join(':');
118 this.uiServerConfiguration
.authentication
?.username
=== username
&&
119 this.uiServerConfiguration
.authentication
?.password
=== password
123 public abstract start(): void;
124 public abstract sendRequest(request
: ProtocolRequest
): void;
125 public abstract sendResponse(response
: ProtocolResponse
): void;
126 public abstract logPrefix(
129 prefixSuffix
?: string,