Packages
nova
0.4.4
0.15.1
0.14.3
0.14.1
0.13.7
0.13.1
0.13.0
0.12.1
0.12.0
0.11.0
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.11
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.4.4
0.4.3
0.4.2
0.4.1
0.2.2
0.1.0
0.0.1
0.0.0
Nova is a web application framework
Current section
Files
Jump to
Current section
Files
src/nova_http_handler.erl
%%% @author Niclas Axelsson <niclas@burbas.se>
%%% @doc
%%% Callback controller for handling http requests
%%% @end
-module(nova_http_handler).
-export([
init/2,
handle/4
]).
-include_lib("nova/include/nova.hrl").
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Public functions %
%%%%%%%%%%%%%%%%%%%%%%%%%%%
-type method() :: '_' | binary().
-type nova_http_state() :: #{mod := atom(),
func := atom(),
methods := [method()] | method(),
_ => _}.
-export_type([nova_http_state/0]).
%%--------------------------------------------------------------------
%% @doc
%% Callback function from nova_handler. This is the initial call where
%% all the logic is handed.
%% @end
%%--------------------------------------------------------------------
-spec init(Req :: cowboy_req:req(), State :: nova_http_state()) ->
{ok, Req1 :: cowboy_req:req(), State0 :: nova_http_state()}.
init(Req, State = #{mod := Mod, func := Func, methods := '_'}) ->
{ok, StatusCode, Headers, Body, State0} = handle(Mod, Func, Req, State),
Req1 = cowboy_req:reply(StatusCode, Headers, Body, Req),
{ok, Req1, State0};
init(Req = #{method := ReqMethod}, State = #{methods := Methods}) ->
case lists:any(fun(X) -> X == ReqMethod end, Methods) of
true ->
init(Req, State#{methods := '_'});
false ->
Req1 =
case nova_router:status_page(405, Req) of
{ok, StatusCode, Headers, Body, _} ->
cowboy_req:reply(StatusCode, Headers, Body, Req);
_ ->
cowboy_req:reply(405, #{}, <<>>, Req)
end,
{ok, Req1, State}
end;
init(Req, State) ->
{ok, StatusCode, Headers, Body, _} = nova_router:status_page(404, Req),
Req1 = cowboy_req:reply(StatusCode, Headers, Body, Req),
{ok, Req1, State}.
%%--------------------------------------------------------------------
%% @doc
%% This function is exposed mostly cause we need to call it from nova_router.
%% It returns the raw handle request instead of the aggregated result returned in init/2.
%% @end
%%--------------------------------------------------------------------
-spec handle(Mod :: atom(), Fun :: atom(), Req :: cowboy_req:req(), State :: nova_http_state()) ->
{ok, StatusCode :: integer(), Headers :: cowboy:http_headers(), Body :: binary(),
State0 :: nova_http_state()}.
handle(Mod, Fun, Req, State) ->
?DEBUG("Handling request for ~p:~p", [Mod, Fun]),
Args =
case maps:get(auth_data, State, undefined) of
undefined ->
[Req];
SecObject ->
[Req, SecObject]
end,
try erlang:apply(Mod, Fun, Args) of
RetObj ->
case nova_handlers:get_handler(element(1, RetObj)) of
{ok, Callback} ->
Callback(RetObj, {Mod, Fun}, Req, State);
_ ->
?ERROR("Unknown return object ~p returned from module: ~p function: ~p", [RetObj, Mod, Fun]),
erlang:throw({unknown_handler_type, RetObj})
end
catch
?WITH_STACKTRACE(Type, Reason, Stacktrace)
?ERROR("Controller (~p:~p/1) failed with ~p:~p.~nStacktrace:~n~p", [Mod, Fun, Type, Reason, Stacktrace]),
case nova_router:status_page(500, Req) of
{error, not_found} ->
%% Render our own view. Hide information if we're not in dev_mode
DevMode = nova:get_env(dev_mode, false),
{ok, HTML} = nova_internal_error_dtl:render([{module, Mod},
{function, Fun},
{type, Type},
{reason, Reason},
{stacktrace, Stacktrace},
{dev_mode, DevMode}], []),
{ok, 500, #{}, HTML, State};
Page ->
Page
end
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Eunit functions %
%%%%%%%%%%%%%%%%%%%%%%%%%%%
-ifdef(TEST).
-endif.