Skip to content

Commit

Permalink
Switch to use Erlang nifs in frame mask application.
Browse files Browse the repository at this point in the history
  • Loading branch information
crertel authored Dec 7, 2023
1 parent 9b55f64 commit 7f1e05c
Showing 1 changed file with 21 additions and 23 deletions.
44 changes: 21 additions & 23 deletions lib/bandit/websocket/frame.ex
Original file line number Diff line number Diff line change
Expand Up @@ -184,28 +184,26 @@ defmodule Bandit.WebSocket.Frame do
|> IO.iodata_to_binary()
end

def mask(payload, mask) do
payload
|> do_mask(<<mask::32>>, [])
|> Enum.reverse()
|> IO.iodata_to_binary()
end

defp do_mask(
<<h::unquote(@mask_size), rest::binary>>,
<<int_mask::unquote(@mask_size)>> = mask,
acc
) do
do_mask(rest, mask, [<<Bitwise.bxor(h, int_mask)::unquote(@mask_size)>> | acc])
end

defp do_mask(<<h::32, rest::binary>>, <<int_mask::32, _mask_rest::binary>> = mask, acc) do
do_mask(rest, mask, [<<Bitwise.bxor(h, int_mask)::32>> | acc])
end

defp do_mask(<<h::8, rest::binary>>, <<current::8, mask::24, _mask_rest::binary>>, acc) do
do_mask(rest, <<mask::24, current::8>>, [<<Bitwise.bxor(h, current)::8>> | acc])
# note that we assume the mask integer is 32-bits and do not check
@spec mask( binary(), integer() ) :: binary()
def mask(payload, mask_integer) do

# 1. Allocate the binary for the mask repetitions
payload_size = byte_size(payload)
mask_repetitions = case { div(payload_size,4), rem(payload_size,4)} do
{count_4_bytes, 0 } ->
count_4_bytes # payload length is a multiple of 4, so the mask will be fine
{count_4_bytes, _count_stragglers} ->
count_4_bytes + 1 # bump up by one mask size
end
mask_binary = :binary.copy( <<mask_integer::32>>, mask_repetitions)
# 2. Trim the binary if needed
fit_mask = :binary.part(mask_binary, 0, payload_size)
# 3. XOR (in a nif) the payload and mask binary
masked_payload = :crypto.exor(payload, fit_mask)
end

defp do_mask(<<>>, _mask, acc), do: acc
end

0 comments on commit 7f1e05c

Please sign in to comment.