Просмотр исходного кода

Move some tests out of the old HTTP test suite

And additional minor tweaks.
Loïc Hoguin 6 лет назад
Родитель
Сommit
dc240adc1e
4 измененных файлов с 154 добавлено и 126 удалено
  1. 2 0
      test/cowboy_test.erl
  2. 12 13
      test/http2_SUITE.erl
  3. 140 13
      test/http_SUITE.erl
  4. 0 100
      test/old_http_SUITE.erl

+ 2 - 0
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.

+ 12 - 13
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),

+ 140 - 13
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, <<C>>),
+		timer:sleep(10)
+	end || <<C>> <= 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),
+	<<Rest:RestSize/binary, Expect/bits>> = 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),
+	<<Rest:RestSize/binary, Expect/bits>> = 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)),
+				<<PartA:PartASize/binary, PartB/binary>> = 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),
+	<<Rest:RestSize/binary, Expect/bits>> = 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),
+		<<Begin:Len/binary, End/binary>> = 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),
+	<<Rest:RestSize/binary, Expect/bits>> = 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),
+	<<Chunk:ChunkSize/binary, Rest/binary>> = 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),

+ 0 - 100
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)),
-				<<PartA:PartASize/binary, PartB/binary>> = 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,