aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2017-11-01 16:27:26 +0000
committerLoïc Hoguin <[email protected]>2017-11-01 16:27:26 +0000
commit836342abb86b3ff15d1c8319a455d776f7027a87 (patch)
tree9d5fb1277006fac53920e8df80c144c7b45171d3 /src
parent5e88a9b394a2ae966ba95efadbf1cc24d060b457 (diff)
downloadcowboy-836342abb86b3ff15d1c8319a455d776f7027a87.tar.gz
cowboy-836342abb86b3ff15d1c8319a455d776f7027a87.tar.bz2
cowboy-836342abb86b3ff15d1c8319a455d776f7027a87.zip
Add {switch_handler, Module} return value to cowboy_rest
Also {switch_handler, Module, Opts}. Allows switching to a different handler type. This is particularly useful for processing most of the request with cowboy_rest and then streaming the response body using cowboy_loop.
Diffstat (limited to 'src')
-rw-r--r--src/cowboy_rest.erl75
1 files changed, 67 insertions, 8 deletions
diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl
index 21d56a5..abec209 100644
--- a/src/cowboy_rest.erl
+++ b/src/cowboy_rest.erl
@@ -20,6 +20,9 @@
-export([upgrade/4]).
-export([upgrade/5]).
+-type switch_handler() :: {switch_handler, module()}
+ | {switch_handler, module(), any()}.
+
%% Common handler callbacks.
-callback init(Req, any())
@@ -35,162 +38,181 @@
-callback allowed_methods(Req, State)
-> {[binary()], Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([allowed_methods/2]).
-callback allow_missing_post(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([allow_missing_post/2]).
-callback charsets_provided(Req, State)
-> {[binary()], Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([charsets_provided/2]).
-callback content_types_accepted(Req, State)
-> {[{binary() | {binary(), binary(), '*' | [{binary(), binary()}]}, atom()}], Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([content_types_accepted/2]).
-callback content_types_provided(Req, State)
-> {[{binary() | {binary(), binary(), '*' | [{binary(), binary()}]}, atom()}], Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([content_types_provided/2]).
-callback delete_completed(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([delete_completed/2]).
-callback delete_resource(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([delete_resource/2]).
-callback expires(Req, State)
-> {calendar:datetime() | binary() | undefined, Req, State}
- | {stop, Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([expires/2]).
-callback forbidden(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([forbidden/2]).
-callback generate_etag(Req, State)
-> {binary() | {weak | strong, binary()}, Req, State}
- | {stop, Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([generate_etag/2]).
-callback is_authorized(Req, State)
-> {true | {false, iodata()}, Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([is_authorized/2]).
-callback is_conflict(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([is_conflict/2]).
-callback known_methods(Req, State)
-> {[binary()], Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([known_methods/2]).
-callback languages_provided(Req, State)
-> {[binary()], Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([languages_provided/2]).
-callback last_modified(Req, State)
-> {calendar:datetime(), Req, State}
- | {stop, Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([last_modified/2]).
-callback malformed_request(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([malformed_request/2]).
-callback moved_permanently(Req, State)
-> {{true, iodata()} | false, Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([moved_permanently/2]).
-callback moved_temporarily(Req, State)
-> {{true, iodata()} | false, Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([moved_temporarily/2]).
-callback multiple_choices(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([multiple_choices/2]).
-callback options(Req, State)
-> {ok, Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([options/2]).
-callback previously_existed(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([previously_existed/2]).
-callback resource_exists(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([resource_exists/2]).
-callback service_available(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([service_available/2]).
-callback uri_too_long(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([uri_too_long/2]).
-callback valid_content_headers(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([valid_content_headers/2]).
-callback valid_entity_length(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
+ | {switch_handler(), Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([valid_entity_length/2]).
-callback variances(Req, State)
-> {[binary()], Req, State}
- | {stop, Req, State}
when Req::cowboy_req:req(), State::any().
-optional_callbacks([variances/2]).
@@ -233,11 +255,17 @@
-spec upgrade(Req, Env, module(), any())
-> {ok, Req, Env} when Req::cowboy_req:req(), Env::cowboy_middleware:env().
-upgrade(Req0, Env, Handler, HandlerState) ->
+upgrade(Req0, Env, Handler, HandlerState0) ->
Method = cowboy_req:method(Req0),
- {ok, Req, Result} = service_available(Req0, #state{method=Method,
- handler=Handler, handler_state=HandlerState}),
- {ok, Req, Env#{result => Result}}.
+ case service_available(Req0, #state{method=Method,
+ handler=Handler, handler_state=HandlerState0}) of
+ {ok, Req, Result} ->
+ {ok, Req, Env#{result => Result}};
+ {Mod, Req, HandlerState} ->
+ Mod:upgrade(Req, Env, Handler, HandlerState);
+ {Mod, Req, HandlerState, Opts} ->
+ Mod:upgrade(Req, Env, Handler, HandlerState, Opts)
+ end.
-spec upgrade(Req, Env, module(), any(), any())
-> {ok, Req, Env} when Req::cowboy_req:req(), Env::cowboy_middleware:env().
@@ -260,6 +288,8 @@ known_methods(Req, State=#state{method=Method}) ->
next(Req, State, 501);
{stop, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
{List, Req2, HandlerState} ->
State2 = State#state{handler_state=HandlerState},
case lists:member(Method, List) of
@@ -285,6 +315,8 @@ allowed_methods(Req, State=#state{method=Method}) ->
[<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]);
{stop, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
{List, Req2, HandlerState} ->
State2 = State#state{handler_state=HandlerState},
case lists:member(Method, List) of
@@ -316,6 +348,8 @@ is_authorized(Req, State) ->
forbidden(Req, State);
{stop, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
{true, Req2, HandlerState} ->
forbidden(Req2, State#state{handler_state=HandlerState});
{{false, AuthHead}, Req2, HandlerState} ->
@@ -346,6 +380,8 @@ options(Req, State=#state{allowed_methods=Methods, method= <<"OPTIONS">>}) ->
= << << ", ", M/binary >> || M <- Methods >>,
Req2 = cowboy_req:set_resp_header(<<"allow">>, Allow, Req),
respond(Req2, State, 200);
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
{stop, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
{ok, Req2, HandlerState} ->
@@ -387,6 +423,8 @@ content_types_provided(Req, State) ->
end;
{stop, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
{[], Req2, HandlerState} ->
not_acceptable(Req2, State#state{handler_state=HandlerState});
{CTP, Req2, HandlerState} ->
@@ -489,6 +527,8 @@ languages_provided(Req, State) ->
charsets_provided(Req, State);
{stop, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
{[], Req2, HandlerState} ->
not_acceptable(Req2, State#state{handler_state=HandlerState});
{LP, Req2, HandlerState} ->
@@ -549,6 +589,8 @@ charsets_provided(Req, State) ->
set_content_type(Req, State);
{stop, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
{[], Req2, HandlerState} ->
not_acceptable(Req2, State#state{handler_state=HandlerState});
{CP, Req2, HandlerState} ->
@@ -832,6 +874,8 @@ moved_permanently(Req, State, OnFalse) ->
OnFalse(Req2, State#state{handler_state=HandlerState});
{stop, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
no_call ->
OnFalse(Req, State)
end.
@@ -853,6 +897,8 @@ moved_temporarily(Req, State) ->
is_post_to_missing_resource(Req2, State#state{handler_state=HandlerState}, 410);
{stop, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
no_call ->
is_post_to_missing_resource(Req, State, 410)
end.
@@ -903,6 +949,8 @@ accept_resource(Req, State) ->
respond(Req, State, 415);
{stop, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
{CTA, Req2, HandlerState} ->
CTA2 = [normalize_content_types(P) || P <- CTA],
State2 = State#state{handler_state=HandlerState},
@@ -938,6 +986,8 @@ process_content_type(Req, State=#state{method=Method, exists=Exists}, Fun) ->
try case call(Req, State, Fun) of
{stop, Req2, HandlerState2} ->
terminate(Req2, State#state{handler_state=HandlerState2});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
{true, Req2, HandlerState2} when Exists ->
State2 = State#state{handler_state=HandlerState2},
next(Req2, State2, fun has_resp_body/2);
@@ -1019,6 +1069,8 @@ set_resp_body(Req, State=#state{content_type_a={_, Callback}}) ->
try case call(Req, State, Callback) of
{stop, Req2, HandlerState2} ->
terminate(Req2, State#state{handler_state=HandlerState2});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
{Body, Req2, HandlerState2} ->
State2 = State#state{handler_state=HandlerState2},
Req3 = cowboy_req:set_resp_body(Body, Req2),
@@ -1114,6 +1166,8 @@ expect(Req, State, Callback, Expected, OnTrue, OnFalse) ->
next(Req, State, OnTrue);
{stop, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
{Expected, Req2, HandlerState} ->
next(Req2, State#state{handler_state=HandlerState}, OnTrue);
{_Unexpected, Req2, HandlerState} ->
@@ -1148,6 +1202,11 @@ next(Req, State, StatusCode) when is_integer(StatusCode) ->
respond(Req, State, StatusCode) ->
terminate(cowboy_req:reply(StatusCode, Req), State).
+switch_handler({switch_handler, Mod}, Req, HandlerState) ->
+ {Mod, Req, HandlerState};
+switch_handler({switch_handler, Mod, Opts}, Req, HandlerState) ->
+ {Mod, Req, HandlerState, Opts}.
+
-spec error_terminate(cowboy_req:req(), #state{}, atom(), any()) -> no_return().
error_terminate(Req, #state{handler=Handler, handler_state=HandlerState}, Class, Reason) ->
cowboy_handler:terminate({crash, Class, Reason}, Req, HandlerState, Handler),