From 2af8cacc762d0fc29510c04a42875103f6f333bc Mon Sep 17 00:00:00 2001 From: Arkitu Date: Fri, 15 Aug 2025 08:36:31 +0200 Subject: [PATCH] save --- Cargo.lock | 28 ++- Cargo.toml | 11 +- src/apps/chat.html | 36 +--- src/apps/chat.js | 26 +++ src/apps/chat.rs | 482 ++++++++++++++++++++++----------------------- src/apps/index.rs | 134 +++++++++---- src/lib.rs | 12 +- src/main.rs | 6 +- src/socket.rs | 4 +- src/socket/ws.rs | 6 +- 10 files changed, 415 insertions(+), 330 deletions(-) create mode 100644 src/apps/chat.js diff --git a/Cargo.lock b/Cargo.lock index 6f1fa0e..adfa525 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,6 +206,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crunchy" version = "0.2.3" @@ -1061,7 +1067,7 @@ dependencies = [ "percent-encoding", "portable-atomic", "rand_core", - "ringbuffer", + "ringbuf", "serde", "serde-json-core", "sha1", @@ -1137,6 +1143,15 @@ dependencies = [ "critical-section", ] +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "precomputed-hash" version = "0.1.1" @@ -1257,10 +1272,15 @@ dependencies = [ ] [[package]] -name = "ringbuffer" -version = "0.15.0" +name = "ringbuf" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df6368f71f205ff9c33c076d170dd56ebf68e8161c733c0caa07a7a5509ed53" +checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c" +dependencies = [ + "crossbeam-utils", + "portable-atomic", + "portable-atomic-util", +] [[package]] name = "rp-pac" diff --git a/Cargo.toml b/Cargo.toml index e2f829a..03c4eac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,15 +4,16 @@ version = "0.1.0" edition = "2024" [features] +json = ["dep:serde-json-core", "dep:serde"] wifi-connect = [ "dep:serde-json-core", "dep:serde", ] # you need to add a wifi.json file for this to work dhcp = ["dep:dhcparse"] dns = ["dep:dnsparse"] -chat = ["dep:ringbuffer"] -ttt = ["dep:serde-json-core", "dep:serde"] -default = ["dhcp", "dns", "ttt"] +chat = ["dep:ringbuf", "json"] +ttt = ["json"] +default = ["dhcp", "dns", "chat"] [dependencies] embassy-executor = { git = "https://github.com/embassy-rs/embassy", features = [ @@ -54,7 +55,9 @@ serde = { version = "*", optional = true, default-features = false, features = [ ] } dhcparse = { version = "*", default-features = false, optional = true } dnsparse = { version = "*", optional = true } -ringbuffer = { version = "*", default-features = false, optional = true } +ringbuf = { version = "*", default-features = false, features = [ + "portable-atomic", +], optional = true } percent-encoding = { version = "*", default-features = false } sha1 = { version = "*", default-features = false } base64 = { version = "*", default-features = false } diff --git a/src/apps/chat.html b/src/apps/chat.html index 3074e84..9afc45c 100644 --- a/src/apps/chat.html +++ b/src/apps/chat.html @@ -1,41 +1,11 @@ - - +

Chat

