From 75adc3d83c35b843b824a8564504de6ff732df24 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Thu, 15 Feb 2024 23:58:56 +0100 Subject: [PATCH] refactor: factor out UI Server helpers into arrow functions MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../ui-server/AbstractUIServer.ts | 20 +--- .../ui-server/UIHttpServer.ts | 4 +- .../ui-server/UIServerFactory.ts | 4 +- .../ui-server/UIServerUtils.ts | 95 ++++++++++--------- .../ui-server/UIWebSocketServer.ts | 12 ++- 5 files changed, 66 insertions(+), 69 deletions(-) diff --git a/src/charging-station/ui-server/AbstractUIServer.ts b/src/charging-station/ui-server/AbstractUIServer.ts index 495729fa..b17dec30 100644 --- a/src/charging-station/ui-server/AbstractUIServer.ts +++ b/src/charging-station/ui-server/AbstractUIServer.ts @@ -5,6 +5,7 @@ import type { WebSocket } from 'ws' import type { AbstractUIService } from './ui-services/AbstractUIService.js' import { UIServiceFactory } from './ui-services/UIServiceFactory.js' +import { getUsernameAndPasswordFromAuthorizationToken } from './UIServerUtils.js' import { BaseError } from '../../exception/index.js' import { ApplicationProtocolVersion, @@ -129,7 +130,7 @@ export abstract class AbstractUIServer { } private isValidBasicAuth (req: IncomingMessage, next: (err?: Error) => void): boolean { - const [username, password] = this.getUsernameAndPasswordFromAuthorizationToken( + const [username, password] = getUsernameAndPasswordFromAuthorizationToken( req.headers.authorization?.split(/\s+/).pop() ?? '', next ) @@ -138,7 +139,7 @@ export abstract class AbstractUIServer { private isValidProtocolBasicAuth (req: IncomingMessage, next: (err?: Error) => void): boolean { const authorizationProtocol = req.headers['sec-websocket-protocol']?.split(/,\s+/).pop() - const [username, password] = this.getUsernameAndPasswordFromAuthorizationToken( + const [username, password] = getUsernameAndPasswordFromAuthorizationToken( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion `${authorizationProtocol}${Array(((4 - (authorizationProtocol!.length % 4)) % 4) + 1).join('=')}` .split('.') @@ -148,21 +149,6 @@ export abstract class AbstractUIServer { return this.isValidUsernameAndPassword(username, password) } - private getUsernameAndPasswordFromAuthorizationToken ( - authorizationToken: string, - next: (err?: Error) => void - ): [string, string] { - if ( - !/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/.test(authorizationToken) - ) { - next(new BaseError('Invalid basic authentication token format')) - } - const authentication = Buffer.from(authorizationToken, 'base64').toString() - const authenticationParts = authentication.split(/:/) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return [authenticationParts.shift()!, authenticationParts.join(':')] - } - private isValidUsernameAndPassword (username: string, password: string): boolean { return ( this.uiServerConfiguration.authentication?.username === username && diff --git a/src/charging-station/ui-server/UIHttpServer.ts b/src/charging-station/ui-server/UIHttpServer.ts index bd10a684..fb967ef4 100644 --- a/src/charging-station/ui-server/UIHttpServer.ts +++ b/src/charging-station/ui-server/UIHttpServer.ts @@ -3,7 +3,7 @@ import type { IncomingMessage, ServerResponse } from 'node:http' import { StatusCodes } from 'http-status-codes' import { AbstractUIServer } from './AbstractUIServer.js' -import { UIServerUtils } from './UIServerUtils.js' +import { isProtocolAndVersionSupported } from './UIServerUtils.js' import { BaseError } from '../../exception/index.js' import { ApplicationProtocolVersion, @@ -109,7 +109,7 @@ export class UIHttpServer extends AbstractUIServer { this.responseHandlers.set(uuid, res) try { const fullProtocol = `${protocol}${version}` - if (!UIServerUtils.isProtocolAndVersionSupported(fullProtocol)) { + if (!isProtocolAndVersionSupported(fullProtocol)) { throw new BaseError(`Unsupported UI protocol version: '${fullProtocol}'`) } this.registerProtocolVersionUIService(version) diff --git a/src/charging-station/ui-server/UIServerFactory.ts b/src/charging-station/ui-server/UIServerFactory.ts index 2a87a582..d6dea71b 100644 --- a/src/charging-station/ui-server/UIServerFactory.ts +++ b/src/charging-station/ui-server/UIServerFactory.ts @@ -2,7 +2,7 @@ import chalk from 'chalk' import type { AbstractUIServer } from './AbstractUIServer.js' import { UIHttpServer } from './UIHttpServer.js' -import { UIServerUtils } from './UIServerUtils.js' +import { isLoopback } from './UIServerUtils.js' import { UIWebSocketServer } from './UIWebSocketServer.js' import { BaseError } from '../../exception/index.js' import { @@ -39,7 +39,7 @@ export class UIServerFactory { if ( uiServerConfiguration.authentication?.enabled !== true && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - !UIServerUtils.isLoopback(uiServerConfiguration.options!.host!) + !isLoopback(uiServerConfiguration.options!.host!) ) { console.warn( chalk.yellow( diff --git a/src/charging-station/ui-server/UIServerUtils.ts b/src/charging-station/ui-server/UIServerUtils.ts index 4d621172..2aa2e92b 100644 --- a/src/charging-station/ui-server/UIServerUtils.ts +++ b/src/charging-station/ui-server/UIServerUtils.ts @@ -1,57 +1,64 @@ import type { IncomingMessage } from 'node:http' +import { BaseError } from '../../exception/index.js' import { Protocol, ProtocolVersion } from '../../types/index.js' import { logPrefix, logger } from '../../utils/index.js' -// eslint-disable-next-line @typescript-eslint/no-extraneous-class -export class UIServerUtils { - private constructor () { - // This is intentional +export const getUsernameAndPasswordFromAuthorizationToken = ( + authorizationToken: string, + next: (err?: Error) => void +): [string, string] => { + if ( + !/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/.test(authorizationToken) + ) { + next(new BaseError('Invalid basic authentication token format')) } + const authentication = Buffer.from(authorizationToken, 'base64').toString() + const authenticationParts = authentication.split(/:/) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return [authenticationParts.shift()!, authenticationParts.join(':')] +} - public static readonly handleProtocols = ( - protocols: Set, - request: IncomingMessage - ): string | false => { - let protocol: Protocol | undefined - let version: ProtocolVersion | undefined - if (protocols.size === 0) { - return false - } - for (const fullProtocol of protocols) { - if (UIServerUtils.isProtocolAndVersionSupported(fullProtocol)) { - return fullProtocol - } - } - logger.error( - `${logPrefix( - ' UI WebSocket Server |' - )} Unsupported protocol: '${protocol}' or protocol version: '${version}'` - ) +export const handleProtocols = ( + protocols: Set, + _request: IncomingMessage +): string | false => { + let protocol: Protocol | undefined + let version: ProtocolVersion | undefined + if (protocols.size === 0) { return false } - - public static readonly isProtocolAndVersionSupported = (protocolStr: string): boolean => { - const [protocol, version] = UIServerUtils.getProtocolAndVersion(protocolStr) - return ( - Object.values(Protocol).includes(protocol) && Object.values(ProtocolVersion).includes(version) - ) + for (const fullProtocol of protocols) { + if (isProtocolAndVersionSupported(fullProtocol)) { + return fullProtocol + } } + logger.error( + `${logPrefix( + ' UI WebSocket Server |' + )} Unsupported protocol: '${protocol}' or protocol version: '${version}'` + ) + return false +} - public static readonly getProtocolAndVersion = ( - protocolStr: string - ): [Protocol, ProtocolVersion] => { - const protocolIndex = protocolStr.indexOf(Protocol.UI) - const protocol = protocolStr.substring( - protocolIndex, - protocolIndex + Protocol.UI.length - ) as Protocol - const version = protocolStr.substring(protocolIndex + Protocol.UI.length) as ProtocolVersion - return [protocol, version] - } +export const isProtocolAndVersionSupported = (protocolStr: string): boolean => { + const [protocol, version] = getProtocolAndVersion(protocolStr) + return ( + Object.values(Protocol).includes(protocol) && Object.values(ProtocolVersion).includes(version) + ) +} - public static isLoopback (address: string): boolean { - // eslint-disable-next-line no-useless-escape - return /^localhost$|^127(?:\.\d+){0,2}\.\d+$|^(?:0*\:)*?:?0*1$/i.test(address) - } +export const getProtocolAndVersion = (protocolStr: string): [Protocol, ProtocolVersion] => { + const protocolIndex = protocolStr.indexOf(Protocol.UI) + const protocol = protocolStr.substring( + protocolIndex, + protocolIndex + Protocol.UI.length + ) as Protocol + const version = protocolStr.substring(protocolIndex + Protocol.UI.length) as ProtocolVersion + return [protocol, version] +} + +export const isLoopback = (address: string): boolean => { + // eslint-disable-next-line no-useless-escape + return /^localhost$|^127(?:\.\d+){0,2}\.\d+$|^(?:0*\:)*?:?0*1$/i.test(address) } diff --git a/src/charging-station/ui-server/UIWebSocketServer.ts b/src/charging-station/ui-server/UIWebSocketServer.ts index 16477146..368a09fe 100644 --- a/src/charging-station/ui-server/UIWebSocketServer.ts +++ b/src/charging-station/ui-server/UIWebSocketServer.ts @@ -5,7 +5,11 @@ import { StatusCodes } from 'http-status-codes' import { type RawData, WebSocket, WebSocketServer } from 'ws' import { AbstractUIServer } from './AbstractUIServer.js' -import { UIServerUtils } from './UIServerUtils.js' +import { + getProtocolAndVersion, + handleProtocols, + isProtocolAndVersionSupported +} from './UIServerUtils.js' import { type ProtocolRequest, type ProtocolResponse, @@ -30,14 +34,14 @@ export class UIWebSocketServer extends AbstractUIServer { public constructor (protected readonly uiServerConfiguration: UIServerConfiguration) { super(uiServerConfiguration) this.webSocketServer = new WebSocketServer({ - handleProtocols: UIServerUtils.handleProtocols, + handleProtocols, noServer: true }) } public start (): void { this.webSocketServer.on('connection', (ws: WebSocket, _req: IncomingMessage): void => { - if (!UIServerUtils.isProtocolAndVersionSupported(ws.protocol)) { + if (!isProtocolAndVersionSupported(ws.protocol)) { logger.error( `${this.logPrefix( moduleName, @@ -46,7 +50,7 @@ export class UIWebSocketServer extends AbstractUIServer { ) ws.close(WebSocketCloseEventStatusCode.CLOSE_PROTOCOL_ERROR) } - const [, version] = UIServerUtils.getProtocolAndVersion(ws.protocol) + const [, version] = getProtocolAndVersion(ws.protocol) this.registerProtocolVersionUIService(version) ws.on('message', rawData => { const request = this.validateRawDataRequest(rawData) -- 2.34.1