add configurable RRDP snapshot and delta timeouts
This commit is contained in:
@@ -8,6 +8,11 @@ ccr-out = "/output/result.ccr"
|
||||
run-timeout-secs = 1800
|
||||
require-complete = true
|
||||
ta-refresh-policy = "allow-valid-cache"
|
||||
# Notification/TA requests use http-timeout; RRDP data bodies have independent
|
||||
# per-resource limits and are selected automatically within the same run.
|
||||
http-timeout-secs = 10
|
||||
rrdp-snapshot-timeout-secs = 600
|
||||
rrdp-delta-timeout-secs = 180
|
||||
# Set false to ignore all constraints files, including adjacent auto-discovery.
|
||||
ta-constraints-enabled = true
|
||||
log-level = "info"
|
||||
|
||||
@@ -64,7 +64,9 @@ The remaining common keys and defaults are:
|
||||
| Key | Default |
|
||||
| --- | ---: |
|
||||
| `max-ca-depth` | 64 |
|
||||
| `http-timeout-secs` | 300 (0 is normalized to 1) |
|
||||
| `http-timeout-secs` | 10 (0 is normalized to 1) |
|
||||
| `rrdp-snapshot-timeout-secs` | 600 (0 is normalized to 1) |
|
||||
| `rrdp-delta-timeout-secs` | 180 (0 is normalized to 1) |
|
||||
| `http-root-cert` | `[]` |
|
||||
| `parallel-max-repo-sync-workers-global` | 8 |
|
||||
| `parallel-phase2-object-workers` | 8 |
|
||||
|
||||
@@ -55,7 +55,9 @@ those choices do not change the built-in defaults.
|
||||
| `ta-constraints-enabled` | Boolean | true | Apply explicit or adjacent TA constraints files. False skips all constraints loading and enforcement, including adjacent discovery; unused missing or malformed files are ignored. |
|
||||
| `ta-refresh-policy` | String | `allow-valid-cache` | On automatic TA download failure, permit a revalidated, usable cached certificate. `require-fresh` prohibits fallback. Does not replace fixed TA files. |
|
||||
| `max-ca-depth` | Nonnegative integer | 64 | Maximum CA validation-tree depth. Zero is accepted by configuration parsing, but is a depth limit, not unlimited traversal. Usually keep 64. |
|
||||
| `http-timeout-secs` | Nonnegative integer, seconds | 300 | HTTP fetch timeout. Zero is normalized to one second, not unlimited. Does not set the whole-run or rsync timeout. |
|
||||
| `http-timeout-secs` | Nonnegative integer, seconds | 10 | Timeout for RRDP notification, TA/TAL and other small HTTP requests. Snapshot/delta bodies use their resource-specific settings below. Zero is normalized to one second, not unlimited. Does not set the whole-run or rsync timeout. |
|
||||
| `rrdp-snapshot-timeout-secs` | Nonnegative integer, seconds | 600 | Timeout for downloading an RRDP snapshot response body. Zero is normalized to one second. Selected by the RRDP resource type, including after same-origin redirects. |
|
||||
| `rrdp-delta-timeout-secs` | Nonnegative integer, seconds | 180 | Timeout for downloading an RRDP delta response body. Zero is normalized to one second. Each delta resource uses this value; it is independent of the validation run's overall timeout. |
|
||||
| `http-root-cert` | Array of path strings | `[]` | Additional HTTP TLS root certificates in PEM format. Every file must be readable and parseable. Empty means no custom additions; system TLS trust remains. These are not RPKI TA certificates. |
|
||||
| `parallel-max-repo-sync-workers-global` | Positive integer | 8 | Global repository-sync worker count. Zero is rejected. |
|
||||
| `parallel-phase2-object-workers` | Positive integer | 8 | Phase-2 object-processing worker count. Zero is rejected. |
|
||||
@@ -279,7 +281,9 @@ TA refresh policy does not make an anchor with an explicit ta automatic.
|
||||
log-format = "json"
|
||||
log-level = "debug"
|
||||
log-progress-interval-secs = 0
|
||||
http-timeout-secs = 600
|
||||
http-timeout-secs = 10 # notification, TA/TAL and other small HTTP requests
|
||||
rrdp-snapshot-timeout-secs = 600
|
||||
rrdp-delta-timeout-secs = 180
|
||||
http-root-cert = ["tls/internal-root.pem"]
|
||||
parallel-phase2-object-workers = 4
|
||||
parallel-max-repo-sync-workers-global = 4
|
||||
@@ -290,6 +294,10 @@ parallel-repo-worker-queue-capacity = 1024
|
||||
Replace existing top-level settings; do not add duplicate keys. Prepare the
|
||||
custom TLS PEM file first, or keep [] when no extra roots are needed. This does
|
||||
not change the TAL public key, relax TA validation or disable system TLS checks.
|
||||
The snapshot and delta timeouts are selected per RRDP resource, not per
|
||||
validation invocation: an `auto` run may use the snapshot value for one
|
||||
repository and the delta value for another. RRDP notification and TA/TAL HTTP
|
||||
requests continue to use `http-timeout-secs`.
|
||||
|
||||
### 8.4 Validation policy and rsync transport: top-level fragment
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ shutdown-grace-secs = 30
|
||||
# Common settings: these choices are explicit, not all built-in defaults.
|
||||
ta-refresh-policy = "allow-valid-cache"
|
||||
max-ca-depth = 64
|
||||
http-timeout-secs = 300
|
||||
http-timeout-secs = 10
|
||||
http-root-cert = [] # optional: ["tls/internal-root.pem"]
|
||||
parallel-max-repo-sync-workers-global = 8
|
||||
parallel-phase2-object-workers = 8
|
||||
|
||||
@@ -8,7 +8,7 @@ shutdown-grace-secs = 30
|
||||
# Common settings: these choices are explicit, not all built-in defaults.
|
||||
ta-refresh-policy = "allow-valid-cache"
|
||||
max-ca-depth = 64
|
||||
http-timeout-secs = 300
|
||||
http-timeout-secs = 10
|
||||
http-root-cert = [] # optional: ["tls/internal-root.pem"]
|
||||
parallel-max-repo-sync-workers-global = 8
|
||||
parallel-phase2-object-workers = 8
|
||||
|
||||
@@ -8,7 +8,9 @@ publish-dir = "publish/full"
|
||||
# Common settings: these choices are explicit, not all built-in defaults.
|
||||
ta-refresh-policy = "allow-valid-cache"
|
||||
max-ca-depth = 64
|
||||
http-timeout-secs = 300
|
||||
http-timeout-secs = 10
|
||||
rrdp-snapshot-timeout-secs = 600
|
||||
rrdp-delta-timeout-secs = 180
|
||||
http-root-cert = [] # optional: ["tls/internal-root.pem"]
|
||||
parallel-max-repo-sync-workers-global = 8
|
||||
parallel-phase2-object-workers = 8
|
||||
|
||||
@@ -21,7 +21,7 @@ rsync-scope = "module-root" # "host" or "publication-point"
|
||||
|
||||
ta-refresh-policy = "allow-valid-cache"
|
||||
max-ca-depth = 64
|
||||
http-timeout-secs = 300
|
||||
http-timeout-secs = 10
|
||||
http-root-cert = []
|
||||
parallel-max-repo-sync-workers-global = 8
|
||||
parallel-phase2-object-workers = 8
|
||||
|
||||
@@ -28,6 +28,8 @@ pub struct Config {
|
||||
pub ta_constraints_enabled: bool,
|
||||
pub max_ca_depth: usize,
|
||||
pub http_timeout_secs: u64,
|
||||
pub rrdp_snapshot_timeout_secs: u64,
|
||||
pub rrdp_delta_timeout_secs: u64,
|
||||
pub http_root_cert: Vec<PathBuf>,
|
||||
pub parallel_max_repo_sync_workers_global: usize,
|
||||
pub parallel_phase2_object_workers: usize,
|
||||
@@ -117,7 +119,11 @@ impl Default for Config {
|
||||
ta_refresh_policy: "allow-valid-cache".into(),
|
||||
ta_constraints_enabled: true,
|
||||
max_ca_depth: 64,
|
||||
http_timeout_secs: 300,
|
||||
http_timeout_secs: 10,
|
||||
rrdp_snapshot_timeout_secs:
|
||||
crate::repository::fetch::http::DEFAULT_RRDP_SNAPSHOT_TIMEOUT_SECS,
|
||||
rrdp_delta_timeout_secs:
|
||||
crate::repository::fetch::http::DEFAULT_RRDP_DELTA_TIMEOUT_SECS,
|
||||
http_root_cert: Vec::new(),
|
||||
parallel_max_repo_sync_workers_global: 8,
|
||||
parallel_phase2_object_workers: 8,
|
||||
@@ -356,6 +362,15 @@ impl Config {
|
||||
normalizations.push(json!({"field":"http-timeout-secs","input":0,"effective":1}));
|
||||
config.http_timeout_secs = 1;
|
||||
}
|
||||
if config.rrdp_snapshot_timeout_secs < 1 {
|
||||
normalizations
|
||||
.push(json!({"field":"rrdp-snapshot-timeout-secs","input":0,"effective":1}));
|
||||
config.rrdp_snapshot_timeout_secs = 1;
|
||||
}
|
||||
if config.rrdp_delta_timeout_secs < 1 {
|
||||
normalizations.push(json!({"field":"rrdp-delta-timeout-secs","input":0,"effective":1}));
|
||||
config.rrdp_delta_timeout_secs = 1;
|
||||
}
|
||||
if config.parallel_repo_worker_queue_capacity < 1024 {
|
||||
normalizations.push(json!({"field":"parallel-repo-worker-queue-capacity","input":config.parallel_repo_worker_queue_capacity,"effective":1024}));
|
||||
config.parallel_repo_worker_queue_capacity = 1024;
|
||||
@@ -618,6 +633,8 @@ resource-validation-mode = "rfc6487"
|
||||
validation-time = "2026-09-22T08:00:00+08:00"
|
||||
sync-preference = "rsync-only"
|
||||
rsync-timeout-secs = 47
|
||||
rrdp-snapshot-timeout-secs = 720
|
||||
rrdp-delta-timeout-secs = 90
|
||||
rsync-command = {:?}
|
||||
rsync-mirror-root = "mirror"
|
||||
rsync-scope = "publication-point"
|
||||
@@ -652,6 +669,8 @@ tal = {:?}
|
||||
);
|
||||
assert_eq!(config.sync_preference().unwrap(), SyncPreference::RsyncOnly);
|
||||
assert_eq!(config.rsync_timeout_secs, 47);
|
||||
assert_eq!(config.rrdp_snapshot_timeout_secs, 720);
|
||||
assert_eq!(config.rrdp_delta_timeout_secs, 90);
|
||||
assert_eq!(config.rsync_scope, RsyncScopePolicy::PublicationPoint);
|
||||
assert_eq!(
|
||||
config.effective_validation_time().unwrap().unix_timestamp(),
|
||||
@@ -662,6 +681,8 @@ tal = {:?}
|
||||
assert_eq!(printed["effective"]["resource-validation-mode"], "rfc6487");
|
||||
assert_eq!(printed["effective"]["sync-preference"], "rsync-only");
|
||||
assert_eq!(printed["effective"]["rsync-timeout-secs"], 47);
|
||||
assert_eq!(printed["effective"]["rrdp-snapshot-timeout-secs"], 720);
|
||||
assert_eq!(printed["effective"]["rrdp-delta-timeout-secs"], 90);
|
||||
assert_eq!(
|
||||
printed["effective"]["validation-time"],
|
||||
"2026-09-22T00:00:00Z"
|
||||
@@ -689,6 +710,51 @@ tal = {:?}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rrdp_timeout_defaults_are_distinct_and_zero_values_are_normalized() {
|
||||
let fixture = crate::test_support::synthetic_repository();
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let config_path = directory.path().join("validate.toml");
|
||||
std::fs::write(
|
||||
&config_path,
|
||||
format!(
|
||||
r#"
|
||||
config-version = 1
|
||||
mode = "validate"
|
||||
out = "out"
|
||||
rrdp-snapshot-timeout-secs = 0
|
||||
rrdp-delta-timeout-secs = 0
|
||||
|
||||
[[anchors]]
|
||||
id = "custom"
|
||||
tal = {:?}
|
||||
"#,
|
||||
fixture.tal().to_string_lossy(),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (config, printed) = Config::load(&config_path, "validate").unwrap();
|
||||
assert_eq!(Config::default().http_timeout_secs, 10);
|
||||
assert_eq!(Config::default().rrdp_snapshot_timeout_secs, 600);
|
||||
assert_eq!(Config::default().rrdp_delta_timeout_secs, 180);
|
||||
assert_eq!(config.rrdp_snapshot_timeout_secs, 1);
|
||||
assert_eq!(config.rrdp_delta_timeout_secs, 1);
|
||||
assert_eq!(printed["effective"]["rrdp-snapshot-timeout-secs"], 1);
|
||||
assert_eq!(printed["effective"]["rrdp-delta-timeout-secs"], 1);
|
||||
let normalizations = printed["normalizations"].as_array().unwrap();
|
||||
assert!(
|
||||
normalizations
|
||||
.iter()
|
||||
.any(|item| item["field"] == "rrdp-snapshot-timeout-secs")
|
||||
);
|
||||
assert!(
|
||||
normalizations
|
||||
.iter()
|
||||
.any(|item| item["field"] == "rrdp-delta-timeout-secs")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constraints_selection_disable_override_and_reload() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
|
||||
@@ -128,6 +128,8 @@ fn execute(config: &Config, run_id: &str) -> Result<i32, String> {
|
||||
strict_policy: Some(config.strict),
|
||||
resource_validation_mode: Some(config.resource_validation_mode),
|
||||
disable_rrdp: sync_preference == SyncPreference::RsyncOnly,
|
||||
rrdp_snapshot_timeout_secs: Some(config.rrdp_snapshot_timeout_secs),
|
||||
rrdp_delta_timeout_secs: Some(config.rrdp_delta_timeout_secs),
|
||||
rsync_command: config.rsync_command.clone(),
|
||||
rsync_timeout_secs: config.rsync_timeout_secs,
|
||||
rsync_mirror_root: config.rsync_mirror_root.clone(),
|
||||
|
||||
@@ -28,6 +28,9 @@ pub fn with_scoped_http_timeout_override<R>(timeout: Duration, f: impl FnOnce()
|
||||
/// Default User-Agent sent with every outgoing HTTP request (RRDP
|
||||
/// notification/snapshot/delta and TAL/TA downloads).
|
||||
pub const DEFAULT_HTTP_USER_AGENT: &str = "panda-rpki/0.2";
|
||||
pub const DEFAULT_LARGE_BODY_TIMEOUT_SECS: u64 = 180;
|
||||
pub const DEFAULT_RRDP_SNAPSHOT_TIMEOUT_SECS: u64 = 600;
|
||||
pub const DEFAULT_RRDP_DELTA_TIMEOUT_SECS: u64 = 180;
|
||||
|
||||
/// Environment variable that overrides the HTTP User-Agent. Unset, empty, or
|
||||
/// values containing characters that are illegal in a header value fall back
|
||||
@@ -53,8 +56,12 @@ pub struct HttpFetcherConfig {
|
||||
pub connect_timeout: Duration,
|
||||
/// Short timeout used for connection establishment and small metadata objects.
|
||||
pub timeout: Duration,
|
||||
/// Larger timeout used for RRDP snapshot / delta bodies.
|
||||
/// Generic large-body timeout, retained for untyped HTTP callers.
|
||||
pub large_body_timeout: Duration,
|
||||
/// Timeout for RRDP snapshot response bodies.
|
||||
pub rrdp_snapshot_timeout: Duration,
|
||||
/// Timeout for RRDP delta response bodies.
|
||||
pub rrdp_delta_timeout: Duration,
|
||||
pub user_agent: String,
|
||||
/// Extra PEM trust anchors for HTTPS transport tests or controlled endpoints.
|
||||
pub extra_root_certificates_pem: Vec<Vec<u8>>,
|
||||
@@ -65,7 +72,9 @@ impl Default for HttpFetcherConfig {
|
||||
Self {
|
||||
connect_timeout: Duration::from_secs(15),
|
||||
timeout: Duration::from_secs(30),
|
||||
large_body_timeout: Duration::from_secs(180),
|
||||
large_body_timeout: Duration::from_secs(DEFAULT_LARGE_BODY_TIMEOUT_SECS),
|
||||
rrdp_snapshot_timeout: Duration::from_secs(DEFAULT_RRDP_SNAPSHOT_TIMEOUT_SECS),
|
||||
rrdp_delta_timeout: Duration::from_secs(DEFAULT_RRDP_DELTA_TIMEOUT_SECS),
|
||||
user_agent: resolve_http_user_agent(std::env::var(HTTP_USER_AGENT_ENV).ok()),
|
||||
extra_root_certificates_pem: Vec::new(),
|
||||
}
|
||||
@@ -83,15 +92,22 @@ pub struct BlockingHttpFetcher {
|
||||
large_body_client: Client,
|
||||
retry_short_client: Client,
|
||||
rrdp_short_client: Client,
|
||||
rrdp_large_body_client: Client,
|
||||
rrdp_snapshot_client: Client,
|
||||
rrdp_delta_client: Client,
|
||||
rrdp_retry_short_client: Client,
|
||||
short_timeout: Duration,
|
||||
large_body_timeout: Duration,
|
||||
rrdp_snapshot_timeout: Duration,
|
||||
rrdp_delta_timeout: Duration,
|
||||
}
|
||||
|
||||
impl BlockingHttpFetcher {
|
||||
pub fn new(config: HttpFetcherConfig) -> Result<Self, String> {
|
||||
let short_timeout = config.timeout;
|
||||
let rrdp_snapshot_timeout = config.rrdp_snapshot_timeout;
|
||||
let rrdp_delta_timeout = config.rrdp_delta_timeout;
|
||||
// Preserve the existing timeout behavior for untyped HTTP callers.
|
||||
// RRDP's typed fetch path below uses its exact snapshot/delta profile.
|
||||
let large_body_timeout = std::cmp::max(config.large_body_timeout, config.timeout);
|
||||
let connect_timeout = std::cmp::min(config.connect_timeout, config.timeout);
|
||||
let short_client = Self::client_builder(
|
||||
@@ -126,10 +142,18 @@ impl BlockingHttpFetcher {
|
||||
)?
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rrdp_large_body_client = Self::rrdp_client_builder(
|
||||
let rrdp_snapshot_client = Self::rrdp_client_builder(
|
||||
&config,
|
||||
connect_timeout,
|
||||
large_body_timeout,
|
||||
rrdp_snapshot_timeout,
|
||||
config.user_agent.clone(),
|
||||
)?
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rrdp_delta_client = Self::rrdp_client_builder(
|
||||
&config,
|
||||
connect_timeout,
|
||||
rrdp_delta_timeout,
|
||||
config.user_agent.clone(),
|
||||
)?
|
||||
.build()
|
||||
@@ -147,10 +171,13 @@ impl BlockingHttpFetcher {
|
||||
large_body_client,
|
||||
retry_short_client,
|
||||
rrdp_short_client,
|
||||
rrdp_large_body_client,
|
||||
rrdp_snapshot_client,
|
||||
rrdp_delta_client,
|
||||
rrdp_retry_short_client,
|
||||
short_timeout,
|
||||
large_body_timeout,
|
||||
rrdp_snapshot_timeout,
|
||||
rrdp_delta_timeout,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -300,18 +327,27 @@ impl BlockingHttpFetcher {
|
||||
}
|
||||
}
|
||||
|
||||
fn rrdp_client_for_uri(&self, uri: &str) -> (&Client, &'static str, Duration) {
|
||||
fn rrdp_client_for_resource(
|
||||
&self,
|
||||
resource: RrdpResourceKind,
|
||||
) -> (&Client, &'static str, Duration) {
|
||||
if let Some(timeout) = HTTP_TIMEOUT_OVERRIDE.with(|cell| *cell.borrow()) {
|
||||
return (&self.rrdp_retry_short_client, "retry_short", timeout);
|
||||
}
|
||||
if uses_large_body_timeout(uri) {
|
||||
(
|
||||
&self.rrdp_large_body_client,
|
||||
"large_body",
|
||||
self.large_body_timeout,
|
||||
)
|
||||
} else {
|
||||
(&self.rrdp_short_client, "short", self.short_timeout)
|
||||
match resource {
|
||||
RrdpResourceKind::Notification => {
|
||||
(&self.rrdp_short_client, "short", self.short_timeout)
|
||||
}
|
||||
RrdpResourceKind::Snapshot => (
|
||||
&self.rrdp_snapshot_client,
|
||||
"rrdp_snapshot",
|
||||
self.rrdp_snapshot_timeout,
|
||||
),
|
||||
RrdpResourceKind::Delta => (
|
||||
&self.rrdp_delta_client,
|
||||
"rrdp_delta",
|
||||
self.rrdp_delta_timeout,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +372,7 @@ impl BlockingHttpFetcher {
|
||||
}
|
||||
|
||||
for redirects in 0..=MAX_RRDP_REDIRECTS {
|
||||
let (client, profile, timeout) = self.rrdp_client_for_uri(current.as_str());
|
||||
let (client, profile, timeout) = self.rrdp_client_for_resource(resource);
|
||||
let response = client
|
||||
.get(current.clone())
|
||||
.send()
|
||||
@@ -729,6 +765,8 @@ mod tests {
|
||||
let http = BlockingHttpFetcher::new(HttpFetcherConfig {
|
||||
timeout: Duration::from_secs(3),
|
||||
large_body_timeout: Duration::from_secs(9),
|
||||
rrdp_snapshot_timeout: Duration::from_secs(9),
|
||||
rrdp_delta_timeout: Duration::from_secs(6),
|
||||
..HttpFetcherConfig::default()
|
||||
})
|
||||
.expect("http");
|
||||
@@ -744,6 +782,68 @@ mod tests {
|
||||
assert_eq!(timeout_large, Duration::from_secs(9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_rrdp_resource_selects_its_own_timeout_independent_of_uri() {
|
||||
let http = BlockingHttpFetcher::new(HttpFetcherConfig {
|
||||
timeout: Duration::from_secs(10),
|
||||
rrdp_snapshot_timeout: Duration::from_secs(600),
|
||||
rrdp_delta_timeout: Duration::from_secs(180),
|
||||
..HttpFetcherConfig::default()
|
||||
})
|
||||
.expect("http");
|
||||
|
||||
let (_, notification_profile, notification_timeout) =
|
||||
http.rrdp_client_for_resource(RrdpResourceKind::Notification);
|
||||
let (_, snapshot_profile, snapshot_timeout) =
|
||||
http.rrdp_client_for_resource(RrdpResourceKind::Snapshot);
|
||||
let (_, delta_profile, delta_timeout) =
|
||||
http.rrdp_client_for_resource(RrdpResourceKind::Delta);
|
||||
|
||||
assert_eq!(notification_profile, "short");
|
||||
assert_eq!(notification_timeout, Duration::from_secs(10));
|
||||
assert_eq!(snapshot_profile, "rrdp_snapshot");
|
||||
assert_eq!(snapshot_timeout, Duration::from_secs(600));
|
||||
assert_eq!(delta_profile, "rrdp_delta");
|
||||
assert_eq!(delta_timeout, Duration::from_secs(180));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rrdp_snapshot_and_delta_requests_enforce_independent_timeouts() {
|
||||
let listener = TcpListener::bind(("127.0.0.1", 0)).expect("bind");
|
||||
let addr = listener.local_addr().expect("addr");
|
||||
let server = thread::spawn(move || {
|
||||
for _ in 0..2 {
|
||||
let (mut stream, _) = listener.accept().expect("accept");
|
||||
let mut request = [0u8; 1024];
|
||||
let _ = stream.read(&mut request);
|
||||
thread::sleep(StdDuration::from_millis(250));
|
||||
let response =
|
||||
b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok";
|
||||
let _ = stream.write_all(response);
|
||||
}
|
||||
});
|
||||
let uri = format!("http://{addr}/resource.xml");
|
||||
let origin = RrdpOrigin::parse(&uri, RrdpResourceKind::Notification).expect("origin");
|
||||
let http = BlockingHttpFetcher::new(HttpFetcherConfig {
|
||||
timeout: Duration::from_secs(3),
|
||||
rrdp_snapshot_timeout: Duration::from_secs(3),
|
||||
rrdp_delta_timeout: Duration::from_millis(100),
|
||||
..HttpFetcherConfig::default()
|
||||
})
|
||||
.expect("http");
|
||||
|
||||
assert_eq!(
|
||||
http.fetch_rrdp(RrdpResourceKind::Snapshot, &uri, &origin)
|
||||
.expect("snapshot response should fit its timeout"),
|
||||
b"ok"
|
||||
);
|
||||
assert!(
|
||||
http.fetch_rrdp(RrdpResourceKind::Delta, &uri, &origin)
|
||||
.is_err()
|
||||
);
|
||||
server.join().expect("server thread");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_http_user_agent_falls_back_to_default() {
|
||||
assert_eq!(resolve_http_user_agent(None), DEFAULT_HTTP_USER_AGENT);
|
||||
|
||||
@@ -112,13 +112,19 @@ pub(crate) fn run_config(args: RunConfig) -> Result<(), String> {
|
||||
let validation_started = std::time::Instant::now();
|
||||
crate::logging::observer::phase("runtime_setup");
|
||||
let collect_current_repo_objects = false;
|
||||
let mut http_config = HttpFetcherConfig {
|
||||
timeout: std::time::Duration::from_secs(args.http_timeout_secs.max(1)),
|
||||
extra_root_certificates_pem: http_root_certificates_pem.clone(),
|
||||
..HttpFetcherConfig::default()
|
||||
};
|
||||
if let Some(timeout) = args.rrdp_snapshot_timeout_secs {
|
||||
http_config.rrdp_snapshot_timeout = std::time::Duration::from_secs(timeout.max(1));
|
||||
}
|
||||
if let Some(timeout) = args.rrdp_delta_timeout_secs {
|
||||
http_config.rrdp_delta_timeout = std::time::Duration::from_secs(timeout.max(1));
|
||||
}
|
||||
let out = if let Some(dir) = args.rsync_local_dir.as_ref() {
|
||||
let http = BlockingHttpFetcher::new(HttpFetcherConfig {
|
||||
timeout: std::time::Duration::from_secs(args.http_timeout_secs.max(1)),
|
||||
extra_root_certificates_pem: http_root_certificates_pem.clone(),
|
||||
..HttpFetcherConfig::default()
|
||||
})
|
||||
.map_err(|e| e.to_string())?;
|
||||
let http = BlockingHttpFetcher::new(http_config.clone()).map_err(|e| e.to_string())?;
|
||||
let rsync = LocalDirRsyncFetcher::new(dir);
|
||||
run_online_validation_with_fetchers(
|
||||
Arc::clone(&store),
|
||||
@@ -132,12 +138,7 @@ pub(crate) fn run_config(args: RunConfig) -> Result<(), String> {
|
||||
timing.as_ref().map(|(_, t)| t),
|
||||
)?
|
||||
} else {
|
||||
let http = BlockingHttpFetcher::new(HttpFetcherConfig {
|
||||
timeout: std::time::Duration::from_secs(args.http_timeout_secs.max(1)),
|
||||
extra_root_certificates_pem: http_root_certificates_pem.clone(),
|
||||
..HttpFetcherConfig::default()
|
||||
})
|
||||
.map_err(|e| e.to_string())?;
|
||||
let http = BlockingHttpFetcher::new(http_config).map_err(|e| e.to_string())?;
|
||||
let rsync = SystemRsyncFetcher::new(SystemRsyncConfig {
|
||||
rsync_bin: args
|
||||
.rsync_command
|
||||
|
||||
@@ -79,6 +79,9 @@ pub struct RunConfig {
|
||||
pub rsync_command: Option<PathBuf>,
|
||||
|
||||
pub http_timeout_secs: u64,
|
||||
/// Per-resource RRDP response timeouts; None selects the fetcher default.
|
||||
pub rrdp_snapshot_timeout_secs: Option<u64>,
|
||||
pub rrdp_delta_timeout_secs: Option<u64>,
|
||||
pub http_root_cert_paths: Vec<PathBuf>,
|
||||
pub rsync_timeout_secs: u64,
|
||||
pub rsync_mirror_root: Option<PathBuf>,
|
||||
|
||||
Reference in New Issue
Block a user