From 25669d2cc4ed8ceaae7f019553ab7a680c6799de Mon Sep 17 00:00:00 2001 From: Diane Huxley Date: Tue, 1 Oct 2024 13:23:43 -0700 Subject: [PATCH 1/8] Feature flag http client --- crates/web5/Cargo.toml | 8 +- .../web5/src/credentials/credential_schema.rs | 20 +- crates/web5/src/dids/methods/did_dht/mod.rs | 42 ++--- .../web5/src/dids/methods/did_web/resolver.rs | 17 +- crates/web5/src/errors.rs | 5 +- crates/web5/src/http.rs | 172 ++++++++++++++++++ crates/web5/src/lib.rs | 3 + 7 files changed, 234 insertions(+), 33 deletions(-) create mode 100644 crates/web5/src/http.rs diff --git a/crates/web5/Cargo.toml b/crates/web5/Cargo.toml index 7c54383a..3cff498a 100644 --- a/crates/web5/Cargo.toml +++ b/crates/web5/Cargo.toml @@ -8,6 +8,7 @@ license-file.workspace = true rust-version = "1.74.0" [dependencies] +async-trait = "0.1.83" base64 = { workspace = true } byteorder = "1.5.0" chrono = { workspace = true } @@ -32,7 +33,12 @@ x25519-dalek = { version = "2.0.1", features = ["getrandom", "static_secrets"] } zbase32 = "0.1.2" lazy_static = { workspace = true } flate2 = "1.0.33" -http-std = { path = "../http-std" } +reqwest = { version = "0.12", optional = true } +once_cell = "1.19.0" + +[features] +default = ["default_http_client"] +default_http_client = ["reqwest"] [dev-dependencies] mockito = "1.5.0" diff --git a/crates/web5/src/credentials/credential_schema.rs b/crates/web5/src/credentials/credential_schema.rs index 7af3159c..96af2b8f 100644 --- a/crates/web5/src/credentials/credential_schema.rs +++ b/crates/web5/src/credentials/credential_schema.rs @@ -1,5 +1,7 @@ +use std::collections::HashMap; + use super::verifiable_credential_1_1::VerifiableCredential; -use crate::errors::{Result, Web5Error}; +use crate::{errors::{Result, Web5Error}, http::get_http_client}; use jsonschema::{Draft, JSONSchema}; use serde::{Deserialize, Serialize}; @@ -28,7 +30,21 @@ pub(crate) async fn validate_credential_schema( let url = &credential_schema.id; - let response = http_std::fetch(url, None).await?; + let headers: HashMap = HashMap::from([ + ("Host".to_string(), "{}".to_string()), + ("Connection".to_string(), "close".to_string()), + ("Accept".to_string(), "application/json".to_string()) + ]); + let response = get_http_client().get(url, Some(headers)) + .await + .map_err(|e| Web5Error::Network(format!("Failed to fetch credential schema: {}", e)))?; + + if !(200..300).contains(&response.status_code) { + return Err(Web5Error::Http(format!( + "Failed to fetch credential schema: non-successful response code {}", + response.status_code + ))); + } if !(200..300).contains(&response.status_code) { return Err(Web5Error::JsonSchema(format!( diff --git a/crates/web5/src/dids/methods/did_dht/mod.rs b/crates/web5/src/dids/methods/did_dht/mod.rs index 773e8c44..bb293762 100644 --- a/crates/web5/src/dids/methods/did_dht/mod.rs +++ b/crates/web5/src/dids/methods/did_dht/mod.rs @@ -17,9 +17,9 @@ use crate::{ resolution_metadata::ResolutionMetadataError, resolution_result::ResolutionResult, }, }, - errors::{Result, Web5Error}, + errors::{Result, Web5Error}, http::get_http_client, }; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; mod bep44; mod document_packet; @@ -190,25 +190,16 @@ impl DidDht { bearer_did.did.id.trim_start_matches('/') ); - let response = http_std::fetch( - &url, - Some(http_std::FetchOptions { - method: Some(http_std::Method::Put), - headers: Some( - [ - ( - "Content-Type".to_string(), - "application/octet-stream".to_string(), - ), - ("Content-Length".to_string(), body.len().to_string()), - ] - .into_iter() - .collect(), - ), - body: Some(body), - }), - ) - .await?; + let headers: HashMap = HashMap::from([ + ("Host".to_string(), "{}".to_string()), + ("Connection".to_string(), "close".to_string()), + ("Content-Length".to_string(), "{}".to_string()), + ("Content-Type".to_string(), "application/octet-stream".to_string()) + ]); + + let response = get_http_client().put(&url, Some(headers), &body) + .await + .map_err(|e| Web5Error::Network(format!("Failed to PUT did:dht: {}", e)))?; if response.status_code != 200 { return Err(Web5Error::Network( "failed to PUT DID to mainline".to_string(), @@ -265,9 +256,12 @@ impl DidDht { did.id.trim_start_matches('/') ); - let response = http_std::fetch(&url, None) - .await // todo here - .map_err(|_| ResolutionMetadataError::InternalError)?; + let headers: HashMap = HashMap::from([ + ("Host".to_string(), "{}".to_string()), + ("Connection".to_string(), "close".to_string()), + ("Accept".to_string(), "application/octet-stream".to_string()) + ]); + let response = get_http_client().get(&url, Some(headers)).await.map_err(|_| ResolutionMetadataError::InternalError)?; if response.status_code == 404 { return Err(ResolutionMetadataError::NotFound); diff --git a/crates/web5/src/dids/methods/did_web/resolver.rs b/crates/web5/src/dids/methods/did_web/resolver.rs index 76dc1bee..82662ea4 100644 --- a/crates/web5/src/dids/methods/did_web/resolver.rs +++ b/crates/web5/src/dids/methods/did_web/resolver.rs @@ -1,10 +1,12 @@ -use crate::dids::{ +use std::collections::HashMap; + +use crate::{dids::{ data_model::document::Document, did::Did, resolution::{ resolution_metadata::ResolutionMetadataError, resolution_result::ResolutionResult, }, -}; +}, http::get_http_client}; use url::Url; // PORT_SEP is the : character that separates the domain from the port in a URI. @@ -43,10 +45,19 @@ impl Resolver { } pub async fn resolve(&self) -> Result { - let response = http_std::fetch(&self.http_url, None) + let headers: HashMap = HashMap::from([ + ("Host".to_string(), "{}".to_string()), + ("Connection".to_string(), "close".to_string()), + ("Accept".to_string(), "application/json".to_string()) + ]); + let response = get_http_client().get(&self.http_url, Some(headers)) .await .map_err(|_| ResolutionMetadataError::InternalError)?; + if !(200..300).contains(&response.status_code) { + return Err(ResolutionMetadataError::InternalError); + } + if response.status_code == 404 { return Err(ResolutionMetadataError::NotFound); } else if !(200..300).contains(&response.status_code) { diff --git a/crates/web5/src/errors.rs b/crates/web5/src/errors.rs index 05b4aa4e..e84803fa 100644 --- a/crates/web5/src/errors.rs +++ b/crates/web5/src/errors.rs @@ -2,7 +2,6 @@ use crate::{ credentials::VerificationError, dids::resolution::resolution_metadata::ResolutionMetadataError, }; use base64::DecodeError; -use http_std::Error as HttpError; use serde_json::Error as SerdeJsonError; use std::sync::PoisonError; @@ -30,9 +29,9 @@ pub enum Web5Error { Network(String), #[error("datetime error {0}")] DateTime(String), + #[error("http error {0}")] + Http(String), - #[error(transparent)] - Http(#[from] HttpError), #[error(transparent)] Resolution(#[from] ResolutionMetadataError), #[error(transparent)] diff --git a/crates/web5/src/http.rs b/crates/web5/src/http.rs new file mode 100644 index 00000000..5887a9b8 --- /dev/null +++ b/crates/web5/src/http.rs @@ -0,0 +1,172 @@ + +use std::{collections::HashMap, sync::Arc}; +use async_trait::async_trait; +use once_cell::sync::OnceCell; + +#[async_trait] +pub trait HttpClient: Send + Sync { + async fn get( + &self, + url: &str, + headers: Option> + ) -> std::result::Result>; + + async fn post( + &self, + url: &str, + headers: Option>, + body: &[u8] + ) -> std::result::Result>; + + async fn put( + &self, + url: &str, + headers: Option>, + body: &[u8] + ) -> std::result::Result>; +} + +pub struct HttpResponse { + pub status_code: u16, + #[allow(dead_code)] + pub headers: HashMap, + pub body: Vec, +} + +static HTTP_CLIENT: OnceCell> = OnceCell::new(); + +#[cfg(feature = "default_http_client")] +pub fn get_http_client() -> &'static dyn HttpClient { + HTTP_CLIENT.get_or_init(|| { + Arc::new(reqwest_http_client::ReqwestHttpClient::new()) + }).as_ref() +} + +#[cfg(not(feature = "default_http_client"))] +pub fn get_http_client() -> &'static dyn HttpClient { + HTTP_CLIENT.get().expect("HttpClient has not been set. Please call set_http_client().").as_ref() +} + +#[cfg(feature = "default_http_client")] +pub fn set_http_client(_: Arc) { + panic!("Cannot set a custom HttpClient when the reqwest feature is enabled."); +} + +#[cfg(not(feature = "default_http_client"))] +pub fn set_http_client(client: Arc) { + HTTP_CLIENT.set(client).unwrap_or_else(|_| panic!("HttpClient has already been set.")); +} + +#[cfg(feature = "default_http_client")] +mod reqwest_http_client { + use super::*; + use reqwest::Client; + use std::collections::HashMap; + + pub struct ReqwestHttpClient { + client: Client, + } + + impl ReqwestHttpClient { + pub fn new() -> Self { + ReqwestHttpClient { + client: Client::new(), + } + } + } + + #[async_trait] + impl HttpClient for ReqwestHttpClient { + async fn get( + &self, + url: &str, + headers: Option>, + ) -> Result> { + let mut req = self.client.get(url); + + if let Some(headers) = headers { + for (key, value) in headers { + req = req.header(&key, &value); + } + } + + let response = req.send().await?; + let status_code = response.status().as_u16(); + let headers = response + .headers() + .iter() + .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())) + .collect(); + + let body = response.bytes().await?.to_vec(); + + Ok(HttpResponse { + status_code, + headers, + body, + }) + } + + async fn post( + &self, + url: &str, + headers: Option>, + body: &[u8], + ) -> Result> { + let mut req = self.client.post(url).body(body.to_vec()); + + if let Some(headers) = headers { + for (key, value) in headers { + req = req.header(&key, &value); + } + } + + let response = req.send().await?; + let status_code = response.status().as_u16(); + let headers = response + .headers() + .iter() + .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())) + .collect(); + + let body = response.bytes().await?.to_vec(); + + Ok(HttpResponse { + status_code, + headers, + body, + }) + } + + async fn put( + &self, + url: &str, + headers: Option>, + body: &[u8], + ) -> Result> { + let mut req = self.client.put(url).body(body.to_vec()); + + if let Some(headers) = headers { + for (key, value) in headers { + req = req.header(&key, &value); + } + } + + let response = req.send().await?; + let status_code = response.status().as_u16(); + let headers = response + .headers() + .iter() + .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())) + .collect(); + + let body = response.bytes().await?.to_vec(); + + Ok(HttpResponse { + status_code, + headers, + body, + }) + } + } +} \ No newline at end of file diff --git a/crates/web5/src/lib.rs b/crates/web5/src/lib.rs index c11653e8..863d5cae 100644 --- a/crates/web5/src/lib.rs +++ b/crates/web5/src/lib.rs @@ -4,8 +4,11 @@ pub mod dids; mod datetime; pub mod errors; +mod http; pub mod jose; pub mod json; +pub use http::set_http_client; + #[cfg(test)] mod test_vectors; From a0fe4aa011c91c66ee99b09f4325d95ea05b2611 Mon Sep 17 00:00:00 2001 From: Diane Huxley Date: Tue, 1 Oct 2024 13:43:11 -0700 Subject: [PATCH 2/8] Fix tests --- crates/web5/src/credentials/create.rs | 10 +++++----- crates/web5/src/credentials/credential_schema.rs | 2 +- .../web5/src/credentials/verifiable_credential_1_1.rs | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/web5/src/credentials/create.rs b/crates/web5/src/credentials/create.rs index 5c824466..150c5a13 100644 --- a/crates/web5/src/credentials/create.rs +++ b/crates/web5/src/credentials/create.rs @@ -605,11 +605,11 @@ mod tests { .await; match result { - Err(Web5Error::Http(err)) => { + Err(Web5Error::Network(err)) => { assert!(err.to_string().contains("error sending request")) } _ => panic!( - "expected Web5Error::Http with specific message but got {:?}", + "expected Web5Error::Network with specific message but got {:?}", result ), }; @@ -636,11 +636,11 @@ mod tests { .await; match result { - Err(Web5Error::JsonSchema(err_msg)) => { - assert_eq!("failed to resolve status code 500", err_msg) + Err(Web5Error::Network(err_msg)) => { + assert!(err_msg.contains("Failed to fetch credential schema")) } _ => panic!( - "expected Web5Error::JsonSchema with specific message but got {:?}", + "expected Web5Error::Network with specific message but got {:?}", result ), } diff --git a/crates/web5/src/credentials/credential_schema.rs b/crates/web5/src/credentials/credential_schema.rs index 96af2b8f..0386d82b 100644 --- a/crates/web5/src/credentials/credential_schema.rs +++ b/crates/web5/src/credentials/credential_schema.rs @@ -40,7 +40,7 @@ pub(crate) async fn validate_credential_schema( .map_err(|e| Web5Error::Network(format!("Failed to fetch credential schema: {}", e)))?; if !(200..300).contains(&response.status_code) { - return Err(Web5Error::Http(format!( + return Err(Web5Error::Network(format!( "Failed to fetch credential schema: non-successful response code {}", response.status_code ))); diff --git a/crates/web5/src/credentials/verifiable_credential_1_1.rs b/crates/web5/src/credentials/verifiable_credential_1_1.rs index 9fd01e5e..f4795053 100644 --- a/crates/web5/src/credentials/verifiable_credential_1_1.rs +++ b/crates/web5/src/credentials/verifiable_credential_1_1.rs @@ -813,11 +813,11 @@ mod tests { let result = VerifiableCredential::from_vc_jwt(vc_jwt_with_invalid_url, true).await; match result { - Err(Web5Error::Http(err)) => { + Err(Web5Error::Network(err)) => { assert!(err.to_string().contains("error sending request")) } _ => panic!( - "expected Web5Error::Http with specific message but got {:?}", + "expected Web5Error::Network with specific message but got {:?}", result ), }; @@ -839,11 +839,11 @@ mod tests { let result = VerifiableCredential::from_vc_jwt(vc_jwt_at_port, true).await; match result { - Err(Web5Error::JsonSchema(err_msg)) => { - assert_eq!("failed to resolve status code 500", err_msg) + Err(Web5Error::Network(err_msg)) => { + assert!(err_msg.contains("Failed to fetch credential schema")) } _ => panic!( - "expected Web5Error::JsonSchema with specific message but got {:?}", + "expected Web5Error::Network with specific message but got {:?}", result ), } From 65786d653c19c65abaf310e6917c9053f3fe70f8 Mon Sep 17 00:00:00 2001 From: Diane Huxley Date: Tue, 1 Oct 2024 14:08:03 -0700 Subject: [PATCH 3/8] Off by default --- crates/web5/Cargo.toml | 6 ++++-- crates/web5/src/http.rs | 10 +++++----- crates/web5/src/lib.rs | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/crates/web5/Cargo.toml b/crates/web5/Cargo.toml index 3cff498a..f5c65a76 100644 --- a/crates/web5/Cargo.toml +++ b/crates/web5/Cargo.toml @@ -37,9 +37,11 @@ reqwest = { version = "0.12", optional = true } once_cell = "1.19.0" [features] -default = ["default_http_client"] -default_http_client = ["reqwest"] +default = [] +http_reqwest = ["reqwest"] [dev-dependencies] mockito = "1.5.0" tokio = { version = "1.38.0", features = ["macros", "test-util"] } +web5 = { path = ".", features = ["http_reqwest"] } + diff --git a/crates/web5/src/http.rs b/crates/web5/src/http.rs index 5887a9b8..723ca88d 100644 --- a/crates/web5/src/http.rs +++ b/crates/web5/src/http.rs @@ -35,29 +35,29 @@ pub struct HttpResponse { static HTTP_CLIENT: OnceCell> = OnceCell::new(); -#[cfg(feature = "default_http_client")] +#[cfg(feature = "http_reqwest")] pub fn get_http_client() -> &'static dyn HttpClient { HTTP_CLIENT.get_or_init(|| { Arc::new(reqwest_http_client::ReqwestHttpClient::new()) }).as_ref() } -#[cfg(not(feature = "default_http_client"))] +#[cfg(not(feature = "http_reqwest"))] pub fn get_http_client() -> &'static dyn HttpClient { HTTP_CLIENT.get().expect("HttpClient has not been set. Please call set_http_client().").as_ref() } -#[cfg(feature = "default_http_client")] +#[cfg(feature = "http_reqwest")] pub fn set_http_client(_: Arc) { panic!("Cannot set a custom HttpClient when the reqwest feature is enabled."); } -#[cfg(not(feature = "default_http_client"))] +#[cfg(not(feature = "http_reqwest"))] pub fn set_http_client(client: Arc) { HTTP_CLIENT.set(client).unwrap_or_else(|_| panic!("HttpClient has already been set.")); } -#[cfg(feature = "default_http_client")] +#[cfg(feature = "http_reqwest")] mod reqwest_http_client { use super::*; use reqwest::Client; diff --git a/crates/web5/src/lib.rs b/crates/web5/src/lib.rs index 863d5cae..99580e6d 100644 --- a/crates/web5/src/lib.rs +++ b/crates/web5/src/lib.rs @@ -12,3 +12,17 @@ pub use http::set_http_client; #[cfg(test)] mod test_vectors; +#[cfg(test)] +mod tests { + #[cfg(feature = "http_reqwest")] + #[test] + fn test_with_reqwest_feature() { + println!("http_reqwest feature is enabled!"); + } + + #[cfg(not(feature = "http_reqwest"))] + #[test] + fn test_without_reqwest_feature() { + println!("http_reqwest feature is NOT enabled!"); + } +} \ No newline at end of file From f4a80e36857be231b04fc0f606ef971c310ba7e5 Mon Sep 17 00:00:00 2001 From: Diane Huxley Date: Tue, 1 Oct 2024 15:57:53 -0700 Subject: [PATCH 4/8] Enable reqwest for kotlin bindings --- bindings/web5_uniffi_wrapper/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/web5_uniffi_wrapper/Cargo.toml b/bindings/web5_uniffi_wrapper/Cargo.toml index d8621950..12debcd9 100644 --- a/bindings/web5_uniffi_wrapper/Cargo.toml +++ b/bindings/web5_uniffi_wrapper/Cargo.toml @@ -10,4 +10,4 @@ license-file.workspace = true serde_json = { workspace = true } thiserror = { workspace = true } tokio = { version = "1.38.0", features = ["full"] } -web5 = { path = "../../crates/web5" } \ No newline at end of file +web5 = { path = "../../crates/web5", features = ["http_reqwest"]} \ No newline at end of file From 30d475edfd56685e45c2f17840d911c731341c97 Mon Sep 17 00:00:00 2001 From: Diane Huxley Date: Tue, 1 Oct 2024 16:12:36 -0700 Subject: [PATCH 5/8] Fix error strings in test --- .../src/test/kotlin/web5/sdk/vc/VerifiableCredentialTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bound/kt/src/test/kotlin/web5/sdk/vc/VerifiableCredentialTest.kt b/bound/kt/src/test/kotlin/web5/sdk/vc/VerifiableCredentialTest.kt index caf0f4dd..e8979a18 100644 --- a/bound/kt/src/test/kotlin/web5/sdk/vc/VerifiableCredentialTest.kt +++ b/bound/kt/src/test/kotlin/web5/sdk/vc/VerifiableCredentialTest.kt @@ -322,7 +322,7 @@ class VerifiableCredentialTest { VerifiableCredential.create(ISSUER, CREDENTIAL_SUBJECT, options) } - assertTrue(exception.message.contains("failed to resolve status code 500")) + assertTrue(exception.message.contains("Failed to fetch credential schema")) mockWebServer.shutdown() } @@ -1054,7 +1054,7 @@ class VerifiableCredentialTest { VerifiableCredential.fromVcJwt(vcJwtAtPort, true) } - assertTrue(exception.message.contains("failed to resolve status code 500")) + assertTrue(exception.message.contains("Failed to fetch credential schema")) mockWebServer.shutdown() } From e004bd0f96a69abb9024bba4b27b6cbb9de7eae9 Mon Sep 17 00:00:00 2001 From: Diane Huxley Date: Thu, 3 Oct 2024 09:43:01 -0700 Subject: [PATCH 6/8] Refactor HttpClient trait to match http-std crate --- .../web5/src/credentials/credential_schema.rs | 10 +- crates/web5/src/dids/methods/did_dht/mod.rs | 18 +- .../web5/src/dids/methods/did_web/resolver.rs | 20 +- crates/web5/src/http.rs | 204 ++++++++++-------- crates/web5/src/lib.rs | 2 +- 5 files changed, 147 insertions(+), 107 deletions(-) diff --git a/crates/web5/src/credentials/credential_schema.rs b/crates/web5/src/credentials/credential_schema.rs index 0386d82b..7ba3c5f0 100644 --- a/crates/web5/src/credentials/credential_schema.rs +++ b/crates/web5/src/credentials/credential_schema.rs @@ -1,7 +1,10 @@ use std::collections::HashMap; use super::verifiable_credential_1_1::VerifiableCredential; -use crate::{errors::{Result, Web5Error}, http::get_http_client}; +use crate::{ + errors::{Result, Web5Error}, + http::get_http_client, +}; use jsonschema::{Draft, JSONSchema}; use serde::{Deserialize, Serialize}; @@ -33,9 +36,10 @@ pub(crate) async fn validate_credential_schema( let headers: HashMap = HashMap::from([ ("Host".to_string(), "{}".to_string()), ("Connection".to_string(), "close".to_string()), - ("Accept".to_string(), "application/json".to_string()) + ("Accept".to_string(), "application/json".to_string()), ]); - let response = get_http_client().get(url, Some(headers)) + let response = get_http_client() + .get(url, Some(headers)) .await .map_err(|e| Web5Error::Network(format!("Failed to fetch credential schema: {}", e)))?; diff --git a/crates/web5/src/dids/methods/did_dht/mod.rs b/crates/web5/src/dids/methods/did_dht/mod.rs index bb293762..b224a603 100644 --- a/crates/web5/src/dids/methods/did_dht/mod.rs +++ b/crates/web5/src/dids/methods/did_dht/mod.rs @@ -17,7 +17,8 @@ use crate::{ resolution_metadata::ResolutionMetadataError, resolution_result::ResolutionResult, }, }, - errors::{Result, Web5Error}, http::get_http_client, + errors::{Result, Web5Error}, + http::get_http_client, }; use std::{collections::HashMap, sync::Arc}; @@ -194,10 +195,14 @@ impl DidDht { ("Host".to_string(), "{}".to_string()), ("Connection".to_string(), "close".to_string()), ("Content-Length".to_string(), "{}".to_string()), - ("Content-Type".to_string(), "application/octet-stream".to_string()) + ( + "Content-Type".to_string(), + "application/octet-stream".to_string(), + ), ]); - let response = get_http_client().put(&url, Some(headers), &body) + let response = get_http_client() + .put(&url, Some(headers), &body) .await .map_err(|e| Web5Error::Network(format!("Failed to PUT did:dht: {}", e)))?; if response.status_code != 200 { @@ -259,9 +264,12 @@ impl DidDht { let headers: HashMap = HashMap::from([ ("Host".to_string(), "{}".to_string()), ("Connection".to_string(), "close".to_string()), - ("Accept".to_string(), "application/octet-stream".to_string()) + ("Accept".to_string(), "application/octet-stream".to_string()), ]); - let response = get_http_client().get(&url, Some(headers)).await.map_err(|_| ResolutionMetadataError::InternalError)?; + let response = get_http_client() + .get(&url, Some(headers)) + .await + .map_err(|_| ResolutionMetadataError::InternalError)?; if response.status_code == 404 { return Err(ResolutionMetadataError::NotFound); diff --git a/crates/web5/src/dids/methods/did_web/resolver.rs b/crates/web5/src/dids/methods/did_web/resolver.rs index 82662ea4..fbc90034 100644 --- a/crates/web5/src/dids/methods/did_web/resolver.rs +++ b/crates/web5/src/dids/methods/did_web/resolver.rs @@ -1,12 +1,15 @@ use std::collections::HashMap; -use crate::{dids::{ - data_model::document::Document, - did::Did, - resolution::{ - resolution_metadata::ResolutionMetadataError, resolution_result::ResolutionResult, +use crate::{ + dids::{ + data_model::document::Document, + did::Did, + resolution::{ + resolution_metadata::ResolutionMetadataError, resolution_result::ResolutionResult, + }, }, -}, http::get_http_client}; + http::get_http_client, +}; use url::Url; // PORT_SEP is the : character that separates the domain from the port in a URI. @@ -48,9 +51,10 @@ impl Resolver { let headers: HashMap = HashMap::from([ ("Host".to_string(), "{}".to_string()), ("Connection".to_string(), "close".to_string()), - ("Accept".to_string(), "application/json".to_string()) + ("Accept".to_string(), "application/json".to_string()), ]); - let response = get_http_client().get(&self.http_url, Some(headers)) + let response = get_http_client() + .get(&self.http_url, Some(headers)) .await .map_err(|_| ResolutionMetadataError::InternalError)?; diff --git a/crates/web5/src/http.rs b/crates/web5/src/http.rs index 723ca88d..f08a5e2e 100644 --- a/crates/web5/src/http.rs +++ b/crates/web5/src/http.rs @@ -1,34 +1,83 @@ - -use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; use once_cell::sync::OnceCell; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, sync::Arc}; #[async_trait] pub trait HttpClient: Send + Sync { - async fn get( - &self, - url: &str, - headers: Option> + async fn fetch( + &self, + url: &str, + options: Option, ) -> std::result::Result>; + async fn get( + &self, + url: &str, + headers: Option>, + ) -> std::result::Result> { + self.fetch( + url, + Some(FetchOptions { + method: Some(Method::Get), + headers, + body: None, + }), + ) + .await + } + async fn post( - &self, - url: &str, - headers: Option>, - body: &[u8] - ) -> std::result::Result>; + &self, + url: &str, + headers: Option>, + body: &[u8], + ) -> std::result::Result> { + self.fetch( + url, + Some(FetchOptions { + method: Some(Method::Post), + headers, + body: Some(body.to_vec()), + }), + ) + .await + } async fn put( - &self, - url: &str, - headers: Option>, - body: &[u8] - ) -> std::result::Result>; + &self, + url: &str, + headers: Option>, + body: &[u8], + ) -> std::result::Result> { + self.fetch( + url, + Some(FetchOptions { + method: Some(Method::Put), + headers, + body: Some(body.to_vec()), + }), + ) + .await + } +} + +#[derive(Default, Serialize, Deserialize)] +pub struct FetchOptions { + pub method: Option, + pub headers: Option>, + pub body: Option>, +} + +#[derive(Serialize, Deserialize)] +pub enum Method { + Get, + Post, + Put, } pub struct HttpResponse { pub status_code: u16, - #[allow(dead_code)] pub headers: HashMap, pub body: Vec, } @@ -37,14 +86,17 @@ static HTTP_CLIENT: OnceCell> = OnceCell::new(); #[cfg(feature = "http_reqwest")] pub fn get_http_client() -> &'static dyn HttpClient { - HTTP_CLIENT.get_or_init(|| { - Arc::new(reqwest_http_client::ReqwestHttpClient::new()) - }).as_ref() + HTTP_CLIENT + .get_or_init(|| Arc::new(reqwest_http_client::ReqwestHttpClient::new())) + .as_ref() } #[cfg(not(feature = "http_reqwest"))] pub fn get_http_client() -> &'static dyn HttpClient { - HTTP_CLIENT.get().expect("HttpClient has not been set. Please call set_http_client().").as_ref() + HTTP_CLIENT + .get() + .expect("HttpClient has not been set. Please call set_http_client().") + .as_ref() } #[cfg(feature = "http_reqwest")] @@ -54,66 +106,49 @@ pub fn set_http_client(_: Arc) { #[cfg(not(feature = "http_reqwest"))] pub fn set_http_client(client: Arc) { - HTTP_CLIENT.set(client).unwrap_or_else(|_| panic!("HttpClient has already been set.")); + HTTP_CLIENT + .set(client) + .unwrap_or_else(|_| panic!("HttpClient has already been set.")); } #[cfg(feature = "http_reqwest")] mod reqwest_http_client { use super::*; - use reqwest::Client; - use std::collections::HashMap; + use reqwest::{Client as ReqwestClient, Method as ReqwestMethod, Response as ReqwestResponse}; + use std::error::Error; pub struct ReqwestHttpClient { - client: Client, + client: ReqwestClient, } impl ReqwestHttpClient { pub fn new() -> Self { ReqwestHttpClient { - client: Client::new(), + client: ReqwestClient::new(), } } - } - #[async_trait] - impl HttpClient for ReqwestHttpClient { - async fn get( - &self, - url: &str, - headers: Option>, - ) -> Result> { - let mut req = self.client.get(url); - - if let Some(headers) = headers { - for (key, value) in headers { - req = req.header(&key, &value); - } + fn map_method(method: Option) -> ReqwestMethod { + match method { + Some(Method::Post) => ReqwestMethod::POST, + Some(Method::Put) => ReqwestMethod::PUT, + _ => ReqwestMethod::GET, } - - let response = req.send().await?; - let status_code = response.status().as_u16(); - let headers = response - .headers() - .iter() - .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())) - .collect(); - - let body = response.bytes().await?.to_vec(); - - Ok(HttpResponse { - status_code, - headers, - body, - }) } - async fn post( + async fn build_request( &self, url: &str, - headers: Option>, - body: &[u8], - ) -> Result> { - let mut req = self.client.post(url).body(body.to_vec()); + options: Option, + ) -> Result> { + let FetchOptions { + method, + headers, + body, + } = options.unwrap_or_default(); + + let req_method = Self::map_method(method); + let mut req = self.client.request(req_method, url); if let Some(headers) = headers { for (key, value) in headers { @@ -121,7 +156,14 @@ mod reqwest_http_client { } } - let response = req.send().await?; + if let Some(body) = body { + req = req.body(body); + } + + Ok(req) + } + + async fn parse_response(response: ReqwestResponse) -> Result> { let status_code = response.status().as_u16(); let headers = response .headers() @@ -137,36 +179,18 @@ mod reqwest_http_client { body, }) } + } - async fn put( + #[async_trait] + impl HttpClient for ReqwestHttpClient { + async fn fetch( &self, url: &str, - headers: Option>, - body: &[u8], - ) -> Result> { - let mut req = self.client.put(url).body(body.to_vec()); - - if let Some(headers) = headers { - for (key, value) in headers { - req = req.header(&key, &value); - } - } - - let response = req.send().await?; - let status_code = response.status().as_u16(); - let headers = response - .headers() - .iter() - .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())) - .collect(); - - let body = response.bytes().await?.to_vec(); - - Ok(HttpResponse { - status_code, - headers, - body, - }) + options: Option, + ) -> Result> { + let req = self.build_request(url, options).await?; + let res = req.send().await?; + Self::parse_response(res).await } } -} \ No newline at end of file +} diff --git a/crates/web5/src/lib.rs b/crates/web5/src/lib.rs index 99580e6d..103c9026 100644 --- a/crates/web5/src/lib.rs +++ b/crates/web5/src/lib.rs @@ -25,4 +25,4 @@ mod tests { fn test_without_reqwest_feature() { println!("http_reqwest feature is NOT enabled!"); } -} \ No newline at end of file +} From f151a776bb0bfb57514cb2f7e433bb736a77c752 Mon Sep 17 00:00:00 2001 From: Diane Huxley Date: Thu, 3 Oct 2024 09:49:42 -0700 Subject: [PATCH 7/8] impl Display --- crates/web5/src/http.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/web5/src/http.rs b/crates/web5/src/http.rs index f08a5e2e..3602dfd6 100644 --- a/crates/web5/src/http.rs +++ b/crates/web5/src/http.rs @@ -1,7 +1,9 @@ use async_trait::async_trait; use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, fmt, str::FromStr, sync::Arc}; + +use crate::errors::Web5Error; #[async_trait] pub trait HttpClient: Send + Sync { @@ -82,6 +84,30 @@ pub struct HttpResponse { pub body: Vec, } +impl fmt::Display for Method { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let method_str = match self { + Method::Get => "GET", + Method::Post => "POST", + Method::Put => "PUT", + }; + write!(f, "{}", method_str) + } +} + +impl FromStr for Method { + type Err = Web5Error; + + fn from_str(s: &str) -> crate::errors::Result { + match s.to_ascii_uppercase().as_ref() { + "GET" => Ok(Method::Get), + "POST" => Ok(Method::Post), + "PUT" => Ok(Method::Put), + _ => Err(Web5Error::Parameter(format!("unknown method {}", s))), + } + } +} + static HTTP_CLIENT: OnceCell> = OnceCell::new(); #[cfg(feature = "http_reqwest")] From d27dea71d71ae687aed20982aa5eaae865356518 Mon Sep 17 00:00:00 2001 From: Diane Huxley Date: Thu, 3 Oct 2024 09:58:34 -0700 Subject: [PATCH 8/8] Remove http-std crate --- Cargo.toml | 1 - .../x86_64_unknown_linux_gnu/Dockerfile | 1 - .../x86_64_unknown_linux_musl/Dockerfile | 1 - .../src/dids/methods/did_web.rs | 3 +- bound/typescript/package-lock.json | 16 +- crates/http-std/Cargo.toml | 22 -- crates/http-std/src/client.rs | 54 ----- crates/http-std/src/default_client.rs | 193 ------------------ crates/http-std/src/error.rs | 26 --- crates/http-std/src/lib.rs | 51 ----- crates/http-std/src/reqwest_client.rs | 63 ------ .../build/x86_64_unknown_linux_gnu/Dockerfile | 1 - .../x86_64_unknown_linux_musl/Dockerfile | 1 - 13 files changed, 4 insertions(+), 429 deletions(-) delete mode 100644 crates/http-std/Cargo.toml delete mode 100644 crates/http-std/src/client.rs delete mode 100644 crates/http-std/src/default_client.rs delete mode 100644 crates/http-std/src/error.rs delete mode 100644 crates/http-std/src/lib.rs delete mode 100644 crates/http-std/src/reqwest_client.rs diff --git a/Cargo.toml b/Cargo.toml index 2bbeb646..b25317c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ members = [ "bindings/web5_uniffi", "bindings/web5_uniffi_wrapper", "bindings/web5_wasm", - "crates/http-std", "crates/web5", "crates/web5_cli", ] diff --git a/bindings/web5_uniffi/libtargets/x86_64_unknown_linux_gnu/Dockerfile b/bindings/web5_uniffi/libtargets/x86_64_unknown_linux_gnu/Dockerfile index 5446db11..a97b68d6 100644 --- a/bindings/web5_uniffi/libtargets/x86_64_unknown_linux_gnu/Dockerfile +++ b/bindings/web5_uniffi/libtargets/x86_64_unknown_linux_gnu/Dockerfile @@ -19,7 +19,6 @@ COPY bindings/web5_c ./bindings/web5_c COPY bindings/web5_uniffi_wrapper ./bindings/web5_uniffi_wrapper COPY bindings/web5_uniffi ./bindings/web5_uniffi COPY bindings/web5_wasm ./bindings/web5_wasm -COPY crates/http-std ./crates/http-std COPY crates/web5 ./crates/web5 COPY crates/web5_cli ./crates/web5_cli diff --git a/bindings/web5_uniffi/libtargets/x86_64_unknown_linux_musl/Dockerfile b/bindings/web5_uniffi/libtargets/x86_64_unknown_linux_musl/Dockerfile index b1f17203..cdc6f609 100644 --- a/bindings/web5_uniffi/libtargets/x86_64_unknown_linux_musl/Dockerfile +++ b/bindings/web5_uniffi/libtargets/x86_64_unknown_linux_musl/Dockerfile @@ -36,7 +36,6 @@ COPY bindings/web5_c ./bindings/web5_c COPY bindings/web5_uniffi_wrapper ./bindings/web5_uniffi_wrapper COPY bindings/web5_uniffi ./bindings/web5_uniffi COPY bindings/web5_wasm ./bindings/web5_wasm -COPY crates/http-std ./crates/http-std COPY crates/web5 ./crates/web5 COPY crates/web5_cli ./crates/web5_cli diff --git a/bindings/web5_uniffi_wrapper/src/dids/methods/did_web.rs b/bindings/web5_uniffi_wrapper/src/dids/methods/did_web.rs index 49d218cd..fa567299 100644 --- a/bindings/web5_uniffi_wrapper/src/dids/methods/did_web.rs +++ b/bindings/web5_uniffi_wrapper/src/dids/methods/did_web.rs @@ -1,7 +1,8 @@ use crate::{ crypto::key_manager::{KeyManager, ToInnerKeyManager}, dids::{bearer_did::BearerDid, resolution::resolution_result::ResolutionResult}, - errors::Result, get_rt, + errors::Result, + get_rt, }; use std::sync::Arc; use web5::{ diff --git a/bound/typescript/package-lock.json b/bound/typescript/package-lock.json index 55a48130..a4a7cb36 100644 --- a/bound/typescript/package-lock.json +++ b/bound/typescript/package-lock.json @@ -7,15 +7,12 @@ "": { "name": "web5", "version": "0.1.0", - "dependencies": { - "base64url": "^3.0.1" - }, "devDependencies": { - "@types/base64url": "^2.0.0", "@types/chai": "4.3.0", "@types/mocha": "9.1.0", "@web/test-runner": "0.18.0", "@web/test-runner-playwright": "0.11.0", + "base64url": "^3.0.1", "chai": "4.3.10", "esbuild": "0.19.9", "mocha": "10.2.0", @@ -1006,16 +1003,6 @@ "integrity": "sha512-Anitqkl3+KrzcW2k77lRlg/GfLZLWXBuNgbEcIOU6M92yw42vsd3xV/Z/yAHEj8m+KUjL6bWOVOFqX8PFPJ4LA==", "dev": true }, - "node_modules/@types/base64url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/base64url/-/base64url-2.0.0.tgz", - "integrity": "sha512-r+d5HWla1etMB9MdDr/wfzhiWdT6GTlO6Xp96FnSsnsxeOi3ZCz1EJyv7OE7Coc2cw5tGblqahMYNL/KwdP8kQ==", - "deprecated": "This is a stub types definition for base64url (https://github.com/brianloveswords/base64url). base64url provides its own type definitions, so you don't need @types/base64url installed!", - "dev": true, - "dependencies": { - "base64url": "*" - } - }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -1715,6 +1702,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "dev": true, "engines": { "node": ">=6.0.0" } diff --git a/crates/http-std/Cargo.toml b/crates/http-std/Cargo.toml deleted file mode 100644 index 4b85f61d..00000000 --- a/crates/http-std/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "http-std" -version = "0.1.0" -edition = "2021" -homepage.workspace = true -repository.workspace = true -license-file.workspace = true - -[dependencies] -async-trait = "0.1.83" -serde = { workspace = true } -lazy_static = { workspace = true } -thiserror = { workspace = true } -url = "2.5.0" - -[target.'cfg(not(target_family = "wasm"))'.dependencies] -reqwest = { version = "0.12.7", features = ["blocking"] } -rustls = { version = "0.23.13", default-features = false, features = [ - "std", - "tls12", -] } -rustls-native-certs = "0.8.0" diff --git a/crates/http-std/src/client.rs b/crates/http-std/src/client.rs deleted file mode 100644 index e5673d7a..00000000 --- a/crates/http-std/src/client.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::{Error, Result}; -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fmt, str::FromStr}; - -#[async_trait] -pub trait Client: Send + Sync { - async fn fetch(&self, url: &str, options: Option) -> Result; -} - -#[derive(Default, Serialize, Deserialize)] -pub struct FetchOptions { - pub method: Option, - pub headers: Option>, - pub body: Option>, -} - -#[derive(Serialize, Deserialize)] -pub struct Response { - pub status_code: u16, - pub headers: HashMap, - pub body: Vec, -} - -#[derive(Serialize, Deserialize)] -pub enum Method { - Get, - Post, - Put, -} - -impl fmt::Display for Method { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let method_str = match self { - Method::Get => "GET", - Method::Post => "POST", - Method::Put => "PUT", - }; - write!(f, "{}", method_str) - } -} - -impl FromStr for Method { - type Err = Error; - - fn from_str(s: &str) -> Result { - match s.to_ascii_uppercase().as_ref() { - "GET" => Ok(Method::Get), - "POST" => Ok(Method::Post), - "PUT" => Ok(Method::Put), - _ => Err(Error::Parameter(format!("unknown method {}", s))), - } - } -} diff --git a/crates/http-std/src/default_client.rs b/crates/http-std/src/default_client.rs deleted file mode 100644 index e7c2d56f..00000000 --- a/crates/http-std/src/default_client.rs +++ /dev/null @@ -1,193 +0,0 @@ -use crate::{Client, Error, FetchOptions, Method, Response, Result}; -use async_trait::async_trait; -use rustls::pki_types::ServerName; -use rustls::{ClientConfig, ClientConnection, RootCertStore, StreamOwned}; -use rustls_native_certs::load_native_certs; -use std::collections::HashMap; -use std::io::{Read, Write}; -use std::net::TcpStream; -use std::sync::Arc; -use std::time::Duration; -use url::Url; - -struct Destination { - pub host: String, - pub path: String, - pub port: u16, - pub schema: String, -} - -fn parse_destination(url: &str) -> Result { - let parsed_url = - Url::parse(url).map_err(|err| Error::Parameter(format!("failed to parse url {}", err)))?; - - let host = parsed_url - .host_str() - .ok_or_else(|| Error::Parameter(format!("url must have a host: {}", url)))?; - - let path = if parsed_url.path().is_empty() { - "/".to_string() - } else { - parsed_url.path().to_string() - }; - - let port = parsed_url - .port_or_known_default() - .ok_or_else(|| Error::Parameter("unable to determine port".to_string()))?; - - let schema = parsed_url.scheme().to_string(); - - Ok(Destination { - host: host.to_string(), - path, - port, - schema, - }) -} - -fn transmit(destination: &Destination, request: &[u8]) -> Result> { - let mut buffer = Vec::new(); - - if destination.schema == "https" { - let mut root_store = RootCertStore::empty(); - for cert in load_native_certs().unwrap() { - root_store.add(cert).unwrap(); - } - - let config = ClientConfig::builder() - .with_root_certificates(root_store) - .with_no_client_auth(); - - let rc_config = Arc::new(config); - - let stream = TcpStream::connect((&destination.host[..], destination.port)) - .map_err(|err| Error::Network(format!("failed to connect to host: {}", err)))?; - - let server_name = ServerName::try_from(destination.host.clone()) - .map_err(|_| Error::Network("invalid DNS name".to_string()))?; - - let client = ClientConnection::new(rc_config, server_name) - .map_err(|err| Error::Network(err.to_string()))?; - let mut tls_stream = StreamOwned::new(client, stream); - - tls_stream - .get_ref() - .set_read_timeout(Some(Duration::from_secs(60))) - .map_err(|err| Error::Network(err.to_string()))?; - tls_stream - .get_ref() - .set_write_timeout(Some(Duration::from_secs(60))) - .map_err(|err| Error::Network(err.to_string()))?; - - tls_stream - .write_all(request) - .map_err(|err| Error::Network(err.to_string()))?; - - tls_stream - .flush() - .map_err(|err| Error::Network(err.to_string()))?; - - tls_stream - .read_to_end(&mut buffer) - .map_err(|err| Error::Network(err.to_string()))?; - } else { - let mut stream = TcpStream::connect((&destination.host[..], destination.port)) - .map_err(|err| Error::Network(format!("failed to connect to host: {}", err)))?; - - stream - .set_read_timeout(Some(Duration::from_secs(60))) - .map_err(|err| Error::Network(err.to_string()))?; - stream - .set_write_timeout(Some(Duration::from_secs(60))) - .map_err(|err| Error::Network(err.to_string()))?; - - stream - .write_all(request) - .map_err(|err| Error::Network(err.to_string()))?; - - stream - .flush() - .map_err(|err| Error::Network(err.to_string()))?; - - stream - .read_to_end(&mut buffer) - .map_err(|err| Error::Network(err.to_string()))?; - } - - Ok(buffer) -} - -fn parse_response(response_bytes: &[u8]) -> Result { - let header_end = response_bytes - .windows(4) - .position(|window| window == b"\r\n\r\n") - .ok_or_else(|| Error::Response("invalid HTTP response format".to_string()))?; - - let header_part = &response_bytes[..header_end]; - - let header_str = String::from_utf8_lossy(header_part); - - let mut header_lines = header_str.lines(); - let status_line = header_lines - .next() - .ok_or_else(|| Error::Response("missing status line".to_string()))?; - - let status_parts: Vec<&str> = status_line.split_whitespace().collect(); - if status_parts.len() < 3 { - return Err(Error::Response("invalid status line format".to_string())); - } - - let status_code = status_parts[1] - .parse::() - .map_err(|_| Error::Response("invalid status code".to_string()))?; - - let mut headers = HashMap::new(); - for line in header_lines { - if let Some((key, value)) = line.split_once(": ") { - headers.insert(key.to_string(), value.to_string()); - } - } - - let body = response_bytes[header_end + 4..].to_vec(); - - Ok(Response { - status_code, - headers, - body, - }) -} - -pub struct DefaultClient; - -#[async_trait] -impl Client for DefaultClient { - async fn fetch(&self, url: &str, options: Option) -> Result { - let options = options.unwrap_or_default(); - let destination = parse_destination(url)?; - let method = options.method.unwrap_or(Method::Get); - - let mut request = format!( - "{} {} HTTP/1.1\r\n\ - Host: {}\r\n\ - Connection: close\r\n", - method, destination.path, destination.host, - ); - if let Some(headers) = &options.headers { - if !headers.is_empty() { - for (key, value) in headers { - request.push_str(&format!("{}: {}\r\n", key, value)); - } - } - } - request.push_str("\r\n"); - - let mut request_bytes = request.into_bytes(); - if let Some(body) = &options.body { - request_bytes.extend_from_slice(body); - } - - let response_bytes = transmit(&destination, &request_bytes)?; - - parse_response(&response_bytes) - } -} diff --git a/crates/http-std/src/error.rs b/crates/http-std/src/error.rs deleted file mode 100644 index 8075c271..00000000 --- a/crates/http-std/src/error.rs +++ /dev/null @@ -1,26 +0,0 @@ -#[cfg(not(target_arch = "wasm32"))] -use reqwest::Error as ReqwestError; - -#[derive(thiserror::Error, Debug, Clone, PartialEq)] -pub enum Error { - #[error("unknown error {0}")] - Unknown(String), - #[error("parameter error {0}")] - Parameter(String), - #[error("network error {0}")] - Network(String), - #[error("response error {0}")] - Response(String), - - #[error("reqwest error {0}")] - Reqwest(String), -} - -#[cfg(not(target_arch = "wasm32"))] -impl From for Error { - fn from(err: ReqwestError) -> Self { - Error::Reqwest(err.to_string()) - } -} - -pub type Result = std::result::Result; diff --git a/crates/http-std/src/lib.rs b/crates/http-std/src/lib.rs deleted file mode 100644 index 7c23bab3..00000000 --- a/crates/http-std/src/lib.rs +++ /dev/null @@ -1,51 +0,0 @@ -mod client; -#[cfg(not(target_arch = "wasm32"))] -mod default_client; -mod error; -#[cfg(not(target_arch = "wasm32"))] -mod reqwest_client; - -#[cfg(target_arch = "wasm32")] -use async_trait::async_trait; -use lazy_static::lazy_static; -use std::sync::{Arc, Mutex}; - -pub use client::{Client, FetchOptions, Method, Response}; -pub use error::{Error, Result}; - -#[cfg(not(target_arch = "wasm32"))] -lazy_static! { - static ref CLIENT: Mutex> = - Mutex::new(Arc::new(reqwest_client::ReqwestClient::new())); -} - -#[cfg(target_arch = "wasm32")] -lazy_static! { - static ref CLIENT: Mutex> = Mutex::new(Arc::new(ForeignEmptyClient)); -} - -pub fn set_client(client: Arc) { - let mut global_client = CLIENT.lock().unwrap(); - *global_client = client; -} - -pub fn get_client() -> Arc { - let client = CLIENT.lock().unwrap(); - client.clone() -} - -pub async fn fetch(url: &str, options: Option) -> Result { - let client = get_client(); - client.fetch(url, options).await -} - -#[cfg(target_arch = "wasm32")] -pub struct ForeignEmptyClient; - -#[cfg(target_arch = "wasm32")] -#[async_trait] -impl Client for ForeignEmptyClient { - async fn fetch(&self, _url: &str, _options: Option) -> Result { - return Err(Error::Unknown("global client not set".to_string())); - } -} diff --git a/crates/http-std/src/reqwest_client.rs b/crates/http-std/src/reqwest_client.rs deleted file mode 100644 index b45551b8..00000000 --- a/crates/http-std/src/reqwest_client.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::{Client, FetchOptions, Method, Response, Result}; -use async_trait::async_trait; -use reqwest::header::HeaderMap; -use std::collections::HashMap; -use std::convert::TryFrom; - -pub struct ReqwestClient { - client: reqwest::Client, -} - -impl ReqwestClient { - pub fn new() -> Self { - ReqwestClient { - client: reqwest::Client::new(), - } - } -} - -#[async_trait] -impl Client for ReqwestClient { - async fn fetch(&self, url: &str, options: Option) -> Result { - let options = options.unwrap_or_default(); - let method = options.method.unwrap_or(Method::Get).to_string(); - - let mut req = match method.as_str() { - "GET" => self.client.get(url), - "POST" => self.client.post(url), - "PUT" => self.client.put(url), - _ => unreachable!(), - }; - - if let Some(headers) = options.headers { - let mut req_headers = HeaderMap::new(); - for (key, value) in headers { - req_headers.insert( - reqwest::header::HeaderName::try_from(key.as_str()).unwrap(), - value.parse().unwrap(), - ); - } - req = req.headers(req_headers); - } - - if let Some(body) = options.body { - req = req.body(body); - } - - let res = req.send().await.map_err(crate::Error::from)?; - - let status_code = res.status().as_u16(); - let mut headers = HashMap::new(); - for (key, value) in res.headers().iter() { - headers.insert(key.to_string(), value.to_str().unwrap().to_string()); - } - - let body = res.bytes().await.map_err(crate::Error::from)?.to_vec(); - - Ok(Response { - status_code, - headers, - body, - }) - } -} diff --git a/crates/web5_cli/build/x86_64_unknown_linux_gnu/Dockerfile b/crates/web5_cli/build/x86_64_unknown_linux_gnu/Dockerfile index 18909259..c4ee155a 100644 --- a/crates/web5_cli/build/x86_64_unknown_linux_gnu/Dockerfile +++ b/crates/web5_cli/build/x86_64_unknown_linux_gnu/Dockerfile @@ -19,7 +19,6 @@ COPY bindings/web5_c ./bindings/web5_c COPY bindings/web5_uniffi_wrapper ./bindings/web5_uniffi_wrapper COPY bindings/web5_uniffi ./bindings/web5_uniffi COPY bindings/web5_wasm ./bindings/web5_wasm -COPY crates/http-std ./crates/http-std COPY crates/web5 ./crates/web5 COPY crates/web5_cli ./crates/web5_cli diff --git a/crates/web5_cli/build/x86_64_unknown_linux_musl/Dockerfile b/crates/web5_cli/build/x86_64_unknown_linux_musl/Dockerfile index 235b6c41..aaa61bc0 100644 --- a/crates/web5_cli/build/x86_64_unknown_linux_musl/Dockerfile +++ b/crates/web5_cli/build/x86_64_unknown_linux_musl/Dockerfile @@ -36,7 +36,6 @@ COPY bindings/web5_c ./bindings/web5_c COPY bindings/web5_uniffi_wrapper ./bindings/web5_uniffi_wrapper COPY bindings/web5_uniffi ./bindings/web5_uniffi COPY bindings/web5_wasm ./bindings/web5_wasm -COPY crates/http-std ./crates/http-std COPY crates/web5 ./crates/web5 COPY crates/web5_cli ./crates/web5_cli