Packages
bandit
1.4.0
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
lib/bandit/http2/frame/settings.ex
defmodule Bandit.HTTP2.Frame.Settings do
@moduledoc false
import Bandit.HTTP2.Frame.Flags
import Bitwise
@max_window_size (1 <<< 31) - 1
@min_frame_size 1 <<< 14
@max_frame_size (1 <<< 24) - 1
defstruct ack: false, settings: nil
@typedoc "An HTTP/2 SETTINGS frame"
@type t ::
%__MODULE__{ack: true, settings: nil}
| %__MODULE__{ack: false, settings: Bandit.HTTP2.Settings.t()}
@ack_bit 0
@spec deserialize(Bandit.HTTP2.Frame.flags(), Bandit.HTTP2.Stream.stream_id(), iodata()) ::
{:ok, t()} | {:error, Bandit.HTTP2.Errors.error_code(), binary()}
def deserialize(flags, 0, payload) when clear?(flags, @ack_bit) do
payload
|> Stream.unfold(fn
<<>> -> nil
<<setting::16, value::32, rest::binary>> -> {{:ok, {setting, value}}, rest}
<<rest::binary>> -> {{:error, rest}, <<>>}
end)
|> Enum.reduce_while({:ok, %Bandit.HTTP2.Settings{}}, fn
{:ok, {0x01, value}}, {:ok, acc} ->
{:cont, {:ok, %{acc | header_table_size: value}}}
{:ok, {0x02, val}}, {:ok, acc} when val in [0x00, 0x01] ->
{:cont, {:ok, acc}}
{:ok, {0x02, _value}}, {:ok, _acc} ->
{:halt,
{:error, Bandit.HTTP2.Errors.protocol_error(), "Invalid enable_push value (RFC9113§6.5)"}}
{:ok, {0x03, value}}, {:ok, acc} ->
{:cont, {:ok, %{acc | max_concurrent_streams: value}}}
{:ok, {0x04, value}}, {:ok, _acc} when value > @max_window_size ->
{:halt,
{:error, Bandit.HTTP2.Errors.flow_control_error(), "Invalid window_size (RFC9113§6.5)"}}
{:ok, {0x04, value}}, {:ok, acc} ->
{:cont, {:ok, %{acc | initial_window_size: value}}}
{:ok, {0x05, value}}, {:ok, _acc} when value < @min_frame_size ->
{:halt,
{:error, Bandit.HTTP2.Errors.frame_size_error(), "Invalid max_frame_size (RFC9113§6.5)"}}
{:ok, {0x05, value}}, {:ok, _acc} when value > @max_frame_size ->
{:halt,
{:error, Bandit.HTTP2.Errors.frame_size_error(), "Invalid max_frame_size (RFC9113§6.5)"}}
{:ok, {0x05, value}}, {:ok, acc} ->
{:cont, {:ok, %{acc | max_frame_size: value}}}
{:ok, {0x06, value}}, {:ok, acc} ->
{:cont, {:ok, %{acc | max_header_list_size: value}}}
{:ok, {_setting, _value}}, {:ok, acc} ->
{:cont, {:ok, acc}}
{:error, _rest}, _acc ->
{:halt,
{:error, Bandit.HTTP2.Errors.frame_size_error(), "Invalid SETTINGS size (RFC9113§6.5)"}}
end)
|> case do
{:ok, settings} -> {:ok, %__MODULE__{ack: false, settings: settings}}
{:error, error_code, reason} -> {:error, error_code, reason}
end
end
def deserialize(flags, 0, <<>>) when set?(flags, @ack_bit) do
{:ok, %__MODULE__{ack: true}}
end
def deserialize(flags, 0, _payload) when set?(flags, @ack_bit) do
{:error, Bandit.HTTP2.Errors.frame_size_error(),
"SETTINGS ack frame with non-empty payload (RFC9113§6.5)"}
end
def deserialize(_flags, _stream_id, _payload) do
{:error, Bandit.HTTP2.Errors.protocol_error(), "Invalid SETTINGS frame (RFC9113§6.5)"}
end
defimpl Bandit.HTTP2.Frame.Serializable do
@ack_bit 0
def serialize(%Bandit.HTTP2.Frame.Settings{ack: true}, _max_frame_size),
do: [{0x4, set([@ack_bit]), 0, <<>>}]
def serialize(%Bandit.HTTP2.Frame.Settings{ack: false} = frame, _max_frame_size) do
# Note that the ordering here corresponds to the keys' alphabetical
# ordering on the Setting struct. However, we know there are no duplicates
# in this list so this is not a problem per RFC9113§6.5
#
# Encode default settings values as empty binaries so that we do not send
# them. This means we can't restore settings back to default values if we
# change them, but since we don't ever change our settings this is fine
payload =
frame.settings
|> Map.from_struct()
|> Enum.map(fn
{:header_table_size, 4_096} -> <<>>
{:header_table_size, value} -> <<0x01::16, value::32>>
{:max_concurrent_streams, :infinity} -> <<>>
{:max_concurrent_streams, value} -> <<0x03::16, value::32>>
{:initial_window_size, 65_535} -> <<>>
{:initial_window_size, value} -> <<0x04::16, value::32>>
{:max_frame_size, 16_384} -> <<>>
{:max_frame_size, value} -> <<0x05::16, value::32>>
{:max_header_list_size, :infinity} -> <<>>
{:max_header_list_size, value} -> <<0x06::16, value::32>>
end)
[{0x4, 0x0, 0, payload}]
end
end
end