546c7642de181d33de56e8f4ea9e214515a2220e
1 import type { IncomingMessage
, RequestListener
, ServerResponse
} from
'http';
3 import { StatusCodes
} from
'http-status-codes';
5 import { BaseError
} from
'../../exception';
10 type ProtocolResponse
,
14 type UIServerConfiguration
,
16 import { logger
} from
'../../utils/Logger';
17 import { Utils
} from
'../../utils/Utils';
18 import { AbstractUIServer
, UIServerUtils
} from
'../internal';
20 const moduleName
= 'UIHttpServer';
22 export class UIHttpServer
extends AbstractUIServer
{
23 public constructor(protected readonly uiServerConfiguration
: UIServerConfiguration
) {
24 super(uiServerConfiguration
);
27 public start(): void {
28 this.httpServer
.on('request', this.requestListener
.bind(this) as RequestListener
);
29 this.startHttpServer();
32 // eslint-disable-next-line @typescript-eslint/no-unused-vars
33 public sendRequest(request
: ProtocolRequest
): void {
34 // This is intentionally left blank
37 public sendResponse(response
: ProtocolResponse
): void {
38 const [uuid
, payload
] = response
;
40 if (this.responseHandlers
.has(uuid
) === true) {
41 const res
= this.responseHandlers
.get(uuid
) as ServerResponse
;
43 .writeHead(this.responseStatusToStatusCode(payload
.status), {
44 'Content-Type': 'application/json',
46 .end(JSON
.stringify(payload
));
49 `${this.logPrefix(moduleName, 'sendResponse')} Response for unknown request id: ${uuid}`
54 `${this.logPrefix(moduleName, 'sendResponse')} Error at sending response id '${uuid}':`,
58 this.responseHandlers
.delete(uuid
);
62 public logPrefix
= (modName
?: string, methodName
?: string, prefixSuffix
?: string): string => {
63 const logMsgPrefix
= prefixSuffix
? `UI HTTP Server ${prefixSuffix}` : 'UI HTTP Server';
65 Utils
.isNotEmptyString(modName
) && Utils
.isNotEmptyString(methodName
)
66 ? ` ${logMsgPrefix} | ${modName}.${methodName}:`
67 : ` ${logMsgPrefix} |`;
68 return Utils
.logPrefix(logMsg
);
71 private requestListener(req
: IncomingMessage
, res
: ServerResponse
): void {
72 this.authenticate(req
, (err
) => {
75 .writeHead(StatusCodes
.UNAUTHORIZED
, {
76 'Content-Type': 'text/plain',
77 'WWW-Authenticate': 'Basic realm=users',
79 .end(`${StatusCodes.UNAUTHORIZED} Unauthorized`)
84 // Expected request URL pathname: /ui/:version/:procedureName
85 const [protocol
, version
, procedureName
] = req
.url
?.split('/').slice(1) as [
90 const uuid
= Utils
.generateUUID();
91 this.responseHandlers
.set(uuid
, res
);
93 const fullProtocol
= `${protocol}${version}`;
94 if (UIServerUtils
.isProtocolAndVersionSupported(fullProtocol
) === false) {
95 throw new BaseError(`Unsupported UI protocol version: '${fullProtocol}'`);
97 this.registerProtocolVersionUIService(version
);
98 req
.on('error', (error
) => {
100 `${this.logPrefix(moduleName, 'requestListener.req.onerror')} Error on HTTP request:`,
104 if (req
.method
=== 'POST') {
105 const bodyBuffer
= [];
107 .on('data', (chunk
) => {
108 bodyBuffer
.push(chunk
);
111 const body
= JSON
.parse(Buffer
.concat(bodyBuffer
).toString()) as RequestPayload
;
114 ?.requestHandler(this.buildProtocolRequest(uuid
, procedureName
, body
?? {}))
116 /* Error caught by AbstractUIService */
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 responseStatusToStatusCode(status: ResponseStatus
): StatusCodes
{
133 case ResponseStatus
.SUCCESS
:
134 return StatusCodes
.OK
;
135 case ResponseStatus
.FAILURE
:
136 return StatusCodes
.BAD_REQUEST
;
138 return StatusCodes
.INTERNAL_SERVER_ERROR
;