Packages
bandit
1.2.2
1.12.1
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.ex
defmodule Bandit.HTTP2.Frame do
@moduledoc false
alias Bandit.HTTP2.{Connection, Errors, Frame, Stream}
@typedoc "Indicates a frame type"
@type frame_type :: non_neg_integer()
@typedoc "The flags passed along with a frame"
@type flags :: byte()
@typedoc "A valid HTTP/2 frame"
@type frame ::
Frame.Data.t()
| Frame.Headers.t()
| Frame.Priority.t()
| Frame.RstStream.t()
| Frame.Settings.t()
| Frame.Ping.t()
| Frame.Goaway.t()
| Frame.WindowUpdate.t()
| Frame.Continuation.t()
| Frame.Unknown.t()
@spec deserialize(binary(), non_neg_integer()) ::
{{:ok, frame()}, iodata()} | {{:error, Connection.error()}, iodata()} | nil
def deserialize(
<<length::24, type::8, flags::8, _reserved::1, stream_id::31,
payload::binary-size(length), rest::binary>>,
max_frame_size
)
when length <= max_frame_size do
type
|> case do
0x0 -> Frame.Data.deserialize(flags, stream_id, payload)
0x1 -> Frame.Headers.deserialize(flags, stream_id, payload)
0x2 -> Frame.Priority.deserialize(flags, stream_id, payload)
0x3 -> Frame.RstStream.deserialize(flags, stream_id, payload)
0x4 -> Frame.Settings.deserialize(flags, stream_id, payload)
0x5 -> Frame.PushPromise.deserialize(flags, stream_id, payload)
0x6 -> Frame.Ping.deserialize(flags, stream_id, payload)
0x7 -> Frame.Goaway.deserialize(flags, stream_id, payload)
0x8 -> Frame.WindowUpdate.deserialize(flags, stream_id, payload)
0x9 -> Frame.Continuation.deserialize(flags, stream_id, payload)
unknown -> Frame.Unknown.deserialize(unknown, flags, stream_id, payload)
end
|> case do
{:ok, frame} -> {{:ok, frame}, rest}
{:error, reason} -> {{:error, reason}, rest}
end
end
# This is a little more aggressive than necessary. RFC9113§4.2 says we only need
# to treat frame size violations as connection level errors if the frame in
# question would affect the connection as a whole, so we could be more surgical
# here and send stream level errors in some cases. However, we are well within
# our rights to consider such errors as connection errors
def deserialize(
<<length::24, _type::8, _flags::8, _reserved::1, _stream_id::31,
_payload::binary-size(length), rest::binary>>,
max_frame_size
)
when length > max_frame_size do
{{:error, {:connection, Errors.frame_size_error(), "Payload size too large (RFC9113§4.2)"}},
rest}
end
# nil is used to indicate for Stream.unfold/2 that the frame deserialization is finished
def deserialize(<<>>, _max_frame_size) do
nil
end
def deserialize(msg, _max_frame_size) do
{{:more, msg}, <<>>}
end
defmodule Flags do
@moduledoc false
import Bitwise
defguard set?(flags, bit) when band(flags, bsl(1, bit)) != 0
defguard clear?(flags, bit) when band(flags, bsl(1, bit)) == 0
@spec set([0..255]) :: 0..255
def set([]), do: 0x0
def set([bit | rest]), do: bor(bsl(1, bit), set(rest))
end
defprotocol Serializable do
@moduledoc false
@spec serialize(any(), non_neg_integer()) :: [
{Frame.frame_type(), Frame.flags(), Stream.stream_id(), iodata()}
]
def serialize(frame, max_frame_size)
end
@spec serialize(frame(), non_neg_integer()) :: iolist()
def serialize(frame, max_frame_size) do
frame
|> Serializable.serialize(max_frame_size)
|> Enum.map(fn {type, flags, stream_id, payload} ->
[<<IO.iodata_length(payload)::24, type::8, flags::8, 0::1, stream_id::31>>, payload]
end)
end
end