aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2019-09-12 14:09:14 +0200
committerLoïc Hoguin <[email protected]>2019-09-12 14:09:14 +0200
commit6baeb05bdbf0254c877cb9b9b8429e6bb1a98d44 (patch)
tree32d6e99ac76610cb72587282a040ed3f8fa0f1f7
parent2c9892cd6194d57177f9d092ffbfb34c9ecd8216 (diff)
downloadcowlib-6baeb05bdbf0254c877cb9b9b8429e6bb1a98d44.tar.gz
cowlib-6baeb05bdbf0254c877cb9b9b8429e6bb1a98d44.tar.bz2
cowlib-6baeb05bdbf0254c877cb9b9b8429e6bb1a98d44.zip
Fix a bug where final empty frames were ignored
When a final empty frame was queued and the data sent made the window go to 0 and the remote end didn't increase the window anymore, the final empty frame could get stuck in the queue and never sent. Also ensure that we never queue empty non-final data.
-rw-r--r--src/cow_http2_machine.erl21
1 files changed, 19 insertions, 2 deletions
diff --git a/src/cow_http2_machine.erl b/src/cow_http2_machine.erl
index b51aeac..4cda558 100644
--- a/src/cow_http2_machine.erl
+++ b/src/cow_http2_machine.erl
@@ -1218,6 +1218,17 @@ send_data(Stream0, State0) ->
send_data_for_one_stream(Stream=#stream{local=nofin, local_buffer_size=0,
local_trailers=Trailers}, State, SendAcc) when Trailers =/= undefined ->
{ok, Stream, State, lists:reverse([{trailers, Trailers}|SendAcc])};
+send_data_for_one_stream(Stream=#stream{local=nofin, local_buffer=Q0, local_buffer_size=0},
+ State, SendAcc) ->
+ case queue:len(Q0) of
+ 0 ->
+ {ok, Stream, State, lists:reverse(SendAcc)};
+ 1 ->
+ %% We know there is a final empty data frame in the queue.
+ %% We need to mark the stream as complete.
+ {{value, {fin, 0, _}}, Q} = queue:out(Q0),
+ {ok, Stream#stream{local=fin, local_buffer=Q}, State, lists:reverse(SendAcc)}
+ end;
send_data_for_one_stream(Stream=#stream{local=IsFin, local_window=StreamWindow,
local_buffer_size=BufferSize}, State=#http2_machine{local_window=ConnWindow}, SendAcc)
when ConnWindow =< 0; IsFin =:= fin; StreamWindow =< 0; BufferSize =:= 0 ->
@@ -1283,8 +1294,14 @@ queue_data(Stream=#stream{local_buffer=Q0, local_buffer_size=Size0}, IsFin, Data
{sendfile, _, Bytes, _} -> Bytes;
{data, Iolist} -> iolist_size(Iolist)
end,
- Q = queue:In({IsFin, DataSize, Data}, Q0),
- Stream#stream{local_buffer=Q, local_buffer_size=Size0 + DataSize}.
+ %% Never queue non-final empty data frames.
+ case {DataSize, IsFin} of
+ {0, nofin} ->
+ Stream;
+ _ ->
+ Q = queue:In({IsFin, DataSize, Data}, Q0),
+ Stream#stream{local_buffer=Q, local_buffer_size=Size0 + DataSize}
+ end.
%% Public interface to update the flow control window.
%%