DQuic Protocol
DQuic is an open-source QUIC implementation purpose-built for DHttp.
Originally designed and promoted by Google, the QUIC protocol was standardized by the IETF in 2021 as the next-generation transport protocol. QUIC represents the pinnacle of modern protocol design and network security, delivering robust security, 0-RTT fast handshakes, the elimination of head-of-line (HOL) blocking, and highly efficient bandwidth utilization.
DQuic
Standard QUIC still strictly differentiates between clients and servers, serving merely as an evolutionary enhancement for traditional client-server (C/S) or browser-server (B/S) architectures. This is clearly insufficient for DHttp. To meet its requirements, QUIC must be extended to support multipath transport and peer-to-peer (P2P) communication.
Peer-to-Peer Communication
Operating over UDP within user space, QUIC is inherently well-suited for peer-to-peer communication. Building upon the “Using QUIC to traverse NATs” draft, DQuic first implements a full-featured NAT traversal algorithm. Furthermore, rather than handling STUN, Relay, and signaling as detached layers, DQuic natively integrates these communication protocols directly into the core QUIC framework. As a result, a single DQuic connection functions as a self-contained, fully fleshed peer-to-peer communication system, completely eliminating any reliance on external, standalone STUN, Relay, or signaling services.
Communication Principles
In real-world networks, many endpoints reside within private networks and must traverse NAT devices to access the WAN. Consequently, external endpoints cannot inherently discover or initiate direct connections to them. Nonetheless, a direct channel can still be established using P2P communication technologies. The core challenge of peer-to-peer communication lies in NAT traversal, a highly complex process involving an orchestrated combination of protocols such as STUN, Relay, signaling, and ICE.
Consider an internal endpoint ep. Although it lacks a public IP address and cannot be connected to directly, it simply needs to be paired with a public endpoint EP. Once ep establishes a cooperative relationship (ep-&EP),
&EP effectively acts as the public delegate endpoint for ep. When other endpoints initiate connections to ep, they can forward signaling messages via &EP, ensuring an immediately viable routing path during the initial connection phase, as illustrated in the diagram on the left below.
In the diagrams, circular
EPnodes represent endpoints with public IP addresses, whereas rectangularepnodes denote internal endpoints located behind NATs. The left diagram illustrates anep-&EPpair. They form an inseparable dual-identity, where&EPserves as the public delegate forep; communicating with&EPis functionally equivalent to communicating directly withep. The right diagram demonstrates a scenario where an internal endpointep1attempts to connect to another internal endpointep2. Prior to successful NAT traversal, a direct peer-to-peer connection is impossible. However,ep1can route data packets toep2via the pathep1 --> &EP2 --> ep2. Conversely, response packets fromep2can be relayed back viaep2 --> &EP1 --> ep1, thereby establishing a bidirectional relay channel. This bootstrapping mechanism is absolutely critical for early-stage signaling exchange and the execution of the NAT traversal process itself.
Relaying
In general, traditional peer-to-peer communication architectures rely on dedicated service clusters for STUN, Relay, and signaling. While these infrastructure services merely assist with connection establishment and only route encrypted packets when NAT traversal fails—which minimizes architectural awareness of the transmitted payload compared to standard web platforms that proxy entire HTTP requests and responses—they nonetheless introduce protocol-level intervention. Furthermore, they enforce specialized endpoint roles, directly contradicting DHttp's core mission of eliminating architectural role asymmetries.
DQuic resolves this architectural bottleneck by empowering any generic endpoint to natively provide STUN, Relay, and signaling services. Within an ep-&EP pair, the EP can act as a STUN server to help the internal ep discover its public IP address and port mapping; it can also function as a signaling server, playing a critical bootstrapping role during early-stage connection setup. Should NAT traversal fail entirely, the EP can fall back to acting as a relay node.
Must an EP be deployed on a dedicated cloud host? In practice, absolutely not. If a subset of endpoints within the network possess public internet connectivity, they can seamlessly serve as public delegate endpoints for internal ep endpoints. Compared with cloud hosts, these user-operated EP endpoints may exhibit lower stability and cannot guarantee 24/7 uptime. However, this is a highly manageable challenge. By organizing and maintaining these ephemeral EP endpoints via a Distributed Hash Table (DHT), an internal ep can aggregate multiple concurrent EP paths to maximize availability—thereby enabling a fully decentralized, robustly peer-to-peer network infrastructure. As detailed in the next section, QUIC’s native multipath transport capability will further elevate connection resilience.
DQuic's approach is fundamentally equivalent to establishing a software-defined routing overlay at the application layer across all eligible endpoints in the network.
Endpoint Network Addresses:
The HTTP listening address of a traditional cloud-hosted server is typically bound to the host’s public IP address via static ports like 80 or 443. However, these fixed well-known ports are entirely unsuitable for internal endpoints, as they have no control over the dynamic external ports assigned by their respective NAT devices. Through the ep-&EP pair model, an internal endpoint ep successfully acquires a deterministic network listening address. This address is a combination of ep’s publicly mapped address (including port) and EP’s public address (including port). This format is used when extending DNS records so that DNS can universally serve all endpoints. Naturally, a standalone EP that already possesses a static public IP address requires no such encapsulation; its own public IP and port are fully self-sufficient. Actively assisting an internal ep with relaying and signaling does not compromise the EP’s own native communication capabilities.
Packet Architecture
DQuic integrates STUN and Relay protocols natively into the transport layer, just as native QUIC tightly couples with TLS 1.3. Specifically, DQuic extends the wire format of both QUIC Long Headers and Short Headers to encapsulate STUN packets and Relay message payloads, offering comprehensive dual-stack support for both IPv4 and IPv6 (detailed extensively in the DQuic documentation). Consequently, DQuic allows endpoints to automatically detect their public addresses and equips endpoints themselves with packet forwarding capabilities.
NAT Traversal
The Using QUIC to traverse NATs extension provides a highly elegant conceptual framework by transmitting NAT traversal-specific frames—such as ADD_ADDRESS, PUNCH_ME_NOW, and REMOVE_ADDRESS—directly within an active QUIC session. This shifts the paradigm: successful NAT traversal is no longer a prerequisite for session initialization. Instead, a QUIC connection is bootstrapped first (for instance, over an initial routing path provided by a signaling relay), and the NAT hole-punching sequence is subsequently executed under full end-to-end encryption over that existing connection. This tight coupling binds the NAT traversal lifecycle to the QUIC connection itself, ensuring that every session can safely and independently establish a peer-to-peer channel.
As Using QUIC to traverse NATs remains a preliminary Internet-Draft, it specifies several frame formats for information exchange; it acknowledges a dependency on STUN and signaling services but leaves the coordination model and concrete NAT traversal procedure undefined. Building on this baseline, DQuic implements a complete NAT traversal capability. Following the principle of endpoint equality, DQuic does not rely on specially deployed service endpoints. Instead, it supports the NAT traversal needed for network-wide connectivity through distributed routing by eligible endpoints across the network. In DQuic, the initial path required to establish a QUIC connection can be provided by
ep1 -- [&EP1, &EP2] -- ep2, where[&EP1, &EP2]is formed from eligible endpoints across the network.
Multipath Transport
QUIC is well suited to mobile networks. A single QUIC connection can use cellular networks, Ethernet, and IPv4 and IPv6 paths across different network interfaces simultaneously for multipath transport. This architectural flexibility is highly beneficial for safeguarding the connection resilience of peer-to-peer communications. Each individual IPv4 or IPv6 path can perform NAT traversal concurrently and independently. This parallel approach significantly increases the probability of establishing a direct peer-to-peer DQuic communication, a capability that will become even more pronounced with the ubiquitous adoption of IPv6.
As shown in the diagram, a single QUIC connection between endpoints
ep1andep2can have eight active paths, and each path performs NAT traversal independently.
DQuic schedules multipath priority as follows:
- Direct paths over relayed paths.
- Local (LAN) paths over wide-area (WAN) public paths.
- Wi-Fi networks over cellular networks.
- IPv6 paths take precedence over IPv4 paths.
The Ubiquitous IPv6 Horizon:
As network communication infrastructure continues to evolve, IPv6 will become increasingly widespread. By then, most endpoints will directly have a publicly routable IPv6 address, further reducing the already low overhead of NAT traversal to near zero. We look forward to broader IPv6 adoption and smoother information delivery.
Multipath Handshake
The core QUIC specification strictly restricts the initial handshake to a single path. For endpoints operating an IPv4/IPv6 dual stack, this constraint severely throttles the transport layer's ability to fully exploit multipath efficiencies during connection establishment.
Consider the Happy Eyeballs algorithm (RFC 8305) as a baseline dual-stack fallback mechanism. When IPv6 is preferentially targeted but path viability remains unverified, a QUIC connection is first initiated over the IPv6 stack. If no cryptographic response returns within a 100 ms connection-attempt delay, a secondary, independent QUIC connection is initiated over IPv4. These two parallel connections then enter a connection race, where the faster handshake takes ownership of the application session. In reality, the application layer ultimately demands only a single, coherent connection; spawning two separate state machines to compete for a single logical session introduces redundant protocol overhead and clear resource inefficiency. Furthermore, should the IPv6 path achieve a delayed handshake success, it should not be eagerly abandoned, as application-level heuristics natively favor IPv6 routing. This structural flaw highlights the clear architectural limitations of Happy Eyeballs in modern multipath transport.
DQuic bypasses this limitation by supporting simultaneous multipath handshakes. By binding concurrent initialization paths to the same Connection ID (CID), DQuic seamlessly aggregates both IPv4 and IPv6 paths into concurrent, valid paths within the exact same QUIC connection. Coordinated by the multipath scheduling strategy, this approach extracts maximum efficiency from dual-stack operations. It preserves connection establishment speed and reduces endpoint resource utilization, while inherently promoting the global transition toward ubiquitous IPv6 adoption.
DQuic Usage Guide
DQuic is fully open source under the Apache-2.0 license. The source code repository is available on GitHub.
Running Examples
DQuic is built in Rust. The following quick-start steps assume that you have already installed the standard Rust toolchain and Git on your local environment.
Clone the Project
Execute the Git command below to clone the DQuic source repository and navigate into the project directory:
git clone https://github.com/genmeta/dquic
cd dquic
Running as a Server
To spin up an HTTP/3 (H3) server that exposes the static file directory within h3-shim, execute the following cargo command (which automatically handles compilation and execution):
cargo run --example h3-server --package h3-shim -- --dir ./h3-shim
Running as a Client
Once the server instance is up and running, you can open another terminal session and initiate an HTTP/3 request with the following command to request a file under h3-shim:
cargo run --example h3-client --package h3-shim -- https://localhost:4433/examples/h3-server.rs
API Reference
DQuic exposes a developer-friendly API prioritizing syntactic minimalism and highly expressive, intuitive semantics. The most straightforward path to establishing a connection with a remote peer is simply to know its human-readable peer name and initiate a direct connection.
Client-Side Initialization
The following code snippet demonstrates how to initialize a client instance without a client certificate and initiate an anonymous request directly to localhost. If no target port is explicitly configured, the default destination port is 443.
use std::path::PathBuf;
use gm_quic::prelude::{handy::ToCertificate, *};
async fn client() -> Result<(), Box<dyn std::error::Error>> {
let mut roots = rustls::RootCertStore::empty();
roots.add_parsable_certificates(rustls_native_certs::load_native_certs().certs);
roots.add_parsable_certificates(PathBuf::from("path/to/your/cert.pem").to_certificate());
let quic_client = QuicClient::builder()
.with_root_certificates(roots)
.without_cert()
.build();
let connection = quic_client.connect("localhost").await?;
// Start using the QUIC connection.
// For more examples, see dquic/examples and h3-shim/examples.
Ok(())
}
Server-Side Initialization
The following code snippet demonstrates how to instantiate a local server instance bound to localhost, configuring it to listen concurrently on both IPv4 and IPv6 loopback addresses to accept incoming QUIC connections.
use std::path::PathBuf;
use gm_quic::prelude::*;
async fn server() -> Result<(), Box<dyn std::error::Error>> {
let listener = QuicListeners::builder()?
.without_client_cert_verifier()
.listen(8192);
listener.add_server(
"localhost",
PathBuf::from("/path/to/server.crt").as_path(),
PathBuf::from("/path/to/server.key").as_path(),
["127.0.0.1:443", "[::1]:443"],
None,
);
while let Ok((connection, server_name, pathway, link)) = listener.accept().await {
// Handle incoming QUIC connections.
// See the examples in dquic/examples and h3-shim/examples.
}
Ok(())
}
For more usage patterns, see the documentation.