mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 21:37:54 +02:00
Add and use new Signals
struct (#13314)
# Description This PR introduces a new `Signals` struct to replace our adhoc passing around of `ctrlc: Option<Arc<AtomicBool>>`. Doing so has a few benefits: - We can better enforce when/where resetting or triggering an interrupt is allowed. - Consolidates `nu_utils::ctrl_c::was_pressed` and other ad-hoc re-implementations into a single place: `Signals::check`. - This allows us to add other types of signals later if we want. E.g., exiting or suspension. - Similarly, we can more easily change the underlying implementation if we need to in the future. - Places that used to have a `ctrlc` of `None` now use `Signals::empty()`, so we can double check these usages for correctness in the future.
This commit is contained in:
@ -5,16 +5,12 @@ use base64::{
|
||||
Engine,
|
||||
};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::ByteStream;
|
||||
use nu_protocol::{ByteStream, Signals};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
sync::{
|
||||
atomic::AtomicBool,
|
||||
mpsc::{self, RecvTimeoutError},
|
||||
Arc,
|
||||
},
|
||||
sync::mpsc::{self, RecvTimeoutError},
|
||||
time::Duration,
|
||||
};
|
||||
use ureq::{Error, ErrorKind, Request, Response};
|
||||
@ -129,7 +125,7 @@ pub fn response_to_buffer(
|
||||
let reader = response.into_reader();
|
||||
|
||||
PipelineData::ByteStream(
|
||||
ByteStream::read(reader, span, engine_state.ctrlc.clone(), response_type)
|
||||
ByteStream::read(reader, span, engine_state.signals().clone(), response_type)
|
||||
.with_known_size(buffer_size),
|
||||
None,
|
||||
)
|
||||
@ -192,13 +188,14 @@ pub fn send_request(
|
||||
request: Request,
|
||||
http_body: HttpBody,
|
||||
content_type: Option<String>,
|
||||
ctrl_c: Option<Arc<AtomicBool>>,
|
||||
span: Span,
|
||||
signals: &Signals,
|
||||
) -> Result<Response, ShellErrorOrRequestError> {
|
||||
let request_url = request.url().to_string();
|
||||
|
||||
match http_body {
|
||||
HttpBody::None => {
|
||||
send_cancellable_request(&request_url, Box::new(|| request.call()), ctrl_c)
|
||||
send_cancellable_request(&request_url, Box::new(|| request.call()), span, signals)
|
||||
}
|
||||
HttpBody::ByteStream(byte_stream) => {
|
||||
let req = if let Some(content_type) = content_type {
|
||||
@ -207,7 +204,7 @@ pub fn send_request(
|
||||
request
|
||||
};
|
||||
|
||||
send_cancellable_request_bytes(&request_url, req, byte_stream, ctrl_c)
|
||||
send_cancellable_request_bytes(&request_url, req, byte_stream, span, signals)
|
||||
}
|
||||
HttpBody::Value(body) => {
|
||||
let (body_type, req) = match content_type {
|
||||
@ -224,20 +221,32 @@ pub fn send_request(
|
||||
Value::Binary { val, .. } => send_cancellable_request(
|
||||
&request_url,
|
||||
Box::new(move || req.send_bytes(&val)),
|
||||
ctrl_c,
|
||||
span,
|
||||
signals,
|
||||
),
|
||||
Value::String { .. } if body_type == BodyType::Json => {
|
||||
let data = value_to_json_value(&body)?;
|
||||
send_cancellable_request(&request_url, Box::new(|| req.send_json(data)), ctrl_c)
|
||||
send_cancellable_request(
|
||||
&request_url,
|
||||
Box::new(|| req.send_json(data)),
|
||||
span,
|
||||
signals,
|
||||
)
|
||||
}
|
||||
Value::String { val, .. } => send_cancellable_request(
|
||||
&request_url,
|
||||
Box::new(move || req.send_string(&val)),
|
||||
ctrl_c,
|
||||
span,
|
||||
signals,
|
||||
),
|
||||
Value::Record { .. } if body_type == BodyType::Json => {
|
||||
let data = value_to_json_value(&body)?;
|
||||
send_cancellable_request(&request_url, Box::new(|| req.send_json(data)), ctrl_c)
|
||||
send_cancellable_request(
|
||||
&request_url,
|
||||
Box::new(|| req.send_json(data)),
|
||||
span,
|
||||
signals,
|
||||
)
|
||||
}
|
||||
Value::Record { val, .. } if body_type == BodyType::Form => {
|
||||
let mut data: Vec<(String, String)> = Vec::with_capacity(val.len());
|
||||
@ -254,7 +263,7 @@ pub fn send_request(
|
||||
.collect::<Vec<(&str, &str)>>();
|
||||
req.send_form(&data)
|
||||
};
|
||||
send_cancellable_request(&request_url, Box::new(request_fn), ctrl_c)
|
||||
send_cancellable_request(&request_url, Box::new(request_fn), span, signals)
|
||||
}
|
||||
Value::List { vals, .. } if body_type == BodyType::Form => {
|
||||
if vals.len() % 2 != 0 {
|
||||
@ -276,11 +285,16 @@ pub fn send_request(
|
||||
.collect::<Vec<(&str, &str)>>();
|
||||
req.send_form(&data)
|
||||
};
|
||||
send_cancellable_request(&request_url, Box::new(request_fn), ctrl_c)
|
||||
send_cancellable_request(&request_url, Box::new(request_fn), span, signals)
|
||||
}
|
||||
Value::List { .. } if body_type == BodyType::Json => {
|
||||
let data = value_to_json_value(&body)?;
|
||||
send_cancellable_request(&request_url, Box::new(|| req.send_json(data)), ctrl_c)
|
||||
send_cancellable_request(
|
||||
&request_url,
|
||||
Box::new(|| req.send_json(data)),
|
||||
span,
|
||||
signals,
|
||||
)
|
||||
}
|
||||
_ => Err(ShellErrorOrRequestError::ShellError(ShellError::IOError {
|
||||
msg: "unsupported body input".into(),
|
||||
@ -295,7 +309,8 @@ pub fn send_request(
|
||||
fn send_cancellable_request(
|
||||
request_url: &str,
|
||||
request_fn: Box<dyn FnOnce() -> Result<Response, Error> + Sync + Send>,
|
||||
ctrl_c: Option<Arc<AtomicBool>>,
|
||||
span: Span,
|
||||
signals: &Signals,
|
||||
) -> Result<Response, ShellErrorOrRequestError> {
|
||||
let (tx, rx) = mpsc::channel::<Result<Response, Error>>();
|
||||
|
||||
@ -310,12 +325,7 @@ fn send_cancellable_request(
|
||||
|
||||
// ...and poll the channel for responses
|
||||
loop {
|
||||
if nu_utils::ctrl_c::was_pressed(&ctrl_c) {
|
||||
// Return early and give up on the background thread. The connection will either time out or be disconnected
|
||||
return Err(ShellErrorOrRequestError::ShellError(
|
||||
ShellError::InterruptedByUser { span: None },
|
||||
));
|
||||
}
|
||||
signals.check(span)?;
|
||||
|
||||
// 100ms wait time chosen arbitrarily
|
||||
match rx.recv_timeout(Duration::from_millis(100)) {
|
||||
@ -336,7 +346,8 @@ fn send_cancellable_request_bytes(
|
||||
request_url: &str,
|
||||
request: Request,
|
||||
byte_stream: ByteStream,
|
||||
ctrl_c: Option<Arc<AtomicBool>>,
|
||||
span: Span,
|
||||
signals: &Signals,
|
||||
) -> Result<Response, ShellErrorOrRequestError> {
|
||||
let (tx, rx) = mpsc::channel::<Result<Response, ShellErrorOrRequestError>>();
|
||||
let request_url_string = request_url.to_string();
|
||||
@ -369,12 +380,7 @@ fn send_cancellable_request_bytes(
|
||||
|
||||
// ...and poll the channel for responses
|
||||
loop {
|
||||
if nu_utils::ctrl_c::was_pressed(&ctrl_c) {
|
||||
// Return early and give up on the background thread. The connection will either time out or be disconnected
|
||||
return Err(ShellErrorOrRequestError::ShellError(
|
||||
ShellError::InterruptedByUser { span: None },
|
||||
));
|
||||
}
|
||||
signals.check(span)?;
|
||||
|
||||
// 100ms wait time chosen arbitrarily
|
||||
match rx.recv_timeout(Duration::from_millis(100)) {
|
||||
|
@ -203,7 +203,6 @@ fn helper(
|
||||
args: Arguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = args.url.span();
|
||||
let ctrl_c = engine_state.ctrlc.clone();
|
||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
||||
let redirect_mode = http_parse_redirect_mode(args.redirect)?;
|
||||
|
||||
@ -214,7 +213,13 @@ fn helper(
|
||||
request = request_add_authorization_header(args.user, args.password, request);
|
||||
request = request_add_custom_headers(args.headers, request)?;
|
||||
|
||||
let response = send_request(request.clone(), args.data, args.content_type, ctrl_c);
|
||||
let response = send_request(
|
||||
request.clone(),
|
||||
args.data,
|
||||
args.content_type,
|
||||
call.head,
|
||||
engine_state.signals(),
|
||||
);
|
||||
|
||||
let request_flags = RequestFlags {
|
||||
raw: args.raw,
|
||||
|
@ -171,7 +171,6 @@ fn helper(
|
||||
args: Arguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = args.url.span();
|
||||
let ctrl_c = engine_state.ctrlc.clone();
|
||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
||||
let redirect_mode = http_parse_redirect_mode(args.redirect)?;
|
||||
|
||||
@ -182,7 +181,13 @@ fn helper(
|
||||
request = request_add_authorization_header(args.user, args.password, request);
|
||||
request = request_add_custom_headers(args.headers, request)?;
|
||||
|
||||
let response = send_request(request.clone(), HttpBody::None, None, ctrl_c);
|
||||
let response = send_request(
|
||||
request.clone(),
|
||||
HttpBody::None,
|
||||
None,
|
||||
call.head,
|
||||
engine_state.signals(),
|
||||
);
|
||||
|
||||
let request_flags = RequestFlags {
|
||||
raw: args.raw,
|
||||
|
@ -1,13 +1,11 @@
|
||||
use super::client::HttpBody;
|
||||
use crate::network::http::client::{
|
||||
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
|
||||
request_add_authorization_header, request_add_custom_headers, request_handle_response_headers,
|
||||
request_set_timeout, send_request,
|
||||
};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
|
||||
use super::client::HttpBody;
|
||||
use nu_protocol::Signals;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -133,9 +131,8 @@ fn run_head(
|
||||
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
||||
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
|
||||
};
|
||||
let ctrl_c = engine_state.ctrlc.clone();
|
||||
|
||||
helper(engine_state, stack, call, args, ctrl_c)
|
||||
helper(engine_state, stack, call, args, engine_state.signals())
|
||||
}
|
||||
|
||||
// Helper function that actually goes to retrieve the resource from the url given
|
||||
@ -145,7 +142,7 @@ fn helper(
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
args: Arguments,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
signals: &Signals,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = args.url.span();
|
||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
||||
@ -158,7 +155,7 @@ fn helper(
|
||||
request = request_add_authorization_header(args.user, args.password, request);
|
||||
request = request_add_custom_headers(args.headers, request)?;
|
||||
|
||||
let response = send_request(request, HttpBody::None, None, ctrlc);
|
||||
let response = send_request(request, HttpBody::None, None, call.head, signals);
|
||||
check_response_redirection(redirect_mode, span, &response)?;
|
||||
request_handle_response_headers(span, response)
|
||||
}
|
||||
|
@ -151,7 +151,6 @@ fn helper(
|
||||
args: Arguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = args.url.span();
|
||||
let ctrl_c = engine_state.ctrlc.clone();
|
||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
||||
|
||||
let client = http_client(args.insecure, RedirectMode::Follow, engine_state, stack)?;
|
||||
@ -161,7 +160,13 @@ fn helper(
|
||||
request = request_add_authorization_header(args.user, args.password, request);
|
||||
request = request_add_custom_headers(args.headers, request)?;
|
||||
|
||||
let response = send_request(request.clone(), HttpBody::None, None, ctrl_c);
|
||||
let response = send_request(
|
||||
request.clone(),
|
||||
HttpBody::None,
|
||||
None,
|
||||
call.head,
|
||||
engine_state.signals(),
|
||||
);
|
||||
|
||||
// http options' response always showed in header, so we set full to true.
|
||||
// And `raw` is useless too because options method doesn't return body, here we set to true
|
||||
|
@ -205,7 +205,6 @@ fn helper(
|
||||
args: Arguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = args.url.span();
|
||||
let ctrl_c = engine_state.ctrlc.clone();
|
||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
||||
let redirect_mode = http_parse_redirect_mode(args.redirect)?;
|
||||
|
||||
@ -216,7 +215,13 @@ fn helper(
|
||||
request = request_add_authorization_header(args.user, args.password, request);
|
||||
request = request_add_custom_headers(args.headers, request)?;
|
||||
|
||||
let response = send_request(request.clone(), args.data, args.content_type, ctrl_c);
|
||||
let response = send_request(
|
||||
request.clone(),
|
||||
args.data,
|
||||
args.content_type,
|
||||
call.head,
|
||||
engine_state.signals(),
|
||||
);
|
||||
|
||||
let request_flags = RequestFlags {
|
||||
raw: args.raw,
|
||||
|
@ -203,7 +203,6 @@ fn helper(
|
||||
args: Arguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = args.url.span();
|
||||
let ctrl_c = engine_state.ctrlc.clone();
|
||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
||||
let redirect_mode = http_parse_redirect_mode(args.redirect)?;
|
||||
|
||||
@ -214,7 +213,13 @@ fn helper(
|
||||
request = request_add_authorization_header(args.user, args.password, request);
|
||||
request = request_add_custom_headers(args.headers, request)?;
|
||||
|
||||
let response = send_request(request.clone(), args.data, args.content_type, ctrl_c);
|
||||
let response = send_request(
|
||||
request.clone(),
|
||||
args.data,
|
||||
args.content_type,
|
||||
call.head,
|
||||
engine_state.signals(),
|
||||
);
|
||||
|
||||
let request_flags = RequestFlags {
|
||||
raw: args.raw,
|
||||
|
@ -204,7 +204,6 @@ fn helper(
|
||||
args: Arguments,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = args.url.span();
|
||||
let ctrl_c = engine_state.ctrlc.clone();
|
||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
||||
let redirect_mode = http_parse_redirect_mode(args.redirect)?;
|
||||
|
||||
@ -215,7 +214,13 @@ fn helper(
|
||||
request = request_add_authorization_header(args.user, args.password, request);
|
||||
request = request_add_custom_headers(args.headers, request)?;
|
||||
|
||||
let response = send_request(request.clone(), args.data, args.content_type, ctrl_c);
|
||||
let response = send_request(
|
||||
request.clone(),
|
||||
args.data,
|
||||
args.content_type,
|
||||
call.head,
|
||||
engine_state.signals(),
|
||||
);
|
||||
|
||||
let request_flags = RequestFlags {
|
||||
raw: args.raw,
|
||||
|
@ -48,7 +48,7 @@ impl Command for SubCommand {
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let args = CellPathOnlyArgs::from(cell_paths);
|
||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||
operate(action, args, input, call.head, engine_state.signals())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -50,15 +50,9 @@ impl Command for SubCommand {
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let args = CellPathOnlyArgs::from(cell_paths);
|
||||
if call.has_flag(engine_state, stack, "all")? {
|
||||
operate(
|
||||
action_all,
|
||||
args,
|
||||
input,
|
||||
call.head,
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
operate(action_all, args, input, call.head, engine_state.signals())
|
||||
} else {
|
||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||
operate(action, args, input, call.head, engine_state.signals())
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user