#[cfg(any(feature = "outbound-http", feature = "outbound-tcp"))]
use std::time::Duration;
use crate::Bucket;
#[cfg(any(feature = "outbound-http", feature = "outbound-tcp"))]
use crate::outbound;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Isolation {
Trusted,
Untrusted,
}
const DEFAULT_INIT_DEADLINE_MS: u64 = 5_000;
const DEFAULT_UNTRUSTED_INIT_DEADLINE_MS: u64 = 250;
const DEFAULT_REQUEST_DEADLINE_MS: u64 = 100;
pub(crate) const DEFAULT_MAX_MEMORY_BYTES: u64 = 64 << 20;
const DEFAULT_CHECKOUT_TIMEOUT_MS: u64 = 250;
const DEFAULT_MAX_REQUESTS_PER_INSTANCE: u64 = 1 << 16;
const TRUSTED_POOL_DEFAULT_CEIL: usize = 8;
fn default_trusted_pool_size() -> usize {
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
.clamp(1, TRUSTED_POOL_DEFAULT_CEIL)
}
#[cfg(feature = "outbound-http")]
const DEFAULT_OUTBOUND_CONNECT_TIMEOUT_MS: u64 = 2_000;
#[cfg(feature = "outbound-http")]
const MAX_OUTBOUND_CONNECT_TIMEOUT_MS: u64 = 10_000;
#[cfg(feature = "outbound-http")]
const DEFAULT_OUTBOUND_TOTAL_TIMEOUT_MS: u64 = 5_000;
#[cfg(feature = "outbound-http")]
const MAX_OUTBOUND_TOTAL_TIMEOUT_MS: u64 = 30_000;
#[cfg(feature = "outbound-http")]
const DEFAULT_OUTBOUND_MAX_RESPONSE_BYTES: u64 = 64 * 1024;
#[cfg(feature = "outbound-http")]
const MAX_OUTBOUND_MAX_RESPONSE_BYTES: u64 = 1 << 20;
#[cfg(feature = "outbound-http")]
const DEFAULT_OUTBOUND_MAX_CONCURRENT: u32 = 8;
#[cfg(feature = "outbound-http")]
const MAX_OUTBOUND_MAX_CONCURRENT: u32 = 64;
#[cfg(feature = "outbound-tcp")]
const DEFAULT_OUTBOUND_TCP_MAX_CONNECTIONS: u32 = 4;
#[cfg(feature = "outbound-tcp")]
const MAX_OUTBOUND_TCP_MAX_CONNECTIONS: u32 = 64;
#[cfg(feature = "outbound-tcp")]
const DEFAULT_OUTBOUND_TCP_IO_DEADLINE_MS: u64 = 5_000;
#[cfg(feature = "outbound-tcp")]
const MAX_OUTBOUND_TCP_IO_DEADLINE_MS: u64 = 30_000;
#[derive(Debug, Clone)]
pub struct LoadOptions {
pub isolation: Isolation,
pub init_deadline_ms: u64,
pub request_deadline_ms: u64,
pub max_memory_bytes: u64,
pub trusted_pool_size: usize,
pub checkout_timeout_ms: u64,
pub max_requests_per_instance: u64,
pub ratelimit_bucket: Option<Bucket>,
#[cfg(feature = "outbound-http")]
pub outbound_http: Option<outbound::OutboundPolicy>,
#[cfg(feature = "outbound-tcp")]
pub outbound_tcp: Option<outbound::OutboundTcpPolicy>,
#[doc(hidden)]
#[cfg(all(feature = "outbound-tcp", feature = "test-support"))]
pub outbound_tcp_static_resolver:
Option<std::collections::HashMap<String, Vec<std::net::IpAddr>>>,
pub config: std::collections::BTreeMap<String, String>,
#[cfg(feature = "fat-guest")]
pub wasi_minimal: bool,
}
impl Default for LoadOptions {
fn default() -> Self {
Self {
isolation: Isolation::Untrusted,
init_deadline_ms: DEFAULT_UNTRUSTED_INIT_DEADLINE_MS,
request_deadline_ms: DEFAULT_REQUEST_DEADLINE_MS,
max_memory_bytes: DEFAULT_MAX_MEMORY_BYTES,
trusted_pool_size: default_trusted_pool_size(),
checkout_timeout_ms: DEFAULT_CHECKOUT_TIMEOUT_MS,
max_requests_per_instance: DEFAULT_MAX_REQUESTS_PER_INSTANCE,
ratelimit_bucket: None,
#[cfg(feature = "outbound-http")]
outbound_http: None,
#[cfg(feature = "outbound-tcp")]
outbound_tcp: None,
#[cfg(all(feature = "outbound-tcp", feature = "test-support"))]
outbound_tcp_static_resolver: None,
config: std::collections::BTreeMap::new(),
#[cfg(feature = "fat-guest")]
wasi_minimal: false,
}
}
}
impl LoadOptions {
pub fn trusted() -> Self {
Self {
isolation: Isolation::Trusted,
init_deadline_ms: DEFAULT_INIT_DEADLINE_MS,
..Self::default()
}
}
pub fn untrusted() -> Self {
Self::default()
}
pub fn with_request_deadline_ms(mut self, ms: u64) -> Self {
self.request_deadline_ms = ms;
self
}
pub fn with_init_deadline_ms(mut self, ms: u64) -> Self {
self.init_deadline_ms = ms;
self
}
pub fn with_max_memory_bytes(mut self, bytes: u64) -> Self {
self.max_memory_bytes = bytes;
self
}
pub fn with_trusted_pool_size(mut self, n: usize) -> Self {
self.trusted_pool_size = n;
self
}
pub fn with_checkout_timeout_ms(mut self, ms: u64) -> Self {
self.checkout_timeout_ms = ms;
self
}
pub fn with_max_requests_per_instance(mut self, n: u64) -> Self {
self.max_requests_per_instance = n;
self
}
pub fn with_ratelimit_bucket(
mut self,
capacity: u64,
refill_tokens: u64,
refill_interval_ms: u64,
) -> Self {
self.ratelimit_bucket = Some(Bucket {
capacity,
refill_tokens,
refill_interval_ms,
});
self
}
pub fn with_config(mut self, config: std::collections::BTreeMap<String, String>) -> Self {
self.config = config;
self
}
#[cfg(feature = "outbound-http")]
#[allow(clippy::too_many_arguments)]
pub fn with_outbound_http(
mut self,
allow: Vec<outbound::AllowEntry>,
allow_private: Vec<String>,
connect_timeout_ms: Option<u64>,
total_timeout_ms: Option<u64>,
max_response_bytes: Option<u64>,
max_concurrent: Option<u32>,
) -> Self {
let clamp_ms = |v: Option<u64>, def: u64, max: u64| v.unwrap_or(def).clamp(1, max);
let allow_private = allow_private
.iter()
.filter_map(|c| c.parse::<ipnet::IpNet>().ok())
.collect();
self.outbound_http = Some(outbound::OutboundPolicy {
allow,
allow_private,
connect_timeout: Duration::from_millis(clamp_ms(
connect_timeout_ms,
DEFAULT_OUTBOUND_CONNECT_TIMEOUT_MS,
MAX_OUTBOUND_CONNECT_TIMEOUT_MS,
)),
total_timeout: Duration::from_millis(clamp_ms(
total_timeout_ms,
DEFAULT_OUTBOUND_TOTAL_TIMEOUT_MS,
MAX_OUTBOUND_TOTAL_TIMEOUT_MS,
)),
max_response_bytes: max_response_bytes
.unwrap_or(DEFAULT_OUTBOUND_MAX_RESPONSE_BYTES)
.clamp(1, MAX_OUTBOUND_MAX_RESPONSE_BYTES),
max_concurrent: max_concurrent
.unwrap_or(DEFAULT_OUTBOUND_MAX_CONCURRENT)
.clamp(1, MAX_OUTBOUND_MAX_CONCURRENT),
});
self
}
#[cfg(feature = "outbound-tcp")]
pub fn with_outbound_tcp(
mut self,
allow: Vec<outbound::TcpAllowEntry>,
allow_private: Vec<String>,
max_connections: Option<u32>,
io_deadline_ms: Option<u64>,
) -> Self {
let allow_private = allow_private
.iter()
.filter_map(|c| c.parse::<ipnet::IpNet>().ok())
.collect();
self.outbound_tcp = Some(outbound::OutboundTcpPolicy {
allow,
allow_private,
max_connections: max_connections
.unwrap_or(DEFAULT_OUTBOUND_TCP_MAX_CONNECTIONS)
.clamp(1, MAX_OUTBOUND_TCP_MAX_CONNECTIONS),
io_deadline: Duration::from_millis(
io_deadline_ms
.unwrap_or(DEFAULT_OUTBOUND_TCP_IO_DEADLINE_MS)
.clamp(1, MAX_OUTBOUND_TCP_IO_DEADLINE_MS),
),
});
self
}
#[cfg(feature = "fat-guest")]
pub fn with_wasi_minimal(mut self) -> Self {
self.wasi_minimal = true;
self
}
#[doc(hidden)]
#[cfg(all(feature = "outbound-tcp", feature = "test-support"))]
pub fn with_outbound_tcp_static_resolver(
mut self,
entries: Vec<(String, Vec<std::net::IpAddr>)>,
) -> Self {
self.outbound_tcp_static_resolver = Some(entries.into_iter().collect());
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn untrusted_init_deadline_is_tight_trusted_is_generous() {
assert_eq!(
LoadOptions::untrusted().init_deadline_ms,
DEFAULT_UNTRUSTED_INIT_DEADLINE_MS
);
assert_eq!(
LoadOptions::trusted().init_deadline_ms,
DEFAULT_INIT_DEADLINE_MS
);
assert!(
LoadOptions::untrusted().init_deadline_ms < LoadOptions::trusted().init_deadline_ms
);
}
#[cfg(feature = "fat-guest")]
#[test]
fn wasi_minimal_is_off_by_default_and_toggled_explicitly() {
assert!(!LoadOptions::untrusted().wasi_minimal);
assert!(!LoadOptions::trusted().wasi_minimal);
assert!(LoadOptions::untrusted().with_wasi_minimal().wasi_minimal);
}
}