# NAME
Dancer2::Plugin::WebSocket - add a websocket interface to your Dancers app
# VERSION
version 0.3.0
# SYNOPSIS
`bin/app.psgi`:
```perl
#!/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
login: 0
mount_path: /ws
```
`MyApp.pm`:
```perl
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";
WebSocket client
END
};
get '/say_hi' => sub {
$_->send([ "Hello!" ]) for websocket_connections;
};
true;
```
# DESCRIPTION
`Dancer2::Plugin::WebSocket` provides an interface to [Plack::App::WebSocket](https://metacpan.org/pod/Plack::App::WebSocket)
and allows to interact with the webSocket connections within the Dancer app.
[Plack::App::WebSocket](https://metacpan.org/pod/Plack::App::WebSocket), and thus this plugin, requires a plack server that
supports the psgi _streaming_, _nonblocking_ and _io_. [Twiggy](https://metacpan.org/pod/Twiggy)
is the most popular server fitting 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](https://metacpan.org/pod/JSON::MaybeXS)
serializer. If the value of `serializer` is a hash, it'll be passed as
arguments to the [JSON::MaybeXS](https://metacpan.org/pod/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
```perl
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 `$conn`
is a [Plack::App::WebSocket::Connection](https://metacpan.org/pod/Plack::App::WebSocket::Connection) object
augmented with the [Dancer2::Plugin::WebSocket::Connection](https://metacpan.org/pod/Dancer2::Plugin::WebSocket::Connection) role.
## websocket\_on\_open sub { ... }
```perl
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\_login sub { ... }
```perl
websocket_on_login sub {
my( $conn, $env ) = @_;
...;
};
```
Code invoked when a new socket is opened. Gets the
connection object and the Plack `$env` hash as arguments.
Example: return true if user is logged in and the webapp http\_cookie is the same as the websocket.
```perl
my $login_conn;
my $cookie_name = 'example.session';
hook before => sub {
if (defined cookies->{$cookie_name}) {
$login_conn->{'cookie_id'} = cookies->{$cookie_name}->value;
}
$login_conn->{'login'} = logged_in_user ? 1 : 0;
};
websocket_on_login sub {
my( $conn, $env ) = @_;
my ($cookie_id) = ($env->{'HTTP_COOKIE'} =~ /$cookie_name=(.*);?/g);
if (($login_conn->{'login'}) and ($login_conn->{'cookie_id'} eq $cookie_id)) {
return 1;
} else {
warn "require login";
return 0;
}
};
```
## websocket\_on\_close sub { ... }
```perl
websocket_on_close sub {
my( $conn ) = @_;
...;
};
```
Code invoked when a new socket is opened. Gets the
connection object as argument.
## websocket\_on\_error sub { ... }
```perl
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
```perl
websocket_on_error sub {
my $env = shift;
return [
500,
["Content-Type" => "text/plain"],
["Error: " . $env->{"plack.app.websocket.error"}]
];
};
```
## websocket\_on\_message sub { ... }
```perl
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](https://metacpan.org/pod/Plack::App::WebSocket::Connection)
object, can have its own (multiple) handlers. So you can do things like
```perl
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\_connections
Returns the list of currently open websocket connections.
## websocket\_url
Returns the full url of the websocket mountpoint.
```perl
# 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](https://metacpan.org/pod/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](https://metacpan.org/pod/Plack::App::WebSocket), which is itself [AnyEvent::WebSocket::Server](https://metacpan.org/pod/AnyEvent::WebSocket::Server)
wrapped in Plackstic.
Mojolicious also has nice WebSocket-related offerings. See
[Mojolicious::Plugin::MountPSGI](https://metacpan.org/pod/Mojolicious::Plugin::MountPSGI) or
[http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Web-server-embedding](http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Web-server-embedding).
(hi Joel!)
# AUTHOR
Yanick Champoux [![endorse](http://api.coderwall.com/yanick/endorsecount.png)](http://coderwall.com/yanick)
# COPYRIGHT AND LICENSE
This software is copyright (c) 2021, 2019, 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.