mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
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.
This commit is contained in:
parent
01e13c0846
commit
eafca21198
3 changed files with 20 additions and 10 deletions
|
@ -1051,8 +1051,9 @@ stream_reset(State, StreamID, StreamError={internal_error, _, _}) ->
|
||||||
% stream_terminate(State#state{out_state=done}, StreamID, StreamError).
|
% stream_terminate(State#state{out_state=done}, StreamID, StreamError).
|
||||||
stream_terminate(State, StreamID, StreamError).
|
stream_terminate(State, StreamID, StreamError).
|
||||||
|
|
||||||
stream_terminate(State0=#state{opts=Opts, in_state=InState, out_streamid=OutStreamID,
|
stream_terminate(State0=#state{opts=Opts, in_streamid=InStreamID, in_state=InState,
|
||||||
out_state=OutState, streams=Streams0, children=Children0}, StreamID, Reason) ->
|
out_streamid=OutStreamID, out_state=OutState, streams=Streams0,
|
||||||
|
children=Children0}, StreamID, Reason) ->
|
||||||
#stream{version=Version} = lists:keyfind(StreamID, #stream.id, Streams0),
|
#stream{version=Version} = lists:keyfind(StreamID, #stream.id, Streams0),
|
||||||
State1 = #state{streams=Streams1} = case OutState of
|
State1 = #state{streams=Streams1} = case OutState of
|
||||||
wait when element(1, Reason) =:= internal_error ->
|
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.
|
%% @todo Only do this if Current =:= StreamID.
|
||||||
MaxSkipBodyLength = maps:get(max_skip_body_length, Opts, 1000000),
|
MaxSkipBodyLength = maps:get(max_skip_body_length, Opts, 1000000),
|
||||||
case InState of
|
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);
|
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);
|
terminate(State#state{streams=Streams, children=Children}, skip_body_too_large);
|
||||||
_ ->
|
_ ->
|
||||||
%% Move on to the next stream.
|
%% Move on to the next stream.
|
||||||
|
|
|
@ -464,9 +464,15 @@ rest_keepalive(Config) ->
|
||||||
rest_keepalive_post(Config) ->
|
rest_keepalive_post(Config) ->
|
||||||
ConnPid = gun_open(Config),
|
ConnPid = gun_open(Config),
|
||||||
Refs = [begin
|
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!"),
|
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!"),
|
gun:data(ConnPid, Ref2, fin, "Hello world!"),
|
||||||
{Ref1, Ref2}
|
{Ref1, Ref2}
|
||||||
end || _ <- lists:seq(1, 5)],
|
end || _ <- lists:seq(1, 5)],
|
||||||
|
@ -550,8 +556,8 @@ rest_resource_etags(Config) ->
|
||||||
{200, <<"\"etag-header-value\"">>, "tuple-strong"},
|
{200, <<"\"etag-header-value\"">>, "tuple-strong"},
|
||||||
{200, <<"W/\"etag-header-value\"">>, "binary-weak-quoted"},
|
{200, <<"W/\"etag-header-value\"">>, "binary-weak-quoted"},
|
||||||
{200, <<"\"etag-header-value\"">>, "binary-strong-quoted"},
|
{200, <<"\"etag-header-value\"">>, "binary-strong-quoted"},
|
||||||
{400, false, "binary-strong-unquoted"},
|
{500, false, "binary-strong-unquoted"},
|
||||||
{400, false, "binary-weak-unquoted"}
|
{500, false, "binary-weak-unquoted"}
|
||||||
],
|
],
|
||||||
_ = [{Status, ETag, Type} = begin
|
_ = [{Status, ETag, Type} = begin
|
||||||
{Ret, RespETag} = rest_resource_get_etag(Config, Type),
|
{Ret, RespETag} = rest_resource_get_etag(Config, Type),
|
||||||
|
|
|
@ -17,7 +17,7 @@ content_types_provided(Req, State) ->
|
||||||
{[{{<<"text">>, <<"plain">>, '*'}, get_text_plain}], Req, State}.
|
{[{{<<"text">>, <<"plain">>, '*'}, get_text_plain}], Req, State}.
|
||||||
|
|
||||||
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
|
Body = if
|
||||||
Param == '*' ->
|
Param == '*' ->
|
||||||
<<"'*'">>;
|
<<"'*'">>;
|
||||||
|
@ -31,5 +31,6 @@ get_text_plain(Req, State) ->
|
||||||
content_types_accepted(Req, State) ->
|
content_types_accepted(Req, State) ->
|
||||||
{[{{<<"text">>, <<"plain">>, '*'}, put_text_plain}], 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}.
|
{true, Req, State}.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue