Natiu: Means mosquito in the Guaraní language, a language spoken primarily in Paraguay. Commonly written as ñati'û or ñati'ũ.
Modular
Decoder
interface type. Users can choose to use non-allocating or allocating implementations of the 3 method interface.RxTx
type lets one build an MQTT implementation from scratch for any transport. No server/client logic defined at this level.No uneeded allocations: The PUBLISH application message is not handled by this library, the user receives an io.Reader
with the underlying transport bytes. This prevents allocations on natiu-mqtt
side.
V3.1.1: Compliant with MQTT version 3.1.1 for QoS0 interactions. QoS1 and QoS2 are WIP.
No external dependencies: Nada. Nope.
Data oriented design: Minimizes abstractions or objects for the data on the wire.
Fuzz tested, robust: Decoding implementation fuzzed to prevent adversarial user input from crashing application (95% coverage).
Simplicity: A simple base package yields simple implementations for different transports. See Implementations section.
Runtime-what?: Unlike other MQTT implementations. No channels, no interface conversions, no goroutines- as little runtimey stuff as possible. You get the best of Go's concrete types when using Natiu's API. Why? Because MQTT deserialization and serialization are an embarrassingly serial and concrete problem.
This implementation will have a simple embedded-systems implementation in the package top level. This implementation will be transport agnostic and non-concurrent. This will make it far easier to modify and reason about. The transport dependent implementations will have their own subpackage, so one package for TCP transport, another for UART, PPP etc.
API subject to before v1.0.0 release.
Client
// Create new client.
client := mqtt.NewClient(mqtt.ClientConfig{
Decoder: mqtt.DecoderNoAlloc{make([]byte, 1500)},
OnPub: func(_ mqtt.Header, _ mqtt.VariablesPublish, r io.Reader) error {
message, _ := io.ReadAll(r)
log.Println("received message:", string(message))
return nil
},
})
// Get a transport for MQTT packets.
const defaultMQTTPort = ":1883"
conn, err := net.Dial("tcp", "127.0.0.1"+defaultMQTTPort)
if err != nil {
fmt.Println(err)
return
}
// Prepare for CONNECT interaction with server.
var varConn mqtt.VariablesConnect
varConn.SetDefaultMQTT([]byte("salamanca"))
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
err = client.Connect(ctx, conn, &varConn) // Connect to server.
cancel()
if err != nil {
// Error or loop until connect success.
log.Fatalf("connect attempt failed: %v\n", err)
}
// Ping forever until error.
for {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
pingErr := client.Ping(ctx)
cancel()
if pingErr != nil {
log.Fatal("ping error: ", pingErr, " with disconnect reason:", client.Err())
}
log.Println("ping success!")
}
Some issues with Eclipse's Paho implementation:
any
interface for the payload, which could simply be a byte slice...I found these issues after a 2 hour dev session. There will undoubtedly be more if I were to try to actually get it working...