Almost done... have to fix send
and then of course test it there will be no bugs, right?
This commit is contained in:
parent
5824aaaf36
commit
1865f03085
103
src/fd_ws.erl
103
src/fd_ws.erl
@ -391,7 +391,7 @@ recv(Sock, Received, Frames) ->
|
|||||||
Message :: ws_msg(),
|
Message :: ws_msg(),
|
||||||
NewFrames :: Frames,
|
NewFrames :: Frames,
|
||||||
Reason :: any().
|
Reason :: any().
|
||||||
% @doc
|
% @private
|
||||||
% try to parse the stack of frames into a single message
|
% try to parse the stack of frames into a single message
|
||||||
%
|
%
|
||||||
% ignores RSV bits
|
% ignores RSV bits
|
||||||
@ -400,14 +400,100 @@ recv(Sock, Received, Frames) ->
|
|||||||
maybe_pop_msg([]) ->
|
maybe_pop_msg([]) ->
|
||||||
incomplete;
|
incomplete;
|
||||||
% case 1: control frames
|
% case 1: control frames
|
||||||
maybe_pop_msg([Frame = #frame{opcode = Opcode} | Frames])
|
% note that maybe_control_msg checks that the fin bit is true
|
||||||
when Opcode =:= close; Opcode =:= ping; Opcode =:= pong ->
|
%
|
||||||
|
% meaning if the client sends a malicious control frame with fin=false, that
|
||||||
|
% error will be caught in maybe_control_msg
|
||||||
|
maybe_pop_msg([Frame = #frame{opcode = ControlOpcode} | Frames])
|
||||||
|
when (ControlOpcode =:= close)
|
||||||
|
orelse (ControlOpcode =:= ping)
|
||||||
|
orelse (ControlOpcode =:= pong) ->
|
||||||
case maybe_control_msg(Frame) of
|
case maybe_control_msg(Frame) of
|
||||||
{ok, Msg} -> {ok, Msg, Frames};
|
{ok, Msg} -> {ok, Msg, Frames};
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
maybe_pop_msg(_) ->
|
% case 2: messages
|
||||||
error(nyi).
|
% finished message in a single frame, just pull here
|
||||||
|
maybe_pop_msg([Frame = #frame{fin = true,
|
||||||
|
opcode = DataOpcode,
|
||||||
|
mask = Mask,
|
||||||
|
masking_key = Key,
|
||||||
|
payload = Payload}
|
||||||
|
| Rest])
|
||||||
|
when DataOpcode =:= text; DataOpcode =:= binary ->
|
||||||
|
case maybe_unmask(Frame, Mask, Key, Payload) of
|
||||||
|
{ok, Unmasked} ->
|
||||||
|
Message = {DataOpcode, Unmasked},
|
||||||
|
{ok, Message, Rest};
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end;
|
||||||
|
% end of a long message
|
||||||
|
maybe_pop_msg(Frames = [#frame{fin = true,
|
||||||
|
opcode = continuation} | _]) ->
|
||||||
|
maybe_long_data_msg(Frames);
|
||||||
|
% unfinished message, say we need more
|
||||||
|
maybe_pop_msg([#frame{fin = false,
|
||||||
|
opcode = continuation}
|
||||||
|
| _]) ->
|
||||||
|
incomplete;
|
||||||
|
% wtf... this case should be impossible
|
||||||
|
maybe_pop_msg([Frame | _]) ->
|
||||||
|
{error, {wtf_frame, Frame}}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-spec maybe_long_data_msg(Frames) -> Result
|
||||||
|
when Frames :: [frame()],
|
||||||
|
Result :: {ok, Message, NewFrames}
|
||||||
|
| {error, Reason},
|
||||||
|
Message :: ws_msg(),
|
||||||
|
NewFrames :: Frames,
|
||||||
|
Reason :: any().
|
||||||
|
% @private
|
||||||
|
% assumes:
|
||||||
|
% 1. top of stack is a finished frame
|
||||||
|
% 2. top opcode is continuation
|
||||||
|
% 3. the stack corresponds to a linear sequence of frames all corresponding to
|
||||||
|
% one message, until we get to the leading frame of the message, which must
|
||||||
|
% have opcode text|binary
|
||||||
|
%
|
||||||
|
% the reason we can make this assumption is because anterior in the call
|
||||||
|
% chain is recv/3, which eagerly consumes control messages
|
||||||
|
%
|
||||||
|
% meaning if we encounter a control frame in the middle here, we can assume
|
||||||
|
% there is some sort of bug
|
||||||
|
%
|
||||||
|
% TODO: I am NOT enforcing that the data message consumes the entire stack of
|
||||||
|
% frames. Given that the context here is eager consumption, this might be a
|
||||||
|
% point of enforcement. Need to think about this.
|
||||||
|
% @end
|
||||||
|
|
||||||
|
maybe_long_data_msg(Frames) ->
|
||||||
|
mldm(Frames, Frames, <<>>).
|
||||||
|
|
||||||
|
|
||||||
|
% general case: decode the payload in this frame
|
||||||
|
mldm(OrigFrames, [Frame | Rest], Acc) ->
|
||||||
|
Opcode = Frame#frame.opcode,
|
||||||
|
Mask = Frame#frame.mask,
|
||||||
|
Key = Frame#frame.masking_key,
|
||||||
|
Payload = Frame#frame.payload,
|
||||||
|
case maybe_unmask(Frame, Mask, Key, Payload) of
|
||||||
|
{ok, Unmasked} ->
|
||||||
|
NewAcc = <<Unmasked/binary, Acc/binary>>,
|
||||||
|
case Opcode of
|
||||||
|
continuation -> mldm(OrigFrames, Rest, NewAcc);
|
||||||
|
text -> {ok, {text, NewAcc}, Rest};
|
||||||
|
binary -> {ok, {binary, NewAcc}, Rest};
|
||||||
|
_ -> {error, {illegal_data_frame, Frame, OrigFrames, Acc}}
|
||||||
|
end;
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end;
|
||||||
|
% out of frames
|
||||||
|
mldm(OrigFrames, [], Acc) ->
|
||||||
|
{error, {no_start_frame, Acc, OrigFrames}}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -419,6 +505,9 @@ maybe_pop_msg(_) ->
|
|||||||
Reason :: any().
|
Reason :: any().
|
||||||
% @private
|
% @private
|
||||||
% assume the frame is a control frame, validate it, and unmask the payload
|
% assume the frame is a control frame, validate it, and unmask the payload
|
||||||
|
%
|
||||||
|
% TODO: this doesn't enforce that messages from the client HAVE to be masked,
|
||||||
|
% which strictly speaking is part of the protocol.
|
||||||
|
|
||||||
maybe_control_msg(F = #frame{fin = true,
|
maybe_control_msg(F = #frame{fin = true,
|
||||||
opcode = Opcode,
|
opcode = Opcode,
|
||||||
@ -611,11 +700,13 @@ recv_frame_await(Frame, Sock, Received) ->
|
|||||||
Reason :: closed | {timeout, RestData} | inet:posix(),
|
Reason :: closed | {timeout, RestData} | inet:posix(),
|
||||||
RestData :: binary() | erlang:iovec().
|
RestData :: binary() | erlang:iovec().
|
||||||
% @doc
|
% @doc
|
||||||
|
% FIXME: this should be sending a message, not an arbitrary payload
|
||||||
|
%
|
||||||
% send binary data over Socket. handles frame nonsense
|
% send binary data over Socket. handles frame nonsense
|
||||||
%
|
%
|
||||||
% types the payload as bytes
|
% types the payload as bytes
|
||||||
%
|
%
|
||||||
% max payload size is 2^64 - 1 bytes
|
% max payload size is 2^63 - 1 bytes
|
||||||
% @end
|
% @end
|
||||||
|
|
||||||
send(Socket, Payload) ->
|
send(Socket, Payload) ->
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user