use std::net::SocketAddr;
use std::sync::Arc;
use axum::Router;
use axum::body::{Body, Bytes};
use axum::extract::State;
use axum::http::{HeaderName, HeaderValue, Method, Response, StatusCode, Uri};
use tokio::net::TcpListener;
use crate::app::{LinoApp, RawRequest};
pub fn router(app: Arc<LinoApp>) -> Router {
Router::new().fallback(dispatch).with_state(app)
}
async fn dispatch(
State(app): State<Arc<LinoApp>>,
method: Method,
uri: Uri,
headers: axum::http::HeaderMap,
body: Bytes,
) -> Response<Body> {
let mut raw = RawRequest::new(method.as_str(), uri.path());
raw.query_string = uri.query().unwrap_or_default().to_string();
for (name, value) in &headers {
raw.headers
.insert(name.as_str(), value.to_str().unwrap_or_default());
}
raw.body = body.to_vec();
let parts = app.respond(raw).await;
let status = StatusCode::from_u16(parts.status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
let mut response = Response::builder().status(status);
for (name, value) in parts.headers.iter() {
if let (Ok(name), Ok(value)) = (
HeaderName::from_bytes(name.as_bytes()),
HeaderValue::from_str(value),
) {
response = response.header(name, value);
}
}
response
.body(Body::from(parts.body))
.unwrap_or_else(|_| Response::new(Body::empty()))
}
pub struct BoundServer {
pub address: SocketAddr,
listener: TcpListener,
router: Router,
}
impl BoundServer {
pub async fn bind(app: LinoApp, address: &str) -> std::io::Result<Self> {
let listener = TcpListener::bind(address).await?;
Ok(Self {
address: listener.local_addr()?,
listener,
router: router(Arc::new(app)),
})
}
pub fn base_url(&self) -> String {
format!("http://{}", self.address)
}
pub async fn serve(self) -> std::io::Result<()> {
axum::serve(self.listener, self.router).await
}
}
pub async fn serve(app: LinoApp, address: &str) -> std::io::Result<()> {
BoundServer::bind(app, address).await?.serve().await
}