NAME
Dancer2::Plugin::WebSocket - add a websocket interface to your Dancers app
VERSION
version 0.1.1
SYNOPSIS
bin/app.psgi
:
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../lib";
use Plack::Builder;
use MyApp;
builder {
mount( MyApp->websocket_mount );
mount '/' => MyApp->to_app;
}
config.yml
:
plugins:
WebSocket:
# default values
serializer: 0
mount_path: /ws
MyApp.pm
:
package MyApp;
use Dancer2;
use Dancer2::Plugin::WebSocket;
websocket_on_message sub {
my( $conn, $message ) = @_;
$conn->send( $message . ' world!' );
};
get '/' => sub {
my $ws_url = websocket_url;
return <<"END";
<html>
<head><script>
var urlMySocket = "$ws_url";
var mySocket = new WebSocket(urlMySocket);
mySocket.onmessage = function (evt) {
console.log( "Got message " + evt.data );
};
mySocket.onopen = function(evt) {
console.log("opening");
setTimeout( function() {
mySocket.send('hello'); }, 2000 );
};
</script></head>
<body><h1>WebSocket client</h1></body>
</html>
END
};
true;
DESCRIPTION
Dancer2::Plugin::WebSocket
provides an interface to Plack::App::WebSocket
and allows to interact with the webSocket connections within the Dancer app.
Plack::App::WebSocket, and thus this plugin, requires a plack server that supports the psgi streaming, nonblocking and io. Twiggy is the most popular server that fits the bill.
CONFIGURATION
-
serializer
If serializer is set to a
true
value, messages will be assumed to be JSON objects and will be automatically encoded/decoded using a JSON::MaybeXS serializer. If the value ofserialier
is a hash, it'll be passed as arguments to the JSON::MaybeXS constructor.plugins: WebSocket: serializer: utf8: 1 allow_nonref: 1
By the way, if you want the connection to automatically serialize data structures to JSON on the client side, you can do something like
var mySocket = new WebSocket(urlMySocket); mySocket.sendJSON = function(message) { return this.send(JSON.stringify(message)) }; // then later... mySocket.sendJSON({ whoa: "auto-serialization ftw!" });
-
mount_path
Path for the websocket mountpoint. Defaults to
/ws
.
PLUGIN KEYWORDS
In the various callbacks, the connection object that is passed is a Plack::App::WebSocket::Connection object augmented with the Dancer2::Plugin::WebSocket::Connection role.
websocket_on_open sub { ... }
websocket_on_open sub {
my( $conn, $env ) = @_;
...;
};
Code invoked when a new socket is opened. Gets the new
connection
object and the Plack
$env
hash as arguments.
websocket_on_close sub { ... }
websocket_on_close sub {
my( $conn ) = @_;
...;
};
Code invoked when a new socket is opened. Gets the connection object as argument.
websocket_on_error sub { ... }
websocket_on_error sub {
my( $env ) = @_;
...;
};
Code invoked when an error is detected. Gets the Plack
$env
hash as argument and is expected to return a
Plack triplet.
If not explicitly set, defaults to
websocket_on_error sub {
my $env = shift;
return [
500,
["Content-Type" => "text/plain"],
["Error: " . $env->{"plack.app.websocket.error"}]
];
};
websocket_on_message sub { ... }
websocket_on_message sub {
my( $conn, $message ) = @_;
...;
};
Code invoked when a message is received. Gets the connection object and the message as arguments.
Note that while websocket_on_message
fires for all messages receives, you can
also be a little more selective. Indeed, each connection, being a Plack::App::WebSocket::Connection
object, can have its own (multiple) handlers. So you can do things like
websocket_on_open sub {
my( $conn, $env ) = @_;
$conn->on( message => sub {
my( $conn, $message ) = @_;
warn "I'm only being executed for messages sent via this connection";
});
};
websocket_url
Returns the full url of the websocket mountpoint.
# assuming host is 'localhost:5000'
# and the mountpoint is '/ws'
print websocket_url; # => ws://localhost:5000/ws
websocket_mount
Returns the mountpoint and the Plack app coderef to be
used for mount
in app.psgi
. See the SYNOPSIS.
GOTCHAS
It seems that the closing the socket causes Google's chrome to burp the following to the console:
WebSocket connection to 'ws://...' failed: Received a broken close frame containing a reserved status code.
Firefox seems to be happy, though. The issue is probably somewhere deep in AnyEvent::WebSocket::Server. Since the socket is being closed anyway, I am not overly worried about it.
SEE ALSO
This plugin is nothing much than a sugar topping atop Plack::App::WebSocket, which is itself AnyEvent::WebSocket::Server wrapped in Plackstic.
Mojolicious also has nice WebSocket-related offerings. See Mojolicious::Plugin::MountPSGI or http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Web-server-embedding. (hi Joel!)
AUTHOR
Yanick Champoux yanick@cpan.org
COPYRIGHT AND LICENSE
This software is copyright (c) 2017 by Yanick Champoux.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.