From: Ian Jackson Date: Mon, 13 Jul 2020 22:26:24 +0000 (+0100) Subject: attempt at avoiding browser leak X-Git-Tag: otter-0.2.0~1330 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=793452e2fd585eaece98c9b61b92be7c23e0a1ce;p=otter.git attempt at avoiding browser leak this did not work - left it running and those tabs ate all ram Might be leaving the developer tools open. Will try again without this patch and without dev tools. --- diff --git a/src/sse.rs b/src/sse.rs index e345b048..02971e63 100644 --- a/src/sse.rs +++ b/src/sse.rs @@ -15,6 +15,12 @@ const UPDATE_READER_SIZE : usize = 1024*32; const UPDATE_MAX_FRAMING_SIZE : usize = 200; const UPDATE_KEEPALIVE : Duration = Duration::from_secs(14); +// We suspect that browsers leak the datastream. Certainly with +// the developer tools network console open, firefox eventually +// displays the whole datastream! +const CLIENT_LEAK_LOW_WATER : usize = 10*1024; +const CLIENT_LEAK_HIGH_WATER : usize = 2*10*1024; + struct UpdateReader { player : PlayerId, client : ClientId, @@ -23,6 +29,7 @@ struct UpdateReader { keepalives : Wrapping, to_send : UpdateId, ami : Arc>, + sent : usize, } #[derive(Error,Debug)] @@ -31,6 +38,8 @@ struct FlushWouldBlockError{} impl Read for UpdateReader { fn read(&mut self, orig_buf: &mut [u8]) -> Result { + if self.sent > CLIENT_LEAK_HIGH_WATER { return Ok(0) } + let em : fn(&'static str) -> io::Error = |s| io::Error::new(io::ErrorKind::Other, anyhow!(s)); @@ -72,6 +81,7 @@ impl Read for UpdateReader { generated, &self.player, &self.client, str::from_utf8(&orig_buf[0..generated]).unwrap()); self.need_flush = true; + self.sent += generated; return Ok(generated) } @@ -80,8 +90,8 @@ impl Read for UpdateReader { return Err(io::Error::new(io::ErrorKind::WouldBlock, FlushWouldBlockError{})); } - // xxx this endless stream is a leak - // restart it occasionally + + if self.sent > CLIENT_LEAK_LOW_WATER { return Ok(0) } amig = cv.wait_timeout(amig, UPDATE_KEEPALIVE) .map_err(|_| em("poison"))?.0; @@ -121,6 +131,21 @@ impl StableIndexOffset for UpdateId { fn zero() -> Self { UpdateId(0) } } +impl<'a,'r> FromRequest<'a,'r> for UpdateId { + type Error = OperationalError; + + fn from_request(req: &'a Request<'r>) + -> request::Outcome { + let header: Vec<_> = request.headers().get("Last-Event-ID").collect(); + match keys.len() { + 0 => Outcome::Failure((Status::BadRequest, ApiKeyError::Missing)), + 1 if is_valid(keys[0]) => Outcome::Success(ApiKey(keys[0].to_string())), + 1 => Outcome::Failure((Status::BadRequest, ApiKeyError::Invalid)), + _ => Outcome::Failure((Status::BadRequest, ApiKeyError::BadCount)), + } + } +} + // ---------- entrypoint for dribbling the http response ---------- #[throws(OE)] @@ -149,6 +174,7 @@ eprintln!("updates content iad={:?} player={:?} cl={:?} updates={:?}", player, client, to_send, ami, need_flush : false, keepalives : Wrapping(0), + sent : 0, init_confirmation_send : iter::once(()), } }; diff --git a/templates/script.ts b/templates/script.ts index 8b18245d..03bfa60b 100644 --- a/templates/script.ts +++ b/templates/script.ts @@ -583,6 +583,7 @@ function startup() { }); es.onerror = function(e) { console.log('FOO',e,es); + if (es.readyState == 0/*CONNECTING*/) return; json_report_error({ updates_error : e, updates_event_source : es,