UI Server: factor out responses handling logic in abstract class
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 7 Sep 2022 22:16:21 +0000 (00:16 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Wed, 7 Sep 2022 22:16:21 +0000 (00:16 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/ui-server/AbstractUIServer.ts
src/charging-station/ui-server/UIHttpServer.ts
src/charging-station/ui-server/UIWebSocketServer.ts

index f6e2010f8edf923fad117ea3ec8fb5c088f46944..75f6e8a89c0e2dcd90438e4ba56070dd1165ba47 100644 (file)
@@ -1,5 +1,4 @@
-import { type IncomingMessage, Server } from 'http';
-import type { Socket } from 'net';
+import { type IncomingMessage, Server, type ServerResponse } from 'http';
 
 import type { WebSocket } from 'ws';
 
@@ -20,13 +19,13 @@ import UIServiceFactory from './ui-services/UIServiceFactory';
 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>();
   }
 
index d6ce53103e31c38bed3910fef957acb56bd282af..d1615dbbcb893a3bae3454ad5aceac7a2cfb6a8d 100644 (file)
@@ -20,23 +20,12 @@ import { UIServiceUtils } from './ui-services/UIServiceUtils';
 
 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);
@@ -51,7 +40,7 @@ export default class UIHttpServer extends AbstractUIServer {
   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',
       });
@@ -86,7 +75,7 @@ export default class UIHttpServer extends AbstractUIServer {
       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}'`);
index bb80c60a18c8f642b6483bcc5f961096348c9dcd..ce773db37e8387e23ed8370c945fc7b015aa628c 100644 (file)
@@ -38,7 +38,6 @@ export default class UIWebSocketServer extends AbstractUIServer {
         );
         ws.close(WebSocketCloseEventStatusCode.CLOSE_PROTOCOL_ERROR);
       }
-      this.sockets.add(ws);
       this.registerProtocolVersionUIService(version);
       ws.on('message', (rawData) => {
         const request = this.validateRawDataRequest(rawData);
@@ -46,10 +45,11 @@ export default class UIWebSocketServer extends AbstractUIServer {
           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 */
           });
@@ -58,7 +58,6 @@ export default class UIWebSocketServer extends AbstractUIServer {
         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,
@@ -94,8 +93,21 @@ export default class UIWebSocketServer extends AbstractUIServer {
   }
 
   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 {