diff options
author | Loïc Hoguin <[email protected]> | 2013-07-12 21:57:27 +0200 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2013-07-12 21:57:27 +0200 |
commit | ddefa33a38e045d1359149f911359343105a0ce0 (patch) | |
tree | 3f8e73da263b70063a4f468265b9adfa3a95a2b3 | |
parent | 01301acdafbf29ab914dc87c86bb0d9456543ae1 (diff) | |
parent | c5c9c398ffea608d22c410668b9b79b80b5bdb91 (diff) | |
download | cowboy-ddefa33a38e045d1359149f911359343105a0ce0.tar.gz cowboy-ddefa33a38e045d1359149f911359343105a0ce0.tar.bz2 cowboy-ddefa33a38e045d1359149f911359343105a0ce0.zip |
Merge branch 'fix/websocket-inflate-unmask' of git://github.com/soundrop/cowboy
-rw-r--r-- | src/cowboy_websocket.erl | 101 | ||||
-rw-r--r-- | test/ws_SUITE.erl | 131 |
2 files changed, 172 insertions, 60 deletions
diff --git a/src/cowboy_websocket.erl b/src/cowboy_websocket.erl index df50162..073d7c6 100644 --- a/src/cowboy_websocket.erl +++ b/src/cowboy_websocket.erl @@ -57,9 +57,8 @@ frag_state = undefined :: frag_state(), utf8_state = <<>> :: binary(), deflate_frame = false :: boolean(), - inflate_state :: any(), - inflate_buffer = <<>> :: binary(), - deflate_state :: any() + inflate_state :: undefined | port(), + deflate_state :: undefined | port() }). %% @doc Upgrade an HTTP request to the Websocket protocol. @@ -121,7 +120,6 @@ websocket_extensions(State, Req) -> {ok, State#state{ deflate_frame = true, inflate_state = Inflate, - inflate_buffer = <<>>, deflate_state = Deflate }, Req2}; _ -> @@ -331,45 +329,49 @@ websocket_data(State, Req, HandlerState, Data) -> websocket_data(State=#state{frag_state=undefined}, Req, HandlerState, Opcode, Len, MaskKey, Data, Rsv, 0) -> websocket_payload(State#state{frag_state={nofin, Opcode, <<>>}}, - Req, HandlerState, 0, Len, MaskKey, <<>>, Data, Rsv); + Req, HandlerState, 0, Len, MaskKey, <<>>, 0, Data, Rsv); %% Subsequent frame fragments. websocket_data(State=#state{frag_state={nofin, _, _}}, Req, HandlerState, 0, Len, MaskKey, Data, Rsv, 0) -> websocket_payload(State, Req, HandlerState, - 0, Len, MaskKey, <<>>, Data, Rsv); + 0, Len, MaskKey, <<>>, 0, Data, Rsv); %% Final frame fragment. websocket_data(State=#state{frag_state={nofin, Opcode, SoFar}}, Req, HandlerState, 0, Len, MaskKey, Data, Rsv, 1) -> websocket_payload(State#state{frag_state={fin, Opcode, SoFar}}, - Req, HandlerState, 0, Len, MaskKey, <<>>, Data, Rsv); + Req, HandlerState, 0, Len, MaskKey, <<>>, 0, Data, Rsv); %% Unfragmented frame. websocket_data(State, Req, HandlerState, Opcode, Len, MaskKey, Data, Rsv, 1) -> websocket_payload(State, Req, HandlerState, - Opcode, Len, MaskKey, <<>>, Data, Rsv). + Opcode, Len, MaskKey, <<>>, 0, Data, Rsv). -spec websocket_payload(#state{}, Req, any(), - opcode(), non_neg_integer(), mask_key(), binary(), binary(), rsv()) + opcode(), non_neg_integer(), mask_key(), binary(), non_neg_integer(), + binary(), rsv()) -> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]} when Req::cowboy_req:req(). %% Close control frames with a payload MUST contain a valid close code. websocket_payload(State, Req, HandlerState, - Opcode=8, Len, MaskKey, <<>>, << MaskedCode:2/binary, Rest/bits >>, Rsv) -> + Opcode=8, Len, MaskKey, <<>>, 0, + << MaskedCode:2/binary, Rest/bits >>, Rsv) -> Unmasked = << Code:16 >> = websocket_unmask(MaskedCode, MaskKey, <<>>), if Code < 1000; Code =:= 1004; Code =:= 1005; Code =:= 1006; (Code > 1011) and (Code < 3000); Code > 4999 -> websocket_close(State, Req, HandlerState, {error, badframe}); true -> websocket_payload(State, Req, HandlerState, - Opcode, Len - 2, MaskKey, Unmasked, Rest, Rsv) + Opcode, Len - 2, MaskKey, Unmasked, byte_size(MaskedCode), + Rest, Rsv) end; %% Text frames and close control frames MUST have a payload that is valid UTF-8. websocket_payload(State=#state{utf8_state=Incomplete}, - Req, HandlerState, Opcode, Len, MaskKey, Unmasked, Data, Rsv) + Req, HandlerState, Opcode, Len, MaskKey, Unmasked, UnmaskedLen, + Data, Rsv) when (byte_size(Data) < Len) andalso ((Opcode =:= 1) orelse ((Opcode =:= 8) andalso (Unmasked =/= <<>>))) -> Unmasked2 = websocket_unmask(Data, - rotate_mask_key(MaskKey, byte_size(Unmasked)), <<>>), + rotate_mask_key(MaskKey, UnmaskedLen), <<>>), {Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, false, State), case is_utf8(<< Incomplete/binary, Unmasked3/binary >>) of false -> @@ -377,14 +379,16 @@ websocket_payload(State=#state{utf8_state=Incomplete}, Utf8State -> websocket_payload_loop(State2#state{utf8_state=Utf8State}, Req, HandlerState, Opcode, Len - byte_size(Data), MaskKey, - << Unmasked/binary, Unmasked3/binary >>, Rsv) + << Unmasked/binary, Unmasked3/binary >>, + UnmaskedLen + byte_size(Data), Rsv) end; websocket_payload(State=#state{utf8_state=Incomplete}, - Req, HandlerState, Opcode, Len, MaskKey, Unmasked, Data, Rsv) + Req, HandlerState, Opcode, Len, MaskKey, Unmasked, UnmaskedLen, + Data, Rsv) when Opcode =:= 1; (Opcode =:= 8) and (Unmasked =/= <<>>) -> << End:Len/binary, Rest/bits >> = Data, Unmasked2 = websocket_unmask(End, - rotate_mask_key(MaskKey, byte_size(Unmasked)), <<>>), + rotate_mask_key(MaskKey, UnmaskedLen), <<>>), {Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, true, State), case is_utf8(<< Incomplete/binary, Unmasked3/binary >>) of <<>> -> @@ -396,10 +400,11 @@ websocket_payload(State=#state{utf8_state=Incomplete}, end; %% Fragmented text frames may cut payload in the middle of UTF-8 codepoints. websocket_payload(State=#state{frag_state={_, 1, _}, utf8_state=Incomplete}, - Req, HandlerState, Opcode=0, Len, MaskKey, Unmasked, Data, Rsv) + Req, HandlerState, Opcode=0, Len, MaskKey, Unmasked, UnmaskedLen, + Data, Rsv) when byte_size(Data) < Len -> Unmasked2 = websocket_unmask(Data, - rotate_mask_key(MaskKey, byte_size(Unmasked)), <<>>), + rotate_mask_key(MaskKey, UnmaskedLen), <<>>), {Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, false, State), case is_utf8(<< Incomplete/binary, Unmasked3/binary >>) of false -> @@ -407,14 +412,16 @@ websocket_payload(State=#state{frag_state={_, 1, _}, utf8_state=Incomplete}, Utf8State -> websocket_payload_loop(State2#state{utf8_state=Utf8State}, Req, HandlerState, Opcode, Len - byte_size(Data), MaskKey, - << Unmasked/binary, Unmasked3/binary >>, Rsv) + << Unmasked/binary, Unmasked3/binary >>, + UnmaskedLen + byte_size(Data), Rsv) end; websocket_payload(State=#state{frag_state={Fin, 1, _}, utf8_state=Incomplete}, - Req, HandlerState, Opcode=0, Len, MaskKey, Unmasked, Data, Rsv) -> + Req, HandlerState, Opcode=0, Len, MaskKey, Unmasked, UnmaskedLen, + Data, Rsv) -> << End:Len/binary, Rest/bits >> = Data, Unmasked2 = websocket_unmask(End, - rotate_mask_key(MaskKey, byte_size(Unmasked)), <<>>), - {Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, true, State), + rotate_mask_key(MaskKey, UnmaskedLen), <<>>), + {Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, Fin =:= fin, State), case is_utf8(<< Incomplete/binary, Unmasked3/binary >>) of <<>> -> websocket_dispatch(State2#state{utf8_state= <<>>}, @@ -429,20 +436,23 @@ websocket_payload(State=#state{frag_state={Fin, 1, _}, utf8_state=Incomplete}, end; %% Other frames have a binary payload. websocket_payload(State, Req, HandlerState, - Opcode, Len, MaskKey, Unmasked, Data, Rsv) + Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Data, Rsv) when byte_size(Data) < Len -> Unmasked2 = websocket_unmask(Data, - rotate_mask_key(MaskKey, byte_size(Unmasked)), Unmasked), + rotate_mask_key(MaskKey, UnmaskedLen), <<>>), {Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, false, State), websocket_payload_loop(State2, Req, HandlerState, - Opcode, Len - byte_size(Data), MaskKey, Unmasked3, Rsv); + Opcode, Len - byte_size(Data), MaskKey, + << Unmasked/binary, Unmasked3/binary >>, UnmaskedLen + byte_size(Data), + Rsv); websocket_payload(State, Req, HandlerState, - Opcode, Len, MaskKey, Unmasked, Data, Rsv) -> + Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Data, Rsv) -> << End:Len/binary, Rest/bits >> = Data, Unmasked2 = websocket_unmask(End, - rotate_mask_key(MaskKey, byte_size(Unmasked)), Unmasked), + rotate_mask_key(MaskKey, UnmaskedLen), <<>>), {Unmasked3, State2} = websocket_inflate_frame(Unmasked2, Rsv, true, State), - websocket_dispatch(State2, Req, HandlerState, Rest, Opcode, Unmasked3). + websocket_dispatch(State2, Req, HandlerState, Rest, Opcode, + << Unmasked/binary, Unmasked3/binary >>). -spec websocket_inflate_frame(binary(), rsv(), boolean(), #state{}) -> {binary(), #state{}}. @@ -450,14 +460,13 @@ websocket_inflate_frame(Data, << Rsv1:1, _:2 >>, _, #state{deflate_frame = DeflateFrame} = State) when DeflateFrame =:= false orelse Rsv1 =:= 0 -> {Data, State}; -websocket_inflate_frame(Data, << 1:1, _:2 >>, false, - #state{inflate_buffer = Buffer} = State) -> - {<<>>, State#state{inflate_buffer = << Buffer/binary, Data/binary >>}}; -websocket_inflate_frame(Data, << 1:1, _:2 >>, true, - #state{inflate_state = Inflate, inflate_buffer = Buffer} = State) -> - Deflated = << Buffer/binary, Data/binary, 0:8, 0:8, 255:8, 255:8 >>, - Result = zlib:inflate(Inflate, Deflated), - {iolist_to_binary(Result), State#state{inflate_buffer = <<>>}}. +websocket_inflate_frame(Data, << 1:1, _:2 >>, false, State) -> + Result = zlib:inflate(State#state.inflate_state, Data), + {iolist_to_binary(Result), State}; +websocket_inflate_frame(Data, << 1:1, _:2 >>, true, State) -> + Result = zlib:inflate(State#state.inflate_state, + << Data/binary, 0:8, 0:8, 255:8, 255:8 >>), + {iolist_to_binary(Result), State}. -spec websocket_unmask(B, mask_key(), B) -> B when B::binary(). websocket_unmask(<<>>, _, Unmasked) -> @@ -516,19 +525,20 @@ is_utf8(_) -> false. -spec websocket_payload_loop(#state{}, Req, any(), - opcode(), non_neg_integer(), mask_key(), binary(), rsv()) + opcode(), non_neg_integer(), mask_key(), binary(), + non_neg_integer(), rsv()) -> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]} when Req::cowboy_req:req(). websocket_payload_loop(State=#state{socket=Socket, transport=Transport, messages={OK, Closed, Error}, timeout_ref=TRef}, - Req, HandlerState, Opcode, Len, MaskKey, Unmasked, Rsv) -> + Req, HandlerState, Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Rsv) -> Transport:setopts(Socket, [{active, once}]), receive {OK, Socket, Data} -> State2 = handler_loop_timeout(State), websocket_payload(State2, Req, HandlerState, - Opcode, Len, MaskKey, Unmasked, Data, Rsv); + Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Data, Rsv); {Closed, Socket} -> handler_terminate(State, Req, HandlerState, {error, closed}); {Error, Socket, Reason} -> @@ -537,13 +547,13 @@ websocket_payload_loop(State=#state{socket=Socket, transport=Transport, websocket_close(State, Req, HandlerState, {normal, timeout}); {timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) -> websocket_payload_loop(State, Req, HandlerState, - Opcode, Len, MaskKey, Unmasked, Rsv); + Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Rsv); Message -> handler_call(State, Req, HandlerState, <<>>, websocket_info, Message, fun (State2, Req2, HandlerState2, _) -> websocket_payload_loop(State2, Req2, HandlerState2, - Opcode, Len, MaskKey, Unmasked, Rsv) + Opcode, Len, MaskKey, Unmasked, UnmaskedLen, Rsv) end) end. @@ -665,19 +675,20 @@ websocket_opcode(close) -> 8; websocket_opcode(ping) -> 9; websocket_opcode(pong) -> 10. --spec websocket_deflate_frame(opcode(), binary(), #state{}) -> {binary(), <<_:3>>, #state{}}. +-spec websocket_deflate_frame(opcode(), binary(), #state{}) -> + {binary(), rsv(), #state{}}. websocket_deflate_frame(Opcode, Payload, State=#state{deflate_frame = DeflateFrame}) when DeflateFrame =:= false orelse Opcode >= 8 -> - {Payload, <<0:3>>, State}; + {Payload, << 0:3 >>, State}; websocket_deflate_frame(_, Payload, State=#state{deflate_state = Deflate}) -> Deflated = iolist_to_binary(zlib:deflate(Deflate, Payload, sync)), DeflatedBodyLength = erlang:size(Deflated) - 4, Deflated1 = case Deflated of - <<Body:DeflatedBodyLength/binary, 0:8, 0:8, 255:8, 255:8>> -> Body; + << Body:DeflatedBodyLength/binary, 0:8, 0:8, 255:8, 255:8 >> -> Body; _ -> Deflated end, - {Deflated1, <<1:1, 0:2>>, State}. + {Deflated1, << 1:1, 0:2 >>, State}. -spec websocket_send(frame(), #state{}) -> {ok, #state{}} | {shutdown, #state{}} | {{error, atom()}, #state{}}. diff --git a/test/ws_SUITE.erl b/test/ws_SUITE.erl index bdb3565..d2a0888 100644 --- a/test/ws_SUITE.erl +++ b/test/ws_SUITE.erl @@ -31,6 +31,8 @@ -export([ws8_single_bytes/1]). -export([ws13/1]). -export([ws_deflate/1]). +-export([ws_deflate_chunks/1]). +-export([ws_deflate_fragments/1]). -export([ws_send_close/1]). -export([ws_send_close_payload/1]). -export([ws_send_many/1]). @@ -53,6 +55,8 @@ groups() -> ws8_single_bytes, ws13, ws_deflate, + ws_deflate_chunks, + ws_deflate_fragments, ws_send_close, ws_send_close_payload, ws_send_many, @@ -315,7 +319,7 @@ ws13(Config) -> ws_deflate(Config) -> {port, Port} = lists:keyfind(port, 1, Config), {ok, Socket} = gen_tcp:connect("localhost", Port, - [binary, {active, false}, {packet, raw}]), + [binary, {active, false}, {packet, raw}, {nodelay, true}]), ok = gen_tcp:send(Socket, [ "GET /ws_echo HTTP/1.1\r\n" "Host: localhost\r\n" @@ -338,25 +342,104 @@ ws_deflate(Config) -> {"sec-websocket-extensions", "x-webkit-deflate-frame"} = lists:keyfind("sec-websocket-extensions", 1, Headers), - % send uncompressed text frame containing the Hello string - ok = gen_tcp:send(Socket, << 16#81, 16#85, 16#37, 16#fa, 16#21, 16#3d, - 16#7f, 16#9f, 16#4d, 16#51, 16#58 >>), + Mask = 16#11223344, + Hello = << 242, 72, 205, 201, 201, 7, 0 >>, + MaskedHello = websocket_mask(Hello, Mask, <<>>), + + % send compressed text frame containing the Hello string + ok = gen_tcp:send(Socket, << 1:1, 1:1, 0:2, 1:4, 1:1, 7:7, Mask:32, + MaskedHello/binary >>), % receive compressed text frame containing the Hello string - {ok, << 1:1, 1:1, 0:2, 1:4, 0:1, 7:7, 242, 72, 205, 201, 201, 7, 0 >>} + {ok, << 1:1, 1:1, 0:2, 1:4, 0:1, 7:7, Hello/binary >>} = gen_tcp:recv(Socket, 0, 6000), - % send uncompressed text frame containing the HelloHello string - % as 2 separate fragments + ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 1:1, 0:7, 0:32 >>), %% close + {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), + {error, closed} = gen_tcp:recv(Socket, 0, 6000), + ok. + +ws_deflate_chunks(Config) -> + {port, Port} = lists:keyfind(port, 1, Config), + {ok, Socket} = gen_tcp:connect("localhost", Port, + [binary, {active, false}, {packet, raw}, {nodelay, true}]), ok = gen_tcp:send(Socket, [ - << 0:1, 0:3, 1:4, 1:1, 5:7 >>, - << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>, - << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>]), + "GET /ws_echo HTTP/1.1\r\n" + "Host: localhost\r\n" + "Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Origin: http://localhost\r\n" + "Sec-WebSocket-Version: 8\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Extensions: x-webkit-deflate-frame\r\n" + "\r\n"]), + {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), + {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} + = erlang:decode_packet(http, Handshake, []), + [Headers, <<>>] = websocket_headers( + erlang:decode_packet(httph, Rest, []), []), + {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), + {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), + {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} + = lists:keyfind("sec-websocket-accept", 1, Headers), + {"sec-websocket-extensions", "x-webkit-deflate-frame"} + = lists:keyfind("sec-websocket-extensions", 1, Headers), + + Mask = 16#11223344, + Hello = << 242, 72, 205, 201, 201, 7, 0 >>, + MaskedHello = websocket_mask(Hello, Mask, <<>>), + + % send compressed text frame containing the Hello string + ok = gen_tcp:send(Socket, << 1:1, 1:1, 0:2, 1:4, 1:1, 7:7, Mask:32, + (binary:part(MaskedHello, 0, 4))/binary >>), + ok = timer:sleep(100), + ok = gen_tcp:send(Socket, binary:part(MaskedHello, 4, 3)), + + % receive compressed text frame containing the Hello string + {ok, << 1:1, 1:1, 0:2, 1:4, 0:1, 7:7, Hello/binary >>} + = gen_tcp:recv(Socket, 0, 6000), + + ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 1:1, 0:7, 0:32 >>), %% close + {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), + {error, closed} = gen_tcp:recv(Socket, 0, 6000), + ok. + +ws_deflate_fragments(Config) -> + {port, Port} = lists:keyfind(port, 1, Config), + {ok, Socket} = gen_tcp:connect("localhost", Port, + [binary, {active, false}, {packet, raw}, {nodelay, true}]), ok = gen_tcp:send(Socket, [ - << 1:1, 0:3, 0:4, 1:1, 5:7 >>, - << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>, - << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>]), - % receive compressed text frame containing the HelloHello string - {ok, << 1:1, 1:1, 0:2, 1:4, 0:1, 5:7, 242, 128, 19, 0, 0 >>} + "GET /ws_echo HTTP/1.1\r\n" + "Host: localhost\r\n" + "Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Origin: http://localhost\r\n" + "Sec-WebSocket-Version: 8\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Extensions: x-webkit-deflate-frame\r\n" + "\r\n"]), + {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), + {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} + = erlang:decode_packet(http, Handshake, []), + [Headers, <<>>] = websocket_headers( + erlang:decode_packet(httph, Rest, []), []), + {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), + {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), + {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} + = lists:keyfind("sec-websocket-accept", 1, Headers), + {"sec-websocket-extensions", "x-webkit-deflate-frame"} + = lists:keyfind("sec-websocket-extensions", 1, Headers), + + Mask = 16#11223344, + Hello = << 242, 72, 205, 201, 201, 7, 0 >>, + + % send compressed text frame containing the Hello string + % as 2 separate fragments + ok = gen_tcp:send(Socket, << 0:1, 1:1, 0:2, 1:4, 1:1, 4:7, Mask:32, + (websocket_mask(binary:part(Hello, 0, 4), Mask, <<>>))/binary >>), + ok = gen_tcp:send(Socket, << 1:1, 1:1, 0:2, 0:4, 1:1, 3:7, Mask:32, + (websocket_mask(binary:part(Hello, 4, 3), Mask, <<>>))/binary >>), + % receive compressed text frame containing the Hello string + {ok, << 1:1, 1:1, 0:2, 1:4, 0:1, 7:7, Hello/binary >>} = gen_tcp:recv(Socket, 0, 6000), ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 1:1, 0:7, 0:32 >>), %% close @@ -632,3 +715,21 @@ websocket_headers({ok, {http_header, _I, Key, _R, Value}, Rest}, Acc) -> F = fun(S) when is_atom(S) -> S; (S) -> string:to_lower(S) end, websocket_headers(erlang:decode_packet(httph, Rest, []), [{F(Key), Value}|Acc]). + +websocket_mask(<<>>, _, Unmasked) -> + Unmasked; +websocket_mask(<< O:32, Rest/bits >>, MaskKey, Acc) -> + T = O bxor MaskKey, + websocket_mask(Rest, MaskKey, << Acc/binary, T:32 >>); +websocket_mask(<< O:24 >>, MaskKey, Acc) -> + << MaskKey2:24, _:8 >> = << MaskKey:32 >>, + T = O bxor MaskKey2, + << Acc/binary, T:24 >>; +websocket_mask(<< O:16 >>, MaskKey, Acc) -> + << MaskKey2:16, _:16 >> = << MaskKey:32 >>, + T = O bxor MaskKey2, + << Acc/binary, T:16 >>; +websocket_mask(<< O:8 >>, MaskKey, Acc) -> + << MaskKey2:8, _:24 >> = << MaskKey:32 >>, + T = O bxor MaskKey2, + << Acc/binary, T:8 >>. |