%%% Copyright (c) 2014-2016, NORDUnet A/S. %%% See LICENSE for licensing information. -module(catlfish_web). -export([start/3, loop/2]). start(Options, Module, Name) -> lager:debug("Starting catlfish web server: ~p", [Module]), Loop = fun (Req) -> ?MODULE:loop(Req, Module) end, mochiweb_http:start([{name, Name}, {loop, Loop} | Options]). add_auth(Path, {Code, Headers, Data}) -> AuthHeader = http_auth:create_auth("REPLY", Path, Data), lager:debug("sent auth header: ~p", [AuthHeader]), {Code, [{"X-Catlfish-Auth", AuthHeader} | Headers], Data}. split_path([]) -> {[], []}; split_path([E]) -> {E, []}; split_path(Parts) -> [Fun | AppRev] = lists:reverse(Parts), App = string:join(lists:reverse(AppRev), "/"), {Fun, App}. loop(Req, Module) -> Path = Req:get(path), {Fun, App} = split_path(string:tokens(Path, "/")), lager:debug("Fun=~s; App=~s;", [Fun, App]), try Starttime = os:timestamp(), AuthHeader = Req:get_header_value("X-Catlfish-Auth"), case Req:get(method) of 'GET' -> Query = Req:parse_qs(), {_, RawQuery, _} = mochiweb_util:urlsplit_path(Req:get(raw_path)), Result = case http_auth:verify_auth(AuthHeader, "GET", Path, RawQuery) of failure -> {403, [{"Content-Type", "text/plain"}], "Invalid credentials"}; success -> lager:info("GET ~p", [Path]), lager:debug("GET ~p ~p", [Path, Query]), add_auth(Path, Module:request(get, App, Fun, Query)); noauth -> lager:info("GET ~p", [Path]), lager:debug("GET ~p ~p", [Path, Query]), Module:request(get, App, Fun, Query) end, lager:info("GET finished: ~p us", [timer:now_diff(os:timestamp(), Starttime)]), case Result of none -> Req:respond({404, [{"Content-Type", "text/plain"}], "Page not found"}); _ -> Req:respond(Result) end; 'POST' -> Body = Req:recv_body(), Result = case http_auth:verify_auth(AuthHeader, "POST", Path, Body) of failure -> {403, [{"Content-Type", "text/plain"}], "Invalid credentials"}; success -> lager:info("POST ~p", [Path]), lager:debug("POST ~p ~p", [Path, Body]), add_auth(Path, Module:request(post, App, Fun, Body)); noauth -> lager:info("POST ~p", [Path]), lager:debug("POST ~p ~p", [Path, Body]), Module:request(post, App, Fun, Body) end, lager:info("POST finished: ~p us", [timer:now_diff(os:timestamp(), Starttime)]), case Result of none -> Req:respond({404, [{"Content-Type", "text/plain"}], "Page not found"}); _ -> Req:respond(Result) end; _ -> Req:respond({501, [], []}) end catch exit:{body_too_large, What} -> lager:info("HTTP POST body too large: ~p", [What]), Req:respond({413, [{"Content-Type", "text/plain"}], "Request Entity Too Large\n"}); Type:What -> [CrashFunction | Stack] = erlang:get_stacktrace(), lager:error("Crash in ~p for path ~p: ~p ~p~n~p~n~p~n", [Module, Path, Type, What, CrashFunction, Stack]), Req:respond({500, [{"Content-Type", "text/plain"}], "Internal Server Error\n"}) end.