diff --git a/net/src/client/http/mod.rs b/net/src/client/http/mod.rs index 326bf02e3..e51f309fd 100644 --- a/net/src/client/http/mod.rs +++ b/net/src/client/http/mod.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use reqwest; use thiserror::Error; @@ -15,8 +17,9 @@ impl WitnetHttpClient { pub async fn send( &self, request: reqwest::RequestBuilder, + timeout: Option, ) -> Result { - let req = match request.build() { + let req = match request.timeout(timeout.unwrap_or(Duration::new(10, 0))).build() { Ok(req) => req, Err(e) => return Err(WitnetHttpError::HttpRequestError { msg: e.to_string() }), }; diff --git a/node/src/actors/rad_manager/handlers.rs b/node/src/actors/rad_manager/handlers.rs index 2d3b57bd3..e9451069f 100644 --- a/node/src/actors/rad_manager/handlers.rs +++ b/node/src/actors/rad_manager/handlers.rs @@ -61,6 +61,7 @@ impl Handler for RadManager { active_wips.clone(), protocol_version, witnessing.clone(), + None, ) }) .map(|fut| { diff --git a/rad/src/lib.rs b/rad/src/lib.rs index 13fe1bd37..7507df37b 100644 --- a/rad/src/lib.rs +++ b/rad/src/lib.rs @@ -33,7 +33,7 @@ use crate::{ user_agents::UserAgent, }; use core::convert::From; -use std::collections::BTreeMap; +use std::{collections::BTreeMap, time::Duration}; use witnet_data_structures::proto::versioning::ProtocolVersion; use witnet_net::client::http::WitnetHttpBody; @@ -70,6 +70,7 @@ pub fn try_data_request( inputs_injection: Option<&[&str]>, witnessing: Option>, too_many_witnesses: bool, + timeout: Option, ) -> RADRequestExecutionReport { #[cfg(not(test))] let active_wips = current_active_wips(); @@ -115,6 +116,7 @@ pub fn try_data_request( active_wips.clone(), protocol_version, witnessing.clone().unwrap_or_default(), + timeout, ) }) .collect::>(), @@ -278,6 +280,7 @@ async fn http_response( context: &mut ReportContext, settings: RadonScriptExecutionSettings, client: Option, + timeout: Option, ) -> Result> { // Validate URL to make sure that we handle malformed URLs nicely before they hit any library if let Err(err) = url::Url::parse(&retrieve.url) { @@ -380,7 +383,7 @@ async fn http_response( request_builder = request_builder.headers(headers); let response = client - .send(request_builder) + .send(request_builder, timeout) .await .map_err(|x| RadError::HttpOther { message: x.to_string(), @@ -502,16 +505,17 @@ pub async fn run_retrieval_report( active_wips: ActiveWips, protocol_version: ProtocolVersion, client: Option, + timeout: Option, ) -> Result> { let context = &mut ReportContext::from_stage(Stage::Retrieval(RetrievalMetadata::default())); context.set_active_wips(active_wips); context.set_protocol_version(protocol_version); match retrieve.kind { - RADType::HttpGet => http_response(retrieve, context, settings, client).await, + RADType::HttpGet => http_response(retrieve, context, settings, client, timeout).await, RADType::Rng => rng_response(context, settings).await, - RADType::HttpPost => http_response(retrieve, context, settings, client).await, - RADType::HttpHead => http_response(retrieve, context, settings, client).await, + RADType::HttpPost => http_response(retrieve, context, settings, client, timeout).await, + RADType::HttpHead => http_response(retrieve, context, settings, client, timeout).await, _ => Err(RadError::UnknownRetrieval), } } @@ -521,6 +525,7 @@ pub async fn run_retrieval( retrieve: &RADRetrieve, active_wips: ActiveWips, protocol_version: ProtocolVersion, + timeout: Option, ) -> Result { // Disable all execution tracing features, as this is the best-effort version of this method run_retrieval_report( @@ -529,6 +534,7 @@ pub async fn run_retrieval( active_wips, protocol_version, None, + timeout, ) .await .map(RadonReport::into_inner) @@ -547,10 +553,11 @@ pub async fn run_paranoid_retrieval( active_wips: ActiveWips, protocol_version: ProtocolVersion, witnessing: WitnessingConfig, + timeout: Option, ) -> Result> { // We can skip paranoid checks for retrieval types that don't use networking (e.g. RNG) if !retrieve.kind.is_http() { - return run_retrieval_report(retrieve, settings, active_wips, protocol_version, None).await; + return run_retrieval_report(retrieve, settings, active_wips, protocol_version, None, timeout).await; } let futures: Result> = witnessing @@ -573,6 +580,7 @@ pub async fn run_paranoid_retrieval( active_wips.clone(), protocol_version, Some(client), + timeout, ) }) }) @@ -1690,6 +1698,7 @@ mod tests { None, None, false, + None, ); let tally_result = report.tally.into_inner(); @@ -1734,6 +1743,7 @@ mod tests { None, None, false, + None, ); let tally_result = report.tally.into_inner(); @@ -1787,6 +1797,7 @@ mod tests { None, None, false, + None, ); let tally_result = report.tally.into_inner(); @@ -1840,6 +1851,7 @@ mod tests { None, None, false, + None, ); let tally_result = report.tally.into_inner(); @@ -1893,6 +1905,7 @@ mod tests { None, None, false, + None, ); let tally_result = report.tally.into_inner(); @@ -1959,6 +1972,7 @@ mod tests { Some(&["1", "1", "error"]), None, false, + None, ); let tally_result = report.tally.into_inner(); @@ -2078,6 +2092,7 @@ mod tests { current_active_wips(), ProtocolVersion::guess(), Some(client), + None, ) .await; diff --git a/toolkit/src/cli/arguments.rs b/toolkit/src/cli/arguments.rs index 17c0f64b0..2011a63cf 100644 --- a/toolkit/src/cli/arguments.rs +++ b/toolkit/src/cli/arguments.rs @@ -29,6 +29,11 @@ pub(crate) struct TryDataRequest { help = "Whether to return the full execution trace, including partial results after each operator." )] pub full_trace: Option, + #[structopt( + long, + help = "Maximum amount of seconds to wait for a data source to respond." + )] + pub timeout: Option, } /// Easy derivation of `DecodeDataRequest` from `TryDataRequest` diff --git a/toolkit/src/cli/data_requests.rs b/toolkit/src/cli/data_requests.rs index 964bf1f1d..e54a5012f 100644 --- a/toolkit/src/cli/data_requests.rs +++ b/toolkit/src/cli/data_requests.rs @@ -1,6 +1,6 @@ //! Implementations of CLI methods related to Witnet data requests. -use std::{fs::File, io::Read, path::Path}; +use std::{fs::File, io::Read, path::Path, time::Duration}; use regex::Regex; @@ -43,9 +43,13 @@ pub(crate) fn try_from_args( args: arguments::TryDataRequest, ) -> Result { let full_trace = args.full_trace.unwrap_or(true); + let timeout: Option = if let Some(timeout) = args.timeout { + Some(Duration::from_secs(timeout)) + } else { + None + }; let request = decode_from_args(args.into())?.data_request; - - witnet_toolkit::data_requests::try_data_request(&request, full_trace) + witnet_toolkit::data_requests::try_data_request(&request, full_trace, timeout) } /// Extract the Protocol Buffers representation of a data request from a Solidity smart contract diff --git a/toolkit/src/data_requests.rs b/toolkit/src/data_requests.rs index 19c6b8e9a..17b8da258 100644 --- a/toolkit/src/data_requests.rs +++ b/toolkit/src/data_requests.rs @@ -1,4 +1,6 @@ //! Functions providing convenient utilities for working with Witnet data requests. +use std::time::Duration; + use witnet_data_structures::{ chain::{DataRequestOutput, RADRequest}, proto::ProtobufConvert, @@ -46,13 +48,14 @@ pub fn decode_rad_from_pb_bytes(pb_bytes: &[u8]) -> Result, ) -> Result { let settings = if full_trace { RadonScriptExecutionSettings::enable_all() } else { RadonScriptExecutionSettings::disable_all() }; - let report = witnet_rad::try_data_request(request, settings, None, None, false); + let report = witnet_rad::try_data_request(request, settings, None, None, false, timeout); Ok(report) }