From 6978c62d0e5ed2a7f24d2dc7afdfabd81b555268 Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Fri, 1 Nov 2024 10:00:47 +0100 Subject: [PATCH 1/9] restructure stuff for the CLI --- Cargo.toml | 2 + src/http/client/cli.rs | 57 +++++++++++++ src/http/{client.rs => client/mod.rs} | 2 + src/http/server/cli.rs | 110 ++++++++++++++++++++++++++ src/http/{server.rs => server/mod.rs} | 2 + src/http/shed/cli.rs | 4 +- src/lib.rs | 8 ++ src/tls/mod.rs | 69 +++++++++++++--- src/tls/sessions.rs | 91 ++++++++++----------- src/tls/tickets.rs | 2 + 10 files changed, 284 insertions(+), 63 deletions(-) create mode 100644 src/http/client/cli.rs rename src/http/{client.rs => client/mod.rs} (99%) create mode 100644 src/http/server/cli.rs rename src/http/{server.rs => server/mod.rs} (99%) diff --git a/Cargo.toml b/Cargo.toml index fd6ced1..e1bdc02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ instant-acme = { version = "0.7.1", default-features = false, features = [ ] } mockall = "0.12" moka = { version = "0.12", features = ["sync", "future"] } +parse-size = { version = "1.0", features = ["std"] } prometheus = "0.13" prost = "0.13" prost-types = "0.13" @@ -66,6 +67,7 @@ rustls-acme = { version = "0.11", default-features = false, features = [ "ring", ] } rustls-pemfile = "2" +rustls-platform-verifier = "0.3.4" serde_json = "1.0" scopeguard = "1.2.0" sha1 = "0.10" diff --git a/src/http/client/cli.rs b/src/http/client/cli.rs new file mode 100644 index 0000000..4c3e2cc --- /dev/null +++ b/src/http/client/cli.rs @@ -0,0 +1,57 @@ +use std::time::Duration; + +use clap::Args; +use humantime::parse_duration; + +use super::CloneableDnsResolver; + +#[derive(Args, Clone, Debug, Eq, PartialEq)] +pub struct HttpClient { + /// Timeout for HTTP connection phase + #[clap(env, long, default_value = "5s", value_parser = parse_duration)] + pub http_client_timeout_connect: Duration, + + /// Timeout for a single read request + #[clap(env, long, default_value = "15s", value_parser = parse_duration)] + pub http_client_timeout_read: Duration, + + /// Timeout for the whole HTTP call: this includes connecting, sending request, + /// receiving response etc. + #[clap(env, long, default_value = "60s", value_parser = parse_duration)] + pub http_client_timeout: Duration, + + /// How long to keep idle HTTP connections open + #[clap(env, long, default_value = "120s", value_parser = parse_duration)] + pub http_client_pool_idle: Duration, + + /// TCP Keepalive interval + #[clap(env, long, default_value = "15s", value_parser = parse_duration)] + pub http_client_tcp_keepalive: Duration, + + /// HTTP2 Keepalive interval + #[clap(env, long, default_value = "10s", value_parser = parse_duration)] + pub http_client_http2_keepalive: Duration, + + /// HTTP2 Keepalive timeout + #[clap(env, long, default_value = "5s", value_parser = parse_duration)] + pub http_client_http2_keepalive_timeout: Duration, +} + +impl From<&HttpClient> for super::Options { + fn from(c: &HttpClient) -> Self { + Self { + timeout_connect: c.http_client_timeout_connect, + timeout_read: c.http_client_timeout_read, + timeout: c.http_client_timeout, + pool_idle_timeout: Some(c.http_client_pool_idle), + pool_idle_max: None, + tcp_keepalive: Some(c.http_client_tcp_keepalive), + http2_keepalive: Some(c.http_client_http2_keepalive), + http2_keepalive_timeout: c.http_client_http2_keepalive_timeout, + http2_keepalive_idle: false, + user_agent: "".into(), + tls_config: None, + dns_resolver: None, + } + } +} diff --git a/src/http/client.rs b/src/http/client/mod.rs similarity index 99% rename from src/http/client.rs rename to src/http/client/mod.rs index e28e09b..0e094e3 100644 --- a/src/http/client.rs +++ b/src/http/client/mod.rs @@ -1,3 +1,5 @@ +pub mod cli; + use std::{ fmt, sync::{ diff --git a/src/http/server/cli.rs b/src/http/server/cli.rs new file mode 100644 index 0000000..05a894f --- /dev/null +++ b/src/http/server/cli.rs @@ -0,0 +1,110 @@ +use std::time::Duration; + +use clap::Args; +use humantime::parse_duration; + +use crate::{parse_size, tls}; + +#[derive(Args, Clone, Debug, Eq, PartialEq)] +pub struct HttpServer { + /// Backlog of incoming connections to set on the listening socket + #[clap(env, long, default_value = "2048")] + pub http_server_backlog: u32, + + /// Maximum number of HTTP requests to serve over a single connection. + /// After this number is reached the connection is gracefully closed. + /// The default is consistent with nginx's `keepalive_requests` parameter. + #[clap(env, long, default_value = "1000")] + pub http_server_max_requests_per_conn: u64, + + /// Timeout for network read calls. + /// If the read call take longer than that - the connection is closed. + /// This effectively closes idle HTTP/1.1 connections. + #[clap(env, long, default_value = "30s", value_parser = parse_duration)] + pub http_server_read_timeout: Duration, + + /// Timeout for network write calls. + /// If the write call take longer than that - the connection is closed. + #[clap(env, long, default_value = "30s", value_parser = parse_duration)] + pub http_server_write_timeout: Duration, + + /// Idle timeout for connections. + /// If no requests are executed during this period - the connections is closed. + /// Mostly needed for HTTP/2 where the read timeout sometimes cannot kick in + /// due to PING frames and other non-request activity. + #[clap(env, long, default_value = "60s", value_parser = parse_duration)] + pub http_server_idle_timeout: Duration, + + /// TLS handshake timeout + #[clap(env, long, default_value = "15s", value_parser = parse_duration)] + pub http_server_tls_handshake_timeout: Duration, + + /// For how long to wait for the client to send headers. + /// Applies only to HTTP1 connections. + /// Should be set lower than the global `http_server_read_timeout`. + #[clap(env, long, default_value = "10s", value_parser = parse_duration)] + pub http_server_http1_header_read_timeout: Duration, + + /// For how long to wait for the client to send full request body. + #[clap(env, long, default_value = "60s", value_parser = parse_duration)] + pub http_server_body_read_timeout: Duration, + + /// Maximum number of HTTP2 streams that the client is allowed to create inside a single connection + #[clap(env, long, default_value = "128")] + pub http_server_http2_max_streams: u32, + + /// Keepalive interval for HTTP2 connections + #[clap(env, long, default_value = "20s", value_parser = parse_duration)] + pub http_server_http2_keepalive_interval: Duration, + + /// Keepalive timeout for HTTP2 connections + #[clap(env, long, default_value = "10s", value_parser = parse_duration)] + pub http_server_http2_keepalive_timeout: Duration, + + /// How long to wait for the existing connections to finish before shutting down. + /// Also applies to the recycling of connections with `http_server_max_requests_per_conn` option. + #[clap(env, long, default_value = "60s", value_parser = parse_duration)] + pub http_server_grace_period: Duration, + + /// Maximum size of cache to store TLS sessions in memory + #[clap(env, long, default_value = "256MB", value_parser = parse_size)] + pub http_server_tls_session_cache_size: u64, + + /// Maximum time that a TLS session key can stay in cache without being requested (Time-to-Idle) + #[clap(env, long, default_value = "18h", value_parser = parse_duration)] + pub http_server_tls_session_cache_tti: Duration, + + /// Lifetime of a TLS1.3 ticket, due to key rotation the actual lifetime will be twice than this + #[clap(env, long, default_value = "9h", value_parser = parse_duration)] + pub http_server_tls_ticket_lifetime: Duration, +} + +impl From<&HttpServer> for super::Options { + fn from(c: &HttpServer) -> Self { + Self { + backlog: c.http_server_backlog, + read_timeout: Some(c.http_server_read_timeout), + write_timeout: Some(c.http_server_write_timeout), + idle_timeout: c.http_server_idle_timeout, + tls_handshake_timeout: c.http_server_tls_handshake_timeout, + http1_header_read_timeout: c.http_server_http1_header_read_timeout, + http2_keepalive_interval: c.http_server_http2_keepalive_interval, + http2_keepalive_timeout: c.http_server_http2_keepalive_timeout, + http2_max_streams: c.http_server_http2_max_streams, + grace_period: c.http_server_grace_period, + max_requests_per_conn: Some(c.http_server_max_requests_per_conn), + } + } +} + +impl From<&HttpServer> for tls::Options { + fn from(c: &HttpServer) -> Self { + Self { + additional_alpn: vec![], + sessions_count: c.http_server_tls_session_cache_size, + sessions_tti: c.http_server_tls_session_cache_tti, + ticket_lifetime: c.http_server_tls_ticket_lifetime, + tls_versions: vec![], + } + } +} diff --git a/src/http/server.rs b/src/http/server/mod.rs similarity index 99% rename from src/http/server.rs rename to src/http/server/mod.rs index 7c9dc72..b7b789f 100644 --- a/src/http/server.rs +++ b/src/http/server/mod.rs @@ -1,3 +1,5 @@ +pub mod cli; + use std::{ fmt::Display, io, diff --git a/src/http/shed/cli.rs b/src/http/shed/cli.rs index 5840abf..ffca497 100644 --- a/src/http/shed/cli.rs +++ b/src/http/shed/cli.rs @@ -27,7 +27,7 @@ where } } -#[derive(Debug, Clone, Args)] +#[derive(Args, Clone, Debug, PartialEq)] pub struct ShedSystem { /// EWMA alpha coefficient in [0.0, 1.0] range. /// It represents the weight of the more recent measurements relative to the older ones. @@ -67,7 +67,7 @@ impl From for SystemOptions { } } -#[derive(Debug, Clone, Args)] +#[derive(Args, Clone, Debug, PartialEq)] pub struct ShedSharded where T::Err: std::error::Error + Send + Sync + 'static, diff --git a/src/lib.rs b/src/lib.rs index cf61859..885f5dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,3 +12,11 @@ pub enum Error { #[error(transparent)] Generic(#[from] anyhow::Error), } + +pub fn parse_size(s: &str) -> Result { + parse_size::Config::new().with_binary().parse_size(s) +} + +pub fn parse_size_usize(s: &str) -> Result { + parse_size(s).map(|x| x as usize) +} diff --git a/src/tls/mod.rs b/src/tls/mod.rs index 128ae67..172f67a 100644 --- a/src/tls/mod.rs +++ b/src/tls/mod.rs @@ -8,12 +8,14 @@ use anyhow::{anyhow, Context}; use fqdn::{Fqdn, FQDN}; use prometheus::Registry; use rustls::{ + client::{ClientSessionMemoryCache, Resumption}, compress::CompressionCache, crypto::ring, - server::{ClientHello, ResolvesServerCert, StoresServerSessions}, + server::{ClientHello, ResolvesServerCert}, sign::CertifiedKey, - ServerConfig, SupportedProtocolVersion, TicketSwitcher, + ClientConfig, ServerConfig, SupportedProtocolVersion, TicketSwitcher, }; +use rustls_platform_verifier::Verifier; use std::{ net::{Ipv4Addr, Ipv6Addr}, time::Duration, @@ -137,28 +139,42 @@ pub fn pem_convert_to_rustls(key: &[u8], certs: &[u8]) -> Result>, + pub sessions_count: u64, + pub sessions_tti: Duration, + pub ticket_lifetime: Duration, + pub tls_versions: Vec<&'static SupportedProtocolVersion>, +} + +/// Creates Rustls server config +/// Must be run in Tokio environment since it spawns a task to record metrics pub fn prepare_server_config( + opts: Options, resolver: Arc, - session_storage: Arc, - additional_alpn: &[Vec], - ticket_lifetime: Duration, - tls_versions: &[&'static SupportedProtocolVersion], registry: &Registry, ) -> ServerConfig { - let mut cfg = ServerConfig::builder_with_protocol_versions(tls_versions) + let mut cfg = ServerConfig::builder_with_protocol_versions(&opts.tls_versions) .with_no_client_auth() .with_cert_resolver(resolver); - // Set custom session storage with to allow effective TLS session resumption - let session_storage = sessions::WithMetrics(session_storage, sessions::Metrics::new(registry)); - cfg.session_storage = Arc::new(session_storage); + // Create custom session storage with to allow effective TLS session resumption + let session_storage = Arc::new(sessions::Storage::new( + opts.sessions_count, + opts.sessions_tti, + registry, + )); + let session_storage_metrics = session_storage.clone(); + // Spawn metrics runner + tokio::spawn(async move { session_storage_metrics.metrics_runner().await }); + cfg.session_storage = session_storage; // Enable ticketer to encrypt/decrypt TLS tickets. // TicketSwitcher rotates the inner ticketers every `ticket_lifetime` // while keeping the previous one available for decryption of tickets // issued earlier than `ticket_lifetime` ago. let ticketer = tickets::WithMetrics( - TicketSwitcher::new(ticket_lifetime.as_secs() as u32, move || { + TicketSwitcher::new(opts.ticket_lifetime.as_secs() as u32, move || { Ok(Box::new(tickets::Ticketer::new())) }) .unwrap(), @@ -168,11 +184,38 @@ pub fn prepare_server_config( // Enable certificate compression cache. // See https://datatracker.ietf.org/doc/rfc8879/ for details - cfg.cert_compression_cache = Arc::new(CompressionCache::new(1024)); + cfg.cert_compression_cache = Arc::new(CompressionCache::new(8192)); // Enable ALPN cfg.alpn_protocols = vec![ALPN_H2.to_vec(), ALPN_H1.to_vec()]; - cfg.alpn_protocols.extend_from_slice(additional_alpn); + cfg.alpn_protocols.extend_from_slice(&opts.additional_alpn); + + cfg +} + +pub fn prepare_client_config(tls_versions: &[&'static SupportedProtocolVersion]) -> ClientConfig { + // Use a custom certificate verifier from rustls project that is more secure. + // It also checks OCSP revocation, though OCSP support for Linux platform for now seems be no-op. + // https://github.com/rustls/rustls-platform-verifier/issues/99 + + // new_with_extra_roots() method isn't available on MacOS, see + // https://github.com/rustls/rustls-platform-verifier/issues/58 + #[cfg(not(target_os = "macos"))] + let verifier = Arc::new(Verifier::new_with_extra_roots( + webpki_roots::TLS_SERVER_ROOTS.to_vec(), + )); + #[cfg(target_os = "macos")] + let verifier = Arc::new(Verifier::new()); + + let mut cfg = ClientConfig::builder_with_protocol_versions(tls_versions) + .dangerous() // Nothing really dangerous here + .with_custom_certificate_verifier(verifier) + .with_no_client_auth(); + + // Session resumption + let store = ClientSessionMemoryCache::new(2048); + cfg.resumption = Resumption::store(Arc::new(store)); + cfg.alpn_protocols = vec![ALPN_H2.to_vec(), ALPN_H1.to_vec()]; cfg } diff --git a/src/tls/sessions.rs b/src/tls/sessions.rs index 095272a..ef0aff7 100644 --- a/src/tls/sessions.rs +++ b/src/tls/sessions.rs @@ -1,9 +1,13 @@ -use std::{sync::Arc, time::Duration}; +use std::time::Duration; use ahash::RandomState; use moka::sync::Cache; -use prometheus::{register_int_counter_vec_with_registry, IntCounterVec, Registry}; +use prometheus::{ + register_int_counter_vec_with_registry, register_int_gauge_with_registry, IntCounterVec, + IntGauge, Registry, +}; use rustls::server::StoresServerSessions; +use tokio::time::interval; use zeroize::ZeroizeOnDrop; type Key = Vec; @@ -18,51 +22,56 @@ fn weigher(k: &Key, v: &Val) -> u32 { (k.len() + v.0.len()) as u32 } -pub struct Stats { - pub entries: u64, - pub size: u64, -} - /// Stores TLS sessions for TLSv1.2 only. /// `SipHash` is replaced with ~10x faster aHash. /// see #[derive(Debug)] pub struct Storage { cache: Cache, + metrics: Metrics, } impl Storage { - pub fn new(capacity: u64, tti: Duration) -> Self { + pub fn new(capacity: u64, tti: Duration, registry: &Registry) -> Self { let cache = Cache::builder() .max_capacity(capacity) .time_to_idle(tti) .weigher(weigher) .build_with_hasher(RandomState::default()); - Self { cache } + let metrics = Metrics::new(registry); + Self { cache, metrics } } - pub fn stats(&self) -> Stats { - self.cache.run_pending_tasks(); - Stats { - entries: self.cache.entry_count(), - size: self.cache.weighted_size(), + pub async fn metrics_runner(&self) { + let mut interval = interval(Duration::from_secs(1)); + interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); + + loop { + interval.tick().await; + self.metrics.size.set(self.cache.weighted_size() as i64); + self.metrics.count.set(self.cache.entry_count() as i64); } } } impl StoresServerSessions for Storage { fn get(&self, key: &[u8]) -> Option> { - self.cache.get(key).map(|x| x.0.clone()) + let v = self.cache.get(key).map(|x| x.0.clone()); + self.metrics.record("get", v.is_some()); + v } fn put(&self, key: Vec, value: Vec) -> bool { self.cache.insert(key, Val(value)); + self.metrics.record("put", true); true } fn take(&self, key: &[u8]) -> Option> { - self.cache.remove(key).map(|x| x.0.clone()) + let v = self.cache.remove(key).map(|x| x.0.clone()); + self.metrics.record("take", v.is_some()); + v } fn can_cache(&self) -> bool { @@ -72,12 +81,28 @@ impl StoresServerSessions for Storage { #[derive(Debug)] pub struct Metrics { + count: IntGauge, + size: IntGauge, processed: IntCounterVec, } impl Metrics { pub fn new(registry: &Registry) -> Self { Self { + count: register_int_gauge_with_registry!( + format!("tls_session_cache_count"), + format!("Number of TLS sessions in the cache"), + registry + ) + .unwrap(), + + size: register_int_gauge_with_registry!( + format!("tls_session_cache_size"), + format!("Size of TLS sessions in the cache"), + registry + ) + .unwrap(), + processed: register_int_counter_vec_with_registry!( format!("tls_sessions"), format!("Number of TLS sessions that were processed"), @@ -87,51 +112,21 @@ impl Metrics { .unwrap(), } } -} - -#[derive(Debug)] -pub struct WithMetrics(pub Arc, pub Metrics); -impl WithMetrics { fn record(&self, action: &str, ok: bool) { - self.1 - .processed + self.processed .with_label_values(&[action, if ok { "yes" } else { "no" }]) .inc(); } } -impl StoresServerSessions for WithMetrics { - fn get(&self, key: &[u8]) -> Option> { - let v = self.0.get(key); - self.record("get", v.is_some()); - v - } - - fn put(&self, key: Vec, value: Vec) -> bool { - let v = self.0.put(key, value); - self.record("put", v); - v - } - - fn take(&self, key: &[u8]) -> Option> { - let v = self.0.take(key); - self.record("take", v.is_some()); - v - } - - fn can_cache(&self) -> bool { - self.0.can_cache() - } -} - #[cfg(test)] mod test { use super::*; #[test] fn test_storage() { - let c = Storage::new(10000, Duration::from_secs(3600)); + let c = Storage::new(10000, Duration::from_secs(3600), &Registry::new()); let key1 = "a".repeat(2500).as_bytes().to_vec(); let key2 = "b".repeat(2500).as_bytes().to_vec(); diff --git a/src/tls/tickets.rs b/src/tls/tickets.rs index 096627e..7d0bdb6 100644 --- a/src/tls/tickets.rs +++ b/src/tls/tickets.rs @@ -35,9 +35,11 @@ impl Metrics { /// Encrypts & decrypts tickets for TLS 1.3 session resumption. /// Must be used with `rustls::ticketer::TicketSwitcher` to facilitate key rotation. +/// /// We're using `XChaCha20Poly1305` authenicated encryption (AEAD). /// `ZeroizeOnDrop` is derived below to make sure the encryption keys are wiped from /// memory when the Ticketer is dropped. +/// /// See #[derive(ZeroizeOnDrop)] pub struct Ticketer { From 3f71a2fc33fe8a4b76be756c9892c573b5bf5a62 Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Tue, 5 Nov 2024 13:06:00 +0100 Subject: [PATCH 2/9] add serde --- Cargo.toml | 3 ++- src/types.rs | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e1bdc02..0a34a17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,8 +68,9 @@ rustls-acme = { version = "0.11", default-features = false, features = [ ] } rustls-pemfile = "2" rustls-platform-verifier = "0.3.4" -serde_json = "1.0" scopeguard = "1.2.0" +serde = "1.0.214" +serde_json = "1.0" sha1 = "0.10" strum = { version = "0.26", features = ["derive"] } strum_macros = "0.26" diff --git a/src/types.rs b/src/types.rs index d1c0a6f..56a2cd2 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,6 @@ use std::fmt::Debug; +use serde::{Deserialize, Serialize}; use strum::{Display, EnumString, IntoStaticStr}; /// Type of IC API request @@ -16,10 +17,14 @@ use strum::{Display, EnumString, IntoStaticStr}; Hash, IntoStaticStr, EnumString, + Serialize, + Deserialize, )] #[strum(serialize_all = "snake_case")] +#[serde(rename_all = "snake_case")] pub enum RequestType { #[default] + Unknown, Status, Query, Call, @@ -27,3 +32,9 @@ pub enum RequestType { ReadState, ReadStateSubnet, } + +impl RequestType { + pub const fn is_call(&self) -> bool { + matches!(self, Self::Call | Self::SyncCall) + } +} From 7e0221dfa704aa2c0d9f22f54934507eea05a6d4 Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Tue, 5 Nov 2024 13:15:40 +0100 Subject: [PATCH 3/9] set specific dep versions --- .gitignore | 1 + Cargo.toml | 104 ++++++++++++++++++++++++++--------------------------- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 96ef6c0..04f6c57 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +.idea /target Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 0a34a17..6f56f10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,88 +8,88 @@ repository = "https://github.com/dfinity/ic-bn-lib" readme = "README.md" [dependencies] -ahash = "0.8" -anyhow = "1.0" -arc-swap = "1" -async-trait = "0.1.81" -axum = "0.7" -backoff = { version = "0.4", features = ["tokio"] } -base64 = "0.22" -bytes = "1.6" -clap = { version = "4.5", features = ["derive", "string", "env"] } -clap_derive = "4.5" -chacha20poly1305 = "0.10" +ahash = "0.8.11" +anyhow = "1.0.92" +arc-swap = "1.7.1" +async-trait = "0.1.83" +axum = "0.7.7" +backoff = { version = "0.4.0", features = ["tokio"] } +base64 = "0.22.1" +bytes = "1.8.0" +clap = { version = "4.5.20", features = ["derive", "string", "env"] } +clap_derive = "4.5.18" +chacha20poly1305 = "0.10.1" cloudflare = { git = "https://github.com/cloudflare/cloudflare-rs.git", rev = "f14720e42184ee176a97676e85ef2d2d85bc3aae", default-features = false, features = [ "rustls-tls", ] } -derive-new = "0.6" -fqdn = "0.3" -futures = "0.3" -futures-util = "0.3" -hickory-proto = "0.24" -hickory-resolver = { version = "0.24", features = [ +derive-new = "0.7.0" +fqdn = "0.4.1" +futures = "0.3.31" +futures-util = "0.3.31" +hickory-proto = "0.24.1" +hickory-resolver = { version = "0.24.1", features = [ "dns-over-https-rustls", "webpki-roots", "dnssec-ring", ] } -http = "1.1" -http-body = "1.0" -http-body-util = "0.1" -humantime = "2.1" -hyper = "1.5" -hyper-util = { version = "0.1", features = ["full"] } -instant-acme = { version = "0.7.1", default-features = false, features = [ +http = "1.1.0" +http-body = "1.0.1" +http-body-util = "0.1.2" +humantime = "2.1.0" +hyper = "1.5.0" +hyper-util = { version = "0.1.10", features = ["full"] } +instant-acme = { version = "0.7.2", default-features = false, features = [ "ring", "hyper-rustls", ] } -mockall = "0.12" -moka = { version = "0.12", features = ["sync", "future"] } -parse-size = { version = "1.0", features = ["std"] } -prometheus = "0.13" -prost = "0.13" -prost-types = "0.13" -rand = "0.8" +mockall = "0.13.0" +moka = { version = "0.12.8", features = ["sync", "future"] } +parse-size = { version = "1.1.0", features = ["std"] } +prometheus = "0.13.4" +prost = "0.13.3" +prost-types = "0.13.3" +rand = "0.8.5" rcgen = "0.13.1" -reqwest = { version = "0.12.7", default-features = false, features = [ +reqwest = { version = "0.12.9", default-features = false, features = [ "http2", "rustls-tls", "hickory-dns", "json", "stream", ] } -rustls = { version = "0.23.12", default-features = false, features = [ +rustls = { version = "0.23.16", default-features = false, features = [ "ring", "std", "brotli", ] } -rustls-acme = { version = "0.11", default-features = false, features = [ +rustls-acme = { version = "0.11.1", default-features = false, features = [ "tls12", "ring", ] } -rustls-pemfile = "2" -rustls-platform-verifier = "0.3.4" +rustls-pemfile = "2.2.0" +rustls-platform-verifier = "0.4.0" scopeguard = "1.2.0" serde = "1.0.214" -serde_json = "1.0" -sha1 = "0.10" -strum = { version = "0.26", features = ["derive"] } -strum_macros = "0.26" -sync_wrapper = "1.0" +serde_json = "1.0.132" +sha1 = "0.10.6" +strum = { version = "0.26.3", features = ["derive"] } +strum_macros = "0.26.4" +sync_wrapper = "1.0.1" systemstat = "0.2.3" -thiserror = "1.0" -tokio = { version = "1.41", features = ["full"] } -tokio-util = { version = "0.7", features = ["full"] } +thiserror = "1.0.68" +tokio = { version = "1.41.0", features = ["full"] } +tokio-util = { version = "0.7.12", features = ["full"] } tokio-rustls = { version = "0.26.0", default-features = false, features = [ "tls12", "logging", "ring", ] } -tokio-io-timeout = "1.2" -tower = { version = "0.5", features = ["util"] } -tower-service = "0.3" -tracing = "0.1" -url = "2.5" -uuid = { version = "1.10", features = ["v7"] } +tokio-io-timeout = "1.2.0" +tower = { version = "0.5.1", features = ["util"] } +tower-service = "0.3.3" +tracing = "0.1.40" +url = "2.5.3" +uuid = { version = "1.10.0", features = ["v7"] } vrl = { version = "0.19.0", default-features = false, features = ["value"] } -x509-parser = "0.16" -zeroize = { version = "1.8", features = ["derive"] } +x509-parser = "0.16.0" +zeroize = { version = "1.8.1", features = ["derive"] } From bd85c8d07da0797a4e3be6c17b96e48c312c506d Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Tue, 5 Nov 2024 13:43:28 +0100 Subject: [PATCH 4/9] Update src/tls/mod.rs Co-authored-by: r-birkner <103420898+r-birkner@users.noreply.github.com> --- src/tls/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tls/mod.rs b/src/tls/mod.rs index 172f67a..bf86a47 100644 --- a/src/tls/mod.rs +++ b/src/tls/mod.rs @@ -158,7 +158,7 @@ pub fn prepare_server_config( .with_no_client_auth() .with_cert_resolver(resolver); - // Create custom session storage with to allow effective TLS session resumption + // Create custom session storage to allow effective TLS session resumption let session_storage = Arc::new(sessions::Storage::new( opts.sessions_count, opts.sessions_tti, From 203dd39183bd32bd4a326314e3c97df38bc37779 Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Tue, 5 Nov 2024 13:47:32 +0100 Subject: [PATCH 5/9] Update src/http/server/cli.rs Co-authored-by: r-birkner <103420898+r-birkner@users.noreply.github.com> --- src/http/server/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http/server/cli.rs b/src/http/server/cli.rs index 05a894f..3e84f66 100644 --- a/src/http/server/cli.rs +++ b/src/http/server/cli.rs @@ -24,7 +24,7 @@ pub struct HttpServer { pub http_server_read_timeout: Duration, /// Timeout for network write calls. - /// If the write call take longer than that - the connection is closed. + /// If the write call takes longer than that - the connection is closed. #[clap(env, long, default_value = "30s", value_parser = parse_duration)] pub http_server_write_timeout: Duration, From e3f35cb8e138bdbf21d8e8780d5491a4d99136e8 Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Tue, 5 Nov 2024 13:47:48 +0100 Subject: [PATCH 6/9] Update src/http/server/cli.rs Co-authored-by: r-birkner <103420898+r-birkner@users.noreply.github.com> --- src/http/server/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http/server/cli.rs b/src/http/server/cli.rs index 3e84f66..40ff800 100644 --- a/src/http/server/cli.rs +++ b/src/http/server/cli.rs @@ -18,7 +18,7 @@ pub struct HttpServer { pub http_server_max_requests_per_conn: u64, /// Timeout for network read calls. - /// If the read call take longer than that - the connection is closed. + /// If the read call takes longer than that - the connection is closed. /// This effectively closes idle HTTP/1.1 connections. #[clap(env, long, default_value = "30s", value_parser = parse_duration)] pub http_server_read_timeout: Duration, From e155aba5fb189b8564cd362bb88ab460a49de496 Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Tue, 5 Nov 2024 13:55:03 +0100 Subject: [PATCH 7/9] make linter happy --- src/http/body.rs | 12 ++++++------ src/http/cache.rs | 4 ++-- src/http/client/mod.rs | 12 ++++++------ src/http/headers.rs | 2 +- src/http/mod.rs | 2 +- src/http/shed/sharded.rs | 4 ++-- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/http/body.rs b/src/http/body.rs index dcf9d2b..1d8499d 100644 --- a/src/http/body.rs +++ b/src/http/body.rs @@ -73,7 +73,7 @@ impl Stream for SyncBodyDataStream { fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { let mut pinned = pin!(self.inner.get_mut()); - match futures_util::ready!(pinned.as_mut().poll_frame(cx)?) { + match ready!(pinned.as_mut().poll_frame(cx)?) { Some(frame) => match frame.into_data() { Ok(data) => return Poll::Ready(Some(Ok(data))), Err(_frame) => {} @@ -121,7 +121,7 @@ impl NotifyingBody { impl HttpBody for NotifyingBody where D: Buf, - E: std::string::ToString, + E: ToString, { type Data = D; type Error = E; @@ -195,7 +195,7 @@ impl CountingBody { impl HttpBody for CountingBody where D: Buf, - E: std::string::ToString, + E: ToString, { type Data = D; type Error = E; @@ -262,7 +262,7 @@ mod test { blahfoobarblahblah"; let stream = tokio_util::io::ReaderStream::new(&data[..]); - let body = axum::body::Body::from_stream(stream); + let body = Body::from_stream(stream); let (body, rx) = CountingBody::new(body); @@ -278,7 +278,7 @@ mod test { #[tokio::test] async fn test_counting_body_full() { let data = vec![0; 512]; - let buf = bytes::Bytes::from_iter(data.clone()); + let buf = Bytes::from_iter(data.clone()); let body = http_body_util::Full::new(buf); let (body, rx) = CountingBody::new(body); @@ -301,7 +301,7 @@ mod test { blahfoobarblahblah"; let stream = tokio_util::io::ReaderStream::new(&data[..]); - let body = axum::body::Body::from_stream(stream); + let body = Body::from_stream(stream); let sig = 357; let (tx, mut rx) = mpsc::channel(10); diff --git a/src/http/cache.rs b/src/http/cache.rs index 29e583c..af481eb 100644 --- a/src/http/cache.rs +++ b/src/http/cache.rs @@ -421,7 +421,7 @@ impl Run for Cache { interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); loop { - tokio::select! { + select! { biased; () = token.cancelled() => { @@ -871,7 +871,7 @@ mod tests { } } - assert!(refresh == 0); + assert_eq!(refresh, 0); // Check mid-expiration with high beta let now2 = now + Duration::from_secs(30); diff --git a/src/http/client/mod.rs b/src/http/client/mod.rs index 0e094e3..8205a74 100644 --- a/src/http/client/mod.rs +++ b/src/http/client/mod.rs @@ -329,8 +329,8 @@ impl Client for ReqwestClientDynamic { pub fn basic_auth(username: U, password: Option

) -> HeaderValue where - U: std::fmt::Display, - P: std::fmt::Display, + U: fmt::Display, + P: fmt::Display, { use base64::prelude::BASE64_STANDARD; use base64::write::EncoderWriter; @@ -363,9 +363,9 @@ mod test { impl Client for TestClient { async fn execute( &self, - _req: reqwest::Request, - ) -> Result { - let resp = ::http::Response::new(vec![]); + _req: Request, + ) -> Result { + let resp = http::Response::new(vec![]); tokio::time::sleep(Duration::from_millis(100)).await; Ok(resp.into()) } @@ -388,7 +388,7 @@ mod test { let mut futs = vec![]; for _ in 0..200 { let req = - reqwest::Request::new(reqwest::Method::GET, url::Url::parse("http://foo").unwrap()); + Request::new(reqwest::Method::GET, url::Url::parse("http://foo").unwrap()); let cli = cli.clone(); futs.push(async move { cli.execute(req).await }); diff --git a/src/http/headers.rs b/src/http/headers.rs index 69cdade..18d3387 100644 --- a/src/http/headers.rs +++ b/src/http/headers.rs @@ -25,7 +25,7 @@ pub const X_IC_CANISTER_ID: HeaderName = HeaderName::from_static("x-ic-canister- pub const X_IC_COUNTRY_CODE: HeaderName = HeaderName::from_static("x-ic-country-code"); pub const X_REQUEST_ID: HeaderName = HeaderName::from_static("x-request-id"); pub const X_REQUESTED_WITH: HeaderName = HeaderName::from_static("x-requested-with"); -pub const X_REAL_IP: http::HeaderName = http::HeaderName::from_static("x-real-ip"); +pub const X_REAL_IP: HeaderName = HeaderName::from_static("x-real-ip"); // Header values pub const CONTENT_TYPE_CBOR: HeaderValue = HeaderValue::from_static("application/cbor"); diff --git a/src/http/mod.rs b/src/http/mod.rs index 2338c13..b39c393 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -140,7 +140,7 @@ impl AsyncWrite for AsyncCounte mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], - ) -> Poll> { + ) -> Poll> { let poll = pin!(&mut self.inner).poll_write(cx, buf); if let Poll::Ready(Ok(v)) = &poll { self.stats.sent.fetch_add(*v as u64, Ordering::SeqCst); diff --git a/src/http/shed/sharded.rs b/src/http/shed/sharded.rs index 8df254e..6dabd8b 100644 --- a/src/http/shed/sharded.rs +++ b/src/http/shed/sharded.rs @@ -164,11 +164,11 @@ mod test { struct StubExtractor(u8); impl TypeExtractor for StubExtractor { - type Request = Duration; type Type = u8; + type Request = Duration; fn extract(&self, _req: &Self::Request) -> Option { - return Some(self.0); + Some(self.0) } } From f74723944a7d0588fe325a5f3b62ed610ba8551b Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Tue, 5 Nov 2024 14:12:19 +0100 Subject: [PATCH 8/9] fix the TLS verifier not(MacOS) --- Cargo.toml | 3 +++ src/tls/mod.rs | 8 +++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6f56f10..bb047ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,3 +93,6 @@ uuid = { version = "1.10.0", features = ["v7"] } vrl = { version = "0.19.0", default-features = false, features = ["value"] } x509-parser = "0.16.0" zeroize = { version = "1.8.1", features = ["derive"] } + +[target.'cfg(not(target_os = "macos"))'.dependencies] +webpki-roots = "0.26.6" diff --git a/src/tls/mod.rs b/src/tls/mod.rs index bf86a47..11038fc 100644 --- a/src/tls/mod.rs +++ b/src/tls/mod.rs @@ -201,15 +201,13 @@ pub fn prepare_client_config(tls_versions: &[&'static SupportedProtocolVersion]) // new_with_extra_roots() method isn't available on MacOS, see // https://github.com/rustls/rustls-platform-verifier/issues/58 #[cfg(not(target_os = "macos"))] - let verifier = Arc::new(Verifier::new_with_extra_roots( - webpki_roots::TLS_SERVER_ROOTS.to_vec(), - )); + let verifier = Verifier::new_with_extra_roots(webpki_roots::TLS_SERVER_ROOTS.to_vec()).unwrap(); #[cfg(target_os = "macos")] - let verifier = Arc::new(Verifier::new()); + let verifier = Verifier::new(); let mut cfg = ClientConfig::builder_with_protocol_versions(tls_versions) .dangerous() // Nothing really dangerous here - .with_custom_certificate_verifier(verifier) + .with_custom_certificate_verifier(Arc::new(verifier)) .with_no_client_auth(); // Session resumption From a12341758f9b561dc3b80ae1a456873fdf07b428 Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Tue, 5 Nov 2024 16:12:08 +0100 Subject: [PATCH 9/9] remove macos conditional build --- Cargo.toml | 4 +--- src/tls/mod.rs | 9 +++------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bb047ed..aa980bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,8 +91,6 @@ tracing = "0.1.40" url = "2.5.3" uuid = { version = "1.10.0", features = ["v7"] } vrl = { version = "0.19.0", default-features = false, features = ["value"] } +webpki-root-certs = "0.26.6" x509-parser = "0.16.0" zeroize = { version = "1.8.1", features = ["derive"] } - -[target.'cfg(not(target_os = "macos"))'.dependencies] -webpki-roots = "0.26.6" diff --git a/src/tls/mod.rs b/src/tls/mod.rs index 11038fc..9c1c81a 100644 --- a/src/tls/mod.rs +++ b/src/tls/mod.rs @@ -198,12 +198,9 @@ pub fn prepare_client_config(tls_versions: &[&'static SupportedProtocolVersion]) // It also checks OCSP revocation, though OCSP support for Linux platform for now seems be no-op. // https://github.com/rustls/rustls-platform-verifier/issues/99 - // new_with_extra_roots() method isn't available on MacOS, see - // https://github.com/rustls/rustls-platform-verifier/issues/58 - #[cfg(not(target_os = "macos"))] - let verifier = Verifier::new_with_extra_roots(webpki_roots::TLS_SERVER_ROOTS.to_vec()).unwrap(); - #[cfg(target_os = "macos")] - let verifier = Verifier::new(); + let verifier = + Verifier::new_with_extra_roots(webpki_root_certs::TLS_SERVER_ROOT_CERTS.iter().cloned()) + .unwrap(); let mut cfg = ClientConfig::builder_with_protocol_versions(tls_versions) .dangerous() // Nothing really dangerous here