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 +}