292 lines
7.8 KiB
Go
292 lines
7.8 KiB
Go
|
package persistence
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
|
||
|
"github.com/mochi-co/mqtt/server/system"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
|
||
|
// KSubscription is the key for subscription data.
|
||
|
KSubscription = "sub"
|
||
|
|
||
|
// KServerInfo is the key for server info data.
|
||
|
KServerInfo = "srv"
|
||
|
|
||
|
// KRetained is the key for retained messages data.
|
||
|
KRetained = "ret"
|
||
|
|
||
|
// KInflight is the key for inflight messages data.
|
||
|
KInflight = "ifm"
|
||
|
|
||
|
// KClient is the key for client data.
|
||
|
KClient = "cl"
|
||
|
)
|
||
|
|
||
|
// Store is an interface which details a persistent storage connector.
|
||
|
type Store interface {
|
||
|
Open() error
|
||
|
Close()
|
||
|
WriteSubscription(v Subscription) error
|
||
|
WriteClient(v Client) error
|
||
|
WriteInflight(v Message) error
|
||
|
WriteServerInfo(v ServerInfo) error
|
||
|
WriteRetained(v Message) error
|
||
|
|
||
|
DeleteSubscription(id string) error
|
||
|
DeleteClient(id string) error
|
||
|
DeleteInflight(id string) error
|
||
|
DeleteRetained(id string) error
|
||
|
|
||
|
ReadSubscriptions() (v []Subscription, err error)
|
||
|
ReadInflight() (v []Message, err error)
|
||
|
ReadRetained() (v []Message, err error)
|
||
|
ReadClients() (v []Client, err error)
|
||
|
ReadServerInfo() (v ServerInfo, err error)
|
||
|
}
|
||
|
|
||
|
// ServerInfo contains information and statistics about the server.
|
||
|
type ServerInfo struct {
|
||
|
system.Info // embed the system info struct.
|
||
|
ID string // the storage key.
|
||
|
}
|
||
|
|
||
|
// Subscription contains the details of a topic filter subscription.
|
||
|
type Subscription struct {
|
||
|
ID string // the storage key.
|
||
|
T string // the type of the stored data.
|
||
|
Client string // the id of the client who the subscription belongs to.
|
||
|
Filter string // the topic filter being subscribed to.
|
||
|
QoS byte // the desired QoS byte.
|
||
|
}
|
||
|
|
||
|
// Message contains the details of a retained or inflight message.
|
||
|
type Message struct {
|
||
|
Payload []byte // the message payload (if retained).
|
||
|
FixedHeader FixedHeader // the header properties of the message.
|
||
|
T string // the type of the stored data.
|
||
|
ID string // the storage key.
|
||
|
Client string // the id of the client who sent the message (if inflight).
|
||
|
TopicName string // the topic the message was sent to (if retained).
|
||
|
Sent int64 // the last time the message was sent (for retries) in unixtime (if inflight).
|
||
|
Resends int // the number of times the message was attempted to be sent (if inflight).
|
||
|
PacketID uint16 // the unique id of the packet (if inflight).
|
||
|
}
|
||
|
|
||
|
// FixedHeader contains the fixed header properties of a message.
|
||
|
type FixedHeader struct {
|
||
|
Remaining int // the number of remaining bytes in the payload.
|
||
|
Type byte // the type of the packet (PUBLISH, SUBSCRIBE, etc) from bits 7 - 4 (byte 1).
|
||
|
Qos byte // indicates the quality of service expected.
|
||
|
Dup bool // indicates if the packet was already sent at an earlier time.
|
||
|
Retain bool // whether the message should be retained.
|
||
|
}
|
||
|
|
||
|
// Client contains client data that can be persistently stored.
|
||
|
type Client struct {
|
||
|
LWT LWT // the last-will-and-testament message for the client.
|
||
|
Username []byte // the username the client authenticated with.
|
||
|
ID string // the storage key.
|
||
|
ClientID string // the id of the client.
|
||
|
T string // the type of the stored data.
|
||
|
Listener string // the last known listener id for the client
|
||
|
}
|
||
|
|
||
|
// LWT contains details about a clients LWT payload.
|
||
|
type LWT struct {
|
||
|
Message []byte // the message that shall be sent when the client disconnects.
|
||
|
Topic string // the topic the will message shall be sent to.
|
||
|
Qos byte // the quality of service desired.
|
||
|
Retain bool // indicates whether the will message should be retained
|
||
|
}
|
||
|
|
||
|
// MockStore is a mock storage backend for testing.
|
||
|
type MockStore struct {
|
||
|
Fail map[string]bool // issue errors for different methods.
|
||
|
FailOpen bool // error on open.
|
||
|
Closed bool // indicate mock store is closed.
|
||
|
Opened bool // indicate mock store is open.
|
||
|
}
|
||
|
|
||
|
// Open opens the storage instance.
|
||
|
func (s *MockStore) Open() error {
|
||
|
if s.FailOpen {
|
||
|
return errors.New("test")
|
||
|
}
|
||
|
|
||
|
s.Opened = true
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Close closes the storage instance.
|
||
|
func (s *MockStore) Close() {
|
||
|
s.Closed = true
|
||
|
}
|
||
|
|
||
|
// WriteSubscription writes a single subscription to the storage instance.
|
||
|
func (s *MockStore) WriteSubscription(v Subscription) error {
|
||
|
if _, ok := s.Fail["write_subs"]; ok {
|
||
|
return errors.New("test")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// WriteClient writes a single client to the storage instance.
|
||
|
func (s *MockStore) WriteClient(v Client) error {
|
||
|
if _, ok := s.Fail["write_clients"]; ok {
|
||
|
return errors.New("test")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// WriteInFlight writes a single InFlight message to the storage instance.
|
||
|
func (s *MockStore) WriteInflight(v Message) error {
|
||
|
if _, ok := s.Fail["write_inflight"]; ok {
|
||
|
return errors.New("test")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// WriteRetained writes a single retained message to the storage instance.
|
||
|
func (s *MockStore) WriteRetained(v Message) error {
|
||
|
if _, ok := s.Fail["write_retained"]; ok {
|
||
|
return errors.New("test")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// WriteServerInfo writes server info to the storage instance.
|
||
|
func (s *MockStore) WriteServerInfo(v ServerInfo) error {
|
||
|
if _, ok := s.Fail["write_info"]; ok {
|
||
|
return errors.New("test")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// DeleteSubscription deletes a subscription from the persistent store.
|
||
|
func (s *MockStore) DeleteSubscription(id string) error {
|
||
|
if _, ok := s.Fail["delete_subs"]; ok {
|
||
|
return errors.New("test")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// DeleteClient deletes a client from the persistent store.
|
||
|
func (s *MockStore) DeleteClient(id string) error {
|
||
|
if _, ok := s.Fail["delete_clients"]; ok {
|
||
|
return errors.New("test")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// DeleteInflight deletes an inflight message from the persistent store.
|
||
|
func (s *MockStore) DeleteInflight(id string) error {
|
||
|
if _, ok := s.Fail["delete_inflight"]; ok {
|
||
|
return errors.New("test")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// DeleteRetained deletes a retained message from the persistent store.
|
||
|
func (s *MockStore) DeleteRetained(id string) error {
|
||
|
if _, ok := s.Fail["delete_retained"]; ok {
|
||
|
return errors.New("test")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// ReadSubscriptions loads the subscriptions from the storage instance.
|
||
|
func (s *MockStore) ReadSubscriptions() (v []Subscription, err error) {
|
||
|
if _, ok := s.Fail["read_subs"]; ok {
|
||
|
return v, errors.New("test_subs")
|
||
|
}
|
||
|
|
||
|
return []Subscription{
|
||
|
{
|
||
|
ID: "test:a/b/c",
|
||
|
Client: "test",
|
||
|
Filter: "a/b/c",
|
||
|
QoS: 1,
|
||
|
T: KSubscription,
|
||
|
},
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// ReadClients loads the clients from the storage instance.
|
||
|
func (s *MockStore) ReadClients() (v []Client, err error) {
|
||
|
if _, ok := s.Fail["read_clients"]; ok {
|
||
|
return v, errors.New("test_clients")
|
||
|
}
|
||
|
|
||
|
return []Client{
|
||
|
{
|
||
|
ID: "cl_client1",
|
||
|
ClientID: "client1",
|
||
|
T: KClient,
|
||
|
Listener: "tcp1",
|
||
|
},
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// ReadInflight loads the inflight messages from the storage instance.
|
||
|
func (s *MockStore) ReadInflight() (v []Message, err error) {
|
||
|
if _, ok := s.Fail["read_inflight"]; ok {
|
||
|
return v, errors.New("test_inflight")
|
||
|
}
|
||
|
|
||
|
return []Message{
|
||
|
{
|
||
|
ID: "client1_if_100",
|
||
|
T: KInflight,
|
||
|
Client: "client1",
|
||
|
PacketID: 100,
|
||
|
TopicName: "d/e/f",
|
||
|
Payload: []byte{'y', 'e', 's'},
|
||
|
Sent: 200,
|
||
|
Resends: 1,
|
||
|
},
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// ReadRetained loads the retained messages from the storage instance.
|
||
|
func (s *MockStore) ReadRetained() (v []Message, err error) {
|
||
|
if _, ok := s.Fail["read_retained"]; ok {
|
||
|
return v, errors.New("test_retained")
|
||
|
}
|
||
|
|
||
|
return []Message{
|
||
|
{
|
||
|
ID: "client1_ret_200",
|
||
|
T: KRetained,
|
||
|
FixedHeader: FixedHeader{
|
||
|
Retain: true,
|
||
|
},
|
||
|
PacketID: 200,
|
||
|
TopicName: "a/b/c",
|
||
|
Payload: []byte{'h', 'e', 'l', 'l', 'o'},
|
||
|
Sent: 100,
|
||
|
Resends: 0,
|
||
|
},
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
//ReadServerInfo loads the server info from the storage instance.
|
||
|
func (s *MockStore) ReadServerInfo() (v ServerInfo, err error) {
|
||
|
if _, ok := s.Fail["read_info"]; ok {
|
||
|
return v, errors.New("test_info")
|
||
|
}
|
||
|
|
||
|
return ServerInfo{
|
||
|
system.Info{
|
||
|
Version: "test",
|
||
|
Started: 100,
|
||
|
},
|
||
|
KServerInfo,
|
||
|
}, nil
|
||
|
}
|