Skip to content

Commit

Permalink
Catch stack unwinds in /answer endpoint (BloopAI#545)
Browse files Browse the repository at this point in the history
* Catch unwind in answer API

Due to the design of the `EventStream` web API, browsers will attempt to
reconnect when a connection is closed. The bloop client has special logic
to prevent this, but only when an error occurs in the stream, signalled
by a serialized error, or when the `[DONE]` message is encountered.
Previously, when the HTTP handler panicked, this would result in a
logged message and a closed stream, as an error would fail to be
transmitted.

Now, we catch stack unwinds, and return an error to the client, so that
we can ensure its error handling logic is triggered in order to display
a proper error message, and to stop automatically reconnecting to the
event stream endpoint.

* remove tauri test

---------

Co-authored-by: Gabriel Gordon-Hall <[email protected]>
  • Loading branch information
calyptobai and ggordonhall authored May 23, 2023
1 parent a8d94e7 commit 0df0aee
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 71 deletions.
15 changes: 10 additions & 5 deletions server/bleep/src/webserver/answer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::{
borrow::Cow,
collections::{HashMap, HashSet},
mem,
panic::AssertUnwindSafe,
path::{Component, PathBuf},
str::FromStr,
time::Duration,
Expand Down Expand Up @@ -179,11 +180,15 @@ pub(super) async fn _handle(
Ok(sse::Event::default().data(params.thread_id.to_string()))
});

let answer_stream = stream.map(|upd: Result<Exchange>| {
sse::Event::default()
.json_data(upd.map_err(|e| e.to_string()))
.map_err(anyhow::Error::new)
});
// We know the stream is unwind safe as it doesn't use synchronization primitives like locks.
let answer_stream = AssertUnwindSafe(stream)
.catch_unwind()
.map(|res| res.unwrap_or_else(|_| Err(anyhow!("stream panicked"))))
.map(|upd: Result<Exchange>| {
sse::Event::default()
.json_data(upd.map_err(|e| e.to_string()))
.map_err(anyhow::Error::new)
});

let done_stream = futures::stream::once(async { Ok(sse::Event::default().data("[DONE]")) });

Expand Down
66 changes: 0 additions & 66 deletions server/bleep/tests/desktop.rs

This file was deleted.

0 comments on commit 0df0aee

Please sign in to comment.