feat(ctrl): report connection mode in welcome
PLAN.md calls out room modes such as relay, direct-p2p, and relay fallback so future transport choices can fit the protocol. Add an explicit ConnectionMode field to ServerWelcome and default it to relay for existing decoded welcomes. The relay still operates only in relay mode today. Client and gateway startup logs now print the selected mode, which makes the current relay path visible without changing routing or forwarding behavior. Test Plan: - cargo fmt --check - cargo test -p lanparty-ctrl server_welcome -- --nocapture - cargo test -p lanparty-client-win -p lanparty-gateway - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check Refs: PLAN.md
This commit is contained in:
@@ -29,7 +29,7 @@ Transport-agnostic tunnel contract shared by all binaries:
|
|||||||
Reliable control-plane schema shared by the QUIC stream handlers:
|
Reliable control-plane schema shared by the QUIC stream handlers:
|
||||||
|
|
||||||
- endpoint hello messages with role, room, MAC, and datagram budget
|
- endpoint hello messages with role, room, MAC, and datagram budget
|
||||||
- server welcome, reject, peer lifecycle, stats, and disconnect messages
|
- server welcome mode, reject, peer lifecycle, stats, and disconnect messages
|
||||||
- initial room gateway-presence status in server welcomes
|
- initial room gateway-presence status in server welcomes
|
||||||
- room-code, role/MAC, peer-id, and effective-MTU validation
|
- room-code, role/MAC, peer-id, and effective-MTU validation
|
||||||
- length-prefixed JSON control frames for reliable QUIC streams
|
- length-prefixed JSON control frames for reliable QUIC streams
|
||||||
|
|||||||
@@ -113,10 +113,11 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
let session = connect_client(config).await?;
|
let session = connect_client(config).await?;
|
||||||
println!(
|
println!(
|
||||||
"lanparty-client-win connected as peer {} in room id {} with TAP MTU {}; LAN gateway connected {}",
|
"lanparty-client-win connected as peer {} in room id {} with TAP MTU {} over {}; LAN gateway connected {}",
|
||||||
session.welcome().peer_id(),
|
session.welcome().peer_id(),
|
||||||
session.welcome().room_id(),
|
session.welcome().room_id(),
|
||||||
session.welcome().effective_tap_mtu(),
|
session.welcome().effective_tap_mtu(),
|
||||||
|
session.welcome().mode(),
|
||||||
yes_no(session.welcome().gateway_connected())
|
yes_no(session.welcome().gateway_connected())
|
||||||
);
|
);
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
|||||||
@@ -98,6 +98,29 @@ pub enum Role {
|
|||||||
Gateway,
|
Gateway,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Clone, Copy, Default, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize,
|
||||||
|
)]
|
||||||
|
pub enum ConnectionMode {
|
||||||
|
#[default]
|
||||||
|
#[serde(rename = "relay")]
|
||||||
|
Relay,
|
||||||
|
#[serde(rename = "direct-p2p")]
|
||||||
|
DirectP2p,
|
||||||
|
#[serde(rename = "direct-failed-relay-fallback")]
|
||||||
|
DirectFailedRelayFallback,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ConnectionMode {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Relay => f.write_str("relay"),
|
||||||
|
Self::DirectP2p => f.write_str("direct-p2p"),
|
||||||
|
Self::DirectFailedRelayFallback => f.write_str("direct-failed-relay-fallback"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct EndpointHello {
|
pub struct EndpointHello {
|
||||||
protocol_version: u16,
|
protocol_version: u16,
|
||||||
@@ -190,6 +213,8 @@ pub struct ServerWelcome {
|
|||||||
peer_id: u32,
|
peer_id: u32,
|
||||||
effective_tap_mtu: u16,
|
effective_tap_mtu: u16,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
mode: ConnectionMode,
|
||||||
|
#[serde(default)]
|
||||||
gateway_connected: bool,
|
gateway_connected: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,10 +236,17 @@ impl ServerWelcome {
|
|||||||
room_id,
|
room_id,
|
||||||
peer_id,
|
peer_id,
|
||||||
effective_tap_mtu,
|
effective_tap_mtu,
|
||||||
|
mode: ConnectionMode::Relay,
|
||||||
gateway_connected: false,
|
gateway_connected: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn with_mode(mut self, mode: ConnectionMode) -> Self {
|
||||||
|
self.mode = mode;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn with_gateway_connected(mut self, gateway_connected: bool) -> Self {
|
pub const fn with_gateway_connected(mut self, gateway_connected: bool) -> Self {
|
||||||
self.gateway_connected = gateway_connected;
|
self.gateway_connected = gateway_connected;
|
||||||
@@ -263,6 +295,11 @@ impl ServerWelcome {
|
|||||||
self.effective_tap_mtu
|
self.effective_tap_mtu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn mode(&self) -> ConnectionMode {
|
||||||
|
self.mode
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn gateway_connected(&self) -> bool {
|
pub const fn gateway_connected(&self) -> bool {
|
||||||
self.gateway_connected
|
self.gateway_connected
|
||||||
@@ -507,10 +544,45 @@ mod tests {
|
|||||||
fn server_welcome_reports_gateway_presence() {
|
fn server_welcome_reports_gateway_presence() {
|
||||||
let welcome = ServerWelcome::new(1, 2, MIN_USEFUL_TAP_MTU as u16).unwrap();
|
let welcome = ServerWelcome::new(1, 2, MIN_USEFUL_TAP_MTU as u16).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(welcome.mode(), ConnectionMode::Relay);
|
||||||
assert!(!welcome.gateway_connected());
|
assert!(!welcome.gateway_connected());
|
||||||
assert!(welcome.with_gateway_connected(true).gateway_connected());
|
assert!(welcome.with_gateway_connected(true).gateway_connected());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn server_welcome_reports_connection_mode() {
|
||||||
|
let welcome = ServerWelcome::new(1, 2, MIN_USEFUL_TAP_MTU as u16)
|
||||||
|
.unwrap()
|
||||||
|
.with_mode(ConnectionMode::DirectFailedRelayFallback);
|
||||||
|
|
||||||
|
assert_eq!(welcome.mode(), ConnectionMode::DirectFailedRelayFallback);
|
||||||
|
assert_eq!(ConnectionMode::Relay.to_string(), "relay");
|
||||||
|
assert_eq!(ConnectionMode::DirectP2p.to_string(), "direct-p2p");
|
||||||
|
assert_eq!(
|
||||||
|
ConnectionMode::DirectFailedRelayFallback.to_string(),
|
||||||
|
"direct-failed-relay-fallback"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
serde_json::to_string(&ConnectionMode::DirectP2p).unwrap(),
|
||||||
|
r#""direct-p2p""#
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
serde_json::from_str::<ConnectionMode>(r#""direct-failed-relay-fallback""#).unwrap(),
|
||||||
|
ConnectionMode::DirectFailedRelayFallback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn server_welcome_defaults_missing_mode_to_relay() {
|
||||||
|
let json = format!(
|
||||||
|
r#"{{"protocol_version":1,"room_id":1,"peer_id":2,"effective_tap_mtu":{}}}"#,
|
||||||
|
MIN_USEFUL_TAP_MTU
|
||||||
|
);
|
||||||
|
let welcome: ServerWelcome = serde_json::from_str(&json).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(welcome.mode(), ConnectionMode::Relay);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn peer_info_enforces_role_mac_rules() {
|
fn peer_info_enforces_role_mac_rules() {
|
||||||
let client = PeerInfo::new(7, Role::Client, Some(client_mac())).unwrap();
|
let client = PeerInfo::new(7, Role::Client, Some(client_mac())).unwrap();
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
let gateway = connect_gateway(config).await?;
|
let gateway = connect_gateway(config).await?;
|
||||||
println!(
|
println!(
|
||||||
"lanparty-gateway connected as peer {} in room id {} with TAP MTU {}",
|
"lanparty-gateway connected as peer {} in room id {} with TAP MTU {} over {}",
|
||||||
gateway.welcome().peer_id(),
|
gateway.welcome().peer_id(),
|
||||||
gateway.welcome().room_id(),
|
gateway.welcome().room_id(),
|
||||||
gateway.welcome().effective_tap_mtu()
|
gateway.welcome().effective_tap_mtu(),
|
||||||
|
gateway.welcome().mode()
|
||||||
);
|
);
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user