1
0
mirror of https://gitlab.com/comunic/comunicapiv3 synced 2024-11-22 13:29:21 +00:00

Relay client call signals to RTC proxy

This commit is contained in:
Pierre HUBERT 2021-02-11 18:29:29 +01:00
parent 024d83619d
commit 4c8d4345a2
9 changed files with 264 additions and 20 deletions

11
Cargo.lock generated
View File

@ -640,6 +640,7 @@ dependencies = [
"serde",
"serde_json",
"sha1",
"webrtc-sdp",
"yaml-rust",
]
@ -2846,6 +2847,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webrtc-sdp"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ca3fe7f015c28dcfbea0b0e05fbcb1a26ed902f80509dd0a4bf40556e73f58"
dependencies = [
"log",
"url",
]
[[package]]
name = "widestring"
version = "0.4.0"

View File

@ -32,3 +32,4 @@ pdf = "0.6.3"
regex = "1.4.2"
dashmap = "3.11.10"
reqwest = { version = "0.10.6", features = ["json", "blocking"] }
webrtc-sdp = "0.3.6"

View File

@ -4,6 +4,8 @@
use std::collections::HashMap;
use webrtc_sdp::attribute_type::SdpAttribute;
use crate::api_data::call_member_info::CallMemberInfo;
use crate::api_data::joined_call_message::JoinedCallMessage;
use crate::api_data::left_call_message::LeftCallMessage;
@ -11,16 +13,55 @@ use crate::api_data::user_calls_config::UserCallsConfig;
use crate::controllers::routes::RequestResult;
use crate::controllers::user_ws_controller;
use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::call_signal::{CallSignal, IceCandidate, NewUserCallSignal, SdpType};
use crate::data::config::conf;
use crate::data::conversation::ConvID;
use crate::data::error::{ExecError, Res};
use crate::data::http_request_handler::HttpRequestHandler;
use crate::data::user::UserID;
use crate::data::user_ws_connection::{ActiveCall, UserWsConnection};
use crate::data::user_ws_message::UserWsMessage;
use crate::data::user_ws_request_handler::UserWsRequestHandler;
use crate::helpers::{calls_helper, conversations_helper, events_helper};
use crate::helpers::events_helper::Event;
impl UserWsRequestHandler {
/// Get the ID of a call included in a WebSocket request
fn post_call_id(&mut self, name: &str) -> Res<ConvID> {
let conv_id = self.post_u64(name)?;
if !self.get_conn().is_having_call_with_conversation(&conv_id) {
self.forbidden("You do not belong to this call!".to_string())?;
}
Ok(conv_id)
}
/// Get the ID of a peer for a call included in the WebSocket request
fn post_call_peer_id(&mut self, call_id: &ConvID, name: &str) -> Res<UserID> {
let peer_id = UserID::new(self.post_u64(name)?);
if peer_id == self.user_id_ref()? {
return Ok(peer_id);
}
let mut found = false;
user_ws_controller::foreach_connection(|p| {
if !found && p.user_id == peer_id && p.is_having_call_with_conversation(call_id) {
found = true;
}
Ok(())
})?;
if !found {
self.forbidden("This peer is not a member of the call !".to_string())?;
}
Ok(peer_id)
}
}
/// Get legacy call configuration
pub fn get_legacy_config(r: &mut HttpRequestHandler) -> RequestResult {
let mut map = HashMap::new();
@ -137,6 +178,89 @@ pub fn get_members_list(r: &mut UserWsRequestHandler) -> RequestResult {
r.set_response(list)
}
/// Get the hash associated to a call
pub fn gen_call_hash(call_id: &ConvID, peer_id: &UserID) -> String {
format!("{}-{}", call_id, peer_id.id())
}
/// Handles client signal
pub fn on_client_signal(r: &mut UserWsRequestHandler) -> RequestResult {
let call_id = r.post_call_id("callID")?;
let peer_id = r.post_call_peer_id(&call_id, "peerID")?;
let sig_type = r.post_string("type")?;
let data: serde_json::Value = serde_json::from_str(&r.post_string("data")?)?;
let data = data
.as_object()
.ok_or(ExecError::boxed_new("Signal data is not an object !"))?;
let signal = match sig_type.as_str() {
"SDP" => {
// Get SDP type
let sdp_type = SdpType::from_str(data.get("type")
.unwrap_or(&serde_json::Value::Null)
.as_str()
.ok_or(ExecError::boxed_new("Missing SDP type !"))?
)?;
let sdp = data
.get("sdp")
.unwrap_or(&serde_json::Value::Null)
.as_str()
.ok_or(ExecError::boxed_new("Failed to get sdp message data!"))?;
let parsed_sdp = webrtc_sdp::parse_sdp(sdp, false)?;
CallSignal::SDP(sdp.to_string(), sdp_type, parsed_sdp)
}
"CANDIDATE" => {
let candidate = data
.get("candidate")
.unwrap_or(&serde_json::Value::Null)
.as_str()
.ok_or(ExecError::boxed_new("Failed to get candidate message data!"))?
.to_string();
let sdp_m_line_index = data
.get("sdpMLineIndex")
.unwrap_or(&serde_json::Value::Null)
.as_u64()
.ok_or(ExecError::boxed_new("Failed to get sdp_mline_index data!"))?;
let sdp_mid = data
.get("sdpMid")
.unwrap_or(&serde_json::Value::Null)
.as_str()
.ok_or(ExecError::boxed_new("Failed to get sdpMid message data!"))?
.to_string();
let parsed_candidate: SdpAttribute = candidate.trim().parse()?;
CallSignal::Candidate(IceCandidate {
candidate,
sdp_m_line_index,
sdp_mid,
}, parsed_candidate)
}
_ => {
r.forbidden("Invalid signal type!".to_string())?;
unreachable!()
}
};
events_helper::propagate_event(&Event::NewUserCallSignal(&NewUserCallSignal {
call_hash: gen_call_hash(&call_id, &peer_id),
user_id: if r.user_id_ref()? == &peer_id { None } else { Some(r.user_id()?) },
signal,
raw_data: r.post_string("data")?,
}))?;
r.success("Signal sent")
}
/// Make the user leave the call
pub fn make_user_leave_call(conv_id: &ConvID, connection: &UserWsConnection) -> Res {

View File

@ -7,7 +7,9 @@ use actix::prelude::*;
use actix_web_actors::ws::{Message, ProtocolError};
use serde::Serialize;
use crate::data::call_signal::CallSignal;
use crate::data::config::conf;
use crate::data::error::{ExecError, Res};
use crate::helpers::events_helper;
use crate::helpers::events_helper::Event;
@ -16,7 +18,8 @@ struct RtcRelayActor {}
#[derive(Message)]
#[rtype(result = "()")]
enum RTCMessages {
CLOSE
Close,
SendMessage(serde_json::Value),
}
#[allow(non_snake_case)]
@ -32,6 +35,21 @@ struct CallsConfigWrapper {
data: CallsConfig,
}
#[derive(Serialize)]
struct CallUserSignalData {
r#type: String,
data: serde_json::Value,
}
#[allow(non_snake_case)]
#[derive(Serialize)]
struct CallUserSignal {
title: String,
callHash: String,
peerId: String,
data: CallUserSignalData,
}
/// Current WebSocket connection
static mut ACTIVE_RTC_CONNECTION: Option<Addr<RtcRelayActor>> = None;
@ -87,8 +105,24 @@ impl StreamHandler<Result<actix_web_actors::ws::Message, actix_web_actors::ws::P
impl Handler<RTCMessages> for RtcRelayActor {
type Result = ();
fn handle(&mut self, _msg: RTCMessages, ctx: &mut Self::Context) -> Self::Result {
ctx.close(None)
fn handle(&mut self, msg: RTCMessages, ctx: &mut Self::Context) -> Self::Result {
match msg {
// Close connection
RTCMessages::Close => {
ctx.close(None)
}
// Send message
RTCMessages::SendMessage(msg) => {
match serde_json::to_string(&msg) {
Ok(txt) => { ctx.text(txt) }
Err(e) => {
eprintln!("Failed to send message to RTC relay ! {:#?}", e);
}
}
}
}
}
}
@ -146,9 +180,43 @@ pub async fn open_ws(req: actix_web::HttpRequest,
// Close previous connection (if any)
if let Some(conn) = get_active_connection() {
conn.do_send(RTCMessages::CLOSE);
conn.do_send(RTCMessages::Close);
}
// Start the actor
actix_web_actors::ws::start(RtcRelayActor {}, &req, stream)
}
/// Send a message to the relay
fn send_message_to_relay<T>(e: T) -> Res where T: Serialize {
let conn = get_active_connection()
.ok_or(ExecError::boxed_new("Connection to RTC relay missing!"))?;
conn.do_send(RTCMessages::SendMessage(serde_json::to_value(e)?));
Ok(())
}
/// Events handler
pub fn handle_event(e: &events_helper::Event) -> Res {
match e {
Event::NewUserCallSignal(signal) => {
send_message_to_relay(CallUserSignal {
title: "signal".to_string(),
callHash: signal.call_hash.to_string(),
peerId: signal.user_id.as_ref().map(|i| i.id()).unwrap_or(0).to_string(),
data: CallUserSignalData {
r#type: match signal.signal {
CallSignal::SDP(_, _, _) => "SDP",
CallSignal::Candidate(_, _) => "CANDIDATE",
}.to_string(),
data: serde_json::from_str(&signal.raw_data)?,
},
})?;
}
_ => {}
}
Ok(())
}

View File

@ -42,6 +42,7 @@ pub fn get_user_ws_routes() -> Vec<UserWsRoute> {
UserWsRoute::new("calls/join", calls_controller::join_call),
UserWsRoute::new("calls/leave", calls_controller::leave_call),
UserWsRoute::new("calls/members", calls_controller::get_members_list),
UserWsRoute::new("calls/signal", calls_controller::on_client_signal),
]
}

45
src/data/call_signal.rs Normal file
View File

@ -0,0 +1,45 @@
//! # Call signal
//!
//! @author Pierre Hubert
use crate::data::error::{ExecError, Res};
use crate::data::user::UserID;
pub enum SdpType {
Offer,
Answer,
}
pub struct IceCandidate {
pub candidate: String,
pub sdp_m_line_index: u64,
pub sdp_mid: String,
}
pub enum CallSignal {
/// Session Description Protocol
SDP(String, SdpType, webrtc_sdp::SdpSession),
/// ICE candidate
Candidate(IceCandidate, webrtc_sdp::attribute_type::SdpAttribute),
}
pub struct NewUserCallSignal {
pub call_hash: String,
/// This value is set to none if the user who streams content is the same
/// as the receiver
pub user_id: Option<UserID>,
pub signal: CallSignal,
pub raw_data: String,
}
impl SdpType {
pub fn from_str(val: &str) -> Res<SdpType> {
match val {
"offer" => Ok(SdpType::Offer),
"answer" => Ok(SdpType::Answer),
_ => Err(ExecError::boxed_new(&format!("SDP type {} is unknown!", val)))
}
}
}

View File

@ -38,3 +38,4 @@ pub mod security_settings;
pub mod new_custom_emoji;
pub mod user_ws_message;
pub mod user_ws_connection;
pub mod call_signal;

View File

@ -7,8 +7,7 @@ use serde::Serialize;
use crate::api_data::http_error::HttpError;
use crate::controllers::routes::RequestResult;
use crate::data::base_request_handler::{BaseRequestHandler, RequestValue};
use crate::data::conversation::ConvID;
use crate::data::error::{Res, ResultBoxError};
use crate::data::error::ResultBoxError;
use crate::data::user::UserID;
use crate::data::user_ws_connection::UserWsConnection;
@ -62,17 +61,6 @@ impl UserWsRequestHandler {
Ok(())
}
/// Get the ID of a call included in a WebSocket request
pub fn post_call_id(&mut self, name: &str) -> Res<ConvID> {
let conv_id = self.post_u64(name)?;
if !self.connection.is_having_call_with_conversation(&conv_id) {
self.forbidden("You do not belong to this call!".to_string())?;
}
Ok(conv_id)
}
}
impl BaseRequestHandler for UserWsRequestHandler {

View File

@ -4,8 +4,9 @@
use crate::controllers::{calls_controller, comments_controller, conversations_controller, notifications_controller, user_ws_controller};
use crate::controllers::{calls_controller, comments_controller, conversations_controller, notifications_controller, rtc_relay_controller, user_ws_controller};
use crate::data::api_client::APIClient;
use crate::data::call_signal::NewUserCallSignal;
use crate::data::comment::Comment;
use crate::data::conversation::ConvID;
use crate::data::conversation_message::ConversationMessage;
@ -55,6 +56,9 @@ pub enum Event<'a> {
/// User left call
UserLeftCall(&'a ConvID, &'a UserID),
/// Got new user call signal
NewUserCallSignal(&'a NewUserCallSignal),
/// No event
None,
}
@ -66,5 +70,6 @@ pub fn propagate_event(e: &Event) -> Res {
notifications_controller::handle_event(e)?;
user_ws_controller::handle_event(e)?;
calls_controller::handle_event(e)?;
rtc_relay_controller::handle_event(e)?;
Ok(())
}