289eda84781ad0d7686e88846a95e028cf45921f
1 import { IncomingMessage
, RequestListener
, Server
, ServerResponse
} from
'http';
3 import { StatusCodes
} from
'http-status-codes';
5 import BaseError from
'../../exception/BaseError';
6 import type { UIServerConfiguration
} from
'../../types/ConfigurationData';
15 } from
'../../types/UIProtocol';
16 import logger from
'../../utils/Logger';
17 import Utils from
'../../utils/Utils';
18 import { AbstractUIServer
} from
'./AbstractUIServer';
19 import UIServiceFactory from
'./ui-services/UIServiceFactory';
20 import { UIServiceUtils
} from
'./ui-services/UIServiceUtils';
22 const moduleName
= 'UIHttpServer';
24 type responseHandler
= { procedureName
: ProcedureName
; res
: ServerResponse
};
26 export default class UIHttpServer
extends AbstractUIServer
{
27 private readonly responseHandlers
: Map
<string, responseHandler
>;
29 public constructor(protected readonly uiServerConfiguration
: UIServerConfiguration
) {
30 super(uiServerConfiguration
);
31 this.httpServer
= new Server(this.requestListener
.bind(this) as RequestListener
);
32 this.responseHandlers
= new Map
<string, responseHandler
>();
35 public start(): void {
36 if (this.httpServer
.listening
=== false) {
37 this.httpServer
.listen(this.uiServerConfiguration
.options
);
42 this.chargingStations
.clear();
45 // eslint-disable-next-line @typescript-eslint/no-unused-vars
46 public sendRequest(request
: ProtocolRequest
): void {
47 // This is intentionally left blank
50 public sendResponse(response
: ProtocolResponse
): void {
51 const [uuid
, payload
] = response
;
52 const statusCode
= this.responseStatusToStatusCode(payload
.status);
53 if (this.responseHandlers
.has(uuid
) === true) {
54 const { res
} = this.responseHandlers
.get(uuid
);
55 res
.writeHead(statusCode
, { 'Content-Type': 'application/json' });
56 res
.write(JSON
.stringify(payload
));
58 this.responseHandlers
.delete(uuid
);
61 `${this.logPrefix(moduleName, 'sendResponse')} Response for unknown request id: ${uuid}`
66 public logPrefix(modName
?: string, methodName
?: string, prefixSuffix
?: string): string {
67 const logMsgPrefix
= prefixSuffix
? `UI HTTP Server ${prefixSuffix}` : 'UI HTTP Server';
69 modName
&& methodName
? ` ${logMsgPrefix} | ${modName}.${methodName}:` : ` ${logMsgPrefix} |`;
70 return Utils
.logPrefix(logMsg
);
73 private requestListener(req
: IncomingMessage
, res
: ServerResponse
): void {
74 if (this.authenticate(req
) === false) {
75 res
.setHeader('Content-Type', 'text/plain');
76 res
.setHeader('WWW-Authenticate', 'Basic realm=users');
77 res
.writeHead(StatusCodes
.UNAUTHORIZED
);
78 res
.end(`${StatusCodes.UNAUTHORIZED} Unauthorized`);
81 // Expected request URL pathname: /ui/:version/:procedureName
82 const [protocol
, version
, procedureName
] = req
.url
?.split('/').slice(1) as [
87 const uuid
= Utils
.generateUUID();
88 this.responseHandlers
.set(uuid
, { procedureName
, res
});
90 if (UIServiceUtils
.isProtocolAndVersionSupported(protocol
, version
) === false) {
91 throw new BaseError(`Unsupported UI protocol version: '/${protocol}/${version}'`);
93 req
.on('error', (error
) => {
95 `${this.logPrefix(moduleName, 'requestListener.req.onerror')} Error on HTTP request:`,
99 if (this.uiServices
.has(version
) === false) {
100 this.uiServices
.set(version
, UIServiceFactory
.getUIServiceImplementation(version
, this));
102 if (req
.method
=== 'POST') {
103 const bodyBuffer
= [];
105 .on('data', (chunk
) => {
106 bodyBuffer
.push(chunk
);
109 const body
= JSON
.parse(Buffer
.concat(bodyBuffer
).toString()) as RequestPayload
;
112 .requestHandler(this.buildProtocolRequest(uuid
, procedureName
, body
?? {}))
115 this.buildProtocolResponse(uuid
, { status: ResponseStatus
.FAILURE
})
120 throw new BaseError(`Unsupported HTTP method: '${req.method}'`);
124 `${this.logPrefix(moduleName, 'requestListener')} Handle HTTP request error:`,
127 this.sendResponse(this.buildProtocolResponse(uuid
, { status: ResponseStatus
.FAILURE
}));
131 private authenticate(req
: IncomingMessage
): boolean {
132 if (this.isBasicAuthEnabled() === true) {
133 if (this.isValidBasicAuth(req
) === true) {
141 private responseStatusToStatusCode(status: ResponseStatus
): StatusCodes
{
143 case ResponseStatus
.SUCCESS
:
144 return StatusCodes
.OK
;
145 case ResponseStatus
.FAILURE
:
146 return StatusCodes
.BAD_REQUEST
;
148 return StatusCodes
.INTERNAL_SERVER_ERROR
;