Skip to content

Commit

Permalink
WHISTLE-1535: update v1_util to decode the request body assuming json…
Browse files Browse the repository at this point in the history
… before reading the content type. some dialyzer updates as well
  • Loading branch information
James Aimonetti committed Aug 13, 2012
1 parent 234a169 commit 3394b72
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 63 deletions.
17 changes: 10 additions & 7 deletions whistle_apps/apps/crossbar/src/modules/cb_modules_util.erl
Original file line number Original file line Diff line number Diff line change
@@ -1,17 +1,18 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @author James Aimonetti <james@2600hz.org> %%% @copyright (C) 2011-2012, VoIP INC
%%% @copyright (C) 2011, VoIP INC
%%% @doc %%% @doc
%%% Functions shared between crossbar modules %%% Functions shared between crossbar modules
%%% @end %%% @end
%%% Created : 17 Nov 2011 by James Aimonetti <james@2600hz.org> %%% @contributors
%%% James Aimonetti
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(cb_modules_util). -module(cb_modules_util).


-export([pass_hashes/2]). -export([pass_hashes/2
-export([get_devices_owned_by/2]). ,get_devices_owned_by/2
]).


-include("../../include/crossbar.hrl"). -include_lib("crossbar.hrl").


-spec pass_hashes/2 :: (ne_binary(), ne_binary()) -> {ne_binary(), ne_binary()}. -spec pass_hashes/2 :: (ne_binary(), ne_binary()) -> {ne_binary(), ne_binary()}.
pass_hashes(Username, Password) -> pass_hashes(Username, Password) ->
Expand All @@ -22,7 +23,9 @@ pass_hashes(Username, Password) ->


