use core::fmt::Write; use core::str::from_utf8; use embassy_net::tcp::TcpSocket; use embassy_time::{Duration, Timer}; use embedded_io_async::Write as _; use heapless::Vec; use log::{info, warn}; use crate::game::{GameClient, Team}; #[embassy_executor::task(pool_size = 2)] pub async fn listen_task(stack: embassy_net::Stack<'static>, team: Team, port: u16) { // loop { // info!("team:{:?}", team); // Timer::after_millis(0).await; // } let mut rx_buffer = [0; 4096]; let mut tx_buffer = [0; 4096]; let mut buf = [0; 4096]; let mut res_head_buf = Vec::::new(); let mut game_client = GameClient::new(team); loop { Timer::after_secs(0).await; let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); socket.set_timeout(Some(Duration::from_secs(30))); info!("Socket {:?}: Listening on TCP:{}...", team, port); if let Err(e) = socket.accept(port).await { warn!("accept error: {:?}", e); continue; } info!( "Socket {:?}: Received connection from {:?}", team, socket.remote_endpoint() ); loop { Timer::after_secs(0).await; let n = match socket.read(&mut buf).await { Ok(0) => { warn!("read EOF"); break; } Ok(n) => n, Err(e) => { warn!("Socket {:?}: read error: {:?}", team, e); break; } }; let mut headers: &[u8] = &buf[..n]; let mut _content: &[u8] = &[]; for i in 0..(n - 1) { if &buf[i..i + 1] == b"\r\n" { headers = &buf[0..i]; if i + 2 < n { _content = &buf[i + 2..n]; } } } let mut headers = headers.split(|x| *x == b'\n'); let (request_type, path) = match headers.next() { None => { warn!("Empty request"); break; } Some(l1) => { let mut l1 = l1.split(|x| *x == b' '); ( match l1.next() { Some(b"GET") => HttpRequestType::Get, Some(b"POST") => HttpRequestType::Post, Some(t) => { match from_utf8(t) { Ok(t) => { warn!("Unknown request type : {}", t); } Err(e) => { warn!( "Error while parsing request type : {}\nRaw type : {:?}", e, t ); } } break; } None => { warn!("No request type"); break; } }, match l1.next() { Some(path) => match from_utf8(path) { Ok(p) => p, Err(e) => { warn!( "Error while parsing requested path : {}\nRaw path : {:?}", e, path ); break; } }, None => { warn!("No path"); break; } }, ) } }; info!("Socket {:?}: {:?} request for {}", team, request_type, path); Timer::after_secs(0).await; let (code, res_type, res_content): (HttpResCode, &str, &[u8]) = match path { "/" => ( HttpResCode::Ok, "html", include_bytes!("../static/index.html"), ), "/htmx.js" => ( HttpResCode::Ok, "javascript", #[cfg(debug_assertions)] include_bytes!("../static/htmx.js"), #[cfg(not(debug_assertions))] include_bytes!("../static/htmx.min.js"), ), p => game_client.handle_request(p).await, }; res_head_buf.clear(); if let Err(e) = write!( &mut res_head_buf, "{}\r\n\ Content-Type: text/{}\r\n\ Content-Length: {}\r\n\r\n", Into::<&str>::into(code), res_type, res_content.len() ) { warn!("res buffer write error: {:?}", e); break; } match socket.write_all(&res_head_buf).await { Ok(()) => {} Err(e) => { warn!("write error: {:?}", e); break; } }; match socket.write_all(&res_content).await { Ok(()) => {} Err(e) => { warn!("write error: {:?}", e); break; } }; } } } #[derive(Clone, Copy, Debug)] enum HttpRequestType { Get, Post, } impl Into<&str> for HttpRequestType { fn into(self) -> &'static str { match self { Self::Get => "GET", Self::Post => "POST", } } } #[derive(Debug, Clone, Copy)] pub enum HttpResCode { Ok, NoContent, NotFound, Forbidden, } impl Into<&str> for HttpResCode { fn into(self) -> &'static str { match self { HttpResCode::Ok => "HTTP/1.1 200 OK", HttpResCode::NoContent => "HTTP/1.1 204 NO CONTENT", HttpResCode::NotFound => "HTTP/1.1 404 NOT FOUND", HttpResCode::Forbidden => "HTTP/1.1 403 FORBIDDEN", } } }