mirror of
https://github.com/mochi-mqtt/server.git
synced 2025-10-24 08:33:14 +08:00
Small fixes and cleanups (#295)
* fix typos, indicate unused returns * Add test for publishToClient acl unauthorized * Add Inline Client as a server option
This commit is contained in:
@@ -99,7 +99,7 @@ func (cl *Clients) GetByListener(id string) []*Client {
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
Properties ClientProperties // client properties
|
Properties ClientProperties // client properties
|
||||||
State ClientState // the operational state of the client.
|
State ClientState // the operational state of the client.
|
||||||
Net ClientConnection // network connection state of the clinet
|
Net ClientConnection // network connection state of the client
|
||||||
ID string // the client id.
|
ID string // the client id.
|
||||||
ops *ops // ops provides a reference to server ops.
|
ops *ops // ops provides a reference to server ops.
|
||||||
sync.RWMutex // mutex
|
sync.RWMutex // mutex
|
||||||
@@ -111,7 +111,7 @@ type ClientConnection struct {
|
|||||||
bconn *bufio.ReadWriter // a buffered net.Conn for reading packets
|
bconn *bufio.ReadWriter // a buffered net.Conn for reading packets
|
||||||
Remote string // the remote address of the client
|
Remote string // the remote address of the client
|
||||||
Listener string // listener id of the client
|
Listener string // listener id of the client
|
||||||
Inline bool // client is an inline programmetic client
|
Inline bool // if true, the client is the built-in 'inline' embedded client
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientProperties contains the properties which define the client behaviour.
|
// ClientProperties contains the properties which define the client behaviour.
|
||||||
@@ -134,7 +134,7 @@ type Will struct {
|
|||||||
Retain bool // -
|
Retain bool // -
|
||||||
}
|
}
|
||||||
|
|
||||||
// State tracks the state of the client.
|
// ClientState tracks the state of the client.
|
||||||
type ClientState struct {
|
type ClientState struct {
|
||||||
TopicAliases TopicAliases // a map of topic aliases
|
TopicAliases TopicAliases // a map of topic aliases
|
||||||
stopCause atomic.Value // reason for stopping
|
stopCause atomic.Value // reason for stopping
|
||||||
@@ -311,7 +311,7 @@ func (cl *Client) ResendInflightMessages(force bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearInflights deletes all inflight messages for the client, eg. for a disconnected user with a clean session.
|
// ClearInflights deletes all inflight messages for the client, e.g. for a disconnected user with a clean session.
|
||||||
func (cl *Client) ClearInflights(now, maximumExpiry int64) []uint16 {
|
func (cl *Client) ClearInflights(now, maximumExpiry int64) []uint16 {
|
||||||
deleted := []uint16{}
|
deleted := []uint16{}
|
||||||
for _, tk := range cl.State.Inflight.GetAll(false) {
|
for _, tk := range cl.State.Inflight.GetAll(false) {
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ func TestClientNextPacketIDOverflow(t *testing.T) {
|
|||||||
cl.State.Inflight.internal[uint16(i)] = packets.Packet{}
|
cl.State.Inflight.internal[uint16(i)] = packets.Packet{}
|
||||||
}
|
}
|
||||||
|
|
||||||
cl.State.packetID = uint32(cl.ops.options.Capabilities.maximumPacketID - 1)
|
cl.State.packetID = cl.ops.options.Capabilities.maximumPacketID - 1
|
||||||
i, err := cl.NextPacketID()
|
i, err := cl.NextPacketID()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, cl.ops.options.Capabilities.maximumPacketID, i)
|
require.Equal(t, cl.ops.options.Capabilities.maximumPacketID, i)
|
||||||
@@ -303,7 +303,7 @@ func TestClientResendInflightMessages(t *testing.T) {
|
|||||||
err := cl.ResendInflightMessages(true)
|
err := cl.ResendInflightMessages(true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
w.Close()
|
_ = w.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
buf, err := io.ReadAll(r)
|
buf, err := io.ReadAll(r)
|
||||||
@@ -315,7 +315,7 @@ func TestClientResendInflightMessages(t *testing.T) {
|
|||||||
func TestClientResendInflightMessagesWriteFailure(t *testing.T) {
|
func TestClientResendInflightMessagesWriteFailure(t *testing.T) {
|
||||||
pk1 := packets.TPacketData[packets.Publish].Get(packets.TPublishQos1Dup)
|
pk1 := packets.TPacketData[packets.Publish].Get(packets.TPublishQos1Dup)
|
||||||
cl, r, _ := newTestClient()
|
cl, r, _ := newTestClient()
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
|
|
||||||
cl.State.Inflight.Set(*pk1.Packet)
|
cl.State.Inflight.Set(*pk1.Packet)
|
||||||
require.Equal(t, 1, cl.State.Inflight.Len())
|
require.Equal(t, 1, cl.State.Inflight.Len())
|
||||||
@@ -342,8 +342,8 @@ func TestClientReadFixedHeader(t *testing.T) {
|
|||||||
|
|
||||||
defer cl.Stop(errClientStop)
|
defer cl.Stop(errClientStop)
|
||||||
go func() {
|
go func() {
|
||||||
r.Write([]byte{packets.Connect << 4, 0x00})
|
_, _ = r.Write([]byte{packets.Connect << 4, 0x00})
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fh := new(packets.FixedHeader)
|
fh := new(packets.FixedHeader)
|
||||||
@@ -357,8 +357,8 @@ func TestClientReadFixedHeaderDecodeError(t *testing.T) {
|
|||||||
defer cl.Stop(errClientStop)
|
defer cl.Stop(errClientStop)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
r.Write([]byte{packets.Connect<<4 | 1<<1, 0x00, 0x00})
|
_, _ = r.Write([]byte{packets.Connect<<4 | 1<<1, 0x00, 0x00})
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fh := new(packets.FixedHeader)
|
fh := new(packets.FixedHeader)
|
||||||
@@ -372,8 +372,8 @@ func TestClientReadFixedHeaderPacketOversized(t *testing.T) {
|
|||||||
defer cl.Stop(errClientStop)
|
defer cl.Stop(errClientStop)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
r.Write(packets.TPacketData[packets.Publish].Get(packets.TPublishQos1Dup).RawBytes)
|
_, _ = r.Write(packets.TPacketData[packets.Publish].Get(packets.TPublishQos1Dup).RawBytes)
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fh := new(packets.FixedHeader)
|
fh := new(packets.FixedHeader)
|
||||||
@@ -387,7 +387,7 @@ func TestClientReadFixedHeaderReadEOF(t *testing.T) {
|
|||||||
defer cl.Stop(errClientStop)
|
defer cl.Stop(errClientStop)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fh := new(packets.FixedHeader)
|
fh := new(packets.FixedHeader)
|
||||||
@@ -401,8 +401,8 @@ func TestClientReadFixedHeaderNoLengthTerminator(t *testing.T) {
|
|||||||
defer cl.Stop(errClientStop)
|
defer cl.Stop(errClientStop)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
r.Write([]byte{packets.Connect << 4, 0xd5, 0x86, 0xf9, 0x9e, 0x01})
|
_, _ = r.Write([]byte{packets.Connect << 4, 0xd5, 0x86, 0xf9, 0x9e, 0x01})
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fh := new(packets.FixedHeader)
|
fh := new(packets.FixedHeader)
|
||||||
@@ -414,7 +414,7 @@ func TestClientReadOK(t *testing.T) {
|
|||||||
cl, r, _ := newTestClient()
|
cl, r, _ := newTestClient()
|
||||||
defer cl.Stop(errClientStop)
|
defer cl.Stop(errClientStop)
|
||||||
go func() {
|
go func() {
|
||||||
r.Write([]byte{
|
_, _ = r.Write([]byte{
|
||||||
packets.Publish << 4, 18, // Fixed header
|
packets.Publish << 4, 18, // Fixed header
|
||||||
0, 5, // Topic Name - LSB+MSB
|
0, 5, // Topic Name - LSB+MSB
|
||||||
'a', '/', 'b', '/', 'c', // Topic Name
|
'a', '/', 'b', '/', 'c', // Topic Name
|
||||||
@@ -424,7 +424,7 @@ func TestClientReadOK(t *testing.T) {
|
|||||||
'd', '/', 'e', '/', 'f', // Topic Name
|
'd', '/', 'e', '/', 'f', // Topic Name
|
||||||
'y', 'e', 'a', 'h', // Payload
|
'y', 'e', 'a', 'h', // Payload
|
||||||
})
|
})
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var pks []packets.Packet
|
var pks []packets.Packet
|
||||||
@@ -499,10 +499,10 @@ func TestClientReadFixedHeaderError(t *testing.T) {
|
|||||||
cl, r, _ := newTestClient()
|
cl, r, _ := newTestClient()
|
||||||
defer cl.Stop(errClientStop)
|
defer cl.Stop(errClientStop)
|
||||||
go func() {
|
go func() {
|
||||||
r.Write([]byte{
|
_, _ = r.Write([]byte{
|
||||||
packets.Publish << 4, 11, // Fixed header
|
packets.Publish << 4, 11, // Fixed header
|
||||||
})
|
})
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
cl.Net.bconn = nil
|
cl.Net.bconn = nil
|
||||||
@@ -516,13 +516,13 @@ func TestClientReadReadHandlerErr(t *testing.T) {
|
|||||||
cl, r, _ := newTestClient()
|
cl, r, _ := newTestClient()
|
||||||
defer cl.Stop(errClientStop)
|
defer cl.Stop(errClientStop)
|
||||||
go func() {
|
go func() {
|
||||||
r.Write([]byte{
|
_, _ = r.Write([]byte{
|
||||||
packets.Publish << 4, 11, // Fixed header
|
packets.Publish << 4, 11, // Fixed header
|
||||||
0, 5, // Topic Name - LSB+MSB
|
0, 5, // Topic Name - LSB+MSB
|
||||||
'd', '/', 'e', '/', 'f', // Topic Name
|
'd', '/', 'e', '/', 'f', // Topic Name
|
||||||
'y', 'e', 'a', 'h', // Payload
|
'y', 'e', 'a', 'h', // Payload
|
||||||
})
|
})
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := cl.Read(func(cl *Client, pk packets.Packet) error {
|
err := cl.Read(func(cl *Client, pk packets.Packet) error {
|
||||||
@@ -536,13 +536,13 @@ func TestClientReadReadPacketOK(t *testing.T) {
|
|||||||
cl, r, _ := newTestClient()
|
cl, r, _ := newTestClient()
|
||||||
defer cl.Stop(errClientStop)
|
defer cl.Stop(errClientStop)
|
||||||
go func() {
|
go func() {
|
||||||
r.Write([]byte{
|
_, _ = r.Write([]byte{
|
||||||
packets.Publish << 4, 11, // Fixed header
|
packets.Publish << 4, 11, // Fixed header
|
||||||
0, 5,
|
0, 5,
|
||||||
'd', '/', 'e', '/', 'f',
|
'd', '/', 'e', '/', 'f',
|
||||||
'y', 'e', 'a', 'h',
|
'y', 'e', 'a', 'h',
|
||||||
})
|
})
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fh := new(packets.FixedHeader)
|
fh := new(packets.FixedHeader)
|
||||||
@@ -573,7 +573,7 @@ func TestClientReadPacket(t *testing.T) {
|
|||||||
t.Run(tt.Desc, func(t *testing.T) {
|
t.Run(tt.Desc, func(t *testing.T) {
|
||||||
atomic.StoreInt64(&cl.ops.info.PacketsReceived, 0)
|
atomic.StoreInt64(&cl.ops.info.PacketsReceived, 0)
|
||||||
go func() {
|
go func() {
|
||||||
r.Write(tt.RawBytes)
|
_, _ = r.Write(tt.RawBytes)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fh := new(packets.FixedHeader)
|
fh := new(packets.FixedHeader)
|
||||||
@@ -600,7 +600,7 @@ func TestClientReadPacket(t *testing.T) {
|
|||||||
|
|
||||||
func TestClientReadPacketInvalidTypeError(t *testing.T) {
|
func TestClientReadPacketInvalidTypeError(t *testing.T) {
|
||||||
cl, _, _ := newTestClient()
|
cl, _, _ := newTestClient()
|
||||||
cl.Net.Conn.Close()
|
_ = cl.Net.Conn.Close()
|
||||||
_, err := cl.ReadPacket(&packets.FixedHeader{})
|
_, err := cl.ReadPacket(&packets.FixedHeader{})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Contains(t, err.Error(), "invalid packet type")
|
require.Contains(t, err.Error(), "invalid packet type")
|
||||||
@@ -624,7 +624,7 @@ func TestClientWritePacket(t *testing.T) {
|
|||||||
require.NoError(t, err, pkInfo, tt.Case, tt.Desc)
|
require.NoError(t, err, pkInfo, tt.Case, tt.Desc)
|
||||||
|
|
||||||
time.Sleep(2 * time.Millisecond)
|
time.Sleep(2 * time.Millisecond)
|
||||||
cl.Net.Conn.Close()
|
_ = cl.Net.Conn.Close()
|
||||||
|
|
||||||
require.Equal(t, tt.RawBytes, <-o, pkInfo, tt.Case, tt.Desc)
|
require.Equal(t, tt.RawBytes, <-o, pkInfo, tt.Case, tt.Desc)
|
||||||
|
|
||||||
@@ -660,13 +660,13 @@ func TestClientReadPacketReadingError(t *testing.T) {
|
|||||||
cl, r, _ := newTestClient()
|
cl, r, _ := newTestClient()
|
||||||
defer cl.Stop(errClientStop)
|
defer cl.Stop(errClientStop)
|
||||||
go func() {
|
go func() {
|
||||||
r.Write([]byte{
|
_, _ = r.Write([]byte{
|
||||||
0, 11, // Fixed header
|
0, 11, // Fixed header
|
||||||
0, 5,
|
0, 5,
|
||||||
'd', '/', 'e', '/', 'f',
|
'd', '/', 'e', '/', 'f',
|
||||||
'y', 'e', 'a', 'h',
|
'y', 'e', 'a', 'h',
|
||||||
})
|
})
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
_, err := cl.ReadPacket(&packets.FixedHeader{
|
_, err := cl.ReadPacket(&packets.FixedHeader{
|
||||||
@@ -680,13 +680,13 @@ func TestClientReadPacketReadUnknown(t *testing.T) {
|
|||||||
cl, r, _ := newTestClient()
|
cl, r, _ := newTestClient()
|
||||||
defer cl.Stop(errClientStop)
|
defer cl.Stop(errClientStop)
|
||||||
go func() {
|
go func() {
|
||||||
r.Write([]byte{
|
_, _ = r.Write([]byte{
|
||||||
0, 11, // Fixed header
|
0, 11, // Fixed header
|
||||||
0, 5,
|
0, 5,
|
||||||
'd', '/', 'e', '/', 'f',
|
'd', '/', 'e', '/', 'f',
|
||||||
'y', 'e', 'a', 'h',
|
'y', 'e', 'a', 'h',
|
||||||
})
|
})
|
||||||
r.Close()
|
_ = r.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
_, err := cl.ReadPacket(&packets.FixedHeader{
|
_, err := cl.ReadPacket(&packets.FixedHeader{
|
||||||
@@ -706,7 +706,7 @@ func TestClientWritePacketWriteNoConn(t *testing.T) {
|
|||||||
|
|
||||||
func TestClientWritePacketWriteError(t *testing.T) {
|
func TestClientWritePacketWriteError(t *testing.T) {
|
||||||
cl, _, _ := newTestClient()
|
cl, _, _ := newTestClient()
|
||||||
cl.Net.Conn.Close()
|
_ = cl.Net.Conn.Close()
|
||||||
|
|
||||||
err := cl.WritePacket(*pkTable[1].Packet)
|
err := cl.WritePacket(*pkTable[1].Packet)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,6 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,6 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,6 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,6 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ func main() {
|
|||||||
done <- true
|
done <- true
|
||||||
}()
|
}()
|
||||||
|
|
||||||
server := mqtt.New(nil)
|
server := mqtt.New(&mqtt.Options{
|
||||||
|
InlineClient: true, // you must enable inline client to use direct publishing and subscribing.
|
||||||
|
})
|
||||||
_ = server.AddHook(new(auth.AllowHook), nil)
|
_ = server.AddHook(new(auth.AllowHook), nil)
|
||||||
|
|
||||||
// Start the server
|
// Start the server
|
||||||
@@ -50,12 +52,13 @@ func main() {
|
|||||||
server.Log.Info("inline client received message from subscription", "client", cl.ID, "subscriptionId", sub.Identifier, "topic", pk.TopicName, "payload", string(pk.Payload))
|
server.Log.Info("inline client received message from subscription", "client", cl.ID, "subscriptionId", sub.Identifier, "topic", pk.TopicName, "payload", string(pk.Payload))
|
||||||
}
|
}
|
||||||
server.Log.Info("inline client subscribing")
|
server.Log.Info("inline client subscribing")
|
||||||
server.Subscribe("direct/#", 1, callbackFn)
|
_ = server.Subscribe("direct/#", 1, callbackFn)
|
||||||
server.Subscribe("direct/#", 2, callbackFn)
|
_ = server.Subscribe("direct/#", 2, callbackFn)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// There is a shorthand convenience function, Publish, for easily sending
|
// There is a shorthand convenience function, Publish, for easily sending publish packets if you are not
|
||||||
// publish packets if you are not concerned with creating your own packets.
|
// concerned with creating your own packets. If you want to have more control over your packets, you can
|
||||||
|
//directly inject a packet of any kind into the broker. See examples/hooks/main.go for usage.
|
||||||
go func() {
|
go func() {
|
||||||
for range time.Tick(time.Second * 3) {
|
for range time.Tick(time.Second * 3) {
|
||||||
err := server.Publish("direct/publish", []byte("scheduled message"), false, 0)
|
err := server.Publish("direct/publish", []byte("scheduled message"), false, 0)
|
||||||
@@ -70,23 +73,8 @@ func main() {
|
|||||||
time.Sleep(time.Second * 10)
|
time.Sleep(time.Second * 10)
|
||||||
// Unsubscribe from the same filter to stop receiving messages.
|
// Unsubscribe from the same filter to stop receiving messages.
|
||||||
server.Log.Info("inline client unsubscribing")
|
server.Log.Info("inline client unsubscribing")
|
||||||
server.Unsubscribe("direct/#", 1)
|
_ = server.Unsubscribe("direct/#", 1)
|
||||||
}()
|
}()
|
||||||
// If you want to have more control over your packets, you can directly inject a packet of any kind into the broker.
|
|
||||||
//go func() {
|
|
||||||
// for range time.Tick(time.Second * 5) {
|
|
||||||
// err := server.InjectPacket(cl, packets.Packet{
|
|
||||||
// FixedHeader: packets.FixedHeader{
|
|
||||||
// Type: packets.Publish,
|
|
||||||
// },
|
|
||||||
// TopicName: "direct/publish",
|
|
||||||
// Payload: []byte("injected scheduled message"),
|
|
||||||
// })
|
|
||||||
// if err != nil {
|
|
||||||
// log.Fatal(err)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}()
|
|
||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,6 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,6 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,6 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,6 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,6 +112,6 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,6 @@ func main() {
|
|||||||
|
|
||||||
<-done
|
<-done
|
||||||
server.Log.Warn("caught signal, stopping...")
|
server.Log.Warn("caught signal, stopping...")
|
||||||
server.Close()
|
_ = server.Close()
|
||||||
server.Log.Info("main.go finished")
|
server.Log.Info("main.go finished")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ func (r RString) Matches(a string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FilterMatches returns true if a filter matches a topic rule.
|
// FilterMatches returns true if a filter matches a topic rule.
|
||||||
func (f RString) FilterMatches(a string) bool {
|
func (r RString) FilterMatches(a string) bool {
|
||||||
_, ok := MatchTopic(string(f), a)
|
_, ok := MatchTopic(string(r), a)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ func (l *Ledger) AuthOk(cl *mqtt.Client, pk packets.Packet) (n int, ok bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ACLOk returns true if the rules indicate the user is allowed to read or write to
|
// ACLOk returns true if the rules indicate the user is allowed to read or write to
|
||||||
// a specific filter or topic respectively, based on the write bool.
|
// a specific filter or topic respectively, based on the `write` bool.
|
||||||
func (l *Ledger) ACLOk(cl *mqtt.Client, topic string, write bool) (n int, ok bool) {
|
func (l *Ledger) ACLOk(cl *mqtt.Client, topic string, write bool) (n int, ok bool) {
|
||||||
// If the users map is set, always check for a predefined user first instead
|
// If the users map is set, always check for a predefined user first instead
|
||||||
// of iterating through global rules.
|
// of iterating through global rules.
|
||||||
@@ -209,7 +209,7 @@ func (l *Ledger) ACLOk(cl *mqtt.Client, topic string, write bool) (n int, ok boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for filter, _ := range rule.Filters {
|
for filter := range rule.Filters {
|
||||||
if filter.FilterMatches(topic) {
|
if filter.FilterMatches(topic) {
|
||||||
return n, false
|
return n, false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -561,17 +561,17 @@ func TestLedgerUpdate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
new := &Ledger{
|
n := &Ledger{
|
||||||
Auth: AuthRules{
|
Auth: AuthRules{
|
||||||
{Remote: "127.0.0.1", Allow: true},
|
{Remote: "127.0.0.1", Allow: true},
|
||||||
{Remote: "192.168.*", Allow: true},
|
{Remote: "192.168.*", Allow: true},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
old.Update(new)
|
old.Update(n)
|
||||||
require.Len(t, old.Auth, 2)
|
require.Len(t, old.Auth, 2)
|
||||||
require.Equal(t, RString("192.168.*"), old.Auth[1].Remote)
|
require.Equal(t, RString("192.168.*"), old.Auth[1].Remote)
|
||||||
require.NotSame(t, new, old)
|
require.NotSame(t, n, old)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLedgerToJSON(t *testing.T) {
|
func TestLedgerToJSON(t *testing.T) {
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ func (h *Hook) OnQosDropped(cl *mqtt.Client, pk packets.Packet) {
|
|||||||
h.Log.Debug("inflight dropped", "m", h.packetMeta(pk))
|
h.Log.Debug("inflight dropped", "m", h.packetMeta(pk))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnLWTSent is called when a will message has been issued from a disconnecting client.
|
// OnLWTSent is called when a Will Message has been issued from a disconnecting client.
|
||||||
func (h *Hook) OnLWTSent(cl *mqtt.Client, pk packets.Packet) {
|
func (h *Hook) OnLWTSent(cl *mqtt.Client, pk packets.Packet) {
|
||||||
h.Log.Debug("sent lwt for client", "method", "OnLWTSent", "client", cl.ID)
|
h.Log.Debug("sent lwt for client", "method", "OnLWTSent", "client", cl.ID)
|
||||||
}
|
}
|
||||||
@@ -136,25 +136,25 @@ func (h *Hook) StoredClients() (v []storage.Client, err error) {
|
|||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoredClients is called when the server restores subscriptions from a store.
|
// StoredSubscriptions is called when the server restores subscriptions from a store.
|
||||||
func (h *Hook) StoredSubscriptions() (v []storage.Subscription, err error) {
|
func (h *Hook) StoredSubscriptions() (v []storage.Subscription, err error) {
|
||||||
h.Log.Debug("", "method", "StoredSubscriptions")
|
h.Log.Debug("", "method", "StoredSubscriptions")
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoredClients is called when the server restores retained messages from a store.
|
// StoredRetainedMessages is called when the server restores retained messages from a store.
|
||||||
func (h *Hook) StoredRetainedMessages() (v []storage.Message, err error) {
|
func (h *Hook) StoredRetainedMessages() (v []storage.Message, err error) {
|
||||||
h.Log.Debug("", "method", "StoredRetainedMessages")
|
h.Log.Debug("", "method", "StoredRetainedMessages")
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoredClients is called when the server restores inflight messages from a store.
|
// StoredInflightMessages is called when the server restores inflight messages from a store.
|
||||||
func (h *Hook) StoredInflightMessages() (v []storage.Message, err error) {
|
func (h *Hook) StoredInflightMessages() (v []storage.Message, err error) {
|
||||||
h.Log.Debug("", "method", "StoredInflightMessages")
|
h.Log.Debug("", "method", "StoredInflightMessages")
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoredClients is called when the server restores system info from a store.
|
// StoredSysInfo is called when the server restores system info from a store.
|
||||||
func (h *Hook) StoredSysInfo() (v storage.SystemInfo, err error) {
|
func (h *Hook) StoredSysInfo() (v storage.SystemInfo, err error) {
|
||||||
h.Log.Debug("", "method", "StoredSysInfo")
|
h.Log.Debug("", "method", "StoredSysInfo")
|
||||||
|
|
||||||
|
|||||||
@@ -128,8 +128,7 @@ func (h *Hook) OnSessionEstablished(cl *mqtt.Client, pk packets.Packet) {
|
|||||||
h.updateClient(cl)
|
h.updateClient(cl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnWillSent is called when a client sends a will message and the will message is removed
|
// OnWillSent is called when a client sends a Will Message and the Will Message is removed from the client record.
|
||||||
// from the client record.
|
|
||||||
func (h *Hook) OnWillSent(cl *mqtt.Client, pk packets.Packet) {
|
func (h *Hook) OnWillSent(cl *mqtt.Client, pk packets.Packet) {
|
||||||
h.updateClient(cl)
|
h.updateClient(cl)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func teardown(t *testing.T, path string, h *Hook) {
|
func teardown(t *testing.T, path string, h *Hook) {
|
||||||
h.Stop()
|
_ = h.Stop()
|
||||||
h.db.Badger().Close()
|
_ = h.db.Badger().Close()
|
||||||
err := os.RemoveAll("./" + strings.Replace(path, "..", "", -1))
|
err := os.RemoveAll("./" + strings.Replace(path, "..", "", -1))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// SPDX-FileCopyrightText: 2022 mochi-mqtt, mochi-co
|
// SPDX-FileCopyrightText: 2022 mochi-mqtt, mochi-co
|
||||||
// SPDX-FileContributor: mochi-co
|
// SPDX-FileContributor: mochi-co
|
||||||
// package bolt is provided for historical compatibility and may not be actively updated, you should use the badger hook instead.
|
|
||||||
|
// Package bolt is provided for historical compatibility and may not be actively updated, you should use the badger hook instead.
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -132,8 +133,7 @@ func (h *Hook) OnSessionEstablished(cl *mqtt.Client, pk packets.Packet) {
|
|||||||
h.updateClient(cl)
|
h.updateClient(cl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnWillSent is called when a client sends a will message and the will message is removed
|
// OnWillSent is called when a client sends a Will Message and the Will Message is removed from the client record.
|
||||||
// from the client record.
|
|
||||||
func (h *Hook) OnWillSent(cl *mqtt.Client, pk packets.Packet) {
|
func (h *Hook) OnWillSent(cl *mqtt.Client, pk packets.Packet) {
|
||||||
h.updateClient(cl)
|
h.updateClient(cl)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func teardown(t *testing.T, path string, h *Hook) {
|
func teardown(t *testing.T, path string, h *Hook) {
|
||||||
h.Stop()
|
_ = h.Stop()
|
||||||
err := os.Remove(path)
|
err := os.Remove(path)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/mochi-mqtt/server/v2/packets"
|
"github.com/mochi-mqtt/server/v2/packets"
|
||||||
"github.com/mochi-mqtt/server/v2/system"
|
"github.com/mochi-mqtt/server/v2/system"
|
||||||
|
|
||||||
redis "github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defaultAddr is the default address to the redis service.
|
// defaultAddr is the default address to the redis service.
|
||||||
@@ -134,7 +134,7 @@ func (h *Hook) Init(config any) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the redis connection.
|
// Stop closes the redis connection.
|
||||||
func (h *Hook) Stop() error {
|
func (h *Hook) Stop() error {
|
||||||
h.Log.Info("disconnecting from redis service")
|
h.Log.Info("disconnecting from redis service")
|
||||||
|
|
||||||
@@ -146,8 +146,7 @@ func (h *Hook) OnSessionEstablished(cl *mqtt.Client, pk packets.Packet) {
|
|||||||
h.updateClient(cl)
|
h.updateClient(cl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnWillSent is called when a client sends a will message and the will message is removed
|
// OnWillSent is called when a client sends a Will Message and the Will Message is removed from the client record.
|
||||||
// from the client record.
|
|
||||||
func (h *Hook) OnWillSent(cl *mqtt.Client, pk packets.Packet) {
|
func (h *Hook) OnWillSent(cl *mqtt.Client, pk packets.Packet) {
|
||||||
h.updateClient(cl)
|
h.updateClient(cl)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ var (
|
|||||||
ErrDBFileNotOpen = errors.New("db file not open")
|
ErrDBFileNotOpen = errors.New("db file not open")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client is a storable representation of an mqtt client.
|
// Client is a storable representation of an MQTT client.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
Will ClientWill `json:"will"` // will topic and payload data if applicable
|
Will ClientWill `json:"will"` // will topic and payload data if applicable
|
||||||
Properties ClientProperties `json:"properties"` // the connect properties for the client
|
Properties ClientProperties `json:"properties"` // the connect properties for the client
|
||||||
@@ -147,7 +147,7 @@ func (d *Message) ToPacket() packets.Packet {
|
|||||||
return pk
|
return pk
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscription is a storable representation of an mqtt subscription.
|
// Subscription is a storable representation of an MQTT subscription.
|
||||||
type Subscription struct {
|
type Subscription struct {
|
||||||
T string `json:"t"`
|
T string `json:"t"`
|
||||||
ID string `json:"id" storm:"id"`
|
ID string `json:"id" storm:"id"`
|
||||||
|
|||||||
@@ -79,9 +79,9 @@ func (l *HTTPHealthCheck) Init(_ *slog.Logger) error {
|
|||||||
// Serve starts listening for new connections and serving responses.
|
// Serve starts listening for new connections and serving responses.
|
||||||
func (l *HTTPHealthCheck) Serve(establish EstablishFn) {
|
func (l *HTTPHealthCheck) Serve(establish EstablishFn) {
|
||||||
if l.listen.TLSConfig != nil {
|
if l.listen.TLSConfig != nil {
|
||||||
l.listen.ListenAndServeTLS("", "")
|
_ = l.listen.ListenAndServeTLS("", "")
|
||||||
} else {
|
} else {
|
||||||
l.listen.ListenAndServe()
|
_ = l.listen.ListenAndServe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ func (l *HTTPHealthCheck) Close(closeClients CloseFn) {
|
|||||||
if atomic.CompareAndSwapUint32(&l.end, 0, 1) {
|
if atomic.CompareAndSwapUint32(&l.end, 0, 1) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
l.listen.Shutdown(ctx)
|
_ = l.listen.Shutdown(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
closeClients(l.id)
|
closeClients(l.id)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func TestHTTPHealthCheckTLSProtocol(t *testing.T) {
|
|||||||
TLSConfig: tlsConfigBasic,
|
TLSConfig: tlsConfigBasic,
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Init(logger)
|
_ = l.Init(logger)
|
||||||
require.Equal(t, "https", l.Protocol())
|
require.Equal(t, "https", l.Protocol())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,9 +81,9 @@ func (l *HTTPStats) Init(_ *slog.Logger) error {
|
|||||||
// Serve starts listening for new connections and serving responses.
|
// Serve starts listening for new connections and serving responses.
|
||||||
func (l *HTTPStats) Serve(establish EstablishFn) {
|
func (l *HTTPStats) Serve(establish EstablishFn) {
|
||||||
if l.listen.TLSConfig != nil {
|
if l.listen.TLSConfig != nil {
|
||||||
l.listen.ListenAndServeTLS("", "")
|
_ = l.listen.ListenAndServeTLS("", "")
|
||||||
} else {
|
} else {
|
||||||
l.listen.ListenAndServe()
|
_ = l.listen.ListenAndServe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ func (l *HTTPStats) Close(closeClients CloseFn) {
|
|||||||
if atomic.CompareAndSwapUint32(&l.end, 0, 1) {
|
if atomic.CompareAndSwapUint32(&l.end, 0, 1) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
l.listen.Shutdown(ctx)
|
_ = l.listen.Shutdown(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
closeClients(l.id)
|
closeClients(l.id)
|
||||||
@@ -107,8 +107,8 @@ func (l *HTTPStats) jsonHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
out, err := json.MarshalIndent(info, "", "\t")
|
out, err := json.MarshalIndent(info, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
io.WriteString(w, err.Error())
|
_, _ = io.WriteString(w, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Write(out)
|
_, _ = w.Write(out)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestHTTPStatsTLSProtocol(t *testing.T) {
|
|||||||
TLSConfig: tlsConfigBasic,
|
TLSConfig: tlsConfigBasic,
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
l.Init(logger)
|
_ = l.Init(logger)
|
||||||
require.Equal(t, "https", l.Protocol())
|
require.Equal(t, "https", l.Protocol())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type Config struct {
|
|||||||
// EstablishFn is a callback function for establishing new clients.
|
// EstablishFn is a callback function for establishing new clients.
|
||||||
type EstablishFn func(id string, c net.Conn) error
|
type EstablishFn func(id string, c net.Conn) error
|
||||||
|
|
||||||
// CloseFunc is a callback function for closing all listener clients.
|
// CloseFn is a callback function for closing all listener clients.
|
||||||
type CloseFn func(id string)
|
type CloseFn func(id string)
|
||||||
|
|
||||||
// Listener is an interface for network listeners. A network listener listens
|
// Listener is an interface for network listeners. A network listener listens
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func TestMockEstablisher(t *testing.T) {
|
|||||||
_, w := net.Pipe()
|
_, w := net.Pipe()
|
||||||
err := MockEstablisher("t1", w)
|
err := MockEstablisher("t1", w)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
w.Close()
|
_ = w.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewMockListener(t *testing.T) {
|
func TestNewMockListener(t *testing.T) {
|
||||||
@@ -86,7 +86,7 @@ func TestMockListenerServe(t *testing.T) {
|
|||||||
require.Equal(t, true, closed)
|
require.Equal(t, true, closed)
|
||||||
<-o
|
<-o
|
||||||
|
|
||||||
mocked.Init(nil)
|
_ = mocked.Init(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockListenerClose(t *testing.T) {
|
func TestMockListenerClose(t *testing.T) {
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ func TestNetEstablishThenEnd(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
net.Dial("tcp", n.Addr().String())
|
_, _ = net.Dial("tcp", n.Addr().String())
|
||||||
require.Equal(t, true, <-established)
|
require.Equal(t, true, <-established)
|
||||||
l.Close(MockCloser)
|
l.Close(MockCloser)
|
||||||
<-o
|
<-o
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func TestTCPProtocolTLS(t *testing.T) {
|
|||||||
TLSConfig: tlsConfigBasic,
|
TLSConfig: tlsConfigBasic,
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Init(logger)
|
_ = l.Init(logger)
|
||||||
defer l.listen.Close()
|
defer l.listen.Close()
|
||||||
require.Equal(t, "tcp", l.Protocol())
|
require.Equal(t, "tcp", l.Protocol())
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ func TestTCPEstablishThenEnd(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
net.Dial("tcp", l.listen.Addr().String())
|
_, _ = net.Dial("tcp", l.listen.Addr().String())
|
||||||
require.Equal(t, true, <-established)
|
require.Equal(t, true, <-established)
|
||||||
l.Close(MockCloser)
|
l.Close(MockCloser)
|
||||||
<-o
|
<-o
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func TestUnixSockEstablishThenEnd(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
net.Dial("unix", l.listen.Addr().String())
|
_, _ = net.Dial("unix", l.listen.Addr().String())
|
||||||
require.Equal(t, true, <-established)
|
require.Equal(t, true, <-established)
|
||||||
l.Close(MockCloser)
|
l.Close(MockCloser)
|
||||||
<-o
|
<-o
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type Websocket struct { // [MQTT-4.2.0-1]
|
|||||||
id string // the internal id of the listener
|
id string // the internal id of the listener
|
||||||
address string // the network address to bind to
|
address string // the network address to bind to
|
||||||
config *Config // configuration values for the listener
|
config *Config // configuration values for the listener
|
||||||
listen *http.Server // an http server for serving websocket connections
|
listen *http.Server // a http server for serving websocket connections
|
||||||
log *slog.Logger // server logger
|
log *slog.Logger // server logger
|
||||||
establish EstablishFn // the server's establish connection handler
|
establish EstablishFn // the server's establish connection handler
|
||||||
upgrader *websocket.Upgrader // upgrade the incoming http/tcp connection to a websocket compliant connection.
|
upgrader *websocket.Upgrader // upgrade the incoming http/tcp connection to a websocket compliant connection.
|
||||||
@@ -112,9 +112,9 @@ func (l *Websocket) Serve(establish EstablishFn) {
|
|||||||
l.establish = establish
|
l.establish = establish
|
||||||
|
|
||||||
if l.listen.TLSConfig != nil {
|
if l.listen.TLSConfig != nil {
|
||||||
l.listen.ListenAndServeTLS("", "")
|
_ = l.listen.ListenAndServeTLS("", "")
|
||||||
} else {
|
} else {
|
||||||
l.listen.ListenAndServe()
|
_ = l.listen.ListenAndServe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ func (l *Websocket) Close(closeClients CloseFn) {
|
|||||||
if atomic.CompareAndSwapUint32(&l.end, 0, 1) {
|
if atomic.CompareAndSwapUint32(&l.end, 0, 1) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
l.listen.Shutdown(ctx)
|
_ = l.listen.Shutdown(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
closeClients(l.id)
|
closeClients(l.id)
|
||||||
@@ -137,7 +137,7 @@ type wsConn struct {
|
|||||||
net.Conn
|
net.Conn
|
||||||
c *websocket.Conn
|
c *websocket.Conn
|
||||||
|
|
||||||
// reader for the current message (may be nil)
|
// reader for the current message (can be nil)
|
||||||
r io.Reader
|
r io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,14 +37,14 @@ func TestWebsocketProtocol(t *testing.T) {
|
|||||||
require.Equal(t, "ws", l.Protocol())
|
require.Equal(t, "ws", l.Protocol())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWebsocketProtocoTLS(t *testing.T) {
|
func TestWebsocketProtocolTLS(t *testing.T) {
|
||||||
l := NewWebsocket("t1", testAddr, &Config{
|
l := NewWebsocket("t1", testAddr, &Config{
|
||||||
TLSConfig: tlsConfigBasic,
|
TLSConfig: tlsConfigBasic,
|
||||||
})
|
})
|
||||||
require.Equal(t, "wss", l.Protocol())
|
require.Equal(t, "wss", l.Protocol())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWebsockeInit(t *testing.T) {
|
func TestWebsocketInit(t *testing.T) {
|
||||||
l := NewWebsocket("t1", testAddr, nil)
|
l := NewWebsocket("t1", testAddr, nil)
|
||||||
require.Nil(t, l.listen)
|
require.Nil(t, l.listen)
|
||||||
err := l.Init(logger)
|
err := l.Init(logger)
|
||||||
@@ -54,7 +54,7 @@ func TestWebsockeInit(t *testing.T) {
|
|||||||
|
|
||||||
func TestWebsocketServeAndClose(t *testing.T) {
|
func TestWebsocketServeAndClose(t *testing.T) {
|
||||||
l := NewWebsocket("t1", testAddr, nil)
|
l := NewWebsocket("t1", testAddr, nil)
|
||||||
l.Init(logger)
|
_ = l.Init(logger)
|
||||||
|
|
||||||
o := make(chan bool)
|
o := make(chan bool)
|
||||||
go func(o chan bool) {
|
go func(o chan bool) {
|
||||||
@@ -96,7 +96,7 @@ func TestWebsocketServeTLSAndClose(t *testing.T) {
|
|||||||
|
|
||||||
func TestWebsocketUpgrade(t *testing.T) {
|
func TestWebsocketUpgrade(t *testing.T) {
|
||||||
l := NewWebsocket("t1", testAddr, nil)
|
l := NewWebsocket("t1", testAddr, nil)
|
||||||
l.Init(logger)
|
_ = l.Init(logger)
|
||||||
|
|
||||||
e := make(chan bool)
|
e := make(chan bool)
|
||||||
l.establish = func(id string, c net.Conn) error {
|
l.establish = func(id string, c net.Conn) error {
|
||||||
@@ -110,12 +110,12 @@ func TestWebsocketUpgrade(t *testing.T) {
|
|||||||
require.Equal(t, true, <-e)
|
require.Equal(t, true, <-e)
|
||||||
|
|
||||||
s.Close()
|
s.Close()
|
||||||
ws.Close()
|
_ = ws.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWebsocketConnectionReads(t *testing.T) {
|
func TestWebsocketConnectionReads(t *testing.T) {
|
||||||
l := NewWebsocket("t1", testAddr, nil)
|
l := NewWebsocket("t1", testAddr, nil)
|
||||||
l.Init(nil)
|
_ = l.Init(nil)
|
||||||
|
|
||||||
recv := make(chan []byte)
|
recv := make(chan []byte)
|
||||||
l.establish = func(id string, c net.Conn) error {
|
l.establish = func(id string, c net.Conn) error {
|
||||||
@@ -151,5 +151,5 @@ func TestWebsocketConnectionReads(t *testing.T) {
|
|||||||
require.Equal(t, pkt, got)
|
require.Equal(t, pkt, got)
|
||||||
|
|
||||||
s.Close()
|
s.Close()
|
||||||
ws.Close()
|
_ = ws.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestCodesString(t *testing.T) {
|
|||||||
require.Equal(t, "test", c.String())
|
require.Equal(t, "test", c.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCodesErrorr(t *testing.T) {
|
func TestCodesError(t *testing.T) {
|
||||||
c := Code{
|
c := Code{
|
||||||
Reason: "error",
|
Reason: "error",
|
||||||
Code: 0x1,
|
Code: 0x1,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// All of the valid packet types and their packet identifier.
|
// All valid packet types and their packet identifiers.
|
||||||
const (
|
const (
|
||||||
Reserved byte = iota // 0 - we use this in packet tests to indicate special-test or all packets.
|
Reserved byte = iota // 0 - we use this in packet tests to indicate special-test or all packets.
|
||||||
Connect // 1
|
Connect // 1
|
||||||
@@ -37,9 +37,9 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrNoValidPacketAvailable indicates the packet type byte provided does not exist in the mqtt specification.
|
// ErrNoValidPacketAvailable indicates the packet type byte provided does not exist in the mqtt specification.
|
||||||
ErrNoValidPacketAvailable error = errors.New("no valid packet available")
|
ErrNoValidPacketAvailable = errors.New("no valid packet available")
|
||||||
|
|
||||||
// PacketNames is a map of packet bytes to human readable names, for easier debugging.
|
// PacketNames is a map of packet bytes to human-readable names, for easier debugging.
|
||||||
PacketNames = map[byte]string{
|
PacketNames = map[byte]string{
|
||||||
0: "Reserved",
|
0: "Reserved",
|
||||||
1: "Connect",
|
1: "Connect",
|
||||||
@@ -272,28 +272,28 @@ func (s Subscription) Merge(n Subscription) Subscription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// encode encodes a subscription and properties into bytes.
|
// encode encodes a subscription and properties into bytes.
|
||||||
func (p Subscription) encode() byte {
|
func (s Subscription) encode() byte {
|
||||||
var flag byte
|
var flag byte
|
||||||
flag |= p.Qos
|
flag |= s.Qos
|
||||||
|
|
||||||
if p.NoLocal {
|
if s.NoLocal {
|
||||||
flag |= 1 << 2
|
flag |= 1 << 2
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.RetainAsPublished {
|
if s.RetainAsPublished {
|
||||||
flag |= 1 << 3
|
flag |= 1 << 3
|
||||||
}
|
}
|
||||||
|
|
||||||
flag |= p.RetainHandling << 4
|
flag |= s.RetainHandling << 4
|
||||||
return flag
|
return flag
|
||||||
}
|
}
|
||||||
|
|
||||||
// decode decodes subscription bytes into a subscription struct.
|
// decode decodes subscription bytes into a subscription struct.
|
||||||
func (p *Subscription) decode(b byte) {
|
func (s *Subscription) decode(b byte) {
|
||||||
p.Qos = b & 3 // byte
|
s.Qos = b & 3 // byte
|
||||||
p.NoLocal = 1&(b>>2) > 0 // bool
|
s.NoLocal = 1&(b>>2) > 0 // bool
|
||||||
p.RetainAsPublished = 1&(b>>3) > 0 // bool
|
s.RetainAsPublished = 1&(b>>3) > 0 // bool
|
||||||
p.RetainHandling = 3 & (b >> 4) // byte
|
s.RetainHandling = 3 & (b >> 4) // byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectEncode encodes a connect packet.
|
// ConnectEncode encodes a connect packet.
|
||||||
@@ -343,7 +343,7 @@ func (pk *Packet) ConnectEncode(buf *bytes.Buffer) error {
|
|||||||
|
|
||||||
pk.FixedHeader.Remaining = nb.Len()
|
pk.FixedHeader.Remaining = nb.Len()
|
||||||
pk.FixedHeader.Encode(buf)
|
pk.FixedHeader.Encode(buf)
|
||||||
nb.WriteTo(buf)
|
_, _ = nb.WriteTo(buf)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -505,7 +505,7 @@ func (pk *Packet) ConnackEncode(buf *bytes.Buffer) error {
|
|||||||
|
|
||||||
pk.FixedHeader.Remaining = nb.Len()
|
pk.FixedHeader.Remaining = nb.Len()
|
||||||
pk.FixedHeader.Encode(buf)
|
pk.FixedHeader.Encode(buf)
|
||||||
nb.WriteTo(buf)
|
_, _ = nb.WriteTo(buf)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,7 +548,7 @@ func (pk *Packet) DisconnectEncode(buf *bytes.Buffer) error {
|
|||||||
|
|
||||||
pk.FixedHeader.Remaining = nb.Len()
|
pk.FixedHeader.Remaining = nb.Len()
|
||||||
pk.FixedHeader.Encode(buf)
|
pk.FixedHeader.Encode(buf)
|
||||||
nb.WriteTo(buf)
|
_, _ = nb.WriteTo(buf)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -619,7 +619,7 @@ func (pk *Packet) PublishEncode(buf *bytes.Buffer) error {
|
|||||||
|
|
||||||
pk.FixedHeader.Remaining = nb.Len()
|
pk.FixedHeader.Remaining = nb.Len()
|
||||||
pk.FixedHeader.Encode(buf)
|
pk.FixedHeader.Encode(buf)
|
||||||
nb.WriteTo(buf)
|
_, _ = nb.WriteTo(buf)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -707,7 +707,7 @@ func (pk *Packet) encodePubAckRelRecComp(buf *bytes.Buffer) error {
|
|||||||
|
|
||||||
pk.FixedHeader.Remaining = nb.Len()
|
pk.FixedHeader.Remaining = nb.Len()
|
||||||
pk.FixedHeader.Encode(buf)
|
pk.FixedHeader.Encode(buf)
|
||||||
nb.WriteTo(buf)
|
_, _ = nb.WriteTo(buf)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -844,7 +844,7 @@ func (pk *Packet) SubackEncode(buf *bytes.Buffer) error {
|
|||||||
|
|
||||||
pk.FixedHeader.Remaining = nb.Len()
|
pk.FixedHeader.Remaining = nb.Len()
|
||||||
pk.FixedHeader.Encode(buf)
|
pk.FixedHeader.Encode(buf)
|
||||||
nb.WriteTo(buf)
|
_, _ = nb.WriteTo(buf)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -901,7 +901,7 @@ func (pk *Packet) SubscribeEncode(buf *bytes.Buffer) error {
|
|||||||
|
|
||||||
pk.FixedHeader.Remaining = nb.Len()
|
pk.FixedHeader.Remaining = nb.Len()
|
||||||
pk.FixedHeader.Encode(buf)
|
pk.FixedHeader.Encode(buf)
|
||||||
nb.WriteTo(buf)
|
_, _ = nb.WriteTo(buf)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -996,7 +996,7 @@ func (pk *Packet) UnsubackEncode(buf *bytes.Buffer) error {
|
|||||||
|
|
||||||
pk.FixedHeader.Remaining = nb.Len()
|
pk.FixedHeader.Remaining = nb.Len()
|
||||||
pk.FixedHeader.Encode(buf)
|
pk.FixedHeader.Encode(buf)
|
||||||
nb.WriteTo(buf)
|
_, _ = nb.WriteTo(buf)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1049,7 +1049,7 @@ func (pk *Packet) UnsubscribeEncode(buf *bytes.Buffer) error {
|
|||||||
|
|
||||||
pk.FixedHeader.Remaining = nb.Len()
|
pk.FixedHeader.Remaining = nb.Len()
|
||||||
pk.FixedHeader.Encode(buf)
|
pk.FixedHeader.Encode(buf)
|
||||||
nb.WriteTo(buf)
|
_, _ = nb.WriteTo(buf)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1109,7 +1109,7 @@ func (pk *Packet) AuthEncode(buf *bytes.Buffer) error {
|
|||||||
|
|
||||||
pk.FixedHeader.Remaining = nb.Len()
|
pk.FixedHeader.Remaining = nb.Len()
|
||||||
pk.FixedHeader.Encode(buf)
|
pk.FixedHeader.Encode(buf)
|
||||||
nb.WriteTo(buf)
|
_, _ = nb.WriteTo(buf)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ func TestPacketEncode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pk := new(Packet)
|
pk := new(Packet)
|
||||||
copier.Copy(pk, wanted.Packet)
|
_ = copier.Copy(pk, wanted.Packet)
|
||||||
require.Equal(t, pkt, pk.FixedHeader.Type, pkInfo, pkt, wanted.Desc)
|
require.Equal(t, pkt, pk.FixedHeader.Type, pkInfo, pkt, wanted.Desc)
|
||||||
|
|
||||||
pk.Mods.AllowResponseInfo = true
|
pk.Mods.AllowResponseInfo = true
|
||||||
@@ -218,7 +218,7 @@ func TestPacketDecode(t *testing.T) {
|
|||||||
|
|
||||||
pk := &Packet{FixedHeader: FixedHeader{Type: pkt}}
|
pk := &Packet{FixedHeader: FixedHeader{Type: pkt}}
|
||||||
pk.Mods.AllowResponseInfo = true
|
pk.Mods.AllowResponseInfo = true
|
||||||
pk.FixedHeader.Decode(wanted.RawBytes[0])
|
_ = pk.FixedHeader.Decode(wanted.RawBytes[0])
|
||||||
if len(wanted.RawBytes) > 0 {
|
if len(wanted.RawBytes) > 0 {
|
||||||
pk.FixedHeader.Remaining = int(wanted.RawBytes[1])
|
pk.FixedHeader.Remaining = int(wanted.RawBytes[1])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ type UserProperty struct { // [MQTT-1.5.7-1]
|
|||||||
Val string `json:"v"`
|
Val string `json:"v"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Properties contains all of the mqtt v5 properties available for a packet.
|
// Properties contains all mqtt v5 properties available for a packet.
|
||||||
// Some properties have valid values of 0 or not-present. In this case, we opt for
|
// Some properties have valid values of 0 or not-present. In this case, we opt for
|
||||||
// property flags to indicate the usage of property.
|
// property flags to indicate the usage of property.
|
||||||
// Refer to mqtt v5 2.2.2.2 Property spec for more information.
|
// Refer to mqtt v5 2.2.2.2 Property spec for more information.
|
||||||
@@ -355,7 +355,7 @@ func (p *Properties) Encode(pkt byte, mods Mods, b *bytes.Buffer, n int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
encodeLength(b, int64(buf.Len()))
|
encodeLength(b, int64(buf.Len()))
|
||||||
buf.WriteTo(b) // [MQTT-3.1.3-10]
|
_, _ = buf.WriteTo(b) // [MQTT-3.1.3-10]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes property bytes into a properties struct.
|
// Decode decodes property bytes into a properties struct.
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ const (
|
|||||||
TConnectMqtt5
|
TConnectMqtt5
|
||||||
TConnectMqtt5LWT
|
TConnectMqtt5LWT
|
||||||
TConnectClean
|
TConnectClean
|
||||||
TConnectCleanLWT
|
|
||||||
TConnectUserPass
|
TConnectUserPass
|
||||||
TConnectUserPassLWT
|
TConnectUserPassLWT
|
||||||
TConnectMalProtocolName
|
TConnectMalProtocolName
|
||||||
@@ -61,7 +60,6 @@ const (
|
|||||||
TConnectInvalidProtocolVersion2
|
TConnectInvalidProtocolVersion2
|
||||||
TConnectInvalidReservedBit
|
TConnectInvalidReservedBit
|
||||||
TConnectInvalidClientIDTooLong
|
TConnectInvalidClientIDTooLong
|
||||||
TConnectInvalidPasswordNoUsername
|
|
||||||
TConnectInvalidFlagNoUsername
|
TConnectInvalidFlagNoUsername
|
||||||
TConnectInvalidFlagNoPassword
|
TConnectInvalidFlagNoPassword
|
||||||
TConnectInvalidUsernameNoFlag
|
TConnectInvalidUsernameNoFlag
|
||||||
@@ -186,7 +184,6 @@ const (
|
|||||||
TUnsubscribe
|
TUnsubscribe
|
||||||
TUnsubscribeMany
|
TUnsubscribeMany
|
||||||
TUnsubscribeMqtt5
|
TUnsubscribeMqtt5
|
||||||
TUnsubscribeDropProperties
|
|
||||||
TUnsubscribeMalPacketID
|
TUnsubscribeMalPacketID
|
||||||
TUnsubscribeMalTopicName
|
TUnsubscribeMalTopicName
|
||||||
TUnsubscribeMalProperties
|
TUnsubscribeMalProperties
|
||||||
@@ -204,7 +201,6 @@ const (
|
|||||||
TDisconnect
|
TDisconnect
|
||||||
TDisconnectTakeover
|
TDisconnectTakeover
|
||||||
TDisconnectMqtt5
|
TDisconnectMqtt5
|
||||||
TDisconnectNormalMqtt5
|
|
||||||
TDisconnectSecondConnect
|
TDisconnectSecondConnect
|
||||||
TDisconnectReceiveMaximum
|
TDisconnectReceiveMaximum
|
||||||
TDisconnectDropProperties
|
TDisconnectDropProperties
|
||||||
|
|||||||
57
server.go
57
server.go
@@ -2,7 +2,7 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 mochi-mqtt, mochi-co
|
// SPDX-FileCopyrightText: 2022 mochi-mqtt, mochi-co
|
||||||
// SPDX-FileContributor: mochi-co
|
// SPDX-FileContributor: mochi-co
|
||||||
|
|
||||||
// package mqtt provides a high performance, fully compliant MQTT v5 broker server with v3.1.1 backward compatibility.
|
// Package mqtt provides a high performance, fully compliant MQTT v5 broker server with v3.1.1 backward compatibility.
|
||||||
package mqtt
|
package mqtt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "2.3.0" // the current server version.
|
Version = "2.4.0" // the current server version.
|
||||||
defaultSysTopicInterval int64 = 1 // the interval between $SYS topic publishes
|
defaultSysTopicInterval int64 = 1 // the interval between $SYS topic publishes
|
||||||
LocalListener = "local"
|
LocalListener = "local"
|
||||||
InlineClientId = "inline"
|
InlineClientId = "inline"
|
||||||
@@ -38,7 +38,7 @@ var (
|
|||||||
MaximumSessionExpiryInterval: math.MaxUint32, // maximum number of seconds to keep disconnected sessions
|
MaximumSessionExpiryInterval: math.MaxUint32, // maximum number of seconds to keep disconnected sessions
|
||||||
MaximumMessageExpiryInterval: 60 * 60 * 24, // maximum message expiry if message expiry is 0 or over
|
MaximumMessageExpiryInterval: 60 * 60 * 24, // maximum message expiry if message expiry is 0 or over
|
||||||
ReceiveMaximum: 1024, // maximum number of concurrent qos messages per client
|
ReceiveMaximum: 1024, // maximum number of concurrent qos messages per client
|
||||||
MaximumQos: 2, // maxmimum qos value available to clients
|
MaximumQos: 2, // maximum qos value available to clients
|
||||||
RetainAvailable: 1, // retain messages is available
|
RetainAvailable: 1, // retain messages is available
|
||||||
MaximumPacketSize: 0, // no maximum packet size
|
MaximumPacketSize: 0, // no maximum packet size
|
||||||
TopicAliasMaximum: math.MaxUint16, // maximum topic alias value
|
TopicAliasMaximum: math.MaxUint16, // maximum topic alias value
|
||||||
@@ -49,8 +49,9 @@ var (
|
|||||||
MaximumClientWritesPending: 1024 * 8, // maximum number of pending message writes for a client
|
MaximumClientWritesPending: 1024 * 8, // maximum number of pending message writes for a client
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrListenerIDExists = errors.New("listener id already exists") // a listener with the same id already exists.
|
ErrListenerIDExists = errors.New("listener id already exists") // a listener with the same id already exists
|
||||||
ErrConnectionClosed = errors.New("connection not open") // connection is closed
|
ErrConnectionClosed = errors.New("connection not open") // connection is closed
|
||||||
|
ErrInlineClientNotEnabled = errors.New("please set Options.InlineClient=true to use this feature") // inline client is not enabled by default
|
||||||
)
|
)
|
||||||
|
|
||||||
// Capabilities indicates the capabilities and features provided by the server.
|
// Capabilities indicates the capabilities and features provided by the server.
|
||||||
@@ -106,6 +107,10 @@ type Options struct {
|
|||||||
|
|
||||||
// SysTopicResendInterval specifies the interval between $SYS topic updates in seconds.
|
// SysTopicResendInterval specifies the interval between $SYS topic updates in seconds.
|
||||||
SysTopicResendInterval int64
|
SysTopicResendInterval int64
|
||||||
|
|
||||||
|
// Enable Inline client to allow direct subscribing and publishing from the parent codebase,
|
||||||
|
// with negligible performance difference (disabled by default to prevent confusion in statistics).
|
||||||
|
InlineClient bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server is an MQTT broker server. It should be created with server.New()
|
// Server is an MQTT broker server. It should be created with server.New()
|
||||||
@@ -119,8 +124,8 @@ type Server struct {
|
|||||||
loop *loop // loop contains tickers for the system event loop
|
loop *loop // loop contains tickers for the system event loop
|
||||||
done chan bool // indicate that the server is ending
|
done chan bool // indicate that the server is ending
|
||||||
Log *slog.Logger // minimal no-alloc logger
|
Log *slog.Logger // minimal no-alloc logger
|
||||||
hooks *Hooks // hooks contains hooks for extra functionality such as auth and persistent storage.
|
hooks *Hooks // hooks contains hooks for extra functionality such as auth and persistent storage
|
||||||
inlineClient *Client // inlineClient is a special client used for inline subscriptions and inline Publish.
|
inlineClient *Client // inlineClient is a special client used for inline subscriptions and inline Publish
|
||||||
}
|
}
|
||||||
|
|
||||||
// loop contains interval tickers for the system events loop.
|
// loop contains interval tickers for the system events loop.
|
||||||
@@ -129,7 +134,7 @@ type loop struct {
|
|||||||
clientExpiry *time.Ticker // interval ticker for cleaning expired clients
|
clientExpiry *time.Ticker // interval ticker for cleaning expired clients
|
||||||
inflightExpiry *time.Ticker // interval ticker for cleaning up expired inflight messages
|
inflightExpiry *time.Ticker // interval ticker for cleaning up expired inflight messages
|
||||||
retainedExpiry *time.Ticker // interval ticker for cleaning retained messages
|
retainedExpiry *time.Ticker // interval ticker for cleaning retained messages
|
||||||
willDelaySend *time.Ticker // interval ticker for sending will messages with a delay
|
willDelaySend *time.Ticker // interval ticker for sending Will Messages with a delay
|
||||||
willDelayed *packets.Packets // activate LWT packets which will be sent after a delay
|
willDelayed *packets.Packets // activate LWT packets which will be sent after a delay
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,8 +178,11 @@ func New(opts *Options) *Server {
|
|||||||
Log: opts.Logger,
|
Log: opts.Logger,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.Options.InlineClient {
|
||||||
s.inlineClient = s.NewClient(nil, LocalListener, InlineClientId, true)
|
s.inlineClient = s.NewClient(nil, LocalListener, InlineClientId, true)
|
||||||
s.Clients.Add(s.inlineClient)
|
s.Clients.Add(s.inlineClient)
|
||||||
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@@ -426,7 +434,7 @@ func (s *Server) receivePacket(cl *Client, pk packets.Packet) error {
|
|||||||
if code, ok := err.(packets.Code); ok &&
|
if code, ok := err.(packets.Code); ok &&
|
||||||
cl.Properties.ProtocolVersion == 5 &&
|
cl.Properties.ProtocolVersion == 5 &&
|
||||||
code.Code >= packets.ErrUnspecifiedError.Code {
|
code.Code >= packets.ErrUnspecifiedError.Code {
|
||||||
s.DisconnectClient(cl, code)
|
_ = s.DisconnectClient(cl, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Log.Warn("error processing packet", "error", err, "client", cl.ID, "listener", cl.Net.Listener, "pk", pk)
|
s.Log.Warn("error processing packet", "error", err, "client", cl.ID, "listener", cl.Net.Listener, "pk", pk)
|
||||||
@@ -464,7 +472,7 @@ func (s *Server) validateConnect(cl *Client, pk packets.Packet) packets.Code {
|
|||||||
// session is abandoned.
|
// session is abandoned.
|
||||||
func (s *Server) inheritClientSession(pk packets.Packet, cl *Client) bool {
|
func (s *Server) inheritClientSession(pk packets.Packet, cl *Client) bool {
|
||||||
if existing, ok := s.Clients.Get(pk.Connect.ClientIdentifier); ok {
|
if existing, ok := s.Clients.Get(pk.Connect.ClientIdentifier); ok {
|
||||||
s.DisconnectClient(existing, packets.ErrSessionTakenOver) // [MQTT-3.1.4-3]
|
_ = s.DisconnectClient(existing, packets.ErrSessionTakenOver) // [MQTT-3.1.4-3]
|
||||||
if pk.Connect.Clean || (existing.Properties.Clean && existing.Properties.ProtocolVersion < 5) { // [MQTT-3.1.2-4] [MQTT-3.1.4-4]
|
if pk.Connect.Clean || (existing.Properties.Clean && existing.Properties.ProtocolVersion < 5) { // [MQTT-3.1.2-4] [MQTT-3.1.4-4]
|
||||||
s.UnsubscribeClient(existing)
|
s.UnsubscribeClient(existing)
|
||||||
existing.ClearInflights(math.MaxInt64, 0)
|
existing.ClearInflights(math.MaxInt64, 0)
|
||||||
@@ -649,11 +657,15 @@ func (s *Server) processPingreq(cl *Client, _ packets.Packet) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publish publishes a publish packet into the broker as if it were sent from the speicfied client.
|
// Publish publishes a publish packet into the broker as if it were sent from the specified client.
|
||||||
// This is a convenience function which wraps InjectPacket. As such, this method can publish packets
|
// This is a convenience function which wraps InjectPacket. As such, this method can publish packets
|
||||||
// to any topic (including $SYS) and bypass ACL checks. The qos byte is used for limiting the
|
// to any topic (including $SYS) and bypass ACL checks. The qos byte is used for limiting the
|
||||||
// outbound qos (mqtt v5) rather than issuing to the broker (we assume qos 2 complete).
|
// outbound qos (mqtt v5) rather than issuing to the broker (we assume qos 2 complete).
|
||||||
func (s *Server) Publish(topic string, payload []byte, retain bool, qos byte) error {
|
func (s *Server) Publish(topic string, payload []byte, retain bool, qos byte) error {
|
||||||
|
if !s.Options.InlineClient {
|
||||||
|
return ErrInlineClientNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
return s.InjectPacket(s.inlineClient, packets.Packet{
|
return s.InjectPacket(s.inlineClient, packets.Packet{
|
||||||
FixedHeader: packets.FixedHeader{
|
FixedHeader: packets.FixedHeader{
|
||||||
Type: packets.Publish,
|
Type: packets.Publish,
|
||||||
@@ -669,11 +681,18 @@ func (s *Server) Publish(topic string, payload []byte, retain bool, qos byte) er
|
|||||||
// Subscribe adds an inline subscription for the specified topic filter and subscription identifier
|
// Subscribe adds an inline subscription for the specified topic filter and subscription identifier
|
||||||
// with the provided handler function.
|
// with the provided handler function.
|
||||||
func (s *Server) Subscribe(filter string, subscriptionId int, handler InlineSubFn) error {
|
func (s *Server) Subscribe(filter string, subscriptionId int, handler InlineSubFn) error {
|
||||||
|
if !s.Options.InlineClient {
|
||||||
|
return ErrInlineClientNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
return packets.ErrInlineSubscriptionHandlerInvalid
|
return packets.ErrInlineSubscriptionHandlerInvalid
|
||||||
} else if !IsValidFilter(filter, false) {
|
}
|
||||||
|
|
||||||
|
if !IsValidFilter(filter, false) {
|
||||||
return packets.ErrTopicFilterInvalid
|
return packets.ErrTopicFilterInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
subscription := packets.Subscription{
|
subscription := packets.Subscription{
|
||||||
Identifier: subscriptionId,
|
Identifier: subscriptionId,
|
||||||
Filter: filter,
|
Filter: filter,
|
||||||
@@ -704,6 +723,10 @@ func (s *Server) Subscribe(filter string, subscriptionId int, handler InlineSubF
|
|||||||
// It allows you to unsubscribe a specific subscription from the internal subscription
|
// It allows you to unsubscribe a specific subscription from the internal subscription
|
||||||
// associated with the given topic filter.
|
// associated with the given topic filter.
|
||||||
func (s *Server) Unsubscribe(filter string, subscriptionId int) error {
|
func (s *Server) Unsubscribe(filter string, subscriptionId int) error {
|
||||||
|
if !s.Options.InlineClient {
|
||||||
|
return ErrInlineClientNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
if !IsValidFilter(filter, false) {
|
if !IsValidFilter(filter, false) {
|
||||||
return packets.ErrTopicFilterInvalid
|
return packets.ErrTopicFilterInvalid
|
||||||
}
|
}
|
||||||
@@ -761,12 +784,12 @@ func (s *Server) processPublish(cl *Client, pk packets.Packet) error {
|
|||||||
return s.DisconnectClient(cl, packets.ErrNotAuthorized)
|
return s.DisconnectClient(cl, packets.ErrNotAuthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pk.FixedHeader.Qos == 1 {
|
ackType := packets.Puback
|
||||||
ack := s.buildAck(pk.PacketID, packets.Puback, 0, pk.Properties, packets.ErrNotAuthorized)
|
if pk.FixedHeader.Qos == 2 {
|
||||||
return cl.WritePacket(ack)
|
ackType = packets.Pubrec
|
||||||
}
|
}
|
||||||
|
|
||||||
ack := s.buildAck(pk.PacketID, packets.Pubrec, 0, pk.Properties, packets.ErrNotAuthorized)
|
ack := s.buildAck(pk.PacketID, ackType, 0, pk.Properties, packets.ErrNotAuthorized)
|
||||||
return cl.WritePacket(ack)
|
return cl.WritePacket(ack)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,7 +813,7 @@ func (s *Server) processPublish(cl *Client, pk packets.Packet) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if pk.FixedHeader.Qos > s.Options.Capabilities.MaximumQos {
|
if pk.FixedHeader.Qos > s.Options.Capabilities.MaximumQos {
|
||||||
pk.FixedHeader.Qos = s.Options.Capabilities.MaximumQos // [MQTT-3.2.2-9] Reduce Qos based on server max qos capability
|
pk.FixedHeader.Qos = s.Options.Capabilities.MaximumQos // [MQTT-3.2.2-9] Reduce qos based on server max qos capability
|
||||||
}
|
}
|
||||||
|
|
||||||
pkx, err := s.hooks.OnPublish(cl, pk)
|
pkx, err := s.hooks.OnPublish(cl, pk)
|
||||||
@@ -1373,7 +1396,7 @@ func (s *Server) Close() error {
|
|||||||
func (s *Server) closeListenerClients(listener string) {
|
func (s *Server) closeListenerClients(listener string) {
|
||||||
clients := s.Clients.GetByListener(listener)
|
clients := s.Clients.GetByListener(listener)
|
||||||
for _, cl := range clients {
|
for _, cl := range clients {
|
||||||
s.DisconnectClient(cl, packets.ErrServerShuttingDown)
|
_ = s.DisconnectClient(cl, packets.ErrServerShuttingDown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
502
server_test.go
502
server_test.go
File diff suppressed because it is too large
Load Diff
@@ -705,7 +705,7 @@ func IsSharedFilter(filter string) bool {
|
|||||||
|
|
||||||
// IsValidFilter returns true if the filter is valid.
|
// IsValidFilter returns true if the filter is valid.
|
||||||
func IsValidFilter(filter string, forPublish bool) bool {
|
func IsValidFilter(filter string, forPublish bool) bool {
|
||||||
if !forPublish && len(filter) == 0 { // publishing can accept zero-length topic filter if topic alias exists, so we don't enforce for publihs.
|
if !forPublish && len(filter) == 0 { // publishing can accept zero-length topic filter if topic alias exists, so we don't enforce for publish.
|
||||||
return false // [MQTT-4.7.3-1]
|
return false // [MQTT-4.7.3-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user