0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-15 12:40:25 +00:00

Reorganize the http test suite

This commit is contained in:
Loïc Hoguin 2013-04-24 20:28:44 +02:00
parent 282e532ba9
commit ad91aaf81a
22 changed files with 25 additions and 25 deletions

View file

@ -0,0 +1,39 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_body_qs).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/3]).
init({_, http}, Req, _) ->
{ok, Req, undefined}.
handle(Req, State) ->
{Method, Req2} = cowboy_req:method(Req),
HasBody = cowboy_req:has_body(Req2),
{ok, Req3} = maybe_echo(Method, HasBody, Req2),
{ok, Req3, State}.
maybe_echo(<<"POST">>, true, Req) ->
case cowboy_req:body_qs(Req) of
{error,badlength} ->
echo(badlength, Req);
{ok, PostVals, Req2} ->
echo(proplists:get_value(<<"echo">>, PostVals), Req2)
end;
maybe_echo(<<"POST">>, false, Req) ->
cowboy_req:reply(400, [], <<"Missing body.">>, Req);
maybe_echo(_, _, Req) ->
%% Method not allowed.
cowboy_req:reply(405, Req).
echo(badlength, Req) ->
cowboy_req:reply(413, [], <<"POST body bigger than 16000 bytes">>, Req);
echo(undefined, Req) ->
cowboy_req:reply(400, [], <<"Missing echo parameter.">>, Req);
echo(Echo, Req) ->
cowboy_req:reply(200,
[{<<"content-encoding">>, <<"utf-8">>}], Echo, Req).
terminate(_, _, _) ->
ok.

View file

@ -0,0 +1,19 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_chunked).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/3]).
init({_Transport, http}, Req, _Opts) ->
{ok, Req, undefined}.
handle(Req, State) ->
{ok, Req2} = cowboy_req:chunked_reply(200, Req),
timer:sleep(100),
cowboy_req:chunk("chunked_handler\r\n", Req2),
timer:sleep(100),
cowboy_req:chunk("works fine!", Req2),
{ok, Req2, State}.
terminate(_, _, _) ->
ok.

View file

@ -0,0 +1,45 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_echo_body).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/3]).
init({_, http}, Req, _) ->
{ok, Req, undefined}.
handle(Req, State) ->
true = cowboy_req:has_body(Req),
{ok, Req3} = case cowboy_req:body(1000000, Req) of
{error, chunked} -> handle_chunked(Req);
{error, badlength} -> handle_badlength(Req);
{ok, Body, Req2} -> handle_body(Req2, Body)
end,
{ok, Req3, State}.
handle_chunked(Req) ->
{ok, Data, Req2} = read_body(Req, <<>>, 1000000),
{ok, Req3} = cowboy_req:reply(200, [], Data, Req2),
{ok, Req3}.
handle_badlength(Req) ->
{ok, Req2} = cowboy_req:reply(413, [], <<"Request entity too large">>, Req),
{ok, Req2}.
handle_body(Req, Body) ->
{Size, Req2} = cowboy_req:body_length(Req),
Size = byte_size(Body),
{ok, Req3} = cowboy_req:reply(200, [], Body, Req2),
{ok, Req3}.
terminate(_, _, _) ->
ok.
% Read chunked request content
read_body(Req, Acc, BodyLengthRemaining) ->
case cowboy_req:stream_body(Req) of
{ok, Data, Req2} ->
BodyLengthRem = BodyLengthRemaining - byte_size(Data),
read_body(Req2, << Acc/binary, Data/binary >>, BodyLengthRem);
{done, Req2} ->
{ok, Acc, Req2}
end.

View file

@ -0,0 +1,40 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_errors).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/3]).
init({_Transport, http}, Req, _Opts) ->
{Case, Req1} = cowboy_req:qs_val(<<"case">>, Req),
case_init(Case, Req1).
case_init(<<"init_before_reply">> = Case, _Req) ->
erlang:error(Case);
case_init(<<"init_after_reply">> = Case, Req) ->
{ok, _Req1} = cowboy_req:reply(200, [], "http_handler_crashes", Req),
erlang:error(Case);
case_init(<<"init_reply_handle_error">> = Case, Req) ->
{ok, Req1} = cowboy_req:reply(200, [], "http_handler_crashes", Req),
{ok, Req1, Case};
case_init(<<"handle_before_reply">> = Case, Req) ->
{ok, Req, Case};
case_init(<<"handle_after_reply">> = Case, Req) ->
{ok, Req, Case}.
handle(_Req, <<"init_reply_handle_error">> = Case) ->
erlang:error(Case);
handle(_Req, <<"handle_before_reply">> = Case) ->
erlang:error(Case);
handle(Req, <<"handle_after_reply">> = Case) ->
{ok, _Req1} = cowboy_req:reply(200, [], "http_handler_crashes", Req),
erlang:error(Case).
terminate(_, _, _) ->
ok.

View file

