Component: crates/sources (http.rs) | Severity: High (UX) | Status: Reproduced
The ureq::Agent is built with only redirect_auth_headers configured; ureq 3.3's default Timeouts are all None (verified in the vendored source), and no call site sets a per-request timeout. Any endpoint that accepts a TCP connection and never responds (half-dead registry, corporate middlebox, wedged raw.githubusercontent edge) blocks the whole command indefinitely - with a spinner on a TTY, in complete silence otherwise. search_all queries registries sequentially, so one hung registry also blocks the others.
Where: crates/sources/src/http.rs:301-312 (UreqClient::new).
Repro (observed): run a loopback listener that accepts and never replies (python3 -c 'import socket;s=socket.socket();s.bind(("127.0.0.1",8942));s.listen();c,_=s.accept();import time;time.sleep(3600)'), point any flow at it (e.g. a url entry, or an HTTP(S)_PROXY for skillfile search docker --no-interactive); the command never returns (killed by external timeout 12, exit 124).
Expected: bounded connect/read timeouts (e.g. 10s connect / 30s read, possibly env-overridable) so a dead endpoint degrades into the existing per-entry/per-registry error paths.
Actual: indefinite hang; no flag or env var to bound it.
Fix sketch: set timeout_connect and timeout_global (or per-request .config().timeout_*) on the agent config in UreqClient::new; map the timeout error to a clear "timed out fetching {url}" message.
Tests:
- Unit (
crates/sources/src/http.rs): spawn a std::net::TcpListener that accepts and stalls; call get_bytes against it; assert it returns Err within a bound (test with a generous outer deadline).
- No functional test needed beyond that (pyramid: this is a transport-layer property; one unit test pins it).
Component:
crates/sources(http.rs) | Severity: High (UX) | Status: ReproducedThe
ureq::Agentis built with onlyredirect_auth_headersconfigured; ureq 3.3's defaultTimeoutsare allNone(verified in the vendored source), and no call site sets a per-request timeout. Any endpoint that accepts a TCP connection and never responds (half-dead registry, corporate middlebox, wedged raw.githubusercontent edge) blocks the whole command indefinitely - with a spinner on a TTY, in complete silence otherwise.search_allqueries registries sequentially, so one hung registry also blocks the others.Where:
crates/sources/src/http.rs:301-312(UreqClient::new).Repro (observed): run a loopback listener that accepts and never replies (
python3 -c 'import socket;s=socket.socket();s.bind(("127.0.0.1",8942));s.listen();c,_=s.accept();import time;time.sleep(3600)'), point any flow at it (e.g. aurlentry, or an HTTP(S)_PROXY forskillfile search docker --no-interactive); the command never returns (killed by externaltimeout 12, exit 124).Expected: bounded connect/read timeouts (e.g. 10s connect / 30s read, possibly env-overridable) so a dead endpoint degrades into the existing per-entry/per-registry error paths.
Actual: indefinite hang; no flag or env var to bound it.
Fix sketch: set
timeout_connectandtimeout_global(or per-request.config().timeout_*) on the agent config inUreqClient::new; map the timeout error to a clear "timed out fetching {url}" message.Tests:
crates/sources/src/http.rs): spawn astd::net::TcpListenerthat accepts and stalls; callget_bytesagainst it; assert it returns Err within a bound (test with a generous outer deadline).