2747738a65d1aadff3b59d654d51570bb0b8a1c1
1 import { IncomingMessage
, RequestListener
, Server
, ServerResponse
} from
'http';
3 import { StatusCodes
} from
'http-status-codes';
5 import BaseError from
'../../exception/BaseError';
6 import type { ServerOptions
} from
'../../types/ConfigurationData';
15 } from
'../../types/UIProtocol';
16 import Configuration from
'../../utils/Configuration';
17 import logger from
'../../utils/Logger';
18 import Utils from
'../../utils/Utils';
19 import { AbstractUIServer
} from
'./AbstractUIServer';
20 import UIServiceFactory from
'./ui-services/UIServiceFactory';
21 import { UIServiceUtils
} from
'./ui-services/UIServiceUtils';
23 const moduleName
= 'UIHttpServer';
25 type responseHandler
= { procedureName
: ProcedureName
; res
: ServerResponse
};
27 export default class UIHttpServer
extends AbstractUIServer
{
28 private readonly responseHandlers
: Map
<string, responseHandler
>;
30 public constructor(private options
?: ServerOptions
) {
32 this.server
= new Server(this.requestListener
.bind(this) as RequestListener
);
33 this.responseHandlers
= new Map
<string, responseHandler
>();
36 public start(): void {
37 if ((this.server
as Server
).listening
=== false) {
38 (this.server
as Server
).listen(this.options
?? Configuration
.getUIServer().options
);
43 this.chargingStations
.clear();
44 this.responseHandlers
.clear();
47 // eslint-disable-next-line @typescript-eslint/no-unused-vars
48 public sendRequest(request
: ProtocolRequest
): void {
49 // This is intentionally left blank
52 public sendResponse(response
: ProtocolResponse
): void {
53 const [uuid
, payload
] = response
;
54 const statusCode
= this.responseStatusToStatusCode(payload
.status);
55 if (this.responseHandlers
.has(uuid
) === true) {
56 const { res
} = this.responseHandlers
.get(uuid
);
57 res
.writeHead(statusCode
, { 'Content-Type': 'application/json' });
58 res
.write(JSON
.stringify(payload
));
60 this.responseHandlers
.delete(uuid
);
63 `${this.logPrefix(moduleName, 'sendResponse')} Response for unknown request id: ${uuid}`
68 public logPrefix(modName
?: string, methodName
?: string, prefixSuffix
?: string): string {
69 const logMsgPrefix
= prefixSuffix
? `UI HTTP Server ${prefixSuffix}` : 'UI HTTP Server';
71 modName
&& methodName
? ` ${logMsgPrefix} | ${modName}.${methodName}:` : ` ${logMsgPrefix} |`;
72 return Utils
.logPrefix(logMsg
);
75 private requestListener(req
: IncomingMessage
, res
: ServerResponse
): void {
76 // Expected request URL pathname: /ui/:version/:procedureName
77 const [protocol
, version
, procedureName
] = req
.url
?.split('/').slice(1) as [
82 const uuid
= Utils
.generateUUID();
83 this.responseHandlers
.set(uuid
, { procedureName
, res
});
85 if (UIServiceUtils
.isProtocolAndVersionSupported(protocol
, version
) === false) {
86 throw new BaseError(`Unsupported UI protocol version: '/${protocol}/${version}'`);
88 req
.on('error', (error
) => {
90 `${this.logPrefix(moduleName, 'requestListener.req.onerror')} Error on HTTP request:`,
94 if (this.uiServices
.has(version
) === false) {
95 this.uiServices
.set(version
, UIServiceFactory
.getUIServiceImplementation(version
, this));
97 if (req
.method
=== 'POST') {
98 const bodyBuffer
= [];
100 .on('data', (chunk
) => {
101 bodyBuffer
.push(chunk
);
104 const body
= JSON
.parse(Buffer
.concat(bodyBuffer
).toString()) as RequestPayload
;
107 .requestHandler(this.buildProtocolRequest(uuid
, procedureName
, body
?? {}))
110 this.buildProtocolResponse(uuid
, { status: ResponseStatus
.FAILURE
})
115 throw new BaseError(`Unsupported HTTP method: '${req.method}'`);
119 `${this.logPrefix(moduleName, 'requestListener')} Handle HTTP request error:`,
122 this.sendResponse(this.buildProtocolResponse(uuid
, { status: ResponseStatus
.FAILURE
}));
126 private responseStatusToStatusCode(status: ResponseStatus
): StatusCodes
{
128 case ResponseStatus
.SUCCESS
:
129 return StatusCodes
.OK
;
130 case ResponseStatus
.FAILURE
:
131 return StatusCodes
.BAD_REQUEST
;
133 return StatusCodes
.INTERNAL_SERVER_ERROR
;