SockJS family:
- SockJS-client JavaScript client library
- SockJS-node Node.js server
- SockJS-erlang Erlang server
SockJS server written in Erlang. Can run with Cowboy http server. SockJS-erlang is in core web-framework agnostic (up to version v0.2.1 we also supported Misultin). SockJS-erlang is compatible with SockJS client version 0.3. See https://github.com/sockjs/sockjs-client for more information on SockJS.
A simplistic echo SockJS server using Cowboy may look more or less like this:
main(_) ->
ok = application:start(xmerl),
ok = application:start(sockjs),
ok = application:start(ranch),
ok = application:start(crypto),
ok = application:start(cowboy),
SockjsState = sockjs_handler:init_state(
<<"/echo">>, fun service_echo/3, state, []),
Routes = [{'_', [{<<"/echo/[...]">>,
sockjs_cowboy_handler, SockjsState}]}],
Dispatch = cowboy_router:compile(Routes),
cowboy:start_http(cowboy_test_http_listener, 100,
[{port, 8081}],
[{env, [{dispatch, Dispatch}]}]),
receive
_ -> ok
end.
service_echo(_Conn, init, state) -> {ok, state};
service_echo(Conn, {recv, Data}, state) -> Conn:send(Data); % or: sockjs:send(Data, Conn)
service_echo(_Conn, {info, _Info}, state) -> {ok, state};
service_echo(_Conn, closed, state) -> {ok, state}.
Dig into the examples
directory to get working code:
You may need a recent version of Erlang/OTP, at least R14B is recommended.
To run Cowboy example:
cd sockjs-erlang
./rebar get-deps
./rebar compile
./examples/cowboy_echo.erl
This will start a simple /echo
SockJS server on
http://localhost:8081
. Open this link in a browser and play
around.
Except for the web framework-specific API's, SockJS-erlang is rather simple. It has just a couple of methods:
-
sockjs_handler:init_state(Prefix, Callback, State, Options) -> service()
Initializes the state of a SockJS service (ie: a thing you can access from the browser, it has an url and a code on the server side).
prefix
is a binary that must exacty match the url prefix of the service, for example, if service will be listening on '/echo', this parameter must be set to<<"/echo">>
.callback
function will be called when a new SockJS connection is established, data received or a connection is closed. The value ofstate
will be passed to the callback and preserved if returned value has changed. Options is a proplist that can contain following tuples:{sockjs_url, string()}
- Transports which don't support cross-domain communication natively ('eventsource' to name one) use an iframe trick. A simple page is served from the SockJS server (using its foreign domain) and is placed in an invisible iframe. Code run from this iframe doesn't need to worry about cross-domain issues, as it's being run from domain local to the SockJS server. This iframe also does need to load SockJS javascript client library, and this option lets you specify its url (if you're unsure, point it to the latest minified SockJS client release, this is the default).{websocket, boolean()}
- are native websockets enabled? This can be usefull when your loadbalancer doesn't support them.{cookie_needed, boolean()}
- is your load balancer relying on cookies to get sticky sessions working?{heartbeat_delay, integer()}
- how often to send heartbeat packets (in ms).{disconnect_delay, integer()}
- how long to hold session state after the client was last connected (in ms).{response_limit, integer()}
- the maximum size of a single http streaming response (in bytes).{logger, fun/3}
- a function called on every request, used to print request to the logs (or on the screen by default).
For more explanation, please do take a look at SockJS-node readme.
-
Connection:send(Payload) -> ok
-
sockjs:send(Payload, Connection) -> ok
Send data over an active SockJS connection. Payload should be of iodata() type. Messages sent after connection gets closed will be lost.
-
Connection:close(Code, Reason) -> ok
-
sockjs:close(Code, Reason, Connection) -> ok
-
sockjs:close(Connection) -> ok
Close an active SockJS connection with code and reason. If code and reason are skipped, the defaults are used.
-
Connection:info() -> proplist()
-
sockjs:info(Connection) -> proplist()
Sometimes you may want to know more about the underlying connection. This method returns a proplist with few attributes extracted from the first HTTP/websocket request that was coming to this connection. You should see:
* peername - ip address and port of the remote host * sockname - ip address and port of the local endpoint * path - the path used by the request that started the connection * headers - a set of headers extracted from the request that may be handy (don't expect to retrieve Cookie header).
The framework-specific calls are more problematic. Instead of trying to explain how to use them, please take a look at the examples.
- type(req() :: {cowboy, request()})
- sockjs_handler:handle_req(service(), req()) -> req()
- sockjs_handler:handle_ws(service(), req()) -> req()
-
sockjs:send(Payload, Channel) -> ok
Send data over a channel. Payload should be of iodata() type. Messages sent after connection gets closed will be lost.
-
sockjs:close(Code, Reason, Channel) -> ok
-
sockjs:close(Channel) -> ok
Close a channel with code and reason. If code and reason are skipped, the defaults are used. But actually, code and reason are not sent to client.
-
sockjs:info(Channel) -> proplist()
Sometimes you may want to know more about the underlying connection. This method returns a proplist with few attributes extracted from the first HTTP/websocket request that was coming to this connection. You should see:
* peername - ip address and port of the remote host * sockname - ip address and port of the local endpoint * path - the path used by the request that started the connection * headers - a set of headers extracted from the request that may be handy (don't expect to retrieve Cookie header).
-
sockjs:to_session(Channel) -> conn()
Convert a channel to connection.
-
sockjs:to_channel(Conn, Topic) -> channel()
Convert a connection to channel with specific topic (channel name).
-
sockjs_multiplex:init_state(Services, {AuthenCallback, Options})
Sometimes you don't want client access directly your channel services and you want to do something with client first. If you use authentication callback, you can decide when allow client to use your services. It's quite simple, see example. Valid authentication callback options:
* `{state, list()}` - initial state of authentication callback
If you do not use multiplexing, you can also implement this mechanism, see example.
-
sockjs_multiplex:init_state(Services)
Initialize state without authentication callback.
- Use jiffy to encode/decode JSON. I use it for better performance, see benchmark from kivikakk.ee.
- Use the latest version of Cowboy (0.10.0).
SockJS-erlang is quite new, but should be reasonably stable. Cowboy is passes all the SockJS-protocol tests.
SockJS servers should work well behind many load balancer setups, but it sometimes requres some additional twaks. For more details, please do take a look at the 'Deployment' section in SockJS-node readme.
You need rebar (instructions). Due to a bug in rebar config handling you need a reasonably recent version - newer than late Oct 2011. Alternatively, SockJS-erlang is bundeled with a recent rebar binary.
SockJS-erlang contains a test_server
, a simple server used for
testing.
To run Cowboy test_server:
cd sockjs-erlang
./rebar get-deps
./rebar compile
./examples/cowboy_test_server.erl
That should start test_server on port 8081. Currently, there are two separate test suits using test_server.
Once test_server is listening on http://localhost:8081
you may test it
using SockJS-protocol:
cd sockjs-protocol
make test_deps
./venv/bin/python sockjs-protocol-dev.py
For details see SockJS-protocol README.
You need to start a second web server (by default listening on 8080) that is serving various static html and javascript files:
cd sockjs-client
make test
At that point you should have two web servers running: sockjs-erlang on 8081 and sockjs-client on 8080. When you open the browser on http://localhost:8080/ you should be able run the QUnit tests against your sockjs-node server.
For details see SockJS-client README.
Additionally, if you're doing more serious development consider using
make serve
, which will automatically the server when you modify the
source code.