<commit_before>%%
%% Copyright (C) 2014, <NAME>
%%
%% This program is licensed under the terms and conditions of the
%% Mozilla Public License, version 2.0.  The full text of the 
%% Mozilla Public License is at https://www.mozilla.org/MPL/2.0/
%%


-module(exoport_exo_http).
-export([instance/3,
	 handle_body/4,
%%	 json_rpc/1,
%%	 json_rpc/2,
	 data_to_json/3]).

-include_lib("exo/include/exo_http.hrl").
-include_lib("lager/include/log.hrl").


instance(SupMod, AppMod, Opts) ->
    ?debug("exoport_exo_http:instance(): SupModule(~p), Opts(~p)",
	   [ SupMod, Opts]),
    Port = opt(port, Opts, 8800),

    Child = {exo_http, {exo_http_server, start_link,
			[Port, [{request_handler,
				 {?MODULE, handle_body, [AppMod]}}]]},
	     permanent, 5000, worker, [exo_http_server]},
    case supervisor:start_child(SupMod, Child) of
	{ok, _} -> ok;
	{ok, _, _} -> ok;
	Err -> Err
    end.


handle_body(Socket, Request, Body, AppMod) when Request#http_request.method == 'POST' ->
    Url = Request#http_request.uri,
    ?debug("exoport_exo_http: handle_body(): Url:       ~p", [Url]),
    ?debug("exoport_exo_http: handle_body(): Socket:    ~p", [Socket]),
    ?debug("exoport_exo_http: handle_body(): AppMod:    ~p", [AppMod]),
    ?debug("exoport_exo_http: handle_body(): Request:   ~p", [Request]),
    ?debug("exoport_exo_http: handle_body(): Body:      ~p", [Body]),

    try decode_json(Body) of
	{call, Id, Method, Args} ->
	    case handle_rpc(AppMod, Method, Args) of
		{ok, Reply} ->
		    success_response(Socket, Id, Reply);
		ok ->
		    ok;
		{error, Error} ->
		    error_response(Socket, Id, Error)
	    end;
	{notification, _Method, _Args} ->
	    %% FIXME: Notification.
	    exo_http_server:response(Socket, undefined, 200, "OK", "");
	{error, _} ->
	    error_response(Socket, parse_error)
    catch
	error:_ ->
	    exo_http_server:response(Socket, undefined, 501,
				     "Internal Error",
				     "Internal Error")
    end;

handle_body(Socket, _Request, _Body, _AppMod) ->
    exo_http_server:response(Socket, undefined, 404, "Not Found",
			     "Object not found. Try using POST method.").


%% Validated RPC
handle_rpc(Mod, Method, Args) ->
    ?debug("exo_http_server:handle_rpc(): Mod:       ~p", [Mod]),
    ?debug("exo_http_server:handle_rpc(): Method:    ~p", [Method]),
    ?debug("exo_http_server:handle_rpc(): Args:      ~p", [Args]),

    try Mod:handle_rpc(Method, Args) of
	{ok, Result} ->
	    ?debug("exo_http_server:handle_rpc(ok): Result:   ~p", [Result]),
	    {ok, Result};
	
	{error, Reason} ->
	    {error, Reason}
    catch
	error:Crash ->
            ?error("rpc_callback() CRASHED: Reason:   ~p", [Crash]),
            ?error("post_request() CRASHED: Stack:    ~p", [erlang:get_stacktrace()]),
	    {error, {internal_error, Crash}}
    end.


%% call_cb_rpc(CBMod, Mod, Method, Args, Meta) when is_atom(CBMod) ->
%%     CBMod:handle_rpc(Mod, Method, Args, Meta);
%% call_cb_rpc({CBMod, St}, Mod, Method, Args, Meta) ->
%%     CBMod:handle_rpc(Mod, Method, Args, Meta, St).


%% json_rpc(Req) ->
%%     json_rpc(Req, []).

