/cowboy-websocket-template

A stripped down Cowboy webserver and template for communicating with client with websockets

Primary LanguageJavaScriptBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

cowboy-websocket-template

A stripped down Cowboy webserver and template for communicating with client with websockets.

Getting Cowboy

This project uses rebar (https://github.com/basho/rebar) for dependency management. If you use rebar as well you can get Cowboy by adding the entry shown below to your rebar.config file.

{deps, [
	{cowboy, ".*",
		{git, "git://github.com/extend/cowboy.git", "1.0.x"}}
]}.

If you don't use rebar then take note that this tutorial uses version 1.0 of Cowboy and will not work with the latest versions. For further help getting Cowboy without rebar I would suggest following 99s getting started guide found here: http://ninenines.eu/docs/en/cowboy/1.0/guide/getting_started/.

Integration Instructions

This template project is just a standard Erlang application. Much of the code contained in the src directory is just standard boilerplate for running an application and will be eliminated by your own application configuration.

To get the demo running there are two source files that will be added to your project unmodified, toppage_handler.erl and ws_handler.erl. There are also a few items that are illustrated in the .app file and template_app.erl that will need to be added to your app file and its application start function.

Start by copying all of the contents of template/priv to your projects priv directory. The priv directory is where the client-side files are kept. The main website is priv/html_ws_client.html this currently contains all of the html to render the main page and the basic Javascript to handle sending to and recieving from the websocket connection.

Next start by copying src/toppage_handler.erl and src/ws_handler.erl to your source directory. The toppage handler just serves the main html page and the ws handler is what you will customize handle websockets. Next open up your app file. The template's application resource file just contains this:

{application, template, [
	{description, "A simple template containing a Cowboy webserver and websocket communication"},
	{vsn, "1"},
	{modules, []},
	{registered, []},
	{applications, [
		kernel,
		stdlib,
		cowboy
	]},
	{mod, {template_app, []}},
	{env, []}
]}.

You just need to modify your app file so that the cowboy is listed in the applications which are started before your application starts. Next open up your application's callback module, the template project's callback module is template_app.erl.

%% The contents of template_app.erl
-module(template_app).
-behaviour(application).

%% API.
-export([start/2]).
-export([stop/1]).

start(_Type, _Args) ->
    Dispatch = 
        cowboy_router:compile([
                               {'_', [
                                      {"/", toppage_handler, []},
                                      {"/websocket", ws_handler, []},
                                      {"/static/[...]", cowboy_static,
                                       {dir, "priv/static"}}]}]),
    {ok, _} = cowboy:start_http(http, 100, [{port, 8080}],
                                    [{env, [{dispatch, Dispatch}]}]),
    template_sup:start_link().

stop(_State) ->
	ok.

The callback module is where Cowboys router is configured and started. Copy the "Dispatch =..." and "{ok, _} = cowboy:start_http..." lines to your applications start function. The line beginning with dispatch maps urls to the Erlang modules that will handle those requests. In this case toppage_handler.erl handles requests to the top level directory, ws_handler.erl handles anything that talks to the "/websocket" location, and any resources requested under the static directory are handled by a built in cowboy component that serves static files. The next line that you copied actually starts a listener on port 8080 with the configuration defined in the previous line.

You should now be ready to start your application and if you connect to http://{YOUR_HOSTNAME}:8080 you should see this screen:

screenshot

If you type into the text box and hit send Erlang should just send the message back to the client with "Erlang recieved the message: " prepended onto the sent text. If you have an Erlang console that can talk to the node running Cowboy you can send messages to the client by sending an Erlang message to the process named ws_handler, e.g. ws_handler ! test will send the atom test to the webpage and you should see it appear there.

You have now integrated all of the functionality provided by this project into your own application. The following sections will be a more detailed discussion of the ws_handler and the client-side code.

Overview of ws_handler

There are five functions that you need to implement to support websocket communications using Cowboy.

  • init/3
  • websocket_init/3
  • websocket_handle/3
  • websocket_info/3
  • websocket_terminate/3

This section will briefly cover the uses of these functions.

The init functions

There are two init functions init and websocket_init. The init function always returns an upgrade tuple:

init({tcp, http}, _Req, _Opts) ->
    {upgrade, protocol, cowboy_websocket}.

You can also update the Req object and the options by returning the longer form of the tuple: {upgrade, protocol, cowboy_websocket, Req, Opts}.

After recieving this tuple Cowboy immediately calls websocket_init. websocket_init is where you place much of your custom initialization code. Typically, however, this is just used to register the process and initialize the state. Returing the "ok" tuple.

websocket_init(_Type, Req, _Opts) ->
    {ok, Req, #state{}}.

Alternatively returning the shutdown tuple, {shutdown, Req}, will send a 404 message to the client. After the ok tuple is recieved by Cowboy it will immediately perform the handshake. It should be noted that both of the init functions are called everytime a new client connects.

websocket_handle

The websocket_handle/3 function is called when Cowboy recieves a frame from the client. Frames take the form of:

frame() = close | ping | pong | {text | binary | close | ping | pong, iodata()} | {close, close_code(), iodata()}

Some action can be taken at this point.

websocket_info

When an Erlang message is recieved by the handler's process cowboy will call websocket_info\3. The handler can send frames back to the client, shutdown or do nothing at this point.

Sending to the client

Communicating back to the client can be done from either websocket_info/3 or websocket_handle/3 by returning a reply tuple. The reply tuple consists of the atom reply followed by either a single frame or a list of frames to be sent to the client, a Req object, and finally the current state.

The frame takes the form mentioned in the websocket_handle section found above.

websocket_terminate

Finally the websocket_terminate function is called to allow you to clean up the state. After this function returns the websocket connection is closed and the handler's process is stopped.

Overview of client-side code

This README will not go into much detail of the client side code. There are many good tutorials on Javascript and using websockets on the client so I wont spend much time discussing that here. Instead this will just quickly go over where everything is on the client side.

The root of the client code is the priv/ directory. In this root directory is the static directory which is served by Cowboy's static handler; src/toppage_handler.erl serves the other item in the priv directory, html_ws_client.html. html_ws_client.html is the main webpage. Within it is all the Javascript and html to render the send and recieve from websockets page.

Inside the static directory is a lib directory where the clients dependencies are stored and the css file.

Additional Resources

Here is a list of additional references or document that this was based off of. If anything said here conflicts with something from the links in this list then assume the link is correct.