4.8 KiB
Connector
This file documents the connection and version negotiation logic.
Registry
The registry module provides a multi-version Registry used by the host for
version negotiation. Accessed through the mtp facade (requires the host
feature). In this repository, Registry::builtin() is generated from
example/type-maps.yaml, which currently contains
protocol version 3.0 only. Downstream projects can register additional versions
in their own YAML configuration.
use mtp::codec::{Version, registry::Registry};
let registry = Registry::builtin(); // loads all TypeMaps from the build config
// Check if a version is supported
assert!(registry.supports(&Version(3, 0)));
// Find highest mutual version for a client
let client_versions = &[Version(2, 0), Version(3, 0)];
let negotiated = registry.negotiate(client_versions);
assert_eq!(negotiated, Some(Version(3, 0)));
// Look up a version's TypeMap
let tm = registry.get(&Version(3, 0)).unwrap();
The Registry::builtin() constructor uses the TypeMap::vX_Y() methods generated from the config.
Host
The host creates a QUIC server, manages the registry, and handles version negotiation with each connecting client.
Initialization
The host binds to the address and port supplied in HostConfig:
use mtp::host::{HostConfig, MTPHost};
let config = HostConfig::new(
"0.0.0.0".parse()?,
4433,
std::fs::read("cert.pem")?,
std::fs::read("key.pem")?,
);
let mut host = MTPHost::new(config).await?;
Accepting Connections with Version Negotiation
while let Some(conn) = host.accept().await? {
// conn.version is the negotiated version
// conn.codec is a VersionedCodec scoped to that version
// conn.sender / conn.receive() for application CommunicationValue I/O
let msg = conn.receive().await?;
}
The host reads the reserved opening frame, extracts DataType::Version, calls registry.negotiate, and returns AcceptError::UnsupportedVersion when no registered version matches.
Authentication follows the version-bearing hello when the host enables it. The sequence is defined in Protocol Reference.
Client
The client connects to a host and uses a single compiled-in protocol version.
use mtp::client::{ClientConfig, MTPClient};
let config = ClientConfig::new("https://host.example.com:4433");
let pinned = config.clone().with_pinned_pem(cert_pem_bytes);
// Connect (unauthenticated, existing client)
let conn = MTPClient::connect(config.clone().with_client_id(8765)).await?;
// Authenticated login
let conn = MTPClient::auth_connect(pinned.with_client_id(8765), &keys, &host_pk).await?;
// Registration (new client)
let conn = MTPClient::auth_register(config, &keys, &host_pk).await?;
The client's PROTOCOL_VERSION constant is set by protocol_version in type-maps.yaml and baked in at compile time. The client uses one version and does not import the registry.
Version Negotiation Flow
Client (v3.0) Host (v3.0)
| |
| QUIC connect |
|----------------------->|
| |
| CommValue{ Ident. } |
| Version -> "3.0" |
| Id -> 8765 |
| (unsigned hello; auth |
| challenge follows) |
|----------------------->|
| | registry.negotiate(&[Version(3,0)])
| | -> Some(Version(3,0))
| |
| Response | selected v3.0 TypeMap
|<-----------------------|
| Status, version |
| |
| subsequent messages |
| use v3.0 TypeMap |
If the client sends an unsupported version (for example, v2.0 to the current
repository builtin host), negotiate returns None and the connection is
closed.
Protocol Ping and Pong
See Protocol Reference.
Protocol Version Changes
Add a protocol version by adding its type-map entry and protocol_version to the YAML configuration, then rebuild both peers. The type-map build script generates a version-specific TypeMap. Native hosts built with the registry feature keep an enum union across configured versions; a browser client and its generated mtp/type-map declarations use only the map selected by that client's protocol_version, plus reserved names.
For a backward-compatible change, keep existing communication and data IDs stable and add new types with the new version. For a breaking change, add a new version and register both versions on the host while clients migrate. A client compiles one protocol version; it can connect only when that version is present in the host registry. Remove an old version only after its clients no longer connect, because the host closes connections whose version is unsupported.