%% json_rpc({call, _Id, Method, {struct, Args}}, CB) ->
%%     case re:split(Method, ":", [{return, list}]) of
%% 	[ModS, FunS] ->
%% 	    try Mod = list_to_existing_atom("yang_rvspec_" ++ ModS),
%% 		 case Mod:rpc(list_to_binary(FunS)) of
%% 		     error ->
%% 			 {error, method_not_found};
%% 		     {rpc, _, _, Data} ->
%% 			 {input,_,_,InputElems} = lists:keyfind(input,1,Data),
%% 			 try yang_json:validate_rpc_request(
%% 			       InputElems, Args) of
%% 			     {ok, Elems, Meta} ->
%% 				 handle_rpc(CB, list_to_binary(ModS),
%% 					    list_to_binary(FunS),
%% 					    Elems, Meta);
%% 			     {error, _Reason} ->
%% 				 {error, parse_error}
%% 			 catch
%% 			     error:E ->
%% 				 io:fwrite("Validation crash~n"
%% 					   "error:~p, ~p~n",
%% 					   [E, erlang:get_stacktrace()]),
%% 				 {error, invalid_request}
%% 			 end
%% 		 end
%% 	    catch
%% 		error:_ ->
%% 		    {error, method_not_found}
%% 	    end;
%% 	_ ->
%% 	    {error, method_not_found}
%%     end.

%% validate_rpc_result(Result, Mod, Method) ->
%%     ErlMod = list_to_existing_atom("yang_spec_" ++ binary_to_list(Mod)),
%%     case ErlMod:rpc(Method) of
%% 	{rpc, _, _, Data} ->
%% 	    %% This really should be FIXed; output is not mandatory
%% 	    {output, _, _, Elems} = lists:keyfind(output, 1, Data),
%% 	    JSON = data_to_json(Elems, [], Result),
%% 	    {ok, {struct, JSON}};
%% 	error ->
%% 	    {error, method_not_found}
%%     end.

success_response(Socket, Id, Reply) ->
    JSON = {struct, [{"jsonrpc", "2.0"},
		     {"id", Id},
		     {"result", {struct,  Reply}}]},
    exo_http_server:response(Socket, undefined, 200, "OK",
			     exo_json:encode(JSON),
			     [{content_type, "application/json"}]).

error_response(Socket, Error) ->
    %% No Id available
    JSON = {struct, [{"jsonrpc", "2.0"},
		     {"error", {struct,
				[{"code", json_error_code(Error)},
				 {"message", json_error_msg(Error)}]}}]},
    Body = list_to_binary(exo_json:encode(JSON)),
    exo_http_server:response(Socket, undefined, 200, "OK", Body,
			     [{content_type, "application/json"}]).

