perf: optimize websocket logic
Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
parent
f31726e98b
commit
af1e64767a
@ -1,5 +0,0 @@
|
||||
export interface InnerClient {
|
||||
id: string
|
||||
send(text: string)
|
||||
close()
|
||||
}
|
@ -12,8 +12,12 @@ export abstract class WebSocketHandlerAdapter {
|
||||
this._Handler = new ChannelInboundHandlerAdapterImpl()
|
||||
}
|
||||
abstract channelRead(ctx: any, channel: any)
|
||||
abstract channelInactive(ctx: any)
|
||||
abstract channelUnregistered(ctx: any)
|
||||
channelInactive(ctx: any) {
|
||||
ctx.fireChannelInactive()
|
||||
}
|
||||
channelUnregistered(ctx: any) {
|
||||
ctx.fireChannelUnregistered()
|
||||
}
|
||||
abstract exceptionCaught(ctx: any, cause: Error)
|
||||
getHandler() {
|
||||
return this._Handler
|
||||
|
@ -1,45 +1,24 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { InnerClient } from '../interfaces'
|
||||
import { Transport } from '../transport'
|
||||
import { AttributeKeys } from './constants'
|
||||
|
||||
const TextWebSocketFrame = Java.type('io.netty.handler.codec.http.websocketx.TextWebSocketFrame')
|
||||
|
||||
export class NettyClient extends EventEmitter implements InnerClient {
|
||||
private _id: string
|
||||
export class NettyClient extends Transport {
|
||||
private channel: any
|
||||
|
||||
server: any
|
||||
readyState: string
|
||||
remoteAddress: string
|
||||
upgraded: boolean
|
||||
request: any
|
||||
|
||||
constructor(server: any, channel: any) {
|
||||
super()
|
||||
this.server = server
|
||||
this.readyState = 'open'
|
||||
super(server)
|
||||
this.remoteAddress = channel.remoteAddress() + ''
|
||||
this.upgraded = true
|
||||
this.request = channel.attr(AttributeKeys.Request).get()
|
||||
|
||||
this.channel = channel
|
||||
this._id = channel.id() + ''
|
||||
this.channel = channel
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this._id
|
||||
doSend(text: string) {
|
||||
this.channel.writeAndFlush(new TextWebSocketFrame(text))
|
||||
}
|
||||
send(text: string) {
|
||||
if (this.readyState == 'open') {
|
||||
this.channel.writeAndFlush(new TextWebSocketFrame(text))
|
||||
} else {
|
||||
console.debug(`send message ${text} to close client ${this._id}`)
|
||||
}
|
||||
}
|
||||
close() {
|
||||
if (this.readyState = 'open') {
|
||||
this.channel.close()
|
||||
this.readyState = 'close'
|
||||
}
|
||||
doClose() {
|
||||
this.channel.close()
|
||||
}
|
||||
}
|
||||
|
@ -11,16 +11,6 @@ export class WebSocketDetect extends WebSocketHandlerAdapter {
|
||||
channelRead(ctx: any, channel: any) {
|
||||
this.event.emit(ServerEvent.detect, ctx, channel)
|
||||
}
|
||||
channelInactive(ctx: any) {
|
||||
console.debug('WebSocketDetect channelUnregistered ' + ctx)
|
||||
this.event.emit(ServerEvent.disconnect, ctx, 'client disconnect')
|
||||
ctx.channelInactive()
|
||||
}
|
||||
channelUnregistered(ctx: any) {
|
||||
console.debug('WebSocketDetect channelUnregistered ' + ctx)
|
||||
this.event.emit(ServerEvent.disconnect, ctx, 'client disconnect')
|
||||
ctx.fireChannelUnregistered()
|
||||
}
|
||||
exceptionCaught(ctx: any, cause: Error) {
|
||||
this.event.emit(ServerEvent.error, ctx, cause)
|
||||
}
|
||||
|
@ -43,14 +43,14 @@ export class WebSocketHandler extends WebSocketHandlerAdapter {
|
||||
|
||||
channelInactive(ctx: any) {
|
||||
console.debug('WebSocketHandler channelInactive ' + ctx)
|
||||
this.options.event.emit(ServerEvent.disconnect, ctx, 'client disconnect')
|
||||
ctx.channelInactive()
|
||||
this.options.event.emit(ServerEvent.disconnect, ctx, 'netty channelInactive')
|
||||
super.channelInactive(ctx)
|
||||
}
|
||||
|
||||
channelUnregistered(ctx: any) {
|
||||
console.debug('WebSocketHandler channelUnregistered ' + ctx)
|
||||
this.options.event.emit(ServerEvent.disconnect, ctx, 'client disconnect')
|
||||
ctx.fireChannelUnregistered()
|
||||
this.options.event.emit(ServerEvent.disconnect, ctx, 'netty channelUnregistered')
|
||||
super.channelUnregistered(ctx)
|
||||
}
|
||||
|
||||
exceptionCaught(ctx: any, cause: Error) {
|
||||
|
@ -5,11 +5,12 @@ import { Namespace, Server, Socket } from './index'
|
||||
import { PacketTypes, SubPacketTypes } from './types'
|
||||
import { ServerEvent } from './constants'
|
||||
import { SocketId } from './adapter'
|
||||
import { Transport } from '../transport'
|
||||
|
||||
const parser = new Parser()
|
||||
|
||||
export class Client extends EventEmitter implements Client {
|
||||
public readonly conn
|
||||
export class Client extends EventEmitter {
|
||||
public readonly conn: Transport
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ -21,6 +22,11 @@ export class Client extends EventEmitter implements Client {
|
||||
private nsps: Map<string, Socket>
|
||||
private connectTimeout: NodeJS.Timeout
|
||||
|
||||
private checkIntervalTimer: NodeJS.Timeout
|
||||
private upgradeTimeoutTimer: NodeJS.Timeout
|
||||
private pingTimeoutTimer: NodeJS.Timeout
|
||||
private pingIntervalTimer: NodeJS.Timeout
|
||||
|
||||
constructor(server: Server, conn) {
|
||||
super()
|
||||
this.server = server
|
||||
@ -32,6 +38,9 @@ export class Client extends EventEmitter implements Client {
|
||||
// =============================
|
||||
this.sockets = new Map()
|
||||
this.nsps = new Map()
|
||||
// ================== engine.io
|
||||
this.onOpen()
|
||||
// ================== Transport
|
||||
this.conn.on(ServerEvent.disconnect, (reason) => {
|
||||
this.onclose(reason)
|
||||
})
|
||||
@ -73,7 +82,7 @@ export class Client extends EventEmitter implements Client {
|
||||
* @private
|
||||
*/
|
||||
private connect(name: string, auth: object = {}) {
|
||||
console.debug(`client ${this.id} connecting to namespace ${name} has: ${this.server._nsps[name]}`)
|
||||
console.debug(`client ${this.id} connecting to namespace ${name} has: ${this.server._nsps.has(name)}`)
|
||||
if (this.server._nsps.has(name)) {
|
||||
return this.doConnect(name, auth)
|
||||
}
|
||||
@ -103,9 +112,7 @@ export class Client extends EventEmitter implements Client {
|
||||
const nsp = this.server.of(name)
|
||||
|
||||
const socket = nsp._add(this, auth, () => {
|
||||
console.debug(`doConnect set sockets ${socket.id}`)
|
||||
this.sockets.set(socket.id, socket)
|
||||
console.debug(`doConnect set nsps ${nsp.name}`)
|
||||
this.nsps.set(nsp.name, socket)
|
||||
})
|
||||
}
|
||||
@ -128,12 +135,16 @@ export class Client extends EventEmitter implements Client {
|
||||
*/
|
||||
_remove(socket: Socket) {
|
||||
if (this.sockets.has(socket.id)) {
|
||||
const nsp = this.sockets.get(socket.id).nsp.name
|
||||
this.sockets.delete(socket.id)
|
||||
this.nsps.delete(nsp)
|
||||
this.nsps.delete(socket.nsp.name)
|
||||
} else {
|
||||
console.debug(`ignoring remove for ${socket.id}`,)
|
||||
}
|
||||
process.nextTick(() => {
|
||||
if (this.sockets.size == 0) {
|
||||
this.onclose('no live socket')
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Closes the underlying connection.
|
||||
@ -141,6 +152,7 @@ export class Client extends EventEmitter implements Client {
|
||||
* @private
|
||||
*/
|
||||
private close() {
|
||||
console.debug(`client ${this.id} close`)
|
||||
if ("open" == this.conn.readyState) {
|
||||
console.debug("forcing transport close")
|
||||
this.onclose("forced server close")
|
||||
@ -154,7 +166,7 @@ export class Client extends EventEmitter implements Client {
|
||||
* @param {Object} opts
|
||||
* @private
|
||||
*/
|
||||
_packet(packet, opts = { preEncoded: false }) {
|
||||
_packet(packet: Packet, opts = { preEncoded: false }) {
|
||||
// opts = opts || {}
|
||||
// const self = this
|
||||
|
||||
@ -178,7 +190,11 @@ export class Client extends EventEmitter implements Client {
|
||||
// } else {
|
||||
// debug("ignoring packet write %j", packet)
|
||||
// }
|
||||
this.conn.send(opts.preEncoded ? packet as unknown as string : parser.encode(packet))
|
||||
if ("open" == this.conn.readyState) {
|
||||
this.conn.send(opts.preEncoded ? packet as unknown as string : parser.encode(packet))
|
||||
} else {
|
||||
console.debug(`ignoring write packet ${JSON.stringify(packet)} to client ${this.id} is already close!`)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Called with incoming transport data.
|
||||
@ -202,15 +218,14 @@ export class Client extends EventEmitter implements Client {
|
||||
if (SubPacketTypes.CONNECT == packet.sub_type) {
|
||||
this.connect(packet.nsp, packet.data)
|
||||
} else {
|
||||
const socket = this.nsps.get(packet.nsp)
|
||||
if (socket) {
|
||||
process.nextTick(function () {
|
||||
process.nextTick(() => {
|
||||
const socket = this.nsps.get(packet.nsp)
|
||||
if (socket) {
|
||||
socket._onpacket(packet)
|
||||
})
|
||||
} else {
|
||||
console.debug(`client ${this.id} no socket for namespace ${packet.nsp} avalibe socket: `)
|
||||
this.nsps.forEach((v, k) => console.debug(`- ${k} => ${v}`))
|
||||
}
|
||||
} else {
|
||||
console.debug(`client ${this.id} no socket for namespace ${packet.nsp}.`)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -226,9 +241,16 @@ export class Client extends EventEmitter implements Client {
|
||||
this.conn.close()
|
||||
}
|
||||
onclose(reason?: string) {
|
||||
this.conn.readyState = "closing"
|
||||
// ======= engine.io
|
||||
this.onClose(reason)
|
||||
// cleanup connectTimeout
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout)
|
||||
this.connectTimeout = null
|
||||
}
|
||||
console.debug(`client ${this.id} close with reason ${reason}`)
|
||||
// ignore a potential subsequent `close` event
|
||||
this.destroy()
|
||||
// `nsps` and `sockets` are cleaned up seamlessly
|
||||
for (const socket of this.sockets.values()) {
|
||||
socket._onclose(reason)
|
||||
@ -242,5 +264,97 @@ export class Client extends EventEmitter implements Client {
|
||||
// this.conn.removeListener('error', this.onerror);
|
||||
// this.conn.removeListener('close', this.onclose);
|
||||
// this.decoder.removeListener('decoded', this.ondecoded);
|
||||
};
|
||||
}
|
||||
|
||||
//================== engine.io
|
||||
onOpen() {
|
||||
this.conn.readyState = "open"
|
||||
this._packet({
|
||||
type: PacketTypes.OPEN,
|
||||
data: {
|
||||
sid: this.id,
|
||||
upgrades: [],
|
||||
pingInterval: this.server.options.pingInterval,
|
||||
pingTimeout: this.server.options.pingTimeout
|
||||
}
|
||||
})
|
||||
this.schedulePing()
|
||||
}
|
||||
|
||||
onPacket(packet: Packet) {
|
||||
if ("open" === this.conn.readyState) {
|
||||
// export packet event
|
||||
// debug("packet")
|
||||
// this.emit("packet", packet)
|
||||
|
||||
// Reset ping timeout on any packet, incoming data is a good sign of
|
||||
// other side's liveness
|
||||
this.resetPingTimeout(this.server.options.pingInterval + this.server.options.pingTimeout * 2)
|
||||
switch (packet.type) {
|
||||
case PacketTypes.PING:
|
||||
this._packet({
|
||||
type: PacketTypes.PONG,
|
||||
data: packet.data
|
||||
})
|
||||
break
|
||||
case PacketTypes.PONG:
|
||||
this.schedulePing()
|
||||
break
|
||||
case PacketTypes.UPGRADE:
|
||||
break
|
||||
case PacketTypes.MESSAGE:
|
||||
this.ondecoded(packet)
|
||||
break
|
||||
case PacketTypes.CLOSE:
|
||||
this.onclose()
|
||||
break
|
||||
default:
|
||||
console.log(`client ${this.id} reciver unknow packet type: ${packet.type}`)
|
||||
}
|
||||
} else {
|
||||
console.debug(`packet received with closed client ${this.id}`)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Called upon transport considered closed.
|
||||
* Possible reasons: `ping timeout`, `client error`, `parse error`,
|
||||
* `transport error`, `server close`, `transport close`
|
||||
*/
|
||||
onClose(reason, description?: string) {
|
||||
// if ("closed" !== this.conn.readyState) {
|
||||
clearTimeout(this.pingIntervalTimer)
|
||||
clearTimeout(this.pingTimeoutTimer)
|
||||
|
||||
clearInterval(this.checkIntervalTimer)
|
||||
this.checkIntervalTimer = null
|
||||
clearTimeout(this.upgradeTimeoutTimer)
|
||||
// this.emit("close", reason, description)
|
||||
// }
|
||||
}
|
||||
/**
|
||||
* Pings client every `this.pingInterval` and expects response
|
||||
* within `this.pingTimeout` or closes connection.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
schedulePing() {
|
||||
clearTimeout(this.pingIntervalTimer)
|
||||
this.pingIntervalTimer = setTimeout(() => {
|
||||
this.resetPingTimeout(this.server.options.pingTimeout)
|
||||
process.nextTick(() => this._packet({ type: PacketTypes.PING }))
|
||||
}, this.server.options.pingInterval)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets ping timeout.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
resetPingTimeout(timeout) {
|
||||
clearTimeout(this.pingTimeoutTimer)
|
||||
this.pingTimeoutTimer = setTimeout(() => {
|
||||
if (this.conn.readyState === "closed") return
|
||||
this.onclose("ping timeout")
|
||||
}, timeout)
|
||||
}
|
||||
}
|
||||
|
@ -4,5 +4,6 @@ export enum ServerEvent {
|
||||
connection = 'connection',
|
||||
message = 'message',
|
||||
error = 'error',
|
||||
disconnect = 'disconnect'
|
||||
}
|
||||
disconnecting = 'disconnecting',
|
||||
disconnect = 'disconnect',
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import { PacketTypes, SubPacketTypes } from './types'
|
||||
import { Packet } from './packet'
|
||||
import { Socket } from './socket'
|
||||
import { Adapter } from './adapter'
|
||||
import { InnerClient } from '../interfaces'
|
||||
import { Transport } from '../transport'
|
||||
import { ParentNamespace } from './parent-namespace'
|
||||
|
||||
interface EngineOptions {
|
||||
@ -144,7 +144,7 @@ class Server {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
readonly _parser: Parser
|
||||
_parser: Parser
|
||||
private readonly encoder
|
||||
|
||||
/**
|
||||
@ -172,29 +172,33 @@ class Server {
|
||||
*/
|
||||
_connectTimeout: number
|
||||
|
||||
options: ServerOptions
|
||||
options: Partial<ServerOptions>
|
||||
private websocketServer: WebSocketServer
|
||||
private allClients: Map<string, Client>
|
||||
|
||||
constructor(instance: any, options: Partial<ServerOptions>) {
|
||||
if (!instance) { throw new Error('instance can\'t be undefiend!') }
|
||||
this.allClients = new Map()
|
||||
this._nsps = new Map()
|
||||
this.connectTimeout(options.connectTimeout || 45000)
|
||||
this._parser = options.parser || new Parser()
|
||||
this.adapter(options.adapter || Adapter)
|
||||
this.options = Object.assign({
|
||||
event: new EventEmitter(),
|
||||
path: '/socket.io',
|
||||
root: root + '/wwwroot',
|
||||
serveClient: false,
|
||||
connectTimeout: 45000,
|
||||
wsEngine: process.env.EIO_WS_ENGINE || "ws",
|
||||
pingTimeout: 5000,
|
||||
pingInterval: 25000,
|
||||
upgradeTimeout: 10000,
|
||||
maxHttpBufferSize: 1e6,
|
||||
transports: 'websocket',
|
||||
allowUpgrades: true,
|
||||
httpCompression: {
|
||||
threshold: 1024
|
||||
},
|
||||
cors: false
|
||||
}, options)
|
||||
this.initServerConfig()
|
||||
this.sockets = this.of('/')
|
||||
if (instance.class.name.startsWith('io.netty.channel')) {
|
||||
let { NettyWebSocketServer } = require("../netty")
|
||||
this.websocketServer = new NettyWebSocketServer(instance, Object.assign({
|
||||
event: new EventEmitter(),
|
||||
path: '/socket.io',
|
||||
root: root + '/wwwroot'
|
||||
}, options))
|
||||
} else {
|
||||
let { TomcatWebSocketServer } = require("../tomcat")
|
||||
this.websocketServer = new TomcatWebSocketServer(instance, options)
|
||||
}
|
||||
this.selectServerImpl(instance)
|
||||
this.initServer()
|
||||
}
|
||||
/**
|
||||
@ -496,15 +500,6 @@ class Server {
|
||||
console.debug(`incoming connection with id ${conn.id}`)
|
||||
let client = new Client(this, conn)
|
||||
this.allClients.set(conn.id, client)
|
||||
client._packet({
|
||||
type: PacketTypes.OPEN,
|
||||
data: {
|
||||
sid: client.id,
|
||||
upgrades: [],
|
||||
pingInterval: 25000,
|
||||
pingTimeout: 5000
|
||||
}
|
||||
})
|
||||
return this
|
||||
}
|
||||
// of(nsp: string): Namespace {
|
||||
@ -560,8 +555,9 @@ class Server {
|
||||
return nsp
|
||||
}
|
||||
close(fn?: () => void): void {
|
||||
for (const socket of this.sockets.sockets.values()) {
|
||||
socket._onclose("server shutting down")
|
||||
this.clients.length
|
||||
for (const client of this.allClients.values()) {
|
||||
client._disconnect()
|
||||
}
|
||||
|
||||
// this.engine.close()
|
||||
@ -605,70 +601,57 @@ class Server {
|
||||
return this.sockets.compress(args[0])
|
||||
}
|
||||
// ===============================
|
||||
private initServerConfig() {
|
||||
this.allClients = new Map()
|
||||
this._nsps = new Map()
|
||||
this.connectTimeout(this.options.connectTimeout || 45000)
|
||||
this._parser = this.options.parser || new Parser()
|
||||
this.adapter(this.options.adapter || Adapter)
|
||||
}
|
||||
private selectServerImpl(instance: any) {
|
||||
let WebSocketServerImpl = undefined
|
||||
if (instance.class.name.startsWith('io.netty.channel')) {
|
||||
WebSocketServerImpl = require("../netty").NettyWebSocketServer
|
||||
} else {
|
||||
WebSocketServerImpl = require("../tomcat").TomcatWebSocketServer
|
||||
}
|
||||
this.websocketServer = new WebSocketServerImpl(instance, this.options)
|
||||
}
|
||||
private initServer() {
|
||||
this.websocketServer.on(ServerEvent.connect, (innerClient: InnerClient) => {
|
||||
this.onconnection(innerClient)
|
||||
this.websocketServer.on(ServerEvent.connect, (transport: Transport) => {
|
||||
this.onconnection(transport)
|
||||
})
|
||||
this.websocketServer.on(ServerEvent.message, (innerClient: InnerClient, text) => {
|
||||
if (this.allClients.has(innerClient.id)) {
|
||||
this.processPacket(this._parser.decode(text), this.allClients.get(innerClient.id))
|
||||
this.websocketServer.on(ServerEvent.message, (transport: Transport, text) => {
|
||||
if (this.allClients.has(transport.id)) {
|
||||
let client = this.allClients.get(transport.id)
|
||||
client.onPacket(this._parser.decode(text))
|
||||
} else {
|
||||
console.error(`unknow engine socket ${innerClient.id} reciver message ${text}`)
|
||||
console.error(`unknow transport ${transport.id} reciver message ${text}`)
|
||||
}
|
||||
})
|
||||
this.websocketServer.on(ServerEvent.disconnect, (innerClient: InnerClient, reason) => {
|
||||
if (this.allClients.has(innerClient.id)) {
|
||||
this.allClients.get(innerClient.id).onclose(reason)
|
||||
this.allClients.delete(innerClient.id)
|
||||
this.websocketServer.on(ServerEvent.disconnect, (transport: Transport, reason) => {
|
||||
if (this.allClients.has(transport.id)) {
|
||||
this.allClients.get(transport.id).onclose(reason)
|
||||
this.allClients.delete(transport.id)
|
||||
} else {
|
||||
console.error(`unknow engine innerClient ${innerClient?.id} disconnect cause ${reason}`)
|
||||
console.error(`unknow transport ${transport?.id} disconnect cause ${reason}`)
|
||||
}
|
||||
})
|
||||
this.websocketServer.on(ServerEvent.error, (innerClient: InnerClient, cause) => {
|
||||
if (this.allClients.has(innerClient?.id)) {
|
||||
let client = this.allClients.get(innerClient?.id)
|
||||
this.websocketServer.on(ServerEvent.error, (transport: Transport, cause) => {
|
||||
if (this.allClients.has(transport?.id)) {
|
||||
let client = this.allClients.get(transport?.id)
|
||||
if (client.listeners(ServerEvent.error).length) {
|
||||
client.emit(ServerEvent.error, cause)
|
||||
} else {
|
||||
console.error(`engine innerClient ${innerClient.id} cause error: ${cause}`)
|
||||
console.error(`client ${client.id} cause error: ${cause}`)
|
||||
console.ex(cause)
|
||||
}
|
||||
} else {
|
||||
console.error(`unknow innerClient ${innerClient?.id} cause error: ${cause}`)
|
||||
console.error(`unknow transport ${transport?.id} cause error: ${cause}`)
|
||||
console.ex(cause)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private processPacket(packet: Packet, client: Client) {
|
||||
switch (packet.type) {
|
||||
case PacketTypes.PING:
|
||||
client._packet({
|
||||
type: PacketTypes.PONG,
|
||||
data: packet.data
|
||||
})
|
||||
break
|
||||
case PacketTypes.UPGRADE:
|
||||
break
|
||||
case PacketTypes.MESSAGE:
|
||||
this.processSubPacket(packet, client)
|
||||
break
|
||||
case PacketTypes.CLOSE:
|
||||
client.onclose()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private processSubPacket(packet: Packet, client: Client) {
|
||||
switch (packet.sub_type) {
|
||||
case SubPacketTypes.CONNECT:
|
||||
client.doConnect(packet.nsp, {})
|
||||
break
|
||||
default:
|
||||
client.ondecoded(packet)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,7 +58,6 @@ export class Namespace extends EventEmitter {
|
||||
public use(
|
||||
fn: (socket: Socket, next: (err?: ExtendedError) => void) => void
|
||||
): Namespace {
|
||||
throw new Error("Method not implemented.")
|
||||
this._fns.push(fn)
|
||||
return this
|
||||
}
|
||||
@ -96,8 +95,8 @@ export class Namespace extends EventEmitter {
|
||||
return this.to(name)
|
||||
}
|
||||
_add(client: Client, query?: any, fn?: () => void) {
|
||||
console.debug(`adding socket to nsp ${this.name}`)
|
||||
const socket = new Socket(this, client, query || {})
|
||||
console.debug(`client ${client.id} adding socket ${socket.id} to nsp ${this.name}`)
|
||||
this.run(socket, err => {
|
||||
process.nextTick(() => {
|
||||
if ("open" == client.conn.readyState) {
|
||||
@ -137,7 +136,7 @@ export class Namespace extends EventEmitter {
|
||||
console.debug(`namespace ${this.name} remove socket ${socket.id}`)
|
||||
this.sockets.delete(socket.id)
|
||||
} else {
|
||||
console.debug(`ignoring remove for ${socket.id}`)
|
||||
console.debug(`namespace ${this.name} ignoring remove for ${socket.id}`)
|
||||
}
|
||||
}
|
||||
emit(event: string, ...args: any[]): boolean {
|
||||
@ -185,9 +184,7 @@ export class Namespace extends EventEmitter {
|
||||
*/
|
||||
public allSockets(): Promise<Set<SocketId>> {
|
||||
if (!this.adapter) {
|
||||
throw new Error(
|
||||
"No adapter for this namespace, are you trying to get the list of clients of a dynamic namespace?"
|
||||
)
|
||||
throw new Error("No adapter for this namespace, are you trying to get the list of clients of a dynamic namespace?")
|
||||
}
|
||||
const rooms = new Set(this._rooms)
|
||||
this._rooms.clear()
|
||||
@ -233,24 +230,9 @@ export class Namespace extends EventEmitter {
|
||||
hasBin(args: any[]) {
|
||||
return false
|
||||
}
|
||||
del(client: Client) {
|
||||
let socket = this.sockets[client.id]
|
||||
socket.disconnect()
|
||||
delete this.sockets[client.id]
|
||||
}
|
||||
clients(fn: (sockets: Socket[]) => Namespace): Namespace {
|
||||
return fn(Object.values(this.sockets))
|
||||
}
|
||||
process(packet: Packet, client: Client) {
|
||||
switch (packet.sub_type) {
|
||||
case SubPacketTypes.CONNECT:
|
||||
client.doConnect(this.name, {})
|
||||
break
|
||||
default:
|
||||
this.sockets.get(client.id)._onpacket(packet)
|
||||
break
|
||||
}
|
||||
}
|
||||
close() {
|
||||
this.removeAllListeners(ServerEvent.connect)
|
||||
this.removeAllListeners(ServerEvent.connection)
|
||||
|
@ -88,15 +88,6 @@ export class Socket extends EventEmitter {
|
||||
private _rooms: Set<Room> = new Set();
|
||||
private _anyListeners: Array<(...args: any[]) => void>
|
||||
|
||||
private events = [
|
||||
'connect',
|
||||
"connect_error",
|
||||
'disconnect',
|
||||
'disconnecting',
|
||||
'newListener',
|
||||
'removeListener'
|
||||
]
|
||||
|
||||
constructor(nsp: Namespace, client: Client, auth = {}) {
|
||||
super()
|
||||
this.nsp = nsp
|
||||
@ -114,12 +105,6 @@ export class Socket extends EventEmitter {
|
||||
this._rooms = new Set()
|
||||
}
|
||||
emit(event: string, ...args: any[]): boolean {
|
||||
if (~this.events.indexOf(event)) {
|
||||
super.emit(event, ...args)
|
||||
// @ts-ignore
|
||||
return this
|
||||
}
|
||||
|
||||
let packet: Packet = {
|
||||
type: PacketTypes.MESSAGE,
|
||||
sub_type: (this.flags.binary !== undefined ? this.flags.binary : this.hasBin(args)) ? SubPacketTypes.BINARY_EVENT : SubPacketTypes.EVENT,
|
||||
@ -213,9 +198,9 @@ export class Socket extends EventEmitter {
|
||||
* @private
|
||||
*/
|
||||
_onconnect(): void {
|
||||
console.debug("socket connected - writing packet")
|
||||
console.debug(`socket ${this.id} connected - writing packet`)
|
||||
this.join(this.id)
|
||||
this.packet({ type: PacketTypes.MESSAGE, sub_type: SubPacketTypes.CONNECT })
|
||||
this.packet({ type: PacketTypes.MESSAGE, sub_type: SubPacketTypes.CONNECT, data: { sid: this.id } })
|
||||
}
|
||||
_onpacket(packet: Packet) {
|
||||
switch (packet.sub_type) {
|
||||
@ -246,7 +231,7 @@ export class Socket extends EventEmitter {
|
||||
}
|
||||
onevent(packet: Packet) {
|
||||
if (null != packet.id) {
|
||||
console.debug('attaching ack callback to event')
|
||||
console.trace(`attaching ack ${packet.id} callback to client ${this.id} event`)
|
||||
this.dispatch(packet, this.ack(packet.id))
|
||||
} else {
|
||||
this.dispatch(packet)
|
||||
@ -266,13 +251,13 @@ export class Socket extends EventEmitter {
|
||||
}
|
||||
}
|
||||
onack(packet: Packet) {
|
||||
let ack = this.acks[packet.id]
|
||||
let ack = this.acks.get(packet.id)
|
||||
if ('function' == typeof ack) {
|
||||
console.debug('calling ack %s with %j', packet.id, packet.data)
|
||||
console.trace(`calling ack ${packet.id} on socket ${this.id} with ${packet.data}`)
|
||||
ack.apply(this, packet.data)
|
||||
delete this.acks[packet.id]
|
||||
this.acks.delete(packet.id)
|
||||
} else {
|
||||
console.debug('bad ack %s', packet.id)
|
||||
console.trace(`bad ack ${packet.id} on socket ${this.id}`)
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -281,7 +266,7 @@ export class Socket extends EventEmitter {
|
||||
* @private
|
||||
*/
|
||||
private ondisconnect(): void {
|
||||
console.debug("got disconnect packet")
|
||||
console.debug(`socket ${this.id} got disconnect packet`)
|
||||
this._onclose("client namespace disconnect")
|
||||
}
|
||||
|
||||
@ -294,7 +279,7 @@ export class Socket extends EventEmitter {
|
||||
if (this.listeners("error").length) {
|
||||
super.emit("error", err)
|
||||
} else {
|
||||
console.error("Missing error handler on `socket`.")
|
||||
console.error(`Missing error handler on 'socket(${this.id})'.`)
|
||||
console.error(err.stack)
|
||||
}
|
||||
}
|
||||
@ -308,15 +293,15 @@ export class Socket extends EventEmitter {
|
||||
* @private
|
||||
*/
|
||||
_onclose(reason: string) {
|
||||
console.debug(`closing socket - reason: ${reason} connected: ${this.connected}`)
|
||||
if (!this.connected) return this
|
||||
this.emit('disconnecting', reason)
|
||||
console.debug(`closing socket ${this.id} - reason: ${reason} connected: ${this.connected}`)
|
||||
super.emit(ServerEvent.disconnecting, reason)
|
||||
this.leaveAll()
|
||||
this.nsp._remove(this)
|
||||
this.client._remove(this)
|
||||
this.connected = false
|
||||
this.disconnected = true
|
||||
this.emit('disconnect', reason)
|
||||
super.emit(ServerEvent.disconnect, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -496,8 +481,8 @@ export class Socket extends EventEmitter {
|
||||
this._onerror(error)
|
||||
}
|
||||
}
|
||||
dispatch(packet: Packet, ack?: Function) {
|
||||
if (ack) { this.acks[packet.id] = ack }
|
||||
dispatch(packet: Packet, ack?: () => void) {
|
||||
if (ack) { this.acks.set(packet.id, ack) }
|
||||
super.emit(packet.name, ...packet.data, ack)
|
||||
}
|
||||
private hasBin(obj: any) {
|
||||
|
@ -1,45 +1,22 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { InnerClient } from '../interfaces'
|
||||
import { Transport } from '../transport'
|
||||
|
||||
export class TomcatClient extends EventEmitter implements InnerClient {
|
||||
private _id: string
|
||||
export class TomcatClient extends Transport {
|
||||
private session: javax.websocket.Session
|
||||
|
||||
server: any
|
||||
readyState: string
|
||||
remoteAddress: string
|
||||
upgraded: boolean
|
||||
request: any
|
||||
|
||||
constructor(server: any, session: javax.websocket.Session) {
|
||||
super()
|
||||
this.server = server
|
||||
this.readyState = 'open'
|
||||
super(server)
|
||||
this.remoteAddress = session + ''
|
||||
this.upgraded = true
|
||||
this.request = {
|
||||
uri: () => `${session.getRequestURI()}`,
|
||||
headers: () => []
|
||||
}
|
||||
|
||||
this.session = session
|
||||
this._id = session.getId() + ''
|
||||
this.session = session
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this._id
|
||||
doSend(text: string) {
|
||||
Java.synchronized(() => this.session.getBasicRemote().sendText(text), this.session)()
|
||||
}
|
||||
send(text: string) {
|
||||
if (this.readyState == 'open') {
|
||||
Java.synchronized(() => this.session.getBasicRemote().sendText(text), this.session)()
|
||||
} else {
|
||||
console.debug(`send message ${text} to close client ${this._id}`)
|
||||
}
|
||||
}
|
||||
close() {
|
||||
if (this.readyState == 'open') {
|
||||
this.readyState = 'close'
|
||||
this.session.close()
|
||||
}
|
||||
doClose() {
|
||||
this.session.close()
|
||||
}
|
||||
}
|
||||
|
35
packages/websocket/src/transport.ts
Normal file
35
packages/websocket/src/transport.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { EventEmitter } from 'events'
|
||||
|
||||
export abstract class Transport extends EventEmitter {
|
||||
protected _id: string
|
||||
|
||||
server: any
|
||||
readyState: 'opening' | 'open' | 'closing' | 'closed'
|
||||
remoteAddress: string
|
||||
upgraded: boolean
|
||||
request: any
|
||||
|
||||
constructor(server: any) {
|
||||
super()
|
||||
this.server = server
|
||||
this.readyState = 'open'
|
||||
this.upgraded = true
|
||||
}
|
||||
get id() {
|
||||
return this._id
|
||||
}
|
||||
send(text: string) {
|
||||
if (this.readyState == 'open') {
|
||||
this.doSend(text)
|
||||
} else {
|
||||
console.debug(`send message ${text} to close client ${this._id}`)
|
||||
}
|
||||
}
|
||||
close() {
|
||||
if ("closed" === this.readyState || "closing" === this.readyState) { return }
|
||||
this.doClose()
|
||||
this.readyState = 'closed'
|
||||
}
|
||||
abstract doSend(text: string)
|
||||
abstract doClose()
|
||||
}
|
Loading…
Reference in New Issue
Block a user