|
|
@ -1,33 +1,17 @@ |
|
|
|
%%% @doc: Elli example callback |
|
|
|
%%% |
|
|
|
%%% Your callback needs to implement two functions, {@link handle/2} and |
|
|
|
%%% {@link handle_event/3}. For every request, Elli will call your handle |
|
|
|
%%% function with the request. When an event happens, like Elli |
|
|
|
%%% completed a request, there was a parsing error or your handler |
|
|
|
%%% threw an error, {@link handle_event/3} is called. |
|
|
|
|
|
|
|
-module(wsEgHer). |
|
|
|
-export([handle/2, handle_event/3]). |
|
|
|
-export([chunk_loop/1]). |
|
|
|
|
|
|
|
-include("wsCom.hrl"). |
|
|
|
-behaviour(wsHer). |
|
|
|
|
|
|
|
-include("wsCom.hrl"). |
|
|
|
-include_lib("kernel/include/file.hrl"). |
|
|
|
|
|
|
|
%% |
|
|
|
%% ELLI REQUEST CALLBACK |
|
|
|
%% |
|
|
|
|
|
|
|
%% @doc Handle a `Req'uest. |
|
|
|
%% Delegate to our handler function. |
|
|
|
%% @see handle/3 |
|
|
|
-spec handle(Req, _Args) -> Result when |
|
|
|
Req :: elli:wsReq(), |
|
|
|
_Args :: wsHer:callback_args(), |
|
|
|
Result :: wsHer:result(). |
|
|
|
handle(Req, _Args) -> handle(Req#wsReq.method, wsReq:path(Req), Req). |
|
|
|
-export([ |
|
|
|
handle/3 |
|
|
|
]). |
|
|
|
|
|
|
|
-export([ |
|
|
|
chunk_loop/1 |
|
|
|
]). |
|
|
|
|
|
|
|
%% @doc Route `Method' and `Path' to the appropriate clause. |
|
|
|
%% |
|
|
@ -49,33 +33,19 @@ handle(Req, _Args) -> handle(Req#wsReq.method, wsReq:path(Req), Req). |
|
|
|
%% allows you to return an empty body. Useful for |
|
|
|
%% implementing the `"304 Not Modified"' response. |
|
|
|
%% |
|
|
|
%% @see elli_request:get_arg/3 |
|
|
|
%% @see elli_request:post_arg/3 |
|
|
|
%% @see elli_request:post_arg_decoded/3 |
|
|
|
%% @see elli_request:get_header/3 |
|
|
|
%% @see elli_request:get_arg_decoded/3 |
|
|
|
%% @see elli_request:get_args_decoded/1 |
|
|
|
%% @see elli_util:file_size/1 |
|
|
|
%% @see elli_request:get_range/1 |
|
|
|
%% @see elli_request:normalize_range/2 |
|
|
|
%% @see elli_request:encode_range/2 |
|
|
|
%% @see elli_request:chunk_ref/1 |
|
|
|
%% @see chunk_loop/1 |
|
|
|
-spec handle(Method, Path, Req) -> wsHer:result() when |
|
|
|
Method :: elli:method(), |
|
|
|
Path :: [binary()], |
|
|
|
Req :: elli:wsReq(). |
|
|
|
handle('GET', [<<"hello">>, <<"world">>], _Req) -> |
|
|
|
|
|
|
|
-spec handle(Method :: method(), Path :: path(), Req :: wsReq()) -> wsHer:response(). |
|
|
|
handle('GET', <<"hello/world">>, wsReq) -> |
|
|
|
%% Reply with a normal response. |
|
|
|
timer:sleep(1000), |
|
|
|
{ok, [], <<"Hello World!">>}; |
|
|
|
|
|
|
|
handle('GET', [<<"hello">>], Req) -> |
|
|
|
handle('GET', <<"hello">>, Req) -> |
|
|
|
%% Fetch a GET argument from the URL. |
|
|
|
Name = wsReq:get_arg(<<"name">>, Req, <<"undefined">>), |
|
|
|
{ok, [], <<"Hello ", Name/binary>>}; |
|
|
|
|
|
|
|
handle('POST', [<<"hello">>], Req) -> |
|
|
|
handle('POST', <<"hello">>, Req) -> |
|
|
|
%% Fetch a POST argument from the POST body. |
|
|
|
Name = wsReq:post_arg(<<"name">>, Req, <<"undefined">>), |
|
|
|
%% Fetch and decode |
|
|
@ -225,102 +195,4 @@ chunk_loop(Ref, N) -> |
|
|
|
ok -> ok; |
|
|
|
{error, Reason} -> ?wsErr("error in sending chunk: ~p~n", [Reason]) |
|
|
|
end, |
|
|
|
|
|
|
|
chunk_loop(Ref, N - 1). |
|
|
|
|
|
|
|
|
|
|
|
%% |
|
|
|
%% ELLI EVENT CALLBACKS |
|
|
|
%% |
|
|
|
|
|
|
|
|
|
|
|
%% @doc Handle Elli events, fired throughout processing a request. |
|
|
|
%% |
|
|
|
%% `elli_startup' is sent when Elli is starting up. If you are |
|
|
|
%% implementing a middleware, you can use it to spawn processes, |
|
|
|
%% create ETS tables or start supervised processes in a supervisor |
|
|
|
%% tree. |
|
|
|
%% |
|
|
|
%% `request_complete' fires *after* Elli has sent the response to the |
|
|
|
%% client. `Timings' contains timestamps (native units) of events like when the |
|
|
|
%% connection was accepted, when headers/body parsing finished, when the |
|
|
|
%% user callback returns, response sent, etc. `Sizes' contains response sizes |
|
|
|
%% like response headers size, response body or file size. |
|
|
|
%% This allows you to collect performance statistics for monitoring your app. |
|
|
|
%% |
|
|
|
%% `request_throw', `request_error' and `request_exit' events are sent if |
|
|
|
%% the user callback code throws an exception, has an error or |
|
|
|
%% exits. After triggering this event, a generated response is sent to |
|
|
|
%% the user. |
|
|
|
%% |
|
|
|
%% `invalid_return' is sent if the user callback code returns a term not |
|
|
|
%% understood by elli, see {@link elli_http:execute_callback/1}. |
|
|
|
%% After triggering this event, a generated response is sent to the user. |
|
|
|
%% |
|
|
|
%% `chunk_complete' fires when a chunked response is completely |
|
|
|
%% sent. It's identical to the `request_complete' event, except instead |
|
|
|
%% of the response body you get the atom `client' or `server' |
|
|
|
%% depending on who closed the connection. `Sizes' will have the key `chunks', |
|
|
|
%% which is the total size of all chunks plus encoding overhead. |
|
|
|
%% |
|
|
|
%% `request_closed' is sent if the client closes the connection when |
|
|
|
%% Elli is waiting for the next request on a keep alive connection. |
|
|
|
%% |
|
|
|
%% `request_timeout' is sent if the client times out when |
|
|
|
%% Elli is waiting for the request. |
|
|
|
%% |
|
|
|
%% `request_parse_error' fires if the request is invalid and cannot be parsed by |
|
|
|
%% [`erlang:decode_packet/3`][decode_packet/3] or it contains a path Elli cannot |
|
|
|
%% parse or does not support. |
|
|
|
%% |
|
|
|
%% [decode_packet/3]: http://erlang.org/doc/man/erlang.html#decode_packet-3 |
|
|
|
%% |
|
|
|
%% `client_closed' can be sent from multiple parts of the request |
|
|
|
%% handling. It's sent when the client closes the connection or if for |
|
|
|
%% any reason the socket is closed unexpectedly. The `Where' atom |
|
|
|
%% tells you in which part of the request processing the closed socket |
|
|
|
%% was detected: `receiving_headers', `receiving_body' or `before_response'. |
|
|
|
%% |
|
|
|
%% `client_timeout' can as with `client_closed' be sent from multiple |
|
|
|
%% parts of the request handling. If Elli tries to receive data from |
|
|
|
%% the client socket and does not receive anything within a timeout, |
|
|
|
%% this event fires and the socket is closed. |
|
|
|
%% |
|
|
|
%% `bad_request' is sent when Elli detects a request is not well |
|
|
|
%% formatted or does not conform to the configured limits. Currently |
|
|
|
%% the `Reason' variable can be `{too_many_headers, Headers}' |
|
|
|
%% or `{body_size, ContentLength}'. |
|
|
|
%% |
|
|
|
%% `file_error' is sent when the user wants to return a file as a |
|
|
|
%% response, but for some reason it cannot be opened. |
|
|
|
-spec handle_event(Event, Args, Config) -> ok when |
|
|
|
Event :: elli:event(), |
|
|
|
Args :: wsHer:callback_args(), |
|
|
|
Config :: [tuple()]. |
|
|
|
handle_event(elli_startup, [], _) -> ok; |
|
|
|
handle_event(request_complete, [_Request, |
|
|
|
_ResponseCode, _ResponseHeaders, _ResponseBody, |
|
|
|
{_Timings, _Sizes}], _) -> ok; |
|
|
|
handle_event(request_throw, [_Request, _Exception, _Stacktrace], _) -> ok; |
|
|
|
handle_event(request_error, [_Request, _Exception, _Stacktrace], _) -> ok; |
|
|
|
handle_event(request_exit, [_Request, _Exception, _Stacktrace], _) -> ok; |
|
|
|
|
|
|
|
handle_event(invalid_return, [_Request, _ReturnValue], _) -> ok; |
|
|
|
|
|
|
|
handle_event(chunk_complete, [_Request, |
|
|
|
_ResponseCode, _ResponseHeaders, _ClosingEnd, |
|
|
|
{_Timings, _Sizes}], _) -> ok; |
|
|
|
|
|
|
|
handle_event(request_closed, [], _) -> ok; |
|
|
|
|
|
|
|
handle_event(request_timeout, [], _) -> ok; |
|
|
|
|
|
|
|
handle_event(request_parse_error, [_], _) -> ok; |
|
|
|
|
|
|
|
handle_event(client_closed, [_Where], _) -> ok; |
|
|
|
|
|
|
|
handle_event(client_timeout, [_Where], _) -> ok; |
|
|
|
|
|
|
|
handle_event(bad_request, [_Reason], _) -> ok; |
|
|
|
|
|
|
|
handle_event(file_error, [_ErrorReason], _) -> ok. |
|
|
|
chunk_loop(Ref, N - 1). |