net/quinn: Add stats property for connection statistics

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1613>
This commit is contained in:
Sanchayan Maity 2024-06-14 13:25:39 +05:30
parent 2b35f009fb
commit e00ebca63f
6 changed files with 132 additions and 5 deletions

1
Cargo.lock generated
View file

@ -2704,6 +2704,7 @@ dependencies = [
"gstreamer-check", "gstreamer-check",
"once_cell", "once_cell",
"quinn", "quinn",
"quinn-proto",
"rcgen", "rcgen",
"rustls 0.23.10", "rustls 0.23.10",
"rustls-pemfile 2.1.2", "rustls-pemfile 2.1.2",

View file

@ -4243,6 +4243,18 @@
"type": "guint", "type": "guint",
"writable": true "writable": true
}, },
"stats": {
"blurb": "Connection statistics",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "stats;",
"mutable": "null",
"readable": true,
"type": "GstStructure",
"writable": false
},
"timeout": { "timeout": {
"blurb": "Value in seconds to timeout QUIC endpoint requests (0 = No timeout).", "blurb": "Value in seconds to timeout QUIC endpoint requests (0 = No timeout).",
"conditionally-available": false, "conditionally-available": false,
@ -4551,6 +4563,18 @@
"type": "guint", "type": "guint",
"writable": true "writable": true
}, },
"stats": {
"blurb": "Connection statistics",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "stats;",
"mutable": "null",
"readable": true,
"type": "GstStructure",
"writable": false
},
"timeout": { "timeout": {
"blurb": "Value in seconds to timeout QUIC endpoint requests (0 = No timeout).", "blurb": "Value in seconds to timeout QUIC endpoint requests (0 = No timeout).",
"conditionally-available": false, "conditionally-available": false,

View file

@ -17,6 +17,7 @@ once_cell.workspace = true
tokio = { version = "1.36.0", default-features = false, features = ["time", "rt-multi-thread"] } tokio = { version = "1.36.0", default-features = false, features = ["time", "rt-multi-thread"] }
futures = "0.3.30" futures = "0.3.30"
quinn = { version = "0.11.2", default-features = true, features = ["ring"]} quinn = { version = "0.11.2", default-features = true, features = ["ring"]}
quinn-proto = "0.11.2"
rustls = { version = "0.23", default-features = false, features = ["ring", "std"]} rustls = { version = "0.23", default-features = false, features = ["ring", "std"]}
rustls-pemfile = "2" rustls-pemfile = "2"
rustls-pki-types = "1" rustls-pki-types = "1"

View file

@ -8,8 +8,8 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use crate::utils::{ use crate::utils::{
client_endpoint, make_socket_addr, server_endpoint, wait, QuinnQuicEndpointConfig, WaitError, client_endpoint, get_stats, make_socket_addr, server_endpoint, wait, QuinnQuicEndpointConfig,
CONNECTION_CLOSE_CODE, CONNECTION_CLOSE_MSG, WaitError, CONNECTION_CLOSE_CODE, CONNECTION_CLOSE_MSG,
}; };
use crate::{common::*, utils}; use crate::{common::*, utils};
use bytes::Bytes; use bytes::Bytes;
@ -264,6 +264,11 @@ impl ObjectImpl for QuinnQuicSink {
.nick("Datagram Send Buffer Size") .nick("Datagram Send Buffer Size")
.blurb("Maximum number of outgoing application datagram bytes to buffer") .blurb("Maximum number of outgoing application datagram bytes to buffer")
.build(), .build(),
glib::ParamSpecBoxed::builder::<gst::Structure>("stats")
.nick("Connection statistics")
.blurb("Connection statistics")
.read_only()
.build()
] ]
}); });
@ -399,6 +404,16 @@ impl ObjectImpl for QuinnQuicSink {
"datagram-send-buffer-size" => { "datagram-send-buffer-size" => {
(settings.transport_config.datagram_send_buffer_size as u64).to_value() (settings.transport_config.datagram_send_buffer_size as u64).to_value()
} }
"stats" => {
let state = self.state.lock().unwrap();
match *state {
State::Started(ref state) => {
let connection = state.connection.clone();
get_stats(Some(connection)).to_value()
}
State::Stopped => get_stats(None).to_value(),
}
}
_ => unimplemented!(), _ => unimplemented!(),
} }
} }

View file

