Misultin: erlang and websockets

Roberto Ostinelli - - January 20, 2010

Inspired by Joe Armstrong’s post, I’ve recently added websocket support to misultin v0.4, my Erlang library for building fast lightweight HTTP servers.

Basically, websockets allow a two-way asynchronous communication between browser and servers, filling the gap that some technologies such as ajax and comet have tried to fulfill in these recent years. If you want to try this out yourself, you will first need to grab a browser which implements websockets, such as Google Chrome.

The typical html page with javascript code to use websockets is as follows:

<html> <head> <script type="text/javascript"> function addStatus(text){ var date = new Date(); document.getElementById('status')[removed] = document.getElementById('status')[removed] + date + ": " + text + "<br>"; } function ready(){ if ("WebSocket" in window) { // browser supports websockets var ws = new WebSocket("ws://localhost:8080/service"); ws.onopen = function() { // websocket is connected addStatus("websocket connected!"); // send hello data to server. ws.send("hello server!"); addStatus("sent message to server: 'hello server'!"); }; ws.onmessage = function (evt) { var receivedMsg = evt.data; addStatus("server sent the following: '" + receivedMsg + "'"); }; ws.onclose = function() { // websocket was closed addStatus("websocket was closed"); }; } else { // browser does not support websockets addStatus("sorry, your browser does not support websockets."); } } </script> </head> <body onload="ready();"> <div id="status"></div> </body>
</html>

Here’s the code to use misultin to handle the requests of this script:

-module(misultin_websocket_example).
-export([start/1, stop/0]). % start misultin http server
start(Port) -> misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, {ws_loop, fun(Ws) -> handle_websocket(Ws) end}]). % stop misultin
stop() -> misultin:stop(). % callback on request received
handle_http(Req, Port) -> % output Req:ok([]). % callback on received websockets data
handle_websocket(Ws) -> receive {browser, Data} -> Ws:send(["received '", Data, "'"]), handle_websocket(Ws); _Ignore -> handle_websocket(Ws) after 5000 -> Ws:send("pushing!"), handle_websocket(Ws) end.

handle_websocket/1 is spawned by misultin to handle the connected websockets. Data coming from a browser will be sent to this process and will have the message format {browser, Data}, where Data is a string(). If you need to send data to the browser, you may do so by using the parametrized function Ws:send(Data), Data being a string() or an iolist().

Compile and run the example here above with misultin_websocket_example:start(8080). Then, open up your Chrome (or other websocket compliant browser) and point it to an .html file containing the above code.

You should normally see this being gradually printed on your browser:

Wed Jan 20 2010 15:18:52 GMT+0100 (CET): websocket connected!
Wed Jan 20 2010 15:18:52 GMT+0100 (CET): sent message to server: 'hello server'!
Wed Jan 20 2010 15:18:52 GMT+0100 (CET): server sent the following: 'received 'hello server!''
Wed Jan 20 2010 15:18:57 GMT+0100 (CET): server sent the following: 'pushing!'
Wed Jan 20 2010 15:19:02 GMT+0100 (CET): server sent the following: 'pushing!'
Wed Jan 20 2010 15:19:07 GMT+0100 (CET): server sent the following: 'pushing!'

In normal environments you may consider serving the .html page from misultin directly. You may do so with the following and complete misultin module:

-module(misultin_websocket_example).
-export([start/1, stop/0]). % start misultin http server
start(Port) -> misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, {ws_loop, fun(Ws) -> handle_websocket(Ws) end}]). % stop misultin
stop() -> misultin:stop(). % callback on request received
handle_http(Req, Port) -> % output Req:ok([{"Content-Type", "text/html"}], [" <html> <head> <script type=\"text/javascript\"> function addStatus(text){ var date = new Date(); document.getElementById('status')[removed] = document.getElementById('status')[removed] + date + \": \" + text + \"<br>\"; } function ready(){ if (\"WebSocket\" in window) { // browser supports websockets var ws = new WebSocket(\"ws://localhost:", integer_to_list(Port) ,"/service\"); ws.onopen = function() { // websocket is connected addStatus(\"websocket connected!\"); // send hello data to server. ws.send(\"hello server!\"); addStatus(\"sent message to server: 'hello server'!\"); }; ws.onmessage = function (evt) { var receivedMsg = evt.data; addStatus(\"server sent the following: '\" + receivedMsg + \"'\"); }; ws.onclose = function() { // websocket was closed addStatus(\"websocket was closed\"); }; } else { // browser does not support websockets addStatus(\"sorry, your browser does not support websockets.\"); } } </script> </head> <body onload=\"ready();\"> <div id=\"status\"></div> </body> </html>"]). % callback on received websockets data
handle_websocket(Ws) -> receive {browser, Data} -> Ws:send(["received '", Data, "'"]), handle_websocket(Ws); _Ignore -> handle_websocket(Ws) after 5000 -> Ws:send("pushing!"), handle_websocket(Ws) end.

Please note that the Websocket Protocol still is draft. use with caution.



Categories: Blogs  Roberto Ostinelli  

Comments

anonymous avatar

I love working with AJAX, but like you mentioned, the gap between browsers and servers is a bit cumbersome sometimes.(seems there is always some hitch no matter what you code with) Having something to bridge that gap would be phenomenal. Draft or not, if the Websocket Protocol is working to fix that issue, I am all for it.

Posted by Nelson M. on 06 Jan 2011 at 15:58



 


Add comment

Name:

Email:

URL:

Smileys

Remember my personal information

Notify me of follow-up comments?