error_response(Socket, Id, Error) ->
    JSON = {struct, [{"jsonrpc", "2.0"},
		     {"id", Id},
		     {"error", {struct,
				[{"code", json_error_code(Error)},<commit_msg>Modified copyright to reflect origin of exoport_exo_http.erl<commit_after>%%
%% Copyright (C) 2014, Feuerlabs 
%%
%% This program is licensed under the terms and conditions of the
%% Mozilla Public License, version 2.0.  The full text of the 
%% Mozilla Public License is at https://www.mozilla.org/MPL/2.0/
%%
-module(exoport_exo_http).
-export([instance/3,
	 handle_body/4,
%%	 json_rpc/1,
%%	 json_rpc/2,
	 data_to_json/3]).

-include_lib("exo/include/exo_http.hrl").
-include_lib("lager/include/log.hrl").


instance(SupMod, AppMod, Opts) ->
    ?debug("exoport_exo_http:instance(): SupModule(~p), Opts(~p)",
	   [ SupMod, Opts]),
    Port = opt(port, Opts, 8800),

    Child = {exo_http, {exo_http_server, start_link,
			[Port, [{request_handler,
				 {?MODULE, handle_body, [AppMod]}}]]},
	     permanent, 5000, worker, [exo_http_server]},
    case supervisor:start_child(SupMod, Child) of
	{ok, _} -> ok;
	{ok, _, _} -> ok;
	Err -> Err
    end.


handle_body(Socket, Request, Body, AppMod) when Request#http_request.method == 'POST' ->
    Url = Request#http_request.uri,
    ?debug("exoport_exo_http: handle_body(): Url:       ~p", [Url]),
    ?debug("exoport_exo_http: handle_body(): Socket:    ~p", [Socket]),
    ?debug("exoport_exo_http: handle_body(): AppMod:    ~p", [AppMod]),
    ?debug("exoport_exo_http: handle_body(): Request:   ~p", [Request]),
    ?debug("exoport_exo_http: handle_body(): Body:      ~p", [Body]),

    try decode_json(Body) of
	{call, Id, Method, Args} ->
	    case handle_rpc(AppMod, Method, Args) of
		{ok, Reply} ->
		    success_response(Socket, Id, Reply);
		ok ->
		    ok;
		{error, Error} ->
		    error_response(Socket, Id, Error)
	    end;
	{notification, _Method, _Args} ->
	    %% FIXME: Notification.
	    exo_http_server:response(Socket, undefined, 200, "OK", "");
	{error, _} ->
	    error_response(Socket, parse_error)
    catch
	error:_ ->
	    exo_http_server:response(Socket, undefined, 501,
				     "Internal Error",
				     "Internal Error")
    end;

handle_body(Socket, _Request, _Body, _AppMod) ->
    exo_http_server:response(Socket, undefined, 404, "Not Found",
			     "Object not found. Try using POST method.").


%% Validated RPC
handle_rpc(Mod, Method, Args) ->
    ?debug("exo_http_server:handle_rpc(): Mod:       ~p", [Mod]),
    ?debug("exo_http_server:handle_rpc(): Method:    ~p", [Method]),
    ?debug("exo_http_server:handle_rpc(): Args:      ~p", [Args]),

    try Mod:handle_rpc(Method, Args) of
	{ok, Result} ->
	    ?debug("exo_http_server:handle_rpc(ok): Result:   ~p", [Result]),
	    {ok, Result};
	
	{error, Reason} ->
	    {error, Reason}
    catch
	error:Crash ->
            ?error("rpc_callback() CRASHED: Reason:   ~p", [Crash]),
            ?error("post_request() CRASHED: Stack:    ~p", [erlang:get_stacktrace()]),
	    {error, {internal_error, Crash}}
    end.

success_response(Socket, Id, Reply) ->
    JSON = {struct, [{"jsonrpc", "2.0"},
		     {"id", Id},
		     {"result", {struct,  Reply}}]},
    exo_http_server:response(Socket, undefined, 200, "OK",
			     exo_json:encode(JSON),
			     [{content_type, "application/json"}]).

error_response(Socket, Error) ->
    %% No Id available
    JSON = {struct, [{"jsonrpc", "2.0"},
		     {"error", {struct,
				[{"code", json_error_code(Error)},
				 {"message", json_error_msg(Error)}]}}]},
    Body = list_to_binary(exo_json:encode(JSON)),
    exo_http_server:response(Socket, undefined, 200, "OK", Body,
			     [{content_type, "application/json"}]).

error_response(Socket, Id, Error) ->
    JSON = {struct, [{"jsonrpc", "2.0"},
		     {"id", Id},
		     {"error", {struct,
				[{"code", json_error_code(Error)},
{'fraction_non_alphanumeric': 0.18346482942549172, 'fraction_numerical': 0.009556617401933549, 'mean_word_length': 3.4291338582677167, 'pattern_counts': {'":': 1, '<': 4, '<?xml version=': 0, '>': 59, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6240930', 'n_tokens_mistral': 3572, 'n_tokens_neox': 3287, 'n_words': 684}