@ -8,8 +8,8 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use crate::utils::{ use crate::utils::{
client_endpoint, make_socket_addr, server_endpoint, wait, Canceller, QuinnQuicEndpointConfig, client_endpoint, get_stats, make_socket_addr, server_endpoint, wait, Canceller,
WaitError, CONNECTION_CLOSE_CODE, CONNECTION_CLOSE_MSG, QuinnQuicEndpointConfig, WaitError, CONNECTION_CLOSE_CODE, CONNECTION_CLOSE_MSG,
}; };
use crate::{common::*, utils}; use crate::{common::*, utils};
use bytes::Bytes; use bytes::Bytes;
@ -273,6 +273,11 @@ impl ObjectImpl for QuinnQuicSrc {
.nick("Datagram Send Buffer Size") .nick("Datagram Send Buffer Size")
.blurb("Maximum number of outgoing application datagram bytes to buffer") .blurb("Maximum number of outgoing application datagram bytes to buffer")
.build(), .build(),
glib::ParamSpecBoxed::builder::<gst::Structure>("stats")
.nick("Connection statistics")
.blurb("Connection statistics")
.read_only()
.build()
] ]
}); });
@ -418,6 +423,16 @@ impl ObjectImpl for QuinnQuicSrc {
"datagram-send-buffer-size" => { "datagram-send-buffer-size" => {
(settings.transport_config.datagram_send_buffer_size as u64).to_value() (settings.transport_config.datagram_send_buffer_size as u64).to_value()
} }
"stats" => {
let state = self.state.lock().unwrap();
match *state {
State::Started(ref state) => {
let connection = state.connection.clone();
get_stats(Some(connection)).to_value()
}
State::Stopped => get_stats(None).to_value(),
}
}
_ => unimplemented!(), _ => unimplemented!(),
} }
} }

View file

@ -14,8 +14,10 @@ use gst::ErrorMessage;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use quinn::{ use quinn::{
crypto::rustls::QuicClientConfig, crypto::rustls::QuicServerConfig, default_runtime, crypto::rustls::QuicClientConfig, crypto::rustls::QuicServerConfig, default_runtime,
ClientConfig, Endpoint, EndpointConfig, MtuDiscoveryConfig, ServerConfig, TransportConfig, ClientConfig, Connection, Endpoint, EndpointConfig, MtuDiscoveryConfig, ServerConfig,
TransportConfig,
}; };
use quinn_proto::{ConnectionStats, FrameStats, PathStats, UdpStats};
use std::error::Error; use std::error::Error;
use std::fs::File; use std::fs::File;
use std::io::BufReader; use std::io::BufReader;
@ -416,3 +418,72 @@ pub fn client_endpoint(ep_config: &QuinnQuicEndpointConfig) -> Result<Endpoint,
Ok(endpoint) Ok(endpoint)
} }
pub fn get_stats(connection: Option<Connection>) -> gst::Structure {
match connection {
Some(conn) => {
// See quinn_proto::ConnectionStats
let stats = conn.stats();
let udp_stats = |udp: UdpStats, name: String| -> gst::Structure {
gst::Structure::builder(name)
.field("datagrams", udp.datagrams)
.field("bytes", udp.bytes)
.field("ios", udp.ios)
.build()
};
let frame_stats = |frame: FrameStats, name: String| -> gst::Structure {
gst::Structure::builder(name)
.field("acks", frame.acks)
.field("ack-frequency", frame.ack_frequency)
.field("crypto", frame.crypto)
.field("connection-close", frame.connection_close)
.field("data-blocked", frame.data_blocked)
.field("datagram", frame.datagram)
.field("handshake-done", frame.handshake_done)
.field("immediate-ack", frame.immediate_ack)
.field("max-data", frame.max_data)
.field("max-stream-data", frame.max_stream_data)
.field("max-streams-bidi", frame.max_streams_bidi)
.field("max-streams-uni", frame.max_streams_uni)
.field("new-connection-id", frame.new_connection_id)
.field("new-token", frame.new_token)
.field("path-challenge", frame.path_challenge)
.field("path-response", frame.path_response)
.field("ping", frame.ping)
.field("reset-stream", frame.reset_stream)
.field("retire-connection-id", frame.retire_connection_id)
.field("stream-data-blocked", frame.stream_data_blocked)
.field("streams-blocked-bidi", frame.streams_blocked_bidi)
.field("streams-blocked-uni", frame.streams_blocked_uni)
.field("stop-sending", frame.stop_sending)
.field("stream", frame.stream)
.build()
};
let path_stats = gst::Structure::builder("path")
.field("cwnd", stats.path.cwnd)
.field("congestion-events", stats.path.congestion_events)
.field("lost-packets", stats.path.lost_packets)
.field("lost-bytes", stats.path.lost_bytes)
.field("sent-packets", stats.path.sent_packets)
.field("sent-plpmtud-probes", stats.path.sent_plpmtud_probes)
.field("lost-plpmtud-probes", stats.path.lost_plpmtud_probes)
.field("black-holes-detected", stats.path.black_holes_detected)
.build();
gst::Structure::builder("stats")
.field("udp-tx", udp_stats(stats.udp_tx, "udp-tx".to_string()))
.field("udp-rx", udp_stats(stats.udp_rx, "udp-rx".to_string()))
.field("path", path_stats)
.field(
"frame-tx",
frame_stats(stats.frame_tx, "frame-tx".to_string()),
)
.field(
"frame-rx",
frame_stats(stats.frame_rx, "frame-rx".to_string()),
)
.build()
}
None => gst::Structure::new_empty("stats"),
}
}