Packages
bandit
0.6.0
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/websocket/frame.ex
defmodule Bandit.WebSocket.Frame do
@moduledoc false
alias Bandit.WebSocket.Frame
@typedoc "Indicates an opcode"
@type opcode :: non_neg_integer()
@typedoc "A valid WebSocket frame"
@type frame ::
Frame.Continuation.t()
| Frame.Text.t()
| Frame.Binary.t()
| Frame.ConnectionClose.t()
| Frame.Ping.t()
| Frame.Pong.t()
@spec deserialize(binary()) :: {{:ok, frame()}, iodata()} | {{:error, term()}, iodata()}
def deserialize(
<<fin::1, compressed::1, rsv::2, opcode::4, 1::1, 127::7, length::64, mask::32,
payload::binary-size(length), rest::binary>>
) do
to_frame(fin, compressed, rsv, opcode, mask, payload, rest)
end
def deserialize(
<<fin::1, compressed::1, rsv::2, opcode::4, 1::1, 126::7, length::16, mask::32,
payload::binary-size(length), rest::binary>>
) do
to_frame(fin, compressed, rsv, opcode, mask, payload, rest)
end
def deserialize(
<<fin::1, compressed::1, rsv::2, opcode::4, 1::1, length::7, mask::32,
payload::binary-size(length), rest::binary>>
)
when length <= 125 do
to_frame(fin, compressed, rsv, opcode, mask, payload, rest)
end
def deserialize(<<>>) do
nil
end
def deserialize(msg) do
{{:more, msg}, <<>>}
end
defp to_frame(_fin, _compressed, rsv, _opcode, _mask, _payload, rest) when rsv != 0x0 do
{{:error, "Received unsupported RSV flags #{rsv}"}, rest}
end
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
defp to_frame(fin, compressed, 0x0, opcode, mask, payload, rest) do
fin = fin == 0x1
compressed = compressed == 0x1
unmasked_payload = mask(payload, mask)
opcode
|> case do
0x0 -> Frame.Continuation.deserialize(fin, compressed, unmasked_payload)
0x1 -> Frame.Text.deserialize(fin, compressed, unmasked_payload)
0x2 -> Frame.Binary.deserialize(fin, compressed, unmasked_payload)
0x8 -> Frame.ConnectionClose.deserialize(fin, compressed, unmasked_payload)
0x9 -> Frame.Ping.deserialize(fin, compressed, unmasked_payload)
0xA -> Frame.Pong.deserialize(fin, compressed, unmasked_payload)
unknown -> {:error, "unknown opcode #{unknown}"}
end
|> case do
{:ok, frame} -> {{:ok, frame}, rest}
{:error, reason} -> {{:error, reason}, rest}
end
end
defprotocol Serializable do
@moduledoc false
@spec serialize(any()) :: [{Frame.opcode(), boolean(), boolean(), iodata()}]
def serialize(frame)
end
@spec serialize(frame()) :: iodata()
def serialize(frame) do
frame
|> Serializable.serialize()
|> Enum.map(fn {opcode, fin, compressed, payload} ->
fin = if fin, do: 0x1, else: 0x0
compressed = if compressed, do: 0x1, else: 0x0
mask_and_length = payload |> IO.iodata_length() |> mask_and_length()
[<<fin::1, compressed::1, 0x0::2, opcode::4>>, mask_and_length, payload]
end)
end
defp mask_and_length(length) when length <= 125, do: <<0::1, length::7>>
defp mask_and_length(length) when length <= 65_535, do: <<0::1, 126::7, length::16>>
defp mask_and_length(length), do: <<0::1, 127::7, length::64>>
# Note that masking is an involution, so we don't need a separate unmask function
def mask(payload, mask, acc \\ <<>>)
def mask(payload, mask, acc) when is_integer(mask), do: mask(payload, <<mask::32>>, acc)
def mask(<<h::32, rest::binary>>, <<mask::32>>, acc) do
mask(rest, mask, acc <> <<Bitwise.bxor(h, mask)::32>>)
end
def mask(<<h::8, rest::binary>>, <<current::8, mask::24>>, acc) do
mask(rest, <<mask::24, current::8>>, acc <> <<Bitwise.bxor(h, current)::8>>)
end
def mask(<<>>, _mask, acc), do: acc
end