[](https://travis-ci.org/zotonic/mqtt_packet_map)
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:
```erlang
% 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:
```erlang
{deps, [
    mqtt_packet_map
]}.
```
Tests
=====
Run tests with:
```bash
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.
```Erlang
#{
    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:
```Erlang
#{ type => connect }
```
Complete:
```erlang
 #{
    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:
```Erlang
#{ type = connack }
```
Complete:
```erlang
#{
    type => connack,
    reason_code => 16#80,
    session_present => true,
    properties => #{
        <<"foo">> => <<"bar">>
    }
}
```
PUBLISH
-------
Minimal:
```Erlang
#{
    type => publish,
    topic => [ <<"foo">>, <<"bar">>, <<"la">> ]
}
```
Complete:
```erlang
#{
    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:
```Erlang
% Type is one of: puback, pubrec, pubrel, or pubcomp
#{ type = puback }
```
Complete:
```erlang
#{
    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:
```Erlang
#{
    type => subscribe,
    topics => [
        [ <<"foo1">>, <<"bar">> ]
    ]
}
```
Complete:
```erlang
#{
    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:
```Erlang
#{
    type => suback,
    acks => [
        {ok, 0}
    ]
}
```
Complete (for four acks):
```erlang
#{
    type => suback,
    packet_id => 12345,
    acks => [
        {ok, 2},
        {ok, 0},
        {ok, 1},
        {error, 16#80}
    ],
    properties => #{
        <<"foo">> => <<"bar">>
    }
}
```
UNSUBSCRIBE
-----------
Minimal:
```Erlang
#{
    type => unsubscribe,
    topics => [
        [ <<"foo">>, <<"bar">> ]
    ]
}
```
Complete:
```erlang
#{
    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:
```Erlang
#{
    type => unsuback,
    acks => [
        {ok, found}
    ]
}
```
Complete (for three acks):
```erlang
#{
    type => unsuback,
    packet_id => 12345,
    acks => [
        {ok, found},
        {ok, notfound},
        {error, 16#80}
    ],
    properties => #{
        <<"foo">> => <<"bar">>
    }
}
```
PINGREQ
-------
No special fields.
```Erlang
#{ type => pingreq }
```
PINGRESP
--------
No special fields
```Erlang
#{ type => pingresp }
```
DISCONNECT
----------
The default reason code for disconnects is `0`.
Minimal:
```Erlang
#{ type => disconnect }
```
Complete:
```erlang
#{
    type => disconnect,
    reason_code => 16#81,
    properties => #{
        <<"foo">> => <<"bar">>
    }
}
```
AUTH
----
Minimal:
```Erlang
#{ type => auth }
```
Complete:
```erlang
{
    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.