Skip to content

Commit

Permalink
Added elli_middlewar, a handler that allows multiple request handlers…
Browse files Browse the repository at this point in the history
…, enough to implement almost any middleware.
  • Loading branch information
knutin committed Mar 29, 2012
1 parent 6b00a52 commit aa32349
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 1 deletion.
3 changes: 2 additions & 1 deletion rebar.config
Expand Up @@ -2,5 +2,6 @@
{erl_opts, [debug_info]}.
{deps, [
{lhttpc, "1.3.0", {git, "git://github.com/oscarh/lhttpc.git", "master"}},
{meck, "0.7.1", {git, "git://github.com/eproxus/meck.git", "master"}}
{meck, "0.7.1", {git, "git://github.com/eproxus/meck.git", "master"}},
{syslog, "1.0", {git, "git://github.com/knutin/erlang_syslog.git", "master"}}
]}.
2 changes: 2 additions & 0 deletions src/elli.erl
Expand Up @@ -40,6 +40,8 @@ init([Opts]) ->
CallbackArgs = proplists:get_value(callback_args, Opts),
Port = proplists:get_value(port, Opts, 8080),

ok = Callback:handle_event(elli_startup, [], CallbackArgs),

{ok, Socket} = gen_tcp:listen(Port, [binary,
{reuseaddr, true},
{backlog, 32768},
Expand Down
54 changes: 54 additions & 0 deletions src/elli_access_log.erl
@@ -0,0 +1,54 @@
-module(elli_access_log).
-behaviour(elli_handler).
-export([handle/2, handle_event/3]).


handle(_Req, _Args) ->
ignore.

handle_event(request_complete, [Req, ResponseCode, _ResponseHeaders,
ResponseBody, _Timings], Config) ->

Msg = io_lib:format("~s \"~s ~s\" ~w ~w",
[elli_request:peer(Req),
elli_request:method(Req),
elli_request:raw_path(Req),
ResponseCode,
byte_size(ResponseBody)]),

syslog:send(proplists:get_value(name, Config), Msg, [{ident, node()}]),
ok;

handle_event(request_throw, [_Req, Exception, Stack], _Config) ->
error_logger:error_msg("exception: ~p~n stack: ~p~n", [Exception, Stack]),
ok;
handle_event(request_exit, [_Req, Exit, Stack], _Config) ->
error_logger:error_msg("exit: ~p~n stack: ~p~n", [Exit, Stack]),
ok;

handle_event(request_error, [_Req, Error, Stack], _Config) ->
error_logger:error_msg("error: ~p~n stack: ~p~n", [Error, Stack]),
ok;

handle_event(request_parse_error, [_Data], _Args) ->
ok;

handle_event(client_closed, [_When], _Config) ->
ok;

handle_event(client_timeout, [_When], _Config) ->
ok;
handle_event(elli_startup, [], Config) ->
case whereis(proplists:get_value(name, Config)) of
undefined ->
{ok, _Pid} = syslog:start_link(proplists:get_value(name, Config),
proplists:get_value(ip, Config),
proplists:get_value(port, Config)),
ok;
Pid when is_pid(Pid) ->
ok
end.




26 changes: 26 additions & 0 deletions src/elli_example_middleware.erl
@@ -0,0 +1,26 @@
-module(elli_example_middleware).
-export([handle/2, handle_event/3]).
-behaviour(elli_handler).


%%
%% ELLI
%%

handle(Req, _Args) ->
case elli_request:path(Req) of
[<<"middleware">>, <<"short-circuit">>] ->
{200, [], <<"short circuit!">>};
_ ->
ignore
end.



%%
%% ELLI EVENT CALLBACKS
%%


handle_event(_Event, _Data, _Args) ->
ok.
40 changes: 40 additions & 0 deletions src/elli_middleware.erl
@@ -0,0 +1,40 @@
-module(elli_middleware).
-behaviour(elli_handler).
-export([handle/2, handle_event/3]).


handle(Req, Config) ->
forward_request(Req, mods(Config)).

handle_event(Event, Args, Config) ->
forward_event(Event, Args, mods(Config)).



%%
%% MIDDLEWARE LOGIC
%%

forward_request(_Req, []) ->
{404, [], <<"Not Found">>};
forward_request(Req, [{Mod, Args} | Mods]) ->
case Mod:handle(Req, Args) of
ignore ->
forward_request(Req, Mods);
{forward, NewReq} ->
forward_request(NewReq, Mods);
Response ->
Response
end.

forward_event(F, A, Mods) ->
lists:map(fun ({M, ExtraArgs}) ->
M:handle_event(F, A, ExtraArgs)
end, Mods),
ok.

%%
%% INTERNAL HELPERS
%%

mods(Config) -> proplists:get_value(mods, Config).
64 changes: 64 additions & 0 deletions test/elli_middleware_tests.erl
@@ -0,0 +1,64 @@
-module(elli_middleware_tests).
-include_lib("eunit/include/eunit.hrl").
-include("elli.hrl").


elli_test_() ->
{setup,
fun setup/0, fun teardown/1,
[
?_test(hello_world()),
?_test(short_circuit())
]}.



setup() ->
application:start(crypto),
application:start(public_key),
application:start(ssl),
ok = lhttpc:start(),

Config = [
{mods, [
{elli_access_log, [{name, elli_syslog},
{ip, "127.0.01"},
{port, 514}]},
{elli_example_middleware, []},
{elli_example_callback, []}
]}
],

{ok, P} = elli:start_link([{callback, elli_middleware},
{callback_args, Config}]),

unlink(P),
[P].

teardown(Pids) ->
[elli:stop(P) || P <- Pids].

short_circuit() ->
URL = "http://localhost:8080/middleware/short-circuit",
{ok, Response} = lhttpc:request(URL, "GET", [], 1000),
?assertEqual(<<"short circuit!">>, body(Response)).

hello_world() ->
URL = "http://localhost:8080/hello/world",
{ok, Response} = lhttpc:request(URL, "GET", [], 1000),
?assertEqual(<<"Hello World!">>, body(Response)).



%%
%% HELPERS
%%

status({Status, _, _}) ->
Status.

body({_, _, Body}) ->
Body.

headers({_, Headers, _}) ->
lists:sort(Headers).

0 comments on commit aa32349

Please sign in to comment.