mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Move the final old HTTP suite tests and remove it
This commit is contained in:
parent
037b286aa8
commit
0223f69fcd
24 changed files with 241 additions and 591 deletions
23
test/handlers/accept_callback_h.erl
Normal file
23
test/handlers/accept_callback_h.erl
Normal file
|
@ -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}.
|
|
@ -1,5 +1,5 @@
|
||||||
%% This module accepts a multipart media type with parameters
|
%% This module returns something different in
|
||||||
%% that do not include boundary.
|
%% content_types_accepted depending on the query string.
|
||||||
|
|
||||||
-module(content_types_accepted_h).
|
-module(content_types_accepted_h).
|
||||||
|
|
||||||
|
@ -19,6 +19,8 @@ content_types_accepted(Req=#{qs := <<"multipart">>}, State) ->
|
||||||
{[
|
{[
|
||||||
{{<<"multipart">>, <<"mixed">>, [{<<"v">>, <<"1">>}]}, put_multipart_mixed}
|
{{<<"multipart">>, <<"mixed">>, [{<<"v">>, <<"1">>}]}, put_multipart_mixed}
|
||||||
], Req, State};
|
], 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) ->
|
content_types_accepted(Req=#{qs := <<"wildcard-param">>}, State) ->
|
||||||
{[{{<<"text">>, <<"plain">>, '*'}, put_text_plain}], Req, State}.
|
{[{{<<"text">>, <<"plain">>, '*'}, put_text_plain}], Req, State}.
|
||||||
|
|
||||||
|
|
17
test/handlers/delete_resource_h.erl
Normal file
17
test/handlers/delete_resource_h.erl
Normal file
|
@ -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.
|
39
test/handlers/generate_etag_h.erl
Normal file
39
test/handlers/generate_etag_h.erl
Normal file
|
@ -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.
|
|
@ -1,183 +0,0 @@
|
||||||
%% Copyright (c) 2011-2017, Loïc Hoguin <essen@ninenines.eu>
|
|
||||||
%% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
|
|
||||||
%%
|
|
||||||
%% 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].
|
|
|
@ -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}.
|
|
|
@ -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).
|
|
|
@ -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).
|
|
|
@ -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}.
|
|
|
@ -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.
|
|
|
@ -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.
|
|
|
@ -1,6 +0,0 @@
|
||||||
-module(rest_empty_resource).
|
|
||||||
|
|
||||||
-export([init/2]).
|
|
||||||
|
|
||||||
init(Req, Opts) ->
|
|
||||||
{cowboy_rest, Req, Opts}.
|
|
|
@ -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}.
|
|
|
@ -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}.
|
|
|
@ -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}.
|
|
|
@ -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}.
|
|
|
@ -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}.
|
|
|
@ -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}.
|
|
|
@ -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.
|
|
|
@ -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}.
|
|
|
@ -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}.
|
|
|
@ -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}.
|
|
|
@ -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}.
|
|
|
@ -39,6 +39,7 @@ end_per_group(Name, _) ->
|
||||||
init_dispatch(_) ->
|
init_dispatch(_) ->
|
||||||
cowboy_router:compile([{'_', [
|
cowboy_router:compile([{'_', [
|
||||||
{"/", rest_hello_h, []},
|
{"/", rest_hello_h, []},
|
||||||
|
{"/accept_callback", accept_callback_h, []},
|
||||||
{"/accept_callback_missing", accept_callback_missing_h, []},
|
{"/accept_callback_missing", accept_callback_missing_h, []},
|
||||||
{"/charsets_provided", charsets_provided_h, []},
|
{"/charsets_provided", charsets_provided_h, []},
|
||||||
{"/charsets_provided_empty", charsets_provided_empty_h, []},
|
{"/charsets_provided_empty", charsets_provided_empty_h, []},
|
||||||
|
@ -50,7 +51,9 @@ init_dispatch(_) ->
|
||||||
charset_in_content_types_provided_implicit_no_callback_h, []},
|
charset_in_content_types_provided_implicit_no_callback_h, []},
|
||||||
{"/content_types_accepted", content_types_accepted_h, []},
|
{"/content_types_accepted", content_types_accepted_h, []},
|
||||||
{"/content_types_provided", content_types_provided_h, []},
|
{"/content_types_provided", content_types_provided_h, []},
|
||||||
|
{"/delete_resource", delete_resource_h, []},
|
||||||
{"/expires", expires_h, []},
|
{"/expires", expires_h, []},
|
||||||
|
{"/generate_etag", generate_etag_h, []},
|
||||||
{"/if_range", if_range_h, []},
|
{"/if_range", if_range_h, []},
|
||||||
{"/last_modified", last_modified_h, []},
|
{"/last_modified", last_modified_h, []},
|
||||||
{"/provide_callback_missing", provide_callback_missing_h, []},
|
{"/provide_callback_missing", provide_callback_missing_h, []},
|
||||||
|
@ -84,6 +87,44 @@ accept_callback_missing(Config) ->
|
||||||
{response, fin, 500, _} = gun:await(ConnPid, Ref),
|
{response, fin, 500, _} = gun:await(ConnPid, Ref),
|
||||||
ok.
|
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) ->
|
charset_in_content_types_provided(Config) ->
|
||||||
doc("When a charset is matched explictly in content_types_provided, "
|
doc("When a charset is matched explictly in content_types_provided, "
|
||||||
"that charset is used and the charsets_provided callback is ignored."),
|
"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),
|
{response, _, 406, _} = gun:await(ConnPid, Ref),
|
||||||
ok.
|
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) ->
|
content_types_accepted_ignore_multipart_boundary(Config) ->
|
||||||
doc("When a multipart content-type is provided for the request "
|
doc("When a multipart content-type is provided for the request "
|
||||||
"body, the boundary parameter is not expected to be returned "
|
"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),
|
{response, _, 204, _} = gun:await(ConnPid, Ref),
|
||||||
ok.
|
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) ->
|
content_types_accepted_wildcard_param_no_content_type_param(Config) ->
|
||||||
doc("When a wildcard is returned for parameters from the "
|
doc("When a wildcard is returned for parameters from the "
|
||||||
"content_types_accepted callback, a content-type header "
|
"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, <<"[]">>} = gun:await_body(ConnPid, Ref),
|
||||||
ok.
|
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) ->
|
error_on_malformed_accept(Config) ->
|
||||||
doc("A malformed Accept header must result in a 400 response."),
|
doc("A malformed Accept header must result in a 400 response."),
|
||||||
do_error_on_malformed_header(Config, <<"accept">>).
|
do_error_on_malformed_header(Config, <<"accept">>).
|
||||||
|
@ -443,6 +518,89 @@ expires_undefined(Config) ->
|
||||||
false = lists:keyfind(<<"expires">>, 1, Headers),
|
false = lists:keyfind(<<"expires">>, 1, Headers),
|
||||||
ok.
|
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) ->
|
if_range_etag_equal(Config) ->
|
||||||
doc("When the if-range header matches, a 206 partial content "
|
doc("When the if-range header matches, a 206 partial content "
|
||||||
"response is expected for an otherwise valid range request. (RFC7233 3.2)"),
|
"response is expected for an otherwise valid range request. (RFC7233 3.2)"),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue