Browse Source

Add optional callbacks

Mostly useful for REST, which has a ton. This is an initial
commit, it still needs to be tested, but it's time to sleep.
Loïc Hoguin 9 years ago
parent
commit
e25634cd9d
5 changed files with 192 additions and 5 deletions
  1. 12 0
      doc/src/manual/cowboy_rest.ezdoc
  2. 3 1
      src/cowboy_handler.erl
  3. 3 1
      src/cowboy_loop.erl
  4. 171 2
      src/cowboy_rest.erl
  5. 3 1
      src/cowboy_websocket.erl

+ 12 - 0
doc/src/manual/cowboy_rest.ezdoc

@@ -290,6 +290,10 @@ the same format as the etag header, including quotes.
 * Value type: true | {false, AuthHeader}
 * Value type: true | {false, AuthHeader}
 * Default value: true
 * Default value: true
 
 
+Types:
+
+* AuthHead = iodata()
+
 Return whether the user is authorized to perform the action.
 Return whether the user is authorized to perform the action.
 
 
 This function should be used to perform any necessary
 This function should be used to perform any necessary
@@ -380,6 +384,10 @@ the request body, which is processed later.
 * Value type: {true, URL} | false
 * Value type: {true, URL} | false
 * Default value: false
 * Default value: false
 
 
+Types:
+
+* URL = iodata()
+
 Return whether the resource was permanently moved.
 Return whether the resource was permanently moved.
 
 
 If it was, its new URL is also returned and sent in the
 If it was, its new URL is also returned and sent in the
@@ -391,6 +399,10 @@ location header in the response.
 * Value type: {true, URL} | false
 * Value type: {true, URL} | false
 * Default value: false
 * Default value: false
 
 
+Types:
+
+* URL = iodata()
+
 Return whether the resource was temporarily moved.
 Return whether the resource was temporarily moved.
 
 
 If it was, its new URL is also returned and sent in the
 If it was, its new URL is also returned and sent in the

+ 3 - 1
src/cowboy_handler.erl

@@ -29,7 +29,9 @@
 	| {module(), Req, any(), timeout()}
 	| {module(), Req, any(), timeout()}
 	| {module(), Req, any(), timeout(), hibernate}
 	| {module(), Req, any(), timeout(), hibernate}
 	when Req::cowboy_req:req().
 	when Req::cowboy_req:req().
-%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.
+
+-callback terminate(any(), cowboy_req:req(), any()) -> ok.
+-optional_callbacks([terminate/3]).
 
 
 -spec execute(Req, Env) -> {ok, Req, Env}
 -spec execute(Req, Env) -> {ok, Req, Env}
 	when Req::cowboy_req:req(), Env::cowboy_middleware:env().
 	when Req::cowboy_req:req(), Env::cowboy_middleware:env().

+ 3 - 1
src/cowboy_loop.erl

@@ -38,7 +38,9 @@
 	| {ok, Req, State, hibernate}
 	| {ok, Req, State, hibernate}
 	| {stop, Req, State}
 	| {stop, Req, State}
 	when Req::cowboy_req:req(), State::any().
 	when Req::cowboy_req:req(), State::any().
-%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.
+
+-callback terminate(any(), cowboy_req:req(), any()) -> ok.
+-optional_callbacks([terminate/3]).
 
 
 -record(state, {
 -record(state, {
 	env :: cowboy_middleware:env(),
 	env :: cowboy_middleware:env(),

+ 171 - 2
src/cowboy_rest.erl

@@ -19,14 +19,183 @@
 
 
 -export([upgrade/6]).
 -export([upgrade/6]).
 
 
+%% Common handler callbacks.
+
 -callback init(Req, any())
 -callback init(Req, any())
 	-> {ok | module(), Req, any()}
 	-> {ok | module(), Req, any()}
 	| {module(), Req, any(), hibernate}
 	| {module(), Req, any(), hibernate}
 	| {module(), Req, any(), timeout()}
 	| {module(), Req, any(), timeout()}
 	| {module(), Req, any(), timeout(), hibernate}
 	| {module(), Req, any(), timeout(), hibernate}
 	when Req::cowboy_req:req().
 	when Req::cowboy_req:req().
-%% @todo optional REST callbacks
-%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.
+
+-callback terminate(any(), cowboy_req:req(), any()) -> ok.
+-optional_callbacks([terminate/3]).
+
+%% REST handler callbacks.
+
+-callback allowed_methods(Req, State)
+	-> {[binary()], Req, State}
+	| {stop, 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}
+	when Req::cowboy_req:req(), State::any().
+-optional_callbacks([allow_missing_post/2]).
+
+-callback charsets_provided(Req, State)
+	-> {[binary()], Req, State}
+	| {stop, 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}
+	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}
+	when Req::cowboy_req:req(), State::any().
+-optional_callbacks([content_types_provided/2]).
+
+-callback delete_completed(Req, State)
+	-> {boolean(), Req, State}
+	| {stop, 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}
+	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}
+	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}
+	when Req::cowboy_req:req(), State::any().
+-optional_callbacks([is_authorized/2]).
+
+-callback is_conflict(Req, State)
+	-> {boolean(), Req, State}
+	| {stop, 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}
+	when Req::cowboy_req:req(), State::any().
+-optional_callbacks([known_methods/2]).
+
+-callback languages_provided(Req, State)
+	-> {[binary()], Req, State}
+	| {stop, 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}
+	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}
+	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}
+	when Req::cowboy_req:req(), State::any().
+-optional_callbacks([moved_temporarily/2]).
+
+-callback multiple_choices(Req, State)
+	-> {boolean(), Req, State}
+	| {stop, Req, State}
+	when Req::cowboy_req:req(), State::any().
+-optional_callbacks([multiple_choices/2]).
+
+-callback options(Req, State)
+	-> {ok, Req, State}
+	| {stop, Req, State}
+	when Req::cowboy_req:req(), State::any().
+-optional_callbacks([options/2]).
+
+-callback previously_existed(Req, State)
+	-> {boolean(), Req, State}
+	| {stop, 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}
+	when Req::cowboy_req:req(), State::any().
+-optional_callbacks([resource_exists/2]).
+
+-callback service_available(Req, State)
+	-> {boolean(), Req, State}
+	| {stop, 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}
+	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}
+	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}
+	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]).
+
+%% End of REST callbacks. Whew!
 
 
 -record(state, {
 -record(state, {
 	env :: cowboy_middleware:env(),
 	env :: cowboy_middleware:env(),

+ 3 - 1
src/cowboy_websocket.erl

@@ -45,7 +45,9 @@
 	| {reply, cow_ws:frame() | [cow_ws:frame()], Req, State, hibernate}
 	| {reply, cow_ws:frame() | [cow_ws:frame()], Req, State, hibernate}
 	| {stop, Req, State}
 	| {stop, Req, State}
 	when Req::cowboy_req:req(), State::any().
 	when Req::cowboy_req:req(), State::any().
-%% @todo optional -callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.
+
+-callback terminate(any(), cowboy_req:req(), any()) -> ok.
+-optional_callbacks([terminate/3]).
 
 
 -record(state, {
 -record(state, {
 	env :: cowboy_middleware:env(),
 	env :: cowboy_middleware:env(),