Have simple-request return an error upon failing to find the system's root certificates

This commit is contained in:
Luke Parker
2025-09-18 17:03:16 -04:00
parent 10c126ad92
commit 18a9cf2535
11 changed files with 34 additions and 28 deletions

View File

@@ -52,7 +52,7 @@ pub struct Client {
} }
impl Client { impl Client {
fn connector() -> Connector { fn connector() -> Result<Connector, Error> {
let mut res = HttpConnector::new(); let mut res = HttpConnector::new();
res.set_keepalive(Some(core::time::Duration::from_secs(60))); res.set_keepalive(Some(core::time::Duration::from_secs(60)));
res.set_nodelay(true); res.set_nodelay(true);
@@ -62,27 +62,31 @@ impl Client {
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
let res = HttpsConnectorBuilder::new() let res = HttpsConnectorBuilder::new()
.with_native_roots() .with_native_roots()
.expect("couldn't fetch system's SSL roots") .map_err(|e| {
Error::ConnectionError(
format!("couldn't load system's SSL root certificates: {e:?}").into(),
)
})?
.https_or_http() .https_or_http()
.enable_http1() .enable_http1()
.wrap_connector(res); .wrap_connector(res);
res Ok(res)
} }
pub fn with_connection_pool() -> Client { pub fn with_connection_pool() -> Result<Client, Error> {
Client { Ok(Client {
connection: Connection::ConnectionPool( connection: Connection::ConnectionPool(
HyperClient::builder(TokioExecutor::new()) HyperClient::builder(TokioExecutor::new())
.pool_idle_timeout(core::time::Duration::from_secs(60)) .pool_idle_timeout(core::time::Duration::from_secs(60))
.build(Self::connector()), .build(Self::connector()?),
), ),
} })
} }
pub fn without_connection_pool(host: &str) -> Result<Client, Error> { pub fn without_connection_pool(host: &str) -> Result<Client, Error> {
Ok(Client { Ok(Client {
connection: Connection::Connection { connection: Connection::Connection {
connector: Self::connector(), connector: Self::connector()?,
host: { host: {
let uri: Uri = host.parse().map_err(|_| Error::InvalidUri)?; let uri: Uri = host.parse().map_err(|_| Error::InvalidUri)?;
if uri.host().is_none() { if uri.host().is_none() {
@@ -149,7 +153,7 @@ impl Client {
*connection_lock = Some(requester); *connection_lock = Some(requester);
} }
let connection = connection_lock.as_mut().unwrap(); let connection = connection_lock.as_mut().expect("lock over the connection was poisoned");
let mut err = connection.ready().await.err(); let mut err = connection.ready().await.err();
if err.is_none() { if err.is_none() {
// Send the request // Send the request
@@ -161,7 +165,7 @@ impl Client {
} }
// Since this connection has been put into an error state, drop it // Since this connection has been put into an error state, drop it
*connection_lock = None; *connection_lock = None;
Err(Error::Hyper(err.unwrap()))? Err(Error::Hyper(err.expect("only here if `err` is some yet no error")))?
} }
}; };

View File

@@ -42,7 +42,8 @@ impl Request {
formatted.zeroize(); formatted.zeroize();
self.request.headers_mut().insert( self.request.headers_mut().insert(
hyper::header::AUTHORIZATION, hyper::header::AUTHORIZATION,
HeaderValue::from_str(&format!("Basic {encoded}")).unwrap(), HeaderValue::from_str(&format!("Basic {encoded}"))
.expect("couldn't form header from base64-encoded string"),
); );
encoded.zeroize(); encoded.zeroize();
} }

View File

@@ -62,7 +62,8 @@ impl Rpc {
/// provided to this library, if the RPC has an incompatible argument layout. That is not checked /// provided to this library, if the RPC has an incompatible argument layout. That is not checked
/// at time of RPC creation. /// at time of RPC creation.
pub async fn new(url: String) -> Result<Rpc, RpcError> { pub async fn new(url: String) -> Result<Rpc, RpcError> {
let rpc = Rpc { client: Client::with_connection_pool(), url }; let rpc =
Rpc { client: Client::with_connection_pool().map_err(|_| RpcError::ConnectionError)?, url };
// Make an RPC request to verify the node is reachable and sane // Make an RPC request to verify the node is reachable and sane
let res: String = rpc.rpc_call("help", json!([])).await?; let res: String = rpc.rpc_call("help", json!([])).await?;

View File

@@ -7,7 +7,7 @@ use std::io;
use alloy_json_rpc::{RequestPacket, ResponsePacket}; use alloy_json_rpc::{RequestPacket, ResponsePacket};
use alloy_transport::{TransportError, TransportErrorKind, TransportFut}; use alloy_transport::{TransportError, TransportErrorKind, TransportFut};
use simple_request::{hyper, Request, Client}; use simple_request::{hyper, Error, Request, Client};
use tower::Service; use tower::Service;
@@ -18,8 +18,8 @@ pub struct SimpleRequest {
} }
impl SimpleRequest { impl SimpleRequest {
pub fn new(url: String) -> Self { pub fn new(url: String) -> Result<Self, Error> {
Self { client: Client::with_connection_pool(), url } Ok(Self { client: Client::with_connection_pool()?, url })
} }
} }

View File

@@ -36,7 +36,7 @@ async fn setup_test() -> (AnvilInstance, Arc<RootProvider>, Address) {
let anvil = Anvil::new().spawn(); let anvil = Anvil::new().spawn();
let provider = Arc::new(RootProvider::new( let provider = Arc::new(RootProvider::new(
ClientBuilder::default().transport(SimpleRequest::new(anvil.endpoint()), true), ClientBuilder::default().transport(SimpleRequest::new(anvil.endpoint()).unwrap(), true),
)); ));
let mut address = [0; 20]; let mut address = [0; 20];

View File

@@ -21,7 +21,7 @@ async fn test_deployer() {
let anvil = Anvil::new().arg("--hardfork").arg(network).spawn(); let anvil = Anvil::new().arg("--hardfork").arg(network).spawn();
let provider = Arc::new(RootProvider::new( let provider = Arc::new(RootProvider::new(
ClientBuilder::default().transport(SimpleRequest::new(anvil.endpoint()), true), ClientBuilder::default().transport(SimpleRequest::new(anvil.endpoint()).unwrap(), true),
)); ));
// Deploy the Deployer // Deploy the Deployer

View File

@@ -129,7 +129,7 @@ impl Test {
.spawn(); .spawn();
let provider = Arc::new(RootProvider::new( let provider = Arc::new(RootProvider::new(
ClientBuilder::default().transport(SimpleRequest::new(anvil.endpoint()), true), ClientBuilder::default().transport(SimpleRequest::new(anvil.endpoint()).unwrap(), true),
)); ));
let chain_id = U256::from(provider.get_chain_id().await.unwrap()); let chain_id = U256::from(provider.get_chain_id().await.unwrap());

View File

@@ -61,7 +61,7 @@ async fn main() {
let db = bin::init(); let db = bin::init();
let provider = Arc::new(RootProvider::new( let provider = Arc::new(RootProvider::new(
ClientBuilder::default().transport(SimpleRequest::new(bin::url()), true), ClientBuilder::default().transport(SimpleRequest::new(bin::url()).unwrap(), true),
)); ));
let chain_id = { let chain_id = {

View File

@@ -158,7 +158,7 @@ impl Serai {
} }
pub async fn new(url: String) -> Result<Self, SeraiError> { pub async fn new(url: String) -> Result<Self, SeraiError> {
let client = Client::with_connection_pool(); let client = Client::with_connection_pool().map_err(|_| SeraiError::ConnectionError)?;
let mut res = Serai { url, client, genesis: [0xfe; 32] }; let mut res = Serai { url, client, genesis: [0xfe; 32] };
res.genesis = res.block_hash(0).await?.ok_or_else(|| { res.genesis = res.block_hash(0).await?.ok_or_else(|| {
SeraiError::InvalidNode("node didn't have the first block's hash".to_string()) SeraiError::InvalidNode("node didn't have the first block's hash".to_string())

View File

@@ -277,7 +277,7 @@ impl Coordinator {
}; };
let provider = Arc::new(RootProvider::<_, Ethereum>::new( let provider = Arc::new(RootProvider::<_, Ethereum>::new(
ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()).unwrap(), true),
)); ));
if handle if handle
@@ -417,7 +417,7 @@ impl Coordinator {
}; };
let provider = RootProvider::<_, Ethereum>::new( let provider = RootProvider::<_, Ethereum>::new(
ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()).unwrap(), true),
); );
let start = provider let start = provider
.get_block(BlockNumberOrTag::Latest.into(), BlockTransactionsKind::Hashes) .get_block(BlockNumberOrTag::Latest.into(), BlockTransactionsKind::Hashes)
@@ -509,7 +509,7 @@ impl Coordinator {
let (expected_number, state) = { let (expected_number, state) = {
let provider = RootProvider::<_, Ethereum>::new( let provider = RootProvider::<_, Ethereum>::new(
ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()).unwrap(), true),
); );
let expected_number = provider let expected_number = provider
@@ -528,7 +528,7 @@ impl Coordinator {
for coordinator in others { for coordinator in others {
let rpc_url = network_rpc(coordinator.network, ops, &coordinator.network_handle); let rpc_url = network_rpc(coordinator.network, ops, &coordinator.network_handle);
let provider = RootProvider::<_, Ethereum>::new( let provider = RootProvider::<_, Ethereum>::new(
ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()).unwrap(), true),
); );
assert!(provider assert!(provider
.raw_request::<_, bool>("anvil_loadState".into(), &[&state]) .raw_request::<_, bool>("anvil_loadState".into(), &[&state])
@@ -605,7 +605,7 @@ impl Coordinator {
}; };
let provider = RootProvider::<_, Ethereum>::new( let provider = RootProvider::<_, Ethereum>::new(
ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()).unwrap(), true),
); );
let _ = provider.send_raw_transaction(tx).await.unwrap(); let _ = provider.send_raw_transaction(tx).await.unwrap();
} }
@@ -662,7 +662,7 @@ impl Coordinator {
ExternalNetworkId::Ethereum => { ExternalNetworkId::Ethereum => {
/* /*
let provider = RootProvider::<_, Ethereum>::new( let provider = RootProvider::<_, Ethereum>::new(
ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()).unwrap(), true),
); );
let mut hash = [0; 32]; let mut hash = [0; 32];
hash.copy_from_slice(tx); hash.copy_from_slice(tx);

View File

@@ -165,7 +165,7 @@ impl Wallet {
ethereum_serai::crypto::address(&(<Secp256k1 as WrappedGroup>::generator() * key)); ethereum_serai::crypto::address(&(<Secp256k1 as WrappedGroup>::generator() * key));
let provider = RootProvider::<_, Ethereum>::new( let provider = RootProvider::<_, Ethereum>::new(
ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()).unwrap(), true),
); );
provider provider
@@ -319,7 +319,7 @@ impl Wallet {
let one_eth = eighteen_decimals; let one_eth = eighteen_decimals;
let provider = Arc::new(RootProvider::<_, Ethereum>::new( let provider = Arc::new(RootProvider::<_, Ethereum>::new(
ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true), ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()).unwrap(), true),
)); ));
let to_as_key = PublicKey::new( let to_as_key = PublicKey::new(