fox-wamp
Version:
Web Application Message Router/Server WAMP/MQTT
176 lines (153 loc) • 4.78 kB
JavaScript
'use strict'
const { BaseRealm, BaseEngine, makeDataSerializable, unSerializeData } = require('../realm')
const { PromiseEngine, PromiseBinder } = require('../binder')
const { MemKeyValueStorage } = require('../mono/memkv')
const INTRA_REALM_NAME = 'sys'
class HistorySegment {
constructor (advanceSegment) {
this.content = new Map()
this.advanceSegment = advanceSegment
this.generator = 0
}
addActor (actor) {
this.generator++
this.content.set(this.generator, actor)
return { segment: this.advanceSegment, id: this.generator }
}
fetchActor (advance) {
if (advance.segment !== this.advanceSegment) {
throw "advance is not identical "+advance.segment+" "+this.advanceSegment
}
let actor = this.content.get(advance.id)
if (actor) {
this.content.delete(advance.id)
}
return actor
}
getAdvanceSegment() {
return this.advanceSegment
}
}
class NetEngine extends PromiseEngine {
doPush (actor) {
this.saveInboundHistory(actor)
}
}
class NetBinder extends PromiseBinder {
constructor (router) {
super()
this.curSegment = null
this.advanceSegmentGen = 0
this.segments = new Map()
this.router = router
this.sysRealm = new BaseRealm(router, new BaseEngine())
router.addRealm(INTRA_REALM_NAME, this.sysRealm)
this.sysRealm.registerKeyValueEngine(['#'], new MemKeyValueStorage())
this.sysApi = this.sysRealm.api()
this.sysApi.subscribe(['beginSegmentAccepted'], (data, opt) => {
if (!data.advanceSegment) {
console.error('ERROR: no advanceSegment in package')
}
if (this.curSegment) {
if (data.advanceSegment == this.curSegment.getAdvanceSegment()) {
// it is time to create new segment if it is necessary
this.curSegment = null
console.log('advance segment completed', data.advanceSegment)
} else {
console.warn('warn: new segment is not accepted, cur:', this.curSegment.getAdvanceSegment(), 'inbound:', data.advanceSegment)
}
}
})
this.sysApi.subscribe(['ackSegment'], (data, opt) => {
console.log('=> ackSegment', data, opt.headers)
this.ackSegment(opt.headers)
})
this.sysApi.subscribe(['dispatchEvent'], (data, opt) => {
console.log('=> dispatchEvent', data, opt.headers)
this.dispatchEvent(opt.headers)
})
}
getSegment () {
if (this.curSegment) {
return this.curSegment
}
this.advanceSegmentGen++
let curAdvanceSegment = '' + this.router.getId() + '-' + this.advanceSegmentGen
this.curSegment = new HistorySegment(curAdvanceSegment)
this.segments.set(curAdvanceSegment, this.curSegment)
this.sysApi.publish('beginSegment', {
advanceSegment: curAdvanceSegment
})
return this.curSegment
}
findSegment (advanceSegment) {
return this.segments.get(advanceSegment)
}
deleteSegment (advanceSegment) {
return this.segments.delete(advanceSegment)
}
ackSegment (syncMessage) {
let segment = this.findSegment(syncMessage.advanceSegment)
if (!segment) {
return
}
console.log('ackSegment', syncMessage.advanceSegment)
for (let event of syncMessage.pkg) {
let actor = segment.fetchActor(event.advanceId)
if (actor) {
actor.setEventId(event.qid)
actor.confirm()
} else {
console.log("actor not found by advanceId", event.advanceId)
}
}
if (syncMessage.final) {
this.deleteSegment(syncMessage.advanceSegment)
if (segment.size() > 0) {
console.log("removing not empty segment")
}
}
}
saveHistory (engine, actor) {
let segment = this.getSegment()
let advanceId = segment.addActor(actor)
return this.sysApi.publish('saveHistory', null, { headers:{
advanceId: advanceId,
realm: engine.getRealmName(),
data: makeDataSerializable(actor.getData()),
uri: actor.getUri(),
opt: actor.getOpt(),
sid: actor.getSid()
}})
}
getHistoryAfter (engine, after, uri, cbRow) {
return this.msg.getEventHistory(
engine.getRealmName(),
{ fromId: after, uri },
(event) => {
cbRow({
qid: event.id,
uri: event.uri,
data: unSerializeData(event.body)
})
}
)
}
dispatchEvent (eventData) {
const realm = this.router.findRealm(eventData.realm)
if (realm) {
realm.getEngine().disperseToSubs({
qid: eventData.qid,
uri: eventData.uri,
data: unSerializeData(eventData.data),
opt: eventData.opt,
sid: eventData.sid
})
}
}
cleanupSession (engine, sessionId) {
return Promise.resolve(true)
}
}
exports.NetBinder = NetBinder
exports.NetEngine = NetEngine