login session: only the child exiting finishes it, not one output pipe closing
This commit is contained in:
parent
c3ff0815ff
commit
25f93d41e1
1 changed files with 18 additions and 9 deletions
|
|
@ -25,7 +25,7 @@ struct State {
|
||||||
/// First URL-looking substring we saw in the output. Surface this on the
|
/// First URL-looking substring we saw in the output. Surface this on the
|
||||||
/// web UI as the link the operator should open.
|
/// web UI as the link the operator should open.
|
||||||
url: Option<String>,
|
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.
|
/// the operator can still paste a code.
|
||||||
finished: bool,
|
finished: bool,
|
||||||
/// Exit status note (e.g. "exited with code 0", "killed by signal 15"),
|
/// 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
|
// we'll miss it until EOF (acceptable for the URL surface — claude
|
||||||
// prints the URL on its own line).
|
// prints the URL on its own line).
|
||||||
match reader.read_line(&mut buf).await {
|
match reader.read_line(&mut buf).await {
|
||||||
Ok(0) => {
|
// EOF means this pipe closed, not that the child exited: only
|
||||||
state.lock().unwrap().finished = true;
|
// `poll` (via `try_wait`) may mark the session finished, or the
|
||||||
break;
|
// next state render would drop a login that is still running.
|
||||||
}
|
Ok(0) => break,
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let mut s = state.lock().unwrap();
|
let mut s = state.lock().unwrap();
|
||||||
if s.url.is_none()
|
if s.url.is_none()
|
||||||
|
|
@ -225,9 +225,7 @@ async fn pump<R: tokio::io::AsyncRead + Unpin>(
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!(error = ?e, %tag, "login pump read error");
|
tracing::warn!(error = ?e, %tag, "login pump read error");
|
||||||
let mut s = state.lock().unwrap();
|
state.lock().unwrap().exit_note = Some(format!("pump {tag} error: {e}"));
|
||||||
s.finished = true;
|
|
||||||
s.exit_note = Some(format!("pump {tag} error: {e}"));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -277,7 +275,18 @@ impl Drop for LoginSession {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
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]
|
#[test]
|
||||||
fn picks_first_https() {
|
fn picks_first_https() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue