line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Dancer2::Plugin::WebSocket; |
2
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:YANICK'; |
3
|
|
|
|
|
|
|
# ABSTRACT: add a websocket interface to your Dancers app |
4
|
|
|
|
|
|
|
$Dancer2::Plugin::WebSocket::VERSION = '0.3.1'; |
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
20
|
use v5.12.0; |
|
1
|
|
|
|
|
4
|
|
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
562
|
use Plack::App::WebSocket; |
|
1
|
|
|
|
|
105943
|
|
|
1
|
|
|
|
|
47
|
|
9
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
742
|
use Dancer2::Plugin; |
|
1
|
|
|
|
|
16199
|
|
|
1
|
|
|
|
|
13
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
has serializer => ( |
13
|
|
|
|
|
|
|
is => 'ro', |
14
|
|
|
|
|
|
|
from_config => 1, |
15
|
|
|
|
|
|
|
coerce => sub { |
16
|
|
|
|
|
|
|
my $serializer = shift or return undef; |
17
|
|
|
|
|
|
|
require JSON::MaybeXS; |
18
|
|
|
|
|
|
|
JSON::MaybeXS->new( ref $serializer ? %$serializer : () ); |
19
|
|
|
|
|
|
|
}, |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
has login => ( |
23
|
|
|
|
|
|
|
is => 'ro', |
24
|
|
|
|
|
|
|
from_config => sub { 0 }, |
25
|
|
|
|
|
|
|
); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
has mount_path => ( |
28
|
|
|
|
|
|
|
is => 'ro', |
29
|
|
|
|
|
|
|
from_config => sub { '/ws' }, |
30
|
|
|
|
|
|
|
); |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
has 'on_'.$_ => ( |
34
|
|
|
|
|
|
|
is => 'rw', |
35
|
|
|
|
|
|
|
plugin_keyword => 'websocket_on_'.$_, |
36
|
|
|
|
|
|
|
default => sub { sub { } }, |
37
|
|
|
|
|
|
|
) for qw/ |
38
|
|
|
|
|
|
|
open |
39
|
|
|
|
|
|
|
message |
40
|
|
|
|
|
|
|
close |
41
|
|
|
|
|
|
|
/; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
has 'on_error' => ( |
44
|
|
|
|
|
|
|
is => 'rw', |
45
|
|
|
|
|
|
|
plugin_keyword => 'websocket_on_error', |
46
|
|
|
|
|
|
|
default => sub { sub { |
47
|
|
|
|
|
|
|
my $env = shift; |
48
|
|
|
|
|
|
|
return [500, |
49
|
|
|
|
|
|
|
["Content-Type" => "text/plain"], |
50
|
|
|
|
|
|
|
["Error: " . $env->{"plack.app.websocket.error"}]]; |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
}, |
53
|
|
|
|
|
|
|
); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
has 'on_login' => ( |
56
|
|
|
|
|
|
|
is => 'rw', |
57
|
|
|
|
|
|
|
plugin_keyword => 'websocket_on_login', |
58
|
|
|
|
|
|
|
default => sub { sub { } }, |
59
|
|
|
|
|
|
|
); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
has connections => ( |
62
|
|
|
|
|
|
|
is => 'ro', |
63
|
|
|
|
|
|
|
default => sub{ {} }, |
64
|
|
|
|
|
|
|
); |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub websocket_connections :PluginKeyword { |
68
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
69
|
0
|
|
|
|
|
0
|
return values %{ $self->connections }; |
|
0
|
|
|
|
|
0
|
|
70
|
1
|
|
|
1
|
|
4718
|
} |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
20
|
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub websocket_url :PluginKeyword { |
74
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
75
|
0
|
|
|
|
|
0
|
my $request = $self->app->request; |
76
|
0
|
0
|
|
|
|
0
|
my $proto = $request->secure ? 'wss://' : 'ws://'; |
77
|
0
|
|
|
|
|
0
|
my $address = $proto . $request->host . $self->mount_path; |
78
|
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
0
|
return $address; |
80
|
1
|
|
|
1
|
|
777
|
} |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
6
|
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub websocket_mount :PluginKeyword { |
84
|
1
|
|
|
1
|
1
|
10
|
my $self = shift; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
return |
87
|
|
|
|
|
|
|
$self->mount_path => Plack::App::WebSocket->new( |
88
|
0
|
|
|
0
|
|
0
|
on_error => sub { $self->on_error->(@_) }, |
89
|
|
|
|
|
|
|
on_establish => sub { |
90
|
1
|
|
|
1
|
|
150005
|
my $conn = shift; ## Plack::App::WebSocket::Connection object |
91
|
1
|
|
|
|
|
4
|
my $env = shift; ## PSGI env |
92
|
|
|
|
|
|
|
|
93
|
1
|
50
|
|
|
|
35
|
if ($self->login) { |
94
|
0
|
0
|
|
|
|
0
|
if (!$self->on_login->($conn, $env)) { |
95
|
0
|
|
|
|
|
0
|
return; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
1
|
|
|
|
|
10
|
require Moo::Role; |
100
|
|
|
|
|
|
|
|
101
|
1
|
|
|
|
|
14
|
Moo::Role->apply_roles_to_object( |
102
|
|
|
|
|
|
|
$conn, 'Dancer2::Plugin::WebSocket::Connection' |
103
|
|
|
|
|
|
|
); |
104
|
1
|
|
|
|
|
3449
|
$conn->manager($self); |
105
|
1
|
|
|
|
|
33
|
$conn->serializer($self->serializer); |
106
|
1
|
|
|
|
|
74
|
$self->connections->{$conn->id} = $conn; |
107
|
|
|
|
|
|
|
|
108
|
1
|
|
|
|
|
12
|
$self->on_open->( $conn, $env, @_ ); |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
$conn->on( |
111
|
|
|
|
|
|
|
message => sub { |
112
|
1
|
|
|
|
|
6331
|
my( $conn, $message ) = @_; |
113
|
1
|
50
|
|
|
|
18
|
if( my $s = $conn->serializer ) { |
114
|
1
|
|
|
|
|
15
|
$message = $s->decode($message); |
115
|
|
|
|
|
|
|
} |
116
|
1
|
|
|
1
|
|
724
|
use Try::Tiny; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
256
|
|
117
|
|
|
|
|
|
|
try { |
118
|
1
|
|
|
|
|
77
|
$self->on_message->( $conn, $message ); |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
catch { |
121
|
0
|
|
|
|
|
|
warn $_; |
122
|
0
|
|
|
|
|
|
die $_; |
123
|
1
|
|
|
|
|
19
|
}; |
124
|
|
|
|
|
|
|
}, |
125
|
|
|
|
|
|
|
finish => sub { |
126
|
0
|
|
|
|
|
|
$self->on_close->($conn); |
127
|
0
|
|
|
|
|
|
delete $self->connections->{$conn->id}; |
128
|
0
|
|
|
|
|
|
$conn = undef; |
129
|
|
|
|
|
|
|
}, |
130
|
1
|
|
|
|
|
26
|
); |
131
|
|
|
|
|
|
|
} |
132
|
1
|
|
|
|
|
32
|
)->to_app; |
133
|
|
|
|
|
|
|
|
134
|
1
|
|
|
1
|
|
8
|
} |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
29
|
|
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
1; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
__END__ |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=pod |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=encoding UTF-8 |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=head1 NAME |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Dancer2::Plugin::WebSocket - add a websocket interface to your Dancers app |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head1 VERSION |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
version 0.3.1 |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head1 SYNOPSIS |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
F<bin/app.psgi>: |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
#!/usr/bin/env perl |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
use strict; |
160
|
|
|
|
|
|
|
use warnings; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
use FindBin; |
163
|
|
|
|
|
|
|
use lib "$FindBin::Bin/../lib"; |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
use Plack::Builder; |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
use MyApp; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
builder { |
170
|
|
|
|
|
|
|
mount( MyApp->websocket_mount ); |
171
|
|
|
|
|
|
|
mount '/' => MyApp->to_app; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
F<config.yml>: |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
plugins: |
177
|
|
|
|
|
|
|
WebSocket: |
178
|
|
|
|
|
|
|
# default values |
179
|
|
|
|
|
|
|
serializer: 0 |
180
|
|
|
|
|
|
|
login: 0 |
181
|
|
|
|
|
|
|
mount_path: /ws |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
F<MyApp.pm>: |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
package MyApp; |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
use Dancer2; |
188
|
|
|
|
|
|
|
use Dancer2::Plugin::WebSocket; |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
websocket_on_message sub { |
191
|
|
|
|
|
|
|
my( $conn, $message ) = @_; |
192
|
|
|
|
|
|
|
$conn->send( $message . ' world!' ); |
193
|
|
|
|
|
|
|
}; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
get '/' => sub { |
196
|
|
|
|
|
|
|
my $ws_url = websocket_url; |
197
|
|
|
|
|
|
|
return <<"END"; |
198
|
|
|
|
|
|
|
<html> |
199
|
|
|
|
|
|
|
<head><script> |
200
|
|
|
|
|
|
|
var urlMySocket = "$ws_url"; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
var mySocket = new WebSocket(urlMySocket); |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
mySocket.onmessage = function (evt) { |
205
|
|
|
|
|
|
|
console.log( "Got message " + evt.data ); |
206
|
|
|
|
|
|
|
}; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
mySocket.onopen = function(evt) { |
209
|
|
|
|
|
|
|
console.log("opening"); |
210
|
|
|
|
|
|
|
setTimeout( function() { |
211
|
|
|
|
|
|
|
mySocket.send('hello'); }, 2000 ); |
212
|
|
|
|
|
|
|
}; |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
</script></head> |
215
|
|
|
|
|
|
|
<body><h1>WebSocket client</h1></body> |
216
|
|
|
|
|
|
|
</html> |
217
|
|
|
|
|
|
|
END |
218
|
|
|
|
|
|
|
}; |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
get '/say_hi' => sub { |
221
|
|
|
|
|
|
|
$_->send([ "Hello!" ]) for websocket_connections; |
222
|
|
|
|
|
|
|
}; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
true; |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=head1 DESCRIPTION |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
C<Dancer2::Plugin::WebSocket> provides an interface to L<Plack::App::WebSocket> |
229
|
|
|
|
|
|
|
and allows to interact with the webSocket connections within the Dancer app. |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
L<Plack::App::WebSocket>, and thus this plugin, requires a plack server that |
232
|
|
|
|
|
|
|
supports the psgi I<streaming>, I<nonblocking> and I<io>. L<Twiggy> |
233
|
|
|
|
|
|
|
is the most popular server fitting the bill. |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=head1 CONFIGURATION |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=over |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=item serializer |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
If serializer is set to a C<true> value, messages will be assumed to be JSON |
242
|
|
|
|
|
|
|
objects and will be automatically encoded/decoded using a L<JSON::MaybeXS> |
243
|
|
|
|
|
|
|
serializer. If the value of C<serializer> is a hash, it'll be passed as |
244
|
|
|
|
|
|
|
arguments to the L<JSON::MaybeXS> constructor. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
plugins: |
247
|
|
|
|
|
|
|
WebSocket: |
248
|
|
|
|
|
|
|
serializer: |
249
|
|
|
|
|
|
|
utf8: 1 |
250
|
|
|
|
|
|
|
allow_nonref: 1 |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
By the way, if you want the connection to automatically serialize data |
253
|
|
|
|
|
|
|
structures to JSON on the client side, you can do something like |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
var mySocket = new WebSocket(urlMySocket); |
256
|
|
|
|
|
|
|
mySocket.sendJSON = function(message) { |
257
|
|
|
|
|
|
|
return this.send(JSON.stringify(message)) |
258
|
|
|
|
|
|
|
}; |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
// then later... |
261
|
|
|
|
|
|
|
mySocket.sendJSON({ whoa: "auto-serialization ftw!" }); |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=item mount_path |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
Path for the websocket mountpoint. Defaults to C</ws>. |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=back |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=head1 PLUGIN KEYWORDS |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
In the various callbacks, the connection object C<$conn> |
272
|
|
|
|
|
|
|
is a L<Plack::App::WebSocket::Connection> object |
273
|
|
|
|
|
|
|
augmented with the L<Dancer2::Plugin::WebSocket::Connection> role. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=head2 websocket_on_open sub { ... } |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
websocket_on_open sub { |
278
|
|
|
|
|
|
|
my( $conn, $env ) = @_; |
279
|
|
|
|
|
|
|
...; |
280
|
|
|
|
|
|
|
}; |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
Code invoked when a new socket is opened. Gets the new |
283
|
|
|
|
|
|
|
connection |
284
|
|
|
|
|
|
|
object and the Plack |
285
|
|
|
|
|
|
|
C<$env> hash as arguments. |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=head2 websocket_on_login sub { ... } |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
websocket_on_login sub { |
290
|
|
|
|
|
|
|
my( $conn, $env ) = @_; |
291
|
|
|
|
|
|
|
...; |
292
|
|
|
|
|
|
|
}; |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
Code invoked when a new socket is opened. Gets the |
295
|
|
|
|
|
|
|
connection object and the Plack C<$env> hash as arguments. |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
Example: return true if user is logged in and the webapp http_cookie is the same as the websocket. |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
my $login_conn; |
300
|
|
|
|
|
|
|
my $cookie_name = 'example.session'; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
hook before => sub { |
303
|
|
|
|
|
|
|
if (defined cookies->{$cookie_name}) { |
304
|
|
|
|
|
|
|
$login_conn->{'cookie_id'} = cookies->{$cookie_name}->value; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
$login_conn->{'login'} = logged_in_user ? 1 : 0; |
307
|
|
|
|
|
|
|
}; |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
websocket_on_login sub { |
310
|
|
|
|
|
|
|
my( $conn, $env ) = @_; |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
my ($cookie_id) = ($env->{'HTTP_COOKIE'} =~ /$cookie_name=(.*);?/g); |
313
|
|
|
|
|
|
|
if (($login_conn->{'login'}) and ($login_conn->{'cookie_id'} eq $cookie_id)) { |
314
|
|
|
|
|
|
|
return 1; |
315
|
|
|
|
|
|
|
} else { |
316
|
|
|
|
|
|
|
warn "require login"; |
317
|
|
|
|
|
|
|
return 0; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
}; |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=head2 websocket_on_close sub { ... } |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
websocket_on_close sub { |
324
|
|
|
|
|
|
|
my( $conn ) = @_; |
325
|
|
|
|
|
|
|
...; |
326
|
|
|
|
|
|
|
}; |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
Code invoked when a new socket is opened. Gets the |
329
|
|
|
|
|
|
|
connection object as argument. |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=head2 websocket_on_error sub { ... } |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
websocket_on_error sub { |
334
|
|
|
|
|
|
|
my( $env ) = @_; |
335
|
|
|
|
|
|
|
...; |
336
|
|
|
|
|
|
|
}; |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
Code invoked when an error is detected. Gets the Plack |
339
|
|
|
|
|
|
|
C<$env> hash as argument and is expected to return a |
340
|
|
|
|
|
|
|
Plack triplet. |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
If not explicitly set, defaults to |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
websocket_on_error sub { |
345
|
|
|
|
|
|
|
my $env = shift; |
346
|
|
|
|
|
|
|
return [ |
347
|
|
|
|
|
|
|
500, |
348
|
|
|
|
|
|
|
["Content-Type" => "text/plain"], |
349
|
|
|
|
|
|
|
["Error: " . $env->{"plack.app.websocket.error"}] |
350
|
|
|
|
|
|
|
]; |
351
|
|
|
|
|
|
|
}; |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=head2 websocket_on_message sub { ... } |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
websocket_on_message sub { |
356
|
|
|
|
|
|
|
my( $conn, $message ) = @_; |
357
|
|
|
|
|
|
|
...; |
358
|
|
|
|
|
|
|
}; |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
Code invoked when a message is received. Gets the connection |
361
|
|
|
|
|
|
|
object and the message as arguments. |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
Note that while C<websocket_on_message> fires for all messages receives, you can |
364
|
|
|
|
|
|
|
also be a little more selective. Indeed, each connection, being a L<Plack::App::WebSocket::Connection> |
365
|
|
|
|
|
|
|
object, can have its own (multiple) handlers. So you can do things like |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
websocket_on_open sub { |
368
|
|
|
|
|
|
|
my( $conn, $env ) = @_; |
369
|
|
|
|
|
|
|
$conn->on( message => sub { |
370
|
|
|
|
|
|
|
my( $conn, $message ) = @_; |
371
|
|
|
|
|
|
|
warn "I'm only being executed for messages sent via this connection"; |
372
|
|
|
|
|
|
|
}); |
373
|
|
|
|
|
|
|
}; |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=head2 websocket_connections |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
Returns the list of currently open websocket connections. |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=head2 websocket_url |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
Returns the full url of the websocket mountpoint. |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
# assuming host is 'localhost:5000' |
384
|
|
|
|
|
|
|
# and the mountpoint is '/ws' |
385
|
|
|
|
|
|
|
print websocket_url; # => ws://localhost:5000/ws |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
=head2 websocket_mount |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
Returns the mountpoint and the Plack app coderef to be |
390
|
|
|
|
|
|
|
used for C<mount> in F<app.psgi>. See the SYNOPSIS. |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
=head1 GOTCHAS |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
It seems that the closing the socket causes Google's chrome to burp the |
395
|
|
|
|
|
|
|
following to the console: |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
WebSocket connection to 'ws://...' failed: Received a broken close frame containing a reserved status code. |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
Firefox seems to be happy, though. The issue is probably somewhere deep in |
400
|
|
|
|
|
|
|
L<AnyEvent::WebSocket::Server>. Since the socket is being closed anyway, I am |
401
|
|
|
|
|
|
|
not overly worried about it. |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
=head1 SEE ALSO |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
This plugin is nothing much than a sugar topping atop |
406
|
|
|
|
|
|
|
L<Plack::App::WebSocket>, which is itself L<AnyEvent::WebSocket::Server> |
407
|
|
|
|
|
|
|
wrapped in Plackstic. |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
Mojolicious also has nice WebSocket-related offerings. See |
410
|
|
|
|
|
|
|
L<Mojolicious::Plugin::MountPSGI> or |
411
|
|
|
|
|
|
|
L<http://mojolicious.org/perldoc/Mojolicious/Guides/Cookbook#Web-server-embedding>. |
412
|
|
|
|
|
|
|
(hi Joel!) |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=head1 AUTHOR |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
Yanick Champoux <yanick@cpan.org> |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
This software is copyright (c) 2021, 2019, 2017 by Yanick Champoux. |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
423
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=cut |