From dc240adc1edf70d9d4aad101ae9d40acdacff3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Wed, 21 Nov 2018 10:25:01 +0100 Subject: Move some tests out of the old HTTP test suite And additional minor tweaks. --- test/cowboy_test.erl | 2 + test/http2_SUITE.erl | 25 ++++---- test/http_SUITE.erl | 153 ++++++++++++++++++++++++++++++++++++++++++++---- test/old_http_SUITE.erl | 100 ------------------------------- 4 files changed, 154 insertions(+), 126 deletions(-) (limited to 'test') diff --git a/test/cowboy_test.erl b/test/cowboy_test.erl index 0c02868..a8cb364 100644 --- a/test/cowboy_test.erl +++ b/test/cowboy_test.erl @@ -152,6 +152,8 @@ raw_recv_head(Socket, Transport, Buffer) -> raw_recv({raw_client, Socket, Transport}, Length, Timeout) -> Transport:recv(Socket, Length, Timeout). +raw_expect_recv({raw_client, _, _}, <<>>) -> + ok; raw_expect_recv({raw_client, Socket, Transport}, Expect) -> {ok, Expect} = Transport:recv(Socket, iolist_size(Expect), 10000), ok. diff --git a/test/http2_SUITE.erl b/test/http2_SUITE.erl index 54a5c98..3af837b 100644 --- a/test/http2_SUITE.erl +++ b/test/http2_SUITE.erl @@ -25,13 +25,12 @@ all() -> [{group, clear}]. groups() -> [{clear, [parallel], ct_helper:all(?MODULE)}]. -init_routes(_) -> [ - {"localhost", [ +init_dispatch(_) -> + cowboy_router:compile([{"localhost", [ {"/", hello_h, []}, {"/echo/:key", echo_h, []}, {"/resp_iolist_body", resp_iolist_body_h, []} - ]} -]. + ]}]). %% Do a prior knowledge handshake (function originally copied from rfc7540_SUITE). do_handshake(Config) -> @@ -53,7 +52,7 @@ do_handshake(Settings, Config) -> idle_timeout(Config) -> doc("Terminate when the idle timeout is reached."), ProtoOpts = #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, idle_timeout => 1000 }, {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts), @@ -70,7 +69,7 @@ idle_timeout(Config) -> idle_timeout_infinity(Config) -> doc("Ensure the idle_timeout option accepts the infinity value."), ProtoOpts = #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, idle_timeout => infinity }, {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts), @@ -87,7 +86,7 @@ idle_timeout_infinity(Config) -> idle_timeout_reset_on_data(Config) -> doc("Terminate when the idle timeout is reached."), ProtoOpts = #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, idle_timeout => 1000 }, {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts), @@ -116,7 +115,7 @@ idle_timeout_reset_on_data(Config) -> inactivity_timeout(Config) -> doc("Terminate when the inactivity timeout is reached."), ProtoOpts = #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, inactivity_timeout => 1000 }, {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts), @@ -135,7 +134,7 @@ initial_connection_window_size(Config) -> "connection window is larger than the default."), ConfiguredSize = 100000, ProtoOpts = #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, initial_connection_window_size => ConfiguredSize }, {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts), @@ -159,7 +158,7 @@ max_frame_size_sent(Config) -> "by the max_frame_size_sent configuration value."), MaxFrameSize = 20000, ProtoOpts = #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, max_frame_size_sent => MaxFrameSize }, {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts), @@ -200,7 +199,7 @@ max_frame_size_sent(Config) -> preface_timeout_infinity(Config) -> doc("Ensure infinity for preface_timeout is accepted."), ProtoOpts = #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, preface_timeout => infinity }, {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts), @@ -224,7 +223,7 @@ resp_iolist_body(Config) -> "include improper lists, empty lists and empty binaries. " "The original issue failed to split the body into frames properly."), ProtoOpts = #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))} + env => #{dispatch => init_dispatch(Config)} }, {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts), Port = ranch:get_port(?FUNCTION_NAME), @@ -244,7 +243,7 @@ resp_iolist_body(Config) -> settings_timeout_infinity(Config) -> doc("Ensure infinity for settings_timeout is accepted."), ProtoOpts = #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, settings_timeout => infinity }, {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts), diff --git a/test/http_SUITE.erl b/test/http_SUITE.erl index 87f8bed..a9a00d1 100644 --- a/test/http_SUITE.erl +++ b/test/http_SUITE.erl @@ -30,20 +30,27 @@ all() -> [{group, clear}]. groups() -> [{clear, [parallel], ct_helper:all(?MODULE)}]. -init_routes(_) -> [ - {"localhost", [ +init_per_group(Name, Config) -> + cowboy_test:init_http(Name, #{ + env => #{dispatch => init_dispatch(Config)} + }, Config). + +end_per_group(Name, _) -> + cowboy:stop_listener(Name). + +init_dispatch(_) -> + cowboy_router:compile([{"localhost", [ {"/", hello_h, []}, {"/echo/:key", echo_h, []}, {"/resp/:key[/:arg]", resp_h, []}, {"/set_options/:key", set_options_h, []} - ]} -]. + ]}]). chunked_false(Config) -> doc("Confirm the option chunked => false disables chunked " "transfer-encoding for HTTP/1.1 connections."), {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, chunked => false }), Port = ranch:get_port(?FUNCTION_NAME), @@ -68,11 +75,131 @@ chunked_false(Config) -> cowboy:stop_listener(?FUNCTION_NAME) end. +chunked_one_byte_at_a_time(Config) -> + doc("Confirm that chunked transfer-encoding works when " + "the body is received one byte at a time."), + Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])), + ChunkedBody = iolist_to_binary(do_chunked_body(50, Body, [])), + Client = raw_open(Config), + ok = raw_send(Client, + "POST /echo/read_body HTTP/1.1\r\n" + "Host: localhost\r\n" + "Transfer-encoding: chunked\r\n\r\n"), + _ = [begin + raw_send(Client, <>), + timer:sleep(10) + end || <> <= ChunkedBody], + Rest = case catch raw_recv_head(Client) of + {'EXIT', _} -> error(closed); + Data -> + {'HTTP/1.1', 200, _, Rest0} = cow_http:parse_status_line(Data), + {_, Rest1} = cow_http:parse_headers(Rest0), + Rest1 + end, + RestSize = byte_size(Rest), + <> = Body, + raw_expect_recv(Client, Expect). + +chunked_one_chunk_at_a_time(Config) -> + doc("Confirm that chunked transfer-encoding works when " + "the body is received one chunk at a time."), + Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])), + Chunks = do_chunked_body(50, Body, []), + Client = raw_open(Config), + ok = raw_send(Client, + "POST /echo/read_body HTTP/1.1\r\n" + "Host: localhost\r\n" + "Transfer-encoding: chunked\r\n\r\n"), + _ = [begin + raw_send(Client, Chunk), + timer:sleep(10) + end || Chunk <- Chunks], + Rest = case catch raw_recv_head(Client) of + {'EXIT', _} -> error(closed); + Data -> + {'HTTP/1.1', 200, _, Rest0} = cow_http:parse_status_line(Data), + {_, Rest1} = cow_http:parse_headers(Rest0), + Rest1 + end, + RestSize = byte_size(Rest), + <> = Body, + raw_expect_recv(Client, Expect). + +chunked_split_delay_in_chunk_body(Config) -> + doc("Confirm that chunked transfer-encoding works when " + "the body is received with a delay inside the chunks."), + Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])), + Chunks = do_chunked_body(50, Body, []), + Client = raw_open(Config), + ok = raw_send(Client, + "POST /echo/read_body HTTP/1.1\r\n" + "Host: localhost\r\n" + "Transfer-encoding: chunked\r\n\r\n"), + _ = [begin + case Chunk of + <<"0\r\n\r\n">> -> + raw_send(Client, Chunk); + _ -> + [Size, ChunkBody, <<>>] = binary:split(Chunk, <<"\r\n">>, [global]), + PartASize = rand:uniform(byte_size(ChunkBody)), + <> = ChunkBody, + raw_send(Client, [Size, <<"\r\n">>, PartA]), + timer:sleep(10), + raw_send(Client, [PartB, <<"\r\n">>]) + end + end || Chunk <- Chunks], + Rest = case catch raw_recv_head(Client) of + {'EXIT', _} -> error(closed); + Data -> + {'HTTP/1.1', 200, _, Rest0} = cow_http:parse_status_line(Data), + {_, Rest1} = cow_http:parse_headers(Rest0), + Rest1 + end, + RestSize = byte_size(Rest), + <> = Body, + raw_expect_recv(Client, Expect). + +chunked_split_delay_in_chunk_crlf(Config) -> + doc("Confirm that chunked transfer-encoding works when " + "the body is received with a delay inside the chunks end CRLF."), + Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])), + Chunks = do_chunked_body(50, Body, []), + Client = raw_open(Config), + ok = raw_send(Client, + "POST /echo/read_body HTTP/1.1\r\n" + "Host: localhost\r\n" + "Transfer-encoding: chunked\r\n\r\n"), + _ = [begin + Len = byte_size(Chunk) - (rand:uniform(2) - 1), + <> = Chunk, + raw_send(Client, Begin), + timer:sleep(10), + raw_send(Client, End) + end || Chunk <- Chunks], + Rest = case catch raw_recv_head(Client) of + {'EXIT', _} -> error(closed); + Data -> + {'HTTP/1.1', 200, _, Rest0} = cow_http:parse_status_line(Data), + {_, Rest1} = cow_http:parse_headers(Rest0), + Rest1 + end, + RestSize = byte_size(Rest), + <> = Body, + raw_expect_recv(Client, Expect). + +do_chunked_body(_, <<>>, Acc) -> + lists:reverse([cow_http_te:last_chunk()|Acc]); +do_chunked_body(ChunkSize0, Data, Acc) -> + ChunkSize = min(byte_size(Data), ChunkSize0), + <> = Data, + do_chunked_body(ChunkSize, Rest, + [iolist_to_binary(cow_http_te:chunk(Chunk))|Acc]). + http10_keepalive_false(Config) -> doc("Confirm the option http10_keepalive => false disables keep-alive " "completely for HTTP/1.0 connections."), {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, http10_keepalive => false }), Port = ranch:get_port(?FUNCTION_NAME), @@ -100,7 +227,7 @@ http10_keepalive_false(Config) -> idle_timeout_infinity(Config) -> doc("Ensure the idle_timeout option accepts the infinity value."), {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, request_timeout => 500, idle_timeout => infinity }), @@ -126,7 +253,7 @@ idle_timeout_infinity(Config) -> request_timeout_infinity(Config) -> doc("Ensure the request_timeout option accepts the infinity value."), {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, request_timeout => infinity }), Port = ranch:get_port(?FUNCTION_NAME), @@ -151,7 +278,7 @@ set_options_chunked_false(Config) -> "chunked transfer-encoding. This results in the closing of the " "connection after the current request."), {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, chunked => true }), Port = ranch:get_port(?FUNCTION_NAME), @@ -181,7 +308,7 @@ set_options_chunked_false_ignored(Config) -> "chunked transfer-encoding, and that it is ignored if the " "response is not streamed."), {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, chunked => true }), Port = ranch:get_port(?FUNCTION_NAME), @@ -206,7 +333,7 @@ set_options_idle_timeout(Config) -> "set to change how long Cowboy will wait before it closes the connection."), %% We start with a long timeout and then cut it short. {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, idle_timeout => 60000 }), Port = ranch:get_port(?FUNCTION_NAME), @@ -232,7 +359,7 @@ set_options_idle_timeout_only_applies_to_current_request(Config) -> doc("Confirm that changes to the idle_timeout option only apply to the current stream."), %% We start with a long timeout and then cut it short. {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, idle_timeout => 500 }), Port = ranch:get_port(?FUNCTION_NAME), @@ -270,7 +397,7 @@ set_options_idle_timeout_only_applies_to_current_request(Config) -> switch_protocol_flush(Config) -> doc("Confirm that switch_protocol does not flush unrelated messages."), ProtoOpts = #{ - env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + env => #{dispatch => init_dispatch(Config)}, stream_handlers => [switch_protocol_flush_h] }, {ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], ProtoOpts), diff --git a/test/old_http_SUITE.erl b/test/old_http_SUITE.erl index 0448407..9eef4fd 100644 --- a/test/old_http_SUITE.erl +++ b/test/old_http_SUITE.erl @@ -446,106 +446,6 @@ slowloris2(Config) -> {_, 408, _, _} = cow_http:parse_status_line(Data), ok. -te_chunked(Config) -> - Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])), - ConnPid = gun_open(Config), - Ref = gun:post(ConnPid, "/echo/body", [{<<"content-type">>, <<"text/plain">>}]), - gun:data(ConnPid, Ref, fin, Body), - {response, nofin, 200, _} = gun:await(ConnPid, Ref), - {ok, Body} = gun:await_body(ConnPid, Ref), - ok. - -do_body_to_chunks(_, <<>>, Acc) -> - lists:reverse([<<"0\r\n\r\n">>|Acc]); -do_body_to_chunks(ChunkSize, Body, Acc) -> - BodySize = byte_size(Body), - ChunkSize2 = case BodySize < ChunkSize of - true -> BodySize; - false -> ChunkSize - end, - << Chunk:ChunkSize2/binary, Rest/binary >> = Body, - ChunkSizeBin = integer_to_binary(ChunkSize2, 16), - do_body_to_chunks(ChunkSize, Rest, - [<< ChunkSizeBin/binary, "\r\n", Chunk/binary, "\r\n" >>|Acc]). - -te_chunked_chopped(Config) -> - Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])), - Body2 = iolist_to_binary(do_body_to_chunks(50, Body, [])), - ConnPid = gun_open(Config), - Ref = gun:post(ConnPid, "/echo/body", - [{<<"content-type">>, <<"text/plain">>}]), - _ = [begin - ok = dbg_send_raw(ConnPid, << C >>), - receive after 10 -> ok end - end || << C >> <= Body2], - {response, nofin, 200, _} = gun:await(ConnPid, Ref), - {ok, Body} = gun:await_body(ConnPid, Ref), - ok. - -te_chunked_delayed(Config) -> - Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])), - Chunks = do_body_to_chunks(50, Body, []), - ConnPid = gun_open(Config), - Ref = gun:post(ConnPid, "/echo/body", - [{<<"content-type">>, <<"text/plain">>}]), - _ = [begin - ok = dbg_send_raw(ConnPid, Chunk), - receive after 10 -> ok end - end || Chunk <- Chunks], - {response, nofin, 200, _} = gun:await(ConnPid, Ref), - {ok, Body} = gun:await_body(ConnPid, Ref), - ok. - -te_chunked_split_body(Config) -> - Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])), - Chunks = do_body_to_chunks(50, Body, []), - ConnPid = gun_open(Config), - Ref = gun:post(ConnPid, "/echo/body", - [{<<"content-type">>, <<"text/plain">>}]), - _ = [begin - case Chunk of - <<"0\r\n\r\n">> -> - ok = dbg_send_raw(ConnPid, Chunk); - _ -> - [Size, ChunkBody, <<>>] = - binary:split(Chunk, [<<"\r\n">>], [global]), - PartASize = random:uniform(byte_size(ChunkBody)), - <> = ChunkBody, - ok = dbg_send_raw(ConnPid, [Size, <<"\r\n">>, PartA]), - receive after 10 -> ok end, - ok = dbg_send_raw(ConnPid, [PartB, <<"\r\n">>]) - end - end || Chunk <- Chunks], - {response, nofin, 200, _} = gun:await(ConnPid, Ref), - {ok, Body} = gun:await_body(ConnPid, Ref), - ok. - -te_chunked_split_crlf(Config) -> - Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])), - Chunks = do_body_to_chunks(50, Body, []), - ConnPid = gun_open(Config), - Ref = gun:post(ConnPid, "/echo/body", - [{<<"content-type">>, <<"text/plain">>}]), - _ = [begin - %% Split in the newline just before the end of the chunk. - Len = byte_size(Chunk) - (random:uniform(2) - 1), - << Chunk2:Len/binary, End/binary >> = Chunk, - ok = dbg_send_raw(ConnPid, Chunk2), - receive after 10 -> ok end, - ok = dbg_send_raw(ConnPid, End) - end || Chunk <- Chunks], - {response, nofin, 200, _} = gun:await(ConnPid, Ref), - {ok, Body} = gun:await_body(ConnPid, Ref), - ok. - -te_identity(Config) -> - Body = list_to_binary(io_lib:format("~p", [lists:seq(1, 100)])), - ConnPid = gun_open(Config), - Ref = gun:post(ConnPid, "/echo/body", [], Body), - {response, nofin, 200, _} = gun:await(ConnPid, Ref), - {ok, Body} = gun:await_body(ConnPid, Ref), - ok. - dbg_send_raw(ConnPid, Data) -> #{ socket := Socket, -- cgit v1.2.3