api: add JSON-RPC API to get ICE servers

Currently with a hardcoded TURN server
so it can be used in the UIs.
This commit is contained in:
link2xt
2025-09-16 19:00:58 +00:00
parent 3d2805bc78
commit c5ada9b203
4 changed files with 68 additions and 0 deletions

View File

@@ -8,6 +8,7 @@ use std::{collections::HashMap, str::FromStr};
use anyhow::{anyhow, bail, ensure, Context, Result};
pub use deltachat::accounts::Accounts;
use deltachat::blob::BlobObject;
use deltachat::calls::ice_servers;
use deltachat::chat::{
self, add_contact_to_chat, forward_msgs, get_chat_media, get_chat_msgs, get_chat_msgs_ex,
marknoticed_chat, remove_contact_from_chat, Chat, ChatId, ChatItem, MessageListOptions,
@@ -2111,6 +2112,12 @@ impl CommandApi {
Ok(())
}
/// Returns JSON with ICE servers, to be used for WebRTC video calls.
async fn ice_servers(&self, account_id: u32) -> Result<String> {
let ctx = self.get_context(account_id).await?;
ice_servers(&ctx).await
}
/// Makes an HTTP GET request and returns a response.
///
/// `url` is the HTTP or HTTPS URL.

View File

@@ -2,6 +2,7 @@
from __future__ import annotations
import json
from dataclasses import dataclass
from typing import TYPE_CHECKING, Optional, Union
from warnings import warn
@@ -470,3 +471,8 @@ class Account:
def initiate_autocrypt_key_transfer(self) -> None:
"""Send Autocrypt Setup Message."""
return self._rpc.initiate_autocrypt_key_transfer(self.id)
def ice_servers(self) -> list:
"""Return ICE servers for WebRTC configuration."""
ice_servers_json = self._rpc.ice_servers(self.id)
return json.loads(ice_servers_json)

View File

@@ -66,3 +66,10 @@ a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid\r
incoming_call_event = bob.wait_for_event(EventType.INCOMING_CALL)
assert incoming_call_event.place_call_info == place_call_info
assert incoming_call_event.has_video
def test_ice_servers(acfactory) -> None:
alice = acfactory.get_online_account()
ice_servers = alice.ice_servers()
assert len(ice_servers) == 1

View File

@@ -11,10 +11,12 @@ use crate::headerdef::HeaderDef;
use crate::log::{info, warn};
use crate::message::{self, Message, MsgId, Viewtype};
use crate::mimeparser::{MimeMessage, SystemMessage};
use crate::net::dns::lookup_host_with_cache;
use crate::param::Param;
use crate::tools::time;
use anyhow::{Context as _, Result, ensure};
use sdp::SessionDescription;
use serde::Serialize;
use std::io::Cursor;
use std::time::Duration;
use tokio::task;
@@ -393,5 +395,51 @@ fn sdp_has_video(sdp: &str) -> Result<bool> {
Ok(false)
}
/// ICE server for JSON serialization.
#[derive(Serialize, Debug, Clone, PartialEq)]
struct IceServer {
/// STUN or TURN URLs.
pub urls: Vec<String>,
/// Username for TURN server authentication.
pub username: Option<String>,
/// Password for logging into the server.
pub credential: Option<String>,
}
/// Returns JSON with ICE servers.
///
/// <https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection#iceservers>
///
/// All returned servers are resolved to their IP addresses.
/// The primary point of DNS lookup is that Delta Chat Desktop
/// relies on the servers being specified by IP,
/// because it itself cannot utilize DNS. See
/// <https://github.com/deltachat/deltachat-desktop/issues/5447>.
pub async fn ice_servers(context: &Context) -> Result<String> {
let hostname = "ci-chatmail.testrun.org";
let port = 3478;
let username = "ohV8aec1".to_string();
let password = "zo3theiY".to_string();
// Do not use cache because there is no TLS.
let load_cache = false;
let urls: Vec<String> = lookup_host_with_cache(context, hostname, port, "", load_cache)
.await?
.into_iter()
.map(|addr| format!("turn:{addr}"))
.collect();
let ice_server = IceServer {
urls,
username: Some(username),
credential: Some(password),
};
let json = serde_json::to_string(&[ice_server])?;
Ok(json)
}
#[cfg(test)]
mod calls_tests;