|
|
@@ -31,26 +31,53 @@
|
|
|
, serialize/2
|
|
|
]).
|
|
|
|
|
|
+%% The new version APIs to avoid saving
|
|
|
+%% anonymous func
|
|
|
+-export([ parse2/1
|
|
|
+ , parse2/2
|
|
|
+ , serialize_opts/0
|
|
|
+ , serialize_opts/1
|
|
|
+ , serialize_pkt/2
|
|
|
+ ]).
|
|
|
+
|
|
|
-export_type([ options/0
|
|
|
, parse_state/0
|
|
|
, parse_result/0
|
|
|
, serialize_fun/0
|
|
|
]).
|
|
|
|
|
|
+-export_type([ parse_state2/0
|
|
|
+ , parse_result2/0
|
|
|
+ , serialize_opts/0
|
|
|
+ ]).
|
|
|
+
|
|
|
-type(options() :: #{strict_mode => boolean(),
|
|
|
max_size => 1..?MAX_PACKET_SIZE,
|
|
|
version => emqx_types:version()
|
|
|
}).
|
|
|
|
|
|
-type(parse_state() :: {none, options()} | cont_fun()).
|
|
|
+-type(parse_state2() :: {none, options()} | {cont_state(), options()}).
|
|
|
|
|
|
-type(parse_result() :: {more, cont_fun()}
|
|
|
| {ok, emqx_types:packet(), binary(), parse_state()}).
|
|
|
|
|
|
+-type(parse_result2() :: {more, parse_state()}
|
|
|
+ | {ok, emqx_types:packet(), binary(), parse_state()}).
|
|
|
+
|
|
|
-type(cont_fun() :: fun((binary()) -> parse_result())).
|
|
|
|
|
|
+-type(cont_state() :: {Stage :: len | body,
|
|
|
+ State :: #{hdr := #mqtt_packet_header{},
|
|
|
+ len := {pos_integer(), non_neg_integer()} | non_neg_integer(),
|
|
|
+ rest => binary()
|
|
|
+ }
|
|
|
+ }).
|
|
|
+
|
|
|
-type(serialize_fun() :: fun((emqx_types:packet()) -> iodata())).
|
|
|
|
|
|
+-type(serialize_opts() :: options()).
|
|
|
+
|
|
|
-define(none(Options), {none, Options}).
|
|
|
|
|
|
-define(DEFAULT_OPTIONS,
|
|
|
@@ -81,6 +108,89 @@ merge_opts(Options) ->
|
|
|
%% Parse MQTT Frame
|
|
|
%%--------------------------------------------------------------------
|
|
|
|
|
|
+-spec(parse2(binary()) -> parse_result2()).
|
|
|
+parse2(Bin) ->
|
|
|
+ parse2(Bin, initial_parse_state()).
|
|
|
+
|
|
|
+-spec(parse2(binary(), parse_state()) -> parse_result2()).
|
|
|
+parse2(<<>>, {none, Options}) ->
|
|
|
+ {more, {none, Options}};
|
|
|
+parse2(<<Type:4, Dup:1, QoS:2, Retain:1, Rest/binary>>,
|
|
|
+ {none, Options = #{strict_mode := StrictMode}}) ->
|
|
|
+ %% Validate header if strict mode.
|
|
|
+ StrictMode andalso validate_header(Type, Dup, QoS, Retain),
|
|
|
+ Header = #mqtt_packet_header{type = Type,
|
|
|
+ dup = bool(Dup),
|
|
|
+ qos = QoS,
|
|
|
+ retain = bool(Retain)
|
|
|
+ },
|
|
|
+ Header1 = case fixqos(Type, QoS) of
|
|
|
+ QoS -> Header;
|
|
|
+ FixedQoS -> Header#mqtt_packet_header{qos = FixedQoS}
|
|
|
+ end,
|
|
|
+ parse_remaining_len2(Rest, Header1, Options);
|
|
|
+
|
|
|
+parse2(Bin, {{len, #{hdr := Header,
|
|
|
+ len := {Multiplier, Length}}
|
|
|
+ }, Options}) when is_binary(Bin) ->
|
|
|
+ parse_remaining_len2(Bin, Header, Multiplier, Length, Options);
|
|
|
+parse2(Bin, {{body, #{hdr := Header,
|
|
|
+ len := Length,
|
|
|
+ rest := Rest}
|
|
|
+ }, Options}) when is_binary(Bin) ->
|
|
|
+ parse_frame2(<<Rest/binary, Bin/binary>>, Header, Length, Options).
|
|
|
+
|
|
|
+parse_remaining_len2(<<>>, Header, Options) ->
|
|
|
+ {more, {{len, #{hdr => Header, len => {1, 0}}}, Options}};
|
|
|
+parse_remaining_len2(Rest, Header, Options) ->
|
|
|
+ parse_remaining_len2(Rest, Header, 1, 0, Options).
|
|
|
+
|
|
|
+parse_remaining_len2(_Bin, _Header, _Multiplier, Length, #{max_size := MaxSize})
|
|
|
+ when Length > MaxSize ->
|
|
|
+ error(frame_too_large);
|
|
|
+parse_remaining_len2(<<>>, Header, Multiplier, Length, Options) ->
|
|
|
+ {more, {{len, #{hdr => Header, len => {Multiplier, Length}}}, Options}};
|
|
|
+%% Match DISCONNECT without payload
|
|
|
+parse_remaining_len2(<<0:8, Rest/binary>>, Header = #mqtt_packet_header{type = ?DISCONNECT}, 1, 0, Options) ->
|
|
|
+ Packet = packet(Header, #mqtt_packet_disconnect{reason_code = ?RC_SUCCESS}),
|
|
|
+ {ok, Packet, Rest, ?none(Options)};
|
|
|
+%% Match PINGREQ.
|
|
|
+parse_remaining_len2(<<0:8, Rest/binary>>, Header, 1, 0, Options) ->
|
|
|
+ parse_frame2(Rest, Header, 0, Options);
|
|
|
+%% Match PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK...
|
|
|
+parse_remaining_len2(<<0:1, 2:7, Rest/binary>>, Header, 1, 0, Options) ->
|
|
|
+ parse_frame2(Rest, Header, 2, Options);
|
|
|
+parse_remaining_len2(<<1:1, Len:7, Rest/binary>>, Header, Multiplier, Value, Options) ->
|
|
|
+ parse_remaining_len2(Rest, Header, Multiplier * ?HIGHBIT, Value + Len * Multiplier, Options);
|
|
|
+parse_remaining_len2(<<0:1, Len:7, Rest/binary>>, Header, Multiplier, Value,
|
|
|
+ Options = #{max_size := MaxSize}) ->
|
|
|
+ FrameLen = Value + Len * Multiplier,
|
|
|
+ if
|
|
|
+ FrameLen > MaxSize -> error(frame_too_large);
|
|
|
+ true -> parse_frame2(Rest, Header, FrameLen, Options)
|
|
|
+ end.
|
|
|
+
|
|
|
+parse_frame2(Bin, Header, 0, Options) ->
|
|
|
+ {ok, packet(Header), Bin, ?none(Options)};
|
|
|
+
|
|
|
+parse_frame2(Bin, Header, Length, Options) ->
|
|
|
+ case Bin of
|
|
|
+ <<FrameBin:Length/binary, Rest/binary>> ->
|
|
|
+ case parse_packet(Header, FrameBin, Options) of
|
|
|
+ {Variable, Payload} ->
|
|
|
+ {ok, packet(Header, Variable, Payload), Rest, ?none(Options)};
|
|
|
+ Variable = #mqtt_packet_connect{proto_ver = Ver} ->
|
|
|
+ {ok, packet(Header, Variable), Rest, ?none(Options#{version := Ver})};
|
|
|
+ Variable ->
|
|
|
+ {ok, packet(Header, Variable), Rest, ?none(Options)}
|
|
|
+ end;
|
|
|
+ TooShortBin ->
|
|
|
+ {more, {{body, #{hdr => Header, len => Length, rest => TooShortBin}}, Options}}
|
|
|
+ end.
|
|
|
+
|
|
|
+%% Deprecated parse funcs
|
|
|
+%% It should be removed after 4.2.x
|
|
|
+
|
|
|
-spec(parse(binary()) -> parse_result()).
|
|
|
parse(Bin) ->
|
|
|
parse(Bin, initial_parse_state()).
|
|
|
@@ -443,6 +553,20 @@ serialize_fun(#{version := Ver, max_size := MaxSize}) ->
|
|
|
end
|
|
|
end.
|
|
|
|
|
|
+serialize_opts() ->
|
|
|
+ ?DEFAULT_OPTIONS.
|
|
|
+
|
|
|
+serialize_opts(#mqtt_packet_connect{proto_ver = ProtoVer, properties = ConnProps}) ->
|
|
|
+ MaxSize = get_property('Maximum-Packet-Size', ConnProps, ?MAX_PACKET_SIZE),
|
|
|
+ #{version => ProtoVer, max_size => MaxSize}.
|
|
|
+
|
|
|
+serialize_pkt(Packet, #{version := Ver, max_size := MaxSize}) ->
|
|
|
+ IoData = serialize(Packet, Ver),
|
|
|
+ case is_too_large(IoData, MaxSize) of
|
|
|
+ true -> <<>>;
|
|
|
+ false -> IoData
|
|
|
+ end.
|
|
|
+
|
|
|
-spec(serialize(emqx_types:packet()) -> iodata()).
|
|
|
serialize(Packet) -> serialize(Packet, ?MQTT_PROTO_V4).
|
|
|
|