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(),
|
||||
NewFrames :: Frames,
|
||||
Reason :: any().
|
||||
% @doc
|
||||
% @private
|
||||
% try to parse the stack of frames into a single message
|
||||
%
|
||||
% ignores RSV bits
|
||||
@ -400,14 +400,100 @@ recv(Sock, Received, Frames) ->
|
||||
maybe_pop_msg([]) ->
|
||||
incomplete;
|
||||
% case 1: control frames
|
||||
maybe_pop_msg([Frame = #frame{opcode = Opcode} | Frames])
|
||||
when Opcode =:= close; Opcode =:= ping; Opcode =:= pong ->
|
||||
% note that maybe_control_msg checks that the fin bit is true
|
||||
%
|
||||
% 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
|
||||
{ok, Msg} -> {ok, Msg, Frames};
|
||||
Error -> Error
|
||||
end;
|
||||
maybe_pop_msg(_) ->
|
||||
error(nyi).
|
||||
% case 2: messages
|
||||
% 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().
|
||||
% @private
|
||||
% 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,
|
||||
opcode = Opcode,
|
||||
@ -611,11 +700,13 @@ recv_frame_await(Frame, Sock, Received) ->
|
||||
Reason :: closed | {timeout, RestData} | inet:posix(),
|
||||
RestData :: binary() | erlang:iovec().
|
||||
% @doc
|
||||
% FIXME: this should be sending a message, not an arbitrary payload
|
||||
%
|
||||
% send binary data over Socket. handles frame nonsense
|
||||
%
|
||||
% types the payload as bytes
|
||||
%
|
||||
% max payload size is 2^64 - 1 bytes
|
||||
% max payload size is 2^63 - 1 bytes
|
||||
% @end
|
||||
|
||||
send(Socket, Payload) ->
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user