diff --git a/lib/mint/http2.ex b/lib/mint/http2.ex index 21527787..c81c77dc 100644 --- a/lib/mint/http2.ex +++ b/lib/mint/http2.ex @@ -1911,8 +1911,9 @@ defmodule Mint.HTTP2 do end defp maybe_refill_conn(frames, conn) do - if conn.receive_window_remaining <= conn.receive_window_update_threshold do - increment = conn.receive_window_size - conn.receive_window_remaining + increment = conn.receive_window_size - conn.receive_window_remaining + + if conn.receive_window_remaining <= conn.receive_window_update_threshold and increment > 0 do [window_update(stream_id: 0, window_size_increment: increment) | frames] else frames @@ -1922,9 +1923,10 @@ defmodule Mint.HTTP2 do defp maybe_refill_stream(frames, conn, stream_id) do case Map.fetch(conn.streams, stream_id) do {:ok, stream} -> - if stream.receive_window_remaining <= conn.receive_window_update_threshold do - increment = stream.receive_window_size - stream.receive_window_remaining + increment = stream.receive_window_size - stream.receive_window_remaining + if stream.receive_window_remaining <= conn.receive_window_update_threshold and + increment > 0 do [ window_update(stream_id: stream_id, window_size_increment: increment) | frames ] @@ -2255,9 +2257,12 @@ defmodule Mint.HTTP2 do for {stream_id, stream} <- streams, stream.state in [:open, :half_closed_local, :reserved_remote], into: streams do - receive_window_size = stream.receive_window_size + diff - - {stream_id, %{stream | receive_window_size: receive_window_size}} + {stream_id, + %{ + stream + | receive_window_size: stream.receive_window_size + diff, + receive_window_remaining: stream.receive_window_remaining + diff + }} end end) diff --git a/test/mint/http2/conn_test.exs b/test/mint/http2/conn_test.exs index b323be46..78753e56 100644 --- a/test/mint/http2/conn_test.exs +++ b/test/mint/http2/conn_test.exs @@ -2247,6 +2247,40 @@ defmodule Mint.HTTP2Test do end describe "settings" do + @tag connect_options: [ + receive_window_update_threshold: 8, + client_settings: [initial_window_size: 16] + ] + test "shrinking the initial window size keeps the remaining credit in sync", + %{conn: conn} do + {conn, ref} = open_request(conn) + + assert_recv_frames [headers(stream_id: stream_id)] + + assert {:ok, %HTTP2{} = conn, _responses} = + stream_frames(conn, [ + {:headers, stream_id, [{":status", "200"}], [:end_headers]}, + data(stream_id: stream_id, data: "123456") + ]) + + assert conn.streams[stream_id].receive_window_remaining == 10 + + {:ok, conn} = HTTP2.put_settings(conn, initial_window_size: 8) + assert_recv_frames [settings(params: [initial_window_size: 8])] + + assert {:ok, %HTTP2{} = conn, []} = + stream_frames(conn, [settings(flags: set_flags(:settings, [:ack]), params: [])]) + + assert conn.streams[stream_id].receive_window_size == 8 + assert conn.streams[stream_id].receive_window_remaining == 2 + + assert {:ok, %HTTP2{} = conn, [{:data, ^ref, "12"}]} = + stream_frames(conn, [data(stream_id: stream_id, data: "12")]) + + assert conn.streams[stream_id].receive_window_remaining == 8 + assert_recv_frames [window_update(stream_id: ^stream_id, window_size_increment: 8)] + end + test "put_settings/2 can be used to send settings to server", %{conn: conn} do {:ok, conn} = HTTP2.put_settings(conn, max_concurrent_streams: 123, initial_window_size: 1_000)