chiark / gitweb /
attempt at avoiding browser leak
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 13 Jul 2020 22:26:24 +0000 (23:26 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 13 Jul 2020 22:26:24 +0000 (23:26 +0100)
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.

src/sse.rs
templates/script.ts

index e345b04807d0493e4cd8af81932fd24bf759824b..02971e633d9088d8c340fe3ffc5bc1696045efb3 100644 (file)
@@ -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<u32>,
   to_send : UpdateId,
   ami : Arc<Mutex<Instance>>,
+  sent : usize,
 }
 
 #[derive(Error,Debug)]
@@ -31,6 +38,8 @@ struct FlushWouldBlockError{}
 
 impl Read for UpdateReader {
   fn read(&mut self, orig_buf: &mut [u8]) -> Result<usize,io::Error> {
+    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<Self,OperationError> {
+    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(()),
     }
   };
index 8b18245dababd7c7e861ae57857d086060811398..03bfa60bc5e96d01c8bb3a1e4d095ed5a3fd3bfe 100644 (file)
@@ -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,