use std::{ hash::{DefaultHasher, Hash, Hasher}, net::SocketAddr, sync::Arc, }; use axum::{ Json, Router, body::Bytes, extract::{Form, Query, State}, http::StatusCode, response::{IntoResponse, Response}, routing::{get, post}, }; use serde::{Deserialize, Serialize}; use tokio::{ net::TcpListener, sync::{Mutex, mpsc::Sender}, }; use crate::AppState; pub struct HttpServer { pub offer_tx: Sender<(i32, String, String)>, pub accept_rx: async_broadcast::Receiver<(i32, String)>, pub appstate: Arc>, pub request_count: Mutex, } impl HttpServer { pub fn start(self) -> Result<(), Box> { let state = Arc::new(self); tokio::spawn(async move { let app = Router::new() .route("/api/catalog", get(catalog_handler)) .route("/api/user", post(create_user_handler)) .route("/api/stream", get(stream_handler)) .route("/api/meow", get(meow_handler)) .with_state(state); let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); let listener = TcpListener::bind(addr).await.unwrap(); axum::serve(listener, app).await.unwrap(); }); Ok(()) } } fn calculate_hash(t: &T) -> u64 { let mut s = DefaultHasher::new(); t.hash(&mut s); s.finish() } async fn catalog_handler(State(state): State>) -> impl IntoResponse { let catalog = catalog_from_state(&state.appstate).await; Json(catalog) } #[derive(Deserialize)] struct CreateUserForm { username: String, password: String, ref_token: String, } async fn create_user_handler(Form(form): Form) -> 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>, Query(query): Query, 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; } } Response::builder() .status(StatusCode::CREATED) .header("content-type", "application/sdp") .body(reply_body) .unwrap() } async fn meow_handler() -> &'static str { "meow" } #[derive(Serialize)] struct StreamCatalog { active_streams: Vec, } async fn catalog_from_state(state: &Arc>) -> StreamCatalog { let app = state.lock().await; let active_streams = app .stream_sessions .iter() .map(|e| e.key().clone()) .collect(); StreamCatalog { active_streams } }