diff --git a/javascript/socketd/package.json b/javascript/socketd/package.json index e0dff691a0705c6ef38cefb7be0b7bbd3dada355..3f16cc6735711f0b6da1f247ce1e592da22c8b6e 100644 --- a/javascript/socketd/package.json +++ b/javascript/socketd/package.json @@ -5,13 +5,14 @@ "author": "noear", "license": "Apache-2.0", "scripts": { - "devNodeBuild": "webpack --mode development --env LIB_TARGET=commonjs2", + "devNodeBuild": "webpack --mode development --env LIB_TARGET=commonjs2 OUT_FILE_NAME=socket.d-node.js", "devBrowserBuild": "webpack --mode development --env LIB_TARGET=window", - "prodNodeBuild": "webpack --mode production --env LIB_TARGET=commonjs2", + "prodNodeBuild": "webpack --mode production --env LIB_TARGET=commonjs2 OUT_FILE_NAME=socket.d-node.js", "prodBrowserBuild": "webpack --mode production --env LIB_TARGET=window" }, "devDependencies": { "webpack": "^5.89.0", - "webpack-cli": "^5.1.4" + "webpack-cli": "^5.1.4", + "ws": "^8.16.0" } } diff --git a/javascript/socketd/src/socketd/SocketD.ts b/javascript/socketd/src/socketd/SocketD.ts index 420d8dfb90ccd0ab53f8b0c6b5fcf58cbeb394c3..732d6bbf08041e8669fb8b12d7dffbb9f513151d 100644 --- a/javascript/socketd/src/socketd/SocketD.ts +++ b/javascript/socketd/src/socketd/SocketD.ts @@ -11,6 +11,7 @@ import type {IoBiConsumer} from "./transport/core/Typealias"; import type {Session} from "./transport/core/Session"; import type {Message} from "./transport/core/Message"; import {EntityMetas} from "./transport/core/Constants"; +import {Logger} from './utils/LogUtils' export class SocketD { /** @@ -133,5 +134,13 @@ export class SocketD { static newPipelineListener(): PipelineListener { return new PipelineListener(); } + + static newLogger(name: string): Logger { + return new Logger(name); + } + + static setLogLevel(level: string) { + Logger.setLevel(level); + } } diff --git a/javascript/socketd/src/socketd/transport_websocket/WsChannelAssistant.ts b/javascript/socketd/src/socketd/transport_websocket/WsChannelAssistant.ts index 7371a46c33240d9ff3fe2c714334f95c9778477b..343dbd7e8ff8ca7d1d4cfd59605d21499eaea672 100644 --- a/javascript/socketd/src/socketd/transport_websocket/WsChannelAssistant.ts +++ b/javascript/socketd/src/socketd/transport_websocket/WsChannelAssistant.ts @@ -2,8 +2,9 @@ import type {ChannelAssistant} from "../transport/core/ChannelAssistant"; import type {Frame} from "../transport/core/Frame"; import type {Config} from "../transport/core/Config"; import {ArrayBufferCodecReader, ArrayBufferCodecWriter} from "../transport/core/Codec"; +import {BridgeWsClient} from "./bridge/BridgeWsClient"; -export class WsChannelAssistant implements ChannelAssistant { +export class WsChannelAssistant implements ChannelAssistant { _config: Config; constructor(config: Config) { @@ -14,25 +15,25 @@ export class WsChannelAssistant implements ChannelAssistant { return this._config.getCodec().read(new ArrayBufferCodecReader(buffer)); } - write(target: WebSocket, frame: Frame) { + write(target: BridgeWsClient, frame: Frame) { let tmp = this._config.getCodec() .write(frame, n => new ArrayBufferCodecWriter(n)); target.send(tmp.getBuffer()); } - isValid(target: WebSocket): boolean { - return target.readyState === WebSocket.OPEN + isValid(target: BridgeWsClient): boolean { + return target.isOpen(); } - close(target: WebSocket) { + close(target: BridgeWsClient) { target.close(); } - getRemoteAddress(target: WebSocket): string { + getRemoteAddress(target: BridgeWsClient): string { throw new Error("Method not implemented."); } - getLocalAddress(target: WebSocket): string { + getLocalAddress(target: BridgeWsClient): string { throw new Error("Method not implemented."); } -} \ No newline at end of file +} diff --git a/javascript/socketd/src/socketd/transport_websocket/WsClient.ts b/javascript/socketd/src/socketd/transport_websocket/WsClient.ts index 582913f00f44a2f4a292b100968f4d3ce189bb5c..5b86e059d46463b04eb9e5fd61c1e410238eee0e 100644 --- a/javascript/socketd/src/socketd/transport_websocket/WsClient.ts +++ b/javascript/socketd/src/socketd/transport_websocket/WsClient.ts @@ -4,8 +4,9 @@ import {WsChannelAssistant} from "./WsChannelAssistant"; import type {ClientConnector } from "../transport/client/ClientConnector"; import {WsClientConnector} from "./WsClientConnector"; import type {ChannelSupporter} from "../transport/core/ChannelSupporter"; +import {BridgeWsClient} from "./bridge/BridgeWsClient"; -export class WsClient extends ClientBase implements ChannelSupporter { +export class WsClient extends ClientBase implements ChannelSupporter { constructor(clientConfig: ClientConfig) { super(clientConfig, new WsChannelAssistant(clientConfig)); } @@ -14,4 +15,4 @@ export class WsClient extends ClientBase implements ChannelS protected createConnector(): ClientConnector { return new WsClientConnector(this); } -} \ No newline at end of file +} diff --git a/javascript/socketd/src/socketd/transport_websocket/bridge/Bridge.ts b/javascript/socketd/src/socketd/transport_websocket/bridge/Bridge.ts new file mode 100644 index 0000000000000000000000000000000000000000..36ad7bcb237de71129c064381d6c761810ce7cf5 --- /dev/null +++ b/javascript/socketd/src/socketd/transport_websocket/bridge/Bridge.ts @@ -0,0 +1,13 @@ +import {EnvUtils} from '../../utils/EnvUtils'; +import {BrowserWsClientImpl} from './impl/BridgeWsClientImpl.browser'; +import {NodeWsClientImpl} from './impl/BridgeWsClientImpl.node'; +import {BridgeWsClientConnector} from "./BridgeWsClient"; + +export function createWsClient(url: string, connector: BridgeWsClientConnector) { + if (EnvUtils.isRunInBrowser()) { + console.log('浏览器端ws'); + return new BrowserWsClientImpl(url, connector); + } + console.log('node端ws'); + return new NodeWsClientImpl(url, connector); +} diff --git a/javascript/socketd/src/socketd/transport_websocket/bridge/BridgeWsClient.ts b/javascript/socketd/src/socketd/transport_websocket/bridge/BridgeWsClient.ts new file mode 100644 index 0000000000000000000000000000000000000000..bd714c90b44bc79aa35a294279937d108551e561 --- /dev/null +++ b/javascript/socketd/src/socketd/transport_websocket/bridge/BridgeWsClient.ts @@ -0,0 +1,49 @@ +export interface BridgeWsEvent { + +} + +export interface BridgeWsMsgEvent extends BridgeWsEvent { + data(): any; +} + +export interface BridgeWsCloseEvent extends BridgeWsEvent { + +} + +export interface BridgeWsClientConnector { + onOpen(e: BridgeWsEvent): void; + onMessage(e: BridgeWsMsgEvent): void; + onClose(e: BridgeWsCloseEvent): void; + onError(e: Error): void; +} + +export interface BridgeWsClient { + isConnecting(): boolean; + isOpen(): boolean; + isClosing(): boolean; + isClosed(): boolean; + close(): void; + send(data: string | ArrayBuffer): void; +} + + +export class BridgeWsEventImpl implements BridgeWsEvent { + constructor() { + + } +} + +export class BridgeWsMsgEventImpl implements BridgeWsMsgEvent { + private _data: any; + constructor(data: any) { + this._data = data; + } + data(): any { + return this._data; + } +} + +export class BridgeWsCloseEventImpl implements BridgeWsCloseEvent { + +} + diff --git a/javascript/socketd/src/socketd/transport_websocket/bridge/impl/BridgeWsClientImpl.browser.ts b/javascript/socketd/src/socketd/transport_websocket/bridge/impl/BridgeWsClientImpl.browser.ts new file mode 100644 index 0000000000000000000000000000000000000000..6ff3c531ec6e8dbfad15a07b7f4a73d2d5ba9aa7 --- /dev/null +++ b/javascript/socketd/src/socketd/transport_websocket/bridge/impl/BridgeWsClientImpl.browser.ts @@ -0,0 +1,74 @@ +import { + BridgeWsClient, + BridgeWsClientConnector, + BridgeWsEventImpl, + BridgeWsMsgEventImpl, + BridgeWsCloseEventImpl +} from "../BridgeWsClient"; +import {Logger} from "../../../utils/LogUtils"; + +export class BrowserWsClientImpl implements BridgeWsClient { + private _real: WebSocket; + private _connector: BridgeWsClientConnector; + private _logger: Logger; + constructor(url: string, connector: BridgeWsClientConnector) { + this._logger = new Logger("BridgeWsClientImpl.browser"); + this._logger.debug("实例化..."); + this._real = new WebSocket(url); + this._connector = connector; + this._real.binaryType = "arraybuffer"; + this._real.onopen = this.onOpen.bind(this); + this._real.onmessage = this.onMessage.bind(this); + this._real.onclose = this.onClose.bind(this); + this._real.onerror = this.onError.bind(this); + } + + isConnecting(): boolean { + return this._real.readyState == WebSocket.CONNECTING; + } + + isClosed(): boolean { + return this._real.readyState == WebSocket.CLOSED; + } + + isClosing(): boolean { + return this._real.readyState == WebSocket.CLOSING; + } + + isOpen(): boolean { + return this._real.readyState == WebSocket.OPEN; + } + + onOpen(e: Event) { + this._logger.debug("onOpen", e); + let evt = new BridgeWsEventImpl(); + // TODO event细节待完善 + this._connector.onOpen(evt); + } + + onMessage(e: MessageEvent) { + let evt = new BridgeWsMsgEventImpl(e.data); + // TODO event细节待完善 + this._connector.onMessage(evt); + } + + onClose(e: CloseEvent) { + this._logger.debug("onClose", e); + let evt = new BridgeWsCloseEventImpl(); + // TODO event细节待完善 + this._connector.onClose(evt); + } + + onError(e) { + this._logger.debug("onError", e); + this._connector.onError(e); + } + + close(): void { + this._real.close(); + } + + send(data: string | ArrayBuffer): void { + this._real.send(data); + } +} diff --git a/javascript/socketd/src/socketd/transport_websocket/bridge/impl/BridgeWsClientImpl.node.ts b/javascript/socketd/src/socketd/transport_websocket/bridge/impl/BridgeWsClientImpl.node.ts new file mode 100644 index 0000000000000000000000000000000000000000..6f7a6cb06882e18f377139d3a6e8be5a14c5306f --- /dev/null +++ b/javascript/socketd/src/socketd/transport_websocket/bridge/impl/BridgeWsClientImpl.node.ts @@ -0,0 +1,76 @@ +import { + BridgeWsClient, + BridgeWsClientConnector, + BridgeWsCloseEventImpl, + BridgeWsEventImpl, + BridgeWsMsgEventImpl +} from "../BridgeWsClient"; +import {Logger} from "../../../utils/LogUtils"; +import NodeWebSocket from 'ws'; + +export class NodeWsClientImpl implements BridgeWsClient { + private _real: NodeWebSocket; + private _connector: BridgeWsClientConnector; + private _logger: Logger; + + constructor(url: string, connector: BridgeWsClientConnector) { + this._logger = new Logger("BridgeWsClientImpl.node"); + this._logger.debug("实例化..."); + this._real = new NodeWebSocket(url); + this._connector = connector; + this._real.binaryType = "arraybuffer"; + this._real.on('open', this.onOpen.bind(this)); + this._real.on('message', this.onMessage.bind(this)); + this._real.on('close', this.onClose.bind(this)); + this._real.on('error', this.onError.bind(this)); + } + + close(): void { + } + + isClosed(): boolean { + return false; + } + + isClosing(): boolean { + return false; + } + + isConnecting(): boolean { + return false; + } + + isOpen(): boolean { + return false; + } + + onOpen() { + this._logger.debug("onOpen"); + let evt = new BridgeWsEventImpl(); + // TODO event细节待完善 + this._connector.onOpen(evt); + } + + onMessage(msg) { + let evt = new BridgeWsMsgEventImpl(msg); + // TODO event细节待完善 + this._connector.onMessage(evt); + } + + onClose() { + this._logger.debug("onClose"); + let evt = new BridgeWsCloseEventImpl(); + // TODO event细节待完善 + this._connector.onClose(evt); + } + + onError(e) { + this._logger.debug("onError", e); + this._connector.onError(e); + } + + send(data: string | ArrayBuffer): void { + this._real.send(data); + } + +} diff --git a/javascript/socketd/src/socketd/transport_websocket/impl/WebSocketClientImpl.ts b/javascript/socketd/src/socketd/transport_websocket/impl/WebSocketClientImpl.ts index aa898c7e4462fa206288a97f90ba7cdebafb6112..c351fd7c0d77dc06fb6b15cde5864620d160c2f4 100644 --- a/javascript/socketd/src/socketd/transport_websocket/impl/WebSocketClientImpl.ts +++ b/javascript/socketd/src/socketd/transport_websocket/impl/WebSocketClientImpl.ts @@ -5,27 +5,30 @@ import type {ChannelInternal} from "../../transport/core/Channel"; import {ChannelDefault} from "../../transport/core/ChannelDefault"; import {Flags} from "../../transport/core/Constants"; import {SocketdConnectionException} from "../../exception/SocketdException"; +import {createWsClient} from '../bridge/Bridge' -export class WebSocketClientImpl { - _real: WebSocket; +import { + BridgeWsClient, + BridgeWsClientConnector, + BridgeWsCloseEvent, + BridgeWsEvent, + BridgeWsMsgEvent +} from "../bridge/BridgeWsClient"; + +export class WebSocketClientImpl implements BridgeWsClientConnector { + _real: BridgeWsClient; _client: WsClient; _channel: ChannelInternal; _handshakeFuture: IoConsumer; constructor(url: string, client: WsClient, handshakeFuture: IoConsumer) { - this._real = new WebSocket(url); + this._real = createWsClient(url, this); this._client = client; this._channel = new ChannelDefault(this._real, client); this._handshakeFuture = handshakeFuture; - - this._real.binaryType = "arraybuffer"; - this._real.onopen = this.onOpen.bind(this); - this._real.onmessage = this.onMessage.bind(this); - this._real.onclose = this.onClose.bind(this); - this._real.onerror = this.onError.bind(this); } - onOpen(e: Event) { + onOpen(e: BridgeWsEvent) { try { this._channel.sendConnect(this._client.getConfig().getUrl()); } catch (err) { @@ -33,12 +36,12 @@ export class WebSocketClientImpl { } } - onMessage(e: MessageEvent) { - if (e.data instanceof String) { + onMessage(e: BridgeWsMsgEvent) { + if (e.data() instanceof String) { console.warn("Client channel unsupported onMessage(String test)"); } else { try { - let frame = this._client.getAssistant().read(e.data); + let frame = this._client.getAssistant().read(e.data()); if (frame != null) { if (frame.flag() == Flags.Connack) { @@ -64,7 +67,7 @@ export class WebSocketClientImpl { } } - onClose(e: CloseEvent) { + onClose(e: BridgeWsCloseEvent) { this._client.getProcessor().onClose(this._channel); } diff --git a/javascript/socketd/src/socketd/utils/EnvUtils.ts b/javascript/socketd/src/socketd/utils/EnvUtils.ts new file mode 100644 index 0000000000000000000000000000000000000000..625ea9ba6d7f97eed5b85ce27077e62190ac7cd4 --- /dev/null +++ b/javascript/socketd/src/socketd/utils/EnvUtils.ts @@ -0,0 +1,22 @@ +export enum Runtime { + None = -1, + Browser = 1, + Node = 2, +} + +export class EnvUtils { + static _runtime = (typeof window != 'undefined') ? Runtime.Browser : + (typeof process !== 'undefined' && process.versions && process.versions.node) ? Runtime.Node : Runtime.None; + + static runtime(): Runtime { + return EnvUtils._runtime; + } + + static isRunInBrowser(): boolean { + return EnvUtils._runtime == Runtime.Browser; + } + + static isRunInNode(): boolean { + return EnvUtils._runtime == Runtime.Node; + } +} diff --git a/javascript/socketd/src/socketd/utils/LogUtils.ts b/javascript/socketd/src/socketd/utils/LogUtils.ts new file mode 100644 index 0000000000000000000000000000000000000000..eb2b8c364744915a9a4221815cec282a636846ce --- /dev/null +++ b/javascript/socketd/src/socketd/utils/LogUtils.ts @@ -0,0 +1,99 @@ +export const LOG_LEVEL = { + all: { + v: 0 + }, + debug: { + v: 1, + color: 'gray' + }, + info: { + v: 2, + color: 'green' + }, + warn: { + v: 3, + color: 'blue' + }, + error: { + v: 4, + color: 'red' + }, + off: { + v: 5 + } +}; + +let _level = 'info'; + +export class Logger { + private _name: string; + constructor(name) { + this._name = name; + } + + private out(curLevel: string, msg: string[] | any[]) { + if (LOG_LEVEL[curLevel].v < LOG_LEVEL[_level].v) { + return; + } + let prefix = `%c [SocketD][${dateFormat(new Date(), 'yyyy-MM-dd hh:mm:ss.SSS')}][${this._name}][level:${curLevel}]`; + let params: string[] | any[] = []; + params.push(prefix); + params.push(`color: ${LOG_LEVEL[curLevel].color}`); + if (msg) { + for(let i = 0; i < msg.length; i++){ + params.push(msg[i]); + } + } + console.log.apply(this,params); + } + + debug(...data: any[]) { + this.out('debug', Array.prototype.slice.apply(arguments)); + } + + info(...data: any[]) { + this.out('info', Array.prototype.slice.apply(arguments)); + } + + warn(...data: any[]) { + this.out('warn', Array.prototype.slice.apply(arguments)); + } + + error(...data: any[]) { + this.out('error', Array.prototype.slice.apply(arguments)); + } + + static setLevel(level: string) { + if (!LOG_LEVEL[level]) { + throw new Error("The level nonsupport: " + level); + } + _level = level; + } +} + +// 日期格式化 +function dateFormat(date,fmt) { + // 默认格式 + fmt = fmt ? fmt : 'yyyy-MM-dd hh:mm:ss'; + + let o = { + "M+" : date.getMonth()+1, // 月份 + "d+" : date.getDate(), // 日 + "h+" : date.getHours(), // 小时 + "m+" : date.getMinutes(), // 分 + "s+" : date.getSeconds(), // 秒 + "q+" : Math.floor((date.getMonth()+3)/3), // 季度 + "S+" : date.getMilliseconds() // 毫秒 + }; + if(/(y+)/.test(fmt)) { + fmt=fmt.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length)); + } + for(let k in o) { + if(new RegExp("("+ k +")").test(fmt)){ + fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : + RegExp.$1.length==2 ? (("00"+ o[k]).substr((""+ o[k]).length)) : (("000"+ o[k]).substr((""+ o[k]).length)) + ); + } + } + return fmt; +} diff --git a/javascript/socketd/webpack.config.js b/javascript/socketd/webpack.config.js index be6daedca4d10665c06ee737d7f75e82e159b943..198705bf7ab25ccb0314f82f9d05e1f63fa61b56 100644 --- a/javascript/socketd/webpack.config.js +++ b/javascript/socketd/webpack.config.js @@ -12,7 +12,7 @@ module.exports = env => { }, output: { // 把所有依赖的模块合并输出到一个 bundle.js 文件 - filename: 'socket.d.js', + filename: env.OUT_FILE_NAME || 'socket.d.js', // 输出文件都放到 dist 目录下 path: path.resolve(__dirname, './dist/release'), libraryTarget: env.LIB_TARGET || 'window'