Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion crates/tinytools-std/src/network/test_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub struct TestNetGate {
used: AtomicUsize,
local_only: bool,
disclosed: Mutex<Vec<(String, bool, bool)>>,
prepared: Mutex<Vec<String>>,
}

impl TestNetGate {
Expand All @@ -62,6 +63,7 @@ impl TestNetGate {
used: AtomicUsize::new(0),
local_only: false,
disclosed: Mutex::new(Vec::new()),
prepared: Mutex::new(Vec::new()),
})
}

Expand All @@ -73,13 +75,19 @@ impl TestNetGate {
used: AtomicUsize::new(0),
local_only: true,
disclosed: Mutex::new(Vec::new()),
prepared: Mutex::new(Vec::new()),
})
}

/// Everything the tool has disclosed so far: `(host, has_body, has_headers)`.
pub fn disclosed(&self) -> Vec<(String, bool, bool)> {
self.disclosed.lock().unwrap().clone()
}

/// The service names `prepare_client` was called with, in order.
pub fn prepared(&self) -> Vec<String> {
self.prepared.lock().unwrap().clone()
}
}

impl NetGate for TestNetGate {
Expand Down Expand Up @@ -114,9 +122,10 @@ impl NetGate for TestNetGate {

fn prepare_client(
&self,
_service: &str,
service: &str,
builder: reqwest::ClientBuilder,
) -> reqwest::ClientBuilder {
self.prepared.lock().unwrap().push(service.to_string());
builder
}

Expand Down
12 changes: 9 additions & 3 deletions crates/tinytools-std/src/network/web_fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,11 +361,13 @@ impl WebFetchTool {
// redirects by default, and a redirect target may be on a host
// outside the allowed-domains list. We surface 3xx responses to
// the caller so they can decide whether to refetch the new URL.
let client = match reqwest::Client::builder()
let builder = reqwest::Client::builder()
.timeout(Duration::from_secs(self.timeout_secs))
.user_agent(USER_AGENT)
.redirect(reqwest::redirect::Policy::none())
// Connect only to the vetted addresses; the URL keeps its host name.
.redirect(reqwest::redirect::Policy::none());
let builder = self.gate.prepare_client("tool.web_fetch", builder);
// Connect only to the vetted addresses; the URL keeps its host name.
let client = match builder
.resolve_to_addrs(&target.host, &target.addrs)
.build()
{
Expand Down Expand Up @@ -672,3 +674,7 @@ pub use tinytools::status::web_fetch_status as status_of;
#[cfg(test)]
#[path = "web_fetch_tests.rs"]
mod tests;

#[cfg(test)]
#[path = "web_fetch_proxy_tests.rs"]
mod proxy_tests;
36 changes: 36 additions & 0 deletions crates/tinytools-std/src/network/web_fetch_proxy_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! web_fetch builds its client through the host's `prepare_client`, so the
//! host's proxy settings apply to it as they do to http_request and curl.

#![allow(clippy::unwrap_used)]

use super::*;
use crate::network::test_support::{DEFAULT_LIMITS, TestHtml, TestNetGate, pinned_to_local};

#[tokio::test]
async fn web_fetch_builds_its_client_through_prepare_client() {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
let (mut socket, _) = listener.accept().await.unwrap();
let _ = socket.read(&mut [0u8; 8192]).await;
let _ = socket
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok")
.await;
});
let gate = TestNetGate::supervised();
let tool = WebFetchTool::new(
gate.clone(),
vec![],
None,
None,
DEFAULT_LIMITS,
Arc::new(TestHtml),
);
let result = tool
.fetch_validated(&pinned_to_local(&format!("http://{addr}/p")), 1_000, false)
.await
.unwrap();
assert!(!result.is_error, "{}", result.output());
assert_eq!(gate.prepared(), vec!["tool.web_fetch".to_string()]);
}
Loading