@ -19,6 +19,8 @@ use Session::Token;
use Config::Simple ;
use Email::Valid ;
use URI ;
use Protocol::SocketIO::Handshake ;
use Protocol::SocketIO::Message ;
use Data::Dumper ;
app - > log - > level ( 'info' ) ;
@ -73,9 +75,12 @@ if ($config->{'etherpad.uri'} =~ m/https?:\/\/.*/ && $config->{'etherpad.api_key
}
}
# GL obal error check
# Gl obal error check
our $ error = undef ;
# Global client hash
our $ socket_peers = { } ;
# Load I18N, and declare supported languages
plugin I18N = > {
namespace = > 'Vroom::I18N' ,
@ -170,9 +175,9 @@ helper login => sub {
} ;
$ sth - > execute ( $ key ) ;
$ self - > session (
name = > $ login ,
ip = > $ self - > tx - > remote_address ,
key = > $ key
name = > $ login ,
ip = > $ self - > tx - > remote_address ,
key = > $ key
) ;
$ self - > app - > log - > info ( $ self - > session ( 'name' ) . " logged in from " . $ self - > tx - > remote_address ) ;
return 1 ;
@ -1036,6 +1041,152 @@ helper key_can_do_this => sub {
return 0 ;
} ;
# Socket.IO handshake
get '/socket.io/:ver' = > sub {
my $ self = shift ;
$ self - > session ( peer_id = > $ self - > get_random ( 256 ) ) ;
my $ handshake = Protocol::SocketIO::Handshake - > new (
session_id = > $ self - > session ( 'peer_id' ) ,
heartbeat_timeout = > 15 ,
close_timeout = > 20 ,
transports = > [ qw/websocket/ ]
) ;
return $ self - > render ( text = > $ handshake - > to_bytes ) ;
} ;
# WebSocket transport for the Socket.IO channel
websocket '/socket.io/:ver/websocket/:id' = > sub {
my $ self = shift ;
my $ id = $ self - > stash ( 'id' ) ;
if ( $ id ne $ self - > session ( 'peer_id' ) || ! $ self - > session ( 'name' ) ) {
$ self - > app - > log - > debug ( 'Sometyhing is wrong, peer ID is ' . $ id . ' but should be ' . $ self - > session ( 'peer_id' ) ) ;
return $ self - > send ( 'Bad session' ) ;
}
$ socket_peers - > { $ id } - > { socket } = $ self - > tx ;
$ socket_peers - > { $ id } - > { last } = time ;
$ self - > on ( 'message' = > sub {
my $ self = shift ;
my $ msg = Protocol::SocketIO::Message - > new - > parse ( shift ) ;
if ( $ msg - > type eq 'event' ) {
if ( $ msg - > { data } - > { name } eq 'join' ) {
my $ room = $ msg - > { data } - > { args } [ 0 ] ;
my $ others = { } ;
foreach my $ peers ( keys %$ socket_peers ) {
next if ( $ peers eq $ id ) ;
$ others - > { $ peers } = $ socket_peers - > { $ peers } - > { details } ;
}
$ socket_peers - > { $ id } - > { details } = {
screen = > \ 0 ,
video = > \ 1 ,
audio = > \ 0
} ;
$ socket_peers - > { $ id } - > { room } = $ room ;
$ self - > app - > log - > debug ( "client ID " . $ id . " joined room " . $ room ) ;
# $self->app->log->debug(Dumper($others));
$ self - > send (
Protocol::SocketIO::Message - > new (
type = > 'ack' ,
message_id = > $ msg - > { id } ,
args = > [
undef ,
{
clients = > $ others
}
]
)
) ;
}
elsif ( $ msg - > { data } - > { name } eq 'message' ) {
my $ room = $ socket_peers - > { $ id } - > { room } ;
$ self - > app - > log - > debug ( "Message received from " . $ id ) ;
foreach my $ peer ( keys %$ socket_peers ) {
next if ( $ peer eq $ id ) ;
next if $ socket_peers - > { $ peer } - > { room } ne $ room ;
#$self->app->log->debug("Relaying message from " . $id . " to " . $peer);
$ msg - > { data } - > { args } [ 0 ] - > { from } = $ id ;
#$self->app->log->debug(Dumper($msg));
$ socket_peers - > { $ peer } - > { socket } - > send (
Protocol::SocketIO::Message - > new ( %$ msg )
) ;
}
}
elsif ( $ msg - > { data } - > { name } eq 'shareScreen' ) {
$ socket_peers - > { $ id } - > { details } - > { screen } = \ 1 ;
}
elsif ( $ msg - > { data } - > { name } eq 'unshareScreen' ) {
$ socket_peers - > { $ id } - > { details } - > { screen } = \ 0 ;
foreach my $ peer ( keys %$ socket_peers ) {
next if ( $ peer eq $ id ) ;
next if $ socket_peers - > { $ peer } - > { room } ne $ socket_peers - > { $ id } - > { room } ;
$ self - > app - > log - > debug ( "Notifying $peer that $id unshared its screen" ) ;
$ socket_peers - > { $ peer } - > { socket } - > send (
Protocol::SocketIO::Message - > new (
type = > 'event' ,
data = > {
name = > 'remove' ,
args = > [
{
id = > $ id ,
type = > 'screen'
}
]
}
)
) ;
}
}
}
elsif ( $ msg - > type eq 'heartbeat' ) {
# Heartbeat reply, update timestamp
$ socket_peers - > { $ id } - > { last } = time ;
}
} ) ;
$ self - > on ( finish = > sub {
my ( $ self , $ code , $ reason ) = @ _ ;
$ self - > app - > log - > debug ( "Client id " . $ id . " closed websocket connection" ) ;
foreach my $ peer ( keys %$ socket_peers ) {
next if ( $ peer eq $ id ) ;
next if $ socket_peers - > { $ peer } - > { room } ne $ socket_peers - > { $ id } - > { room } ;
$ self - > app - > log - > debug ( "Relaying message from " . $ id . " to " . $ peer ) ;
$ socket_peers - > { $ peer } - > { socket } - > send (
Protocol::SocketIO::Message - > new (
type = > 'event' ,
data = > {
name = > 'remove' ,
args = > [
{
id = > $ id ,
type = > 'video'
}
]
}
)
) ;
}
delete $ socket_peers - > { $ id } ;
} ) ;
$ self - > send ( Protocol::SocketIO::Message - > new ( type = > 'connect' ) ) ;
} ;
# Send heartbeats to all websocket clients
Mojo::IOLoop - > recurring ( 2 = > sub {
foreach my $ peer ( keys %$ socket_peers ) {
if ( $ socket_peers - > { $ peer } - > { last } < time - 10 ) {
app - > log - > debug ( "Peer $peer didn't reply in 10 sec, disconnecting" ) ;
$ socket_peers - > { $ peer } - > { socket } - > finish ;
}
else {
$ socket_peers - > { $ peer } - > { socket } - > send (
Protocol::SocketIO::Message - > new ( type = > 'heartbeat' )
) ;
}
}
} ) ;
# Route / to the index page
get '/' = > sub {
my $ self = shift ;