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.js'
7 import { UIServiceFactory
} from
'./ui-services/UIServiceFactory.js'
8 import { BaseError
} from
'../../exception/index.js'
10 ApplicationProtocolVersion
,
12 type ChargingStationData
,
15 type ProtocolResponse
,
19 type UIServerConfiguration
20 } from
'../../types/index.js'
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
]
58 public stop (): void {
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 await (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
) {
77 this.httpServer
.listen(this.uiServerConfiguration
.options
)
81 protected registerProtocolVersionUIService (version
: ProtocolVersion
): void {
82 if (!this.uiServices
.has(version
)) {
83 this.uiServices
.set(version
, UIServiceFactory
.getUIServiceImplementation(version
, this))
87 protected authenticate (req
: IncomingMessage
, next
: (err
?: Error) => void): void {
88 if (this.isBasicAuthEnabled()) {
89 if (!this.isValidBasicAuth(req
)) {
90 next(new BaseError('Unauthorized'))
97 private stopHttpServer (): void {
98 if (this.httpServer
.listening
) {
99 this.httpServer
.close()
103 private isBasicAuthEnabled (): boolean {
105 this.uiServerConfiguration
.authentication
?.enabled
=== true &&
106 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
107 this.uiServerConfiguration
.authentication
.type === AuthenticationType
.BASIC_AUTH
111 private isValidBasicAuth (req
: IncomingMessage
): boolean {
112 const authorizationHeader
= req
.headers
.authorization
?? ''
113 const authorizationToken
= authorizationHeader
.split(/\s
+/).pop() ?? ''
114 const authentication
= Buffer
.from(authorizationToken
, 'base64').toString()
115 const authenticationParts
= authentication
.split(/:/)
116 const username
= authenticationParts
.shift()
117 const password
= authenticationParts
.join(':')
119 this.uiServerConfiguration
.authentication
?.username
=== username
&&
120 this.uiServerConfiguration
.authentication
?.password
=== password
124 public abstract start (): void
125 public abstract sendRequest (request
: ProtocolRequest
): void
126 public abstract sendResponse (response
: ProtocolResponse
): void
127 public abstract logPrefix (moduleName
?: string, methodName
?: string, prefixSuffix
?: string): string