http.rs/ hyper -> axum

This commit is contained in:
2026-06-19 17:07:34 +01:00
parent cbfa07f8e8
commit de96bc030d
+97 -50
View File
@@ -1,73 +1,120 @@
use std::{convert::Infallible, error::Error, net::SocketAddr, sync::Arc}; use std::{
hash::{DefaultHasher, Hash, Hasher},
net::SocketAddr,
sync::Arc,
};
use bytes::Bytes; use axum::{
use http_body_util::Full; Json, Router,
use hyper::{Request, Response, server::conn::http2, service::service_fn}; body::Bytes,
use hyper_util::rt::{TokioExecutor, TokioIo}; extract::{Form, Query, State},
use serde::Serialize; http::StatusCode,
response::{IntoResponse, Response},
routing::{get, post},
};
use serde::{Deserialize, Serialize};
use tokio::{ use tokio::{
net::TcpListener, net::TcpListener,
sync::{ sync::{Mutex, mpsc::Sender},
Mutex,
mpsc::{Receiver, Sender},
},
}; };
use crate::AppState; use crate::AppState;
pub struct HttpServer { pub struct HttpServer {
pub offer_tx: Sender<(String, String)>, pub offer_tx: Sender<(i32, String, String)>,
pub accept_rx: Receiver<(String, String)>, pub accept_rx: async_broadcast::Receiver<(i32, String)>,
pub appstate: Arc<Mutex<AppState>>, pub appstate: Arc<Mutex<AppState>>,
pub request_count: Mutex<i32>,
} }
impl HttpServer { impl HttpServer {
pub fn start(&mut self) -> Result<(), Box<dyn Error>> { pub fn start(self) -> Result<(), Box<dyn std::error::Error>> {
let state = self.appstate.clone(); let state = Arc::new(self);
tokio::spawn(async move { tokio::spawn(async move {
let socket = SocketAddr::from(([127, 0, 0, 1], 3000)); let app = Router::new()
let listener = TcpListener::bind(socket).await.unwrap(); .route("/api/catalog", get(catalog_handler))
loop { .route("/api/user", post(create_user_handler))
let (stream, _) = listener.accept().await.unwrap(); .route("/api/stream", get(stream_handler))
let tokio_io = TokioIo::new(stream); .route("/api/meow", get(meow_handler))
let state = state.clone(); .with_state(state);
tokio::spawn(async move {
let service = service_fn(move |req| { let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let state = state.clone(); let listener = TcpListener::bind(addr).await.unwrap();
async move { handle_request(req, &state).await } axum::serve(listener, app).await.unwrap();
});
let hyper = http2::Builder::new(TokioExecutor::new());
hyper.serve_connection(tokio_io, service).await.unwrap();
});
}
}); });
Ok(()) Ok(())
} }
} }
async fn handle_request( fn calculate_hash<T: Hash>(t: &T) -> u64 {
request: Request<impl hyper::body::Body>, let mut s = DefaultHasher::new();
appstate: &Arc<Mutex<AppState>>, t.hash(&mut s);
) -> Result<Response<Full<Bytes>>, Infallible> { s.finish()
match request.uri().path() { }
"/api/catalog" => {
let catalog = catalog_from_state(appstate).await; async fn catalog_handler(State(state): State<Arc<HttpServer>>) -> impl IntoResponse {
let body = serde_json::to_vec(&catalog).unwrap(); let catalog = catalog_from_state(&state.appstate).await;
Ok(Response::builder() Json(catalog)
.status(200) }
.header("content-type", "application/json")
.body(Full::new(Bytes::from(body))) #[derive(Deserialize)]
.unwrap()) struct CreateUserForm {
username: String,
password: String,
ref_token: String,
}
async fn create_user_handler(Form(form): Form<CreateUserForm>) -> StatusCode {
// TODO: ref_token validation, db integration
let _ = (
form.username,
calculate_hash(&form.password),
form.ref_token,
);
todo!("user creation not yet implemented")
}
#[derive(Deserialize)]
struct StreamQuery {
stream_label: String,
}
async fn stream_handler(
State(state): State<Arc<HttpServer>>,
Query(query): Query<StreamQuery>,
body: Bytes,
) -> impl IntoResponse {
let body_str = String::from_utf8(body.to_vec()).unwrap_or_default();
let mut request_id = state.request_count.lock().await;
*request_id += 1;
let request_id_clone = *request_id;
drop(request_id);
let mut accept_rx = state.accept_rx.new_receiver();
let _ = state
.offer_tx
.send((request_id_clone, query.stream_label, body_str))
.await;
let mut reply_body = String::new();
while let Ok(answer) = accept_rx.recv().await {
if answer.0 == request_id_clone {
reply_body = answer.1;
break;
} }
"/api/meow" => Ok(Response::builder()
.status(200)
.body(Full::new(Bytes::from("meow")))
.unwrap()),
_ => Ok(Response::builder()
.status(404)
.body(Full::new(Bytes::from("")))
.unwrap()),
} }
Response::builder()
.status(StatusCode::CREATED)
.header("content-type", "application/sdp")
.body(reply_body)
.unwrap()
}
async fn meow_handler() -> &'static str {
"meow"
} }
#[derive(Serialize)] #[derive(Serialize)]