@bigtyphoon/melo 1.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +55 -0
- package/bin/commadtest.ts +10 -0
- package/bin/commands/add.ts +43 -0
- package/bin/commands/init.ts +292 -0
- package/bin/commands/kill.ts +21 -0
- package/bin/commands/list.ts +60 -0
- package/bin/commands/masterha.ts +40 -0
- package/bin/commands/restart.ts +48 -0
- package/bin/commands/start.ts +65 -0
- package/bin/commands/stop.ts +26 -0
- package/bin/melo.ts +30 -0
- package/bin/utils/constants.ts +27 -0
- package/bin/utils/utils.ts +130 -0
- package/dist/bin/commadtest.js +9 -0
- package/dist/bin/commands/add.js +40 -0
- package/dist/bin/commands/init.js +279 -0
- package/dist/bin/commands/kill.js +21 -0
- package/dist/bin/commands/list.js +65 -0
- package/dist/bin/commands/masterha.js +36 -0
- package/dist/bin/commands/restart.js +45 -0
- package/dist/bin/commands/start.js +58 -0
- package/dist/bin/commands/stop.js +20 -0
- package/dist/bin/melo.js +26 -0
- package/dist/bin/utils/constants.js +28 -0
- package/dist/bin/utils/utils.js +134 -0
- package/dist/lib/application.js +888 -0
- package/dist/lib/common/manager/appManager.js +112 -0
- package/dist/lib/common/manager/taskManager.js +39 -0
- package/dist/lib/common/remote/backend/msgRemote.js +63 -0
- package/dist/lib/common/remote/frontend/channelRemote.js +78 -0
- package/dist/lib/common/remote/frontend/sessionRemote.js +76 -0
- package/dist/lib/common/service/backendSessionService.js +337 -0
- package/dist/lib/common/service/channelService.js +514 -0
- package/dist/lib/common/service/connectionService.js +95 -0
- package/dist/lib/common/service/filterService.js +112 -0
- package/dist/lib/common/service/handlerService.js +187 -0
- package/dist/lib/common/service/sessionService.js +610 -0
- package/dist/lib/components/backendSession.js +14 -0
- package/dist/lib/components/channel.js +13 -0
- package/dist/lib/components/connection.js +12 -0
- package/dist/lib/components/connector.js +437 -0
- package/dist/lib/components/dictionary.js +93 -0
- package/dist/lib/components/master.js +39 -0
- package/dist/lib/components/monitor.js +25 -0
- package/dist/lib/components/protobuf.js +156 -0
- package/dist/lib/components/proxy.js +236 -0
- package/dist/lib/components/pushScheduler.js +62 -0
- package/dist/lib/components/remote.js +127 -0
- package/dist/lib/components/server.js +63 -0
- package/dist/lib/components/session.js +20 -0
- package/dist/lib/connectors/commands/handshake.js +119 -0
- package/dist/lib/connectors/commands/heartbeat.js +67 -0
- package/dist/lib/connectors/commands/kick.js +15 -0
- package/dist/lib/connectors/common/coder.js +90 -0
- package/dist/lib/connectors/common/handler.js +57 -0
- package/dist/lib/connectors/hybrid/IHybridSocket.js +3 -0
- package/dist/lib/connectors/hybrid/switcher.js +100 -0
- package/dist/lib/connectors/hybrid/tcpprocessor.js +40 -0
- package/dist/lib/connectors/hybrid/tcpsocket.js +171 -0
- package/dist/lib/connectors/hybrid/wsprocessor.js +49 -0
- package/dist/lib/connectors/hybridconnector.js +89 -0
- package/dist/lib/connectors/hybridsocket.js +139 -0
- package/dist/lib/connectors/mqtt/generate.js +113 -0
- package/dist/lib/connectors/mqtt/mqttadaptor.js +81 -0
- package/dist/lib/connectors/mqtt/protocol.js +48 -0
- package/dist/lib/connectors/mqttconnector.js +107 -0
- package/dist/lib/connectors/mqttsocket.js +59 -0
- package/dist/lib/connectors/sioconnector.js +135 -0
- package/dist/lib/connectors/siosocket.js +69 -0
- package/dist/lib/connectors/udpconnector.js +76 -0
- package/dist/lib/connectors/udpsocket.js +93 -0
- package/dist/lib/filters/handler/serial.js +44 -0
- package/dist/lib/filters/handler/time.js +32 -0
- package/dist/lib/filters/handler/timeout.js +45 -0
- package/dist/lib/filters/handler/toobusy.js +36 -0
- package/dist/lib/filters/rpc/rpcLog.js +43 -0
- package/dist/lib/filters/rpc/toobusy.js +41 -0
- package/dist/lib/index.js +81 -0
- package/dist/lib/interfaces/IComponent.js +3 -0
- package/dist/lib/interfaces/IConnector.js +3 -0
- package/dist/lib/interfaces/IHandlerFilter.js +3 -0
- package/dist/lib/interfaces/ILifeCycle.js +3 -0
- package/dist/lib/interfaces/IPlugin.js +3 -0
- package/dist/lib/interfaces/IPushScheduler.js +3 -0
- package/dist/lib/interfaces/ISocket.js +3 -0
- package/dist/lib/interfaces/IStore.js +3 -0
- package/dist/lib/interfaces/define.js +3 -0
- package/dist/lib/master/master.js +129 -0
- package/dist/lib/master/starter.js +236 -0
- package/dist/lib/master/watchdog.js +120 -0
- package/dist/lib/melo.js +125 -0
- package/dist/lib/modules/console.js +436 -0
- package/dist/lib/modules/masterwatcher.js +98 -0
- package/dist/lib/modules/monitorwatcher.js +124 -0
- package/dist/lib/modules/onlineUser.js +69 -0
- package/dist/lib/modules/restartNotifyModule.js +107 -0
- package/dist/lib/modules/watchServer.js +737 -0
- package/dist/lib/monitor/monitor.js +80 -0
- package/dist/lib/pushSchedulers/buffer.js +96 -0
- package/dist/lib/pushSchedulers/direct.js +58 -0
- package/dist/lib/pushSchedulers/multi.js +80 -0
- package/dist/lib/server/server.js +500 -0
- package/dist/lib/util/appUtil.js +306 -0
- package/dist/lib/util/constants.js +117 -0
- package/dist/lib/util/countDownLatch.js +51 -0
- package/dist/lib/util/events.js +20 -0
- package/dist/lib/util/handlerHelper.js +8 -0
- package/dist/lib/util/log.js +14 -0
- package/dist/lib/util/moduleUtil.js +101 -0
- package/dist/lib/util/pathUtil.js +134 -0
- package/dist/lib/util/remoterHelper.js +8 -0
- package/dist/lib/util/utils.js +358 -0
- package/dist/test/application.js +522 -0
- package/dist/test/config/log4js.json +28 -0
- package/dist/test/config/master.json +9 -0
- package/dist/test/config/servers.json +6 -0
- package/dist/test/filters/handler/serial.js +41 -0
- package/dist/test/filters/handler/time.js +41 -0
- package/dist/test/filters/handler/timeout.js +41 -0
- package/dist/test/filters/handler/toobusy.js +57 -0
- package/dist/test/filters/rpc/rpcLog.js +22 -0
- package/dist/test/filters/rpc/toobusy.js +39 -0
- package/dist/test/manager/mockChannelManager.js +77 -0
- package/dist/test/manager/taskManager.js +68 -0
- package/dist/test/mock-base/app/servers/other-file +0 -0
- package/dist/test/mock-plugin/components/mockPlugin.js +10 -0
- package/dist/test/mock-plugin/events/mockEvent.js +12 -0
- package/dist/test/modules/console.js +242 -0
- package/dist/test/pomelo.js +19 -0
- package/dist/test/remote/channelRemote.js +159 -0
- package/dist/test/service/channel.js +134 -0
- package/dist/test/service/channelService.js +216 -0
- package/dist/test/service/connectionService.js +114 -0
- package/dist/test/service/filterService.js +144 -0
- package/dist/test/service/handlerService.js +65 -0
- package/dist/test/service/sessionService.js +387 -0
- package/dist/test/util/countDownLatch.js +70 -0
- package/dist/test/util/pathUtil.js +108 -0
- package/dist/test/util/utils.js +140 -0
- package/lib/application.ts +1240 -0
- package/lib/common/manager/appManager.ts +118 -0
- package/lib/common/manager/taskManager.ts +50 -0
- package/lib/common/remote/backend/msgRemote.ts +134 -0
- package/lib/common/remote/frontend/channelRemote.ts +91 -0
- package/lib/common/remote/frontend/sessionRemote.ts +91 -0
- package/lib/common/service/backendSessionService.ts +388 -0
- package/lib/common/service/channelService.ts +609 -0
- package/lib/common/service/connectionService.ts +112 -0
- package/lib/common/service/filterService.ts +118 -0
- package/lib/common/service/handlerService.ts +224 -0
- package/lib/common/service/sessionService.ts +731 -0
- package/lib/components/backendSession.ts +14 -0
- package/lib/components/channel.ts +11 -0
- package/lib/components/connection.ts +13 -0
- package/lib/components/connector.ts +533 -0
- package/lib/components/dictionary.ts +121 -0
- package/lib/components/master.ts +41 -0
- package/lib/components/monitor.ts +30 -0
- package/lib/components/protobuf.ts +208 -0
- package/lib/components/proxy.ts +282 -0
- package/lib/components/pushScheduler.ts +70 -0
- package/lib/components/remote.ts +166 -0
- package/lib/components/server.ts +71 -0
- package/lib/components/session.ts +22 -0
- package/lib/connectors/commands/handshake.ts +155 -0
- package/lib/connectors/commands/heartbeat.ts +83 -0
- package/lib/connectors/commands/kick.ts +11 -0
- package/lib/connectors/common/coder.ts +93 -0
- package/lib/connectors/common/handler.ts +62 -0
- package/lib/connectors/hybrid/IHybridSocket.ts +9 -0
- package/lib/connectors/hybrid/switcher.ts +142 -0
- package/lib/connectors/hybrid/tcpprocessor.ts +43 -0
- package/lib/connectors/hybrid/tcpsocket.ts +223 -0
- package/lib/connectors/hybrid/wsprocessor.ts +57 -0
- package/lib/connectors/hybridconnector.ts +134 -0
- package/lib/connectors/hybridsocket.ts +168 -0
- package/lib/connectors/mqtt/generate.ts +103 -0
- package/lib/connectors/mqtt/mqttadaptor.ts +114 -0
- package/lib/connectors/mqtt/protocol.ts +49 -0
- package/lib/connectors/mqttconnector.ts +134 -0
- package/lib/connectors/mqttsocket.ts +79 -0
- package/lib/connectors/sioconnector.ts +161 -0
- package/lib/connectors/siosocket.ts +85 -0
- package/lib/connectors/udpconnector.ts +113 -0
- package/lib/connectors/udpsocket.ts +110 -0
- package/lib/filters/handler/serial.ts +46 -0
- package/lib/filters/handler/time.ts +35 -0
- package/lib/filters/handler/timeout.ts +50 -0
- package/lib/filters/handler/toobusy.ts +37 -0
- package/lib/filters/rpc/rpcLog.ts +42 -0
- package/lib/filters/rpc/toobusy.ts +41 -0
- package/lib/index.ts +74 -0
- package/lib/interfaces/IComponent.ts +47 -0
- package/lib/interfaces/IConnector.ts +20 -0
- package/lib/interfaces/IHandlerFilter.ts +15 -0
- package/lib/interfaces/ILifeCycle.ts +16 -0
- package/lib/interfaces/IPlugin.ts +65 -0
- package/lib/interfaces/IPushScheduler.ts +52 -0
- package/lib/interfaces/ISocket.ts +26 -0
- package/lib/interfaces/IStore.ts +10 -0
- package/lib/interfaces/define.ts +15 -0
- package/lib/master/master.ts +148 -0
- package/lib/master/starter.ts +234 -0
- package/lib/master/watchdog.ts +135 -0
- package/lib/melo.ts +152 -0
- package/lib/modules/console.ts +465 -0
- package/lib/modules/masterwatcher.ts +120 -0
- package/lib/modules/monitorwatcher.ts +151 -0
- package/lib/modules/onlineUser.ts +78 -0
- package/lib/modules/restartNotifyModule.ts +128 -0
- package/lib/modules/watchServer.ts +766 -0
- package/lib/monitor/monitor.ts +99 -0
- package/lib/pushSchedulers/buffer.ts +117 -0
- package/lib/pushSchedulers/direct.ts +65 -0
- package/lib/pushSchedulers/multi.ts +94 -0
- package/lib/server/server.ts +554 -0
- package/lib/util/appUtil.ts +313 -0
- package/lib/util/constants.ts +154 -0
- package/lib/util/countDownLatch.ts +72 -0
- package/lib/util/events.ts +15 -0
- package/lib/util/handlerHelper.ts +5 -0
- package/lib/util/log.ts +11 -0
- package/lib/util/moduleUtil.ts +110 -0
- package/lib/util/pathUtil.ts +132 -0
- package/lib/util/remoterHelper.ts +68 -0
- package/lib/util/utils.ts +365 -0
- package/package.json +93 -0
- package/template/game-server/.vscode/launch.json +27 -0
- package/template/game-server/app/servers/connector/handler/entryHandler.ts +50 -0
- package/template/game-server/app/servers/connector/remote/authRemoter.ts +36 -0
- package/template/game-server/app.ts +30 -0
- package/template/game-server/app.ts.mqtt +32 -0
- package/template/game-server/app.ts.sio +36 -0
- package/template/game-server/app.ts.sio.wss +34 -0
- package/template/game-server/app.ts.udp +31 -0
- package/template/game-server/app.ts.wss +40 -0
- package/template/game-server/config/adminServer.json +5 -0
- package/template/game-server/config/adminUser.json +22 -0
- package/template/game-server/config/clientProtos.json +1 -0
- package/template/game-server/config/dictionary.json +1 -0
- package/template/game-server/config/log4js.json +150 -0
- package/template/game-server/config/master.json +8 -0
- package/template/game-server/config/serverProtos.json +1 -0
- package/template/game-server/config/servers.json +12 -0
- package/template/game-server/copy.js +5 -0
- package/template/game-server/package.json +28 -0
- package/template/game-server/preload.ts +40 -0
- package/template/game-server/tsconfig.json +36 -0
- package/template/npm-install.bat +4 -0
- package/template/npm-install.sh +5 -0
- package/template/readme.md +31 -0
- package/template/shared/server.crt +15 -0
- package/template/shared/server.key +15 -0
- package/template/web-server/app.js +25 -0
- package/template/web-server/app.js.https +36 -0
- package/template/web-server/bin/component.bat +1 -0
- package/template/web-server/bin/component.sh +1 -0
- package/template/web-server/package.json +10 -0
- package/template/web-server/public/css/base.css +76 -0
- package/template/web-server/public/image/logo.png +0 -0
- package/template/web-server/public/image/sp.png +0 -0
- package/template/web-server/public/index.html +57 -0
- package/template/web-server/public/index.html.sio +58 -0
- package/template/web-server/public/js/lib/build/build.js +1730 -0
- package/template/web-server/public/js/lib/build/build.js.wss +1721 -0
- package/template/web-server/public/js/lib/component.json +6 -0
- package/template/web-server/public/js/lib/local/boot/component.json +11 -0
- package/template/web-server/public/js/lib/local/boot/index.js +11 -0
- package/template/web-server/public/js/lib/meloclient.js +456 -0
- package/template/web-server/public/js/lib/meloclient.js.wss +456 -0
- package/template/web-server/public/js/lib/socket.io.js +3 -0
- package/test/application.ts +607 -0
- package/test/filters/handler/serial.ts +47 -0
- package/test/filters/handler/time.ts +47 -0
- package/test/filters/handler/timeout.ts +46 -0
- package/test/filters/handler/toobusy.ts +59 -0
- package/test/filters/rpc/rpcLog.ts +20 -0
- package/test/filters/rpc/toobusy.ts +40 -0
- package/test/manager/mockChannelManager.ts +92 -0
- package/test/manager/taskManager.ts +78 -0
- package/test/mock-base/app/servers/other-file +0 -0
- package/test/mock-plugin/components/mockPlugin.ts +8 -0
- package/test/mock-plugin/events/mockEvent.ts +12 -0
- package/test/modules/console.ts +264 -0
- package/test/pomelo.ts +18 -0
- package/test/remote/channelRemote.ts +176 -0
- package/test/service/channel.ts +161 -0
- package/test/service/channelService.ts +243 -0
- package/test/service/connectionService.ts +142 -0
- package/test/service/filterService.ts +165 -0
- package/test/service/handlerService.ts +77 -0
- package/test/service/sessionService.ts +464 -0
- package/test/util/countDownLatch.ts +81 -0
- package/test/util/pathUtil.ts +122 -0
- package/test/util/utils.ts +165 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import * as util from 'util';
|
|
2
|
+
import * as net from 'net';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
import { default as handler } from './common/handler';
|
|
5
|
+
import { Package } from '@bigtyphoon/melo-protocol';
|
|
6
|
+
import { getLogger } from '@bigtyphoon/melo-logger';
|
|
7
|
+
import { ISocket } from '../interfaces/ISocket';
|
|
8
|
+
import * as WebSocket from 'ws';
|
|
9
|
+
import { TcpSocket } from './hybrid/tcpsocket';
|
|
10
|
+
import { IHybridSocket } from './hybrid/IHybridSocket';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
|
|
13
|
+
let logger = getLogger('melo', path.basename(__filename));
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
let ST_INITED = 0;
|
|
17
|
+
let ST_WAIT_ACK = 1;
|
|
18
|
+
let ST_WORKING = 2;
|
|
19
|
+
let ST_CLOSED = 3;
|
|
20
|
+
|
|
21
|
+
export interface HybridSocketOptions {
|
|
22
|
+
realIPKey?: string; // 代理过后真实客户端ip获取字段 Header name must be lower-cased.
|
|
23
|
+
realPortKey?: string; // 代理过后真实客户端port获取字段 Header name must be lower-cased.
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Socket class that wraps socket and websocket to provide unified interface for up level.
|
|
28
|
+
*/
|
|
29
|
+
export class HybridSocket extends EventEmitter implements ISocket {
|
|
30
|
+
id: number;
|
|
31
|
+
socket: IHybridSocket;
|
|
32
|
+
remoteAddress: { ip: string, port: number };
|
|
33
|
+
state: number;
|
|
34
|
+
|
|
35
|
+
constructor(id: number, socket: IHybridSocket, request: any, opts: HybridSocketOptions) {
|
|
36
|
+
super();
|
|
37
|
+
this.id = id;
|
|
38
|
+
this.socket = socket;
|
|
39
|
+
|
|
40
|
+
if (request && (opts.realIPKey || opts.realPortKey)) {
|
|
41
|
+
let ip = request['headers'][opts.realIPKey];
|
|
42
|
+
if (ip) {
|
|
43
|
+
this.remoteAddress = {
|
|
44
|
+
ip: ip,
|
|
45
|
+
port: opts.realPortKey ? request['headers'][opts.realPortKey] : 0
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!this.remoteAddress) {
|
|
50
|
+
if (!(socket as TcpSocket)._socket) {
|
|
51
|
+
this.remoteAddress = {
|
|
52
|
+
ip: (socket as any).address().address,
|
|
53
|
+
port: (socket as any).address().port
|
|
54
|
+
};
|
|
55
|
+
} else {
|
|
56
|
+
this.remoteAddress = {
|
|
57
|
+
ip: (socket as TcpSocket)._socket.remoteAddress,
|
|
58
|
+
port: (socket as TcpSocket)._socket.remotePort
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let self = this;
|
|
64
|
+
|
|
65
|
+
socket.once('close', this.emit.bind(this, 'disconnect'));
|
|
66
|
+
socket.on('error', this.emit.bind(this, 'error'));
|
|
67
|
+
|
|
68
|
+
socket.on('message', function (msg) {
|
|
69
|
+
if (msg) {
|
|
70
|
+
msg = Package.decode(msg);
|
|
71
|
+
handler(self, msg);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this.state = ST_INITED;
|
|
76
|
+
|
|
77
|
+
// TODO: any other events?
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Send raw byte data.
|
|
83
|
+
*
|
|
84
|
+
* @api private
|
|
85
|
+
*/
|
|
86
|
+
sendRaw(msg: any) {
|
|
87
|
+
if (this.state !== ST_WORKING) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
let self = this;
|
|
91
|
+
|
|
92
|
+
this.socket.send(msg, { binary: true }, (err) => {
|
|
93
|
+
if (!!err) {
|
|
94
|
+
logger.error('websocket send binary data failed: %j', err.stack);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Send byte data package to client.
|
|
102
|
+
*
|
|
103
|
+
* @param {Buffer} msg byte data
|
|
104
|
+
*/
|
|
105
|
+
send(msg: any) {
|
|
106
|
+
if (msg instanceof String) {
|
|
107
|
+
msg = Buffer.from(msg as string);
|
|
108
|
+
} else if (!(msg instanceof Buffer)) {
|
|
109
|
+
msg = Buffer.from(JSON.stringify(msg));
|
|
110
|
+
}
|
|
111
|
+
this.sendRaw(Package.encode(Package.TYPE_DATA, msg));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Send byte data packages to client in batch.
|
|
116
|
+
*
|
|
117
|
+
* @param {Buffer} msgs byte data
|
|
118
|
+
*/
|
|
119
|
+
sendBatch(msgs: any[]) {
|
|
120
|
+
let rs = [];
|
|
121
|
+
for (let i = 0; i < msgs.length; i++) {
|
|
122
|
+
let src = Package.encode(Package.TYPE_DATA, msgs[i]);
|
|
123
|
+
rs.push(src);
|
|
124
|
+
}
|
|
125
|
+
this.sendRaw(Buffer.concat(rs));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Send message to client no matter whether handshake.
|
|
130
|
+
*
|
|
131
|
+
* @api private
|
|
132
|
+
*/
|
|
133
|
+
sendForce(msg: any) {
|
|
134
|
+
if (this.state === ST_CLOSED) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.socket.send(msg, { binary: true });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Response handshake request
|
|
142
|
+
*
|
|
143
|
+
* @api private
|
|
144
|
+
*/
|
|
145
|
+
handshakeResponse(resp: any) {
|
|
146
|
+
if (this.state !== ST_INITED) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
this.socket.send(resp, { binary: true });
|
|
151
|
+
this.state = ST_WAIT_ACK;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Close the connection.
|
|
156
|
+
*
|
|
157
|
+
* @api private
|
|
158
|
+
*/
|
|
159
|
+
disconnect() {
|
|
160
|
+
if (this.state === ST_CLOSED) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
this.state = ST_CLOSED;
|
|
165
|
+
this.socket.emit('close');
|
|
166
|
+
this.socket.close();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as protocol from './protocol';
|
|
2
|
+
import * as crypto from 'crypto';
|
|
3
|
+
|
|
4
|
+
/* TODO: consider rewriting these functions using buffers instead
|
|
5
|
+
* of arrays
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* Publish */
|
|
9
|
+
export function publish(opts: any) {
|
|
10
|
+
opts = opts || {};
|
|
11
|
+
let dup = opts.dup ? protocol.DUP_MASK : 0;
|
|
12
|
+
let qos = opts.qos || 0;
|
|
13
|
+
let retain = opts.retain ? protocol.RETAIN_MASK : 0;
|
|
14
|
+
let topic = opts.topic;
|
|
15
|
+
let payload = opts.payload || Buffer.alloc(0);
|
|
16
|
+
let id = (typeof opts.messageId === 'undefined') ? randint() : opts.messageId;
|
|
17
|
+
let packet = { header: 0, payload: [] as any[] };
|
|
18
|
+
|
|
19
|
+
/* Check required fields */
|
|
20
|
+
if (typeof topic !== 'string' || topic.length <= 0) return null;
|
|
21
|
+
/* if payload is a string, we'll convert it into a buffer */
|
|
22
|
+
if (typeof payload === 'string') {
|
|
23
|
+
payload = Buffer.from(payload);
|
|
24
|
+
}
|
|
25
|
+
/* accepting only a buffer for payload */
|
|
26
|
+
if (!Buffer.isBuffer(payload)) return null;
|
|
27
|
+
if (typeof qos !== 'number' || qos < 0 || qos > 2) return null;
|
|
28
|
+
if (typeof id !== 'number' || id < 0 || id > 0xFFFF) return null;
|
|
29
|
+
|
|
30
|
+
/* Generate header */
|
|
31
|
+
packet.header = protocol.codes.publish << protocol.CMD_SHIFT | dup | qos << protocol.QOS_SHIFT | retain;
|
|
32
|
+
|
|
33
|
+
/* Topic name */
|
|
34
|
+
packet.payload = packet.payload.concat(gen_string(topic));
|
|
35
|
+
|
|
36
|
+
/* Message ID */
|
|
37
|
+
if (qos > 0) packet.payload = packet.payload.concat(gen_number(id));
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
let buf = Buffer.from([packet.header]
|
|
41
|
+
.concat(gen_length(packet.payload.length + payload.length))
|
|
42
|
+
.concat(packet.payload));
|
|
43
|
+
|
|
44
|
+
return Buffer.concat([buf, payload]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Requires length be a number > 0 */
|
|
48
|
+
let gen_length = function (length: number) {
|
|
49
|
+
if (typeof length !== 'number') return null;
|
|
50
|
+
if (length < 0) return null;
|
|
51
|
+
|
|
52
|
+
let len = [];
|
|
53
|
+
let digit = 0;
|
|
54
|
+
|
|
55
|
+
do {
|
|
56
|
+
digit = length % 128 | 0;
|
|
57
|
+
length = length / 128 | 0;
|
|
58
|
+
if (length > 0) {
|
|
59
|
+
digit = digit | 0x80;
|
|
60
|
+
}
|
|
61
|
+
len.push(digit);
|
|
62
|
+
} while (length > 0);
|
|
63
|
+
|
|
64
|
+
return len;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
let gen_string = function (str: string, without_length ?: boolean) { /* based on code in (from http://farhadi.ir/downloads/utf8.js) */
|
|
68
|
+
if (arguments.length < 2) without_length = false;
|
|
69
|
+
if (typeof str !== 'string') return null;
|
|
70
|
+
if (typeof without_length !== 'boolean') return null;
|
|
71
|
+
|
|
72
|
+
let nums: number[] = [];
|
|
73
|
+
let length = 0;
|
|
74
|
+
for (let i = 0; i < str.length; i++) {
|
|
75
|
+
let code = str.charCodeAt(i);
|
|
76
|
+
if (code < 128) {
|
|
77
|
+
nums.push(code); ++length;
|
|
78
|
+
|
|
79
|
+
} else if (code < 2048) {
|
|
80
|
+
nums.push(192 + ((code >> 6))); ++length;
|
|
81
|
+
nums.push(128 + ((code) & 63)); ++length;
|
|
82
|
+
} else if (code < 65536) {
|
|
83
|
+
nums.push(224 + ((code >> 12))); ++length;
|
|
84
|
+
nums.push(128 + ((code >> 6) & 63)); ++length;
|
|
85
|
+
nums.push(128 + ((code) & 63)); ++length;
|
|
86
|
+
} else if (code < 2097152) {
|
|
87
|
+
nums.push(240 + ((code >> 18))); ++length;
|
|
88
|
+
nums.push(128 + ((code >> 12) & 63)); ++length;
|
|
89
|
+
nums.push(128 + ((code >> 6) & 63)); ++length;
|
|
90
|
+
nums.push(128 + ((code) & 63)); ++length;
|
|
91
|
+
} else {
|
|
92
|
+
throw new Error('Can\'t encode character with code ' + code);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return without_length ? nums : gen_number(length).concat(nums);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
let gen_number = function (num: number) {
|
|
99
|
+
let nums: number[] = [num >> 8, num & 0x00FF];
|
|
100
|
+
return nums;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
let randint = function () { return Math.floor(Math.random() * 0xFFFF); };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import { MQTTSocket } from '../mqttsocket';
|
|
4
|
+
export interface MqttAdaptorOptions {
|
|
5
|
+
publishRoute ?: string;
|
|
6
|
+
subscribeRoute ?: string;
|
|
7
|
+
|
|
8
|
+
}
|
|
9
|
+
export interface SubscribePacket {
|
|
10
|
+
messageId: number;
|
|
11
|
+
subscriptions: { topic: string, qos: number }[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface PublishPacket {
|
|
15
|
+
topic: string;
|
|
16
|
+
payload: any;
|
|
17
|
+
messageId: number;
|
|
18
|
+
qos: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class MqttAdaptor {
|
|
22
|
+
subReqs: {[messageId: number]: SubscribePacket} = {};
|
|
23
|
+
publishRoute: string;
|
|
24
|
+
subscribeRoute: string;
|
|
25
|
+
constructor(opts ?: MqttAdaptorOptions) {
|
|
26
|
+
opts = opts || {};
|
|
27
|
+
this.publishRoute = opts.publishRoute;
|
|
28
|
+
this.subscribeRoute = opts.subscribeRoute;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
onPublish(client: MQTTSocket, packet: PublishPacket) {
|
|
32
|
+
let route = this.publishRoute;
|
|
33
|
+
|
|
34
|
+
if (!route) {
|
|
35
|
+
throw new Error('unspecified publish route.');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let payload = packet.payload;
|
|
39
|
+
if (payload instanceof Buffer) {
|
|
40
|
+
payload = payload.toString('utf8');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let req = {
|
|
44
|
+
id: packet.messageId,
|
|
45
|
+
route: route,
|
|
46
|
+
body: packet
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
client.emit('message', req);
|
|
50
|
+
|
|
51
|
+
if (packet.qos === 1) {
|
|
52
|
+
client.socket.puback({ messageId: packet.messageId });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
onSubscribe(client: MQTTSocket, packet: SubscribePacket) {
|
|
57
|
+
let route = this.subscribeRoute;
|
|
58
|
+
|
|
59
|
+
if (!route) {
|
|
60
|
+
throw new Error('unspecified subscribe route.');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let req = {
|
|
64
|
+
id: packet.messageId,
|
|
65
|
+
route: route,
|
|
66
|
+
body: {
|
|
67
|
+
subscriptions: packet.subscriptions
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
this.subReqs[packet.messageId] = packet;
|
|
72
|
+
|
|
73
|
+
client.emit('message', req);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
onPubAck(client: MQTTSocket, packet: SubscribePacket) {
|
|
77
|
+
let req = {
|
|
78
|
+
id: packet.messageId,
|
|
79
|
+
route: 'connector.mqttHandler.pubAck',
|
|
80
|
+
body: {
|
|
81
|
+
mid: packet.messageId
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
this.subReqs[packet.messageId] = packet;
|
|
86
|
+
|
|
87
|
+
client.emit('message', req);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Publish message or subscription ack.
|
|
92
|
+
*
|
|
93
|
+
* if packet.id exist and this.subReqs[packet.id] exist then packet is a suback.
|
|
94
|
+
* Subscription is request/response mode.
|
|
95
|
+
* packet.id is pass from client in packet.messageId and record in Melo context and attached to the subscribe response packet.
|
|
96
|
+
* packet.body is the context that returned by subscribe next callback.
|
|
97
|
+
*
|
|
98
|
+
* if packet.id not exist then packet is a publish message.
|
|
99
|
+
*
|
|
100
|
+
* otherwise packet is a illegal packet.
|
|
101
|
+
*/
|
|
102
|
+
publish(client: MQTTSocket, packet: {id: number , body: any}) {
|
|
103
|
+
let mid = packet.id;
|
|
104
|
+
let subreq = this.subReqs[mid];
|
|
105
|
+
if (subreq) {
|
|
106
|
+
// is suback
|
|
107
|
+
client.socket.suback({ messageId: mid, granted: packet.body });
|
|
108
|
+
delete this.subReqs[mid];
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
client.socket.publish(packet.body);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* Protocol - protocol constants */
|
|
2
|
+
|
|
3
|
+
/* Command code => mnemonic */
|
|
4
|
+
export let types = {
|
|
5
|
+
0: 'reserved',
|
|
6
|
+
1: 'connect',
|
|
7
|
+
2: 'connack',
|
|
8
|
+
3: 'publish',
|
|
9
|
+
4: 'puback',
|
|
10
|
+
5: 'pubrec',
|
|
11
|
+
6: 'pubrel',
|
|
12
|
+
7: 'pubcomp',
|
|
13
|
+
8: 'subscribe',
|
|
14
|
+
9: 'suback',
|
|
15
|
+
10: 'unsubscribe',
|
|
16
|
+
11: 'unsuback',
|
|
17
|
+
12: 'pingreq',
|
|
18
|
+
13: 'pingresp',
|
|
19
|
+
14: 'disconnect',
|
|
20
|
+
15: 'reserved'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/* Mnemonic => Command code */
|
|
24
|
+
export let codes: {[key: string]: number} = {};
|
|
25
|
+
for(let k in types) {
|
|
26
|
+
let v = (types as any)[k];
|
|
27
|
+
codes[v] = Number(k);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Header */
|
|
31
|
+
export let CMD_SHIFT = 4;
|
|
32
|
+
export let CMD_MASK = 0xF0;
|
|
33
|
+
export let DUP_MASK = 0x08;
|
|
34
|
+
export let QOS_MASK = 0x03;
|
|
35
|
+
export let QOS_SHIFT = 1;
|
|
36
|
+
export let RETAIN_MASK = 0x01;
|
|
37
|
+
|
|
38
|
+
/* Length */
|
|
39
|
+
export let LENGTH_MASK = 0x7F;
|
|
40
|
+
export let LENGTH_FIN_MASK = 0x80;
|
|
41
|
+
|
|
42
|
+
/* Connect */
|
|
43
|
+
export let USERNAME_MASK = 0x80;
|
|
44
|
+
export let PASSWORD_MASK = 0x40;
|
|
45
|
+
export let WILL_RETAIN_MASK = 0x20;
|
|
46
|
+
export let WILL_QOS_MASK = 0x18;
|
|
47
|
+
export let WILL_QOS_SHIFT = 3;
|
|
48
|
+
export let WILL_FLAG_MASK = 0x04;
|
|
49
|
+
export let CLEAN_SESSION_MASK = 0x02;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import * as util from 'util';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import * as net from 'net';
|
|
4
|
+
import * as constants from '../util/constants';
|
|
5
|
+
import { MQTTSocket } from './mqttsocket';
|
|
6
|
+
import { MqttAdaptor } from './mqtt/mqttadaptor';
|
|
7
|
+
import * as generate from './mqtt/generate';
|
|
8
|
+
import { getLogger } from '@bigtyphoon/melo-logger';
|
|
9
|
+
import { IConnector } from '../interfaces/IConnector';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { MqttConnectionConstructor } from '@bigtyphoon/melo-admin';
|
|
12
|
+
let logger = getLogger('melo', path.basename(__filename));
|
|
13
|
+
const mqtt_connection: MqttConnectionConstructor = require('mqtt-connection');
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export interface MQTTConnectorOptions {
|
|
17
|
+
publishRoute ?: string;
|
|
18
|
+
subscribeRoute ?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let curId = 1;
|
|
22
|
+
/**
|
|
23
|
+
* Connector that manager low level connection and protocol bewteen server and client.
|
|
24
|
+
* Develper can provide their own connector to switch the low level prototol, such as tcp or probuf.
|
|
25
|
+
*/
|
|
26
|
+
export class MQTTConnector extends EventEmitter implements IConnector {
|
|
27
|
+
|
|
28
|
+
port: number;
|
|
29
|
+
host: string;
|
|
30
|
+
opts: any;
|
|
31
|
+
adaptor: MqttAdaptor;
|
|
32
|
+
server: net.Server;
|
|
33
|
+
constructor(port: number, host: string, opts ?: MQTTConnectorOptions) {
|
|
34
|
+
super();
|
|
35
|
+
this.port = port;
|
|
36
|
+
this.host = host;
|
|
37
|
+
this.opts = opts || {};
|
|
38
|
+
|
|
39
|
+
this.adaptor = new MqttAdaptor(this.opts);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Start connector to listen the specified port
|
|
44
|
+
*/
|
|
45
|
+
start(cb: () => void) {
|
|
46
|
+
let self = this;
|
|
47
|
+
this.server = new net.Server();
|
|
48
|
+
this.server.listen(this.port);
|
|
49
|
+
logger.info('[MQTTConnector] listen on %d', this.port);
|
|
50
|
+
|
|
51
|
+
this.server.on('error', function (err) {
|
|
52
|
+
// logger.error('mqtt server is error: %j', err.stack);
|
|
53
|
+
self.emit('error', err);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
this.server.on('connection', (stream) => {
|
|
57
|
+
let client = mqtt_connection(stream);
|
|
58
|
+
|
|
59
|
+
client.on('error', function (err: Error) {
|
|
60
|
+
client.destroy();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
client.on('close', function () {
|
|
64
|
+
client.destroy();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
client.on('disconnect', function (packet: any) {
|
|
68
|
+
client.destroy();
|
|
69
|
+
});
|
|
70
|
+
// stream timeout
|
|
71
|
+
stream.on('timeout', function () { client.destroy(); });
|
|
72
|
+
// client published
|
|
73
|
+
client.on('publish', function (packet: any) {
|
|
74
|
+
// send a puback with messageId (for QoS > 0)
|
|
75
|
+
client.puback({ messageId: packet.messageId });
|
|
76
|
+
});
|
|
77
|
+
// client pinged
|
|
78
|
+
client.on('pingreq', function () {
|
|
79
|
+
// send a pingresp
|
|
80
|
+
client.pingresp();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (self.opts.disconnectOnTimeout) {
|
|
84
|
+
let timeout = self.opts.timeout * 1000 || constants.TIME.DEFAULT_MQTT_HEARTBEAT_TIMEOUT;
|
|
85
|
+
stream.setTimeout(timeout, function () {
|
|
86
|
+
client.destroy();
|
|
87
|
+
client.emit('close');
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
client.on('connect', function (packet: any) {
|
|
92
|
+
client.connack({ returnCode: 0 });
|
|
93
|
+
let mqttsocket = new MQTTSocket(curId++, client, self.adaptor);
|
|
94
|
+
self.emit('connection', mqttsocket);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
process.nextTick(cb);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
stop() {
|
|
103
|
+
this.server.close();
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
encode(reqId: number, route: string, msgBody: any) {
|
|
109
|
+
if (!!reqId) {
|
|
110
|
+
return composeResponse(reqId, route, msgBody);
|
|
111
|
+
} else {
|
|
112
|
+
return composePush(route, msgBody);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
close() {
|
|
117
|
+
this.server.close();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
let composeResponse = function (msgId: number, route: string, msgBody: any) {
|
|
121
|
+
return {
|
|
122
|
+
id: msgId,
|
|
123
|
+
body: msgBody
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
let composePush = function (route: string, msgBody: any) {
|
|
128
|
+
let msg = generate.publish(msgBody);
|
|
129
|
+
if (!msg) {
|
|
130
|
+
logger.error('invalid mqtt publish message: %j', msgBody);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return msg;
|
|
134
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as util from 'util';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { ISocket } from '../interfaces/ISocket';
|
|
4
|
+
import { MqttAdaptor } from './mqtt/mqttadaptor';
|
|
5
|
+
import { MqttConnection } from '@bigtyphoon/melo-admin';
|
|
6
|
+
|
|
7
|
+
let ST_INITED = 1;
|
|
8
|
+
let ST_CLOSED = 2;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Socket class that wraps socket and websocket to provide unified interface for up level.
|
|
12
|
+
*/
|
|
13
|
+
export class MQTTSocket extends EventEmitter implements ISocket {
|
|
14
|
+
id: number;
|
|
15
|
+
socket: MqttConnection;
|
|
16
|
+
remoteAddress: { ip: string, port: number };
|
|
17
|
+
adaptor: MqttAdaptor;
|
|
18
|
+
|
|
19
|
+
state: number;
|
|
20
|
+
|
|
21
|
+
constructor(id: number, socket: MqttConnection, adaptor: MqttAdaptor) {
|
|
22
|
+
super();
|
|
23
|
+
this.id = id;
|
|
24
|
+
this.socket = socket;
|
|
25
|
+
this.remoteAddress = {
|
|
26
|
+
ip: socket.stream.remoteAddress,
|
|
27
|
+
port: socket.stream.remotePort
|
|
28
|
+
};
|
|
29
|
+
this.adaptor = adaptor;
|
|
30
|
+
|
|
31
|
+
let self = this;
|
|
32
|
+
|
|
33
|
+
socket.on('close', this.emit.bind(this, 'disconnect'));
|
|
34
|
+
socket.on('error', this.emit.bind(this, 'disconnect'));
|
|
35
|
+
socket.on('disconnect', this.emit.bind(this, 'disconnect'));
|
|
36
|
+
|
|
37
|
+
socket.on('pingreq', function (packet: any) {
|
|
38
|
+
socket.pingresp();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
socket.on('subscribe', this.adaptor.onSubscribe.bind(this.adaptor, this));
|
|
42
|
+
|
|
43
|
+
socket.on('publish', this.adaptor.onPublish.bind(this.adaptor, this));
|
|
44
|
+
|
|
45
|
+
this.state = ST_INITED;
|
|
46
|
+
|
|
47
|
+
// TODO: any other events?
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
send(msg: any) {
|
|
52
|
+
if (this.state !== ST_INITED) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (msg instanceof Buffer) {
|
|
56
|
+
// if encoded, send directly
|
|
57
|
+
this.socket.stream.write(msg);
|
|
58
|
+
} else {
|
|
59
|
+
this.adaptor.publish(this, msg);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
sendRaw = this.send;
|
|
64
|
+
|
|
65
|
+
sendBatch(msgs: any[]) {
|
|
66
|
+
for (let i = 0, l = msgs.length; i < l; i++) {
|
|
67
|
+
this.send(msgs[i]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
disconnect() {
|
|
72
|
+
if (this.state === ST_CLOSED) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.state = ST_CLOSED;
|
|
77
|
+
this.socket.stream.destroy();
|
|
78
|
+
}
|
|
79
|
+
}
|