Add a `HttpRepositoryBuilder` and move min_bytes_per_second
The `HttpRepository::new` method takes a lot of options that I
expect people will just set the default. This makes it easier
to ignore unset options.
Also, the `min_bytes_per_second` seems more like a setting that
we'd want set globally, rather than on a per-repository-method call.
Since this setting really only makes sense on `HttpRepository`,
I've also moved this setting into the builder.
diff --git a/src/client.rs b/src/client.rs
index 4815084..f306552 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -13,7 +13,7 @@
//! # use tuf::metadata::{RootMetadata, SignedMetadata, Role, MetadataPath,
//! # MetadataVersion};
//! # use tuf::interchange::Json;
-//! # use tuf::repository::{Repository, FileSystemRepository, HttpRepository};
+//! # use tuf::repository::{Repository, FileSystemRepository, HttpRepositoryBuilder};
//!
//! static TRUSTED_ROOT_KEY_IDS: &'static [&str] = &[
//! "diNfThTFm0PI8R-Bq7NztUIvZbZiaC_weJBgcqaHlWw=",
@@ -29,11 +29,12 @@
//!
//! let local = FileSystemRepository::<Json>::new(PathBuf::from("~/.rustup"))?;
//!
-//! let remote = HttpRepository::new(
+//! let remote = HttpRepositoryBuilder::new(
//! url::Url::parse("https://static.rust-lang.org/").unwrap(),
//! HttpClient::new(),
-//! Some("rustup/1.4.0".into()),
-//! None);
+//! )
+//! .user_agent_prefix("rustup/1.4.0")
+//! .build();
//!
//! let mut client = await!(Client::with_root_pinned(
//! &key_ids,
@@ -142,7 +143,6 @@
&root_path,
&root_version,
&config.max_root_size,
- config.min_bytes_per_second,
None,
))?;
@@ -173,7 +173,6 @@
&root_path,
&root_version,
&config.max_root_size,
- config.min_bytes_per_second,
None,
)) {
Ok(root) => root,
@@ -183,7 +182,6 @@
&root_path,
&root_version,
&config.max_root_size,
- config.min_bytes_per_second,
None,
))?;
@@ -249,7 +247,6 @@
&root_path,
&MetadataVersion::None,
&self.config.max_root_size,
- self.config.min_bytes_per_second,
None,
))?;
let latest_version = latest_root.version();
@@ -274,7 +271,6 @@
&root_path,
&version,
&self.config.max_root_size,
- self.config.min_bytes_per_second,
None,
))?;
@@ -312,7 +308,6 @@
×tamp_path,
&MetadataVersion::None,
&self.config.max_timestamp_size,
- self.config.min_bytes_per_second,
None,
))?;
@@ -358,7 +353,6 @@
&snapshot_path,
&version,
&snapshot_size,
- self.config.min_bytes_per_second,
Some((alg, value.clone())),
))?;
@@ -405,7 +399,6 @@
&targets_path,
&version,
&targets_size,
- self.config.min_bytes_per_second,
Some((alg, value.clone())),
))?;
@@ -451,7 +444,6 @@
await!(self.remote.fetch_target(
target,
&target_description,
- self.config.min_bytes_per_second,
))
}
@@ -526,7 +518,6 @@
delegation.role(),
&MetadataVersion::None,
&role_size,
- self.config.min_bytes_per_second(),
Some((alg, value.clone())),
));
@@ -537,7 +528,6 @@
delegation.role(),
&version,
&role_size,
- self.config.min_bytes_per_second(),
Some((alg, value.clone())),
)) {
Ok(m) => m,
@@ -613,7 +603,6 @@
/// let config = Config::default();
/// assert_eq!(config.max_root_size(), &Some(1024 * 1024));
/// assert_eq!(config.max_timestamp_size(), &Some(32 * 1024));
-/// assert_eq!(config.min_bytes_per_second(), 4096);
/// assert_eq!(config.max_delegation_depth(), 8);
/// let _: &DefaultTranslator = config.path_translator();
/// ```
@@ -624,7 +613,6 @@
{
max_root_size: Option<usize>,
max_timestamp_size: Option<usize>,
- min_bytes_per_second: u32,
max_delegation_depth: u32,
path_translator: T,
}
@@ -650,11 +638,6 @@
&self.max_timestamp_size
}
- /// The minimum bytes per second for a read to be considered good.
- pub fn min_bytes_per_second(&self) -> u32 {
- self.min_bytes_per_second
- }
-
/// The maximum number of steps used when walking the delegation graph.
pub fn max_delegation_depth(&self) -> u32 {
self.max_delegation_depth
@@ -671,7 +654,6 @@
Config {
max_root_size: Some(1024 * 1024),
max_timestamp_size: Some(32 * 1024),
- min_bytes_per_second: 4096,
max_delegation_depth: 8,
path_translator: DefaultTranslator::new(),
}
@@ -686,7 +668,6 @@
{
max_root_size: Option<usize>,
max_timestamp_size: Option<usize>,
- min_bytes_per_second: u32,
max_delegation_depth: u32,
path_translator: T,
}
@@ -700,7 +681,6 @@
Ok(Config {
max_root_size: self.max_root_size,
max_timestamp_size: self.max_timestamp_size,
- min_bytes_per_second: self.min_bytes_per_second,
max_delegation_depth: self.max_delegation_depth,
path_translator: self.path_translator,
})
@@ -718,12 +698,6 @@
self
}
- /// Set the minimum bytes per second for a read to be considered good.
- pub fn min_bytes_per_second(mut self, min: u32) -> Self {
- self.min_bytes_per_second = min;
- self
- }
-
/// Set the maximum number of steps used when walking the delegation graph.
pub fn max_delegation_depth(mut self, max: u32) -> Self {
self.max_delegation_depth = max;
@@ -738,7 +712,6 @@
ConfigBuilder {
max_root_size: self.max_root_size,
max_timestamp_size: self.max_timestamp_size,
- min_bytes_per_second: self.min_bytes_per_second,
max_delegation_depth: self.max_delegation_depth,
path_translator,
}
@@ -751,7 +724,6 @@
ConfigBuilder {
max_root_size: cfg.max_root_size,
max_timestamp_size: cfg.max_timestamp_size,
- min_bytes_per_second: cfg.min_bytes_per_second,
max_delegation_depth: cfg.max_delegation_depth,
path_translator: cfg.path_translator,
}
@@ -771,7 +743,6 @@
use chrono::prelude::*;
use futures::executor::block_on;
use lazy_static::lazy_static;
- use std::u32;
lazy_static! {
static ref KEYS: Vec<PrivateKey> = {
@@ -937,7 +908,6 @@
&MetadataPath::from_role(&Role::Root),
&MetadataVersion::Number(1),
&None,
- u32::MAX,
None
))
.unwrap(),
@@ -986,7 +956,6 @@
&MetadataPath::from_role(&Role::Root),
&MetadataVersion::Number(3),
&None,
- u32::MAX,
None
))
.unwrap(),
diff --git a/src/repository.rs b/src/repository.rs
index 4f798ca..50f1923 100644
--- a/src/repository.rs
+++ b/src/repository.rs
@@ -53,7 +53,6 @@
meta_path: &'a MetadataPath,
version: &'a MetadataVersion,
max_size: &'a Option<usize>,
- min_bytes_per_second: u32,
hash_data: Option<(&'static HashAlgorithm, HashValue)>,
) -> TufFuture<'a, Result<SignedMetadata<D, M>>>
where
@@ -73,7 +72,6 @@
&'a self,
target_path: &'a TargetPath,
target_description: &'a TargetDescription,
- min_bytes_per_second: u32,
) -> TufFuture<'a, Result<Box<dyn AsyncRead>>>;
/// Perform a sanity check that `M`, `Role`, and `MetadataPath` all desrcribe the same entity.
@@ -160,7 +158,6 @@
meta_path: &'a MetadataPath,
version: &'a MetadataVersion,
max_size: &'a Option<usize>,
- min_bytes_per_second: u32,
hash_data: Option<(&'static HashAlgorithm, HashValue)>,
) -> TufFuture<'a, Result<SignedMetadata<D, M>>>
where
@@ -176,7 +173,7 @@
let mut reader = SafeReader::new(
AllowStdIo::new(File::open(&path)?),
max_size.unwrap_or(::std::usize::MAX) as u64,
- min_bytes_per_second,
+ 0,
hash_data,
)?;
@@ -218,7 +215,6 @@
&'a self,
target_path: &'a TargetPath,
target_description: &'a TargetDescription,
- min_bytes_per_second: u32,
) -> TufFuture<'a, Result<Box<dyn AsyncRead>>> {
Box::pinned(
async move {
@@ -234,7 +230,7 @@
let reader: Box<dyn AsyncRead> = Box::new(SafeReader::new(
AllowStdIo::new(File::open(&path)?),
target_description.size(),
- min_bytes_per_second,
+ 0,
Some((alg, value.clone())),
)?);
@@ -258,16 +254,92 @@
}
}
+/// A builder to create a repository accessible over HTTP.
+pub struct HttpRepositoryBuilder<C, D>
+where
+ C: Connect + Sync + 'static,
+ D: DataInterchange,
+{
+ url: Url,
+ client: Client<C>,
+ interchange: PhantomData<D>,
+ user_agent_prefix: Option<String>,
+ metadata_prefix: Option<Vec<String>>,
+ min_bytes_per_second: u32,
+}
+
+impl<C, D> HttpRepositoryBuilder<C, D>
+where
+ C: Connect + Sync + 'static,
+ D: DataInterchange,
+{
+ /// Create a new repository with the given `Url` and `Client`.
+ pub fn new(url: Url, client: Client<C>) -> Self {
+ HttpRepositoryBuilder {
+ url: url,
+ client: client,
+ interchange: PhantomData,
+ user_agent_prefix: None,
+ metadata_prefix: None,
+ min_bytes_per_second: 4096,
+ }
+ }
+
+ /// Set the User-Agent prefix.
+ ///
+ /// Callers *should* include a custom User-Agent prefix to help maintainers of TUF repositories
+ /// keep track of which client versions exist in the field.
+ ///
+ pub fn user_agent_prefix<T: Into<String>>(mut self, user_agent_prefix: T) -> Self {
+ self.user_agent_prefix = Some(user_agent_prefix.into());
+ self
+ }
+
+ /// The argument `metadata_prefix` is used provide an alternate path where metadata is stored on
+ /// the repository. If `None`, this defaults to `/`. For example, if there is a TUF repository
+ /// at `https://tuf.example.com/`, but all metadata is stored at `/meta/`, then passing the
+ /// arg `Some("meta".into())` would cause `root.json` to be fetched from
+ /// `https://tuf.example.com/meta/root.json`.
+ pub fn metadata_prefix(mut self, metadata_prefix: Vec<String>) -> Self {
+ self.metadata_prefix = Some(metadata_prefix);
+ self
+ }
+
+ /// Set the minimum bytes per second for a read to be considered good.
+ pub fn min_bytes_per_second(mut self, min: u32) -> Self {
+ self.min_bytes_per_second = min;
+ self
+ }
+
+ /// Build a `HttpRepository`.
+ pub fn build(self) -> HttpRepository<C, D> {
+ let user_agent = match self.user_agent_prefix {
+ Some(ua) => format!("{} (rust-tuf/{})", ua, env!("CARGO_PKG_VERSION")),
+ None => format!("rust-tuf/{}", env!("CARGO_PKG_VERSION")),
+ };
+
+ HttpRepository {
+ url: self.url,
+ client: self.client,
+ interchange: self.interchange,
+ user_agent: user_agent,
+ metadata_prefix: self.metadata_prefix,
+ min_bytes_per_second: self.min_bytes_per_second,
+ }
+ }
+}
+
/// A repository accessible over HTTP.
pub struct HttpRepository<C, D>
where
- C: Connect + Sync,
+ C: Connect + Sync + 'static,
D: DataInterchange,
{
url: Url,
client: Client<C>,
user_agent: String,
metadata_prefix: Option<Vec<String>>,
+ min_bytes_per_second: u32,
interchange: PhantomData<D>,
}
@@ -276,36 +348,6 @@
C: Connect + Sync + 'static,
D: DataInterchange,
{
- /// Create a new repository with the given `Url` and `Client`.
- ///
- /// Callers *should* include a custom User-Agent prefix to help maintainers of TUF repositories
- /// keep track of which client versions exist in the field.
- ///
- /// The argument `metadata_prefix` is used provide an alternate path where metadata is stored on
- /// the repository. If `None`, this defaults to `/`. For example, if there is a TUF repository
- /// at `https://tuf.example.com/`, but all metadata is stored at `/meta/`, then passing the
- /// arg `Some("meta".into())` would cause `root.json` to be fetched from
- /// `https://tuf.example.com/meta/root.json`.
- pub fn new(
- url: Url,
- client: Client<C>,
- user_agent_prefix: Option<String>,
- metadata_prefix: Option<Vec<String>>,
- ) -> Self {
- let user_agent = match user_agent_prefix {
- Some(ua) => format!("{} (rust-tuf/{})", ua, env!("CARGO_PKG_VERSION")),
- None => format!("rust-tuf/{}", env!("CARGO_PKG_VERSION")),
- };
-
- HttpRepository {
- url,
- client,
- user_agent,
- metadata_prefix,
- interchange: PhantomData,
- }
- }
-
async fn get<'a>(
&'a self,
prefix: &'a Option<Vec<String>>,
@@ -378,7 +420,6 @@
meta_path: &'a MetadataPath,
version: &'a MetadataVersion,
max_size: &'a Option<usize>,
- min_bytes_per_second: u32,
hash_data: Option<(&'static HashAlgorithm, HashValue)>,
) -> TufFuture<'a, Result<SignedMetadata<D, M>>>
where
@@ -399,7 +440,7 @@
let mut reader = SafeReader::new(
IntoAsyncRead::new(stream),
max_size.unwrap_or(::std::usize::MAX) as u64,
- min_bytes_per_second,
+ self.min_bytes_per_second,
hash_data,
)?;
@@ -423,7 +464,6 @@
&'a self,
target_path: &'a TargetPath,
target_description: &'a TargetDescription,
- min_bytes_per_second: u32,
) -> TufFuture<'a, Result<Box<dyn AsyncRead>>> {
Box::pinned(
async move {
@@ -439,7 +479,7 @@
let reader = SafeReader::new(
IntoAsyncRead::new(stream),
target_description.size(),
- min_bytes_per_second,
+ self.min_bytes_per_second,
Some((alg, value.clone())),
)?;
@@ -514,7 +554,6 @@
meta_path: &'a MetadataPath,
version: &'a MetadataVersion,
max_size: &'a Option<usize>,
- min_bytes_per_second: u32,
hash_data: Option<(&'static HashAlgorithm, HashValue)>,
) -> TufFuture<'a, Result<SignedMetadata<D, M>>>
where
@@ -530,7 +569,7 @@
let mut reader = SafeReader::new(
&**bytes,
max_size.unwrap_or(::std::usize::MAX) as u64,
- min_bytes_per_second,
+ 0,
hash_data,
)?;
@@ -569,7 +608,6 @@
&'a self,
target_path: &'a TargetPath,
target_description: &'a TargetDescription,
- min_bytes_per_second: u32,
) -> TufFuture<'a, Result<Box<dyn AsyncRead>>> {
Box::pinned(
async move {
@@ -582,7 +620,7 @@
let reader: Box<dyn AsyncRead> = Box::new(SafeReader::new(
cur,
target_description.size(),
- min_bytes_per_second,
+ 0,
Some((alg, value.clone())),
)?);
@@ -615,14 +653,14 @@
let path = TargetPath::new("batty".into()).unwrap();
await!(repo.store_target(data, &path)).unwrap();
- let mut read = await!(repo.fetch_target(&path, &target_description, 0)).unwrap();
+ let mut read = await!(repo.fetch_target(&path, &target_description)).unwrap();
let mut buf = Vec::new();
await!(read.read_to_end(&mut buf)).unwrap();
assert_eq!(buf.as_slice(), data);
let bad_data: &[u8] = b"you're in a desert";
await!(repo.store_target(bad_data, &path)).unwrap();
- let mut read = await!(repo.fetch_target(&path, &target_description, 0)).unwrap();
+ let mut read = await!(repo.fetch_target(&path, &target_description)).unwrap();
assert!(await!(read.read_to_end(&mut buf)).is_err());
},
)
@@ -664,14 +702,14 @@
// files in a mode that allows the file to be opened multiple times.
{
let mut read =
- await!(repo.fetch_target(&path, &target_description, 0)).unwrap();
+ await!(repo.fetch_target(&path, &target_description)).unwrap();
await!(read.read_to_end(&mut buf)).unwrap();
assert_eq!(buf.as_slice(), data);
}
let bad_data: &[u8] = b"you're in a desert";
await!(repo.store_target(bad_data, &path)).unwrap();
- let mut read = await!(repo.fetch_target(&path, &target_description, 0)).unwrap();
+ let mut read = await!(repo.fetch_target(&path, &target_description)).unwrap();
assert!(await!(read.read_to_end(&mut buf)).is_err());
},
)