async-http-codec 0.8.0

async HTTP 1.1 encoding and decoding
Documentation
use async_http_codec::{BodyDecodeWithContinue, BodyEncode, RequestHead, ResponseHead};
use async_web_server::TcpIncoming;
use futures::prelude::*;
use http::header::{CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING};
use http::{Method, Request, Response, StatusCode};
use log::LevelFilter;
use simple_logger::SimpleLogger;
use smol::future::block_on;
use smol::spawn;
use std::convert::TryInto;
use std::net::Ipv6Addr;

const HTML: &[u8] = include_bytes!("echo-client.html");

fn main() -> anyhow::Result<()> {
    SimpleLogger::new()
        .with_level(LevelFilter::Info)
        .init()
        .unwrap();

    let mut incoming = TcpIncoming::bind((Ipv6Addr::UNSPECIFIED, 8080))?;

    block_on(async {
        while let Some(transport) = incoming.next().await {
            spawn(async {
                if let Err(err) = handle(transport).await {
                    log::error!("error handling request: {:?}", err);
                }
            })
            .detach();
        }
        unreachable!()
    })
}

async fn handle(mut transport: impl AsyncRead + AsyncWrite + Unpin) -> anyhow::Result<()> {
    let (_, request_head) = RequestHead::decode(&mut transport).await.unwrap();

    let mut request_body = String::new();
    BodyDecodeWithContinue::from_head(&request_head, &mut transport)?
        .read_to_string(&mut request_body)
        .await?;

    let request = Request::from_parts(request_head.into(), request_body);
    log::info!("received request: {:?}", &request);

    let mut response = Response::<&[u8]>::new(&[]);
    response
        .headers_mut()
        .insert(CONNECTION, "close".try_into()?);
    match (request.method(), request.uri().path()) {
        (&Method::GET, "/") => {
            response
                .headers_mut()
                .insert(CONTENT_LENGTH, HTML.len().into());
            *response.body_mut() = HTML;
        }
        (&Method::POST, _) => {
            response
                .headers_mut()
                .insert(TRANSFER_ENCODING, "chunked".try_into()?);
            *response.body_mut() = request.body().as_bytes();
        }
        (&Method::GET, _) => *response.status_mut() = StatusCode::NOT_FOUND,
        (_, _) => *response.status_mut() = StatusCode::METHOD_NOT_ALLOWED,
    }

    ResponseHead::ref_response(&response)
        .encode(&mut transport)
        .await?;
    let mut body_encode = BodyEncode::from_headers(&response.headers(), transport)?;
    body_encode.write_all(response.body()).await?;
    body_encode.close().await?;
    log::info!(
        "sent response with status \"{}\" on \"{}\"",
        response.status(),
        request.uri().path()
    );

    Ok(())
}