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

Add a max_keepalive HTTP protocol option

Based on the patch by Louis-Philippe Gauthier.
Loïc Hoguin 13 лет назад
Родитель
Сommit
72d91583b9
2 измененных файлов с 37 добавлено и 10 удалено
  1. 10 4
      src/cowboy_http_protocol.erl
  2. 27 6
      test/http_SUITE.erl

+ 10 - 4
src/cowboy_http_protocol.erl

@@ -50,6 +50,8 @@
 	urldecode :: {fun((binary(), T) -> binary()), T},
 	req_empty_lines = 0 :: integer(),
 	max_empty_lines :: integer(),
+	req_keepalive = 1 :: integer(),
+	max_keepalive :: integer(),
 	max_line_length :: integer(),
 	timeout :: timeout(),
 	buffer = <<>> :: binary(),
@@ -73,6 +75,7 @@ start_link(ListenerPid, Socket, Transport, Opts) ->
 init(ListenerPid, Socket, Transport, Opts) ->
 	Dispatch = proplists:get_value(dispatch, Opts, []),
 	MaxEmptyLines = proplists:get_value(max_empty_lines, Opts, 5),
+	MaxKeepalive = proplists:get_value(max_keepalive, Opts, infinity),
 	MaxLineLength = proplists:get_value(max_line_length, Opts, 4096),
 	Timeout = proplists:get_value(timeout, Opts, 5000),
 	URLDecDefault = {fun cowboy_http:urldecode/2, crash},
@@ -80,7 +83,8 @@ init(ListenerPid, Socket, Transport, Opts) ->
 	ok = cowboy:accept_ack(ListenerPid),
 	wait_request(#state{listener=ListenerPid, socket=Socket, transport=Transport,
 		dispatch=Dispatch, max_empty_lines=MaxEmptyLines,
-		max_line_length=MaxLineLength, timeout=Timeout, urldecode=URLDec}).
+		max_keepalive=MaxKeepalive, max_line_length=MaxLineLength,
+		timeout=Timeout, urldecode=URLDec}).
 
 %% @private
 -spec parse_request(#state{}) -> ok | none().
@@ -351,13 +355,15 @@ terminate_request(HandlerState, Req, State) ->
 
 -spec next_request(#http_req{}, #state{}, any()) -> ok | none().
 next_request(Req=#http_req{connection=Conn, buffer=Buffer},
-		State, HandlerRes) ->
+		State=#state{req_keepalive=Keepalive, max_keepalive=MaxKeepalive},
+		HandlerRes) ->
 	BodyRes = ensure_body_processed(Req),
 	RespRes = ensure_response(Req),
 	case {HandlerRes, BodyRes, RespRes, Conn} of
-		{ok, ok, ok, keepalive} ->
+		{ok, ok, ok, keepalive} when Keepalive < MaxKeepalive ->
 			?MODULE:parse_request(State#state{
-				buffer=Buffer, req_empty_lines=0});
+				buffer=Buffer, req_empty_lines=0,
+				req_keepalive=Keepalive + 1});
 		_Closed ->
 			terminate(State)
 	end.

+ 27 - 6
test/http_SUITE.erl

@@ -19,9 +19,9 @@
 -export([all/0, groups/0, init_per_suite/1, end_per_suite/1,
 	init_per_group/2, end_per_group/2]). %% ct.
 -export([chunked_response/1, headers_dupe/1, headers_huge/1,
-	keepalive_nl/1, nc_rand/1, nc_zero/1, pipeline/1, raw/1,
-	set_resp_header/1, set_resp_overwrite/1, set_resp_body/1,
-	response_as_req/1]). %% http.
+	keepalive_nl/1, max_keepalive/1, nc_rand/1, nc_zero/1,
+	pipeline/1, raw/1, set_resp_header/1, set_resp_overwrite/1,
+	set_resp_body/1, response_as_req/1]). %% http.
 -export([http_200/1, http_404/1]). %% http and https.
 -export([http_10_hostless/1]). %% misc.
 -export([rest_simple/1, rest_keepalive/1]). %% rest.
@@ -34,7 +34,7 @@ all() ->
 groups() ->
 	BaseTests = [http_200, http_404],
 	[{http, [], [chunked_response, headers_dupe, headers_huge,
-		keepalive_nl, nc_rand, nc_zero, pipeline, raw,
+		keepalive_nl, max_keepalive, nc_rand, nc_zero, pipeline, raw,
 		set_resp_header, set_resp_overwrite,
 		set_resp_body, response_as_req] ++ BaseTests},
 	{https, [], BaseTests},
@@ -55,7 +55,8 @@ init_per_group(http, Config) ->
 	Port = 33080,
 	cowboy:start_listener(http, 100,
 		cowboy_tcp_transport, [{port, Port}],
-		cowboy_http_protocol, [{dispatch, init_http_dispatch()}]
+		cowboy_http_protocol, [{max_keepalive, 50},
+			{dispatch, init_http_dispatch()}]
 	),
 	[{scheme, "http"}, {port, Port}|Config];
 init_per_group(https, Config) ->
@@ -148,7 +149,7 @@ keepalive_nl(Config) ->
 	{port, Port} = lists:keyfind(port, 1, Config),
 	{ok, Socket} = gen_tcp:connect("localhost", Port,
 		[binary, {active, false}, {packet, raw}]),
-	ok = keepalive_nl_loop(Socket, 100),
+	ok = keepalive_nl_loop(Socket, 10),
 	ok = gen_tcp:close(Socket).
 
 keepalive_nl_loop(_Socket, 0) ->
@@ -162,6 +163,26 @@ keepalive_nl_loop(Socket, N) ->
 	ok = gen_tcp:send(Socket, "\r\n"), %% extra nl
 	keepalive_nl_loop(Socket, N - 1).
 
+max_keepalive(Config) ->
+	{port, Port} = lists:keyfind(port, 1, Config),
+	{ok, Socket} = gen_tcp:connect("localhost", Port,
+		[binary, {active, false}, {packet, raw}]),
+	ok = max_keepalive_loop(Socket, 50),
+	{error, closed} = gen_tcp:recv(Socket, 0, 1000).
+
+max_keepalive_loop(_Socket, 0) ->
+	ok;
+max_keepalive_loop(Socket, N) ->
+	ok = gen_tcp:send(Socket, "GET / HTTP/1.1\r\n"
+		"Host: localhost\r\nConnection: keep-alive\r\n\r\n"),
+	{ok, Data} = gen_tcp:recv(Socket, 0, 6000),
+	{0, 12} = binary:match(Data, <<"HTTP/1.1 200">>),
+	case N of
+		1 -> {_, _} = binary:match(Data, <<"Connection: close">>);
+		N -> nomatch = binary:match(Data, <<"Connection: close">>)
+	end,
+	keepalive_nl_loop(Socket, N - 1).
+
 nc_rand(Config) ->
 	nc_reqs(Config, "/dev/urandom").