|
|
|
|
@@ -2,6 +2,7 @@ use std::convert::Infallible;
|
|
|
|
|
use std::fs::read;
|
|
|
|
|
use std::io::ErrorKind::NotFound;
|
|
|
|
|
use std::net::SocketAddr;
|
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
use hyper::{Body, body, Method, Request, Response, Server, StatusCode};
|
|
|
|
|
use hyper::server::conn::AddrStream;
|
|
|
|
|
@@ -16,17 +17,17 @@ use pong::event::event::{Event, EventReader, EventWriter};
|
|
|
|
|
use futures::{sink::SinkExt, stream::StreamExt};
|
|
|
|
|
use crate::kafka::{KafkaEventReaderImpl, KafkaSessionEventWriterImpl};
|
|
|
|
|
use crate::player::Player;
|
|
|
|
|
use crate::session::{CachingSessionManager, SessionManager};
|
|
|
|
|
use crate::session::{SessionManager};
|
|
|
|
|
use crate::utils::http_utils::{get_query_params, read_json_body};
|
|
|
|
|
|
|
|
|
|
pub struct HttpServer {
|
|
|
|
|
addr: [u8; 4],
|
|
|
|
|
port: u16,
|
|
|
|
|
session_manager: Arc<Mutex<CachingSessionManager>>
|
|
|
|
|
session_manager: Arc<Mutex<SessionManager>>
|
|
|
|
|
}
|
|
|
|
|
impl HttpServer {
|
|
|
|
|
pub fn new(addr: [u8; 4], port: u16, kafka_host: &str) -> HttpServer {
|
|
|
|
|
let session_manager = Arc::new(Mutex::new(CachingSessionManager::new(kafka_host)));
|
|
|
|
|
let session_manager = Arc::new(Mutex::new(SessionManager::new(kafka_host)));
|
|
|
|
|
HttpServer {addr, port, session_manager}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -39,12 +40,40 @@ impl HttpServer {
|
|
|
|
|
let mut session_manager = Arc::clone(&session_manager);
|
|
|
|
|
async move {
|
|
|
|
|
if hyper_tungstenite::is_upgrade_request(&req) {
|
|
|
|
|
println!("Received request to upgrade to websocket connection: {:?}", req);
|
|
|
|
|
let params = get_query_params(&req);
|
|
|
|
|
println!("Ws request params: {:?}", params);
|
|
|
|
|
if !params.contains_key("session_id") {
|
|
|
|
|
eprintln!("Missing session id request param for websocket connection, don't upgrade connection to ws.");
|
|
|
|
|
return build_error_res("Missing request param: session_id", StatusCode::BAD_REQUEST);
|
|
|
|
|
}
|
|
|
|
|
if !params.contains_key("connection_type") {
|
|
|
|
|
eprintln!("Missing connection type request param for websocket connection, don't upgrade connection to ws.");
|
|
|
|
|
let res = build_error_res("Missing request param: connection_type", StatusCode::BAD_REQUEST);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
let session_id = params.get("session_id").unwrap();
|
|
|
|
|
let connection_type_raw = params.get("connection_type").unwrap();
|
|
|
|
|
let connection_type = WebSocketConnectionType::from_str(connection_type_raw);
|
|
|
|
|
if let Err(_) = connection_type {
|
|
|
|
|
let error = format!("Invalid connection type: {}", connection_type_raw);
|
|
|
|
|
eprintln!("{}", error);
|
|
|
|
|
return build_error_res(error.as_str(), StatusCode::BAD_REQUEST);
|
|
|
|
|
}
|
|
|
|
|
if let None = session_manager.lock().await.get_session(session_id) {
|
|
|
|
|
let error = format!("Session does not exist: {}", session_id);
|
|
|
|
|
eprintln!("{}", error);
|
|
|
|
|
return build_error_res(error.as_str(), StatusCode::NOT_FOUND);
|
|
|
|
|
}
|
|
|
|
|
let websocket_session = WebSocketSession {session_id: session_id.to_string(), connection_type: connection_type.unwrap()};
|
|
|
|
|
println!("Websocket upgrade request is valid, will now upgrade to websocket: {:?}", req);
|
|
|
|
|
|
|
|
|
|
let (response, websocket) = hyper_tungstenite::upgrade(req, None).unwrap();
|
|
|
|
|
|
|
|
|
|
// Spawn a task to handle the websocket connection.
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
if let Err(e) = serve_websocket(websocket, session_manager).await {
|
|
|
|
|
eprintln!("Error in websocket connection: {}", e);
|
|
|
|
|
if let Err(e) = serve_websocket(websocket_session, websocket, session_manager).await {
|
|
|
|
|
eprintln!("Error in websocket connection: {:?}", e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@@ -68,10 +97,21 @@ impl HttpServer {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Handle a websocket connection.
|
|
|
|
|
async fn serve_websocket(websocket: HyperWebsocket, session_manager: Arc<Mutex<CachingSessionManager>>) -> Result<(), Error> {
|
|
|
|
|
async fn serve_websocket(websocket_session: WebSocketSession, websocket: HyperWebsocket, session_manager: Arc<Mutex<SessionManager>>) -> Result<(), Error> {
|
|
|
|
|
let mut websocket = websocket.await?;
|
|
|
|
|
let (mut write, mut read) = websocket.split();
|
|
|
|
|
|
|
|
|
|
let session_manager_reader = Arc::clone(&session_manager);
|
|
|
|
|
let websocket_session_read_copy = websocket_session.clone();
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
while let Some(message) = websocket.next().await {
|
|
|
|
|
let mut locked = session_manager_reader.lock().await;
|
|
|
|
|
let writer = locked.get_session_writer(&websocket_session_read_copy.session_id);
|
|
|
|
|
if let Err(e) = writer {
|
|
|
|
|
eprintln!("Failed to retrieve session writer for session {}: {}", websocket_session_read_copy.session_id, e);
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
let mut writer = writer.unwrap();
|
|
|
|
|
while let Some(message) = read.next().await {
|
|
|
|
|
match message.unwrap() {
|
|
|
|
|
Message::Text(msg) => {
|
|
|
|
|
let events = serde_json::from_str::<SessionEventListDTO>(&msg);
|
|
|
|
|
@@ -80,15 +120,12 @@ async fn serve_websocket(websocket: HyperWebsocket, session_manager: Arc<Mutex<C
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
let event_wrapper = events.unwrap();
|
|
|
|
|
let mut locked = session_manager.lock().await;
|
|
|
|
|
let writer = locked.get_session_writer(&event_wrapper.session_id);
|
|
|
|
|
if let Err(e) = writer {
|
|
|
|
|
eprintln!("Failed to retrieve session writer for session {}: {}", event_wrapper.session_id, e);
|
|
|
|
|
if event_wrapper.session_id != websocket_session_read_copy.session_id {
|
|
|
|
|
eprintln!("Websocket has session {:?} but was asked to write to session {} - skip.", websocket_session_read_copy, event_wrapper.session_id);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
let writer = writer.unwrap();
|
|
|
|
|
let mut writer = writer.lock().await;
|
|
|
|
|
let mut any_error = false;
|
|
|
|
|
let event_count = event_wrapper.events.len();
|
|
|
|
|
for event in event_wrapper.events {
|
|
|
|
|
let write_res = writer.write_to_session(&event.topic, &event.msg);
|
|
|
|
|
if let Err(e) = write_res {
|
|
|
|
|
@@ -98,6 +135,8 @@ async fn serve_websocket(websocket: HyperWebsocket, session_manager: Arc<Mutex<C
|
|
|
|
|
}
|
|
|
|
|
if any_error {
|
|
|
|
|
eprintln!("Failed to write at least one message for session {}", event_wrapper.session_id);
|
|
|
|
|
} else {
|
|
|
|
|
println!("Successfully wrote {} messages to kafka for session {:?}", event_count, websocket_session_read_copy)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Message::Close(msg) => {
|
|
|
|
|
@@ -112,16 +151,28 @@ async fn serve_websocket(websocket: HyperWebsocket, session_manager: Arc<Mutex<C
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
let session_manager_writer = Arc::clone(&session_manager);
|
|
|
|
|
let websocket_session_write_copy = websocket_session.clone();
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
// TODO: Get session writer and query consumer
|
|
|
|
|
// TODO: Differentiate player1 (receives only input of player2 and session updates) vs player2 (receives all messages)
|
|
|
|
|
let consumer = {
|
|
|
|
|
let consumer_async = async {
|
|
|
|
|
let mut session_manager_writer_locked = session_manager_writer.lock().await;
|
|
|
|
|
return session_manager_writer_locked.get_session_reader(&websocket_session_write_copy.session_id, websocket_session_write_copy.connection_type.get_topics());
|
|
|
|
|
};
|
|
|
|
|
consumer_async.await
|
|
|
|
|
};
|
|
|
|
|
let mut consumer = consumer.unwrap();
|
|
|
|
|
loop {
|
|
|
|
|
let messages = consumer.read_from_session();
|
|
|
|
|
println!("Read messages for websocket_session {:?} from consumer: {:?}", websocket_session_write_copy, messages);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: How to handle event writes/reads? This must be a websocket, but how to implement in hyper (if possible)?
|
|
|
|
|
// https://github.com/de-vri-es/hyper-tungstenite-rs
|
|
|
|
|
async fn handle_request(session_manager: &Arc<Mutex<CachingSessionManager>>, req: Request<Body>, addr: SocketAddr) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
async fn handle_request(session_manager: &Arc<Mutex<SessionManager>>, req: Request<Body>, addr: SocketAddr) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
println!("req to {} with method {}", req.uri().path(), req.method());
|
|
|
|
|
match (req.method(), req.uri().path()) {
|
|
|
|
|
(&Method::GET, "/session") => handle_get_session(session_manager, req).await,
|
|
|
|
|
@@ -133,7 +184,7 @@ async fn handle_request(session_manager: &Arc<Mutex<CachingSessionManager>>, req
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn handle_get_session(session_manager: &Arc<Mutex<CachingSessionManager>>, req: Request<Body>) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
async fn handle_get_session(session_manager: &Arc<Mutex<SessionManager>>, req: Request<Body>) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
let mut locked = session_manager.lock().await;
|
|
|
|
|
let query_params = get_query_params(&req);
|
|
|
|
|
let session_id = query_params.get("session_id");
|
|
|
|
|
@@ -148,7 +199,7 @@ async fn handle_get_session(session_manager: &Arc<Mutex<CachingSessionManager>>,
|
|
|
|
|
return build_success_res(&serde_json::to_string(&session.unwrap()).unwrap());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn handle_session_create(session_manager: &Arc<Mutex<CachingSessionManager>>, req: Request<Body>, addr: SocketAddr) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
async fn handle_session_create(session_manager: &Arc<Mutex<SessionManager>>, req: Request<Body>, addr: SocketAddr) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
let mut locked = session_manager.lock().await;
|
|
|
|
|
let session_create_res = locked.create_session(Player {id: addr.to_string()}).await;
|
|
|
|
|
if let Err(e) = session_create_res {
|
|
|
|
|
@@ -158,7 +209,8 @@ async fn handle_session_create(session_manager: &Arc<Mutex<CachingSessionManager
|
|
|
|
|
return build_success_res(&serialized.to_string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn handle_session_join(session_manager: &Arc<Mutex<CachingSessionManager>>, mut req: Request<Body>, addr: SocketAddr) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
async fn handle_session_join(session_manager: &Arc<Mutex<SessionManager>>, mut req: Request<Body>, addr: SocketAddr) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
println!("Received request to join session: {:?}", req);
|
|
|
|
|
let mut locked = session_manager.lock().await;
|
|
|
|
|
let body = read_json_body::<SessionJoinDto>(&mut req).await;
|
|
|
|
|
let session_join_res = locked.join_session(body.session_id, Player {id: addr.to_string()}).await;
|
|
|
|
|
@@ -166,11 +218,13 @@ async fn handle_session_join(session_manager: &Arc<Mutex<CachingSessionManager>>
|
|
|
|
|
eprintln!("Failed to join session: {:?}", e);
|
|
|
|
|
return Ok(Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR).body(Body::from(e)).unwrap());
|
|
|
|
|
}
|
|
|
|
|
let serialized = json!(session_join_res.unwrap());
|
|
|
|
|
let session = session_join_res.unwrap();
|
|
|
|
|
println!("Successfully joined session: {:?}", session);
|
|
|
|
|
let serialized = json!(session);
|
|
|
|
|
return build_success_res(&serialized.to_string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn handle_event_write(session_manager: &Arc<Mutex<CachingSessionManager>>, mut req: Request<Body>) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
async fn handle_event_write(session_manager: &Arc<Mutex<SessionManager>>, mut req: Request<Body>) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
let mut locked = session_manager.lock().await;
|
|
|
|
|
let event = read_json_body::<SessionEventWriteDTO>(&mut req).await;
|
|
|
|
|
let writer = locked.get_session_writer(&event.session_id);
|
|
|
|
|
@@ -182,9 +236,8 @@ async fn handle_event_write(session_manager: &Arc<Mutex<CachingSessionManager>>,
|
|
|
|
|
return Ok(res);
|
|
|
|
|
}
|
|
|
|
|
let mut writer = writer.unwrap();
|
|
|
|
|
let mut writer_locked = writer.lock().await;
|
|
|
|
|
println!("Writing session event to kafka: {:?}", event);
|
|
|
|
|
let write_res = writer_locked.write_to_session(&event.topic, &event.msg);
|
|
|
|
|
let write_res = writer.write_to_session(&event.topic, &event.msg);
|
|
|
|
|
if let Err(e) = write_res {
|
|
|
|
|
let err = format!("Failed to write event: {}", e);
|
|
|
|
|
println!("{}", err);
|
|
|
|
|
@@ -196,10 +249,10 @@ async fn handle_event_write(session_manager: &Arc<Mutex<CachingSessionManager>>,
|
|
|
|
|
build_success_res(&serde_json::to_string(&event).unwrap())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn handle_event_read(session_manager: &Arc<Mutex<CachingSessionManager>>, mut req: Request<Body>) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
async fn handle_event_read(session_manager: &Arc<Mutex<SessionManager>>, mut req: Request<Body>) -> Result<Response<Body>, Infallible> {
|
|
|
|
|
let mut locked = session_manager.lock().await;
|
|
|
|
|
let read_payload = read_json_body::<SessionReadDTO>(&mut req).await;
|
|
|
|
|
let reader = locked.get_session_reader(&read_payload.session_id);
|
|
|
|
|
let reader = locked.get_session_reader(&read_payload.session_id, &["move", "status", "input", "session"]);
|
|
|
|
|
if let Err(e) = reader {
|
|
|
|
|
let err = format!("Failed to read events: {}", e);
|
|
|
|
|
println!("{}", err);
|
|
|
|
|
@@ -208,9 +261,8 @@ async fn handle_event_read(session_manager: &Arc<Mutex<CachingSessionManager>>,
|
|
|
|
|
return Ok(res);
|
|
|
|
|
}
|
|
|
|
|
let mut reader = reader.unwrap();
|
|
|
|
|
let mut reader_locked = reader.lock().await;
|
|
|
|
|
println!("Reading session events from kafka for session: {}", read_payload.session_id);
|
|
|
|
|
let events = reader_locked.read_from_session();
|
|
|
|
|
let events = reader.read_from_session();
|
|
|
|
|
if let Err(e) = events {
|
|
|
|
|
let err = format!("Failed to read events: {}", e);
|
|
|
|
|
println!("{}", err);
|
|
|
|
|
@@ -268,3 +320,36 @@ struct SessionReadDTO {
|
|
|
|
|
struct SessionJoinDto {
|
|
|
|
|
session_id: String
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
enum WebSocketConnectionType {
|
|
|
|
|
HOST, PEER, OBSERVER
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FromStr for WebSocketConnectionType {
|
|
|
|
|
type Err = ();
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
match s.to_lowercase().as_str() {
|
|
|
|
|
"host" => Ok(WebSocketConnectionType::HOST),
|
|
|
|
|
"peer" => Ok(WebSocketConnectionType::PEER),
|
|
|
|
|
"observer" => Ok(WebSocketConnectionType::OBSERVER),
|
|
|
|
|
_ => Err(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
struct WebSocketSession {
|
|
|
|
|
pub connection_type: WebSocketConnectionType,
|
|
|
|
|
pub session_id: String
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl WebSocketConnectionType {
|
|
|
|
|
pub fn get_topics(&self) -> &[&str] {
|
|
|
|
|
match self {
|
|
|
|
|
WebSocketConnectionType::HOST => &["input", "session"],
|
|
|
|
|
WebSocketConnectionType::PEER | WebSocketConnectionType::OBSERVER => &["move", "input", "status", "session"],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|