feat: unify HTTP client creation and enable domain fronting
Enhanced the base nym_http_api_client to reduce fragmentation and enable domain fronting: - Added SerializationFormat enum for explicit JSON/bincode choice (no auto-detection) - Added from_network() method to create clients from NymNetworkDetails with domain fronting - Added with_bincode() builder method for explicit serialization configuration - Set Accept header based on serialization preference - Added deprecation paths for NymApiClient wrapper and nym_api::Client re-export - Enabled domain fronting support via network defaults feature This is part of a broader effort to consolidate HTTP client implementations across the codebase, reducing ~500 lines of wrapper code and providing automatic domain fronting for censorship resistance.
This commit is contained in:
@@ -385,6 +385,11 @@ impl<C, S> Client<C, S> {
|
||||
}
|
||||
}
|
||||
|
||||
/// DEPRECATED: Use nym_http_api_client::Client with from_network() or with_bincode() instead
|
||||
#[deprecated(
|
||||
since = "1.2.0",
|
||||
note = "Use nym_http_api_client::Client::from_network() or ClientBuilder::with_bincode() instead"
|
||||
)]
|
||||
#[derive(Clone)]
|
||||
pub struct NymApiClient {
|
||||
pub use_bincode: bool,
|
||||
|
||||
@@ -50,6 +50,8 @@ use time::Date;
|
||||
use tracing::instrument;
|
||||
|
||||
pub use nym_coconut_dkg_common::types::EpochId;
|
||||
// DEPRECATED: Use nym_http_api_client::Client directly
|
||||
#[deprecated(since = "1.2.0", note = "Use nym_http_api_client::Client directly")]
|
||||
pub use nym_http_api_client::Client;
|
||||
|
||||
pub mod error;
|
||||
|
||||
@@ -13,6 +13,7 @@ license.workspace = true
|
||||
[features]
|
||||
default=["tunneling"]
|
||||
tunneling=[]
|
||||
network-defaults = ["dep:nym-network-defaults"]
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
@@ -34,6 +35,7 @@ mime = { workspace = true }
|
||||
|
||||
nym-http-api-common = { path = "../http-api-common", default-features = false }
|
||||
nym-bin-common = { path = "../bin-common" }
|
||||
nym-network-defaults = { path = "../network-defaults", optional = true }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
|
||||
hickory-resolver = { workspace = true, features = ["https-ring", "tls-ring", "webpki-roots"] }
|
||||
|
||||
@@ -162,6 +162,8 @@ use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "tunneling")]
|
||||
mod fronted;
|
||||
#[cfg(feature = "tunneling")]
|
||||
pub use fronted::FrontPolicy;
|
||||
mod url;
|
||||
pub use url::{IntoUrl, Url};
|
||||
mod user_agent;
|
||||
@@ -192,6 +194,15 @@ pub type Params<'a, K, V> = &'a [(K, V)];
|
||||
/// Empty collection of HTTP Request Parameters.
|
||||
pub const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
|
||||
|
||||
/// Serialization format for API requests and responses
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SerializationFormat {
|
||||
/// Use JSON serialization (default, always works)
|
||||
Json,
|
||||
/// Use bincode serialization (must be explicitly opted into)
|
||||
Bincode,
|
||||
}
|
||||
|
||||
/// The Errors that may occur when creating or using an HTTP client.
|
||||
#[derive(Debug, Error)]
|
||||
#[allow(missing_docs)]
|
||||
@@ -371,6 +382,7 @@ pub struct ClientBuilder {
|
||||
front: Option<fronted::Front>,
|
||||
|
||||
retry_limit: usize,
|
||||
serialization: SerializationFormat,
|
||||
}
|
||||
|
||||
impl ClientBuilder {
|
||||
@@ -396,6 +408,43 @@ impl ClientBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a client builder from network details with sensible defaults
|
||||
#[cfg(feature = "network-defaults")]
|
||||
pub fn from_network(network: &nym_network_defaults::NymNetworkDetails) -> Result<Self, HttpClientError> {
|
||||
let urls = network.nym_api_urls
|
||||
.as_ref()
|
||||
.ok_or_else(|| HttpClientError::GenericRequestFailure("No API URLs configured in network details".to_string()))?
|
||||
.iter()
|
||||
.map(|api_url| {
|
||||
// Convert ApiUrl to our Url type with fronting support
|
||||
let mut url = Url::parse(&api_url.url)?;
|
||||
|
||||
// Add fronting domains if available
|
||||
#[cfg(feature = "tunneling")]
|
||||
if let Some(ref front_hosts) = api_url.front_hosts {
|
||||
let fronts: Vec<String> = front_hosts
|
||||
.iter()
|
||||
.map(|host| format!("https://{}", host))
|
||||
.collect();
|
||||
url = Url::new(api_url.url.clone(), Some(fronts))
|
||||
.map_err(|e| HttpClientError::GenericRequestFailure(e.to_string()))?;
|
||||
}
|
||||
|
||||
Ok(url)
|
||||
})
|
||||
.collect::<Result<Vec<_>, HttpClientError>>()?;
|
||||
|
||||
let mut builder = Self::new_with_urls(urls);
|
||||
|
||||
// Enable domain fronting by default (on retry)
|
||||
#[cfg(feature = "tunneling")]
|
||||
{
|
||||
builder = builder.with_fronting(FrontPolicy::OnRetry);
|
||||
}
|
||||
|
||||
Ok(builder)
|
||||
}
|
||||
|
||||
/// Constructs a new http `ClientBuilder` from a valid url.
|
||||
pub fn new_with_urls(urls: Vec<Url>) -> Self {
|
||||
let urls = Self::check_urls(urls);
|
||||
@@ -429,6 +478,7 @@ impl ClientBuilder {
|
||||
front: None,
|
||||
|
||||
retry_limit: 0,
|
||||
serialization: SerializationFormat::Json,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,6 +551,17 @@ impl ClientBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the serialization format for API requests and responses
|
||||
pub fn with_serialization(mut self, format: SerializationFormat) -> Self {
|
||||
self.serialization = format;
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure the client to use bincode serialization
|
||||
pub fn with_bincode(self) -> Self {
|
||||
self.with_serialization(SerializationFormat::Bincode)
|
||||
}
|
||||
|
||||
/// Returns a Client that uses this ClientBuilder configuration.
|
||||
pub fn build<E>(self) -> Result<Client, HttpClientError<E>>
|
||||
where
|
||||
@@ -542,6 +603,7 @@ impl ClientBuilder {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
request_timeout: self.timeout.unwrap_or(DEFAULT_TIMEOUT),
|
||||
retry_limit: self.retry_limit,
|
||||
serialization: self.serialization,
|
||||
};
|
||||
|
||||
Ok(client)
|
||||
@@ -562,6 +624,7 @@ pub struct Client {
|
||||
request_timeout: Duration,
|
||||
|
||||
retry_limit: usize,
|
||||
serialization: SerializationFormat,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
@@ -619,6 +682,7 @@ impl Client {
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
request_timeout: self.request_timeout,
|
||||
serialization: self.serialization,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -743,6 +807,13 @@ impl ApiClientCore for Client {
|
||||
|
||||
let mut rb = RequestBuilder::from_parts(self.reqwest_client.clone(), req);
|
||||
|
||||
// Set Accept header based on serialization preference
|
||||
let accept_header = match self.serialization {
|
||||
SerializationFormat::Json => "application/json",
|
||||
SerializationFormat::Bincode => "application/bincode",
|
||||
};
|
||||
rb = rb.header(reqwest::header::ACCEPT, accept_header);
|
||||
|
||||
if let Some(body) = json_body {
|
||||
rb = rb.json(body);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user