ic_agent/agent/builder.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
use crate::{
agent::{agent_config::AgentConfig, Agent},
AgentError, Identity, NonceFactory, NonceGenerator,
};
use std::sync::Arc;
use super::{route_provider::RouteProvider, HttpService};
/// A builder for an [`Agent`].
#[derive(Default)]
pub struct AgentBuilder {
config: AgentConfig,
}
impl AgentBuilder {
/// Create an instance of [Agent] with the information from this builder.
pub fn build(self) -> Result<Agent, AgentError> {
Agent::new(self.config)
}
/// Set the dynamic transport layer for the [`Agent`], performing continuous discovery of the API boundary nodes
/// and routing traffic via them based on latency. Cannot be set together with `with_route_provider`.
///
/// See [`DynamicRouteProvider`](super::route_provider::DynamicRouteProvider) if more customization is needed such as polling intervals.
pub async fn with_background_dynamic_routing(mut self) -> Self {
assert!(
self.config.route_provider.is_none(),
"with_background_dynamic_routing cannot be called with with_route_provider"
);
self.config.background_dynamic_routing = true;
self
}
/// Set the URL of the [`Agent`]. Either this or `with_route_provider` must be called (but not both).
pub fn with_url<S: Into<String>>(mut self, url: S) -> Self {
assert!(
self.config.route_provider.is_none(),
"with_url cannot be called with with_route_provider"
);
self.config.url = Some(url.into().parse().unwrap());
self
}
/// Add a `NonceFactory` to this Agent. By default, no nonce is produced.
pub fn with_nonce_factory(self, nonce_factory: NonceFactory) -> AgentBuilder {
self.with_nonce_generator(nonce_factory)
}
/// Same as [`Self::with_nonce_factory`], but for any `NonceGenerator` type
pub fn with_nonce_generator<N: 'static + NonceGenerator>(
self,
nonce_factory: N,
) -> AgentBuilder {
self.with_arc_nonce_generator(Arc::new(nonce_factory))
}
/// Same as [`Self::with_nonce_generator`], but reuses an existing `Arc`.
pub fn with_arc_nonce_generator(
mut self,
nonce_factory: Arc<dyn NonceGenerator>,
) -> AgentBuilder {
self.config.nonce_factory = Arc::new(nonce_factory);
self
}
/// Add an identity provider for signing messages. This is required.
pub fn with_identity<I>(self, identity: I) -> Self
where
I: 'static + Identity,
{
self.with_arc_identity(Arc::new(identity))
}
/// Same as [`Self::with_identity`], but reuses an existing box
pub fn with_boxed_identity(self, identity: Box<dyn Identity>) -> Self {
self.with_arc_identity(Arc::from(identity))
}
/// Same as [`Self::with_identity`], but reuses an existing `Arc`
pub fn with_arc_identity(mut self, identity: Arc<dyn Identity>) -> Self {
self.config.identity = identity;
self
}
/// Provides a _default_ ingress expiry. This is the delta that will be applied
/// at the time an update or query is made. The default expiry cannot be a
/// fixed system time. This is also used when checking certificate timestamps.
///
/// The timestamp corresponding to this duration may be rounded in order to reduce
/// cache misses. The current implementation rounds to the nearest minute if the
/// expiry is more than a minute, but this is not guaranteed.
pub fn with_ingress_expiry(mut self, ingress_expiry: std::time::Duration) -> Self {
self.config.ingress_expiry = ingress_expiry;
self
}
/// Allows disabling query signature verification. Query signatures improve resilience but require
/// a separate read-state call to fetch node keys.
pub fn with_verify_query_signatures(mut self, verify_query_signatures: bool) -> Self {
self.config.verify_query_signatures = verify_query_signatures;
self
}
/// Sets the maximum number of requests that the agent will make concurrently. The replica is configured
/// to only permit 50 concurrent requests per client. Set this value lower if you have multiple agents,
/// to avoid the slowdown of retrying any 429 errors.
pub fn with_max_concurrent_requests(mut self, max_concurrent_requests: usize) -> Self {
self.config.max_concurrent_requests = max_concurrent_requests;
self
}
/// Add a `RouteProvider` to this agent, to provide the URLs of boundary nodes.
pub fn with_route_provider(self, provider: impl RouteProvider + 'static) -> Self {
self.with_arc_route_provider(Arc::new(provider))
}
/// Same as [`Self::with_route_provider`], but reuses an existing `Arc`.
pub fn with_arc_route_provider(mut self, provider: Arc<dyn RouteProvider>) -> Self {
assert!(
!self.config.background_dynamic_routing,
"with_background_dynamic_routing cannot be called with with_route_provider"
);
assert!(
self.config.url.is_none(),
"with_url cannot be called with with_route_provider"
);
self.config.route_provider = Some(provider);
self
}
/// Provide a pre-configured HTTP client to use. Use this to set e.g. HTTP timeouts or proxy configuration.
pub fn with_http_client(mut self, client: reqwest::Client) -> Self {
assert!(
self.config.http_service.is_none(),
"with_arc_http_middleware cannot be called with with_http_client"
);
self.config.client = Some(client);
self
}
/// Provide a custom `reqwest`-compatible HTTP service, e.g. to add per-request headers for custom boundary nodes.
/// Most users will not need this and should use `with_http_client`. Cannot be called with `with_http_client`.
///
/// The trait is automatically implemented for any `tower::Service` impl matching the one `reqwest::Client` uses,
/// including `reqwest-middleware`. This is a low-level interface, and direct implementations must provide all automatic retry logic.
pub fn with_arc_http_middleware(mut self, service: Arc<dyn HttpService>) -> Self {
assert!(
self.config.client.is_none(),
"with_arc_http_middleware cannot be called with with_http_client"
);
self.config.http_service = Some(service);
self
}
/// Retry up to the specified number of times upon encountering underlying TCP errors.
pub fn with_max_tcp_error_retries(mut self, retries: usize) -> Self {
self.config.max_tcp_error_retries = retries;
self
}
/// Don't accept HTTP bodies any larger than `max_size` bytes.
pub fn with_max_response_body_size(mut self, max_size: usize) -> Self {
self.config.max_response_body_size = Some(max_size);
self
}
/// Set the maximum time to wait for a response from the replica.
pub fn with_max_polling_time(mut self, max_polling_time: std::time::Duration) -> Self {
self.config.max_polling_time = max_polling_time;
self
}
}