Skip to content

Commit

Permalink
Merge pull request sanmiguel#25 from sanmiguel/mc-gh20-update-docs-ex…
Browse files Browse the repository at this point in the history
…amples

Update docs and examples to work with 0.8.4
  • Loading branch information
sanmiguel committed Jul 20, 2015
2 parents 858bee2 + 7de2b23 commit 8c35946
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 43 deletions.
60 changes: 27 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,28 @@
## Existing features

1. Client to Server Masking
2. gen_server like callback behaviour
2. OTP compliant
3. Callback-driven behaviour
3. Handshake validation
4. tcp and ssl support
4. TCP and SSL support
5. Handling of text, binary, ping, pong, and close frames
6. Handling of continuation frames
7. Automated ping/pong and keepalive

## Usage

For basic usage, please see the source files in the `examples/`
directory. Writing a handler is easy:
For basic usage, see `examples/sample_ws_handler.erl`:

```erlang
-module(sample_ws_handler).

-behaviour(websocket_client_handler).
-behaviour(websocket_client).

-export([
start_link/0,
init/2,
init/1,
onconnect/2,
ondisconnect/2,
websocket_handle/3,
websocket_info/3,
websocket_terminate/3
Expand All @@ -34,15 +37,21 @@ start_link() ->
ssl:start(),
websocket_client:start_link("wss://echo.websocket.org", ?MODULE, []).

init([], _ConnState) ->
init([]) ->
{once, 2}.

onconnect(_WSReq, State) ->
websocket_client:cast(self(), {text, <<"message 1">>}),
{ok, 2}.
{ok, State}.

ondisconnect({remote, closed}, State) ->
{reconnect, State}.

websocket_handle({pong, _}, _ConnState, State) ->
{ok, State};
websocket_handle({text, Msg}, _ConnState, 5) ->
io:format("Received msg ~p~n", [Msg]),
{close, <<>>, 10};
{close, <<>>, "done"};
websocket_handle({text, Msg}, _ConnState, State) ->
io:format("Received msg ~p~n", [Msg]),
timer:sleep(1000),
Expand All @@ -52,46 +61,31 @@ websocket_handle({text, Msg}, _ConnState, State) ->
websocket_info(start, _ConnState, State) ->
{reply, {text, <<"erlang message received">>}, State}.

websocket_terminate({close, Code, Payload}, _ConnState, State) ->
io:format("Websocket closed in state ~p wih code ~p and payload ~p~n",
[State, Code, Payload]),
websocket_terminate(Reason, _ConnState, State) ->
io:format("Websocket closed in state ~p wih reason ~p~n",
[State, Reason]),
ok.
```

The above code will send messages to the echo server that count up
from 1. It will also print all replies from the server:
from 1 through 4. It will also print all replies from the server:

```
Received msg <<"this is message 1">>
Received msg <<"hello, this is message #2">>
Received msg <<"hello, this is message #3">>
Received msg <<"hello, this is message #4">>
Received msg <<"hello, this is message #5">>
Received msg <<"hello, this is message #6">>
...
```

Erlang is typically used to write server applications. Now that
applications like `cowboy` supporting websocket applications are more
commonplace, it is important to have a compliant websocket client for
benchmarking and debugging purposes.

This client implements a cowboy like `websocket_client_handler` to
interact with a websocket server. Currently, it can connect via tcp or
ssl via the `ws` and `wss` protocols. It can also send and receive
contiguous text or binary websocket frames.

## TODO

The client as is is still missing a lot of functionality. We still
need to:

1. Close the connection in a number of error cases (malformed headers,
etc).
2. Add tests!! (hint hint)
3. Stop using `verify_none` by default
The client has been significantly reworked, now backed by `gen_fsm`. There may still be bugs.
Please report them.

This is being released without the above functionality in case it is
useful as is for benchmarking or debugging as mentioned above (this is
the case for me). The hope is that the community (and me, as time
permits) will contribute key pieces to the codebase so that the major
pieces are taken care of.
1. Stop using `verify_none` by default
2. Add more complete testing - preferably based on / using Autobahn.
4 changes: 2 additions & 2 deletions examples/sample_ws_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ start_link() ->
websocket_client:start_link("wss://echo.websocket.org", ?MODULE, []).

