Skip to content

zotonic/mqtt_packet_map

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status

MQTT Packet Encoder and Decoder

Encoder and decoder for MQTT v5 and earlier.

Maps are used for the representation of all MQTT messages. There are two functions:

  1. mqtt_packet_map:encode/1
  2. mqtt_packet_map:decode/1

Both have a variant where the MQTT version (protocol level) is passed. This defaults to 5, valid values are 3, 4 (v3.1.1) and 5.

Example usage:

% Decode an incoming binary, return the message
case mqtt_packet_map:decode(Bin) of
    {ok, {Msg, RestBin}} ->
        % Decoded a packet, RestBin contains the
        % remaining data for the next packet(s).
        ...;
    {error, incomplete_packet} -> ...;
        % Packet is too short, fetch more data first
        ...;
    {error, malformed_header} ->
        % Illegal package, close the connection
        ...;
    {error, unknown_protocol} ->
        % Only for connect messages
        ...
end.

% Encode a message
{ok, Bin} = mqtt_packet_map:encode(Msg).

% Encode a message as MQTT v3.1.1 (protocol level 4)
{ok, Bin} = mqtt_packet_map:encode(4, Msg).

MQTT v5 Specification

This library follows the following specification:

http://docs.oasis-open.org/mqtt/mqtt/v5.0/cs01/mqtt-v5.0-cs01.html

Usage

Include the mqtt_packet_map directly in your rebar.config:

{deps, [
    mqtt_packet_map
]}.

Tests

Run tests with:

make test

Sample output:

./rebar3 ct --config rebar.test.config
===> Verifying dependencies...
===> Compiling mqtt_packet_map
===> Running Common Test suites...
%%% mqtt_packet_map_SUITE ==> variable_byte_integer: OK
%%% mqtt_packet_map_SUITE ==> partial_packet: OK
%%% mqtt_packet_map_SUITE ==> connect_v5: OK
%%% mqtt_packet_map_SUITE ==> connect_v5_full: OK
%%% mqtt_packet_map_SUITE ==> connack_v5: OK
%%% mqtt_packet_map_SUITE ==> publish_v5: OK
%%% mqtt_packet_map_SUITE ==> puback_et_al_v5: OK
%%% mqtt_packet_map_SUITE ==> subscribe_v5: OK
%%% mqtt_packet_map_SUITE ==> suback_v5: OK
%%% mqtt_packet_map_SUITE ==> unsubscribe_v5: OK
%%% mqtt_packet_map_SUITE ==> unsuback_v5: OK
%%% mqtt_packet_map_SUITE ==> pingreq: OK
%%% mqtt_packet_map_SUITE ==> pingresp: OK
%%% mqtt_packet_map_SUITE ==> disconnect_v5: OK
%%% mqtt_packet_map_SUITE ==> auth_v5: OK
All 15 tests passed.

Packet Types

Below is the list of packet types and their fields.

Fields that are omitted are set to their defaults.

For example, reason_code, qos, and packet_id will all default to 0.

Some fields, like the topic for publish, are obligatory. The encoder will crash if you leave out oblibatory fields.

Topics

Topics are parsed as lists (i.e. they are split on the / separator).

When encoding, both a binary and a list are accepted.

So the following are acceptable topics for the encoder:

  • <<"foo/bar">>
  • [ <<"foo">> | <<"bar">> ]

Which are both decoded as:

  • [ <<"foo">> | <<"bar">> ]

Properties

The (optional) properties of a package are represented as a map.

Known properties have an atom as key, user properties a binary.

Below is an example map with all properties and one user (<<"myuserprop">>) property. The example values are random and have no bearing in reality.

#{
    payload_format_indicator => true,
    message_expiry_interval => 1,
    content_type => <<"text/plain">>,
    response_topic => [ <<"response">>, <<"topic">> ],
    correlation_data => <<"corrdata">>,
    subscription_identifier => 2,
    session_expiry_interval => 3,
    assigned_client_identifier => <<"assclientid">>,
    server_keep_alive => 4,
    authentication_method => <<"authmethod">>,
    authentication_data => <<"authdata">>,
    request_problem_information => true,
    will_delay_interval => 5,
    request_response_information => false,
    response_information => <<"respinfo">>,
    server_reference => <<"servref">>,
    reason_string => <<"reason">>,
    receive_maximum => 12345,
    topic_alias_maximum => 6,
    topic_alias => 7,
    maximum_qos => 2,
    retain_available => true,
    <<"myuserprop">> => <<"foobar">>,
    maximum_packet_size => 1234567,
    wildcard_subscription_available => true,
    subscription_identifier_available => false,
    shared_subscription_available => true
}.

