Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor WebSockets and add support of optional callback functions
Main changes: * Fix some bugs about UTF-8 encoding and messages fragmentation * Add support of optional callback functions * Add support of many startup options * Add support of outgoing fragmented messages * Add a websocket testsuite - * - *** bug fixes *** First of all, an huge part of yaws_websocket.erl was rewritten to fix bugs about the messages fragmentation and the UTF-8 encoding of incoming text messages: * UTF-8 encoding before, when a text message was fragmented, only the first frame was checked and partial UTF-8 sequences were not supported. Now, checks are done on each message part and a partial UTF-8 sequence at the end of a frame is accumulated and checked with the next frame (for basic callback only). * Messages fragmentation for basic callback modules, because of a buggy mapping between frames and messages, the messages fragmentation was almost unusable. To fix this, the message handling was rewritten. Now, all tests in the autobahn testsuite[1] pass successfully. - * - *** Optional callback functions *** Then, from an idea of François de Metz[2], yaws_websocket module was extended to support optional callback functions. See the documentation for details (www/websockets.yaws). Quickly, optional callback functions are: * Module:init/1 (for basic and advanced callback modules) * Module:terminate/2 (for basic and advanced callback modules) * Module:handle_open/2 (for basic and advanced callback modules) * Module:handle_info/2 (for basic and advanced callback modules) * Module:handle_message/2 (for basic callback modules only, used in place of Module:handle_message/1) Thanks to Pablo Vieytes[3] which added handle_info to optional callback functions. - * - *** Startup options *** To start a websocket process a script must return the following term from its out/1 function: {websocket, CallbackMod, Options} where 'Options' is a (possibly empty) proplist. Following parameters are supported: * {origin, Orig} * {callback, Type} * {keepalive, Boolean} * {keepalive_timeout, Tout} * {keepalive_grace_period, Time} * {drop_on_timeout, Boolean} * {close_timeout, Tout} * {close_if_unmasked, Boolean} * {max_frame_size, Int} * {max_message_size, Int} * {auto_fragment_message, Boolean} * {auto_fragment_threshold, Int} See the documentation for details (www/websockets.yaws). - * - *** Outgoing fragmented messages *** A callback module can now send fragmented messages to clients using the record #ws_frame{}: #ws_frame{fin = true, %% true | false rsv = 0, opcode, %% text | binary | continuation... payload = <<>>}. %% binary(), unmasked data -- [1] http://autobahn.ws/testsuite [2] #99 [3] https://github.com/pvieytes
- Loading branch information
Christopher Faulet
committed
Feb 11, 2013
1 parent
c341c31
commit 29a7989
Showing
20 changed files
with
4,295 additions
and
704 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
%%%=========================================================== | ||
%%% compiled using erlc -I include src/basic_echo_callback.erl | ||
%%%=========================================================== | ||
|
||
-module(basic_echo_callback_extended). | ||
|
||
-include("yaws_api.hrl"). | ||
|
||
%% Export for websocket callbacks | ||
-export([init/1, terminate/2, handle_open/2, handle_message/2, handle_info/2]). | ||
|
||
%% Export for apply | ||
-export([say_hi/1]). | ||
|
||
-record(state, {nb_texts=0, nb_bins=0}). | ||
|
||
init([_Arg, Params]) -> | ||
io:format("Initalize ~p: ~p~n", [self(), Params]), | ||
{ok, #state{}}. | ||
|
||
handle_open(WSState, State) -> | ||
yaws_websockets:send(WSState, {text, <<"Welcome !">>}), | ||
{ok, State}. | ||
|
||
handle_message({text, <<"bye">>}, #state{nb_texts=N, nb_bins=M}=State) -> | ||
io:format("User said bye. ~p text / ~p binary messages echoed ~n", [N, M]), | ||
NbTexts = list_to_binary(integer_to_list(N)), | ||
NbBins = list_to_binary(integer_to_list(M)), | ||
Messages = [ | ||
{text, <<"Goodbye !">>}, | ||
{text, <<NbTexts/binary, " text messages echoed">>}, | ||
{text, <<NbBins/binary, " binary messages echoed">>} | ||
], | ||
{close, {1000, <<"bye">>}, Messages, State}; | ||
|
||
handle_message({text, <<"something">>}, State) -> | ||
io:format("Some action without a reply~n", []), | ||
{noreply, State}; | ||
|
||
handle_message({text, <<"say hi later">>}, State) -> | ||
timer:apply_after(3000, ?MODULE, say_hi, [self()]), | ||
{noreply, State}; | ||
|
||
handle_message({text, <<"fragmented message">>}, State) -> | ||
io:format("Send a message fragmented in 3 frames~n", []), | ||
Frag1 = #ws_frame{fin = false, | ||
opcode = text, | ||
payload = <<"frag1">>}, | ||
Frag2 = #ws_frame{fin = false, | ||
opcode = continuation, | ||
payload = <<"frag2">>}, | ||
Frag3 = #ws_frame{fin = true, | ||
opcode = continuation, | ||
payload = <<"frag3">>}, | ||
{reply, [Frag1, Frag2, Frag3], State}; | ||
|
||
handle_message({text, Msg}, #state{nb_texts=N}=State) -> | ||
io:format("Receive text message (N=~p): ~p bytes~n", [N, byte_size(Msg)]), | ||
{reply, {text, Msg}, State#state{nb_texts=N+1}}; | ||
|
||
handle_message({binary, Msg}, #state{nb_bins=M}=State) -> | ||
io:format("Receive binary message (M=~p): ~p bytes~n", [M, byte_size(Msg)]), | ||
{reply, {binary, Msg}, State#state{nb_bins=M+1}}; | ||
|
||
handle_message({close, Status, Reason}, _) -> | ||
io:format("Close connection: ~p - ~p~n", [Status, Reason]), | ||
{close, Status}. | ||
|
||
|
||
handle_info(timeout, State) -> | ||
io:format("process timed out~n", []), | ||
{reply, {text, <<"Anybody Else ?">>}, State}; | ||
handle_info(_Info, State) -> | ||
{noreply, State}. | ||
|
||
terminate(Reason, State) -> | ||
io:format("terminate ~p: ~p (state:~p)~n", [self(), Reason, State]), | ||
ok. | ||
|
||
say_hi(Pid) -> | ||
io:format("asynchronous greeting~n", []), | ||
yaws_api:websocket_send(Pid, {text, <<"hi there!">>}). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.