aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--test/handlers/accept_callback_h.erl23
-rw-r--r--test/handlers/content_types_accepted_h.erl6
-rw-r--r--test/handlers/delete_resource_h.erl17
-rw-r--r--test/handlers/generate_etag_h.erl39
-rw-r--r--test/old_http_SUITE.erl183
-rw-r--r--test/old_http_SUITE_data/http_chunked.erl15
-rw-r--r--test/old_http_SUITE_data/http_echo_body.erl21
-rw-r--r--test/old_http_SUITE_data/http_errors.erl19
-rw-r--r--test/old_http_SUITE_data/http_handler.erl10
-rw-r--r--test/old_http_SUITE_data/http_loop_stream_recv.erl34
-rw-r--r--test/old_http_SUITE_data/http_set_resp.erl25
-rw-r--r--test/old_http_SUITE_data/rest_empty_resource.erl6
-rw-r--r--test/old_http_SUITE_data/rest_expires.erl22
-rw-r--r--test/old_http_SUITE_data/rest_expires_binary.erl18
-rw-r--r--test/old_http_SUITE_data/rest_forbidden_resource.erl32
-rw-r--r--test/old_http_SUITE_data/rest_missing_callbacks.erl24
-rw-r--r--test/old_http_SUITE_data/rest_nodelete_resource.erl18
-rw-r--r--test/old_http_SUITE_data/rest_param_all.erl36
-rw-r--r--test/old_http_SUITE_data/rest_patch_resource.erl38
-rw-r--r--test/old_http_SUITE_data/rest_post_charset_resource.erl19
-rw-r--r--test/old_http_SUITE_data/rest_postonly_resource.erl18
-rw-r--r--test/old_http_SUITE_data/rest_resource_etags.erl37
-rw-r--r--test/old_http_SUITE_data/rest_simple_resource.erl14
-rw-r--r--test/rest_handler_SUITE.erl158
24 files changed, 241 insertions, 591 deletions
diff --git a/test/handlers/accept_callback_h.erl b/test/handlers/accept_callback_h.erl
new file mode 100644
index 0000000..1912e9f
--- /dev/null
+++ b/test/handlers/accept_callback_h.erl
@@ -0,0 +1,23 @@
+%% This module returns something different in
+%% AcceptCallback depending on the query string.
+
+-module(accept_callback_h).
+
+-export([init/2]).
+-export([allowed_methods/2]).
+-export([content_types_accepted/2]).
+-export([put_text_plain/2]).
+
+init(Req, Opts) ->
+ {cowboy_rest, Req, Opts}.
+
+allowed_methods(Req, State) ->
+ {[<<"PUT">>, <<"POST">>, <<"PATCH">>], Req, State}.
+
+content_types_accepted(Req, State) ->
+ {[{{<<"text">>, <<"plain">>, []}, put_text_plain}], Req, State}.
+
+put_text_plain(Req=#{qs := <<"false">>}, State) ->
+ {false, Req, State};
+put_text_plain(Req=#{qs := <<"true">>}, State) ->
+ {true, Req, State}.
diff --git a/test/handlers/content_types_accepted_h.erl b/test/handlers/content_types_accepted_h.erl
index b871dc8..d34135e 100644
--- a/test/handlers/content_types_accepted_h.erl
+++ b/test/handlers/content_types_accepted_h.erl
@@ -1,5 +1,5 @@
-%% This module accepts a multipart media type with parameters
-%% that do not include boundary.
+%% This module returns something different in
+%% content_types_accepted depending on the query string.
-module(content_types_accepted_h).
@@ -19,6 +19,8 @@ content_types_accepted(Req=#{qs := <<"multipart">>}, State) ->
{[
{{<<"multipart">>, <<"mixed">>, [{<<"v">>, <<"1">>}]}, put_multipart_mixed}
], Req, State};
+content_types_accepted(Req=#{qs := <<"param">>}, State) ->
+ {[{{<<"text">>, <<"plain">>, [{<<"charset">>, <<"utf-8">>}]}, put_text_plain}], Req, State};
content_types_accepted(Req=#{qs := <<"wildcard-param">>}, State) ->
{[{{<<"text">>, <<"plain">>, '*'}, put_text_plain}], Req, State}.
diff --git a/test/handlers/delete_resource_h.erl b/test/handlers/delete_resource_h.erl
new file mode 100644
index 0000000..f7202a9
--- /dev/null
+++ b/test/handlers/delete_resource_h.erl
@@ -0,0 +1,17 @@
+%% This module accepts a multipart media type with parameters
+%% that do not include boundary.
+
+-module(delete_resource_h).
+
+-export([init/2]).
+-export([allowed_methods/2]).
+-export([delete_resource/2]).
+
+init(Req, Opts) ->
+ {cowboy_rest, Req, Opts}.
+
+allowed_methods(Req, State) ->
+ {[<<"DELETE">>], Req, State}.
+
+delete_resource(#{qs := <<"missing">>}, _) ->
+ no_call.
diff --git a/test/handlers/generate_etag_h.erl b/test/handlers/generate_etag_h.erl
new file mode 100644
index 0000000..97ee82b
--- /dev/null
+++ b/test/handlers/generate_etag_h.erl
@@ -0,0 +1,39 @@
+%% This module sends a different etag value
+%% depending on the query string.
+
+-module(generate_etag_h).
+
+-export([init/2]).
+-export([content_types_provided/2]).
+-export([get_text_plain/2]).
+-export([generate_etag/2]).
+
+init(Req, Opts) ->
+ {cowboy_rest, Req, Opts}.
+
+content_types_provided(Req, State) ->
+ {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
+
+get_text_plain(Req, State) ->
+ {<<"This is REST!">>, Req, State}.
+
+%% Correct return values from generate_etag/2.
+generate_etag(Req=#{qs := <<"tuple-weak">>}, State) ->
+ {{weak, <<"etag-header-value">>}, Req, State};
+generate_etag(Req=#{qs := <<"tuple-strong">>}, State) ->
+ {{strong, <<"etag-header-value">>}, Req, State};
+%% Backwards compatible return values from generate_etag/2.
+generate_etag(Req=#{qs := <<"binary-weak-quoted">>}, State) ->
+ {<<"W/\"etag-header-value\"">>, Req, State};
+generate_etag(Req=#{qs := <<"binary-strong-quoted">>}, State) ->
+ {<<"\"etag-header-value\"">>, Req, State};
+%% Invalid return values from generate_etag/2.
+generate_etag(Req=#{qs := <<"binary-weak-unquoted">>}, State) ->
+ ct_helper_error_h:ignore(cow_http_hd, parse_etag, 1),
+ {<<"W/etag-header-value">>, Req, State};
+generate_etag(Req=#{qs := <<"binary-strong-unquoted">>}, State) ->
+ ct_helper_error_h:ignore(cow_http_hd, parse_etag, 1),
+ {<<"etag-header-value">>, Req, State};
+%% Simulate the callback being missing in other cases.
+generate_etag(#{qs := <<"missing">>}, _) ->
+ no_call.
diff --git a/test/old_http_SUITE.erl b/test/old_http_SUITE.erl
deleted file mode 100644
index 81bb36c..0000000
--- a/test/old_http_SUITE.erl
+++ /dev/null
@@ -1,183 +0,0 @@
-%% Copyright (c) 2011-2017, Loïc Hoguin <[email protected]>
-%% Copyright (c) 2011, Anthony Ramine <[email protected]>
-%%
-%% Permission to use, copy, modify, and/or distribute this software for any
-%% purpose with or without fee is hereby granted, provided that the above
-%% copyright notice and this permission notice appear in all copies.
-%%
-%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
--module(old_http_SUITE).
--compile(export_all).
--compile(nowarn_export_all).
-
--import(ct_helper, [config/2]).
--import(cowboy_test, [gun_open/1]).
--import(cowboy_test, [gun_open/2]).
--import(cowboy_test, [gun_down/1]).
--import(cowboy_test, [raw_open/1]).
--import(cowboy_test, [raw_send/2]).
--import(cowboy_test, [raw_recv_head/1]).
--import(cowboy_test, [raw_expect_recv/2]).
-
-%% ct.
-
-all() ->
- [
- {group, http},
- {group, https},
- {group, http_compress},
- {group, https_compress}
- ].
-
-groups() ->
- Tests = ct_helper:all(?MODULE),
- [
- {http, [], Tests}, %% @todo parallel
- {https, [parallel], Tests},
- {http_compress, [parallel], Tests},
- {https_compress, [parallel], Tests}
- ].
-
-init_per_group(Name = http, Config) ->
- cowboy_test:init_http(Name, #{env => #{dispatch => init_dispatch(Config)}}, Config);
-init_per_group(Name = https, Config) ->
- cowboy_test:init_https(Name, #{env => #{dispatch => init_dispatch(Config)}}, Config);
-init_per_group(Name = http_compress, Config) ->
- cowboy_test:init_http(Name, #{
- env => #{dispatch => init_dispatch(Config)},
- stream_handlers => [cowboy_compress_h, cowboy_stream_h]
- }, Config);
-init_per_group(Name = https_compress, Config) ->
- cowboy_test:init_https(Name, #{
- env => #{dispatch => init_dispatch(Config)},
- stream_handlers => [cowboy_compress_h, cowboy_stream_h]
- }, Config).
-
-end_per_group(Name, _) ->
- ok = cowboy:stop_listener(Name).
-
-%% Dispatch configuration.
-
-init_dispatch(_) ->
- cowboy_router:compile([
- {"localhost", [
- {"/chunked_response", http_chunked, []},
- {"/headers/dupe", http_handler,
- [{headers, #{<<"connection">> => <<"close">>}}]},
- {"/set_resp/header", http_set_resp,
- [{headers, #{<<"vary">> => <<"Accept">>}}]},
- {"/set_resp/overwrite", http_set_resp,
- [{headers, #{<<"server">> => <<"DesireDrive/1.0">>}}]},
- {"/set_resp/body", http_set_resp,
- [{body, <<"A flameless dance does not equal a cycle">>}]},
- {"/handler_errors", http_errors, []},
- {"/echo/body", http_echo_body, []},
- {"/param_all", rest_param_all, []},
- {"/bad_accept", rest_simple_resource, []},
- {"/bad_content_type", rest_patch_resource, []},
- {"/simple", rest_simple_resource, []},
- {"/forbidden_post", rest_forbidden_resource, [true]},
- {"/simple_post", rest_forbidden_resource, [false]},
- {"/missing_get_callbacks", rest_missing_callbacks, []},
- {"/missing_put_callbacks", rest_missing_callbacks, []},
- {"/nodelete", rest_nodelete_resource, []},
- {"/post_charset", rest_post_charset_resource, []},
- {"/postonly", rest_postonly_resource, []},
- {"/patch", rest_patch_resource, []},
- {"/resetags", rest_resource_etags, []},
- {"/rest_expires", rest_expires, []},
- {"/rest_expires_binary", rest_expires_binary, []},
- {"/rest_empty_resource", rest_empty_resource, []},
- {"/loop_stream_recv", http_loop_stream_recv, []},
- {"/", http_handler, []}
- ]}
- ]).
-
-%% Tests.
-
-rest_bad_content_type(Config) ->
- ConnPid = gun_open(Config),
- Ref = gun:patch(ConnPid, "/bad_content_type",
- [{<<"content-type">>, <<"text/plain, text/html">>}], <<"Whatever">>),
- {response, fin, 415, _} = gun:await(ConnPid, Ref),
- ok.
-
-rest_nodelete(Config) ->
- ConnPid = gun_open(Config),
- Ref = gun:delete(ConnPid, "/nodelete"),
- {response, fin, 500, _} = gun:await(ConnPid, Ref),
- ok.
-
-rest_patch(Config) ->
- Tests = [
- {204, [{<<"content-type">>, <<"text/plain">>}], <<"whatever">>},
- {400, [{<<"content-type">>, <<"text/plain">>}], <<"false">>},
- {400, [{<<"content-type">>, <<"text/plain">>}], <<"stop">>},
- {415, [{<<"content-type">>, <<"application/json">>}], <<"bad_content_type">>}
- ],
- ConnPid = gun_open(Config),
- _ = [begin
- Ref = gun:patch(ConnPid, "/patch", Headers, Body),
- {response, fin, Status, _} = gun:await(ConnPid, Ref)
- end || {Status, Headers, Body} <- Tests],
- ok.
-
-rest_post_charset(Config) ->
- ConnPid = gun_open(Config),
- Ref = gun:post(ConnPid, "/post_charset",
- [{<<"content-type">>, <<"text/plain;charset=UTF-8">>}], "12345"),
- {response, fin, 204, _} = gun:await(ConnPid, Ref),
- ok.
-
-rest_postonly(Config) ->
- ConnPid = gun_open(Config),
- Ref = gun:post(ConnPid, "/postonly",
- [{<<"content-type">>, <<"text/plain">>}], "12345"),
- {response, fin, 204, _} = gun:await(ConnPid, Ref),
- ok.
-
-rest_resource_get_etag(Config, Type) ->
- rest_resource_get_etag(Config, Type, []).
-
-rest_resource_get_etag(Config, Type, Headers) ->
- ConnPid = gun_open(Config),
- Ref = gun:get(ConnPid, "/resetags?type=" ++ Type, Headers),
- {response, _, Status, RespHeaders} = gun:await(ConnPid, Ref),
- case lists:keyfind(<<"etag">>, 1, RespHeaders) of
- false -> {Status, false};
- {<<"etag">>, ETag} -> {Status, ETag}
- end.
-
-rest_resource_etags(Config) ->
- Tests = [
- {200, <<"W/\"etag-header-value\"">>, "tuple-weak"},
- {200, <<"\"etag-header-value\"">>, "tuple-strong"},
- {200, <<"W/\"etag-header-value\"">>, "binary-weak-quoted"},
- {200, <<"\"etag-header-value\"">>, "binary-strong-quoted"},
- {500, false, "binary-strong-unquoted"},
- {500, false, "binary-weak-unquoted"}
- ],
- _ = [{Status, ETag, Type} = begin
- {Ret, RespETag} = rest_resource_get_etag(Config, Type),
- {Ret, RespETag, Type}
- end || {Status, ETag, Type} <- Tests].
-
-rest_resource_etags_if_none_match(Config) ->
- Tests = [
- {304, <<"W/\"etag-header-value\"">>, "tuple-weak"},
- {304, <<"\"etag-header-value\"">>, "tuple-strong"},
- {304, <<"W/\"etag-header-value\"">>, "binary-weak-quoted"},
- {304, <<"\"etag-header-value\"">>, "binary-strong-quoted"}
- ],
- _ = [{Status, Type} = begin
- {Ret, _} = rest_resource_get_etag(Config, Type,
- [{<<"if-none-match">>, ETag}]),
- {Ret, Type}
- end || {Status, ETag, Type} <- Tests].
diff --git a/test/old_http_SUITE_data/http_chunked.erl b/test/old_http_SUITE_data/http_chunked.erl
deleted file mode 100644
index 645eefe..0000000
--- a/test/old_http_SUITE_data/http_chunked.erl
+++ /dev/null
@@ -1,15 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_chunked).
-
--export([init/2]).
-
-init(Req, Opts) ->
- Req2 = cowboy_req:stream_reply(200, Req),
- %% Try an empty chunk to make sure the stream doesn't get closed.
- cowboy_req:stream_body([<<>>], nofin, Req2),
- timer:sleep(100),
- cowboy_req:stream_body("chunked_handler\r\n", nofin, Req2),
- timer:sleep(100),
- cowboy_req:stream_body("works fine!", fin, Req2),
- {ok, Req2, Opts}.
diff --git a/test/old_http_SUITE_data/http_echo_body.erl b/test/old_http_SUITE_data/http_echo_body.erl
deleted file mode 100644
index d803108..0000000
--- a/test/old_http_SUITE_data/http_echo_body.erl
+++ /dev/null
@@ -1,21 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_echo_body).
-
--export([init/2]).
-
-init(Req, Opts) ->
- true = cowboy_req:has_body(Req),
- Req3 = case cowboy_req:read_body(Req, #{length => 1000000}) of
- {ok, Body, Req2} -> handle_body(Req2, Body);
- {more, _, Req2} -> handle_badlength(Req2)
- end,
- {ok, Req3, Opts}.
-
-handle_badlength(Req) ->
- cowboy_req:reply(413, #{}, <<"Request entity too large">>, Req).
-
-handle_body(Req, Body) ->
- Size = cowboy_req:body_length(Req),
- Size = byte_size(Body),
- cowboy_req:reply(200, #{}, Body, Req).
diff --git a/test/old_http_SUITE_data/http_errors.erl b/test/old_http_SUITE_data/http_errors.erl
deleted file mode 100644
index 9e376a2..0000000
--- a/test/old_http_SUITE_data/http_errors.erl
+++ /dev/null
@@ -1,19 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_errors).
-
--export([init/2]).
-
--spec init(_, _) -> no_return().
-init(Req, _Opts) ->
- #{'case' := Case} = cowboy_req:match_qs(['case'], Req),
- case_init(Case, Req).
-
--spec case_init(_, _) -> no_return().
-case_init(<<"init_before_reply">> = Case, _Req) ->
- ct_helper_error_h:ignore(?MODULE, case_init, 2),
- error(Case);
-case_init(<<"init_after_reply">> = Case, Req) ->
- ct_helper_error_h:ignore(?MODULE, case_init, 2),
- _ = cowboy_req:reply(200, #{}, "http_handler_crashes", Req),
- error(Case).
diff --git a/test/old_http_SUITE_data/http_handler.erl b/test/old_http_SUITE_data/http_handler.erl
deleted file mode 100644
index eb31aa8..0000000
--- a/test/old_http_SUITE_data/http_handler.erl
+++ /dev/null
@@ -1,10 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_handler).
-
--export([init/2]).
-
-init(Req, Opts) ->
- Headers = proplists:get_value(headers, Opts, #{}),
- Body = proplists:get_value(body, Opts, "http_handler"),
- {ok, cowboy_req:reply(200, Headers, Body, Req), Opts}.
diff --git a/test/old_http_SUITE_data/http_loop_stream_recv.erl b/test/old_http_SUITE_data/http_loop_stream_recv.erl
deleted file mode 100644
index 8c6f6b0..0000000
--- a/test/old_http_SUITE_data/http_loop_stream_recv.erl
+++ /dev/null
@@ -1,34 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_loop_stream_recv).
-
--export([init/2]).
--export([info/3]).
--export([terminate/3]).
-
-init(Req, _) ->
- receive after 100 -> ok end,
- self() ! stream,
- {cowboy_loop, Req, undefined}.
-
-info(stream, Req, undefined) ->
- stream(Req, 1, <<>>).
-
-stream(Req, ID, Acc) ->
- case cowboy_req:read_body(Req) of
- {ok, <<>>, Req2} ->
- {stop, cowboy_req:reply(200, Req2), undefined};
- {_, Data, Req2} ->
- parse_id(Req2, ID, << Acc/binary, Data/binary >>)
- end.
-
-parse_id(Req, ID, Data) ->
- case Data of
- << ID:32, Rest/bits >> ->
- parse_id(Req, ID + 1, Rest);
- _ ->
- stream(Req, ID, Data)
- end.
-
-terminate(stop, _, _) ->
- ok.
diff --git a/test/old_http_SUITE_data/http_set_resp.erl b/test/old_http_SUITE_data/http_set_resp.erl
deleted file mode 100644
index e575aab..0000000
--- a/test/old_http_SUITE_data/http_set_resp.erl
+++ /dev/null
@@ -1,25 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_set_resp).
-
--export([init/2]).
-
-init(Req, Opts) ->
- Headers = proplists:get_value(headers, Opts, #{}),
- Body = proplists:get_value(body, Opts, <<"http_handler_set_resp">>),
- Req2 = lists:foldl(fun({Name, Value}, R) ->
- cowboy_req:set_resp_header(Name, Value, R)
- end, Req, maps:to_list(Headers)),
- Req3 = cowboy_req:set_resp_body(Body, Req2),
- Req4 = cowboy_req:set_resp_header(<<"x-cowboy-test">>, <<"ok">>, Req3),
- Req5 = cowboy_req:set_resp_cookie(<<"cake">>, <<"lie">>, Req4),
- case cowboy_req:has_resp_header(<<"x-cowboy-test">>, Req5) of
- false -> {ok, Req5, Opts};
- true ->
- case cowboy_req:has_resp_body(Req5) of
- false ->
- {ok, Req5, Opts};
- true ->
- {ok, cowboy_req:reply(200, Req5), Opts}
- end
- end.
diff --git a/test/old_http_SUITE_data/rest_empty_resource.erl b/test/old_http_SUITE_data/rest_empty_resource.erl
deleted file mode 100644
index 8a944cd..0000000
--- a/test/old_http_SUITE_data/rest_empty_resource.erl
+++ /dev/null
@@ -1,6 +0,0 @@
--module(rest_empty_resource).
-
--export([init/2]).
-
-init(Req, Opts) ->
- {cowboy_rest, Req, Opts}.
diff --git a/test/old_http_SUITE_data/rest_expires.erl b/test/old_http_SUITE_data/rest_expires.erl
deleted file mode 100644
index 8665b06..0000000
--- a/test/old_http_SUITE_data/rest_expires.erl
+++ /dev/null
@@ -1,22 +0,0 @@
--module(rest_expires).
-
--export([init/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
--export([expires/2]).
--export([last_modified/2]).
-
-init(Req, Opts) ->
- {cowboy_rest, Req, Opts}.
-
-content_types_provided(Req, State) ->
- {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
- {<<"This is REST!">>, Req, State}.
-
-expires(Req, State) ->
- {{{2012, 9, 21}, {22, 36, 14}}, Req, State}.
-
-last_modified(Req, State) ->
- {{{2012, 9, 21}, {22, 36, 14}}, Req, State}.
diff --git a/test/old_http_SUITE_data/rest_expires_binary.erl b/test/old_http_SUITE_data/rest_expires_binary.erl
deleted file mode 100644
index 4d6bd3c..0000000
--- a/test/old_http_SUITE_data/rest_expires_binary.erl
+++ /dev/null
@@ -1,18 +0,0 @@
--module(rest_expires_binary).
-
--export([init/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
--export([expires/2]).
-
-init(Req, Opts) ->
- {cowboy_rest, Req, Opts}.
-
-content_types_provided(Req, State) ->
- {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
- {<<"This is REST!">>, Req, State}.
-
-expires(Req, State) ->
- {<<"0">>, Req, State}.
diff --git a/test/old_http_SUITE_data/rest_forbidden_resource.erl b/test/old_http_SUITE_data/rest_forbidden_resource.erl
deleted file mode 100644
index 0a65228..0000000
--- a/test/old_http_SUITE_data/rest_forbidden_resource.erl
+++ /dev/null
@@ -1,32 +0,0 @@
--module(rest_forbidden_resource).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([forbidden/2]).
--export([content_types_provided/2]).
--export([content_types_accepted/2]).
--export([to_text/2]).
--export([from_text/2]).
-
-init(Req, [Forbidden]) ->
- {cowboy_rest, Req, Forbidden}.
-
-allowed_methods(Req, State) ->
- {[<<"GET">>, <<"HEAD">>, <<"POST">>], Req, State}.
-
-forbidden(Req, State=true) ->
- {true, Req, State};
-forbidden(Req, State=false) ->
- {false, Req, State}.
-
-content_types_provided(Req, State) ->
- {[{{<<"text">>, <<"plain">>, []}, to_text}], Req, State}.
-
-content_types_accepted(Req, State) ->
- {[{{<<"text">>, <<"plain">>, []}, from_text}], Req, State}.
-
-to_text(Req, State) ->
- {<<"This is REST!">>, Req, State}.
-
-from_text(Req, State) ->
- {{true, cowboy_req:path(Req)}, Req, State}.
diff --git a/test/old_http_SUITE_data/rest_missing_callbacks.erl b/test/old_http_SUITE_data/rest_missing_callbacks.erl
deleted file mode 100644
index bf77c22..0000000
--- a/test/old_http_SUITE_data/rest_missing_callbacks.erl
+++ /dev/null
@@ -1,24 +0,0 @@
--module(rest_missing_callbacks).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_accepted/2]).
--export([content_types_provided/2]).
-
-init(Req, Opts) ->
- {cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
- {[<<"GET">>, <<"PUT">>], Req, State}.
-
-content_types_accepted(Req, State) ->
- ct_helper_error_h:ignore(cowboy_rest, process_content_type, 3),
- {[
- {<<"application/json">>, put_application_json}
- ], Req, State}.
-
-content_types_provided(Req, State) ->
- ct_helper_error_h:ignore(cowboy_rest, set_resp_body, 2),
- {[
- {<<"text/plain">>, get_text_plain}
- ], Req, State}.
diff --git a/test/old_http_SUITE_data/rest_nodelete_resource.erl b/test/old_http_SUITE_data/rest_nodelete_resource.erl
deleted file mode 100644
index b9f40bd..0000000
--- a/test/old_http_SUITE_data/rest_nodelete_resource.erl
+++ /dev/null
@@ -1,18 +0,0 @@
--module(rest_nodelete_resource).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
-
-init(Req, Opts) ->
- {cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
- {[<<"GET">>, <<"HEAD">>, <<"DELETE">>], Req, State}.
-
-content_types_provided(Req, State) ->
- {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
- {<<"This is REST!">>, Req, State}.
diff --git a/test/old_http_SUITE_data/rest_param_all.erl b/test/old_http_SUITE_data/rest_param_all.erl
deleted file mode 100644
index 784214b..0000000
--- a/test/old_http_SUITE_data/rest_param_all.erl
+++ /dev/null
@@ -1,36 +0,0 @@
--module(rest_param_all).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
--export([content_types_accepted/2]).
--export([put_text_plain/2]).
-
-init(Req, Opts) ->
- {cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
- {[<<"GET">>, <<"PUT">>], Req, State}.
-
-content_types_provided(Req, State) ->
- {[{{<<"text">>, <<"plain">>, '*'}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
- {_, _, Param} = maps:get(media_type, Req, {<<"text">>, <<"plain">>, []}),
- Body = if
- Param == '*' ->
- <<"'*'">>;
- Param == [] ->
- <<"[]">>;
- Param /= [] ->
- iolist_to_binary([[Key, $=, Value] || {Key, Value} <- Param])
- end,
- {Body, Req, State}.
-
-content_types_accepted(Req, State) ->
- {[{{<<"text">>, <<"plain">>, '*'}, put_text_plain}], Req, State}.
-
-put_text_plain(Req0, State) ->
- {ok, _, Req} = cowboy_req:read_body(Req0),
- {true, Req, State}.
diff --git a/test/old_http_SUITE_data/rest_patch_resource.erl b/test/old_http_SUITE_data/rest_patch_resource.erl
deleted file mode 100644
index 341920d..0000000
--- a/test/old_http_SUITE_data/rest_patch_resource.erl
+++ /dev/null
@@ -1,38 +0,0 @@
--module(rest_patch_resource).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
--export([content_types_accepted/2]).
--export([patch_text_plain/2]).
-
-init(Req, Opts) ->
- {cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
- {[<<"HEAD">>, <<"GET">>, <<"PATCH">>], Req, State}.
-
-content_types_provided(Req, State) ->
- {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
- {<<"This is REST!">>, Req, State}.
-
-content_types_accepted(Req, State) ->
- case cowboy_req:method(Req) of
- <<"PATCH">> ->
- {[{{<<"text">>, <<"plain">>, []}, patch_text_plain}], Req, State};
- _ ->
- {[], Req, State}
- end.
-
-patch_text_plain(Req, State) ->
- case cowboy_req:read_body(Req) of
- {ok, <<"stop">>, Req0} ->
- {stop, cowboy_req:reply(400, Req0), State};
- {ok, <<"false">>, Req0} ->
- {false, Req0, State};
- {ok, _Body, Req0} ->
- {true, Req0, State}
- end.
diff --git a/test/old_http_SUITE_data/rest_post_charset_resource.erl b/test/old_http_SUITE_data/rest_post_charset_resource.erl
deleted file mode 100644
index 0be6aa3..0000000
--- a/test/old_http_SUITE_data/rest_post_charset_resource.erl
+++ /dev/null
@@ -1,19 +0,0 @@
--module(rest_post_charset_resource).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_accepted/2]).
--export([from_text/2]).
-
-init(Req, Opts) ->
- {cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
- {[<<"POST">>], Req, State}.
-
-content_types_accepted(Req, State) ->
- {[{{<<"text">>, <<"plain">>, [{<<"charset">>, <<"utf-8">>}]},
- from_text}], Req, State}.
-
-from_text(Req, State) ->
- {true, Req, State}.
diff --git a/test/old_http_SUITE_data/rest_postonly_resource.erl b/test/old_http_SUITE_data/rest_postonly_resource.erl
deleted file mode 100644
index 942e55d..0000000
--- a/test/old_http_SUITE_data/rest_postonly_resource.erl
+++ /dev/null
@@ -1,18 +0,0 @@
--module(rest_postonly_resource).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_accepted/2]).
--export([from_text/2]).
-
-init(Req, Opts) ->
- {cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
- {[<<"POST">>], Req, State}.
-
-content_types_accepted(Req, State) ->
- {[{{<<"text">>, <<"plain">>, '*'}, from_text}], Req, State}.
-
-from_text(Req, State) ->
- {true, Req, State}.
diff --git a/test/old_http_SUITE_data/rest_resource_etags.erl b/test/old_http_SUITE_data/rest_resource_etags.erl
deleted file mode 100644
index 25b3080..0000000
--- a/test/old_http_SUITE_data/rest_resource_etags.erl
+++ /dev/null
@@ -1,37 +0,0 @@
--module(rest_resource_etags).
-
--export([init/2]).
--export([generate_etag/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
-
-init(Req, Opts) ->
- {cowboy_rest, Req, Opts}.
-
-generate_etag(Req, State) ->
- #{type := Type} = cowboy_req:match_qs([type], Req),
- case Type of
- %% Correct return values from generate_etag/2.
- <<"tuple-weak">> ->
- {{weak, <<"etag-header-value">>}, Req, State};
- <<"tuple-strong">> ->
- {{strong, <<"etag-header-value">>}, Req, State};
- %% Backwards compatible return values from generate_etag/2.
- <<"binary-weak-quoted">> ->
- {<<"W/\"etag-header-value\"">>, Req, State};
- <<"binary-strong-quoted">> ->
- {<<"\"etag-header-value\"">>, Req, State};
- %% Invalid return values from generate_etag/2.
- <<"binary-strong-unquoted">> ->
- ct_helper_error_h:ignore(cow_http_hd, parse_etag, 1),
- {<<"etag-header-value">>, Req, State};
- <<"binary-weak-unquoted">> ->
- ct_helper_error_h:ignore(cow_http_hd, parse_etag, 1),
- {<<"W/etag-header-value">>, Req, State}
- end.
-
-content_types_provided(Req, State) ->
- {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
- {<<"This is REST!">>, Req, State}.
diff --git a/test/old_http_SUITE_data/rest_simple_resource.erl b/test/old_http_SUITE_data/rest_simple_resource.erl
deleted file mode 100644
index 68e1b95..0000000
--- a/test/old_http_SUITE_data/rest_simple_resource.erl
+++ /dev/null
@@ -1,14 +0,0 @@
--module(rest_simple_resource).
-
--export([init/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
-
-init(Req, Opts) ->
- {cowboy_rest, Req, Opts}.
-
-content_types_provided(Req, State) ->
- {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
- {<<"This is REST!">>, Req, State}.
diff --git a/test/rest_handler_SUITE.erl b/test/rest_handler_SUITE.erl
index b21dda6..cb734d9 100644
--- a/test/rest_handler_SUITE.erl
+++ b/test/rest_handler_SUITE.erl
@@ -39,6 +39,7 @@ end_per_group(Name, _) ->
init_dispatch(_) ->
cowboy_router:compile([{'_', [
{"/", rest_hello_h, []},
+ {"/accept_callback", accept_callback_h, []},
{"/accept_callback_missing", accept_callback_missing_h, []},
{"/charsets_provided", charsets_provided_h, []},
{"/charsets_provided_empty", charsets_provided_empty_h, []},
@@ -50,7 +51,9 @@ init_dispatch(_) ->
charset_in_content_types_provided_implicit_no_callback_h, []},
{"/content_types_accepted", content_types_accepted_h, []},
{"/content_types_provided", content_types_provided_h, []},
+ {"/delete_resource", delete_resource_h, []},
{"/expires", expires_h, []},
+ {"/generate_etag", generate_etag_h, []},
{"/if_range", if_range_h, []},
{"/last_modified", last_modified_h, []},
{"/provide_callback_missing", provide_callback_missing_h, []},
@@ -84,6 +87,44 @@ accept_callback_missing(Config) ->
{response, fin, 500, _} = gun:await(ConnPid, Ref),
ok.
+accept_callback_patch_false(Config) ->
+ do_accept_callback_false(Config, patch).
+
+accept_callback_patch_true(Config) ->
+ do_accept_callback_true(Config, patch).
+
+accept_callback_post_false(Config) ->
+ do_accept_callback_false(Config, post).
+
+accept_callback_post_true(Config) ->
+ do_accept_callback_true(Config, post).
+
+accept_callback_put_false(Config) ->
+ do_accept_callback_false(Config, put).
+
+accept_callback_put_true(Config) ->
+ do_accept_callback_true(Config, put).
+
+do_accept_callback_false(Config, Fun) ->
+ doc("When AcceptCallback returns false a 400 response must be returned."),
+ ConnPid = gun_open(Config),
+ Ref = gun:Fun(ConnPid, "/accept_callback?false", [
+ {<<"accept-encoding">>, <<"gzip">>},
+ {<<"content-type">>, <<"text/plain">>}
+ ], <<"Request body.">>),
+ {response, _, 400, _} = gun:await(ConnPid, Ref),
+ ok.
+
+do_accept_callback_true(Config, Fun) ->
+ doc("When AcceptCallback returns true a 204 response must be returned."),
+ ConnPid = gun_open(Config),
+ Ref = gun:Fun(ConnPid, "/accept_callback?true", [
+ {<<"accept-encoding">>, <<"gzip">>},
+ {<<"content-type">>, <<"text/plain">>}
+ ], <<"Request body.">>),
+ {response, _, 204, _} = gun:await(ConnPid, Ref),
+ ok.
+
charset_in_content_types_provided(Config) ->
doc("When a charset is matched explictly in content_types_provided, "
"that charset is used and the charsets_provided callback is ignored."),
@@ -282,6 +323,17 @@ charsets_provided_empty_noheader(Config) ->
{response, _, 406, _} = gun:await(ConnPid, Ref),
ok.
+content_type_invalid(Config) ->
+ doc("An invalid content-type in a POST/PATCH/PUT request "
+ "must be rejected with a 415 unsupported media type response. (RFC7231 6.5.13)"),
+ ConnPid = gun_open(Config),
+ Ref = gun:put(ConnPid, "/content_types_accepted?wildcard-param", [
+ {<<"accept-encoding">>, <<"gzip">>},
+ {<<"content-type">>, <<"text/plain, text/html">>}
+ ]),
+ {response, fin, 415, _} = gun:await(ConnPid, Ref),
+ ok.
+
content_types_accepted_ignore_multipart_boundary(Config) ->
doc("When a multipart content-type is provided for the request "
"body, the boundary parameter is not expected to be returned "
@@ -295,6 +347,18 @@ content_types_accepted_ignore_multipart_boundary(Config) ->
{response, _, 204, _} = gun:await(ConnPid, Ref),
ok.
+content_types_accepted_param(Config) ->
+ doc("When a parameter is returned from the content_types_accepted "
+ "callback, and the same parameter is found in the content-type "
+ "header, the negotiation succeeds and the request is processed."),
+ ConnPid = gun_open(Config),
+ Ref = gun:put(ConnPid, "/content_types_accepted?param", [
+ {<<"accept-encoding">>, <<"gzip">>},
+ {<<"content-type">>, <<"text/plain;charset=UTF-8">>}
+ ], "12345"),
+ {response, fin, 204, _} = gun:await(ConnPid, Ref),
+ ok.
+
content_types_accepted_wildcard_param_no_content_type_param(Config) ->
doc("When a wildcard is returned for parameters from the "
"content_types_accepted callback, a content-type header "
@@ -381,6 +445,17 @@ content_types_provided_wildcard_param_no_accept_header(Config) ->
{ok, <<"[]">>} = gun:await_body(ConnPid, Ref),
ok.
+delete_resource_missing(Config) ->
+ doc("When a resource accepts the DELETE method and the "
+ "delete_resource callback is not exported, the "
+ "resource is incorrect and a 500 response is expected."),
+ ConnPid = gun_open(Config),
+ Ref = gun:delete(ConnPid, "/delete_resource?missing", [
+ {<<"accept-encoding">>, <<"gzip">>}
+ ]),
+ {response, _, 500, _} = gun:await(ConnPid, Ref),
+ ok.
+
error_on_malformed_accept(Config) ->
doc("A malformed Accept header must result in a 400 response."),
do_error_on_malformed_header(Config, <<"accept">>).
@@ -443,6 +518,89 @@ expires_undefined(Config) ->
false = lists:keyfind(<<"expires">>, 1, Headers),
ok.
+generate_etag_missing(Config) ->
+ doc("The etag header must not be sent when "
+ "the generate_etag callback is not exported."),
+ ConnPid = gun_open(Config),
+ Ref = gun:get(ConnPid, "/generate_etag?missing", [
+ {<<"accept-encoding">>, <<"gzip">>}
+ ]),
+ {response, _, 200, Headers} = gun:await(ConnPid, Ref),
+ false = lists:keyfind(<<"etag">>, 1, Headers),
+ ok.
+
+generate_etag_binary_strong(Config) ->
+ doc("The etag header must be sent when the generate_etag "
+ "callback returns a strong binary. (RFC7232 2.3)"),
+ do_generate_etag(Config, "binary-strong-quoted",
+ [], 200, {<<"etag">>, <<"\"etag-header-value\"">>}).
+
+generate_etag_binary_weak(Config) ->
+ doc("The etag header must be sent when the generate_etag "
+ "callback returns a weak binary. (RFC7232 2.3)"),
+ do_generate_etag(Config, "binary-weak-quoted",
+ [], 200, {<<"etag">>, <<"W/\"etag-header-value\"">>}).
+
+generate_etag_invalid_binary_strong_unquoted(Config) ->
+ doc("When Cowboy cannot parse the generate_etag callback's "
+ "return value, a 500 response is returned without the etag header."),
+ do_generate_etag(Config, "binary-strong-unquoted", [], 500, false).
+
+generate_etag_invalid_binary_weak_unquoted(Config) ->
+ doc("When Cowboy cannot parse the generate_etag callback's "
+ "return value, a 500 response is returned without the etag header."),
+ do_generate_etag(Config, "binary-weak-unquoted", [], 500, false).
+
+generate_etag_tuple_strong(Config) ->
+ doc("The etag header must be sent when the generate_etag "
+ "callback returns a strong tuple. (RFC7232 2.3)"),
+ do_generate_etag(Config, "tuple-strong",
+ [], 200, {<<"etag">>, <<"\"etag-header-value\"">>}).
+
+generate_etag_tuple_weak(Config) ->
+ doc("The etag header must be sent when the generate_etag "
+ "callback returns a weak tuple. (RFC7232 2.3)"),
+ do_generate_etag(Config, "tuple-weak",
+ [], 200, {<<"etag">>, <<"W/\"etag-header-value\"">>}).
+
+if_none_match_binary_strong(Config) ->
+ doc("When the if-none-match header matches a strong etag, "
+ "a 304 not modified response is returned. (RFC7232 3.2)"),
+ do_generate_etag(Config, "binary-strong-quoted",
+ [{<<"if-none-match">>, <<"\"etag-header-value\"">>}],
+ 304, {<<"etag">>, <<"\"etag-header-value\"">>}).
+
+if_none_match_binary_weak(Config) ->
+ doc("When the if-none-match header matches a weak etag, "
+ "a 304 not modified response is returned. (RFC7232 3.2)"),
+ do_generate_etag(Config, "binary-weak-quoted",
+ [{<<"if-none-match">>, <<"W/\"etag-header-value\"">>}],
+ 304, {<<"etag">>, <<"W/\"etag-header-value\"">>}).
+
+if_none_match_tuple_strong(Config) ->
+ doc("When the if-none-match header matches a strong etag, "
+ "a 304 not modified response is returned. (RFC7232 3.2)"),
+ do_generate_etag(Config, "tuple-strong",
+ [{<<"if-none-match">>, <<"\"etag-header-value\"">>}],
+ 304, {<<"etag">>, <<"\"etag-header-value\"">>}).
+
+if_none_match_tuple_weak(Config) ->
+ doc("When the if-none-match header matches a weak etag, "
+ "a 304 not modified response is returned. (RFC7232 3.2)"),
+ do_generate_etag(Config, "tuple-weak",
+ [{<<"if-none-match">>, <<"W/\"etag-header-value\"">>}],
+ 304, {<<"etag">>, <<"W/\"etag-header-value\"">>}).
+
+do_generate_etag(Config, Qs, ReqHeaders, Status, Etag) ->
+ ConnPid = gun_open(Config),
+ Ref = gun:get(ConnPid, "/generate_etag?" ++ Qs, [
+ {<<"accept-encoding">>, <<"gzip">>}
+ |ReqHeaders
+ ]),
+ {response, _, Status, RespHeaders} = gun:await(ConnPid, Ref),
+ Etag = lists:keyfind(<<"etag">>, 1, RespHeaders),
+ ok.
+
if_range_etag_equal(Config) ->
doc("When the if-range header matches, a 206 partial content "
"response is expected for an otherwise valid range request. (RFC7233 3.2)"),