Loading Experiments.h +1 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ class Experiments { "dot_validation_latency_factor", "dot_validation_latency_offset_ms", "doh", "doh_early_data", "doh_query_timeout_ms", "doh_probe_timeout_ms", "doh_idle_timeout_ms", Loading PrivateDnsConfiguration.cpp +3 −1 Original line number Diff line number Diff line Loading @@ -510,10 +510,12 @@ int PrivateDnsConfiguration::setDoh(int32_t netId, uint32_t mark, getTimeoutFromFlag("doh_idle_timeout_ms", kDohIdleDefaultTimeoutMs), .use_session_resumption = Experiments::getInstance()->getFlag("doh_session_resumption", 0) == 1, .enable_early_data = Experiments::getInstance()->getFlag("doh_early_data", 0) == 1, }; LOG(DEBUG) << __func__ << ": probe_timeout_ms=" << flags.probe_timeout_ms << ", idle_timeout_ms=" << flags.idle_timeout_ms << ", use_session_resumption=" << flags.use_session_resumption; << ", use_session_resumption=" << flags.use_session_resumption << ", enable_early_data=" << flags.enable_early_data; return doh_net_new(mDohDispatcher, netId, dohId.httpsTemplate.c_str(), dohId.host.c_str(), dohId.ipAddr.c_str(), mark, caCert.c_str(), &flags); Loading doh.h +1 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ struct FeatureFlags { uint64_t probe_timeout_ms; uint64_t idle_timeout_ms; bool use_session_resumption; bool enable_early_data; }; using ValidationCallback = void (*)(uint32_t net_id, bool success, const char* ip_addr, Loading doh/config.rs +36 −13 Original line number Diff line number Diff line Loading @@ -63,6 +63,9 @@ impl Config { } None => config.verify_peer(false), } if key.enable_early_data { config.enable_early_data(); } // Some of these configs are necessary, or the server can't respond the HTTP/3 request. config.set_max_idle_timeout(key.max_idle_timeout); Loading Loading @@ -126,6 +129,7 @@ pub struct Cache { pub struct Key { pub cert_path: Option<String>, pub max_idle_timeout: u64, pub enable_early_data: bool, } impl Cache { Loading Loading @@ -174,13 +178,15 @@ impl Cache { #[test] fn create_quiche_config() { assert!( Config::from_key(&Key { cert_path: None, max_idle_timeout: 1000 }).is_ok(), Config::from_key(&Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: true }) .is_ok(), "quiche config without cert creating failed" ); assert!( Config::from_key(&Key { cert_path: Some("data/local/tmp/".to_string()), max_idle_timeout: 1000 max_idle_timeout: 1000, enable_early_data: true, }) .is_ok(), "quiche config with cert creating failed" Loading @@ -191,38 +197,53 @@ fn create_quiche_config() { fn shared_cache() { let cache_a = Cache::new(); let cache_b = cache_a.clone(); let config_a = cache_a.get(&Key { cert_path: None, max_idle_timeout: 1000 }).unwrap(); let config_a = cache_a .get(&Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: true }) .unwrap(); assert_eq!(Arc::strong_count(&config_a.0), 2); let _config_b = cache_b.get(&Key { cert_path: None, max_idle_timeout: 1000 }).unwrap(); let _config_b = cache_b .get(&Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: true }) .unwrap(); assert_eq!(Arc::strong_count(&config_a.0), 3); } #[test] fn different_keys() { let cache = Cache::new(); let key_a = Key { cert_path: None, max_idle_timeout: 1000 }; let key_b = Key { cert_path: Some("a".to_string()), max_idle_timeout: 1000 }; let key_c = Key { cert_path: Some("a".to_string()), max_idle_timeout: 5000 }; let key_a = Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: false }; let key_b = Key { cert_path: Some("a".to_string()), max_idle_timeout: 1000, enable_early_data: false }; let key_c = Key { cert_path: Some("a".to_string()), max_idle_timeout: 5000, enable_early_data: false }; let key_d = Key { cert_path: Some("a".to_string()), max_idle_timeout: 5000, enable_early_data: true }; let config_a = cache.get(&key_a).unwrap(); let config_b = cache.get(&key_b).unwrap(); let _config_b = cache.get(&key_b).unwrap(); let config_c = cache.get(&key_c).unwrap(); let _config_c = cache.get(&key_c).unwrap(); let config_d = cache.get(&key_d).unwrap(); let _config_d = cache.get(&key_d).unwrap(); assert_eq!(Arc::strong_count(&config_a.0), 1); assert_eq!(Arc::strong_count(&config_b.0), 2); assert_eq!(Arc::strong_count(&config_c.0), 2); // config_c was most recently created, so it should have an extra strong reference due to // config_d was most recently created, so it should have an extra strong reference due to // keep-alive in the cache. assert_eq!(Arc::strong_count(&config_c.0), 3); assert_eq!(Arc::strong_count(&config_d.0), 3); } #[test] fn lifetimes() { let cache = Cache::new(); let key_a = Key { cert_path: Some("a".to_string()), max_idle_timeout: 1000 }; let key_b = Key { cert_path: Some("b".to_string()), max_idle_timeout: 1000 }; let config_none = cache.get(&Key { cert_path: None, max_idle_timeout: 1000 }).unwrap(); let key_a = Key { cert_path: Some("a".to_string()), max_idle_timeout: 1000, enable_early_data: true }; let key_b = Key { cert_path: Some("b".to_string()), max_idle_timeout: 1000, enable_early_data: true }; let config_none = cache .get(&Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: true }) .unwrap(); let config_a = cache.get(&key_a).unwrap(); let config_b = cache.get(&key_b).unwrap(); Loading Loading @@ -268,7 +289,9 @@ fn lifetimes() { #[tokio::test] async fn quiche_connect() { use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; let mut config = Config::from_key(&Key { cert_path: None, max_idle_timeout: 10 }).unwrap(); let mut config = Config::from_key(&Key { cert_path: None, max_idle_timeout: 10, enable_early_data: true }) .unwrap(); let socket_addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 42)); let conn_id = quiche::ConnectionId::from_ref(&[]); quiche::connect(None, &conn_id, socket_addr, config.take().await.deref_mut()).unwrap(); Loading doh/connection/driver.rs +17 −16 Original line number Diff line number Diff line Loading @@ -194,6 +194,19 @@ impl Driver { } async fn drive_once(mut self) -> Result<Self> { // If the QUIC connection is live, but the HTTP/3 is not, try to bring it up if self.quiche_conn.is_established() || self.quiche_conn.is_in_early_data() { info!( "Connection {} established on network {}", self.quiche_conn.trace_id(), self.net_id ); let h3_config = h3::Config::new()?; let h3_conn = h3::Connection::with_transport(&mut self.quiche_conn, &h3_config)?; self = H3Driver::new(self, h3_conn).drive().await?; let _ = self.status_tx.send(Status::QUIC); } let timer = optional_timeout(self.quiche_conn.timeout(), self.net_id); select! { // If a quiche timer would fire, call their callback Loading @@ -210,19 +223,6 @@ impl Driver { // Any of the actions in the select could require us to send packets to the peer self.flush_tx().await?; // If the QUIC connection is live, but the HTTP/3 is not, try to bring it up if self.quiche_conn.is_established() { info!( "Connection {} established on network {}", self.quiche_conn.trace_id(), self.net_id ); let h3_config = h3::Config::new()?; let h3_conn = h3::Connection::with_transport(&mut self.quiche_conn, &h3_config)?; self = H3Driver::new(self, h3_conn).drive().await?; let _ = self.status_tx.send(Status::QUIC); } // If the connection has entered draining state (the server is closing the connection), // tell the status watcher not to use the connection. Besides, per Quiche document, // the connection should not be dropped until is_closed() returns true. Loading Loading @@ -285,7 +285,8 @@ impl H3Driver { } select! { // Only attempt to enqueue new requests if we have no buffered request and aren't // closing // closing. Maybe limit the number of in-flight queries if the handshake // still hasn't finished. msg = self.driver.request_rx.recv(), if !self.driver.closing && self.buffered_request.is_none() => { match msg { Some(request) => self.handle_request(request)?, Loading Loading @@ -321,8 +322,8 @@ impl H3Driver { } fn handle_request(&mut self, request: Request) -> Result<()> { info!("Handling DNS request on network {}, stats=[{:?}], peer_streams_left_bidi={}, peer_streams_left_uni={}", self.driver.net_id, self.driver.quiche_conn.stats(), self.driver.quiche_conn.peer_streams_left_bidi(), self.driver.quiche_conn.peer_streams_left_uni()); info!("Handling DNS request on network {}, is_in_early_data={}, stats=[{:?}], peer_streams_left_bidi={}, peer_streams_left_uni={}", self.driver.net_id, self.driver.quiche_conn.is_in_early_data(), self.driver.quiche_conn.stats(), self.driver.quiche_conn.peer_streams_left_bidi(), self.driver.quiche_conn.peer_streams_left_uni()); // If the request has already timed out, don't issue it to the server. if let Some(expiry) = request.expiry { if BootTime::now() > expiry { Loading Loading
Experiments.h +1 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ class Experiments { "dot_validation_latency_factor", "dot_validation_latency_offset_ms", "doh", "doh_early_data", "doh_query_timeout_ms", "doh_probe_timeout_ms", "doh_idle_timeout_ms", Loading
PrivateDnsConfiguration.cpp +3 −1 Original line number Diff line number Diff line Loading @@ -510,10 +510,12 @@ int PrivateDnsConfiguration::setDoh(int32_t netId, uint32_t mark, getTimeoutFromFlag("doh_idle_timeout_ms", kDohIdleDefaultTimeoutMs), .use_session_resumption = Experiments::getInstance()->getFlag("doh_session_resumption", 0) == 1, .enable_early_data = Experiments::getInstance()->getFlag("doh_early_data", 0) == 1, }; LOG(DEBUG) << __func__ << ": probe_timeout_ms=" << flags.probe_timeout_ms << ", idle_timeout_ms=" << flags.idle_timeout_ms << ", use_session_resumption=" << flags.use_session_resumption; << ", use_session_resumption=" << flags.use_session_resumption << ", enable_early_data=" << flags.enable_early_data; return doh_net_new(mDohDispatcher, netId, dohId.httpsTemplate.c_str(), dohId.host.c_str(), dohId.ipAddr.c_str(), mark, caCert.c_str(), &flags); Loading
doh.h +1 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ struct FeatureFlags { uint64_t probe_timeout_ms; uint64_t idle_timeout_ms; bool use_session_resumption; bool enable_early_data; }; using ValidationCallback = void (*)(uint32_t net_id, bool success, const char* ip_addr, Loading
doh/config.rs +36 −13 Original line number Diff line number Diff line Loading @@ -63,6 +63,9 @@ impl Config { } None => config.verify_peer(false), } if key.enable_early_data { config.enable_early_data(); } // Some of these configs are necessary, or the server can't respond the HTTP/3 request. config.set_max_idle_timeout(key.max_idle_timeout); Loading Loading @@ -126,6 +129,7 @@ pub struct Cache { pub struct Key { pub cert_path: Option<String>, pub max_idle_timeout: u64, pub enable_early_data: bool, } impl Cache { Loading Loading @@ -174,13 +178,15 @@ impl Cache { #[test] fn create_quiche_config() { assert!( Config::from_key(&Key { cert_path: None, max_idle_timeout: 1000 }).is_ok(), Config::from_key(&Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: true }) .is_ok(), "quiche config without cert creating failed" ); assert!( Config::from_key(&Key { cert_path: Some("data/local/tmp/".to_string()), max_idle_timeout: 1000 max_idle_timeout: 1000, enable_early_data: true, }) .is_ok(), "quiche config with cert creating failed" Loading @@ -191,38 +197,53 @@ fn create_quiche_config() { fn shared_cache() { let cache_a = Cache::new(); let cache_b = cache_a.clone(); let config_a = cache_a.get(&Key { cert_path: None, max_idle_timeout: 1000 }).unwrap(); let config_a = cache_a .get(&Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: true }) .unwrap(); assert_eq!(Arc::strong_count(&config_a.0), 2); let _config_b = cache_b.get(&Key { cert_path: None, max_idle_timeout: 1000 }).unwrap(); let _config_b = cache_b .get(&Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: true }) .unwrap(); assert_eq!(Arc::strong_count(&config_a.0), 3); } #[test] fn different_keys() { let cache = Cache::new(); let key_a = Key { cert_path: None, max_idle_timeout: 1000 }; let key_b = Key { cert_path: Some("a".to_string()), max_idle_timeout: 1000 }; let key_c = Key { cert_path: Some("a".to_string()), max_idle_timeout: 5000 }; let key_a = Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: false }; let key_b = Key { cert_path: Some("a".to_string()), max_idle_timeout: 1000, enable_early_data: false }; let key_c = Key { cert_path: Some("a".to_string()), max_idle_timeout: 5000, enable_early_data: false }; let key_d = Key { cert_path: Some("a".to_string()), max_idle_timeout: 5000, enable_early_data: true }; let config_a = cache.get(&key_a).unwrap(); let config_b = cache.get(&key_b).unwrap(); let _config_b = cache.get(&key_b).unwrap(); let config_c = cache.get(&key_c).unwrap(); let _config_c = cache.get(&key_c).unwrap(); let config_d = cache.get(&key_d).unwrap(); let _config_d = cache.get(&key_d).unwrap(); assert_eq!(Arc::strong_count(&config_a.0), 1); assert_eq!(Arc::strong_count(&config_b.0), 2); assert_eq!(Arc::strong_count(&config_c.0), 2); // config_c was most recently created, so it should have an extra strong reference due to // config_d was most recently created, so it should have an extra strong reference due to // keep-alive in the cache. assert_eq!(Arc::strong_count(&config_c.0), 3); assert_eq!(Arc::strong_count(&config_d.0), 3); } #[test] fn lifetimes() { let cache = Cache::new(); let key_a = Key { cert_path: Some("a".to_string()), max_idle_timeout: 1000 }; let key_b = Key { cert_path: Some("b".to_string()), max_idle_timeout: 1000 }; let config_none = cache.get(&Key { cert_path: None, max_idle_timeout: 1000 }).unwrap(); let key_a = Key { cert_path: Some("a".to_string()), max_idle_timeout: 1000, enable_early_data: true }; let key_b = Key { cert_path: Some("b".to_string()), max_idle_timeout: 1000, enable_early_data: true }; let config_none = cache .get(&Key { cert_path: None, max_idle_timeout: 1000, enable_early_data: true }) .unwrap(); let config_a = cache.get(&key_a).unwrap(); let config_b = cache.get(&key_b).unwrap(); Loading Loading @@ -268,7 +289,9 @@ fn lifetimes() { #[tokio::test] async fn quiche_connect() { use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; let mut config = Config::from_key(&Key { cert_path: None, max_idle_timeout: 10 }).unwrap(); let mut config = Config::from_key(&Key { cert_path: None, max_idle_timeout: 10, enable_early_data: true }) .unwrap(); let socket_addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 42)); let conn_id = quiche::ConnectionId::from_ref(&[]); quiche::connect(None, &conn_id, socket_addr, config.take().await.deref_mut()).unwrap(); Loading
doh/connection/driver.rs +17 −16 Original line number Diff line number Diff line Loading @@ -194,6 +194,19 @@ impl Driver { } async fn drive_once(mut self) -> Result<Self> { // If the QUIC connection is live, but the HTTP/3 is not, try to bring it up if self.quiche_conn.is_established() || self.quiche_conn.is_in_early_data() { info!( "Connection {} established on network {}", self.quiche_conn.trace_id(), self.net_id ); let h3_config = h3::Config::new()?; let h3_conn = h3::Connection::with_transport(&mut self.quiche_conn, &h3_config)?; self = H3Driver::new(self, h3_conn).drive().await?; let _ = self.status_tx.send(Status::QUIC); } let timer = optional_timeout(self.quiche_conn.timeout(), self.net_id); select! { // If a quiche timer would fire, call their callback Loading @@ -210,19 +223,6 @@ impl Driver { // Any of the actions in the select could require us to send packets to the peer self.flush_tx().await?; // If the QUIC connection is live, but the HTTP/3 is not, try to bring it up if self.quiche_conn.is_established() { info!( "Connection {} established on network {}", self.quiche_conn.trace_id(), self.net_id ); let h3_config = h3::Config::new()?; let h3_conn = h3::Connection::with_transport(&mut self.quiche_conn, &h3_config)?; self = H3Driver::new(self, h3_conn).drive().await?; let _ = self.status_tx.send(Status::QUIC); } // If the connection has entered draining state (the server is closing the connection), // tell the status watcher not to use the connection. Besides, per Quiche document, // the connection should not be dropped until is_closed() returns true. Loading Loading @@ -285,7 +285,8 @@ impl H3Driver { } select! { // Only attempt to enqueue new requests if we have no buffered request and aren't // closing // closing. Maybe limit the number of in-flight queries if the handshake // still hasn't finished. msg = self.driver.request_rx.recv(), if !self.driver.closing && self.buffered_request.is_none() => { match msg { Some(request) => self.handle_request(request)?, Loading Loading @@ -321,8 +322,8 @@ impl H3Driver { } fn handle_request(&mut self, request: Request) -> Result<()> { info!("Handling DNS request on network {}, stats=[{:?}], peer_streams_left_bidi={}, peer_streams_left_uni={}", self.driver.net_id, self.driver.quiche_conn.stats(), self.driver.quiche_conn.peer_streams_left_bidi(), self.driver.quiche_conn.peer_streams_left_uni()); info!("Handling DNS request on network {}, is_in_early_data={}, stats=[{:?}], peer_streams_left_bidi={}, peer_streams_left_uni={}", self.driver.net_id, self.driver.quiche_conn.is_in_early_data(), self.driver.quiche_conn.stats(), self.driver.quiche_conn.peer_streams_left_bidi(), self.driver.quiche_conn.peer_streams_left_uni()); // If the request has already timed out, don't issue it to the server. if let Some(expiry) = request.expiry { if BootTime::now() > expiry { Loading