@ -0,0 +1,19 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_handler).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/3]).
-record(state, {headers, body}).
init({_Transport, http}, Req, Opts) ->
Headers = proplists:get_value(headers, Opts, []),
Body = proplists:get_value(body, Opts, "http_handler"),
{ok, Req, #state{headers=Headers, body=Body}}.
handle(Req, State=#state{headers=Headers, body=Body}) ->
{ok, Req2} = cowboy_req:reply(200, Headers, Body, Req),
{ok, Req2, State}.
terminate(_, _, _) ->
ok.

View file

@ -0,0 +1,17 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_init_shutdown).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/3]).
init({_Transport, http}, Req, _Opts) ->
{ok, Req2} = cowboy_req:reply(<<"666 Init Shutdown Testing">>,
[{<<"connection">>, <<"close">>}], Req),
{shutdown, Req2, undefined}.
handle(Req, State) ->
{ok, Req2} = cowboy_req:reply(200, [], "Hello world!", Req),
{ok, Req2, State}.
terminate(_, _, _) ->
ok.

View file

@ -0,0 +1,24 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_long_polling).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, info/3, terminate/3]).
init({_Transport, http}, Req, _Opts) ->
erlang:send_after(500, self(), timeout),
{loop, Req, 5, 5000, hibernate}.
handle(_Req, _State) ->
exit(badarg).
info(timeout, Req, 0) ->
{ok, Req2} = cowboy_req:reply(102, Req),
{ok, Req2, 0};
info(timeout, Req, State) ->
erlang:send_after(500, self(), timeout),
{loop, Req, State - 1, hibernate}.
terminate({normal, shutdown}, _, _) ->
ok;
terminate({error, overflow}, _, _) ->
ok.

View file

@ -0,0 +1,18 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_loop_recv).
-behaviour(cowboy_loop_handler).
-export([init/3, info/3, terminate/3]).
init({_, http}, Req, _) ->
self() ! recv_timeout,
{loop, Req, undefined, 500, hibernate}.
info(recv_timeout, Req, State) ->
{ok, Body, Req1} = cowboy_req:body(Req),
100000 = byte_size(Body),
{ok, Req2} = cowboy_req:reply(200, Req1),
{ok, Req2, State}.
terminate({normal, shutdown}, _, _) ->
ok.

View file

@ -0,0 +1,16 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_loop_timeout).
-behaviour(cowboy_loop_handler).
-export([init/3, info/3, terminate/3]).
init({_, http}, Req, _) ->
erlang:send_after(1000, self(), error_timeout),
{loop, Req, undefined, 500, hibernate}.
info(error_timeout, Req, State) ->
{ok, Req2} = cowboy_req:reply(500, Req),
{ok, Req2, State}.
terminate({normal, timeout}, _, _) ->
ok.

View file

@ -0,0 +1,29 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_multipart).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/3]).
init({_Transport, http}, Req, []) ->
{ok, Req, {}}.
handle(Req, State) ->
{Result, Req2} = acc_multipart(Req),
{ok, Req3} = cowboy_req:reply(200, [], term_to_binary(Result), Req2),
{ok, Req3, State}.
terminate(_, _, _) ->
ok.
acc_multipart(Req) ->
acc_multipart(cowboy_req:multipart_data(Req), []).
acc_multipart({headers, Headers, Req}, Acc) ->
acc_multipart(cowboy_req:multipart_data(Req), [{Headers, []}|Acc]);
acc_multipart({body, Data, Req}, [{Headers, BodyAcc}|Acc]) ->
acc_multipart(cowboy_req:multipart_data(Req), [{Headers, [Data|BodyAcc]}|Acc]);
acc_multipart({end_of_part, Req}, [{Headers, BodyAcc}|Acc]) ->
acc_multipart(cowboy_req:multipart_data(Req),
[{Headers, list_to_binary(lists:reverse(BodyAcc))}|Acc]);
acc_multipart({eof, Req}, Acc) ->
{lists:reverse(Acc), Req}.

View file

@ -0,0 +1,31 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_set_resp).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/3]).
init({_Transport, http}, 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, 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),
{ok, Req5, undefined}.
handle(Req, State) ->
case cowboy_req:has_resp_header(<<"x-cowboy-test">>, Req) of
false -> {ok, Req, State};
true ->
case cowboy_req:has_resp_body(Req) of
false -> {ok, Req, State};
true ->
{ok, Req2} = cowboy_req:reply(200, Req),
{ok, Req2, State}
end
end.
terminate(_, _, _) ->
ok.

View file

