From 97a34e4836fa5ecbf3bc1bf91fcd9867399aeace Mon Sep 17 00:00:00 2001 From: Christophe CAMENSULI Date: Sun, 4 Feb 2024 18:24:29 +0100 Subject: [PATCH] feat : add websocket servers --- src/nodefony/src/types/nodefony.d.ts | 4 +- src/packages/@nodefony/http/index.ts | 2 + .../http/nodefony/service/http-kernel.ts | 14 ++ .../servers/server-websocket-secure.ts | 137 ++++++++++++++++++ .../service/servers/server-websocket.ts | 134 ++++++++--------- .../http/nodefony/src/context/context.ts | 2 +- 6 files changed, 225 insertions(+), 68 deletions(-) create mode 100644 src/packages/@nodefony/http/nodefony/service/servers/server-websocket-secure.ts diff --git a/src/nodefony/src/types/nodefony.d.ts b/src/nodefony/src/types/nodefony.d.ts index 44f3fc3..f44a54e 100644 --- a/src/nodefony/src/types/nodefony.d.ts +++ b/src/nodefony/src/types/nodefony.d.ts @@ -56,7 +56,7 @@ export * from "../command/Command"; export * from "../command/Builder"; export * from "../Cli"; export * from "../Nodefony"; -export * from "../Kernel/Kernel"; -export * from "../Kernel/Module"; +export * from "../kernel/Kernel"; +export * from "../kernel/Module"; //export * from "../index"; diff --git a/src/packages/@nodefony/http/index.ts b/src/packages/@nodefony/http/index.ts index e9e6f38..2eb75fe 100644 --- a/src/packages/@nodefony/http/index.ts +++ b/src/packages/@nodefony/http/index.ts @@ -4,6 +4,7 @@ import HttpKernel from "./nodefony/service/http-kernel"; import HttpServer from "./nodefony/service/servers/server-http"; import HttpsServer from "./nodefony/service/servers/server-https"; import WebsocketServer from "./nodefony/service/servers/server-websocket"; +import WebsocketSecureServer from "./nodefony/service/servers/server-websocket-secure"; import StaticServer from "./nodefony/service/servers/server-static"; import networkCommand from "./nodefony/command/networkCommand"; import { fileURLToPath } from "url"; @@ -26,6 +27,7 @@ class Http extends Module { this.addService(HttpsServer, this.httpKernel); this.addService(StaticServer, this.httpKernel); this.addService(WebsocketServer, this.httpKernel); + this.addService(WebsocketSecureServer, this.httpKernel); return this; } diff --git a/src/packages/@nodefony/http/nodefony/service/http-kernel.ts b/src/packages/@nodefony/http/nodefony/service/http-kernel.ts index 7f17751..170b43b 100644 --- a/src/packages/@nodefony/http/nodefony/service/http-kernel.ts +++ b/src/packages/@nodefony/http/nodefony/service/http-kernel.ts @@ -12,6 +12,8 @@ import https from "node:http"; import http2 from "node:http2"; import httpServer from "../service/servers/server-http"; import httpsServer from "../service/servers/server-https"; +import websocketServer from "../service/servers/server-websocket"; +import websocketSecureServer from "../service/servers/server-websocket-secure"; import Statics from "./servers/server-static"; import WebsocketContext from "../src/context/websocket/WebsocketContext"; import HttpContext from "../src/context/http/HttpContext"; @@ -206,6 +208,18 @@ class HttpKernel extends Service { await serverHttps.createServer(); servers.push(serverHttps); } + const serverWebsocket: websocketServer = this.get("server-websocket"); + if (serverWebsocket && serverHttp) { + await serverWebsocket.createServer(serverHttp); + servers.push(serverWebsocket); + } + const serverWebsocketSecure: websocketSecureServer = this.get( + "server-websocket-secure" + ); + if (serverWebsocketSecure && serverHttps) { + await serverWebsocketSecure.createServer(serverHttps); + servers.push(serverWebsocketSecure); + } return servers; } diff --git a/src/packages/@nodefony/http/nodefony/service/servers/server-websocket-secure.ts b/src/packages/@nodefony/http/nodefony/service/servers/server-websocket-secure.ts new file mode 100644 index 0000000..4196e4f --- /dev/null +++ b/src/packages/@nodefony/http/nodefony/service/servers/server-websocket-secure.ts @@ -0,0 +1,137 @@ +// https://github.com/Worlize/WebSocket-Node/wiki/Documentation + +import websocket from "websocket"; +import nodefony, { + extend, + Service, + Kernel, + Container, + Event, + Module, + FamilyType, + DefaultOptionsService, +} from "nodefony"; +import HttpKernel, { ProtocolType, ServerType } from "../http-kernel"; +import { AddressInfo } from "node:net"; +import https from "node:https"; +import httpsServers from "./server-https"; + +class Websocket extends Service { + module: Module; + httpKernel: HttpKernel; + ready: boolean = false; + server: websocket.server | null = null; + port: number; + domain: string; + protocol: ProtocolType = "1.1"; + family: FamilyType | null = null; + scheme: string = "wss"; + address: string | null = null; + type: ServerType = "websocket-secure"; + infos: AddressInfo | null = null; + constructor(module: Module, httpKernel: HttpKernel) { + super( + "server-websocket-secure", + module.container as Container, + module.notificationsCenter as Event, + module.options.websocket + ); + this.module = module; + this.httpKernel = httpKernel; + this.port = this.setPort(); + this.domain = this.kernel?.domain as string; + this.ready = false; + } + + setPort(): number { + if (this.kernel?.options.servers?.https) { + return this.kernel?.options.servers?.https?.port || 0; + } + return 0; + } + + async createServer(serverHttps: httpsServers): Promise { + return new Promise((resolve, reject) => { + try { + this.infos = ( + serverHttps.server as https.Server + ).address() as AddressInfo; + if (this.infos) { + this.port = this.infos.port; + this.address = this.infos.address; + this.family = this.infos.family as FamilyType; + this.protocol = serverHttps.protocol; + } + const conf: websocket.IServerConfig = extend(true, {}, this.options); + conf.httpServer = serverHttps.server as https.Server; + this.server = new websocket.server(conf); + this.server.on("request", (request) => + this.httpKernel.onWebsocketRequest(request, this.type) + ); + this.kernel?.prependOnceListener( + "onTerminate", + () => + new Promise((resolve, reject) => { + if (this.server && this.ready) { + this.server.broadcast( + JSON.stringify({ + nodefony: { + state: "shutDown", + }, + }) + ); + setTimeout(() => { + try { + if (this.server?.config?.httpServer) { + this.server.shutDown(); + } + this.log( + ` SHUTDOWN WEBSOCKET Server is listening on DOMAIN : ${this.domain} PORT : ${this.port}`, + "INFO" + ); + return resolve(true); + } catch (e) { + return reject(e); + } + }, 500); + return; + } + return resolve(true); + }) + ); + if (this.server) { + this.ready = true; + } + this.module.fire("onServersReady", this.type, this); + return resolve(this.server); + } catch (e) { + this.log(e, "ERROR"); + return reject(e); + } + }); + } + + removePendingRequests(url: string) { + if (url && this.server) { + this.server.pendingRequests.forEach((request, index) => { + if (request.httpRequest.url === url) { + try { + request.emit("requestResolved", request); + request.emit("requestRejected", request); + this.server?.pendingRequests.splice(index, 1); + } catch (e) {} + } + }); + } + } + + showBanner(): void { + if (this.infos) { + this.log( + `Server Listen on ${this.scheme}://${this.infos.address}:${this.infos.port} Family: ${this.infos.family} Protocol : ${this.protocol}` + ); + } + } +} + +export default Websocket; diff --git a/src/packages/@nodefony/http/nodefony/service/servers/server-websocket.ts b/src/packages/@nodefony/http/nodefony/service/servers/server-websocket.ts index 60f8e85..f99895d 100644 --- a/src/packages/@nodefony/http/nodefony/service/servers/server-websocket.ts +++ b/src/packages/@nodefony/http/nodefony/service/servers/server-websocket.ts @@ -2,6 +2,7 @@ import websocket from "websocket"; import nodefony, { + extend, Service, Kernel, Container, @@ -13,8 +14,7 @@ import nodefony, { import HttpKernel, { ProtocolType, ServerType } from "../http-kernel"; import { AddressInfo } from "node:net"; import http from "node:http"; - -import http2 from "node:http2"; +import httpServer from "./server-http"; class Websocket extends Service { module: Module; @@ -23,6 +23,7 @@ class Websocket extends Service { server: websocket.server | null = null; port: number; domain: string; + protocol: ProtocolType = "1.1"; family: FamilyType | null = null; scheme: string = "ws"; address: string | null = null; @@ -43,75 +44,70 @@ class Websocket extends Service { } setPort(): number { - if (this.kernel?.options.servers?.https) { - return this.kernel?.options.servers?.https?.port || 0; + if (this.kernel?.options.servers?.http) { + return this.kernel?.options.servers?.http?.port || 0; } return 0; } - createServer(http: http.Server) { - this.kernel?.once("onServersReady", (type) => { - if (type === "http") { - try { - this.infos = http.address() as AddressInfo; - if (this.infos) { - this.port = this.infos.port; - this.address = this.infos.address; - this.family = this.infos.family as FamilyType; - } - //this.settings = this.getParameters("bundles.http").websocket || {}; - const conf = nodefony.extend(true, {}, this.options); - conf.httpServer = http; - this.server = new websocket.server(conf); - this.server.on("request", (request) => - this.httpKernel.onWebsocketRequest(request, this.type) - ); - - this.kernel?.prependOnceListener( - "onTerminate", - () => - new Promise((resolve, reject) => { - if (this.server && this.ready) { - this.server.broadcast( - JSON.stringify({ - nodefony: { - state: "shutDown", - }, - }) - ); - setTimeout(() => { - try { - if (this.server?.config?.httpServer) { - this.server.shutDown(); - } - this.log( - ` SHUTDOWN WEBSOCKET Server is listening on DOMAIN : ${this.domain} PORT : ${this.port}`, - "INFO" - ); - return resolve(true); - } catch (e) { - return reject(e); + async createServer(serverHttp: httpServer): Promise { + return new Promise((resolve, reject) => { + try { + this.infos = ( + serverHttp.server as http.Server + ).address() as AddressInfo; + if (this.infos) { + this.port = this.infos.port; + this.address = this.infos.address; + this.family = this.infos.family as FamilyType; + this.protocol = serverHttp.protocol; + } + //this.settings = this.getParameters("bundles.http").websocket || {}; + const conf: websocket.IServerConfig = extend(true, {}, this.options); + conf.httpServer = serverHttp.server as http.Server; + this.server = new websocket.server(conf); + this.server.on("request", (request) => + this.httpKernel.onWebsocketRequest(request, this.type) + ); + this.kernel?.prependOnceListener( + "onTerminate", + () => + new Promise((resolve, reject) => { + if (this.server && this.ready) { + this.server.broadcast( + JSON.stringify({ + nodefony: { + state: "shutDown", + }, + }) + ); + setTimeout(() => { + try { + if (this.server?.config?.httpServer) { + this.server.shutDown(); } - }, 500); - return; - } - return resolve(true); - }) - ); - - if (this.server) { - this.ready = true; - } - this.module.fire("onServersReady", this.type, this); - this.log( - `Listening on DOMAIN : ws://${this.domain}:${this.port}`, - "INFO" - ); - return this.server; - } catch (e) { - this.log(e, "ERROR"); - throw e; + this.log( + ` SHUTDOWN WEBSOCKET Server is listening on DOMAIN : ${this.domain} PORT : ${this.port}`, + "INFO" + ); + return resolve(true); + } catch (e) { + return reject(e); + } + }, 500); + return; + } + return resolve(true); + }) + ); + if (this.server) { + this.ready = true; } + this.module.fire("onServersReady", this.type, this); + return resolve(this.server); + } catch (e) { + this.log(e, "ERROR"); + return reject(e); } }); } @@ -129,6 +125,14 @@ class Websocket extends Service { }); } } + + showBanner(): void { + if (this.infos) { + this.log( + `Server Listen on ${this.scheme}://${this.infos.address}:${this.infos.port} Family: ${this.infos.family} Protocol : ${this.protocol}` + ); + } + } } export default Websocket; diff --git a/src/packages/@nodefony/http/nodefony/src/context/context.ts b/src/packages/@nodefony/http/nodefony/src/context/context.ts index 4f0548b..84c612e 100644 --- a/src/packages/@nodefony/http/nodefony/src/context/context.ts +++ b/src/packages/@nodefony/http/nodefony/src/context/context.ts @@ -15,12 +15,12 @@ import WebsocketResponse from "./websocket/Response"; import HttpResquest from "./http/Request"; import Http2Resquest from "./http2/Request"; import clc from "cli-color"; -const colorLogEvent = clc.cyan.bgBlue("EVENT CONTEXT"); import http from "node:http"; import http2 from "node:http2"; import { URL } from "node:url"; import Session from "../session/session"; import Cookie from "../cookies/cookie"; +const colorLogEvent = clc.cyan.bgBlue("EVENT CONTEXT"); export type contextRequest = | HttpResquest