Open
Description
PROS
- Low level, user code has the full flow-control,
spawn
free in hyper level - How-to/When-to use
spawn
could be full decided by the user level code- Connection Level
- Stream Level
- Stream with upgrading
- Request Level
- Easy to implement a full-controllable graceful shutdown
- Listener Level
- Connection level
- Stream Level
- Easy to implement priority control for user code
- Easy to implement memory/resource/number-of-tasks control for user code
- Possible to let user level code to known response has been sent.
CONS
- Maybe low level too much
Example
async fn main() -> Result<()> {
let mut listener = TcpListener::bind(...).await?;
while let Ok((conn, _)) = listener.accept().await {
// wrap low-level connection into http connection
let conn = HttpProtocol::new(conn).await?;
// stream could be h1/h2 or h3 someday, for h1: each conn yield only one stream
while let Some(http_stream) = conn.next().await {
handle_stream(http_stream?).await.ok();
}
}
Ok(())
}
async fn handle_stream(stream: HttpStream) -> Result<()> {
let (mut input, mut output) = http_stream.split();
// pull a new request
// we know that request is one-by-one inside a stream
while let Some(item) = input.next().await {
// key could be used to ensure request/response is matched (Eg. no re-order)
// key is cheap to Clone, maybe Copy
let (key, req) = item?;
// handle upgrade if upgradable
if key.is_upgradable() {
let stream = output.upgrade(input, key, ...).await?; // consume input & output, into stream
return handle_upgraded_stream(stream).await;
}
// handle request
let res = handle_request(req).await;
// now, we have the opportunity to know that reply has been sent
let now = Instant::now();
output.send((key, res)).await?;
eprintln!("reply time: {:?}", now.elapsed());
}
Ok(())
}