diff --git a/Cargo.lock b/Cargo.lock index c582c1aca..622c2e775 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5148,7 +5148,6 @@ dependencies = [ "heck 0.5.0", "hex", "home", - "http 1.1.0", "humantime", "itertools 0.10.5", "jsonrpsee-core", diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 074be1811..94d15f024 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -77,10 +77,9 @@ ed25519-dalek = { workspace = true } reqwest = { version = "0.12.7", features = ["json", "blocking", "stream"] } jsonrpsee-http-client = "0.20.1" jsonrpsee-core = "0.20.1" -http = "1.1.0" regex = "1.6.0" wasm-opt = { version = "0.114.0", optional = true } -chrono = { version = "0.4.27", features = ["serde"]} +chrono = { version = "0.4.27", features = ["serde"] } rpassword = "7.2.0" dirs = "4.0.0" toml = "0.5.9" @@ -106,12 +105,12 @@ gix = { version = "0.58.0", default-features = false, features = [ "blocking-http-transport-reqwest-rust-tls", "worktree-mutation", ] } -async-compression = { version = "0.4.12", features = [ "tokio", "gzip" ] } +async-compression = { version = "0.4.12", features = ["tokio", "gzip"] } tempfile = "3.8.1" toml_edit = "0.21.0" rust-embed = { version = "8.2.0", features = ["debug-embed"] } -bollard = { workspace=true } +bollard = { workspace = true } futures-util = "0.3.30" futures = "0.3.30" home = "0.5.9" @@ -125,7 +124,12 @@ open = "5.3.0" url = "2.5.2" [target.'cfg(unix)'.dependencies] -reqwest = { version = "0.12.7", features = ["json", "blocking", "stream", "native-tls-vendored"] } +reqwest = { version = "0.12.7", features = [ + "json", + "blocking", + "stream", + "native-tls-vendored", +] } [build-dependencies] crate-git-revision = "0.0.4" diff --git a/cmd/soroban-cli/src/commands/network/mod.rs b/cmd/soroban-cli/src/commands/network/mod.rs index 8aedebcac..8dd61b394 100644 --- a/cmd/soroban-cli/src/commands/network/mod.rs +++ b/cmd/soroban-cli/src/commands/network/mod.rs @@ -67,8 +67,6 @@ pub enum Error { #[error("network arg or rpc url and network passphrase are required if using the network")] Network, #[error(transparent)] - Http(#[from] http::Error), - #[error(transparent)] Rpc(#[from] rpc::Error), #[error(transparent)] HttpClient(#[from] reqwest::Error), diff --git a/cmd/soroban-cli/src/commands/snapshot/create.rs b/cmd/soroban-cli/src/commands/snapshot/create.rs index 400aa3cb9..848def914 100644 --- a/cmd/soroban-cli/src/commands/snapshot/create.rs +++ b/cmd/soroban-cli/src/commands/snapshot/create.rs @@ -2,9 +2,9 @@ use async_compression::tokio::bufread::GzipDecoder; use bytesize::ByteSize; use clap::{arg, Parser, ValueEnum}; use futures::StreamExt; -use http::Uri; use humantime::format_duration; use itertools::{Either, Itertools}; +use reqwest::Url; use sha2::{Digest, Sha256}; use soroban_ledger_snapshot::LedgerSnapshot; use std::{ @@ -86,7 +86,7 @@ pub struct Cmd { network: config::network::Args, /// Archive URL #[arg(long, help_heading = HEADING_RPC, env = "STELLAR_ARCHIVE_URL")] - archive_url: Option, + archive_url: Option, } #[derive(thiserror::Error, Debug)] @@ -102,7 +102,7 @@ pub enum Error { #[error("opening cached bucket to read: {0}")] ReadOpeningCachedBucket(io::Error), #[error("parsing bucket url: {0}")] - ParsingBucketUrl(http::uri::InvalidUri), + ParsingBucketUrl(url::ParseError), #[error("getting bucket: {0}")] GettingBucket(reqwest::Error), #[error("getting bucket: got status code {0}")] @@ -366,7 +366,7 @@ impl Cmd { Ok(()) } - fn archive_url(&self) -> Result { + fn archive_url(&self) -> Result { // Return the configured archive URL, or if one is not configured, guess // at an appropriate archive URL given the network passphrase. self.archive_url @@ -384,7 +384,7 @@ impl Cmd { passphrase::LOCAL => Some("http://localhost:8000/archive"), _ => None, } - .map(|s| Uri::from_str(s).expect("archive url valid")) + .map(|s| Url::from_str(s).expect("archive url valid")) }) }) .ok_or(Error::ArchiveUrlNotConfigured) @@ -393,7 +393,7 @@ impl Cmd { async fn get_history( print: &print::Print, - archive_url: &Uri, + archive_url: &Url, ledger: Option, ) -> Result { let archive_url = archive_url.to_string(); @@ -407,11 +407,11 @@ async fn get_history( } else { format!("{archive_url}/.well-known/stellar-history.json") }; - let history_url = Uri::from_str(&history_url).unwrap(); + let history_url = Url::from_str(&history_url).unwrap(); print.globe(format!("Downloading history {history_url}")); - let response = reqwest::get(&history_url.to_string()) + let response = reqwest::get(history_url.as_str()) .await .map_err(Error::DownloadingHistory)?; @@ -445,7 +445,7 @@ async fn get_history( async fn cache_bucket( print: &print::Print, - archive_url: &Uri, + archive_url: &Url, bucket_index: usize, bucket: &str, ) -> Result { @@ -460,9 +460,9 @@ async fn cache_bucket( print.globe(format!("Downloading bucket {bucket_index} {bucket}…")); - let bucket_url = Uri::from_str(&bucket_url).map_err(Error::ParsingBucketUrl)?; + let bucket_url = Url::from_str(&bucket_url).map_err(Error::ParsingBucketUrl)?; - let response = reqwest::get(&bucket_url.to_string()) + let response = reqwest::get(bucket_url.as_str()) .await .map_err(Error::GettingBucket)?; diff --git a/cmd/soroban-cli/src/config/data.rs b/cmd/soroban-cli/src/config/data.rs index 23dedc619..b0dc2e2af 100644 --- a/cmd/soroban-cli/src/config/data.rs +++ b/cmd/soroban-cli/src/config/data.rs @@ -1,6 +1,6 @@ use crate::rpc::{GetTransactionResponse, GetTransactionResponseRaw, SimulateTransactionResponse}; use directories::ProjectDirs; -use http::Uri; +use reqwest::Url; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -15,7 +15,7 @@ pub enum Error { #[error(transparent)] SerdeJson(#[from] serde_json::Error), #[error(transparent)] - Http(#[from] http::uri::InvalidUri), + InvalidUrl(#[from] url::ParseError), #[error(transparent)] Ulid(#[from] ulid::DecodeError), #[error(transparent)] @@ -56,7 +56,7 @@ pub fn bucket_dir() -> Result { Ok(dir) } -pub fn write(action: Action, rpc_url: &Uri) -> Result { +pub fn write(action: Action, rpc_url: &Url) -> Result { let data = Data { action, rpc_url: rpc_url.to_string(), @@ -67,10 +67,10 @@ pub fn write(action: Action, rpc_url: &Uri) -> Result { Ok(id) } -pub fn read(id: &ulid::Ulid) -> Result<(Action, Uri), Error> { +pub fn read(id: &ulid::Ulid) -> Result<(Action, Url), Error> { let file = actions_dir()?.join(id.to_string()).with_extension("json"); let data: Data = serde_json::from_str(&std::fs::read_to_string(file)?)?; - Ok((data.action, http::Uri::from_str(&data.rpc_url)?)) + Ok((data.action, Url::from_str(&data.rpc_url)?)) } pub fn write_spec(hash: &str, spec_entries: &[xdr::ScSpecEntry]) -> Result<(), Error> { @@ -117,7 +117,7 @@ pub fn list_actions() -> Result, Error> { .collect::, Error>>() } -pub struct DatedAction(ulid::Ulid, Action, Uri); +pub struct DatedAction(ulid::Ulid, Action, Url); impl std::fmt::Display for DatedAction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -200,7 +200,7 @@ mod test { fn test_write_read() { let t = assert_fs::TempDir::new().unwrap(); std::env::set_var(XDG_DATA_HOME, t.path().to_str().unwrap()); - let rpc_uri = http::uri::Uri::from_str("http://localhost:8000").unwrap(); + let rpc_uri = Url::from_str("http://localhost:8000").unwrap(); let sim = SimulateTransactionResponse::default(); let original_action: Action = sim.into(); diff --git a/cmd/soroban-cli/src/config/network.rs b/cmd/soroban-cli/src/config/network.rs index 04ab45dc8..88c022738 100644 --- a/cmd/soroban-cli/src/config/network.rs +++ b/cmd/soroban-cli/src/config/network.rs @@ -1,9 +1,9 @@ -use std::str::FromStr; - use clap::arg; use phf::phf_map; +use reqwest::Url; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::str::FromStr; use stellar_strkey::ed25519::PublicKey; use crate::{ @@ -21,8 +21,6 @@ pub enum Error { #[error("network arg or rpc url and network passphrase are required if using the network")] Network, #[error(transparent)] - Http(#[from] http::Error), - #[error(transparent)] Rpc(#[from] rpc::Error), #[error(transparent)] HttpClient(#[from] reqwest::Error), @@ -106,28 +104,24 @@ pub struct Network { } impl Network { - pub async fn helper_url(&self, addr: &str) -> Result { - use http::Uri; + pub async fn helper_url(&self, addr: &str) -> Result { tracing::debug!("address {addr:?}"); - let rpc_uri = Uri::from_str(&self.rpc_url) + let rpc_url = Url::from_str(&self.rpc_url) .map_err(|_| Error::InvalidUrl(self.rpc_url.to_string()))?; if self.network_passphrase.as_str() == passphrase::LOCAL { - let auth = rpc_uri.authority().unwrap().clone(); - let scheme = rpc_uri.scheme_str().unwrap(); - Ok(Uri::builder() - .authority(auth) - .scheme(scheme) - .path_and_query(format!("/friendbot?addr={addr}")) - .build()?) + let mut local_url = rpc_url; + local_url.set_path("/friendbot"); + local_url.set_query(Some(&format!("addr={addr}"))); + Ok(local_url) } else { let client = Client::new(&self.rpc_url)?; let network = client.get_network().await?; tracing::debug!("network {network:?}"); - let uri = client.friendbot_url().await?; - tracing::debug!("URI {uri:?}"); - Uri::from_str(&format!("{uri}?addr={addr}")).map_err(|e| { + let url = client.friendbot_url().await?; + tracing::debug!("URL {url:?}"); + Url::from_str(&format!("{url}?addr={addr}")).map_err(|e| { tracing::error!("{e}"); - Error::InvalidUrl(uri.to_string()) + Error::InvalidUrl(url.to_string()) }) } } @@ -136,7 +130,7 @@ impl Network { pub async fn fund_address(&self, addr: &PublicKey) -> Result<(), Error> { let uri = self.helper_url(&addr.to_string()).await?; tracing::debug!("URL {uri:?}"); - let response = reqwest::get(uri.to_string()).await?; + let response = reqwest::get(uri.as_str()).await?; let request_successful = response.status().is_success(); let body = response.bytes().await?; @@ -161,8 +155,8 @@ impl Network { Ok(()) } - pub fn rpc_uri(&self) -> Result { - http::Uri::from_str(&self.rpc_url).map_err(|_| Error::InvalidUrl(self.rpc_url.to_string())) + pub fn rpc_uri(&self) -> Result { + Url::from_str(&self.rpc_url).map_err(|_| Error::InvalidUrl(self.rpc_url.to_string())) } } @@ -194,3 +188,33 @@ impl From<&(&str, &str)> for Network { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_helper_url_local_network() { + let network = Network { + rpc_url: "http://localhost:8000".to_string(), + network_passphrase: passphrase::LOCAL.to_string(), + }; + + let result = network + .helper_url("GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI") + .await; + + assert!(result.is_ok()); + let url = result.unwrap(); + assert_eq!(url.as_str(), "http://localhost:8000/friendbot?addr=GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI"); + } + + #[tokio::test] + async fn test_helper_url_test_network() { + // It might be a bit difficult to conduct client mock here. + let friendbot_url = "https://friendbot.stellar.org/secret_key"; + let addr = "GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI"; + let url = Url::from_str(&format!("{friendbot_url}?addr={addr}")).unwrap(); + assert_eq!(url.as_str(), "https://friendbot.stellar.org/secret_key?addr=GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI"); + } +} diff --git a/cmd/soroban-cli/src/upgrade_check.rs b/cmd/soroban-cli/src/upgrade_check.rs index 957457348..41c8df003 100644 --- a/cmd/soroban-cli/src/upgrade_check.rs +++ b/cmd/soroban-cli/src/upgrade_check.rs @@ -34,7 +34,10 @@ async fn fetch_latest_crate_info() -> Result> { .default_headers({ // crates.io requires a User-Agent header let mut headers = reqwest::header::HeaderMap::new(); - headers.insert("User-Agent", reqwest::header::HeaderValue::from_static(crate_name)); + headers.insert( + "User-Agent", + reqwest::header::HeaderValue::from_static(crate_name), + ); headers }) .build()?;