-spec get_devices_owned_by/2 :: (ne_binary(), ne_binary()) -> wh_json:json_objects(). -spec get_devices_owned_by/2 :: (ne_binary(), ne_binary()) -> wh_json:json_objects().
get_devices_owned_by(OwnerID, DB) -> get_devices_owned_by(OwnerID, DB) ->
case couch_mgr:get_results(DB, <<"cf_attributes/owned">>, [{<<"key">>, [OwnerID, <<"device">>]}, {<<"include_docs">>, true}]) of case couch_mgr:get_results(DB, <<"cf_attributes/owned">>, [{key, [OwnerID, <<"device">>]}
,include_docs
]) of
{ok, JObjs} -> {ok, JObjs} ->
lager:debug("Found ~b devices owned by ~s", [length(JObjs), OwnerID]), lager:debug("Found ~b devices owned by ~s", [length(JObjs), OwnerID]),
[wh_json:get_value(<<"doc">>, JObj) || JObj <- JObjs]; [wh_json:get_value(<<"doc">>, JObj) || JObj <- JObjs];
Expand Down
4 changes: 2 additions & 2 deletions whistle_apps/apps/crossbar/src/modules/cb_onboard.erl
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ put(#cb_context{req_data=JObj, doc=Data}=Context) ->
IContext1 = save_invite_code(IContext, wh_json:set_value(<<"status">>, <<"used">>, Doc)), IContext1 = save_invite_code(IContext, wh_json:set_value(<<"status">>, <<"used">>, Doc)),
case populate_new_account(Data, Context) of case populate_new_account(Data, Context) of
#cb_context{account_id=undefined}=Else -> #cb_context{account_id=undefined}=Else ->
save_invite_code(IContext1, wh_json:set_value(<<"status">>, <<"unused">>, IContext1#cb_context.doc)), _ = save_invite_code(IContext1, wh_json:set_value(<<"status">>, <<"unused">>, IContext1#cb_context.doc)),
create_response(Else); create_response(Else);
#cb_context{account_id=AcctId}=Context1 -> #cb_context{account_id=AcctId}=Context1 ->
save_invite_code(IContext1, wh_json:set_value(<<"used_by">>, AcctId, IContext1#cb_context.doc)), _ = save_invite_code(IContext1, wh_json:set_value(<<"used_by">>, AcctId, IContext1#cb_context.doc)),
create_response(Context1) create_response(Context1)
end; end;
Else -> Else ->
Expand Down
2 changes: 1 addition & 1 deletion whistle_apps/apps/crossbar/src/modules/cb_user_auth.erl
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ init() ->
%% @end %% @end
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-spec allowed_methods/0 :: () -> http_methods(). -spec allowed_methods/0 :: () -> http_methods().
-spec allowed_methods/1 :: (path_tokens()) -> http_methods(). -spec allowed_methods/1 :: (path_token()) -> http_methods().
allowed_methods() -> allowed_methods() ->
['PUT']. ['PUT'].
allowed_methods(<<"recovery">>) -> allowed_methods(<<"recovery">>) ->
Expand Down
119 changes: 66 additions & 53 deletions whistle_apps/apps/crossbar/src/v1_util.erl
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -103,63 +103,80 @@ get_cors_headers(#cb_context{allow_methods=Allowed}) ->
,{<<"Access-Control-Max-Age">>, wh_util:to_binary(?SECONDS_IN_DAY)} ,{<<"Access-Control-Max-Age">>, wh_util:to_binary(?SECONDS_IN_DAY)}
]. ].


-spec get_req_data/2 :: (#cb_context{}, #http_req{}) -> {#cb_context{}, #http_req{}} | -spec get_req_data/2 :: (#cb_context{}, #http_req{}) ->
{'halt', #cb_context{}, #http_req{}}. {#cb_context{}, #http_req{}} |
{'halt', #cb_context{}, #http_req{}}.
-spec get_req_data/3 :: (#cb_context{}, {content_type(), #http_req{}}, wh_json:json_object()) ->
{#cb_context{}, #http_req{}} |
{'halt', #cb_context{}, #http_req{}}.
get_req_data(Context, Req0) -> get_req_data(Context, Req0) ->
{QS0, Req1} = cowboy_http_req:qs_vals(Req0), {QS0, Req1} = cowboy_http_req:qs_vals(Req0),
QS = wh_json:from_list(QS0), QS = wh_json:from_list(QS0),
case cowboy_http_req:parse_header('Content-Type', Req1) of
{undefined, Req2} -> try get_json_body(Req1) of
lager:debug("undefined content type when getting req data, assuming application/json"), {JSON, Req2} ->
{JSON, Req3_1} = get_json_body(Req2), lager:debug("request body successfully parsed as JSON"),
{Context#cb_context{req_json=JSON
,req_data=wh_json:get_value(<<"data">>, JSON, wh_json:new())
,query_json=QS
}
,Req3_1};
{{<<"multipart">>, <<"form-data">>, _}, Req2} ->
lager:debug("multipart/form-data content type when getting req data"),
case catch extract_multipart(Context#cb_context{query_json=QS}, Req2) of
{'EXIT', _E} ->
lager:debug("failed to extract multipart: ~p", [_E]),
{halt, Context, Req2};
Resp -> Resp
end;
{{<<"application">>, <<"x-www-form-urlencoded">>, _}, Req2} ->
lager:debug("application/x-www-form-urlencoded content type when getting req data"),
case catch extract_multipart(Context#cb_context{query_json=QS}, Req2) of
{'EXIT', _} ->
lager:debug("failed to extract multipart"),
{halt, Context, Req2};
Resp -> Resp
end;
{{<<"application">>, <<"json">>, _}, Req2} ->
lager:debug("application/json content type when getting req data"),
{JSON, Req3_1} = get_json_body(Req2),
{Context#cb_context{req_json=JSON
,req_data=wh_json:get_value(<<"data">>, JSON, wh_json:new())
,query_json=QS
}
,Req3_1};
{{<<"application">>, <<"x-json">>, _}, Req2} ->
lager:debug("application/x-json content type when getting req data"),
{JSON, Req3_1} = get_json_body(Req2),
{Context#cb_context{req_json=JSON {Context#cb_context{req_json=JSON
,req_data=wh_json:get_value(<<"data">>, JSON, wh_json:new()) ,req_data=wh_json:get_value(<<"data">>, JSON, wh_json:new())
,query_json=QS ,query_json=QS
} }
,Req3_1}; ,Req2}
{{<<"application">>, <<"base64">>, _}, Req2} -> catch
lager:debug("application/base64 content type when getting req data"), _E:_R ->
decode_base64(Context#cb_context{query_json=QS}, <<"application/base64">>, Req2); lager:debug("failed to parse request body as JSON, checking content-type"),
{{<<"application">>, <<"x-base64">>, _}, Req2} -> get_req_data(Context, cowboy_http_req:parse_header('Content-Type', Req0), QS)
lager:debug("application/x-base64 content type when getting req data"),
decode_base64(Context#cb_context{query_json=QS}, <<"application/base64">>, Req2);
{{ContentType, ContentSubType, _}, Req2} ->
lager:debug("unknown content-type: ~s/~s", [ContentType, ContentSubType]),
extract_file(Context#cb_context{query_json=QS}, list_to_binary([ContentType, "/", ContentSubType]), Req2)
end. end.


get_req_data(Context, {undefined, Req1}, QS) ->
lager:debug("undefined content type when getting req data, assuming application/json"),
{JSON, Req2} = get_json_body(Req1),
{Context#cb_context{req_json=JSON
,req_data=wh_json:get_value(<<"data">>, JSON, wh_json:new())
,query_json=QS
}
,Req2};
get_req_data(Context, {{<<"multipart">>, <<"form-data">>, _}, Req1}, QS) ->
lager:debug("multipart/form-data content type when getting req data"),
case catch extract_multipart(Context#cb_context{query_json=QS}, Req1) of
{'EXIT', _E} ->
lager:debug("failed to extract multipart: ~p", [_E]),
{halt, Context, Req1};
Resp -> Resp
end;
get_req_data(Context, {{<<"application">>, <<"x-www-form-urlencoded">>, _}, Req1}, QS) ->
lager:debug("application/x-www-form-urlencoded content type when getting req data"),
case catch extract_multipart(Context#cb_context{query_json=QS}, Req1) of
{'EXIT', _} ->
lager:debug("failed to extract multipart"),
{halt, Context, Req1};
Resp -> Resp
end;
get_req_data(Context, {{<<"application">>, <<"json">>, _}, Req1}, QS) ->
lager:debug("application/json content type when getting req data"),
{JSON, Req2} = get_json_body(Req1),
{Context#cb_context{req_json=JSON
,req_data=wh_json:get_value(<<"data">>, JSON, wh_json:new())
,query_json=QS
}
,Req2};
get_req_data(Context, {{<<"application">>, <<"x-json">>, _}, Req1}, QS) ->
lager:debug("application/x-json content type when getting req data"),
{JSON, Req2} = get_json_body(Req1),
{Context#cb_context{req_json=JSON
,req_data=wh_json:get_value(<<"data">>, JSON, wh_json:new())
,query_json=QS
}
,Req2};
get_req_data(Context, {{<<"application">>, <<"base64">>, _}, Req1}, QS) ->
lager:debug("application/base64 content type when getting req data"),
decode_base64(Context#cb_context{query_json=QS}, <<"application/base64">>, Req1);
get_req_data(Context, {{<<"application">>, <<"x-base64">>, _}, Req1}, QS) ->
lager:debug("application/x-base64 content type when getting req data"),
decode_base64(Context#cb_context{query_json=QS}, <<"application/base64">>, Req1);
get_req_data(Context, {{ContentType, ContentSubType, _}, Req1}, QS) ->
lager:debug("unknown content-type: ~s/~s", [ContentType, ContentSubType]),
extract_file(Context#cb_context{query_json=QS}, list_to_binary([ContentType, "/", ContentSubType]), Req1).

-spec extract_multipart/2 :: (#cb_context{}, #http_req{}) -> {#cb_context{}, #http_req{}}. -spec extract_multipart/2 :: (#cb_context{}, #http_req{}) -> {#cb_context{}, #http_req{}}.
extract_multipart(#cb_context{req_files=Files}=Context, #http_req{}=Req0) -> extract_multipart(#cb_context{req_files=Files}=Context, #http_req{}=Req0) ->
MPData = cowboy_http_req:multipart_data(Req0), MPData = cowboy_http_req:multipart_data(Req0),
Expand Down Expand Up @@ -276,10 +293,7 @@ get_json_body(Req0) ->
catch catch
_:{badmatch, {comma,{decoder,_,S,_,_,_}}} -> _:{badmatch, {comma,{decoder,_,S,_,_,_}}} ->
lager:debug("failed to decode json: comma error around char ~s", [wh_util:to_list(S)]), lager:debug("failed to decode json: comma error around char ~s", [wh_util:to_list(S)]),
{{malformed, list_to_binary(["Failed to decode: comma error around char ", wh_util:to_list(S)])}, Req1}; {{malformed, list_to_binary(["Failed to decode: comma error around char ", wh_util:to_list(S)])}, Req1}
_:E ->
lager:debug("failed to decode json: ~p", [E]),
{{malformed, <<"JSON failed to validate; check your commas and curlys">>}, Req1}
end end
end. end.


Expand All @@ -293,7 +307,6 @@ get_json_body(Req0) ->
is_valid_request_envelope(JSON) -> is_valid_request_envelope(JSON) ->
wh_json:get_value([<<"data">>], JSON, undefined) =/= undefined. wh_json:get_value([<<"data">>], JSON, undefined) =/= undefined.



-spec get_http_verb/2 :: (http_method(), #cb_context{}) -> ne_binary(). -spec get_http_verb/2 :: (http_method(), #cb_context{}) -> ne_binary().
get_http_verb(Method, #cb_context{req_json=ReqJObj, query_json=ReqQs}) -> get_http_verb(Method, #cb_context{req_json=ReqJObj, query_json=ReqQs}) ->
case wh_json:get_value(<<"verb">>, ReqJObj) of case wh_json:get_value(<<"verb">>, ReqJObj) of
Expand Down

0 comments on commit 3394b72

Please sign in to comment.