aboutsummaryrefslogtreecommitdiff
path: root/src/coap_parse.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/coap_parse.erl')
-rw-r--r--src/coap_parse.erl137
1 files changed, 137 insertions, 0 deletions
diff --git a/src/coap_parse.erl b/src/coap_parse.erl
new file mode 100644
index 0000000..bb61a28
--- /dev/null
+++ b/src/coap_parse.erl
@@ -0,0 +1,137 @@
+-module(coap_parse).
+-export([parse/1,
+ get_option_values/2]).
+
+-include("coaperl.hrl").
+
+
+-spec parse(<<>>) -> #coap_request{} | #coap_response{} | #coap_empty_message{}.
+% @doc: parse a binary coap message
+parse(Packet) ->
+ {coap, Parsed} = parse_raw(Packet),
+ parse_to_rec(Parsed).
+
+
+% @doc get the named option values from the given response record
+get_option_values(#coap_response{options=Options}, OptNum) ->
+ [ Val || {coap_option, Num, Val} <- Options, Num =:= OptNum ].
+
+
+% @doc: convert raw message to request, response or empty message records
+parse_to_rec({_Type, Code, MID, _Token, Options, Payload}) when Code > 0, Code =< 31 ->
+ #coap_request{method=coap_unparse:method_name(Code),
+ path=parse_path(Options),
+ query=parse_query(Options),
+ mid=MID,
+ ct=parse_content_type(Options),
+ payload=Payload};
+parse_to_rec({_Type, 0, MID , _Token, _Options, <<>>}) ->
+ #coap_empty_message{mid=MID};
+parse_to_rec({_Type, Code, MID, _Token, Options, Payload}) ->
+ <<Class:3, Detail:5>> = <<Code>>,
+ #coap_response{mid=MID,
+ ct=parse_content_type(Options),
+ options=Options,
+ class=Class,
+ detail=Detail,
+ block2=parse_block2(Options),
+ payload=Payload}.
+
+
+% @doc: parse path from raw options
+parse_path(Options) ->
+ erlang:iolist_to_binary(lists:reverse(parse_path(Options, []))).
+
+parse_path([{coap_option, ?COAP_OPTION_URI_PATH, Comp}|T], Acc) ->
+ parse_path(T, [["/"|Comp]|Acc]);
+parse_path([{coap_option, _, _}|T], Acc) ->
+ parse_path(T, Acc);
+parse_path([], Acc) ->
+ Acc.
+
+
+% @doc: parse query from raw options
+parse_query(Options) ->
+ parse_query(Options, []).
+
+parse_query([{coap_option, ?COAP_OPTION_URI_QUERY, Opt}|T], Acc) ->
+ parse_query(T, [Opt|Acc]);
+parse_query([{coap_option, _, _}|T], Acc) ->
+ parse_query(T, Acc);
+parse_query([], Acc) ->
+ Acc.
+
+
+
+% @doc: parse content type from raw options
+parse_content_type(Options) ->
+ OptVals = [Value || {coap_option, Format, Value} <- Options, Format =:= ?COAP_OPTION_CONTENT_FORMAT],
+ case OptVals of
+ [<<Val:8>>] -> corerl:content_format_type(Val);
+ _ -> undefined
+ end.
+
+
+% @doc: parse block2 option from raw options
+parse_block2(Options) ->
+ OptVals = [Value || {coap_option, Format, Value} <- Options, Format =:= ?COAP_OPTION_BLOCK2],
+ case OptVals of
+ [Val] ->
+ NumLen = bit_size(Val)-4,
+ <<Num:NumLen, M:1, SZX:3>> = Val,
+ {Num, M, 1 bsl (SZX+4)};
+ _ -> undefined
+ end.
+
+
+% @doc: parse raw packet
+parse_raw(<<Version:2, Type:2, TKL: 4, Code:8, MID:16, Token:TKL/bytes, Tail/bytes>>) when Version =:= 1 ->
+ {Options, Payload} = parse_raw_options(Tail),
+ {coap, {Type, Code, MID, Token, Options, Payload}};
+parse_raw(_AnythingElse) ->
+ {bad, "unable to parse packet"}.
+
+% @doc: parse options in raw packet, don't interpret any option types yet
+parse_raw_options(OptionBin) ->
+ parse_raw_options(OptionBin, 0, []).
+
+parse_raw_options(<<>>, _LastNum, OptionList) ->
+ {OptionList, <<>>};
+parse_raw_options(<<16#FF, Payload/bytes>>, _LastNum, OptionList) ->
+ {OptionList, Payload};
+parse_raw_options(<<BaseOptNum:4, BaseOptLen:4, Tail/bytes>>, LastNum, OptionList) ->
+ {Tail1, OptNum} = case BaseOptNum of
+ X when X < 13 ->
+ {Tail, BaseOptNum + LastNum};
+ X when X =:= 13 ->
+ <<ExtOptNum, NewTail/bytes>> = Tail,
+ {NewTail, ExtOptNum + 13 + LastNum};
+ X when X =:= 14 ->
+ <<ExtOptNum:2/bytes, NewTail/bytes>> = Tail,
+ {NewTail, ExtOptNum + 269 + LastNum}
+ end,
+ {OptLen, Tail3} = case BaseOptLen of
+ Y when Y < 13 ->
+ {BaseOptLen, Tail1};
+ Y when Y =:= 13 ->
+ <<ExtOptLen, Tail2/bytes>> = Tail1,
+ {ExtOptLen + 13, Tail2};
+ Y when Y =:= 14 ->
+ <<ExtOptLen:2/bytes, Tail2/bytes>> = Tail1,
+ {ExtOptLen + 269, Tail2}
+ end,
+ <<OptVal:OptLen/bytes, NextOpt/bytes>> = Tail3,
+ parse_raw_options(NextOpt, OptNum, [{coap_option, OptNum, OptVal}|OptionList]).
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+parse_to_rec_test() ->
+ % Check if parse to the right rec
+ ?assertEqual(parse_to_rec({egal, 100, 1, egal, [{coap_option, ?COAP_OPTION_CONTENT_FORMAT, <<50:8>>}], payload}),
+ {coap_response,3,4,1,
+ <<"application/json">>,
+ undefined,
+ [{coap_option,12,<<"2">>}],
+ payload}).
+-endif.