init([]) ->
websocket_client:cast(self(), {text, <<"message 1">>}),
{ok, 2}.
{once, 2}.

onconnect(_WSReq, State) ->
websocket_client:cast(self(), {text, <<"message 1">>}),
{ok, State}.

ondisconnect({remote, closed}, State) ->
Expand Down
2 changes: 1 addition & 1 deletion examples/ws_ping_example.erl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ start_link(URI) ->
websocket_client:start_link(URI, ?MODULE, []).

init([]) ->
{ok, 1}.
{once, 1}.

onconnect(_WSReq, 1) ->
websocket_client:cast(self(), {text, <<"message 1">>}),
Expand Down
45 changes: 38 additions & 7 deletions src/websocket_client.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-module(websocket_client).
%% @author Jeremy Ong
%% @author Michael Coles
%% @doc Erlang websocket client (FSM implementation)
-module(websocket_client).

-behaviour(gen_fsm).
%-compile([export_all]).
Expand Down Expand Up @@ -35,34 +35,55 @@
-type close_type() :: normal | error | remote.
-type reason() :: term().

% Create handler state based on options.
-callback init(list()) ->
{ok, state()}
| {once, state()}
| {reconnect, state()}.
%| {ok, state(), keepalive()}.
%% TODO FIXME Actually handle the keepalive case
{ok, state()} % Will start `disconnected`.
| {once, state()} % Will attempt to connect once only.
| {reconnect, state()}. % Will keep trying to connect.

% Called when a websocket connection is established, including
% successful handshake with the other end.
-callback onconnect(websocket_req:req(), state()) ->
{ok, state()}
% Simple client: only server-initiated pings will be
% automatically responded to.
{ok, state()}
% Keepalive client: will automatically initiate a ping to the server
% every keepalive() ms.
| {ok, state(), keepalive()}
% Immediately send a message to the server.
| {reply, websocket_req:frame(), state()}
% Close the connection.
| {close, binary(), state()}.

% Called when the socket is closed for any reason.
-callback ondisconnect(reason(), state()) ->
% Return to `disconnected` state but keep process alive.
{ok, state()}
% Immediately attempt to reconnect.
| {reconnect, state()}
% Shut the process down cleanly.
| {close, reason(), state()}.

% Called for every received frame from the server.
% NB this will also get called for pings, which are automatically ponged.
-callback websocket_handle({text | binary | ping | pong, binary()}, websocket_req:req(), state()) ->
% Do nothing.
{ok, state()}
% Send the given frame to the server.
| {reply, websocket_req:frame(), state()}
% Shut the process down cleanly.
| {close, binary(), state()}.

% Called for any received erlang message.
-callback websocket_info(any(), websocket_req:req(), state()) ->
% Do nothing.
{ok, state()}
% Send the given frame to the server.
| {reply, websocket_req:frame(), state()}
% Shut the process down cleanly.
| {close, binary(), state()}.

% Called when the process exits abnormally.
-callback websocket_terminate({close_type(), term()} | {close_type(), integer(), binary()},
websocket_req:req(), state()) ->
ok.
Expand All @@ -81,11 +102,21 @@
}).

%% @doc Start the websocket client
%%
%% URL : Supported schema: (ws | wss)
%% Handler: module()
%% Args : arguments to pass to Handler:init/1
-spec start_link(URL :: string(), Handler :: module(), Args :: list()) ->
{ok, pid()} | {error, term()}.
start_link(URL, Handler, Args) ->
start_link(URL, Handler, Args, []).

%% @doc Start the websocket client
%%
%% Supported Opts:
%% - {keepalive, integer()}: keepalive timeout in ms
%% - {extra_headers, list({K, V})}: a kv-list of headers to send in the handshake
%% (useful if you need to add an e.g. 'Origin' header on connection.
start_link(URL, Handler, HandlerArgs, Opts) when is_list(Opts) ->
case http_uri:parse(URL, [{scheme_defaults, [{ws,80},{wss,443}]}]) of
{ok, {Protocol, _, Host, Port, Path, Query}} ->
Expand Down

0 comments on commit 8c35946

Please sign in to comment.