Skip to content

Commit

Permalink
Feature flag http client
Browse files Browse the repository at this point in the history
  • Loading branch information
Diane Huxley committed Oct 1, 2024
1 parent 79d3702 commit 25669d2
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 33 deletions.
8 changes: 7 additions & 1 deletion crates/web5/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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"
Expand Down
20 changes: 18 additions & 2 deletions crates/web5/src/credentials/credential_schema.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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<String, String> = 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!(
Expand Down
42 changes: 18 additions & 24 deletions crates/web5/src/dids/methods/did_dht/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String, String> = 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(),
Expand Down Expand Up @@ -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<String, String> = 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);
Expand Down
17 changes: 14 additions & 3 deletions crates/web5/src/dids/methods/did_web/resolver.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -43,10 +45,19 @@ impl Resolver {
}

pub async fn resolve(&self) -> Result<ResolutionResult, ResolutionMetadataError> {
let response = http_std::fetch(&self.http_url, None)
let headers: HashMap<String, String> = 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) {
Expand Down
5 changes: 2 additions & 3 deletions crates/web5/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)]
Expand Down
172 changes: 172 additions & 0 deletions crates/web5/src/http.rs
Original file line number Diff line number Diff line change
@@ -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<HashMap<String, String>>
) -> std::result::Result<HttpResponse, Box<dyn std::error::Error>>;

async fn post(
&self,
url: &str,
headers: Option<HashMap<String, String>>,
body: &[u8]
) -> std::result::Result<HttpResponse, Box<dyn std::error::Error>>;

async fn put(
&self,
url: &str,
headers: Option<HashMap<String, String>>,
body: &[u8]
) -> std::result::Result<HttpResponse, Box<dyn std::error::Error>>;
}

pub struct HttpResponse {
pub status_code: u16,
#[allow(dead_code)]
pub headers: HashMap<String, String>,
pub body: Vec<u8>,
}

static HTTP_CLIENT: OnceCell<Arc<dyn HttpClient>> = 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<dyn HttpClient>) {
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<dyn HttpClient>) {
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<HashMap<String, String>>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
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<HashMap<String, String>>,
body: &[u8],
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
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<HashMap<String, String>>,
body: &[u8],
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
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,
})
}
}
}
3 changes: 3 additions & 0 deletions crates/web5/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

0 comments on commit 25669d2

Please sign in to comment.