-import { type IncomingMessage, Server } from 'http';
-import type { Socket } from 'net';
+import { type IncomingMessage, Server, type ServerResponse } from 'http';
import type { WebSocket } from 'ws';
export abstract class AbstractUIServer {
public readonly chargingStations: Map<string, ChargingStationData>;
protected httpServer: Server;
- protected sockets: Set<Socket | WebSocket>;
+ protected responseHandlers: Map<string, ServerResponse | WebSocket>;
protected readonly uiServices: Map<ProtocolVersion, AbstractUIService>;
public constructor(protected readonly uiServerConfiguration: UIServerConfiguration) {
this.chargingStations = new Map<string, ChargingStationData>();
this.httpServer = new Server();
- this.sockets = new Set<Socket>();
+ this.responseHandlers = new Map<string, ServerResponse | WebSocket>();
this.uiServices = new Map<ProtocolVersion, AbstractUIService>();
}
const moduleName = 'UIHttpServer';
-type responseHandler = { procedureName: ProcedureName; res: ServerResponse };
-
export default class UIHttpServer extends AbstractUIServer {
- private readonly responseHandlers: Map<string, responseHandler>;
-
public constructor(protected readonly uiServerConfiguration: UIServerConfiguration) {
super(uiServerConfiguration);
- this.responseHandlers = new Map<string, responseHandler>();
}
public start(): void {
- this.httpServer.on('connection', (socket) => {
- this.sockets.add(socket);
- socket.on('close', () => {
- this.sockets.delete(socket);
- });
- });
this.httpServer.on('request', this.requestListener.bind(this) as RequestListener);
if (this.httpServer.listening === false) {
this.httpServer.listen(this.uiServerConfiguration.options);
public sendResponse(response: ProtocolResponse): void {
const [uuid, payload] = response;
if (this.responseHandlers.has(uuid) === true) {
- const { res } = this.responseHandlers.get(uuid);
+ const res = this.responseHandlers.get(uuid) as ServerResponse;
res.writeHead(this.responseStatusToStatusCode(payload.status), {
'Content-Type': 'application/json',
});
ProcedureName
];
const uuid = Utils.generateUUID();
- this.responseHandlers.set(uuid, { procedureName, res });
+ this.responseHandlers.set(uuid, res);
try {
if (UIServiceUtils.isProtocolAndVersionSupported(protocol, version) === false) {
throw new BaseError(`Unsupported UI protocol version: '/${protocol}/${version}'`);
);
ws.close(WebSocketCloseEventStatusCode.CLOSE_PROTOCOL_ERROR);
}
- this.sockets.add(ws);
this.registerProtocolVersionUIService(version);
ws.on('message', (rawData) => {
const request = this.validateRawDataRequest(rawData);
ws.close(WebSocketCloseEventStatusCode.CLOSE_INVALID_PAYLOAD);
return;
}
- const [messageId, procedureName, payload] = request as ProtocolRequest;
+ const [requestId] = request as ProtocolRequest;
+ this.responseHandlers.set(requestId, ws);
this.uiServices
.get(version)
- .requestHandler(this.buildProtocolRequest(messageId, procedureName, payload))
+ .requestHandler(request)
.catch(() => {
/* Error caught by AbstractUIService */
});
logger.error(`${this.logPrefix(moduleName, 'start.ws.onerror')} WebSocket error:`, error);
});
ws.on('close', (code, reason) => {
- this.sockets.delete(ws);
logger.debug(
`${this.logPrefix(
moduleName,
}
public sendResponse(response: ProtocolResponse): void {
- // TODO: send response only to the client that sent the request
- this.broadcastToClients(JSON.stringify(response));
+ const responseId = response[0];
+ if (this.responseHandlers.has(responseId)) {
+ const ws = this.responseHandlers.get(responseId) as WebSocket;
+ if (ws?.readyState === WebSocket.OPEN) {
+ ws.send(JSON.stringify(response));
+ }
+ this.responseHandlers.delete(responseId);
+ } else {
+ logger.error(
+ `${this.logPrefix(
+ moduleName,
+ 'sendResponse'
+ )} Response for unknown request id: ${responseId}`
+ );
+ }
}
public logPrefix(modName?: string, methodName?: string, prefixSuffix?: string): string {