-
- Enter your name : - - -
+
+
diff --git a/src/apps/chat.js b/src/apps/chat.js new file mode 100644 index 0000000..75245e6 --- /dev/null +++ b/src/apps/chat.js @@ -0,0 +1,26 @@ +const id = 0; +const username = "testName"; +if (id === undefined) { + throw "id is undefined!"; +} +if (username === undefined) { + throw "username is undefined!"; +} +const ws = new WebSocket("ws://192.254.0.2:" + (9000 + id) + "/" + username); + +ws.onmessage = (event) => { + if (typeof event.data == "string") { + let msg = JSON.parse(event.data); + if (event.data[0] == "[") { + let usernames = []; + for (u of msg) { + if (u.length() > 0) { + let un = document.createElement("div"); + un.innerText = u; + usernames.push(un); + } + } + document.getElementById("usernames").replaceChildren(...usernames); + } + } +}; diff --git a/src/apps/chat.rs b/src/apps/chat.rs index c100e67..41c7f7d 100644 --- a/src/apps/chat.rs +++ b/src/apps/chat.rs @@ -1,30 +1,167 @@ -use core::fmt::Write; -use dhcparse::dhcpv4::MAX_MESSAGE_SIZE; +use core::str::from_utf8_unchecked; + use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, mutex::Mutex}; -use heapless::{String, Vec}; +use embassy_time::Timer; +use heapless::String; use log::{info, warn}; -use percent_encoding::percent_decode_str; -use pico_website::unwrap; -use ringbuffer::{ConstGenericRingBuffer, RingBuffer}; +use pico_website::{unimplemented, unwrap, unwrap_opt}; +use serde::Serialize; -use crate::socket::{HttpRequestType, HttpResCode}; +use crate::{apps::App, socket::ws::WsMsg}; -use super::App; +// Must be <= u8::MAX-1; +pub const USERS_LEN: u8 = 4; -const MEMORY_SIZE: usize = 16; -const USERNAME_MIN_SIZE: usize = 3; -const USERNAME_SIZE: usize = 16; -const MSG_SIZE: usize = 128; +const MSG_MAX_SIZE: usize = 10; +#[derive(Debug, Serialize)] +struct Msg<'a> { + id: usize, + author: u8, + content: &'a str, +} +// {"id"=999999,"author"="","content"=""} -static MESSAGES: Mutex = Mutex::new(Messages::new()); +const MSGS_SIZE: usize = 30; +#[derive(Debug)] +struct Msgs { + /// Memory layout with sizes in bytes : ...|content: len|len: 2|author+1: 1|... + /// `author=0` means theres no message, it's just padding and should be skipped. + /// No message is splitted + inner: [u8; MSGS_SIZE], + /// next byte index + head: usize, + next_msg: usize, +} +impl Msgs { + const fn default() -> Self { + Self { + inner: [0; _], + head: 0, + next_msg: 0, + } + } + fn push(&mut self, author: u8, content: &str) { + if self.head + content.len() + 3 >= MSGS_SIZE { + self.inner[self.head..].fill(0); + self.head = 0 + } + self.inner[self.head..self.head + content.len()].copy_from_slice(content.as_bytes()); + self.head += content.len(); + self.inner[self.head..self.head + 2].copy_from_slice(&(content.len() as u16).to_le_bytes()); + self.inner[self.head + 2] = author + 1; + self.head += 3; + } + // Iter messages from present to past + fn iter(&self) -> MsgsIter { + if self.head == 0 { + MsgsIter { + msgs: self, + head: 0, + current_id: 0, + finished: true, + } + } else { + MsgsIter { + msgs: self, + head: self.head, + current_id: self.next_msg - 1, + finished: false, + } + } + } +} +struct MsgsIter<'a> { + msgs: &'a Msgs, + /// next byte index + head: usize, + finished: bool, + current_id: usize, +} +impl<'a> Iterator for MsgsIter<'a> { + type Item = Msg<'a>; + /// We trust msgs.inner validity in this function, it might panic or do UB if msgs.inner is not valid + fn next(&mut self) -> Option { + if self.finished { + return None; + } + if self.head == 0 { + self.head = MSGS_SIZE; + } + let above = self.head > self.msgs.head; + if above && self.head < self.msgs.head + 3 { + self.finished = true; + return None; + } + let author = self.msgs.inner[self.head - 1]; + self.head -= 1; + if author == 0 { + return self.next(); + } + let author = author - 1; + let len = u16::from_le_bytes([ + self.msgs.inner[self.head - 2], + self.msgs.inner[self.head - 1], + ]) as usize; + self.head -= 2; + + let content = + unsafe { str::from_utf8_unchecked(&self.msgs.inner[self.head - len..self.head]) }; + self.head -= len; + if above && self.head < self.msgs.head { + self.finished = true; + return None; + } + if self.current_id == 0 { + self.finished = true; + } else { + self.current_id -= 1; + } + + Some(Msg { + id: self.current_id, + author, + content, + }) + } +} + +static MSGS: Mutex = Mutex::new(Msgs::default()); +const USERNAME_MAX_LEN: usize = 16; +#[derive(Serialize)] +pub struct Usernames([Option>; USERS_LEN as usize]); +impl Usernames { + const fn default() -> Self { + Self([None, None, None, None]) + } + pub fn get_id(&mut self, name: &str) -> Option { + for (i, un) in self.0.iter().enumerate() { + if let Some(n) = un { + if n.as_str() == name { + return Some(i as u8); + } + } + } + for (i, un) in self.0.iter_mut().enumerate() { + if *un == None { + *un = Some(String::new()); + un.as_mut().unwrap().push_str(name).unwrap(); + return Some(i as u8); + } + } + None + } +} +pub static USERNAMES: Mutex = Mutex::new(Usernames::default()); pub struct ChatApp { - res_buf: String<1100>, + id: u8, + json_buf: [u8; 48 + USERNAME_MAX_LEN + MSG_MAX_SIZE], } impl ChatApp { - pub fn new() -> Self { + pub fn new(id: u8) -> Self { Self { - res_buf: String::new(), + id, + json_buf: [0; _], } } } @@ -32,245 +169,106 @@ impl App for ChatApp { fn socket_name(&self) -> &'static str { "chat" } - async fn handle_request<'a>( + fn accept_ws(&self, path: &str) -> bool { + path.len() > 1 && path.len() <= 17 + } + async fn handle_ws<'a, const BUF_SIZE: usize, const RES_HEAD_BUF_SIZE: usize>( &'a mut self, - path: &str, - req_type: HttpRequestType, - content: &str, - ) -> (HttpResCode, &'static str, &'a str) { - match (req_type, path) { - (HttpRequestType::Get, "/" | "/index" | "/index.html" | "/chat" | "/chat.html") => { - (HttpResCode::Ok, "html", include_str!("./chat.html")) + _path: &str, + mut ws: crate::socket::ws::Ws<'a, BUF_SIZE, RES_HEAD_BUF_SIZE>, + ) { + Timer::after_millis(500).await; + let r: Result<(), ()> = try { + // Send all messages at the beginning + { + let msgs = MSGS.lock().await; + for m in msgs.iter() { + let n = unwrap(serde_json_core::to_slice(&m, &mut self.json_buf)).await; + let json = + unsafe { from_utf8_unchecked(&unwrap_opt(self.json_buf.get(..n)).await) }; + ws.send(WsMsg::Text(json)).await?; + } + } + { + let usernames = USERNAMES.lock().await; + let n = unwrap(serde_json_core::to_slice(&(*usernames), &mut self.json_buf)).await; + let json = + unsafe { from_utf8_unchecked(&unwrap_opt(self.json_buf.get(..n)).await) }; + ws.send(WsMsg::Text(json)).await?; } - (_, path) => { - let (path, args) = path.split_once('?').unwrap_or((path, "")); - let mut load = None; - let mut username = None; - let mut msg_content = None; - let mut poll = false; - for arg in args.split('&').chain(content.split('&')) { - match arg.split_once('=') { - Some(("load", n)) => { - let n: u16 = match n.parse() { - Ok(v) => v, - Err(_) => return (HttpResCode::BadRequest, "", ""), - }; - if n > 0 { - load = Some(n); - } - } - Some(("username", u)) => { - let mut name = String::::new(); - for c in percent_decode_str(u) { - if let Err(_) = name.push(c as char) { - return (HttpResCode::BadRequest, "", ""); - } - } - if u.len() < USERNAME_MIN_SIZE { - return (HttpResCode::BadRequest, "", ""); - } - username = Some(name); - } - Some(("msg", m)) => { - let mut msg = Vec::::new(); - let mut i = 0; - while i < m.len() { - let c = if m.as_bytes()[i] == b'%' { - let c = match m - .get(i + 1..=i + 2) - .map(|s| u8::from_str_radix(s, 16)) - { - Some(Ok(c)) => c, - _ => { - warn!("Invalid percent encoding of msg argument"); - return (HttpResCode::BadRequest, "", ""); - } - }; - i += 2; - c - } else { - m.as_bytes()[i] - }; - if let Err(_) = msg.push(c) { - return (HttpResCode::BadRequest, "", ""); - } - i += 1; - } - msg_content = Some(match String::from_utf8(msg) { - Ok(msg) => msg, - Err(_) => { - warn!("Invalid utf8 msg argument"); - return (HttpResCode::BadRequest, "", ""); - } - }); - } - Some(("poll", "true")) => poll = true, - _ => {} + loop { + Timer::after_secs(1).await; + { + let msgs = MSGS.lock().await; + info!("{:?}", msgs); + Timer::after_millis(100).await; + for m in msgs.iter() { + info!("{:?}", m); + Timer::after_millis(100).await; } } - info!( - "load:{:?} | username:{:?} | msg:{:?}", - load, username, msg_content - ); - if path == "/chat/connect" && username.is_some() { - self.res_buf.clear(); - unwrap(write!( - &mut self.res_buf, - " -
\ -
\ -
\ -
\ - \ - -
", - username.unwrap(), - MEMORY_SIZE, - MAX_MESSAGE_SIZE - )) - .await; - return (HttpResCode::Ok, "html", &self.res_buf); - } else if path == "/chat/send" && username.is_some() && msg_content.is_some() { - let mut msgs = MESSAGES.lock().await; - msgs.push(Message { - author: username.unwrap(), - content: msg_content.unwrap(), - }); - self.res_buf.clear(); - unwrap(write!( - &mut self.res_buf, - "\ - ", - MAX_MESSAGE_SIZE - )) - .await; - return (HttpResCode::Ok, "html", &self.res_buf); - } else if path.starts_with("/chat/message/") && path.len() > 14 { - let msg_id: u16 = match path[14..].parse() { - Ok(n) => n, - Err(_) => return (HttpResCode::BadRequest, "", ""), - }; - let msgs = MESSAGES.lock().await; - if msg_id > msgs.next { - return (HttpResCode::BadRequest, "", ""); - } - self.res_buf.clear(); - unwrap(write!(&mut self.res_buf, "
{ - unwrap(write!( - &mut self.res_buf, - ">{}: {}
", - msg.author, msg.content - )) + while let Some(r) = ws.rcv().await? { + info!("{:?}", r); + if let WsMsg::Text(r) = r { + if r.starts_with("send ") { + if r.len() > 5 + MSG_MAX_SIZE { + warn!("Message too long! (len={})", r.len() - 5); + return; + } + MSGS.lock() .await - } - None => { - if load.is_some() { - if (msg_id as isize) - == (msgs.next as isize - MEMORY_SIZE as isize - 1) - { - unwrap(write!( - &mut self.res_buf, - ">Older messages forgotten" - )) - .await; - } else { - unwrap(write!( - &mut self.res_buf, - " style=\"display: none;\">" - )) - .await; - } - } else { - return (HttpResCode::NoContent, "", ""); - } - } - }; - }; + .push(self.id, r.get(5..).unwrap_or_default()); + } + // if r.starts_with("reqmsg ") { + // let Ok(msg_id) = r.get(7..).unwrap_or_default().parse::() else { + // warn!("Invalid requested message : {}", r); + // return; + // }; + // // 0 is next msg + // let msg_rel_id = if msg_id >= 0 { - return (HttpResCode::Ok, "html", &self.res_buf); - } else { - (HttpResCode::NotFound, "", "") + // } + // if msg_id < 0 { + + // } + // } + } } } + }; + if r.is_err() { + warn!( + "Socket {}: error in ws, terminating connection", + self.socket_name() + ); } } } -struct Message { - author: String, - content: String, -} +// #[derive(Deserialize)] +// enum ClientMsg { +// ReqMsg(usize), +// } -struct Messages { - inner: ConstGenericRingBuffer, - next: u16, -} -impl Messages { - const fn new() -> Self { - Self { - inner: ConstGenericRingBuffer::new(), - next: 0, - } - } - fn get_abs(&self, id: u16) -> Option<&Message> { - if (id as isize) < (self.next as isize - MEMORY_SIZE as isize) { - return None; - } - self.inner.get_signed((id as isize) - (self.next as isize)) - } - fn push(&mut self, msg: Message) { - info!("{}: {}", msg.author, msg.content); - self.inner.push(msg); - self.next += 1; +pub async fn id_to_static_str(id: u8) -> &'static str { + match id { + 0 => "0", + 1 => "1", + 2 => "2", + 3 => "3", + 4 => "4", + 5 => "5", + 6 => "6", + 7 => "7", + 8 => "8", + 9 => "9", + 10 => "10", + 11 => "11", + 12 => "12", + 13 => "13", + 14 => "14", + 15 => "15", + _ => unimplemented().await, } } diff --git a/src/apps/index.rs b/src/apps/index.rs index 4f767ae..94e827a 100644 --- a/src/apps/index.rs +++ b/src/apps/index.rs @@ -1,8 +1,8 @@ -use heapless::Vec; +use heapless::{LinearMap, Vec}; use pico_website::unwrap; use crate::{ - apps::Content, + apps::{self, Content, chat::id_to_static_str}, socket::{HttpRequestType, HttpResCode}, }; @@ -39,52 +39,108 @@ impl App for IndexApp { path => { let (path, args) = path.split_once('?').unwrap_or((path, "")); let mut team = None; + let mut name = None; + let mut id = None; for arg in args.split('&') { match arg.split_once('=') { Some(("team", "0")) => team = Some("0"), Some(("team", "1")) => team = Some("1"), + Some(("name", n)) => { + if n.len() >= 1 && n.len() <= 16 { + name = Some(n); + } + } + Some(("id", id)) => { + if let Ok(id) = id.parse::() { + if id < apps::chat::USERS_LEN {} + } + } _ => {} } } - if path == "/ttt" { - let Some(team) = team else { - return (HttpResCode::BadRequest, "", None); - }; - #[cfg(debug_assertions)] - let html = include_str!("ttt.html"); - #[cfg(not(debug_assertions))] - let html = include_str!("../../static/ttt.min.html"); - - let mut content = Vec::new(); - let r: Result<(), &str> = try { - let (html1, html2) = html.split_once("/ttt.js").ok_or("")?; - - content.push(html1)?; - content.push("/ttt.js?team=")?; - content.push(team)?; - content.push(html2)?; - }; - unwrap(r).await; - (HttpResCode::Ok, "html", Some(Content(content))) - } else if path == "/ttt.js" { - let Some(team) = team else { - return (HttpResCode::BadRequest, "", None); - }; - - let mut content = Vec::new(); - let r: Result<(), &str> = try { - content.push("const team = ")?; - content.push(team)?; - content.push(";")?; + match path { + "/ttt" => { + let Some(team) = team else { + return (HttpResCode::BadRequest, "", None); + }; #[cfg(debug_assertions)] - content.push(include_str!("ttt.js"))?; + let html = include_str!("ttt.html"); #[cfg(not(debug_assertions))] - content.push(include_str!("../../static/ttt.min.js"))?; - }; - unwrap(r).await; - (HttpResCode::Ok, "javascript", Some(Content(content))) - } else { - (HttpResCode::NotFound, "", None) + let html = include_str!("../../static/ttt.min.html"); + + let mut content = Vec::new(); + let r: Result<(), &str> = try { + let (html1, html2) = html.split_once("/ttt.js").ok_or("")?; + + content.push(html1)?; + content.push("/ttt.js?team=")?; + content.push(team)?; + content.push(html2)?; + }; + unwrap(r).await; + (HttpResCode::Ok, "html", Some(Content(content))) + } + "/ttt.js" => { + let Some(team) = team else { + return (HttpResCode::BadRequest, "", None); + }; + + let mut content = Vec::new(); + let r: Result<(), &str> = try { + content.push("const team = ")?; + content.push(team)?; + content.push(";")?; + #[cfg(debug_assertions)] + content.push(include_str!("ttt.js"))?; + #[cfg(not(debug_assertions))] + content.push(include_str!("../../static/ttt.min.js"))?; + }; + unwrap(r).await; + (HttpResCode::Ok, "javascript", Some(Content(content))) + } + "/chat" => { + let Some(name) = name else { + return (HttpResCode::BadRequest, "", None); + }; + // #[cfg(debug_assertions)] + let html = include_str!("chat.html"); + // #[cfg(not(debug_assertions))] + // let html = include_str!("../../static/chat.min.html"); + let Some(id) = apps::chat::USERNAMES.lock().await.get_id(name) else { + return (HttpResCode::NoContent, "", None); + }; + + let mut content = Vec::new(); + let r: Result<(), &str> = try { + let (html1, html2) = html.split_once("/chat.js").ok_or("")?; + + content.push(html1)?; + content.push("/chat.js?id=")?; + content.push(id_to_static_str(id).await)?; + content.push(html2)?; + }; + unwrap(r).await; + (HttpResCode::Ok, "html", Some(Content(content))) + } + "/chat.js" => { + let Some(id) = id else { + return (HttpResCode::BadRequest, "", None); + }; + + let mut content = Vec::new(); + let r: Result<(), &str> = try { + content.push("const team = ")?; + content.push(team)?; + content.push(";")?; + #[cfg(debug_assertions)] + content.push(include_str!("ttt.js"))?; + #[cfg(not(debug_assertions))] + content.push(include_str!("../../static/ttt.min.js"))?; + }; + unwrap(r).await; + (HttpResCode::Ok, "javascript", Some(Content(content))) + } + _ => (HttpResCode::NotFound, "", None), } } } diff --git a/src/lib.rs b/src/lib.rs index 7e6dcf7..21609b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ pub async fn unwrap(res: Result) -> T { } } pub async fn unwrap_opt(opt: Option) -> T { - unwrap(opt.ok_or(())).await + expect_opt("option unwraped", opt).await } pub async fn assert(condition: bool) { @@ -24,6 +24,16 @@ pub async fn assert(condition: bool) { } } +pub async fn expect_opt(msg: &str, opt: Option) -> T { + unwrap(opt.ok_or(msg)).await +} + +pub async fn unimplemented() -> ! { + let err: Result<(), &str> = Err("unimplemented"); + unwrap(err).await; + loop {} +} + // TODO: make this log work #[panic_handler] fn panic(info: &PanicInfo) -> ! { diff --git a/src/main.rs b/src/main.rs index 885c0f2..b4dc43e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ #![feature(slice_split_once)] #![feature(try_blocks)] #![feature(impl_trait_in_bindings)] +#![feature(generic_arg_infer)] +#![feature(array_repeat)] #[cfg(feature = "wifi-connect")] use core::net::Ipv4Addr; @@ -178,8 +180,8 @@ async fn main(spawner: Spawner) { unwrap(spawner.spawn(socket::ttt_listen_task(stack, apps::ttt::Team::One, 8081))).await; } #[cfg(feature = "chat")] - for _ in 0..4 { - unwrap(spawner.spawn(socket::chat_listen_task(stack, 8082))).await; + for i in 0..4 { + unwrap(spawner.spawn(socket::chat_listen_task(stack, i, 9000 + i as u16))).await; } info!("All apps lauched!"); } diff --git a/src/socket.rs b/src/socket.rs index be147fd..f12cd7c 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -27,8 +27,8 @@ pub async fn index_listen_task(stack: embassy_net::Stack<'static>, port: u16) { #[cfg(feature = "chat")] #[embassy_executor::task(pool_size = 4)] -pub async fn chat_listen_task(stack: embassy_net::Stack<'static>, port: u16) { - listen_task(stack, apps::chat::ChatApp::new(), port).await +pub async fn chat_listen_task(stack: embassy_net::Stack<'static>, id: u8, port: u16) { + listen_task(stack, apps::chat::ChatApp::new(id), port).await } pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps::App, port: u16) { diff --git a/src/socket/ws.rs b/src/socket/ws.rs index fe87cf5..71f2c5c 100644 --- a/src/socket/ws.rs +++ b/src/socket/ws.rs @@ -1,11 +1,11 @@ use core::str::from_utf8; use embassy_net::tcp::{TcpReader, TcpSocket, TcpWriter}; -use embassy_time::Instant; +use embassy_time::{Instant, Timer}; use embedded_io_async::{ErrorType, ReadReady, Write}; use heapless::Vec; use log::{info, warn}; -use pico_website::{assert, unwrap, unwrap_opt}; +use pico_website::{assert, expect_opt, unwrap, unwrap_opt}; #[derive(Clone, Copy, Debug)] pub enum WsMsg<'a> { @@ -179,7 +179,7 @@ impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEA .iter_mut() .enumerate() { - *x ^= unwrap_opt(mask_key.get(i & 0xff)).await; + *x ^= unwrap_opt(mask_key.get(i % 4)).await; } if n_after_length + 4 + (length as usize) < n { self.rx.msg_in_buf = Some((