msquic-async
MsQuic based quic library that supports async operation.

Getting Started
Note that MsQuic, which is used to implement QUIC, needs to be built and linked. This is done automatically when building msquic-async, but requires the cmake command to be available during the build process.
Windows, Linux, MacOS
Add msquic-async in dependencies of your Cargo.toml.
msquic-async = { version = "0.4.0" }
If you use SEERA MsQuic for the backend, please specify msquic-seera feature with
default-features = false.
msquic-async = { version = "0.4.0", default-features = false, features = ["tokio", "msquic-seera"] }
The examples directory can help get started.
Server
let registration = msquic::Registration::new(&msquic::RegistrationConfig::default())?;
let alpn = [msquic::BufferRef::from("sample")];
let configuration = msquic::Configuration::open(
®istration,
&alpn,
Some(
&msquic::Settings::new()
.set_IdleTimeoutMs(10000)
.set_PeerBidiStreamCount(100)
.set_PeerUnidiStreamCount(100)
.set_DatagramReceiveEnabled()
.set_StreamMultiReceiveEnabled(),
),
)?;
#[cfg(not(windows))]
{
use std::io::Write;
use tempfile::NamedTempFile;
let cert = include_bytes!("cert.pem");
let key = include_bytes!("key.pem");
let mut cert_file = NamedTempFile::new()?;
cert_file.write_all(cert)?;
let cert_path = cert_file.into_temp_path();
let cert_path = cert_path.to_string_lossy().into_owned();
let mut key_file = NamedTempFile::new()?;
key_file.write_all(key)?;
let key_path = key_file.into_temp_path();
let key_path = key_path.to_string_lossy().into_owned();
let cred_config = msquic::CredentialConfig::new().set_credential(
msquic::Credential::CertificateFile(msquic::CertificateFile::new(key_path, cert_path)),
);
configuration.load_credential(&cred_config)?;
}
#[cfg(windows)]
{
use schannel::cert_context::{CertContext, KeySpec};
use schannel::cert_store::{CertAdd, Memory};
use schannel::crypt_prov::{AcquireOptions, ProviderType};
use schannel::RawPointer;
let cert = include_str!("../examples/cert.pem");
let key = include_bytes!("../examples/key.pem");
let mut store = Memory::new().unwrap().into_store();
let name = String::from("msquic-async-example");
let cert_ctx = CertContext::from_pem(cert).unwrap();
let mut options = AcquireOptions::new();
options.container(&name);
let type_ = ProviderType::rsa_full();
let mut container = match options.acquire(type_) {
Ok(container) => container,
Err(_) => options.new_keyset(true).acquire(type_).unwrap(),
};
container.import().import_pkcs8_pem(key).unwrap();
cert_ctx
.set_key_prov_info()
.container(&name)
.type_(type_)
.keep_open(true)
.key_spec(KeySpec::key_exchange())
.set()
.unwrap();
let context = store.add_cert(&cert_ctx, CertAdd::Always).unwrap();
let cred_config = msquic::CredentialConfig::new().set_credential(
msquic::Credential::CertificateContext(unsafe { context.as_ptr() }),
);
configuration.load_credential(&cred_config)?;
};
let listener = msquic_async::Listener::new(®istration, configuration)?;
let addr: SocketAddr = "127.0.0.1:4567".parse()?;
listener.start(&alpn, Some(addr))?;
let server_addr = listener.local_addr()?;
info!("listening on {}", server_addr);
while let Ok(conn) = listener.accept().await {
info!("new connection established");
tokio::spawn(async move {
loop {
match conn.accept_inbound_stream().await {
Ok(mut stream) => {
info!("new stream id: {}", stream.id().expect("stream id"));
let mut buf = [0u8; 1024];
let len = stream.read(&mut buf).await?;
info!(
"reading from stream: {}",
String::from_utf8_lossy(&buf[0..len])
);
stream.write_all(&buf[0..len]).await?;
poll_fn(|cx| stream.poll_finish_write(cx)).await?;
mem::drop(stream);
}
Err(err) => {
error!("error on accept stream: {}", err);
break;
}
}
}
Ok::<_, anyhow::Error>(())
});
}
You can find a full server example in msquic-async/examples/server.rs
Client
let registration = msquic::Registration::new(&msquic::RegistrationConfig::default())?;
let alpn = [msquic::BufferRef::from("sample")];
let configuration = msquic::Configuration::open(
®istration,
&alpn,
Some(
&msquic::Settings::new()
.set_IdleTimeoutMs(10000)
.set_PeerBidiStreamCount(100)
.set_PeerUnidiStreamCount(100)
.set_DatagramReceiveEnabled()
.set_StreamMultiReceiveEnabled(),
),
)?;
let cred_config = msquic::CredentialConfig::new_client()
.set_credential_flags(msquic::CredentialFlags::NO_CERTIFICATE_VALIDATION);
configuration.load_credential(&cred_config)?;
let conn = msquic_async::Connection::new(®istration)?;
conn.start(&configuration, "127.0.0.1", 4567).await?;
let mut stream = conn
.open_outbound_stream(msquic_async::StreamType::Bidirectional, false)
.await?;
stream.write_all("hello".as_bytes()).await?;
poll_fn(|cx| stream.poll_finish_write(cx)).await?;
let mut buf = [0u8; 1024];
let len = stream.read(&mut buf).await?;
info!("received: {}", String::from_utf8_lossy(&buf[0..len]));
poll_fn(|cx| conn.poll_shutdown(cx, 0)).await?;
You can find a full client example in msquic-async/examples/client.rs
License
msquic-async is provided under the MIT license. See LICENSE.