HTTP Upgrade (WebSocket)
aioduct supports HTTP/1.1 protocol upgrades (101 Switching Protocols) and HTTP/2 extended CONNECT (RFC 8441), commonly used for WebSocket connections. After a successful upgrade handshake, you get a bidirectional IO stream.
Basic Usage (HTTP/1.1)
#![allow(unused)]
fn main() {
use aioduct::TokioClient;
async fn example() -> Result<(), aioduct::Error> {
let client = TokioClient::new();
let resp = client
.get("http://example.com/ws")?
.upgrade() // sets Connection: Upgrade + Upgrade: websocket + HTTP/1.1
.send()
.await?;
assert_eq!(resp.status(), http::StatusCode::SWITCHING_PROTOCOLS);
let upgraded = resp.upgrade().await?;
// `upgraded` implements hyper's Read + Write traits
// With the `tokio` feature, it also implements tokio::io::AsyncRead + AsyncWrite
Ok(())
}
}
HTTP/2 Extended CONNECT (RFC 8441)
For HTTP/2 upstreams that support the extended CONNECT protocol, use Protocol to signal the desired sub-protocol:
#![allow(unused)]
fn main() {
use aioduct::{TokioClient, Protocol};
async fn example() -> Result<(), aioduct::Error> {
let client = TokioClient::builder()
.build()?;
let mut req = client
.get("http://example.com/ws/chat")?
.h2c_prior_knowledge()
.build();
*req.method_mut() = http::Method::CONNECT;
req.extensions_mut().insert(Protocol::from_static("websocket"));
let resp = client.execute(req).await?;
assert_eq!(resp.status(), http::StatusCode::OK);
let upgraded = resp.upgrade().await?;
// Bidirectional tunnel over the H2 stream
Ok(())
}
}
For proxy/gateway use cases, see Request Forwarding which auto-detects both upgrade mechanisms.
How It Works
- HTTP/1.1: Call
.upgrade()onRequestBuilderSendorRequestBuilderLocalto set the required headers (Connection: Upgrade,Upgrade: websocket) and force HTTP/1.1. - HTTP/2: Insert a
Protocolextension into the request and useCONNECTmethod. The server must haveSETTINGS_ENABLE_CONNECT_PROTOCOLenabled. - Send the request and check for
101(H1) or200(H2 CONNECT). - Call
.upgrade()on theResponseto consume it and obtain anUpgradedSendstream. - The connection is not returned to the pool — it’s exclusively yours.
The UpgradedSend Type
UpgradedSend is a bidirectional IO stream:
- Implements
hyper::rt::Readandhyper::rt::Write(always available) - Implements
tokio::io::AsyncReadandtokio::io::AsyncWrite(when thetokiofeature is enabled) - Can be converted to the underlying
hyper::upgrade::Upgradedvia.into_inner() - Can be constructed from
hyper::upgrade::UpgradedviaUpgradedSend::from()
The local runtime path returns UpgradedLocal.
Using with WebSocket Libraries
Pass the UpgradedSend stream to your WebSocket library of choice. For example, with tokio-tungstenite:
let upgraded = resp.upgrade().await?;
let ws_stream = tokio_tungstenite::WebSocketStream::from_raw_socket(
upgraded,
tokio_tungstenite::tungstenite::protocol::Role::Client,
None,
).await;
Notes
- HTTP/1.1 upgrades use
Connection: Upgrade+Upgrade: websocketheaders → 101 - HTTP/2 extended CONNECT uses
CONNECTmethod +:protocolpseudo-header → 200 - After upgrade, the connection/stream is consumed — it won’t be returned to the pool
- You can set additional WebSocket-specific headers (like
Sec-WebSocket-Key) manually via.header_str()