Skip to content

Commit 761e202

Browse files
authored
Table size fixes (#21)
Fixes #20. evict_to_size should only evict if it needs to. Previously, it would *always* evict at least one record even if size was <= new_size. Also, ensure we *always* send a dynamic table resize command. Closer reading of RFC9113§4.3.1para¶4 (https://www.rfc-editor.org/rfc/rfc9113.html#section-4.3.1) suggests that we should always send a dynamic resize.
1 parent f59c2d8 commit 761e202

File tree

3 files changed

+63
-21
lines changed

3 files changed

+63
-21
lines changed

lib/hpax/table.ex

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -264,15 +264,6 @@ defmodule HPAX.Table do
264264
In all cases, the table's `:protocol_max_table_size` is updated accordingly
265265
"""
266266
@spec resize(t(), non_neg_integer()) :: t()
267-
def resize(%__MODULE__{max_table_size: max_table_size} = table, new_protocol_max_table_size)
268-
when new_protocol_max_table_size >= max_table_size do
269-
%__MODULE__{
270-
table
271-
| protocol_max_table_size: new_protocol_max_table_size,
272-
max_table_size: new_protocol_max_table_size
273-
}
274-
end
275-
276267
def resize(%__MODULE__{} = table, new_protocol_max_table_size) do
277268
pending_minimum_resize =
278269
case table.pending_minimum_resize do
@@ -317,6 +308,8 @@ defmodule HPAX.Table do
317308

318309
# Removes records as necessary to have the total size of entries within the table be less than
319310
# or equal to the specified value. Does not change the table's max size.
311+
defp evict_to_size(%__MODULE__{size: size} = table, new_size) when size <= new_size, do: table
312+
320313
defp evict_to_size(%__MODULE__{entries: entries, size: size} = table, new_size) do
321314
{new_entries_reversed, new_size} =
322315
evict_towards_size(Enum.reverse(entries), size, new_size)

test/hpax/table_test.exs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,25 +80,44 @@ defmodule HPAX.TableTest do
8080
assert table.protocol_max_table_size == 8192
8181
end
8282

83-
test "decreasing the protocol max table size not below the max table size" do
83+
test "decreasing the protocol max table size but above table size" do
84+
table = Table.new(4096, :never)
85+
table = Table.add(table, "aaaa", "AAAA")
86+
table = Table.resize(table, 2048)
87+
assert table.size == 40
88+
assert table.max_table_size == 2048
89+
assert table.protocol_max_table_size == 2048
90+
end
91+
92+
test "decreasing the protocol max table size below current size should evict" do
8493
table = Table.new(4096, :never)
8594
table = Table.add(table, "aaaa", "AAAA")
8695
table = Table.add(table, "bbbb", "BBBB")
96+
table = Table.resize(table, 60)
97+
assert table.size == 40
98+
assert table.max_table_size == 60
99+
assert table.protocol_max_table_size == 60
100+
end
101+
end
102+
103+
describe "dynamically resizing" do
104+
test "decreasing the max table size but above table size" do
105+
table = Table.new(4096, :never)
106+
table = Table.add(table, "aaaa", "AAAA")
87107
table = Table.dynamic_resize(table, 2048)
88-
table = Table.resize(table, 6000)
89108
assert table.size == 40
90-
assert table.max_table_size == 6000
91-
assert table.protocol_max_table_size == 6000
109+
assert table.max_table_size == 2048
110+
assert table.protocol_max_table_size == 4096
92111
end
93112

94-
test "decreasing the protocol max table size below the max table size" do
113+
test "decreasing the protocol max table size below current size should evict" do
95114
table = Table.new(4096, :never)
96115
table = Table.add(table, "aaaa", "AAAA")
97116
table = Table.add(table, "bbbb", "BBBB")
98-
table = Table.resize(table, 40)
117+
table = Table.dynamic_resize(table, 60)
99118
assert table.size == 40
100-
assert table.max_table_size == 40
101-
assert table.protocol_max_table_size == 40
119+
assert table.max_table_size == 60
120+
assert table.protocol_max_table_size == 4096
102121
end
103122
end
104123
end

test/hpax_test.exs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ defmodule HPAXTest do
109109

110110
property "encode/3 prepends dynamic resizes at the start of a block" do
111111
enc_table = HPAX.new(20_000)
112-
# Start with a non-empty decode table
113112
dec_table = HPAX.new(20_000)
114113

115114
# Put a record in both to prime the pump. The table sizes should match
@@ -120,14 +119,45 @@ defmodule HPAXTest do
120119
assert enc_table.max_table_size == 20_000
121120
assert dec_table.max_table_size == 20_000
122121

123-
# Encode a record after resizing the table. We expect a dynamic resize to be
124-
# encoded and the for two table sizes to be identical after decoding
122+
# Scenario 1: Simulate the decoder growing the table via settings
123+
124+
# First, the decoder resizes its table to some maximum size
125+
dec_table = HPAX.resize(dec_table, 40_000)
126+
127+
# It then communicates that size to the encoder, who chooses a smaller size
128+
enc_table = HPAX.resize(enc_table, 30_000)
129+
130+
# Now, encode a header
131+
{encoded, enc_table} = HPAX.encode([{:store, "lame", "LAME"}], enc_table)
132+
encoded = IO.iodata_to_binary(encoded)
133+
134+
# Ensure that we encoded a resize on the wire
135+
assert <<0b001::3, rest::bitstring>> = encoded
136+
assert {:ok, 30_000, _rest} = HPAX.Types.decode_integer(rest, 5)
137+
138+
# Finally, ensure that the decoder makes proper sense of this encoding and that it resizes
139+
# back down to the size chosen by the encoder
140+
assert {:ok, _decoded, dec_table} = HPAX.decode(encoded, dec_table)
141+
assert dec_table.size == enc_table.size
142+
assert enc_table.max_table_size == 30_000
143+
assert dec_table.max_table_size == 30_000
144+
145+
# Scenario 2: Simulate the decoder shrinking the table ia settings
146+
147+
# First, the decoder resizes its table to some maximum size
148+
dec_table = HPAX.resize(dec_table, 10_000)
149+
150+
# It then communicates that size to the encoder, who chooses a smaller size
125151
enc_table = HPAX.resize(enc_table, 0)
152+
153+
# It then changes its mind and goes back up to a size still smaller than the decoder's choice
126154
enc_table = HPAX.resize(enc_table, 1234)
155+
156+
# Now, encode a header
127157
{encoded, enc_table} = HPAX.encode([{:store, "lame", "LAME"}], enc_table)
128158
encoded = IO.iodata_to_binary(encoded)
129159

130-
# Ensure that we see two resizes in order
160+
# Ensure that we encoded two resizes in order on the wire
131161
assert <<0b001::3, rest::bitstring>> = encoded
132162
assert {:ok, 0, rest} = HPAX.Types.decode_integer(rest, 5)
133163
assert <<0b001::3, rest::bitstring>> = rest

0 commit comments

Comments
 (0)