Packages
bandit
0.7.5
1.12.0
1.11.1
1.11.0
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
retired
1.9.0
1.8.0
1.7.0
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.2
1.4.1
1.4.0
1.3.0
1.2.3
1.2.2
1.2.1
1.2.0
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-pre.18
1.0.0-pre.17
1.0.0-pre.16
1.0.0-pre.15
1.0.0-pre.14
1.0.0-pre.13
1.0.0-pre.12
1.0.0-pre.11
1.0.0-pre.10
1.0.0-pre.9
1.0.0-pre.8
1.0.0-pre.7
1.0.0-pre.6
1.0.0-pre.5
1.0.0-pre.4
1.0.0-pre.3
1.0.0-pre.2
1.0.0-pre.1
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.2.3
0.2.2
0.2.1
0.2.0
0.1.1
0.1.0
A pure-Elixir HTTP server built for Plug & WebSock apps
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
test/bandit/http2/frame_deserialization_test.exs
defmodule HTTP2FrameDeserializationTest do
use ExUnit.Case, async: true
alias Bandit.HTTP2.{Errors, Frame, Settings}
describe "insufficient data" do
test "asks for more" do
frame = <<0, 0, 0, 4>>
assert Frame.deserialize(frame, 16_384) == {{:more, <<0, 0, 0, 4>>}, <<>>}
end
test "ends the stream when empty" do
frame = <<>>
assert Frame.deserialize(frame, 16_384) == nil
end
end
describe "extra data" do
test "returns extra data" do
frame = <<0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Data{stream_id: 1, data: <<>>}}, <<1, 2, 3>>}
end
end
describe "unknown frame types" do
test "returns an Unknown frame" do
frame = <<0, 0, 3, 254, 123, 0, 0, 0, 234, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok,
%Frame.Unknown{type: 254, flags: 123, stream_id: 234, payload: <<1, 2, 3>>}},
<<>>}
end
end
describe "DATA frames" do
test "deserializes frames with padding" do
frame = <<0, 0, 6, 0, 0x08, 0, 0, 0, 1, 2, 1, 2, 3, 4, 5>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Data{stream_id: 1, end_stream: false, data: <<1, 2, 3>>}}, <<>>}
end
test "deserializes frames without padding" do
frame = <<0, 0, 3, 0, 0, 0, 0, 0, 1, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Data{stream_id: 1, end_stream: false, data: <<1, 2, 3>>}}, <<>>}
end
test "sets end_stream" do
frame = <<0, 0, 3, 0, 0x01, 0, 0, 0, 1, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Data{stream_id: 1, end_stream: true, data: <<1, 2, 3>>}}, <<>>}
end
test "rejects frames with 0 stream_id" do
frame = <<0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:error, {:connection, 1, "DATA frame with zero stream_id (RFC9113§6.1)"}}, <<>>}
end
test "rejects frames with invalid padding" do
frame = <<0, 0, 6, 0, 0x08, 0, 0, 0, 1, 6, 1, 2, 3, 4, 5>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, 1, "DATA frame with invalid padding length (RFC9113§6.1)"}}, <<>>}
end
end
describe "HEADERS frames" do
test "deserializes frames with padding and priority" do
frame = <<0, 0, 11, 1, 0x28, 0, 0, 0, 1, 2, 1::1, 12::31, 34, 1, 2, 3, 4, 5>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok,
%Frame.Headers{
stream_id: 1,
end_stream: false,
end_headers: false,
exclusive_dependency: true,
stream_dependency: 12,
weight: 34,
fragment: <<1, 2, 3>>
}}, <<>>}
end
test "deserializes frames with padding but not priority" do
frame = <<0, 0, 6, 1, 0x08, 0, 0, 0, 1, 2, 1, 2, 3, 4, 5>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok,
%Frame.Headers{
stream_id: 1,
end_stream: false,
end_headers: false,
fragment: <<1, 2, 3>>
}}, <<>>}
end
test "deserializes frames with priority but not padding" do
frame = <<0, 0, 8, 1, 0x20, 0, 0, 0, 1, 0::1, 12::31, 34, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok,
%Frame.Headers{
stream_id: 1,
end_stream: false,
end_headers: false,
exclusive_dependency: false,
stream_dependency: 12,
weight: 34,
fragment: <<1, 2, 3>>
}}, <<>>}
end
test "deserializes frames with neither priority nor padding" do
frame = <<0, 0, 3, 1, 0x00, 0, 0, 0, 1, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok,
%Frame.Headers{
stream_id: 1,
end_stream: false,
end_headers: false,
fragment: <<1, 2, 3>>
}}, <<>>}
end
test "sets end_stream" do
frame = <<0, 0, 3, 1, 0x01, 0, 0, 0, 1, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok,
%Frame.Headers{
stream_id: 1,
end_stream: true,
end_headers: false,
fragment: <<1, 2, 3>>
}}, <<>>}
end
test "sets end_headers" do
frame = <<0, 0, 3, 1, 0x04, 0, 0, 0, 1, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok,
%Frame.Headers{
stream_id: 1,
end_stream: false,
end_headers: true,
fragment: <<1, 2, 3>>
}}, <<>>}
end
test "rejects frames with 0 stream_id" do
frame = <<0, 0, 3, 1, 0x04, 0, 0, 0, 0, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:error, {:connection, 1, "HEADERS frame with zero stream_id (RFC9113§6.2)"}},
<<>>}
end
test "rejects frames with invalid padding and priority" do
frame = <<0, 0, 11, 1, 0x28, 0, 0, 0, 1, 6, 1::1, 12::31, 34, 1, 2, 3, 4, 5>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, 1, "HEADERS frame with invalid padding length (RFC9113§6.2)"}},
<<>>}
end
test "rejects frames with invalid padding and not priority" do
frame = <<0, 0, 6, 1, 0x08, 0, 0, 0, 1, 6, 1, 2, 3, 4, 5>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, 1, "HEADERS frame with invalid padding length (RFC9113§6.2)"}},
<<>>}
end
end
describe "PRIORITY frames" do
test "deserializes frames" do
frame = <<0, 0, 5, 2, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Priority{stream_id: 1, dependent_stream_id: 2, weight: 3}}, <<>>}
end
test "rejects frames with 0 stream_id" do
frame = <<0, 0, 5, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:error, {:connection, 1, "PRIORITY frame with zero stream_id (RFC9113§6.3)"}},
<<>>}
end
test "rejects frames with invalid size" do
frame = <<0, 0, 6, 2, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 0>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, 6, "Invalid payload size in PRIORITY frame (RFC9113§6.3)"}}, <<>>}
end
end
describe "RST_STREAM frames" do
test "deserializes frames" do
frame = <<0, 0, 4, 3, 0, 0, 0, 0, 1, 0, 0, 0, 123>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.RstStream{stream_id: 1, error_code: 123}}, <<>>}
end
test "rejects frames with 0 stream_id" do
frame = <<0, 0, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 123>>
assert Frame.deserialize(frame, 16_384) ==
{{:error, {:connection, 1, "RST_STREAM frame with zero stream_id (RFC9113§6.4)"}},
<<>>}
end
test "rejects frames with invalid size" do
frame = <<0, 0, 5, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 123>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, 6, "Invalid payload size in RST_STREAM frame (RFC9113§6.4)"}},
<<>>}
end
end
describe "SETTINGS frames" do
test "deserializes non-ack frames when there are no non-default settings" do
frame = <<0, 0, 0, 4, 0, 0, 0, 0, 0>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Settings{ack: false, settings: %Settings{}}}, <<>>}
end
test "deserializes non-ack frames when there are non-default settings" do
frame = <<0, 0, 6, 4, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 255>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Settings{ack: false, settings: %Settings{header_table_size: 255}}},
<<>>}
end
test "rejects non-ack frames when there is a malformed payload" do
frame = <<0, 0, 1, 4, 0, 0, 0, 0, 0, 1>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.frame_size_error(), "Invalid SETTINGS size (RFC9113§6.5)"}},
<<>>}
end
test "rejects non-ack frames with invalid enable_push_promise value" do
frame = <<0, 0, 6, 4, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.protocol_error(), "Invalid enable_push value (RFC9113§6.5)"}},
<<>>}
end
test "rejects non-ack frames with invalid initial_window_size value" do
frame = <<0, 0, 6, 4, 0, 0, 0, 0, 0, 0, 4, 255, 255, 255, 255>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.flow_control_error(), "Invalid window_size (RFC9113§6.5)"}},
<<>>}
end
test "rejects non-ack frames with invalid large max_frame_size value" do
frame = <<0, 0, 6, 4, 0, 0, 0, 0, 0, 0, 5, 255, 255, 255, 255>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.frame_size_error(), "Invalid max_frame_size (RFC9113§6.5)"}},
<<>>}
end
test "rejects non-ack frames with invalid small max_frame_size value" do
frame = <<0, 0, 6, 4, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.frame_size_error(), "Invalid max_frame_size (RFC9113§6.5)"}},
<<>>}
end
test "rejects non-ack frames when there is stream identifier" do
frame = <<0, 0, 0, 4, 0, 0, 0, 0, 1>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.protocol_error(), "Invalid SETTINGS frame (RFC9113§6.5)"}},
<<>>}
end
test "deserializes ack frames" do
frame = <<0, 0, 0, 4, 1, 0, 0, 0, 0>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Settings{ack: true, settings: nil}}, <<>>}
end
test "rejects ack frames when there is a payload" do
frame = <<0, 0, 1, 4, 1, 0, 0, 0, 0, 1>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.frame_size_error(),
"SETTINGS ack frame with non-empty payload (RFC9113§6.5)"}}, <<>>}
end
test "rejects ack frames when there is stream identifier" do
frame = <<0, 0, 0, 4, 1, 0, 0, 0, 1>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.protocol_error(), "Invalid SETTINGS frame (RFC9113§6.5)"}},
<<>>}
end
end
describe "PUSH_PROMISE frames" do
test "rejects all frames" do
frame = <<0, 0, 7, 5, 0, 0, 0, 0, 1, 0, 0, 0, 3, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:error, {:connection, 1, "PUSH_PROMISE frame received (RFC9113§8.4)"}}, <<>>}
end
end
describe "PING frames" do
test "deserializes non-ack frames" do
frame = <<0, 0, 8, 6, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Ping{ack: false, payload: <<1, 2, 3, 4, 5, 6, 7, 8>>}}, <<>>}
end
test "deserializes ack frames" do
frame = <<0, 0, 8, 6, 1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Ping{ack: true, payload: <<1, 2, 3, 4, 5, 6, 7, 8>>}}, <<>>}
end
test "rejects frames when there is a malformed payload" do
frame = <<0, 0, 7, 6, 1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.frame_size_error(),
"PING frame with invalid payload size (RFC9113§6.7)"}}, <<>>}
end
test "rejects frames when there is stream identifier" do
frame = <<0, 0, 8, 6, 1, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.protocol_error(),
"Invalid stream ID in PING frame (RFC9113§6.7)"}}, <<>>}
end
end
describe "GOAWAY frames" do
test "deserializes frames without debug data" do
frame = <<0, 0, 8, 7, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Goaway{last_stream_id: 1, error_code: 2, debug_data: <<>>}}, <<>>}
end
test "deserializes frames with debug data" do
frame = <<0, 0, 10, 7, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 4>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.Goaway{last_stream_id: 1, error_code: 2, debug_data: <<3, 4>>}},
<<>>}
end
test "rejects frames when there is a malformed payload" do
frame = <<0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.frame_size_error(),
"GOAWAY frame with invalid payload size (RFC9113§6.8)"}}, <<>>}
end
test "rejects frames when there is stream identifier" do
frame = <<0, 0, 7, 7, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.protocol_error(),
"Invalid stream ID in GOAWAY frame (RFC9113§6.8)"}}, <<>>}
end
end
describe "WINDOW_UPDATE frames" do
test "deserializes frames" do
frame = <<0, 0, 4, 8, 0, 0, 0, 0, 123, 0, 0, 0, 234>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok, %Frame.WindowUpdate{stream_id: 123, size_increment: 234}}, <<>>}
end
test "rejects frames when there is a 0 size increment" do
frame = <<0, 0, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.flow_control_error(),
"Invalid WINDOW_UPDATE size increment (RFC9113§6.9)"}}, <<>>}
end
test "rejects frames when there is a 0 size increment on a stream" do
frame = <<0, 0, 4, 8, 0, 0, 0, 0, 123, 0, 0, 0, 0>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.flow_control_error(),
"Invalid WINDOW_UPDATE size increment (RFC9113§6.9)"}}, <<>>}
end
test "rejects frames when there is a malformed payload" do
frame = <<0, 0, 3, 8, 0, 0, 0, 0, 123, 0, 0, 234>>
assert Frame.deserialize(frame, 16_384) ==
{{:error,
{:connection, Errors.frame_size_error(),
"Invalid WINDOW_UPDATE frame (RFC9113§6.9)"}}, <<>>}
end
end
describe "CONTINUATION frames" do
test "deserializes frames" do
frame = <<0, 0, 3, 9, 0x00, 0, 0, 0, 1, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok,
%Frame.Continuation{
stream_id: 1,
end_headers: false,
fragment: <<1, 2, 3>>
}}, <<>>}
end
test "sets end_headers" do
frame = <<0, 0, 3, 9, 0x04, 0, 0, 0, 1, 1, 2, 3>>
assert Frame.deserialize(frame, 16_384) ==
{{:ok,
%Frame.Continuation{
stream_id: 1,
end_headers: true,
fragment: <<1, 2, 3>>
}}, <<>>}
end
end
end