Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
142 lines (120 sloc) 5.486 kB
%%%==============================================================
%%% compiled using erlc -I include src/advanced_echo_callback.erl
%%%==============================================================
-module(advanced_echo_callback).
-export([handle_message/2]).
-include("yaws_api.hrl").
%% define callback state to accumulate a fragmented WS message
%% which we echo back when all fragments are in, returning to
%% initial state.
-record(state, {frag_type = none, % fragment type
acc = <<>>}). % accumulate fragment data
%% unfragmented text message
handle_message(#ws_frame_info{fin=1, opcode=text, data=Data},
#state{frag_type=none, acc = <<>>}=State) ->
case unicode:characters_to_binary(Data, utf8, utf8) of
Data -> {reply, {text, Data}, State};
_ -> {close, {1007, <<"invalid utf-8">>}}
end;
%% start of a fragmented text message
handle_message(#ws_frame_info{fin=0, opcode=text, data=Data},
#state{frag_type=none, acc = <<>>}) ->
case unicode:characters_to_binary(Data, utf8, utf8) of
Data ->
{noreply, #state{frag_type=text, acc={[Data], <<>>}}};
{incomplete, Dec, Rest} ->
{noreply, #state{frag_type=text, acc={[Dec], Rest}}};
_ ->
{close, {1007, <<"invalid utf-8">>}}
end;
%% non-final continuation of a fragmented text message
handle_message(#ws_frame_info{fin=0, data=Data, opcode=continuation},
#state{frag_type=text, acc={Dec0, Rest0}}=State) ->
Data1 = <<Rest0/binary, Data/binary>>,
case unicode:characters_to_binary(Data1, utf8, utf8) of
Data1 ->
{noreply, State#state{acc={[Data1|Dec0], <<>>}}};
{incomplete, Dec1, Rest1} ->
{noreply, State#state{acc={[Dec1|Dec0], Rest1}}};
_ ->
{close, {1007, <<"invalid utf-8">>}}
end;
%% end of text fragmented message
handle_message(#ws_frame_info{fin=1, opcode=continuation, data=Data},
#state{frag_type=text, acc={Dec, Rest}}) ->
Data1 = <<Rest/binary, Data/binary>>,
case unicode:characters_to_binary(Data1, utf8, utf8) of
Data1 ->
Msg = list_to_binary(lists:reverse([Data1|Dec])),
{reply, {text, Msg}, #state{}};
_ ->
{close, {1007, <<"invalid utf-8">>}}
end;
%% unfragmented binary message
handle_message(#ws_frame_info{fin=1, opcode=binary, data=Data},
#state{frag_type=none, acc = <<>>}=State) ->
{reply, {binary, Data}, State};
%% start of a fragmented binary message
handle_message(#ws_frame_info{fin=0, opcode=binary, data=Data},
#state{frag_type=none, acc = <<>>}) ->
{noreply, #state{frag_type=binary, acc=Data}};
%% non-final continuation of a fragmented binary message
handle_message(#ws_frame_info{fin=0, data=Data, opcode=continuation},
#state{frag_type=binary, acc=FragAcc}=State) ->
{noreply, State#state{acc = <<FragAcc/binary,Data/binary>>}};
%% end of binary fragmented message
handle_message(#ws_frame_info{fin=1, opcode=continuation, data=Data},
#state{frag_type=binary, acc=FragAcc}) ->
Unfragged = <<FragAcc/binary, Data/binary>>,
{reply, {binary, Unfragged}, #state{}};
handle_message(#ws_frame_info{opcode=ping, data=Data}, State) ->
io:format("replying pong to ping~n",[]),
{reply, {pong, Data}, State};
handle_message(#ws_frame_info{opcode=pong}, State) ->
%% A response to an unsolicited pong frame is not expected.
%% http://tools.ietf.org/html/\
%% draft-ietf-hybi-thewebsocketprotocol-08#section-4
io:format("ignoring unsolicited pong~n",[]),
{noreply, State};
%% According to RFC 6455 section 5.4, control messages like close
%% MAY be injected in the middle of a fragmented message, which is
%% why we pass FragType and FragAcc along below. Whether any clients
%% actually do this in practice, I don't know.
handle_message(#ws_frame_info{opcode=close, length=Len,
data=Data, ws_state=WSState},
_State) ->
Reason = case Len of
0 -> {1000, <<>>};
1 -> {1002, <<"protocol error">>};
_ ->
<<Status:16/big, Msg/binary>> = Data,
case unicode:characters_to_binary(Msg, utf8, utf8) of
Msg -> {check_close_code(Status, WSState), Msg};
_ -> {1007, <<"invalid utf-8">>}
end
end,
io:format("got close. reply reason: ~p~n", [Reason]),
{close, Reason};
handle_message(#ws_frame_info{}=FrameInfo, State) ->
io:format("WS Endpoint Unhandled message: ~p~n~p~n", [FrameInfo, State]),
{close, {1002, <<"protocol error">>}};
handle_message({fail_connection, Status, Msg}, State) ->
io:format("Connection failure: ~p:~p~n~p~n", [Status, Msg, State]),
{close, {Status, Msg}}.
%% The checks for close status codes here are based on RFC 6455 and on the
%% autobahn testsuite (http://autobahn.ws/testsuite).
check_close_code(Code, WSState) ->
if
Code >= 3000 andalso Code =< 4999 ->
Code;
Code < 1000 ->
1002;
Code == 1006 andalso WSState#ws_state.sock == undefined ->
Code;
Code >= 1004 andalso Code =< 1006 ->
1002;
Code > 1011 ->
1002;
true ->
Code
end.
Jump to Line
Something went wrong with that request. Please try again.