2019-07-17 19:37:18 +02:00
# define HAVE_JSON
# include "WebClient.h"
# include <log/LogUtils.h>
# include <misc/endianness.h>
# include <src/client/voice/VoiceClient.h>
2020-01-27 02:21:39 +01:00
# include <src/VirtualServerManager.h>
2019-07-17 19:37:18 +02:00
# include <netinet/tcp.h>
# include <src/InstanceHandler.h>
# include <misc/memtracker.h>
# include <src/client/ConnectedClient.h>
# include <misc/std_unique_ptr.h>
# include <src/client/SpeakingClient.h>
# if defined(TCP_CORK) && !defined(TCP_NOPUSH)
# define TCP_NOPUSH TCP_CORK
# endif
using namespace std ;
using namespace std : : chrono ;
using namespace ts ;
using namespace ts : : server ;
using namespace ts : : protocol ;
2019-07-23 10:37:56 +02:00
WebClient : : WebClient ( WebControlServer * server , int fd ) : SpeakingClient ( server - > getTS ( ) - > getSql ( ) , server - > getTS ( ) ) , handle ( server ) {
2020-01-24 02:57:58 +01:00
memtrack : : allocated < WebClient > ( this ) ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
assert ( server - > getTS ( ) ) ;
2019-07-17 19:37:18 +02:00
this - > state = ConnectionState : : INIT_LOW ;
this - > file_descriptor = fd ;
}
void WebClient : : initialize ( ) {
2020-01-24 02:57:58 +01:00
this - > event_handle_packet = make_shared < event : : ProxiedEventEntry < WebClient > > ( dynamic_pointer_cast < WebClient > ( this - > ref ( ) ) , & WebClient : : processNextMessage ) ;
int enabled = 1 ;
int disabled = 0 ;
setsockopt ( this - > file_descriptor , SOL_SOCKET , SO_KEEPALIVE , & enabled , sizeof ( enabled ) ) ;
if ( setsockopt ( this - > file_descriptor , IPPROTO_TCP , TCP_NOPUSH , & disabled , sizeof disabled ) < 0 )
logError ( this - > getServerId ( ) , " {} Cant disable nopush! system error: {} => {} " , CLIENT_STR_LOG_PREFIX , errno , strerror ( errno ) ) ;
auto event_loop = serverInstance - > getWebIoLoop ( ) - > next_loop ( ) ;
this - > readEvent = event_new ( event_loop - > loop , this - > file_descriptor , EV_READ | EV_PERSIST , [ ] ( int a , short b , void * c ) { ( ( WebClient * ) c ) - > handleMessageRead ( a , b , c ) ; } , this ) ;
this - > writeEvent = event_new ( event_loop - > loop , this - > file_descriptor , EV_WRITE , [ ] ( int a , short b , void * c ) { ( ( WebClient * ) c ) - > handleMessageWrite ( a , b , c ) ; } , this ) ;
{
this - > ws_handler . direct_process ( pipes : : PROCESS_DIRECTION_IN , true ) ;
this - > ws_handler . direct_process ( pipes : : PROCESS_DIRECTION_OUT , true ) ;
this - > ws_handler . callback_invalid_request = [ & ] ( const http : : HttpRequest & request , http : : HttpResponse & response ) {
response . setHeader ( " Connection " , { " close " } ) ; /* close the connection to avoid a second request */
response . code = http : : code : : code ( 301 , " Invalid request (Not a web socket!) " ) ;
const auto redirect_target = [ & ] {
if ( request . parameters . count ( " forward_url " ) )
response . setHeader ( " Location " , { request . parameters . at ( " forward_url " ) } ) ;
else if ( request . findHeader ( " Origin " ) )
response . header . push_back ( { " Location " , request . findHeader ( " Origin " ) . values } ) ;
else
response . header . push_back ( { " Location " , { " https://web.teaspeak.de " } } ) ;
} ;
/* we're running https */
if ( this - > ssl_encrypted ) {
redirect_target ( ) ;
} else {
/* lets redirect to https */
if ( request . findHeader ( " Host " ) ) {
const auto redirect_forward = [ & ] ( const std : : string & url ) {
response . setHeader ( " Location " , { " https:// " + request . findHeader ( " Host " ) . values [ 0 ] + " /?forward_url= " + http : : encode_url ( url ) } ) ;
} ;
if ( request . parameters . count ( " forward_url " ) )
redirect_forward ( request . parameters . at ( " forward_url " ) ) ;
else if ( request . findHeader ( " Origin " ) )
redirect_forward ( request . findHeader ( " Origin " ) . values [ 0 ] ) ;
else
redirect_forward ( " https://web.teaspeak.de " ) ;
} else {
/* we could not find our host */
redirect_target ( ) ;
}
}
} ;
this - > ws_handler . on_connect = bind ( & WebClient : : onWSConnected , this ) ;
this - > ws_handler . on_disconnect = bind ( & WebClient : : onWSDisconnected , this , placeholders : : _1 ) ;
this - > ws_handler . callback_data ( [ & ] ( const pipes : : WSMessage & msg ) {
this - > onWSMessage ( msg ) ;
} ) ;
this - > ws_handler . callback_write ( [ & ] ( const pipes : : buffer_view & data ) {
if ( this - > ssl_encrypted )
this - > ssl_handler . send ( data ) ;
else
this - > enqueue_raw_packet ( data ) ;
} ) ;
this - > ws_handler . callback_error ( [ & ] ( int error , const std : : string & message ) {
logError ( this - > getServerId ( ) , " {} Got ws pipeline error ({} | {}) " , CLIENT_STR_LOG_PREFIX , error , message ) ;
} ) ;
}
{
this - > ssl_handler . direct_process ( pipes : : PROCESS_DIRECTION_IN , true ) ;
this - > ssl_handler . direct_process ( pipes : : PROCESS_DIRECTION_OUT , true ) ;
this - > ssl_handler . callback_data ( [ & ] ( const pipes : : buffer_view & msg ) {
this - > ws_handler . process_incoming_data ( msg ) ;
} ) ;
this - > ssl_handler . callback_write ( [ & ] ( const pipes : : buffer_view & msg ) { this - > enqueue_raw_packet ( msg ) ; } ) ;
this - > ssl_handler . callback_error ( [ & ] ( int error , const std : : string & message ) {
logError ( this - > getServerId ( ) , " {} Got ssl pipeline error ({} | {}) " , CLIENT_STR_LOG_PREFIX , error , message ) ;
} ) ;
}
this - > ssl_handler . initialize ( serverInstance - > sslManager ( ) - > web_ssl_options ( ) ) ;
this - > ws_handler . initialize ( ) ;
2019-07-17 19:37:18 +02:00
}
WebClient : : ~ WebClient ( ) {
2020-01-24 02:57:58 +01:00
memtrack : : freed < WebClient > ( this ) ;
2019-07-17 19:37:18 +02:00
}
static Json : : StreamWriterBuilder stream_write_builder = [ ] {
2020-01-24 02:57:58 +01:00
Json : : StreamWriterBuilder builder ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
builder [ " commentStyle " ] = " None " ;
builder [ " indentation " ] = " " ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
return builder ;
2019-07-17 19:37:18 +02:00
} ( ) ;
void WebClient : : sendJson ( const Json : : Value & json ) {
2020-01-24 02:57:58 +01:00
unique_ptr < Json : : StreamWriter > writer { stream_write_builder . newStreamWriter ( ) } ;
std : : ostringstream stream ;
if ( writer - > write ( json , & stream ) ! = 0 ) {
//TODO log error, but this shall never happen
return ;
}
if ( ! stream . good ( ) ) {
//TODO log error
return ;
}
auto data = stream . str ( ) ;
pipes : : buffer buffer = buffer : : allocate_buffer ( data . length ( ) ) ;
buffer . write ( data . data ( ) , data . length ( ) ) ;
2019-07-17 19:37:18 +02:00
this - > ws_handler . send ( { pipes : : TEXT , std : : move ( buffer ) } ) ;
}
void WebClient : : sendCommand ( const ts : : Command & command , bool low ) {
Json : : Value value = command . buildJson ( ) ;
value [ " type " ] = " command " ;
this - > sendJson ( value ) ;
}
2020-01-26 14:21:34 +01:00
void WebClient : : sendCommand ( const ts : : command_builder & command , bool low ) {
# if false
Json : : Value value { } ;
value [ " type " ] = " command2 " ;
value [ " payload " ] = command . build ( ) ;
this - > sendJson ( value ) ;
# else
auto data = command . build ( ) ;
Command parsed_command = Command : : parse ( pipes : : buffer_view { data . data ( ) , data . length ( ) } , true , false ) ;
this - > sendCommand ( parsed_command , low ) ;
# endif
}
2019-07-17 19:37:18 +02:00
bool WebClient : : closeConnection ( const std : : chrono : : system_clock : : time_point & timeout ) {
2020-01-24 02:57:58 +01:00
bool flushing = timeout . time_since_epoch ( ) . count ( ) > 0 ;
auto self_lock = dynamic_pointer_cast < WebClient > ( _this . lock ( ) ) ;
auto server_lock = this - > server ;
assert ( self_lock ) ;
unique_lock state_lock ( this - > state_lock ) ;
if ( this - > state = = ConnectionState : : DISCONNECTED ) return false ;
if ( this - > state = = ConnectionState : : DISCONNECTING & & flushing ) return true ;
this - > state = flushing ? ConnectionState : : DISCONNECTING : ConnectionState : : DISCONNECTED ;
unique_lock close_lock ( this - > close_lock ) ;
state_lock . unlock ( ) ;
if ( this - > readEvent )
event_del_noblock ( this - > readEvent ) ;
if ( this - > server ) {
{
unique_lock server_channel_lock ( this - > server - > channel_tree_lock ) ;
this - > server - > unregisterClient ( _this . lock ( ) , " disconnected " , server_channel_lock ) ;
}
this - > server - > groups - > disableCache ( this - > getClientDatabaseId ( ) ) ;
//this->server = nullptr;
}
if ( flushing ) {
this - > flush_thread = std : : thread ( [ self_lock , server_lock , timeout ] {
bool flag_flushed ;
while ( true ) {
{
lock_guard lock ( self_lock - > state_lock ) ;
if ( self_lock - > state ! = ConnectionState : : DISCONNECTING ) return ; /* somebody else had this problem now */
}
flag_flushed = true ;
{
lock_guard lock ( self_lock - > queue_lock ) ;
flag_flushed & = self_lock - > queue_read . empty ( ) ;
flag_flushed & = self_lock - > queue_write . empty ( ) ;
}
if ( flag_flushed )
break ;
auto now = system_clock : : now ( ) ;
if ( timeout < now )
break ;
else
this_thread : : sleep_for ( min ( duration_cast < milliseconds > ( timeout - now ) , milliseconds ( 10 ) ) ) ;
}
{
lock_guard lock ( self_lock - > state_lock ) ;
if ( self_lock - > state ! = ConnectionState : : DISCONNECTING ) return ; /* somebody else had this problem now */
self_lock - > state = ConnectionState : : DISCONNECTED ;
}
/* we can lock here again because we've already ensured that we're still disconnecting and updated the status to disconnected.
* So no thread will wait for this thread while close_lock had been locked
*/
lock_guard close_lock ( self_lock - > close_lock ) ;
self_lock - > disconnectFinal ( ) ;
} ) ;
} else {
/* lets wait 'till the flush thread recognized that we're overriding close */
if ( this - > flush_thread . joinable ( ) )
this - > flush_thread . join ( ) ;
disconnectFinal ( ) ;
}
return true ;
2019-07-17 19:37:18 +02:00
}
2020-01-25 23:42:37 +01:00
command_result WebClient : : handleCommand ( Command & command ) {
2020-01-24 02:57:58 +01:00
if ( this - > connectionState ( ) = = ConnectionState : : INIT_HIGH & & this - > handshake . state = = HandshakeState : : SUCCEEDED ) {
if ( command . command ( ) = = " clientinit " ) {
auto result = this - > handleCommandClientInit ( command ) ;
2020-01-25 23:42:37 +01:00
if ( result . error_code ( ) )
2020-01-24 02:57:58 +01:00
this - > closeConnection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
return result ;
}
}
return SpeakingClient : : handleCommand ( command ) ;
2019-07-17 19:37:18 +02:00
}
void WebClient : : tick ( const std : : chrono : : system_clock : : time_point & point ) {
SpeakingClient : : tick ( point ) ;
2020-01-24 02:57:58 +01:00
{
shared_lock read_voice_bridge_lock ( this - > voice_bridge_lock ) ;
if ( this - > voice_bridge )
this - > voice_bridge - > execute_tick ( ) ;
}
if ( this - > ping . last_request + seconds ( 1 ) < point ) {
if ( this - > ping . last_response > this - > ping . last_request | | this - > ping . last_response + this - > ping . timeout < point ) {
this - > ping . current_id + + ;
this - > ping . last_request = point ;
char buffer [ 2 ] ;
le2be8 ( this - > ping . current_id , buffer ) ;
this - > ws_handler . send ( { pipes : : PING , { buffer , 2 } } ) ;
}
}
if ( this - > js_ping . last_request + seconds ( 1 ) < point ) {
if ( this - > js_ping . last_response > this - > js_ping . last_request | | this - > js_ping . last_request + this - > js_ping . timeout < point ) {
this - > js_ping . current_id + + ;
this - > js_ping . last_request = point ;
Json : : Value jsonCandidate ;
jsonCandidate [ " type " ] = " ping " ;
jsonCandidate [ " payload " ] = to_string ( this - > js_ping . current_id ) ;
this - > sendJson ( jsonCandidate ) ;
}
}
2019-07-17 19:37:18 +02:00
}
void WebClient : : onWSConnected ( ) {
this - > state = ConnectionState : : INIT_HIGH ;
this - > handshake . state = HandshakeState : : BEGIN ;
debugMessage ( this - > getServerId ( ) , " {} WebSocket handshake completed! " , CLIENT_STR_LOG_PREFIX ) ;
//TODO here!
this - > properties ( ) [ property : : CLIENT_TYPE ] = ClientType : : CLIENT_TEAMSPEAK ;
this - > properties ( ) [ property : : CLIENT_TYPE_EXACT ] = ClientType : : CLIENT_WEB ;
this - > properties ( ) [ property : : CLIENT_UNIQUE_IDENTIFIER ] = " UnknownWebClient " ;
this - > properties ( ) [ property : : CLIENT_NICKNAME ] = string ( ) + " UnknownWebClient # " ;
2020-01-24 02:57:58 +01:00
/*
2019-07-17 19:37:18 +02:00
Command init ( " handshakeidentify " ) ;
init [ " successfully " ] = true ;
this - > sendCommand ( init ) ;
2020-01-24 02:57:58 +01:00
*/
2019-07-17 19:37:18 +02:00
}
void WebClient : : onWSDisconnected ( const string & error ) {
2020-01-24 02:57:58 +01:00
string message ;
uint16_t close_code = 0 ;
if ( error . length ( ) < 2 )
close_code = static_cast < uint16_t > ( - 1 ) ;
else {
close_code = be2le16 ( & error [ 0 ] ) ;
message = error . substr ( 2 ) ;
}
2019-07-17 19:37:18 +02:00
debugMessage ( this - > getServerId ( ) , " {} WS disconnected ({}). Application data: {} " , CLIENT_STR_LOG_PREFIX , close_code , message ) ;
2020-01-24 02:57:58 +01:00
this - > closeConnection ( ) ; //TODO?
2019-07-17 19:37:18 +02:00
}
void WebClient : : onWSMessage ( const pipes : : WSMessage & message ) {
2020-01-24 02:57:58 +01:00
if ( message . code = = pipes : : OpCode : : TEXT )
this - > handleMessage ( message . data . string ( ) ) ;
else if ( message . code = = pipes : : OpCode : : PING ) {
logTrace ( this - > getServerId ( ) , " {} Received ping on web socket. Application data length: {}. Sending pong " , CLIENT_STR_LOG_PREFIX , message . data . length ( ) ) ;
this - > ws_handler . send ( { pipes : : PONG , message . data } ) ;
} else if ( message . code = = pipes : : OpCode : : PONG ) {
if ( message . data . length ( ) < 1 ) {
logError ( this - > getServerId ( ) , " {} Received pong on web socket with too short payload: {}. Ignoring pong " , CLIENT_STR_LOG_PREFIX , message . data . length ( ) ) ;
return ;
}
uint8_t response_id = be2le8 ( & message . data [ 0 ] ) ;
if ( response_id ! = this - > ping . current_id ) {
debugMessage (
this - > getServerId ( ) ,
" {} Received pong on web socket which is older than the last request. Delay may over {}ms? (Index: {}, Current index: {}) " ,
CLIENT_STR_LOG_PREFIX ,
duration_cast < milliseconds > ( this - > ping . timeout ) . count ( ) ,
response_id ,
this - > ping . current_id
) ;
return ;
}
this - > ping . last_response = system_clock : : now ( ) ;
this - > ping . value = duration_cast < nanoseconds > ( this - > ping . last_response - this - > ping . last_request ) ;
/*
debugMessage (
this - > getServerId ( ) ,
" {} Received pong on web socket. Ping: {}, PingID: {} " ,
CLIENT_STR_LOG_PREFIX ,
( float ) duration_cast < microseconds > ( this - > ping . value ) . count ( ) / 1000.f ,
response_id
) ;
*/
}
2019-07-17 19:37:18 +02:00
}
/* called while helding close_lock*/
void WebClient : : disconnectFinal ( ) {
2020-01-24 02:57:58 +01:00
auto self_lock = this - > _this . lock ( ) ;
{
/* waiting to finish all executes */
lock_guard lock ( this - > execute_lock ) ;
}
if ( this - > flush_thread . get_id ( ) = = this_thread : : get_id ( ) )
this - > flush_thread . detach ( ) ;
else
assert ( ! this - > flush_thread . joinable ( ) ) ; /* shall be already joined via closeConnection(...)*/
{
: : event * event_read , * event_write ;
{
unique_lock event_lock ( this - > event_lock ) ;
event_read = this - > readEvent ;
event_write = this - > writeEvent ;
this - > readEvent = nullptr ;
this - > writeEvent = nullptr ;
}
if ( event_read ) {
event_del_block ( event_read ) ;
event_free ( event_read ) ;
}
if ( event_write ) {
event_del_block ( event_write ) ;
event_free ( event_write ) ;
}
}
2019-07-17 19:37:18 +02:00
if ( this - > file_descriptor > 0 ) {
shutdown ( this - > file_descriptor , SHUT_RDWR ) ;
close ( this - > file_descriptor ) ;
this - > file_descriptor = - 1 ;
}
this - > processLeave ( ) ;
2020-01-24 02:57:58 +01:00
{
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
unique_lock read_voice_bridge_lock ( this - > voice_bridge_lock ) ;
if ( this - > voice_bridge ) {
//TODO correct close?
this - > voice_bridge = nullptr ;
}
}
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
this - > ssl_handler . finalize ( ) ;
2019-07-17 19:37:18 +02:00
this - > handle - > unregisterConnection ( static_pointer_cast < WebClient > ( self_lock ) ) ;
}
Json : : CharReaderBuilder json_reader_builder = [ ] {
2020-01-24 02:57:58 +01:00
Json : : CharReaderBuilder reader_builder ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
return reader_builder ;
2019-07-17 19:37:18 +02:00
} ( ) ;
void WebClient : : handleMessage ( const std : : string & message ) {
2020-01-24 02:57:58 +01:00
/* Not really a need, this will directly be called via the ssl ws pipe, which has been triggered via progress message */
threads : : MutexLock lock ( this - > execute_lock ) ;
2019-07-17 19:37:18 +02:00
Json : : Value val ;
try {
2020-01-24 02:57:58 +01:00
unique_ptr < Json : : CharReader > reader { json_reader_builder . newCharReader ( ) } ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
string error_message ;
2019-07-17 19:37:18 +02:00
if ( ! reader - > parse ( message . data ( ) , message . data ( ) + message . length ( ) , & val , & error_message ) ) throw Json : : Exception ( " Could not parse payload! ( " + error_message + " ) " ) ;
} catch ( const std : : exception & ex ) {
logError ( this - > server - > getServerId ( ) , " Could not parse web message! Message: " + string ( ex . what ( ) ) ) ;
logTrace ( this - > server - > getServerId ( ) , " Payload: " + message ) ;
return ;
}
2020-01-24 02:57:58 +01:00
logTrace ( this - > server - > getServerId ( ) , " [{}] Read message {} " , CLIENT_STR_LOG_PREFIX_ ( this ) , message ) ;
2019-07-17 19:37:18 +02:00
try {
if ( val [ " type " ] . isNull ( ) ) {
logError ( this - > server - > getServerId ( ) , " [{}] Invalid web json package! " ) ;
return ;
}
if ( val [ " type " ] . asString ( ) = = " command " ) {
Command cmd ( val [ " command " ] . asString ( ) ) ;
for ( int index = 0 ; index < val [ " data " ] . size ( ) ; index + + ) {
for ( auto it = val [ " data " ] [ index ] . begin ( ) ; it ! = val [ " data " ] [ index ] . end ( ) ; it + + )
cmd [ index ] [ it . key ( ) . asString ( ) ] = ( * it ) . asString ( ) ;
}
2020-01-24 02:57:58 +01:00
for ( const auto & index : val [ " flags " ] ) {
cmd . enableParm ( index . asString ( ) ) ;
}
2019-07-17 19:37:18 +02:00
this - > handleCommandFull ( cmd , true ) ;
} else if ( val [ " type " ] . asString ( ) = = " WebRTC " ) {
auto subType = val [ " request " ] . asString ( ) ;
if ( subType = = " create " ) {
2020-01-24 02:57:58 +01:00
unique_lock voice_bridge_lock ( this - > voice_bridge_lock ) ;
2019-07-17 19:37:18 +02:00
if ( this - > voice_bridge ) {
logError ( this - > server - > getServerId ( ) , " [{}] Tried to register a WebRTC channel twice! " , CLIENT_STR_LOG_PREFIX_ ( this ) ) ;
//return;
2020-01-24 02:57:58 +01:00
this - > voice_bridge = nullptr ;
2019-07-17 19:37:18 +02:00
}
//TODO test if bridge already exists!
this - > voice_bridge = make_unique < web : : VoiceBridge > ( dynamic_pointer_cast < WebClient > ( _this . lock ( ) ) ) ; //FIXME Add config
2020-01-24 02:57:58 +01:00
this - > voice_bridge - > callback_voice_data = [ & ] ( const pipes : : buffer_view & buffer , bool a , bool b ) {
/* may somehow get the "real" packet size? */
this - > connectionStatistics - > logIncomingPacket ( stats : : ConnectionStatistics : : category : : VOICE , buffer . length ( ) ) ;
this - > handlePacketVoice ( buffer , a , b ) ;
} ;
this - > voice_bridge - > callback_initialized = [ & ] ( ) {
debugMessage ( this - > getServerId ( ) , " {} Voice bridge initialized! " , CLIENT_STR_LOG_PREFIX ) ;
} ;
this - > voice_bridge - > callback_failed = [ & ] {
auto vb_ptr = & * this - > voice_bridge ; /* read only no lock needed */
std : : thread ( [ & , vb_ptr , lock = this - > ref ( ) ] {
unique_lock vbl { this - > voice_bridge_lock } ;
if ( & * this - > voice_bridge = = vb_ptr )
this - > voice_bridge . release ( ) ;
} ) . detach ( ) ;
Json : : Value response ;
response [ " type " ] = " WebRTC " ;
response [ " request " ] = " status " ;
response [ " state " ] = " failed " ;
response [ " reason " ] = " voice bridge setup failed! " ;
response [ " allow_reconnect " ] = true ;
this - > sendJson ( response ) ;
} ;
this - > voice_bridge - > callback_ice_candidate = [ & ] ( const rtc : : IceCandidate & ice ) {
Json : : Value jsonCandidate ;
jsonCandidate [ " type " ] = " WebRTC " ;
jsonCandidate [ " request " ] = " ice " ;
jsonCandidate [ " msg " ] [ " candidate " ] = ice . candidate ;
jsonCandidate [ " msg " ] [ " sdpMid " ] = ice . sdpMid ;
jsonCandidate [ " msg " ] [ " sdpMLineIndex " ] = ice . sdpMLineIndex ;
this - > sendJson ( jsonCandidate ) ;
} ;
this - > voice_bridge - > callback_ice_candidate_finished = [ & ] {
Json : : Value jsonCandidate ;
jsonCandidate [ " type " ] = " WebRTC " ;
jsonCandidate [ " request " ] = " ice_finish " ;
this - > sendJson ( jsonCandidate ) ;
} ;
auto vbp = & * this - > voice_bridge ;
voice_bridge_lock . unlock ( ) ;
shared_lock read_voice_bridge_lock ( this - > voice_bridge_lock ) ;
if ( vbp ! = & * this - > voice_bridge ) {
Json : : Value response ;
response [ " type " ] = " WebRTC " ;
response [ " request " ] = " status " ;
response [ " state " ] = " failed " ;
response [ " reason " ] = " initialize failed (obsolete bridge) " ;
response [ " allow_reconnect " ] = true ;
this - > sendJson ( response ) ;
return ;
}
string error ;
if ( ! this - > voice_bridge - > initialize ( error ) ) {
Json : : Value response ;
response [ " type " ] = " WebRTC " ;
response [ " request " ] = " status " ;
response [ " state " ] = " failed " ;
response [ " reason " ] = " initialize failed ( " + error + " ) " ;
response [ " allow_reconnect " ] = true ;
this - > sendJson ( response ) ;
return ;
}
if ( ! this - > voice_bridge - > parse_offer ( val [ " msg " ] [ " sdp " ] . asString ( ) ) ) {
Json : : Value response ;
response [ " type " ] = " WebRTC " ;
response [ " request " ] = " status " ;
response [ " state " ] = " failed " ;
response [ " reason " ] = " offer apply failed ( " + error + " ) " ;
response [ " allow_reconnect " ] = true ;
this - > sendJson ( response ) ;
return ;
} else {
auto sdp_response = this - > voice_bridge - > generate_answer ( ) ;
Json : : Value response ;
response [ " type " ] = " WebRTC " ;
response [ " request " ] = " answer " ;
response [ " msg " ] [ " sdp " ] = sdp_response ;
response [ " msg " ] [ " type " ] = " answer " ;
this - > sendJson ( response ) ;
}
2019-07-17 19:37:18 +02:00
} else if ( subType = = " ice " ) {
2020-01-24 02:57:58 +01:00
shared_lock read_voice_bridge_lock ( this - > voice_bridge_lock ) ;
2019-07-17 19:37:18 +02:00
if ( ! this - > voice_bridge ) {
2020-01-24 02:57:58 +01:00
debugMessage ( this - > getServerId ( ) , " [{}] Received remote ICE candidate without having a voice bridge! Dropping candidate. " , CLIENT_STR_LOG_PREFIX ) ;
2019-07-17 19:37:18 +02:00
return ;
} else {
2020-01-24 02:57:58 +01:00
auto candidate_string = val [ " msg " ] [ " candidate " ] . asString ( ) ;
auto sdp_mid = val [ " msg " ] [ " sdpMid " ] . asString ( ) ;
auto sdp_line_index = val [ " msg " ] [ " sdpMLineIndex " ] . asInt ( ) ;
deque < shared_ptr < rtc : : IceCandidate > > candidates ;
candidates . push_back ( make_shared < rtc : : IceCandidate > ( candidate_string , sdp_mid , sdp_line_index ) ) ;
auto result = this - > voice_bridge - > apply_ice ( candidates ) ;
if ( result ! = candidates . size ( ) ) {
logError ( this - > getServerId ( ) ,
" [{}] Failed to apply remote ICE candidate, result: {}. Channel: {} ({}). Candidate: {} " ,
CLIENT_STR_LOG_PREFIX_ ( this ) ,
result ,
sdp_line_index ,
sdp_mid ,
candidate_string
) ;
} else {
logTrace ( this - > getServerId ( ) , " [{}] Successfully added ICE candidate for channel {} ({}). " , CLIENT_STR_LOG_PREFIX_ ( this ) , sdp_line_index , sdp_mid ) ;
}
2019-07-17 19:37:18 +02:00
}
} else if ( subType = = " ice_finish " ) {
2020-01-24 02:57:58 +01:00
shared_lock read_voice_bridge_lock ( this - > voice_bridge_lock ) ;
if ( ! this - > voice_bridge ) {
debugMessage ( this - > getServerId ( ) , " [{}] Received remote ICE candidate without having a voice bridge! Dropping candidate. " , CLIENT_STR_LOG_PREFIX ) ;
return ;
}
this - > voice_bridge - > remote_ice_finished ( ) ;
2019-07-17 19:37:18 +02:00
}
2019-07-21 10:43:26 +02:00
} else if ( val [ " type " ] . asString ( ) = = " ping " ) {
2020-01-24 02:57:58 +01:00
Json : : Value response ;
response [ " type " ] = " pong " ;
response [ " payload " ] = val [ " payload " ] ;
response [ " ping_native " ] = to_string ( duration_cast < microseconds > ( this - > ping . value ) . count ( ) ) ;
this - > sendJson ( response ) ;
return ;
2019-07-21 10:43:26 +02:00
} else if ( val [ " type " ] . asString ( ) = = " pong " ) {
2020-01-24 02:57:58 +01:00
auto payload = val [ " payload " ] . isString ( ) ? val [ " payload " ] . asString ( ) : " " ;
uint8_t response_id = 0 ;
try {
response_id = ( uint8_t ) stoul ( payload ) ;
} catch ( std : : exception & ex ) {
debugMessage ( this - > getServerId ( ) , " [{}] Failed to parse pong payload. " ) ;
return ;
}
if ( response_id ! = this - > js_ping . current_id ) {
debugMessage (
this - > getServerId ( ) ,
" {} Received pong on web socket from javascript which is older than the last request. Delay may over {}ms? (Index: {}, Current index: {}) " ,
CLIENT_STR_LOG_PREFIX ,
duration_cast < milliseconds > ( this - > js_ping . timeout ) . count ( ) ,
response_id ,
this - > js_ping . current_id
) ;
return ;
}
this - > js_ping . last_response = system_clock : : now ( ) ;
this - > js_ping . value = duration_cast < nanoseconds > ( this - > js_ping . last_response - this - > js_ping . last_request ) ;
2019-07-17 19:37:18 +02:00
}
} catch ( const std : : exception & ex ) {
logError ( this - > server - > getServerId ( ) , " Could not handle json packet! Message {} " , ex . what ( ) ) ;
logTrace ( this - > server - > getServerId ( ) , " Message: {} " , message ) ;
}
}
//candidate:3260795824 1 udp 2122194687 192.168.43.141 37343 typ host generation 0 ufrag JCsw network-id 2
//candidate:0 1 UDP 2122252543 192.168.43.141 34590 typ host
bool WebClient : : disconnect ( const std : : string & reason ) {
2020-01-24 02:57:58 +01:00
{
unique_lock server_channel_lock ( this - > server - > channel_tree_lock ) ;
{
unique_lock own_lock ( this - > channel_lock ) ;
this - > notifyClientLeftViewKicked ( this - > ref ( ) , nullptr , reason , this - > server - > serverRoot , false ) ;
}
this - > server - > client_move ( this - > ref ( ) , nullptr , this - > server - > serverRoot , reason , ViewReasonId : : VREASON_SERVER_KICK , false , server_channel_lock ) ;
this - > server - > unregisterClient ( _this . lock ( ) , " disconnected " , server_channel_lock ) ;
}
2019-07-17 19:37:18 +02:00
return this - > closeConnection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
}
2020-01-25 23:42:37 +01:00
command_result WebClient : : handleCommandClientInit ( Command & command ) {
2020-01-24 02:57:58 +01:00
return SpeakingClient : : handleCommandClientInit ( command ) ;
2019-07-17 19:37:18 +02:00
}
bool WebClient : : shouldReceiveVoice ( const std : : shared_ptr < ConnectedClient > & sender ) {
2020-01-24 02:57:58 +01:00
shared_lock read_voice_bridge_lock ( this - > voice_bridge_lock ) ;
if ( ! this - > voice_bridge | | ! this - > voice_bridge - > voice_channel ( ) ) return false ;
return SpeakingClient : : shouldReceiveVoice ( sender ) ;
2019-07-17 19:37:18 +02:00
}
void WebClient : : handlePacketVoiceWhisper ( const pipes : : buffer_view & string , bool flag ) {
2020-01-24 02:57:58 +01:00
shared_lock read_voice_bridge_lock ( this - > voice_bridge_lock ) ;
if ( ! this - > voice_bridge | | ! this - > voice_bridge - > voice_channel ( ) ) return ;
SpeakingClient : : handlePacketVoiceWhisper ( string , flag ) ;
2019-07-17 19:37:18 +02:00
}
void WebClient : : send_voice_packet ( const pipes : : buffer_view & view , const SpeakingClient : : VoicePacketFlags & flags ) {
2020-01-24 02:57:58 +01:00
shared_lock read_voice_bridge_lock ( this - > voice_bridge_lock ) ;
if ( this - > voice_bridge ) {
auto channel = this - > voice_bridge - > voice_channel ( ) ;
if ( channel ) {
channel - > send ( view ) ;
/* may somehow get the "real" packet size? */
this - > connectionStatistics - > logOutgoingPacket ( stats : : ConnectionStatistics : : category : : VOICE , view . length ( ) ) ;
}
}
2019-07-17 19:37:18 +02:00
}
void WebClient : : send_voice_whisper_packet ( const pipes : : buffer_view & view , const SpeakingClient : : VoicePacketFlags & flags ) {
2020-01-24 02:57:58 +01:00
logError ( this - > server - > getServerId ( ) , " Web client got whisper packet " ) ;
//As well log the data!
2019-07-17 19:37:18 +02:00
}