From 6baeb05bdbf0254c877cb9b9b8429e6bb1a98d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 12 Sep 2019 14:09:14 +0200 Subject: 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. --- src/cow_http2_machine.erl | 21 +++++++++++++++++++-- 1 file 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. %% -- cgit v1.2.3