diff --git a/Cargo.toml b/Cargo.toml index 03e2f23..3ce7dbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ categories = ["web-programming", "encoding", "data-structures"] name = "cloudevents" [features] +http-binding = ["async-trait", "bytes", "futures", "http"] actix = ["actix-web", "async-trait", "bytes", "futures", "http"] reqwest = ["reqwest-lib", "async-trait", "bytes", "http"] rdkafka = ["rdkafka-lib", "bytes", "futures"] @@ -43,17 +44,20 @@ bytes = { version = "^1.0", optional = true } futures = { version = "^0.3", optional = true } http = { version = "0.2", optional = true } hyper = { version = "^0.14", optional = true } -axum-lib = { version = "^0.2", optional = true , package="axum"} +axum-lib = { version = "^0.2", optional = true, package="axum"} http-body = { version = "^0.4", optional = true} [target."cfg(not(target_arch = \"wasm32\"))".dependencies] hostname = "^0.3" uuid = { version = "^0.8", features = ["v4"] } -[target.'cfg(target_arch = "wasm32")'.dependencies] +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] web-sys = { version = "^0.3", features = ["Window", "Location"] } uuid = { version = "^0.8", features = ["v4", "wasm-bindgen"] } +[target.'cfg(all(target_arch = "wasm32", target_os="wasi"))'.dependencies] +uuid = { version = "^0.8", features = ["v4"] } + [dev-dependencies] rstest = "0.6" claim = "0.3.1" diff --git a/src/binding/http/mod.rs b/src/binding/http/mod.rs index 4fcae76..52b0e03 100644 --- a/src/binding/http/mod.rs +++ b/src/binding/http/mod.rs @@ -1,5 +1,5 @@ pub mod builder; -mod deserializer; +pub mod deserializer; mod headers; use crate::{ @@ -11,7 +11,11 @@ pub use headers::Headers; mod serializer; pub use builder::Builder; +use core::convert::TryFrom; +use http::Response; pub use serializer::Serializer; +use std::convert::TryInto; +use std::fmt::Debug; pub static SPEC_VERSION_HEADER: &str = "ce-specversion"; @@ -26,3 +30,42 @@ pub fn to_event<'a, T: Headers<'a>>( pub fn header_prefix(name: &str) -> String { super::header_prefix("ce-", name) } + +impl TryFrom> for Event +where + T: TryInto>, + >>::Error: Debug, +{ + type Error = crate::message::Error; + + fn try_from(response: Response) -> Result { + let headers = response.headers().to_owned(); + let body = T::try_into(response.into_body()).unwrap(); + + to_event(&headers, body) + } +} + +#[cfg(test)] +mod tests { + use crate::test::fixtures; + use crate::Event; + use core::convert::TryFrom; + use http::Response; + + #[test] + fn test_response_to_event() { + let event = fixtures::v10::minimal_string_extension(); + + let response = Response::builder() + .header("ce-id", fixtures::id()) + .header("ce-source", fixtures::source()) + .header("ce-type", fixtures::ty()) + .header("ce-specversion", "1.0") + .header("ce-someint", "10") + .body(Vec::new()) + .unwrap(); + + assert_eq!(event, Event::try_from(response).unwrap()); + } +} diff --git a/src/binding/http/serializer.rs b/src/binding/http/serializer.rs index 1741ce9..30ca8e1 100644 --- a/src/binding/http/serializer.rs +++ b/src/binding/http/serializer.rs @@ -6,7 +6,14 @@ use crate::binding::{ CLOUDEVENTS_JSON_HEADER, }; use crate::event::SpecVersion; -use crate::message::{BinarySerializer, MessageAttributeValue, Result, StructuredSerializer}; +use crate::message::BinaryDeserializer; +use crate::message::{ + BinarySerializer, Error, MessageAttributeValue, Result, StructuredSerializer, +}; +use crate::Event; +use http::Request; +use std::convert::TryFrom; +use std::fmt::Debug; macro_rules! str_to_header_value { ($header_value:expr) => { @@ -70,3 +77,81 @@ impl StructuredSerializer for Serializer { builder.body(bytes) } } + +impl BinarySerializer>> for http::request::Builder +where + T: TryFrom>, + >>::Error: Debug, +{ + fn set_spec_version(mut self, sv: SpecVersion) -> Result { + self = self.header(SPEC_VERSION_HEADER, &sv.to_string()); + Ok(self) + } + + fn set_attribute(mut self, name: &str, value: MessageAttributeValue) -> Result { + let key = &header_prefix(name); + self = self.header(key, &value.to_string()); + Ok(self) + } + + fn set_extension(mut self, name: &str, value: MessageAttributeValue) -> Result { + let key = &header_prefix(name); + self = self.header(key, &value.to_string()); + Ok(self) + } + + fn end_with_data(self, bytes: Vec) -> Result>> { + let body = T::try_from(bytes).unwrap(); + self.body(Some(body)).map_err(|e| Error::Other { + source: Box::new(e), + }) + } + + fn end(self) -> Result>> { + self.body(None).map_err(|e| Error::Other { + source: Box::new(e), + }) + } +} + +impl TryFrom for Request> +where + T: TryFrom>, + >>::Error: Debug, +{ + type Error = crate::message::Error; + + fn try_from(event: Event) -> Result { + BinaryDeserializer::deserialize_binary(event, http::request::Builder::new()) + } +} + +#[cfg(test)] +mod tests { + use crate::test::fixtures; + use bytes::Bytes; + use http::Request; + use std::convert::TryFrom; + + #[test] + fn test_event_to_http_request() { + let event = fixtures::v10::minimal_string_extension(); + let request: Request>> = Request::try_from(event).unwrap(); + + assert_eq!(request.headers()["ce-id"], "0001"); + assert_eq!(request.headers()["ce-type"], "test_event.test_application"); + } + + #[test] + fn test_event_to_bytes_body() { + let event = fixtures::v10::full_binary_json_data_string_extension(); + let request: Request>> = Request::try_from(event).unwrap(); + + assert_eq!(request.headers()["ce-id"], "0001"); + assert_eq!(request.headers()["ce-type"], "test_event.test_application"); + assert_eq!( + request.body().as_ref().unwrap(), + &Bytes::from(fixtures::json_data().to_string()) + ); + } +} diff --git a/src/binding/mod.rs b/src/binding/mod.rs index 245cb1d..6cd36a3 100644 --- a/src/binding/mod.rs +++ b/src/binding/mod.rs @@ -5,6 +5,7 @@ pub mod actix; #[cfg(feature = "axum")] pub mod axum; #[cfg(any( + feature = "http-binding", feature = "actix", feature = "warp", feature = "reqwest", diff --git a/src/event/attributes.rs b/src/event/attributes.rs index 12cce2a..87921ab 100644 --- a/src/event/attributes.rs +++ b/src/event/attributes.rs @@ -268,7 +268,7 @@ pub(crate) fn default_hostname() -> Url { .unwrap() } -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] pub(crate) fn default_hostname() -> Url { use std::str::FromStr; @@ -281,3 +281,10 @@ pub(crate) fn default_hostname() -> Url { ) .unwrap() } + +#[cfg(all(target_arch = "wasm32", target_os = "wasi"))] +pub(crate) fn default_hostname() -> Url { + use std::str::FromStr; + + Url::from_str("http://localhost").unwrap() +}