aboutsummaryrefslogtreecommitdiffstats
path: root/test/http_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'test/http_SUITE.erl')
-rw-r--r--test/http_SUITE.erl282
1 files changed, 233 insertions, 49 deletions
diff --git a/test/http_SUITE.erl b/test/http_SUITE.erl
index c90e585..afe62c3 100644
--- a/test/http_SUITE.erl
+++ b/test/http_SUITE.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011-2012, Loïc Hoguin <[email protected]>
+%% Copyright (c) 2011-2013, Loïc Hoguin <[email protected]>
%% Copyright (c) 2011, Anthony Ramine <[email protected]>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
@@ -45,21 +45,26 @@
-export([nc_zero/1]).
-export([onrequest/1]).
-export([onrequest_reply/1]).
+-export([onresponse_capitalize/1]).
-export([onresponse_crash/1]).
-export([onresponse_reply/1]).
-export([pipeline/1]).
-export([rest_bad_accept/1]).
+-export([rest_created_path/1]).
-export([rest_expires/1]).
-export([rest_keepalive/1]).
-export([rest_keepalive_post/1]).
-export([rest_missing_get_callbacks/1]).
-export([rest_missing_put_callbacks/1]).
-export([rest_nodelete/1]).
+-export([rest_patch/1]).
-export([rest_resource_etags/1]).
-export([rest_resource_etags_if_none_match/1]).
-export([set_resp_body/1]).
-export([set_resp_header/1]).
-export([set_resp_overwrite/1]).
+-export([slowloris/1]).
+-export([slowloris2/1]).
-export([static_attribute_etag/1]).
-export([static_function_etag/1]).
-export([static_mimetypes_function/1]).
@@ -68,14 +73,24 @@
-export([static_test_file/1]).
-export([static_test_file_css/1]).
-export([stream_body_set_resp/1]).
+-export([stream_body_set_resp_close/1]).
-export([te_chunked/1]).
+-export([te_chunked_chopped/1]).
-export([te_chunked_delayed/1]).
-export([te_identity/1]).
%% ct.
all() ->
- [{group, http}, {group, https}, {group, onrequest}, {group, onresponse}].
+ [
+ {group, http},
+ {group, https},
+ {group, http_compress},
+ {group, https_compress},
+ {group, onrequest},
+ {group, onresponse},
+ {group, onresponse_capitalize}
+ ].
groups() ->
Tests = [
@@ -98,17 +113,21 @@ groups() ->
nc_zero,
pipeline,
rest_bad_accept,
+ rest_created_path,
rest_expires,
rest_keepalive,
rest_keepalive_post,
rest_missing_get_callbacks,
rest_missing_put_callbacks,
rest_nodelete,
+ rest_patch,
rest_resource_etags,
rest_resource_etags_if_none_match,
set_resp_body,
set_resp_header,
set_resp_overwrite,
+ slowloris,
+ slowloris2,
static_attribute_etag,
static_function_etag,
static_mimetypes_function,
@@ -117,13 +136,17 @@ groups() ->
static_test_file,
static_test_file_css,
stream_body_set_resp,
+ stream_body_set_resp_close,
te_chunked,
+ te_chunked_chopped,
te_chunked_delayed,
te_identity
],
[
{http, [], Tests},
{https, [], Tests},
+ {http_compress, [], Tests},
+ {https_compress, [], Tests},
{onrequest, [], [
onrequest,
onrequest_reply
@@ -131,11 +154,13 @@ groups() ->
{onresponse, [], [
onresponse_crash,
onresponse_reply
+ ]},
+ {onresponse_capitalize, [], [
+ onresponse_capitalize
]}
].
init_per_suite(Config) ->
- application:start(inets),
application:start(crypto),
application:start(ranch),
application:start(cowboy),
@@ -145,7 +170,6 @@ end_per_suite(_Config) ->
application:stop(cowboy),
application:stop(ranch),
application:stop(crypto),
- application:stop(inets),
ok.
init_per_group(http, Config) ->
@@ -153,7 +177,7 @@ init_per_group(http, Config) ->
Transport = ranch_tcp,
Config1 = init_static_dir(Config),
{ok, _} = cowboy:start_http(http, 100, [{port, Port}], [
- {dispatch, init_dispatch(Config1)},
+ {env, [{dispatch, init_dispatch(Config1)}]},
{max_keepalive, 50},
{timeout, 500}
]),
@@ -172,18 +196,51 @@ init_per_group(https, Config) ->
application:start(public_key),
application:start(ssl),
{ok, _} = cowboy:start_https(https, 100, Opts ++ [{port, Port}], [
- {dispatch, init_dispatch(Config1)},
+ {env, [{dispatch, init_dispatch(Config1)}]},
{max_keepalive, 50},
{timeout, 500}
]),
{ok, Client} = cowboy_client:init(Opts),
[{scheme, <<"https">>}, {port, Port}, {opts, Opts},
{transport, Transport}, {client, Client}|Config1];
-init_per_group(onrequest, Config) ->
+init_per_group(http_compress, Config) ->
Port = 33082,
Transport = ranch_tcp,
+ Config1 = init_static_dir(Config),
+ {ok, _} = cowboy:start_http(http_compress, 100, [{port, Port}], [
+ {compress, true},
+ {env, [{dispatch, init_dispatch(Config1)}]},
+ {max_keepalive, 50},
+ {timeout, 500}
+ ]),
+ {ok, Client} = cowboy_client:init([]),
+ [{scheme, <<"http">>}, {port, Port}, {opts, []},
+ {transport, Transport}, {client, Client}|Config1];
+init_per_group(https_compress, Config) ->
+ Port = 33083,
+ Transport = ranch_ssl,
+ Opts = [
+ {certfile, ?config(data_dir, Config) ++ "cert.pem"},
+ {keyfile, ?config(data_dir, Config) ++ "key.pem"},
+ {password, "cowboy"}
+ ],
+ Config1 = init_static_dir(Config),
+ application:start(public_key),
+ application:start(ssl),
+ {ok, _} = cowboy:start_https(https_compress, 100, Opts ++ [{port, Port}], [
+ {compress, true},
+ {env, [{dispatch, init_dispatch(Config1)}]},
+ {max_keepalive, 50},
+ {timeout, 500}
+ ]),
+ {ok, Client} = cowboy_client:init(Opts),
+ [{scheme, <<"https">>}, {port, Port}, {opts, Opts},
+ {transport, Transport}, {client, Client}|Config1];
+init_per_group(onrequest, Config) ->
+ Port = 33084,
+ Transport = ranch_tcp,
{ok, _} = cowboy:start_http(onrequest, 100, [{port, Port}], [
- {dispatch, init_dispatch(Config)},
+ {env, [{dispatch, init_dispatch(Config)}]},
{max_keepalive, 50},
{onrequest, fun onrequest_hook/1},
{timeout, 500}
@@ -192,25 +249,37 @@ init_per_group(onrequest, Config) ->
[{scheme, <<"http">>}, {port, Port}, {opts, []},
{transport, Transport}, {client, Client}|Config];
init_per_group(onresponse, Config) ->
- Port = 33083,
+ Port = 33085,
Transport = ranch_tcp,
{ok, _} = cowboy:start_http(onresponse, 100, [{port, Port}], [
- {dispatch, init_dispatch(Config)},
+ {env, [{dispatch, init_dispatch(Config)}]},
{max_keepalive, 50},
{onresponse, fun onresponse_hook/4},
{timeout, 500}
]),
{ok, Client} = cowboy_client:init([]),
[{scheme, <<"http">>}, {port, Port}, {opts, []},
+ {transport, Transport}, {client, Client}|Config];
+init_per_group(onresponse_capitalize, Config) ->
+ Port = 33086,
+ Transport = ranch_tcp,
+ {ok, _} = cowboy:start_http(onresponse_capitalize, 100, [{port, Port}], [
+ {env, [{dispatch, init_dispatch(Config)}]},
+ {max_keepalive, 50},
+ {onresponse, fun onresponse_capitalize_hook/4},
+ {timeout, 500}
+ ]),
+ {ok, Client} = cowboy_client:init([]),
+ [{scheme, <<"http">>}, {port, Port}, {opts, []},
{transport, Transport}, {client, Client}|Config].
-end_per_group(https, Config) ->
+end_per_group(Group, Config) when Group =:= https; Group =:= https_compress ->
cowboy:stop_listener(https),
application:stop(ssl),
application:stop(public_key),
end_static_dir(Config),
ok;
-end_per_group(http, Config) ->
+end_per_group(Group, Config) when Group =:= http; Group =:= http_compress ->
cowboy:stop_listener(http),
end_static_dir(Config);
end_per_group(Name, _) ->
@@ -220,54 +289,60 @@ end_per_group(Name, _) ->
%% Dispatch configuration.
init_dispatch(Config) ->
- [
- {[<<"localhost">>], [
- {[<<"chunked_response">>], chunked_handler, []},
- {[<<"init_shutdown">>], http_handler_init_shutdown, []},
- {[<<"long_polling">>], http_handler_long_polling, []},
- {[<<"headers">>, <<"dupe">>], http_handler,
+ cowboy_router:compile([
+ {"localhost", [
+ {"/chunked_response", chunked_handler, []},
+ {"/init_shutdown", http_handler_init_shutdown, []},
+ {"/long_polling", http_handler_long_polling, []},
+ {"/headers/dupe", http_handler,
[{headers, [{<<"connection">>, <<"close">>}]}]},
- {[<<"set_resp">>, <<"header">>], http_handler_set_resp,
+ {"/set_resp/header", http_handler_set_resp,
[{headers, [{<<"vary">>, <<"Accept">>}]}]},
- {[<<"set_resp">>, <<"overwrite">>], http_handler_set_resp,
+ {"/set_resp/overwrite", http_handler_set_resp,
[{headers, [{<<"server">>, <<"DesireDrive/1.0">>}]}]},
- {[<<"set_resp">>, <<"body">>], http_handler_set_resp,
+ {"/set_resp/body", http_handler_set_resp,
[{body, <<"A flameless dance does not equal a cycle">>}]},
- {[<<"stream_body">>, <<"set_resp">>], http_handler_stream_body,
+ {"/stream_body/set_resp", http_handler_stream_body,
[{reply, set_resp}, {body, <<"stream_body_set_resp">>}]},
- {[<<"static">>, '...'], cowboy_static,
+ {"/stream_body/set_resp_close",
+ http_handler_stream_body, [
+ {reply, set_resp_close},
+ {body, <<"stream_body_set_resp_close">>}]},
+ {"/static/[...]", cowboy_static,
[{directory, ?config(static_dir, Config)},
{mimetypes, [{<<".css">>, [<<"text/css">>]}]}]},
- {[<<"static_mimetypes_function">>, '...'], cowboy_static,
+ {"/static_mimetypes_function/[...]", cowboy_static,
[{directory, ?config(static_dir, Config)},
{mimetypes, {fun(Path, data) when is_binary(Path) ->
[<<"text/html">>] end, data}}]},
- {[<<"handler_errors">>], http_handler_errors, []},
- {[<<"static_attribute_etag">>, '...'], cowboy_static,
+ {"/handler_errors", http_handler_errors, []},
+ {"/static_attribute_etag/[...]", cowboy_static,
[{directory, ?config(static_dir, Config)},
{etag, {attributes, [filepath, filesize, inode, mtime]}}]},
- {[<<"static_function_etag">>, '...'], cowboy_static,
+ {"/static_function_etag/[...]", cowboy_static,
[{directory, ?config(static_dir, Config)},
{etag, {fun static_function_etag/2, etag_data}}]},
- {[<<"static_specify_file">>, '...'], cowboy_static,
+ {"/static_specify_file/[...]", cowboy_static,
[{directory, ?config(static_dir, Config)},
{mimetypes, [{<<".css">>, [<<"text/css">>]}]},
{file, <<"test_file.css">>}]},
- {[<<"multipart">>], http_handler_multipart, []},
- {[<<"echo">>, <<"body">>], http_handler_echo_body, []},
- {[<<"bad_accept">>], rest_simple_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, []},
- {[<<"resetags">>], rest_resource_etags, []},
- {[<<"rest_expires">>], rest_expires, []},
- {[<<"loop_timeout">>], http_handler_loop_timeout, []},
- {[], http_handler, []}
+ {"/multipart", http_handler_multipart, []},
+ {"/echo/body", http_handler_echo_body, []},
+ {"/bad_accept", rest_simple_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, []},
+ {"/patch", rest_patch_resource, []},
+ {"/created_path", rest_created_path_resource, []},
+ {"/resetags", rest_resource_etags, []},
+ {"/rest_expires", rest_expires, []},
+ {"/loop_timeout", http_handler_loop_timeout, []},
+ {"/", http_handler, []}
]}
- ].
+ ]).
init_static_dir(Config) ->
Dir = filename:join(?config(priv_dir, Config), "static"),
@@ -408,10 +483,16 @@ check_status(Config) ->
{Ret, URL}
end || {Status, URL} <- Tests].
-%% @todo Convert to cowboy_client.
chunked_response(Config) ->
- {ok, {{"HTTP/1.1", 200, "OK"}, _, "chunked_handler\r\nworks fine!"}}
- = httpc:request(binary_to_list(build_url("/chunked_response", Config))).
+ Client = ?config(client, Config),
+ {ok, Client2} = cowboy_client:request(<<"GET">>,
+ build_url("/chunked_response", Config), Client),
+ {ok, 200, Headers, Client3} = cowboy_client:response(Client2),
+ true = lists:keymember(<<"transfer-encoding">>, 1, Headers),
+ {ok, Transport, Socket} = cowboy_client:transport(Client3),
+ {ok, <<"11\r\nchunked_handler\r\n\r\nB\r\nworks fine!\r\n0\r\n\r\n">>}
+ = Transport:recv(Socket, 44, 1000),
+ {error, closed} = cowboy_client:response(Client3).
%% Check if sending requests whose size is around the MTU breaks something.
echo_body(Config) ->
@@ -503,8 +584,8 @@ http10_hostless(Config) ->
ranch:start_listener(Name, 5,
?config(transport, Config), ?config(opts, Config) ++ [{port, Port10}],
cowboy_protocol, [
- {dispatch, [{'_', [
- {[<<"http1.0">>, <<"hostless">>], http_handler, []}]}]},
+ {env, [{dispatch, cowboy_router:compile([
+ {'_', [{"/http1.0/hostless", http_handler, []}]}])}]},
{max_keepalive, 50},
{timeout, 500}]
),
@@ -517,7 +598,8 @@ keepalive_max(Config) ->
URL = build_url("/", Config),
ok = keepalive_max_loop(Client, URL, 50).
-keepalive_max_loop(_, _, 0) ->
+keepalive_max_loop(Client, _, 0) ->
+ {error, closed} = cowboy_client:response(Client),
ok;
keepalive_max_loop(Client, URL, N) ->
Headers = [{<<"connection">>, <<"keep-alive">>}],
@@ -536,7 +618,8 @@ keepalive_nl(Config) ->
URL = build_url("/", Config),
ok = keepalive_nl_loop(Client, URL, 10).
-keepalive_nl_loop(_, _, 0) ->
+keepalive_nl_loop(Client, _, 0) ->
+ {error, closed} = cowboy_client:response(Client),
ok;
keepalive_nl_loop(Client, URL, N) ->
Headers = [{<<"connection">>, <<"keep-alive">>}],
@@ -619,6 +702,21 @@ onrequest_hook(Req) ->
Req3
end.
+onresponse_capitalize(Config) ->
+ Client = ?config(client, Config),
+ {ok, Client2} = cowboy_client:request(<<"GET">>,
+ build_url("/", Config), Client),
+ {ok, Transport, Socket} = cowboy_client:transport(Client2),
+ {ok, Data} = Transport:recv(Socket, 0, 1000),
+ false = nomatch =:= binary:match(Data, <<"Content-Length">>).
+
+%% Hook for the above onresponse_capitalize test.
+onresponse_capitalize_hook(Status, Headers, Body, Req) ->
+ Headers2 = [{cowboy_bstr:capitalize_token(N), V}
+ || {N, V} <- Headers],
+ {ok, Req2} = cowboy_req:reply(Status, Headers2, Body, Req),
+ Req2.
+
onresponse_crash(Config) ->
Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>,
@@ -668,6 +766,18 @@ rest_bad_accept(Config) ->
Client),
{ok, 400, _, _} = cowboy_client:response(Client2).
+rest_created_path(Config) ->
+ Headers = [{<<"content-type">>, <<"text/plain">>}],
+ Body = <<"Whatever">>,
+ Client = ?config(client, Config),
+ URL = build_url("/created_path", Config),
+ {ok, Client2} = cowboy_client:request(<<"POST">>, URL, Headers,
+ Body, Client),
+ {ok, 303, ResHeaders, _} = cowboy_client:response(Client2),
+ {<<"location">>, _Location} =
+ lists:keyfind(<<"location">>, 1, ResHeaders),
+ ok.
+
rest_expires(Config) ->
Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>,
@@ -742,6 +852,21 @@ rest_nodelete(Config) ->
build_url("/nodelete", Config), Client),
{ok, 500, _, _} = cowboy_client:response(Client2).
+rest_patch(Config) ->
+ Tests = [
+ {204, [{<<"content-type">>, <<"text/plain">>}], <<"whatever">>},
+ {422, [{<<"content-type">>, <<"text/plain">>}], <<"false">>},
+ {400, [{<<"content-type">>, <<"text/plain">>}], <<"halt">>},
+ {415, [{<<"content-type">>, <<"application/json">>}], <<"bad_content_type">>}
+ ],
+ Client = ?config(client, Config),
+ _ = [begin
+ {ok, Client2} = cowboy_client:request(<<"PATCH">>,
+ build_url("/patch", Config), Headers, Body, Client),
+ {ok, Status, _, _} = cowboy_client:response(Client2),
+ ok
+ end || {Status, Headers, Body} <- Tests].
+
rest_resource_get_etag(Config, Type) ->
rest_resource_get_etag(Config, Type, []).
@@ -806,6 +931,34 @@ set_resp_overwrite(Config) ->
{<<"server">>, <<"DesireDrive/1.0">>}
= lists:keyfind(<<"server">>, 1, Headers).
+slowloris(Config) ->
+ Client = ?config(client, Config),
+ Transport = ?config(transport, Config),
+ {ok, Client2} = cowboy_client:connect(
+ Transport, "localhost", ?config(port, Config), Client),
+ try
+ [begin
+ {ok, _} = cowboy_client:raw_request([C], Client2),
+ receive after 25 -> ok end
+ end || C <- "GET / HTTP/1.1\r\nHost: localhost\r\n"
+ "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US)\r\n"
+ "Cookie: name=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n\r\n"],
+ error(failure)
+ catch error:{badmatch, _} ->
+ ok
+ end.
+
+slowloris2(Config) ->
+ Client = ?config(client, Config),
+ Transport = ?config(transport, Config),
+ {ok, Client2} = cowboy_client:connect(
+ Transport, "localhost", ?config(port, Config), Client),
+ {ok, _} = cowboy_client:raw_request("GET / HTTP/1.1\r\n", Client2),
+ receive after 300 -> ok end,
+ {ok, _} = cowboy_client:raw_request("Host: localhost\r\n", Client2),
+ receive after 300 -> ok end,
+ {ok, 408, _, _} = cowboy_client:response(Client2).
+
static_attribute_etag(Config) ->
Client = ?config(client, Config),
{ok, Client2} = cowboy_client:request(<<"GET">>,
@@ -892,6 +1045,22 @@ stream_body_set_resp(Config) ->
{ok, <<"stream_body_set_resp">>, _}
= cowboy_client:response_body(Client3).
+stream_body_set_resp_close(Config) ->
+ Client = ?config(client, Config),
+ {ok, Client2} = cowboy_client:request(<<"GET">>,
+ build_url("/stream_body/set_resp_close", Config), Client),
+ {ok, 200, _, Client3} = cowboy_client:response(Client2),
+ {ok, Transport, Socket} = cowboy_client:transport(Client3),
+ case element(7, Client3) of
+ <<"stream_body_set_resp_close">> ->
+ ok;
+ Buffer ->
+ {ok, Rest} = Transport:recv(Socket, 26 - byte_size(Buffer), 1000),
+ <<"stream_body_set_resp_close">> = << Buffer/binary, Rest/binary >>,
+ ok
+ end,
+ {error, closed} = Transport:recv(Socket, 0, 1000).
+
te_chunked(Config) ->
Client = ?config(client, Config),
Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])),
@@ -903,6 +1072,21 @@ te_chunked(Config) ->
{ok, 200, _, Client3} = cowboy_client:response(Client2),
{ok, Body, _} = cowboy_client:response_body(Client3).
+te_chunked_chopped(Config) ->
+ Client = ?config(client, Config),
+ Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])),
+ Body2 = iolist_to_binary(body_to_chunks(50, Body, [])),
+ {ok, Client2} = cowboy_client:request(<<"GET">>,
+ build_url("/echo/body", Config),
+ [{<<"transfer-encoding">>, <<"chunked">>}], Client),
+ {ok, Transport, Socket} = cowboy_client:transport(Client2),
+ _ = [begin
+ ok = Transport:send(Socket, << C >>),
+ ok = timer:sleep(10)
+ end || << C >> <= Body2],
+ {ok, 200, _, Client3} = cowboy_client:response(Client2),
+ {ok, Body, _} = cowboy_client:response_body(Client3).
+
te_chunked_delayed(Config) ->
Client = ?config(client, Config),
Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])),