echoserver 0.1.0

Echo Server
use std::net::SocketAddr;
use std::time::Duration;

use anyhow::Result;
use axum::extract::Request;
use axum::{
    response::{IntoResponse, Response},
    routing::{get, post},
    Router,
};
use tokio::net::TcpListener;
use tower_http::cors::CorsLayer;
use tower_http::timeout::TimeoutLayer;
use tower_http::trace::{DefaultMakeSpan, DefaultOnRequest, DefaultOnResponse, TraceLayer};
use tracing::{info, Level};

async fn ping(_req: Request) -> Response {
    "pong".into_response()
}

async fn echo(req: Request) -> Response {
    let (req_parts, body) = req.into_parts();

    let (mut resp_parts, body) = Response::new(body).into_parts();
    resp_parts.headers = req_parts.headers;

    Response::from_parts(resp_parts, body)
}

pub async fn serve(addr: SocketAddr) -> Result<()> {
    info!("Serving HTTP on {}", addr);

    let app = Router::new()
        .route("/ping", get(ping))
        .route("/echo", post(echo))
        .layer(CorsLayer::permissive())
        .layer(TimeoutLayer::new(Duration::from_secs(10)))
        .layer(
            TraceLayer::new_for_http()
                .make_span_with(
                    DefaultMakeSpan::new()
                        .level(Level::INFO)
                        .include_headers(true),
                )
                .on_request(DefaultOnRequest::new().level(Level::INFO))
                .on_response(DefaultOnResponse::new().level(Level::INFO)),
        );

    let listener = TcpListener::bind(addr)
        .await
        .expect(&format!("bind HTTP server on {addr}"));
    axum::serve(listener, app)
        .await
        .expect(&format!("serve HTTP server on {addr}"));

    Ok(())
}