首次提交
This commit is contained in:
+272
@@ -0,0 +1,272 @@
|
||||
'use strict'
|
||||
|
||||
const retimer = require('retimer')
|
||||
const { pipeline } = require('stream')
|
||||
const write = require('../write')
|
||||
const QoSPacket = require('../qos-packet')
|
||||
const { through } = require('../utils')
|
||||
const handleSubscribe = require('./subscribe')
|
||||
const uniqueId = require('hyperid')()
|
||||
|
||||
function Connack (arg) {
|
||||
this.cmd = 'connack'
|
||||
this.returnCode = arg.returnCode
|
||||
this.sessionPresent = arg.sessionPresent
|
||||
}
|
||||
|
||||
function ClientPacketStatus (client, packet) {
|
||||
this.client = client
|
||||
this.packet = packet
|
||||
}
|
||||
|
||||
const connectActions = [
|
||||
authenticate,
|
||||
setKeepAlive,
|
||||
fetchSubs,
|
||||
restoreSubs,
|
||||
storeWill,
|
||||
registerClient,
|
||||
doConnack,
|
||||
emptyQueue
|
||||
]
|
||||
|
||||
const errorMessages = [
|
||||
'',
|
||||
'unacceptable protocol version',
|
||||
'identifier rejected',
|
||||
'Server unavailable',
|
||||
'bad user name or password',
|
||||
'not authorized',
|
||||
'keep alive limit exceeded'
|
||||
]
|
||||
|
||||
function handleConnect (client, packet, done) {
|
||||
clearTimeout(client._connectTimer)
|
||||
client._connectTimer = null
|
||||
client.connecting = true
|
||||
client.broker.preConnect(client, packet, negate)
|
||||
|
||||
function negate (err, successful) {
|
||||
if (!err && successful === true) {
|
||||
setImmediate(init, client, packet, done)
|
||||
} else {
|
||||
client.connecting = false
|
||||
done(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function init (client, packet, done) {
|
||||
const clientId = packet.clientId
|
||||
let returnCode = 0
|
||||
// [MQTT-3.1.2-2]
|
||||
if (packet.protocolVersion < 3 || packet.protocolVersion > 4) {
|
||||
returnCode = 1
|
||||
}
|
||||
// MQTT 3.1.0 allows <= 23 client id length
|
||||
if (packet.protocolVersion === 3 && clientId.length > client.broker.maxClientsIdLength) {
|
||||
returnCode = 2
|
||||
}
|
||||
// check if the client keepalive is compatible with broker settings
|
||||
if (client.broker.keepaliveLimit && (!packet.keepalive || packet.keepalive > client.broker.keepaliveLimit)) {
|
||||
returnCode = 6
|
||||
}
|
||||
if (returnCode > 0) {
|
||||
const error = new Error(errorMessages[returnCode])
|
||||
error.errorCode = returnCode
|
||||
doConnack(
|
||||
{ client, returnCode, sessionPresent: false },
|
||||
done.bind(this, error))
|
||||
return
|
||||
}
|
||||
|
||||
client.id = clientId || 'aedes_' + uniqueId()
|
||||
client.clean = packet.clean
|
||||
client.version = packet.protocolVersion
|
||||
client._will = packet.will
|
||||
|
||||
client.broker._series(
|
||||
new ClientPacketStatus(client, packet),
|
||||
connectActions,
|
||||
{ returnCode: 0, sessionPresent: false }, // [MQTT-3.1.4-4], [MQTT-3.2.2-4]
|
||||
function (err) {
|
||||
this.client.connecting = false
|
||||
if (!err) {
|
||||
this.client.connected = true
|
||||
this.client.broker.emit('clientReady', client)
|
||||
this.client.emit('connected')
|
||||
}
|
||||
done(err)
|
||||
})
|
||||
}
|
||||
|
||||
function authenticate (arg, done) {
|
||||
const client = this.client
|
||||
client.pause()
|
||||
client.broker.authenticate(
|
||||
client,
|
||||
this.packet.username,
|
||||
this.packet.password,
|
||||
negate)
|
||||
|
||||
function negate (err, successful) {
|
||||
if (client.closed || client.broker.closed) {
|
||||
// a hack, sometimes client.close() or broker.close() happened
|
||||
// before authenticate() comes back
|
||||
// we stop here for not to register it and deregister it in write()
|
||||
return
|
||||
}
|
||||
if (!err && successful) {
|
||||
client._authorized = true
|
||||
return done()
|
||||
}
|
||||
|
||||
if (err) {
|
||||
const errCode = err.returnCode
|
||||
if (errCode && (errCode >= 2 && errCode <= 5)) {
|
||||
arg.returnCode = errCode
|
||||
} else {
|
||||
arg.returnCode = 5
|
||||
}
|
||||
if (!err.message) {
|
||||
err.message = errorMessages[arg.returnCode]
|
||||
}
|
||||
} else {
|
||||
arg.returnCode = 5
|
||||
err = new Error(errorMessages[arg.returnCode])
|
||||
}
|
||||
err.errorCode = arg.returnCode
|
||||
arg.client = client
|
||||
doConnack(arg,
|
||||
// [MQTT-3.2.2-5]
|
||||
client.close.bind(client, done.bind(this, err)))
|
||||
}
|
||||
}
|
||||
|
||||
function setKeepAlive (arg, done) {
|
||||
if (this.packet.keepalive > 0) {
|
||||
const client = this.client
|
||||
// [MQTT-3.1.2-24]
|
||||
client._keepaliveInterval = (this.packet.keepalive * 1500) + 1
|
||||
client._keepaliveTimer = retimer(function keepaliveTimeout () {
|
||||
client.broker.emit('keepaliveTimeout', client)
|
||||
client.emit('error', new Error('keep alive timeout'))
|
||||
}, client._keepaliveInterval)
|
||||
}
|
||||
done()
|
||||
}
|
||||
|
||||
function fetchSubs (arg, done) {
|
||||
const client = this.client
|
||||
if (!this.packet.clean) {
|
||||
client.broker.persistence.subscriptionsByClient({
|
||||
id: client.id,
|
||||
done,
|
||||
arg
|
||||
}, gotSubs)
|
||||
return
|
||||
}
|
||||
arg.sessionPresent = false // [MQTT-3.2.2-1]
|
||||
client.broker.persistence.cleanSubscriptions(
|
||||
client,
|
||||
done)
|
||||
}
|
||||
|
||||
function gotSubs (err, subs, client) {
|
||||
if (err) {
|
||||
return client.done(err)
|
||||
}
|
||||
client.arg.subs = subs
|
||||
client.done()
|
||||
}
|
||||
|
||||
function restoreSubs (arg, done) {
|
||||
if (arg.subs) {
|
||||
handleSubscribe(this.client, { subscriptions: arg.subs }, true, done)
|
||||
arg.sessionPresent = !!arg.subs // cast to boolean, [MQTT-3.2.2-2]
|
||||
return
|
||||
}
|
||||
arg.sessionPresent = false // [MQTT-3.2.2-1], [MQTT-3.2.2-3]
|
||||
done()
|
||||
}
|
||||
|
||||
function storeWill (arg, done) {
|
||||
const client = this.client
|
||||
client.will = client._will
|
||||
// delete any existing will messages from persistence
|
||||
client.broker.persistence.delWill(client, function () {
|
||||
if (client.will) {
|
||||
client.broker.persistence.putWill(
|
||||
client,
|
||||
client.will,
|
||||
done)
|
||||
} else {
|
||||
done()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function registerClient (arg, done) {
|
||||
const client = this.client
|
||||
client.broker.registerClient(client)
|
||||
done()
|
||||
}
|
||||
|
||||
function doConnack (arg, done) {
|
||||
const client = arg.client || this.client
|
||||
const connack = new Connack(arg)
|
||||
write(client, connack, function (err) {
|
||||
if (!err) {
|
||||
client.broker.emit('connackSent', connack, client)
|
||||
client.connackSent = true
|
||||
}
|
||||
done(err)
|
||||
})
|
||||
}
|
||||
|
||||
// push any queued messages (included retained messages) at the disconnected time
|
||||
// when QoS > 0 and session is true
|
||||
function emptyQueue (arg, done) {
|
||||
const client = this.client
|
||||
const persistence = client.broker.persistence
|
||||
const outgoing = persistence.outgoingStream(client)
|
||||
|
||||
client.resume()
|
||||
|
||||
pipeline(
|
||||
outgoing,
|
||||
through(function clearQueue (data, enc, next) {
|
||||
const packet = new QoSPacket(data, client)
|
||||
// Here we are deliberatly passing only the error
|
||||
// This is because there is no destination stream so the "client"
|
||||
// Object filled the buffer up to the highWaterMark preventing stored messages
|
||||
// being sent
|
||||
packet.writeCallback = (error, _client) => next(error)
|
||||
persistence.outgoingUpdate(client, packet, emptyQueueFilter)
|
||||
}),
|
||||
done
|
||||
)
|
||||
}
|
||||
|
||||
function emptyQueueFilter (err, client, packet) {
|
||||
const next = packet.writeCallback
|
||||
|
||||
if (err) {
|
||||
client.emit('error', err)
|
||||
return next()
|
||||
}
|
||||
|
||||
const authorized = (packet.cmd === 'publish')
|
||||
? client.broker.authorizeForward(client, packet)
|
||||
: true
|
||||
|
||||
const persistence = client.broker.persistence
|
||||
|
||||
if (client.clean || !authorized) {
|
||||
persistence.outgoingClearMessageId(client, packet, next)
|
||||
} else {
|
||||
write(client, packet, next)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = handleConnect
|
||||
Reference in New Issue
Block a user