Browse Source

Fix HTTP/1.1 pipelining

Cases where a request body was involved could sometimes
fail depending on timing. Also fix all of the old
http_SUITE tests.
Loïc Hoguin 7 years ago
parent
commit
eafca21198
3 changed files with 20 additions and 10 deletions
  1. 7 4
      src/cowboy_http.erl
  2. 10 4
      test/http_SUITE.erl
  3. 3 2
      test/http_SUITE_data/rest_param_all.erl

+ 7 - 4
src/cowboy_http.erl

@@ -1051,8 +1051,9 @@ stream_reset(State, StreamID, StreamError={internal_error, _, _}) ->
 %	stream_terminate(State#state{out_state=done}, StreamID, StreamError).
 	stream_terminate(State, StreamID, StreamError).
 
-stream_terminate(State0=#state{opts=Opts, in_state=InState, out_streamid=OutStreamID,
-		out_state=OutState, streams=Streams0, children=Children0}, StreamID, Reason) ->
+stream_terminate(State0=#state{opts=Opts, in_streamid=InStreamID, in_state=InState,
+		out_streamid=OutStreamID, out_state=OutState, streams=Streams0,
+		children=Children0}, StreamID, Reason) ->
 	#stream{version=Version} = lists:keyfind(StreamID, #stream.id, Streams0),
 	State1 = #state{streams=Streams1} = case OutState of
 		wait when element(1, Reason) =:= internal_error ->
@@ -1084,9 +1085,11 @@ stream_terminate(State0=#state{opts=Opts, in_state=InState, out_streamid=OutStre
 	%% @todo Only do this if Current =:= StreamID.
 	MaxSkipBodyLength = maps:get(max_skip_body_length, Opts, 1000000),
 	case InState of
-		#ps_body{length=undefined} ->
+		#ps_body{length=undefined}
+				when InStreamID =:= OutStreamID ->
 			terminate(State#state{streams=Streams, children=Children}, skip_body_unknown_length);
-		#ps_body{length=Len, received=Received} when Received + MaxSkipBodyLength < Len ->
+		#ps_body{length=Len, received=Received}
+				when InStreamID =:= OutStreamID, Received + MaxSkipBodyLength < Len ->
 			terminate(State#state{streams=Streams, children=Children}, skip_body_too_large);
 		_ ->
 			%% Move on to the next stream.

+ 10 - 4
test/http_SUITE.erl

@@ -464,9 +464,15 @@ rest_keepalive(Config) ->
 rest_keepalive_post(Config) ->
 	ConnPid = gun_open(Config),
 	Refs = [begin
-		Ref1 = gun:post(ConnPid, "/forbidden_post", [{<<"content-type">>, <<"text/plain">>}]),
+		Ref1 = gun:post(ConnPid, "/forbidden_post", [
+			{<<"content-type">>, <<"text/plain">>},
+			{<<"content-length">>, <<"12">>}
+		]),
 		gun:data(ConnPid, Ref1, fin, "Hello world!"),
-		Ref2 = gun:post(ConnPid, "/simple_post", [{<<"content-type">>, <<"text/plain">>}]),
+		Ref2 = gun:post(ConnPid, "/simple_post", [
+			{<<"content-type">>, <<"text/plain">>},
+			{<<"content-length">>, <<"12">>}
+		]),
 		gun:data(ConnPid, Ref2, fin, "Hello world!"),
 		{Ref1, Ref2}
 	end || _ <- lists:seq(1, 5)],
@@ -550,8 +556,8 @@ rest_resource_etags(Config) ->
 		{200, <<"\"etag-header-value\"">>, "tuple-strong"},
 		{200, <<"W/\"etag-header-value\"">>, "binary-weak-quoted"},
 		{200, <<"\"etag-header-value\"">>, "binary-strong-quoted"},
-		{400, false, "binary-strong-unquoted"},
-		{400, false, "binary-weak-unquoted"}
+		{500, false, "binary-strong-unquoted"},
+		{500, false, "binary-weak-unquoted"}
 	],
 	_ = [{Status, ETag, Type} = begin
 		{Ret, RespETag} = rest_resource_get_etag(Config, Type),

+ 3 - 2
test/http_SUITE_data/rest_param_all.erl

@@ -17,7 +17,7 @@ content_types_provided(Req, State) ->
 	{[{{<<"text">>, <<"plain">>, '*'}, get_text_plain}], Req, State}.
 
 get_text_plain(Req, State) ->
-	{_, _, Param} = maps:get(media_type, Req, {{<<"text">>, <<"plain">>}, []}),
+	{_, _, Param} = maps:get(media_type, Req, {<<"text">>, <<"plain">>, []}),
 	Body = if
 		Param == '*' ->
 			<<"'*'">>;
@@ -31,5 +31,6 @@ get_text_plain(Req, State) ->
 content_types_accepted(Req, State) ->
 	{[{{<<"text">>, <<"plain">>, '*'}, put_text_plain}], Req, State}.
 
-put_text_plain(Req, State) ->
+put_text_plain(Req0, State) ->
+	{ok, _, Req} = cowboy_req:read_body(Req0),
 	{true, Req, State}.