net/quinn: Enable client to keep QUIC conn alive

Co-authored-by: Felician Nemeth <nemethf@tmit.bme.hu>
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1568>
This commit is contained in:
Tamas Levai 2024-05-10 11:30:16 +02:00
parent 5549dc7a15
commit 71cd80f204
3 changed files with 61 additions and 14 deletions

View file

@ -3961,6 +3961,20 @@
"type": "guint",
"writable": true
},
"keep-alive-interval": {
"blurb": "Keeps QUIC connection alive by periodically pinging the server. Value set in ms, 0 disables this feature",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "18446744073709551615",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint64",
"writable": true
},
"private-key-file": {
"blurb": "Path to a PKCS8 or RSA private key file",
"conditionally-available": false,

View file

@ -66,6 +66,7 @@ struct Settings {
server_name: String,
alpns: Vec<String>,
timeout: u32,
keep_alive_interval: u64,
secure_conn: bool,
use_datagram: bool,
certificate_file: Option<PathBuf>,
@ -82,6 +83,7 @@ impl Default for Settings {
server_name: DEFAULT_SERVER_NAME.to_string(),
alpns: vec![DEFAULT_ALPN.to_string()],
timeout: DEFAULT_TIMEOUT,
keep_alive_interval: 0,
secure_conn: DEFAULT_SECURE_CONNECTION,
use_datagram: false,
certificate_file: None,
@ -210,6 +212,12 @@ impl ObjectImpl for QuinnQuicSink {
.default_value(DEFAULT_TIMEOUT)
.readwrite()
.build(),
glib::ParamSpecUInt64::builder("keep-alive-interval")
.nick("QUIC connection keep alive interval in ms")
.blurb("Keeps QUIC connection alive by periodically pinging the server. Value set in ms, 0 disables this feature")
.default_value(0)
.readwrite()
.build(),
glib::ParamSpecBoolean::builder("secure-connection")
.nick("Use secure connection")
.blurb("Use certificates for QUIC connection. False: Insecure connection, True: Secure connection.")
@ -269,6 +277,9 @@ impl ObjectImpl for QuinnQuicSink {
"timeout" => {
settings.timeout = value.get().expect("type checked upstream");
}
"keep-alive-interval" => {
settings.keep_alive_interval = value.get().expect("type checked upstream");
}
"secure-connection" => {
settings.secure_conn = value.get().expect("type checked upstream");
}
@ -307,6 +318,7 @@ impl ObjectImpl for QuinnQuicSink {
gst::Array::new(alpns).to_value()
}
"timeout" => settings.timeout.to_value(),
"keep-alive-interval" => settings.keep_alive_interval.to_value(),
"secure-connection" => settings.secure_conn.to_value(),
"certificate-file" => {
let certfile = settings.certificate_file.as_ref();
@ -512,6 +524,7 @@ impl QuinnQuicSink {
let server_name;
let alpns;
let use_datagram;
let keep_alive_interval;
let secure_conn;
let cert_file;
let private_key_file;
@ -529,20 +542,26 @@ impl QuinnQuicSink {
server_name = settings.server_name.clone();
alpns = settings.alpns.clone();
use_datagram = settings.use_datagram;
keep_alive_interval = settings.keep_alive_interval;
secure_conn = settings.secure_conn;
cert_file = settings.certificate_file.clone();
private_key_file = settings.private_key_file.clone();
}
let endpoint =
client_endpoint(client_addr, secure_conn, alpns, cert_file, private_key_file).map_err(
|err| {
WaitError::FutureError(gst::error_msg!(
gst::ResourceError::Failed,
["Failed to configure endpoint: {}", err]
))
},
)?;
let endpoint = client_endpoint(
client_addr,
secure_conn,
alpns,
cert_file,
private_key_file,
keep_alive_interval,
)
.map_err(|err| {
WaitError::FutureError(gst::error_msg!(
gst::ResourceError::Failed,
["Failed to configure endpoint: {}", err]
))
})?;
let connection = endpoint
.connect(server_addr, &server_name)

View file

@ -13,7 +13,7 @@ use gst::ErrorMessage;
use once_cell::sync::Lazy;
use quinn::{
crypto::rustls::QuicClientConfig, crypto::rustls::QuicServerConfig, ClientConfig, Endpoint,
ServerConfig,
ServerConfig, TransportConfig,
};
use std::error::Error;
use std::fs::File;
@ -180,6 +180,7 @@ fn configure_client(
certificate_file: Option<PathBuf>,
private_key_file: Option<PathBuf>,
alpns: Vec<String>,
keep_alive_interval_ms: u64,
) -> Result<ClientConfig, Box<dyn Error>> {
let mut crypto = if secure_conn {
let (certs, key) = read_certs_from_file(certificate_file, private_key_file)?;
@ -203,9 +204,15 @@ fn configure_client(
crypto.alpn_protocols = alpn_protocols;
crypto.key_log = Arc::new(rustls::KeyLogFile::new());
Ok(ClientConfig::new(Arc::new(QuicClientConfig::try_from(
crypto,
)?)))
let mut client_config = ClientConfig::new(Arc::new(QuicClientConfig::try_from(crypto)?));
if keep_alive_interval_ms > 0 {
let mut transport_config = TransportConfig::default();
transport_config.keep_alive_interval(Some(Duration::from_millis(keep_alive_interval_ms)));
client_config.transport_config(Arc::new(transport_config));
}
Ok(client_config)
}
fn read_certs_from_file(
@ -343,8 +350,15 @@ pub fn client_endpoint(
alpns: Vec<String>,
certificate_file: Option<PathBuf>,
private_key_file: Option<PathBuf>,
keep_alive_interval_ms: u64,
) -> Result<Endpoint, Box<dyn Error>> {
let client_cfg = configure_client(secure_conn, certificate_file, private_key_file, alpns)?;
let client_cfg = configure_client(
secure_conn,
certificate_file,
private_key_file,
alpns,
keep_alive_interval_ms,
)?;
let mut endpoint = Endpoint::client(client_addr)?;
endpoint.set_default_client_config(client_cfg);