The subscription_identifier can be present multiple times, making it either a single integer or a list of integers.

CONNECT

Minimal:

#{ type => connect }

Complete:

 #{
    type => connect,
    client_id => <<"foobar">>,
    username => <<"someone">>,
    password => <<"secret">>,
    clean_start => true,
    keep_alive => 120,
    properties => #{
        <<"foo">> => <<"bar">>,
        will_delay_interval => 10
    },
    will_flag => true,
    will_payload => <<>>,
    will_properties => #{},
    will_qos => 0,
    will_retain => false,
    will_topic => [ <<"good">>, <<"bye">> ]
}

CONNACK

Minimal:

#{ type = connack }

Complete:

#{
    type => connack,
    reason_code => 16#80,
    session_present => true,
    properties => #{
        <<"foo">> => <<"bar">>
    }
}

PUBLISH

Minimal:

#{
    type => publish,
    topic => [ <<"foo">>, <<"bar">>, <<"la">> ]
}

Complete:

#{
    type => publish,
    topic => [ <<"foo">>, <<"bar">>, <<"la">> ],
    qos => 2,
    dup => true,
    retain => true,
    packet_id => 1234,
    payload => <<"aloha">>,
    properties => #{
        <<"foo">> => <<"bar">>
    }
}

PUBACK / PUBREC / PUBREL / PUBCOMP

These for packets are the same. Only the type code is different.

Minimal:

% Type is one of: puback, pubrec, pubrel, or pubcomp
#{ type = puback }

Complete:

#{
    type => puback,
    reason_code => 16#81,
    packet_id => 4321,
    properties => #{
        <<"bar">> => <<"fooooo">>
    }
}

SUBSCRIBE

The topics subscribed to are either maps with options or just a topic.

Minimal:

#{
    type => subscribe,
    topics => [
        [ <<"foo1">>, <<"bar">> ]
    ]
}

Complete:

#{
    type => subscribe,
    packet_id => 1234,
    topics => [
        #{
            topic => [ "foo1", "bar" ],
            no_local => true,
            qos => 2,
            retain_as_published => true,
            retain_handling => 2
        },
        #{
            topic => [ <<"foo2">>, <<"bar">> ]
        }
    ],
    properties => #{
        <<"foo">> => <<"bar">>
    }
}

SUBACK

All acknowledgements are tuples {ok, QoS} or {error, ReasonCode}.

Minimal:

#{
    type => suback,
    acks => [
        {ok, 0}
    ]
}

Complete (for four acks):

#{
    type => suback,
    packet_id => 12345,
    acks => [
        {ok, 2},
        {ok, 0},
        {ok, 1},
        {error, 16#80}
    ],
    properties => #{
        <<"foo">> => <<"bar">>
    }
}

UNSUBSCRIBE

Minimal:

#{
    type => unsubscribe,
    topics => [
        [ <<"foo">>, <<"bar">> ]
    ]
}

Complete:

#{
    type => unsubscribe,
    packet_id => 42,
    topics => [
        <<"foo1/bar">>,
        [ <<"foo2">>, <<"bar">> ]
    ],
    properties => #{
        <<"foo">> => <<"bar">>
    }
}

UNSUBACK

The acknowledgements are one of:

  • {ok, found}
  • {ok, notfound}
  • {error, ReasonCode}

Minimal:

#{
    type => unsuback,
    acks => [
        {ok, found}
    ]
}

Complete (for three acks):

#{
    type => unsuback,
    packet_id => 12345,
    acks => [
        {ok, found},
        {ok, notfound},
        {error, 16#80}
    ],
    properties => #{
        <<"foo">> => <<"bar">>
    }
}

PINGREQ

No special fields.

#{ type => pingreq }

PINGRESP

No special fields

#{ type => pingresp }

DISCONNECT

The default reason code for disconnects is 0.

Minimal:

#{ type => disconnect }

Complete:

#{
    type => disconnect,
    reason_code => 16#81,
    properties => #{
        <<"foo">> => <<"bar">>
    }
}

AUTH

Minimal:

#{ type => auth }

Complete:

{
    type => auth,
    reason_code => 16#80,
    properties => #{
        <<"foo">> => <<"bar">>,
        authentication_method => <<"...">>,
        authentication_data => <<"...">>
    }
}

License

This library is licensed under the Apache License version 2.0.

See the LICENSE file.

About

Encoder and decoder for MQTT v5 and earlier.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages