summaryrefslogtreecommitdiff
path: root/src/catlfish_web.erl
blob: 52441fccf3d60680d1b67baa8747640c3552b252 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
%%% 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.