2020-01-04 13:10:36 +00:00
|
|
|
/*
|
2022-06-09 14:40:23 +00:00
|
|
|
* Copyright (c) 2013 IBM Corp and others.
|
2020-01-04 13:10:36 +00:00
|
|
|
*
|
|
|
|
* All rights reserved. This program and the accompanying materials
|
2022-06-09 14:40:23 +00:00
|
|
|
* are made available under the terms of the Eclipse Public License v2.0
|
|
|
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
|
|
|
*
|
|
|
|
* The Eclipse Public License is available at
|
|
|
|
* https://www.eclipse.org/legal/epl-2.0/
|
|
|
|
* and the Eclipse Distribution License is available at
|
|
|
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
2020-01-04 13:10:36 +00:00
|
|
|
*
|
|
|
|
* Contributors:
|
|
|
|
* Seth Hoenig
|
|
|
|
* Allan Stockdill-Mander
|
|
|
|
* Mike Robertson
|
2022-06-09 14:40:23 +00:00
|
|
|
* Matt Brittan
|
2020-01-04 13:10:36 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
package mqtt
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// MId is 16 bit message id as specified by the MQTT spec.
|
|
|
|
// In general, these values should not be depended upon by
|
|
|
|
// the client application.
|
|
|
|
type MId uint16
|
|
|
|
|
|
|
|
type messageIds struct {
|
|
|
|
sync.RWMutex
|
|
|
|
index map[uint16]tokenCompletor
|
2021-09-01 20:08:47 +00:00
|
|
|
|
|
|
|
lastIssuedID uint16 // The most recently issued ID. Used so we cycle through ids rather than immediately reusing them (can make debugging easier)
|
2020-01-04 13:10:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
midMin uint16 = 1
|
|
|
|
midMax uint16 = 65535
|
|
|
|
)
|
|
|
|
|
2022-06-09 14:40:23 +00:00
|
|
|
// cleanup clears the message ID map; completes all token types and sets error on PUB, SUB and UNSUB tokens.
|
2020-01-04 13:10:36 +00:00
|
|
|
func (mids *messageIds) cleanUp() {
|
|
|
|
mids.Lock()
|
|
|
|
for _, token := range mids.index {
|
|
|
|
switch token.(type) {
|
|
|
|
case *PublishToken:
|
2021-09-01 20:08:47 +00:00
|
|
|
token.setError(fmt.Errorf("connection lost before Publish completed"))
|
2020-01-04 13:10:36 +00:00
|
|
|
case *SubscribeToken:
|
2021-09-01 20:08:47 +00:00
|
|
|
token.setError(fmt.Errorf("connection lost before Subscribe completed"))
|
2020-01-04 13:10:36 +00:00
|
|
|
case *UnsubscribeToken:
|
2021-09-01 20:08:47 +00:00
|
|
|
token.setError(fmt.Errorf("connection lost before Unsubscribe completed"))
|
2022-06-09 14:40:23 +00:00
|
|
|
case nil: // should not be any nil entries
|
2020-01-04 13:10:36 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
token.flowComplete()
|
|
|
|
}
|
|
|
|
mids.index = make(map[uint16]tokenCompletor)
|
|
|
|
mids.Unlock()
|
|
|
|
DEBUG.Println(MID, "cleaned up")
|
|
|
|
}
|
|
|
|
|
2022-06-09 14:40:23 +00:00
|
|
|
// cleanUpSubscribe removes all SUBSCRIBE and UNSUBSCRIBE tokens (setting error)
|
|
|
|
// This may be called when the connection is lost, and we will not be resending SUB/UNSUB packets
|
|
|
|
func (mids *messageIds) cleanUpSubscribe() {
|
|
|
|
mids.Lock()
|
|
|
|
for mid, token := range mids.index {
|
|
|
|
switch token.(type) {
|
|
|
|
case *SubscribeToken:
|
|
|
|
token.setError(fmt.Errorf("connection lost before Subscribe completed"))
|
|
|
|
delete(mids.index, mid)
|
|
|
|
case *UnsubscribeToken:
|
|
|
|
token.setError(fmt.Errorf("connection lost before Unsubscribe completed"))
|
|
|
|
delete(mids.index, mid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mids.Unlock()
|
|
|
|
DEBUG.Println(MID, "cleaned up subs")
|
|
|
|
}
|
|
|
|
|
2020-01-04 13:10:36 +00:00
|
|
|
func (mids *messageIds) freeID(id uint16) {
|
|
|
|
mids.Lock()
|
|
|
|
delete(mids.index, id)
|
|
|
|
mids.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mids *messageIds) claimID(token tokenCompletor, id uint16) {
|
|
|
|
mids.Lock()
|
|
|
|
defer mids.Unlock()
|
|
|
|
if _, ok := mids.index[id]; !ok {
|
|
|
|
mids.index[id] = token
|
|
|
|
} else {
|
|
|
|
old := mids.index[id]
|
|
|
|
old.flowComplete()
|
|
|
|
mids.index[id] = token
|
|
|
|
}
|
2021-09-01 20:08:47 +00:00
|
|
|
if id > mids.lastIssuedID {
|
|
|
|
mids.lastIssuedID = id
|
|
|
|
}
|
2020-01-04 13:10:36 +00:00
|
|
|
}
|
|
|
|
|
2021-09-01 20:08:47 +00:00
|
|
|
// getID will return an available id or 0 if none available
|
|
|
|
// The id will generally be the previous id + 1 (because this makes tracing messages a bit simpler)
|
2020-01-04 13:10:36 +00:00
|
|
|
func (mids *messageIds) getID(t tokenCompletor) uint16 {
|
|
|
|
mids.Lock()
|
|
|
|
defer mids.Unlock()
|
2021-09-01 20:08:47 +00:00
|
|
|
i := mids.lastIssuedID // note: the only situation where lastIssuedID is 0 the map will be empty
|
|
|
|
looped := false // uint16 will loop from 65535->0
|
|
|
|
for {
|
|
|
|
i++
|
|
|
|
if i == 0 { // skip 0 because its not a valid id (Control Packets MUST contain a non-zero 16-bit Packet Identifier [MQTT-2.3.1-1])
|
|
|
|
i++
|
|
|
|
looped = true
|
|
|
|
}
|
2020-01-04 13:10:36 +00:00
|
|
|
if _, ok := mids.index[i]; !ok {
|
|
|
|
mids.index[i] = t
|
2021-09-01 20:08:47 +00:00
|
|
|
mids.lastIssuedID = i
|
2020-01-04 13:10:36 +00:00
|
|
|
return i
|
|
|
|
}
|
2021-09-01 20:08:47 +00:00
|
|
|
if (looped && i == mids.lastIssuedID) || (mids.lastIssuedID == 0 && i == midMax) { // lastIssuedID will be 0 at startup
|
|
|
|
return 0 // no free ids
|
|
|
|
}
|
2020-01-04 13:10:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mids *messageIds) getToken(id uint16) tokenCompletor {
|
|
|
|
mids.RLock()
|
|
|
|
defer mids.RUnlock()
|
|
|
|
if token, ok := mids.index[id]; ok {
|
|
|
|
return token
|
|
|
|
}
|
|
|
|
return &DummyToken{id: id}
|
|
|
|
}
|
|
|
|
|
|
|
|
type DummyToken struct {
|
|
|
|
id uint16
|
|
|
|
}
|
|
|
|
|
2021-09-01 20:08:47 +00:00
|
|
|
// Wait implements the Token Wait method.
|
2020-01-04 13:10:36 +00:00
|
|
|
func (d *DummyToken) Wait() bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-09-01 20:08:47 +00:00
|
|
|
// WaitTimeout implements the Token WaitTimeout method.
|
2020-01-04 13:10:36 +00:00
|
|
|
func (d *DummyToken) WaitTimeout(t time.Duration) bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-09-01 20:08:47 +00:00
|
|
|
// Done implements the Token Done method.
|
|
|
|
func (d *DummyToken) Done() <-chan struct{} {
|
|
|
|
ch := make(chan struct{})
|
|
|
|
close(ch)
|
|
|
|
return ch
|
|
|
|
}
|
|
|
|
|
2020-01-04 13:10:36 +00:00
|
|
|
func (d *DummyToken) flowComplete() {
|
|
|
|
ERROR.Printf("A lookup for token %d returned nil\n", d.id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *DummyToken) Error() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *DummyToken) setError(e error) {}
|
2021-09-01 20:08:47 +00:00
|
|
|
|
|
|
|
// PlaceHolderToken does nothing and was implemented to allow a messageid to be reserved
|
|
|
|
// it differs from DummyToken in that calling flowComplete does not generate an error (it
|
|
|
|
// is expected that flowComplete will be called when the token is overwritten with a real token)
|
|
|
|
type PlaceHolderToken struct {
|
|
|
|
id uint16
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait implements the Token Wait method.
|
|
|
|
func (p *PlaceHolderToken) Wait() bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// WaitTimeout implements the Token WaitTimeout method.
|
|
|
|
func (p *PlaceHolderToken) WaitTimeout(t time.Duration) bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Done implements the Token Done method.
|
|
|
|
func (p *PlaceHolderToken) Done() <-chan struct{} {
|
|
|
|
ch := make(chan struct{})
|
|
|
|
close(ch)
|
|
|
|
return ch
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *PlaceHolderToken) flowComplete() {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *PlaceHolderToken) Error() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *PlaceHolderToken) setError(e error) {}
|