From 0eba180fdbfdc19e15c743f00382d3fc79f65a10 Mon Sep 17 00:00:00 2001 From: melbourne2991 <4619280+melbourne2991@users.noreply.github.com> Date: Tue, 20 Aug 2024 10:21:12 +1000 Subject: [PATCH] fix(repl): Prevent panic on broken pipe (#21945) --- cli/tools/repl/mod.rs | 22 +++++++++++++++++----- tests/integration/run_tests.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index d754b4dd23..ed3d94c846 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -1,5 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::io; +use std::io::Write; + use std::sync::Arc; use crate::args::CliOptions; @@ -241,15 +244,24 @@ pub async fn run( // Doing this manually, instead of using `log::info!` because these messages // are supposed to go to stdout, not stderr. + // Using writeln, because println panics in certain cases + // (eg: broken pipes - https://github.com/denoland/deno/issues/21861) if !cli_options.is_quiet() { - println!("Deno {}", crate::version::DENO_VERSION_INFO.deno); - println!("exit using ctrl+d, ctrl+c, or close()"); + let mut handle = io::stdout().lock(); + + writeln!(handle, "Deno {}", crate::version::DENO_VERSION_INFO.deno)?; + writeln!(handle, "exit using ctrl+d, ctrl+c, or close()")?; + if repl_flags.is_default_command { - println!( + writeln!( + handle, "{}", colors::yellow("REPL is running with all permissions allowed.") - ); - println!("To specify permissions, run `deno repl` with allow flags.") + )?; + writeln!( + handle, + "To specify permissions, run `deno repl` with allow flags." + )?; } } diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index 0b3d85deb9..c6637e0058 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -4343,6 +4343,32 @@ fn broken_stdout() { assert!(!stderr.contains("panic")); } +#[test] +fn broken_stdout_repl() { + let (reader, writer) = os_pipe::pipe().unwrap(); + // drop the reader to create a broken pipe + drop(reader); + + let output = util::deno_cmd() + .current_dir(util::testdata_path()) + .arg("repl") + .stdout(writer) + .stderr_piped() + .spawn() + .unwrap() + .wait_with_output() + .unwrap(); + + assert!(!output.status.success()); + let stderr = std::str::from_utf8(output.stderr.as_ref()).unwrap().trim(); + if cfg!(windows) { + assert_contains!(stderr, "The pipe is being closed. (os error 232)"); + } else { + assert_contains!(stderr, "Broken pipe (os error 32)"); + } + assert_not_contains!(stderr, "panic"); +} + itest!(error_cause { args: "run run/error_cause.ts", output: "run/error_cause.ts.out",