@ -0,0 +1,28 @@
%% Feel free to use, reuse and abuse the code in this file.
-module(http_stream_body).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/3]).
-record(state, {headers, body, reply}).
init({_Transport, http}, Req, Opts) ->
Headers = proplists:get_value(headers, Opts, []),
Body = proplists:get_value(body, Opts, "http_handler_stream_body"),
Reply = proplists:get_value(reply, Opts),
{ok, Req, #state{headers=Headers, body=Body, reply=Reply}}.
handle(Req, State=#state{headers=_Headers, body=Body, reply=Reply}) ->
SFun = fun(Socket, Transport) -> Transport:send(Socket, Body) end,
Req2 = case Reply of
set_resp ->
SLen = iolist_size(Body),
cowboy_req:set_resp_body_fun(SLen, SFun, Req);
set_resp_close ->
cowboy_req:set_resp_body_fun(SFun, Req)
end,
{ok, Req3} = cowboy_req:reply(200, Req2),
{ok, Req3, State}.
terminate(_, _, _) ->
ok.

View file

@ -0,0 +1,5 @@
-module(rest_empty_resource).
-export([init/3]).
init(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.

View file

@ -0,0 +1,22 @@
-module(rest_expires).
-export([init/3]).
-export([content_types_provided/2]).
-export([get_text_plain/2]).
-export([expires/2]).
-export([last_modified/2]).
init(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.
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}.

View file

@ -0,0 +1,31 @@
-module(rest_forbidden_resource).
-export([init/3, rest_init/2, allowed_methods/2, forbidden/2,
content_types_provided/2, content_types_accepted/2,
to_text/2, from_text/2]).
init(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.
rest_init(Req, [Forbidden]) ->
{ok, 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) ->
{Path, Req2} = cowboy_req:path(Req),
{Path, Req2, State}.

View file

@ -0,0 +1,21 @@
-module(rest_missing_callbacks).
-export([init/3]).
-export([allowed_methods/2]).
-export([content_types_accepted/2]).
-export([content_types_provided/2]).
init(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.
allowed_methods(Req, State) ->
{[<<"GET">>, <<"PUT">>], Req, State}.
content_types_accepted(Req, State) ->
{[
{<<"application/json">>, put_application_json}
], Req, State}.
content_types_provided(Req, State) ->
{[
{<<"text/plain">>, get_text_plain}
], Req, State}.

View file

@ -0,0 +1,17 @@
-module(rest_nodelete_resource).
-export([init/3, allowed_methods/2, content_types_provided/2,
get_text_plain/2]).
init(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.
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}.

View file

@ -0,0 +1,36 @@
-module(rest_param_all).
-export([init/3]).
-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(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.
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}, Req2} =
cowboy_req:meta(media_type, Req, {{<<"text">>, <<"plain">>}, []}),
Body = if
Param == '*' ->
<<"'*'">>;
Param == [] ->
<<"[]">>;
Param /= [] ->
iolist_to_binary([[Key, $=, Value] || {Key, Value} <- Param])
end,
{Body, Req2, State}.
content_types_accepted(Req, State) ->
{[{{<<"text">>, <<"plain">>, '*'}, put_text_plain}], Req, State}.
put_text_plain(Req, State) ->
{true, Req, State}.

View file

@ -0,0 +1,34 @@
-module(rest_patch_resource).
-export([init/3, allowed_methods/2, content_types_provided/2, get_text_plain/2,
content_types_accepted/2, patch_text_plain/2]).
init(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.
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">>, Req0} ->
{[{{<<"text">>, <<"plain">>, []}, patch_text_plain}], Req0, State};
{_, Req0} ->
{[], Req0, State}
end.
patch_text_plain(Req, State) ->
case cowboy_req:body(Req) of
{ok, <<"halt">>, Req0} ->
{ok, Req1} = cowboy_req:reply(400, Req0),
{halt, Req1, State};
{ok, <<"false">>, Req0} ->
{false, Req0, State};
{ok, _Body, Req0} ->
{true, Req0, State}
end.

View file

@ -0,0 +1,30 @@
-module(rest_resource_etags).
-export([init/3, generate_etag/2, content_types_provided/2, get_text_plain/2]).
init(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.
generate_etag(Req, State) ->
case cowboy_req:qs_val(<<"type">>, Req) of
%% Correct return values from generate_etag/2.
{<<"tuple-weak">>, Req2} ->
{{weak, <<"etag-header-value">>}, Req2, State};
{<<"tuple-strong">>, Req2} ->
{{strong, <<"etag-header-value">>}, Req2, State};
%% Backwards compatible return values from generate_etag/2.
{<<"binary-weak-quoted">>, Req2} ->
{<<"W/\"etag-header-value\"">>, Req2, State};
{<<"binary-strong-quoted">>, Req2} ->
{<<"\"etag-header-value\"">>, Req2, State};
%% Invalid return values from generate_etag/2.
{<<"binary-strong-unquoted">>, Req2} ->
{<<"etag-header-value">>, Req2, State};
{<<"binary-weak-unquoted">>, Req2} ->
{<<"W/etag-header-value">>, Req2, State}
end.
content_types_provided(Req, State) ->
{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
get_text_plain(Req, State) ->
{<<"This is REST!">>, Req, State}.

View file

@ -0,0 +1,12 @@
-module(rest_simple_resource).
-export([init/3, content_types_provided/2, get_text_plain/2]).
init(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.
content_types_provided(Req, State) ->
{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
get_text_plain(Req, State) ->
{<<"This is REST!">>, Req, State}.