Watch
0
0
Fork
You've already forked hyperhive
0

login session: only the child exiting finishes it, not one output pipe closing

This commit is contained in:
damocles 2026-10-21 21:27:26 +02:00 • committed by mara
commit 25f93d41e1

View file

@ -25,7 +25,7 @@ struct State {
/// First URL-looking substring we saw in the output. Surface this on the
/// web UI as the link the operator should open.
url: Option<String>,
/// Set when the child has exited. The web UI uses this to know whether
/// Set by `poll` when the child has exited. The web UI uses this to know whether
/// the operator can still paste a code.
finished: bool,
/// Exit status note (e.g. "exited with code 0", "killed by signal 15"),
@ -209,10 +209,10 @@ async fn pump<R: tokio::io::AsyncRead + Unpin>(
// we'll miss it until EOF (acceptable for the URL surface — claude
// prints the URL on its own line).
match reader.read_line(&mut buf).await {
Ok(0) => {
state.lock().unwrap().finished = true;
break;
}
// EOF means this pipe closed, not that the child exited: only
// `poll` (via `try_wait`) may mark the session finished, or the
// next state render would drop a login that is still running.
Ok(0) => break,
Ok(_) => {
let mut s = state.lock().unwrap();
if s.url.is_none()
@ -225,9 +225,7 @@ async fn pump<R: tokio::io::AsyncRead + Unpin>(
}
Err(e) => {
tracing::warn!(error = ?e, %tag, "login pump read error");
let mut s = state.lock().unwrap();
s.finished = true;
s.exit_note = Some(format!("pump {tag} error: {e}"));
state.lock().unwrap().exit_note = Some(format!("pump {tag} error: {e}"));
break;
}
}
@ -277,7 +275,18 @@ impl Drop for LoginSession {
#[cfg(test)]
mod tests {
use super::extract_url;
use super::{State, extract_url, pump};
use std::sync::{Arc, Mutex};
use tokio::io::BufReader;
#[tokio::test]
async fn a_closed_output_pipe_does_not_finish_the_session() {
let state = Arc::new(Mutex::new(State::default()));
pump(BufReader::new(tokio::io::empty()), state.clone(), "stdout").await;
assert!(!state.lock().unwrap().finished);
}
#[test]
fn picks_first_https() {