Upgrade dependencies, including http and hyper, where possible. (#233)

* Upgrade axum.
Breaks docs.

Signed-off-by: Omar Zabala-Ferrera <73452461+ozabalaferrera@users.noreply.github.com>

* Upgrade several dependencies.
delegate-attr, base64, snafu, bitflags, hostname, and serde_yaml.

Signed-off-by: Omar Zabala-Ferrera <73452461+ozabalaferrera@users.noreply.github.com>

* Change target wasm32-wasi to wasm32-wasip1.

Signed-off-by: Omar Zabala-Ferrera <73452461+ozabalaferrera@users.noreply.github.com>

---------

Signed-off-by: Omar Zabala-Ferrera <73452461+ozabalaferrera@users.noreply.github.com>
This commit is contained in:
Omar Zabala-Ferrera 2024-12-02 11:45:10 -05:00 committed by GitHub
parent 9b38aead8d
commit 09661ddaf7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 636 additions and 169 deletions

View File

@ -22,7 +22,7 @@ jobs:
- x86_64-unknown-linux-gnu - x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl - x86_64-unknown-linux-musl
- wasm32-unknown-unknown - wasm32-unknown-unknown
- wasm32-wasi - wasm32-wasip1
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -104,14 +104,14 @@ jobs:
with: with:
command: build command: build
toolchain: ${{ matrix.toolchain }} toolchain: ${{ matrix.toolchain }}
args: --target ${{ matrix.target }} --features "http-binding hyper hyper_wasi" args: --target ${{ matrix.target }} --features "http-0-2-binding hyper-0-14 hyper_wasi"
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
name: "Test" name: "Test"
if: matrix.target == 'wasm32-wasi' if: matrix.target == 'wasm32-wasi'
with: with:
command: test command: test
toolchain: ${{ matrix.toolchain }} toolchain: ${{ matrix.toolchain }}
args: --target ${{ matrix.target }} --features "http-binding hyper hyper_wasi" args: --target ${{ matrix.target }} --features "http-0-2-binding hyper-0-14 hyper_wasi"
env: env:
CARGO_TARGET_WASM32_WASI_RUNNER: wasmedge CARGO_TARGET_WASM32_WASI_RUNNER: wasmedge
# Build examples # Build examples

View File

@ -1,6 +1,6 @@
[package] [package]
name = "cloudevents-sdk" name = "cloudevents-sdk"
version = "0.7.0" version = "0.8.0"
authors = ["Francesco Guardiani <francescoguard@gmail.com>"] authors = ["Francesco Guardiani <francescoguard@gmail.com>"]
license-file = "LICENSE" license-file = "LICENSE"
edition = "2018" edition = "2018"
@ -23,57 +23,60 @@ name = "cloudevents"
[features] [features]
http-binding = ["async-trait", "bytes", "futures", "http"] http-binding = ["async-trait", "bytes", "futures", "http"]
actix = ["actix-web", "actix-http", "async-trait", "bytes", "futures", "http"] http-0-2-binding = ["async-trait", "bytes", "futures", "http-0-2"]
actix = ["actix-web", "actix-http", "async-trait", "bytes", "futures", "http-0-2"]
reqwest = ["reqwest-lib", "async-trait", "bytes", "http", "uuid/js"] reqwest = ["reqwest-lib", "async-trait", "bytes", "http", "uuid/js"]
rdkafka = ["rdkafka-lib", "bytes", "futures"] rdkafka = ["rdkafka-lib", "bytes", "futures"]
warp = ["warp-lib", "bytes", "http", "hyper"] warp = ["warp-lib", "bytes", "http-0-2", "http-body-util", "hyper-0-14"]
axum = ["bytes", "http", "hyper", "axum-lib", "http-body", "async-trait"] axum = ["bytes", "http", "hyper", "axum-lib", "http-body-util", "async-trait"]
poem = ["bytes", "http", "poem-lib", "hyper", "async-trait"] poem = ["bytes", "http", "poem-lib", "hyper", "async-trait", "http-body-util", "futures"]
nats = ["nats-lib"] nats = ["nats-lib"]
[dependencies] [dependencies]
serde = { version = "^1.0", features = ["derive"] } serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0" serde_json = "^1.0"
chrono = { version = "^0.4", features = ["serde"] } chrono = { version = "^0.4", features = ["serde"] }
delegate-attr = "^0.2" delegate-attr = "^0.3"
base64 = "^0.12" base64 = "^0.22"
url = { version = "^2.1", features = ["serde"] } url = { version = "^2.5", features = ["serde"] }
snafu = "^0.6" snafu = "^0.8"
bitflags = "^1.2" bitflags = "^2.6"
uuid = { version = "1", features = ["v4"] } uuid = { version = "1", features = ["v4"] }
# runtime optional deps # runtime optional deps
actix-web = { version = "4", optional = true } actix-web = { version = "4", optional = true }
actix-http = { version = "3", optional = true } actix-http = { version = "3", optional = true }
reqwest-lib = { version = "^0.11", default-features = false, features = ["rustls-tls"], optional = true, package = "reqwest" } reqwest-lib = { version = "^0.12", default-features = false, features = ["rustls-tls"], optional = true, package = "reqwest" }
rdkafka-lib = { version = "^0.36", features = ["cmake-build"], optional = true, package = "rdkafka" } rdkafka-lib = { version = "^0.36", features = ["cmake-build"], optional = true, package = "rdkafka" }
warp-lib = { version = "^0.3", optional = true, package = "warp" } warp-lib = { version = "^0.3", optional = true, package = "warp" }
async-trait = { version = "^0.1.33", optional = true } async-trait = { version = "^0.1", optional = true }
bytes = { version = "^1.0", optional = true } bytes = { version = "^1.0", optional = true }
futures = { version = "^0.3", optional = true } futures = { version = "^0.3", optional = true, features = ["compat"]}
http = { version = "0.2", optional = true } http = { version = "1.1", optional = true}
axum-lib = { version = "^0.6", optional = true, package="axum"} http-0-2 = { version = "0.2", optional = true, package = "http"}
http-body = { version = "^0.4", optional = true } axum-lib = { version = "^0.7", optional = true, package="axum"}
poem-lib = { version = "=1.2.34", optional = true, package = "poem" } http-body-util = {version = "^0.1", optional = true}
nats-lib = { version = "0.21.0", optional = true, package = "nats" } poem-lib = { version = "^3.1", optional = true, package = "poem" }
nats-lib = { version = "0.25.0", optional = true, package = "nats" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies] [target."cfg(not(target_arch = \"wasm32\"))".dependencies]
hostname = "^0.3" hostname = "^0.4"
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
web-sys = { version = "^0.3", features = ["Window", "Location"] } web-sys = { version = "^0.3", features = ["Window", "Location"] }
[target.'cfg(not(target_os = "wasi"))'.dependencies] [target.'cfg(not(target_os = "wasi"))'.dependencies]
hyper = { version = "^0.14", optional = true } hyper = { version = "^1.4", optional = true, package="hyper" }
hyper-0-14 = { version = "^0.14", optional = true, package = "hyper"}
[target.'cfg(all(target_arch = "wasm32", target_os = "wasi"))'.dependencies] [target.'cfg(all(target_arch = "wasm32", target_os = "wasi"))'.dependencies]
hyper_wasi = { version = "0.15", features = ["full"], optional = true } hyper_wasi = { version = "0.15", features = ["full"], optional = true }
[dev-dependencies] [dev-dependencies]
rstest = "0.6" rstest = "0.23"
claims = "0.7.1" claims = "0.7.1"
version-sync = "0.9.2" version-sync = "0.9.2"
serde_yaml = "0.8" serde_yaml = "^0.9"
rmp-serde = "1" rmp-serde = "1"
# runtime dev-deps # runtime dev-deps
@ -81,14 +84,14 @@ rmp-serde = "1"
url = { version = "^2.1", features = ["serde"] } url = { version = "^2.1", features = ["serde"] }
serde_json = { version = "^1.0" } serde_json = { version = "^1.0" }
chrono = { version = "^0.4", features = ["serde"] } chrono = { version = "^0.4", features = ["serde"] }
mockito = "0.25.1" mockito = "0.31.1"
mime = "0.3" mime = "0.3"
[target.'cfg(not(target_os = "wasi"))'.dev-dependencies] [target.'cfg(not(target_os = "wasi"))'.dev-dependencies]
actix-rt = { version = "^2" } actix-rt = { version = "^2" }
tokio = { version = "^1.0", features = ["full"] } tokio = { version = "^1.0", features = ["full"] }
tower = { version = "0.4", features = ["util"] } tower = { version = "0.5", features = ["util"] }
[target.'cfg(all(target_arch = "wasm32", target_os = "wasi"))'.dev-dependencies] [target.'cfg(all(target_arch = "wasm32", target_os = "wasi"))'.dev-dependencies]
tokio_wasi = { version = "1", features = [ tokio_wasi = { version = "1", features = [

View File

@ -42,7 +42,7 @@ enabling your Protocol Binding of choice:
```toml ```toml
[dependencies] [dependencies]
cloudevents-sdk = { version = "0.7.0" } cloudevents-sdk = { version = "0.8.0" }
``` ```
Now you can start creating events: Now you can start creating events:

View File

@ -7,7 +7,7 @@ edition = "2018"
[dependencies] [dependencies]
cloudevents-sdk = { path = "../..", features = ["actix"] } cloudevents-sdk = { path = "../..", features = ["actix"] }
actix-web = "4" actix-web = "4"
actix-cors = "0.6.0-beta.8" actix-cors = "^0.7"
serde_json = "^1.0" serde_json = "^1.0"
url = { version = "^2.1" } url = { version = "^2.1" }
env_logger = "0.7.1" env_logger = "^0.11"

View File

@ -6,16 +6,16 @@ edition = "2021"
[dependencies] [dependencies]
cloudevents-sdk = { path = "../..", features = ["axum"] } cloudevents-sdk = { path = "../..", features = ["axum"] }
axum = "^0.6" axum = "^0.7"
http = "^0.2" http = "^1.1"
tokio = { version = "^1", features = ["full"] } tokio = { version = "^1", features = ["full"] }
tracing = "^0.1" tracing = "^0.1"
tracing-subscriber = "^0.2" tracing-subscriber = "^0.3"
tower-http = { version = "^0.1", features = ["trace"] } tower-http = { version = "^0.5", features = ["trace"] }
[dev-dependencies] [dev-dependencies]
tower = { version = "^0.4", features = ["util"] } tower = { version = "^0.4", features = ["util"] }
serde = { version = "^1.0", features = ["derive"] } serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0" serde_json = "^1.0"
chrono = { version = "^0.4", features = ["serde"] } chrono = { version = "^0.4", features = ["serde"] }
hyper = { version = "^0.14" } hyper = { version = "^1.4" }

View File

@ -4,7 +4,6 @@ use axum::{
}; };
use cloudevents::Event; use cloudevents::Event;
use http::StatusCode; use http::StatusCode;
use std::net::SocketAddr;
use tower_http::trace::TraceLayer; use tower_http::trace::TraceLayer;
fn echo_app() -> Router { fn echo_app() -> Router {
@ -27,12 +26,8 @@ async fn main() {
} }
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let service = echo_app(); let service = echo_app();
let addr = SocketAddr::from(([127, 0, 0, 1], 8080)); let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
tracing::debug!("listening on {}", addr); axum::serve(listener, service).await.unwrap();
axum::Server::bind(&addr)
.serve(service.into_make_service())
.await
.unwrap();
} }
#[cfg(test)] #[cfg(test)]

View File

@ -9,4 +9,4 @@ edition = "2021"
[dependencies] [dependencies]
cloudevents-sdk = { path = "../..", features = ["nats"] } cloudevents-sdk = { path = "../..", features = ["nats"] }
serde_json = "^1.0" serde_json = "^1.0"
nats = "0.21.0" nats = "^0.25"

View File

@ -7,8 +7,8 @@ edition = "2021"
cloudevents-sdk = { path = "../..", features = ["poem"] } cloudevents-sdk = { path = "../..", features = ["poem"] }
tokio = { version = "1.13", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.13", features = ["macros", "rt-multi-thread"] }
tracing = "0.1" tracing = "0.1"
poem = { version = "1" } poem = { version = "^3.0" }
tracing-subscriber = "0.2" tracing-subscriber = "0.3"
serde_json = "1.0" serde_json = "1.0"
[dev-dependencies] [dev-dependencies]

View File

@ -10,7 +10,7 @@ edition = "2018"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
reqwest = "^0.11" reqwest = "^0.12"
uuid = "1" uuid = "1"
cloudevents-sdk = { path = "../..", features = ["reqwest"] } cloudevents-sdk = { path = "../..", features = ["reqwest"] }
url = { version = "^2.1" } url = { version = "^2.1" }

View File

@ -5,11 +5,11 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
cloudevents-sdk = { path = "../..", features = ["http-binding", "hyper_wasi", "hyper" ] } cloudevents-sdk = { path = "../..", features = ["http-0-2-binding", "hyper_wasi", "hyper-0-14" ] }
hyper_wasi = { version = "0.15", features = ["full"] } hyper_wasi = { version = "0.15", features = ["full"] }
log = "0.4.21" log = "0.4.21"
tokio_wasi = { version = "1", features = ["io-util", "fs", "net", "time", "rt", "macros"] } tokio_wasi = { version = "1", features = ["io-util", "fs", "net", "time", "rt", "macros"] }
serde_json = " 1.0.116" serde_json = "^1.0"
[dev-dependencies] [dev-dependencies]
bytes = "1.6.0" bytes = "1.6.0"

View File

@ -1,5 +1,5 @@
use cloudevents::binding::http::builder::adapter::to_response; use cloudevents::binding::http_0_2::builder::adapter::to_response;
use cloudevents::binding::http::to_event; use cloudevents::binding::http_0_2::to_event;
use hyper::service::{make_service_fn, service_fn}; use hyper::service::{make_service_fn, service_fn};
use hyper::Server; use hyper::Server;
@ -23,7 +23,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
} }
Ok(()) Ok(())
} }
async fn handle_request(req: Request<Body>) -> Result<Response<Body>, anyhow::Error> { async fn handle_request(
req: Request<Body>,
) -> Result<Response<Body>, anyhow::Error> {
match (req.method(), req.uri().path()) { match (req.method(), req.uri().path()) {
(&Method::POST, "/") => { (&Method::POST, "/") => {
let headers = req.headers().clone(); let headers = req.headers().clone();
@ -34,7 +36,9 @@ async fn handle_request(req: Request<Body>) -> Result<Response<Body>, anyhow::Er
to_response(_respevt).map_err(|err| err.into()) to_response(_respevt).map_err(|err| err.into())
} }
(&Method::GET, "/health/readiness") => Ok(Response::new(Body::from(""))), (&Method::GET, "/health/readiness") => {
Ok(Response::new(Body::from("")))
}
(&Method::GET, "/health/liveness") => Ok(Response::new(Body::from(""))), (&Method::GET, "/health/liveness") => Ok(Response::new(Body::from(""))),
_ => { _ => {
let mut not_found = Response::default(); let mut not_found = Response::default();

View File

@ -1,4 +1,4 @@
use crate::binding::http::{to_event, Headers}; use crate::binding::http_0_2::{to_event, Headers};
use crate::Event; use crate::Event;
use actix_web::dev::Payload; use actix_web::dev::Payload;
use actix_web::web::BytesMut; use actix_web::web::BytesMut;
@ -6,6 +6,7 @@ use actix_web::{web, HttpRequest};
use async_trait::async_trait; use async_trait::async_trait;
use futures::{future::LocalBoxFuture, FutureExt, StreamExt}; use futures::{future::LocalBoxFuture, FutureExt, StreamExt};
use http::header::{AsHeaderName, HeaderName, HeaderValue}; use http::header::{AsHeaderName, HeaderName, HeaderValue};
use http_0_2 as http;
/// Implement Headers for the actix HeaderMap /// Implement Headers for the actix HeaderMap
impl<'a> Headers<'a> for actix_http::header::HeaderMap { impl<'a> Headers<'a> for actix_http::header::HeaderMap {

View File

@ -1,8 +1,9 @@
use crate::binding::http::{Builder, Serializer}; use crate::binding::http_0_2::{Builder, Serializer};
use crate::message::{BinaryDeserializer, Result}; use crate::message::{BinaryDeserializer, Result};
use crate::Event; use crate::Event;
use actix_web::http::StatusCode; use actix_web::http::StatusCode;
use actix_web::{HttpRequest, HttpResponse, HttpResponseBuilder}; use actix_web::{HttpRequest, HttpResponse, HttpResponseBuilder};
use http_0_2 as http;
impl Builder<HttpResponse> for HttpResponseBuilder { impl Builder<HttpResponse> for HttpResponseBuilder {
fn header(&mut self, key: &str, value: http::header::HeaderValue) { fn header(&mut self, key: &str, value: http::header::HeaderValue) {

View File

@ -1,45 +1,46 @@
use axum_lib as axum;
use async_trait::async_trait; use async_trait::async_trait;
use axum::extract::FromRequest; use axum::body::Bytes;
use axum::http::Request; use axum::extract::{FromRequest, Request};
use http::request::Parts; use axum::response::Response;
use axum_lib as axum;
use http;
use http::StatusCode; use http::StatusCode;
use http_body::Body;
use hyper::body;
use crate::binding::http::to_event; use crate::binding::http::to_event;
use crate::event::Event; use crate::event::Event;
type BoxError = Box<dyn std::error::Error + Send + Sync>;
#[async_trait] #[async_trait]
impl<S, B> FromRequest<S, B> for Event impl<S> FromRequest<S> for Event
where where
B: Body + Send + 'static, Bytes: FromRequest<S>,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync, S: Send + Sync,
{ {
type Rejection = (StatusCode, String); type Rejection = Response;
async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> { async fn from_request(req: Request, _state: &S) -> Result<Self, Self::Rejection> {
let (Parts { headers, .. }, req_body) = req.into_parts(); let (parts, body) = req.into_parts();
let buf = body::to_bytes(req_body)
.await
.map_err(|e| (StatusCode::BAD_REQUEST, format!("{}", e.into())))?
.to_vec();
to_event(&headers, buf).map_err(|e| (StatusCode::BAD_REQUEST, format!("{}", e))) let body = axum::body::to_bytes(body, usize::MAX).await.map_err(|e| {
Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(axum::body::Body::from(e.to_string()))
.unwrap()
})?;
to_event(&parts.headers, body.to_vec()).map_err(|e| {
Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(axum::body::Body::from(e.to_string()))
.unwrap()
})
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use axum_lib as axum;
use super::*; use super::*;
use axum::body::Body; use axum::body::Body;
use axum::extract::FromRequest;
use axum::http::{self, Request, StatusCode}; use axum::http::{self, Request, StatusCode};
use crate::test::fixtures; use crate::test::fixtures;
@ -80,7 +81,7 @@ mod tests {
assert!(result.is_err()); assert!(result.is_err());
let rejection = result.unwrap_err(); let rejection = result.unwrap_err();
let reason = rejection.0; let reason = rejection.status();
assert_eq!(reason, StatusCode::BAD_REQUEST) assert_eq!(reason, StatusCode::BAD_REQUEST)
} }

View File

@ -155,7 +155,7 @@ mod tests {
); );
let (_, body) = resp.into_parts(); let (_, body) = resp.into_parts();
let body = hyper::body::to_bytes(body).await.unwrap(); let body = axum::body::to_bytes(body, usize::MAX).await.unwrap();
assert_eq!(j.to_string().as_bytes(), body); assert_eq!(j.to_string().as_bytes(), body);
} }

View File

@ -1,27 +1,21 @@
use axum_lib as axum;
use axum::{
body::{boxed, BoxBody},
http::Response,
response::IntoResponse,
};
use http::{header, StatusCode};
use hyper::body::Body;
use crate::binding::http::builder::adapter::to_response; use crate::binding::http::builder::adapter::to_response;
use crate::event::Event; use crate::event::Event;
use axum::{body::Body, http::Response, response::IntoResponse};
use axum_lib as axum;
use http;
use http::{header, StatusCode};
impl IntoResponse for Event { impl IntoResponse for Event {
fn into_response(self) -> Response<BoxBody> { fn into_response(self) -> Response<Body> {
match to_response(self) { match to_response(self) {
Ok(resp) => { Ok(resp) => {
let (parts, body) = resp.into_parts(); let (parts, body) = resp.into_parts();
Response::from_parts(parts, boxed(body)) Response::from_parts(parts, Body::new(body))
} }
Err(err) => Response::builder() Err(err) => Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR) .status(StatusCode::INTERNAL_SERVER_ERROR)
.header(header::CONTENT_TYPE, "text/plain") .header(header::CONTENT_TYPE, "text/plain")
.body(boxed(Body::from(err.to_string()))) .body(Body::from(err.to_string()))
.unwrap(), .unwrap(),
} }
} }
@ -105,7 +99,7 @@ mod tests {
); );
let (_, body) = resp.into_parts(); let (_, body) = resp.into_parts();
let body = hyper::body::to_bytes(body).await.unwrap(); let body = axum::body::to_bytes(body, usize::MAX).await.unwrap();
assert_eq!(fixtures::json_data_binary(), body); assert_eq!(fixtures::json_data_binary(), body);
} }

View File

@ -1,33 +1,38 @@
use bytes::Bytes;
use http::Response; use http::Response;
use hyper::body::Body; use http_body_util::Full;
use std::cell::Cell; use std::cell::Cell;
use crate::binding::http::{Builder, Serializer}; use crate::binding::http::{Builder, Serializer};
use crate::message::{BinaryDeserializer, Error, Result}; use crate::message::{BinaryDeserializer, Error, Result};
use crate::Event; use crate::Event;
use std::convert::Infallible;
type BoxBody = http_body_util::combinators::UnsyncBoxBody<Bytes, Infallible>;
struct Adapter { struct Adapter {
builder: Cell<http::response::Builder>, builder: Cell<http::response::Builder>,
} }
impl Builder<Response<Body>> for Adapter { impl Builder<Response<BoxBody>> for Adapter {
fn header(&mut self, key: &str, value: http::header::HeaderValue) { fn header(&mut self, key: &str, value: http::header::HeaderValue) {
self.builder.set(self.builder.take().header(key, value)); self.builder.set(self.builder.take().header(key, value));
} }
fn body(&mut self, bytes: Vec<u8>) -> Result<Response<Body>> {
fn body(&mut self, bytes: Vec<u8>) -> Result<Response<BoxBody>> {
self.builder self.builder
.take() .take()
.body(Body::from(bytes)) .body(BoxBody::new(Full::from(bytes)))
.map_err(|e| crate::message::Error::Other { .map_err(|e| crate::message::Error::Other {
source: Box::new(e), source: Box::new(e),
}) })
} }
fn finish(&mut self) -> Result<Response<Body>> {
fn finish(&mut self) -> Result<Response<BoxBody>> {
self.body(Vec::new()) self.body(Vec::new())
} }
} }
pub fn to_response(event: Event) -> std::result::Result<Response<Body>, Error> { pub fn to_response(event: Event) -> std::result::Result<Response<BoxBody>, Error> {
BinaryDeserializer::deserialize_binary( BinaryDeserializer::deserialize_binary(
event, event,
Serializer::new(Adapter { Serializer::new(Adapter {

View File

@ -3,6 +3,8 @@ pub mod adapter;
use crate::message::Result; use crate::message::Result;
use http;
pub trait Builder<R> { pub trait Builder<R> {
fn header(&mut self, key: &str, value: http::header::HeaderValue); fn header(&mut self, key: &str, value: http::header::HeaderValue);
fn body(&mut self, bytes: Vec<u8>) -> Result<R>; fn body(&mut self, bytes: Vec<u8>) -> Result<R>;

View File

@ -8,6 +8,8 @@ use crate::{
Result, StructuredDeserializer, StructuredSerializer, Result, StructuredDeserializer, StructuredSerializer,
}, },
}; };
use http;
use std::convert::TryFrom; use std::convert::TryFrom;
pub struct Deserializer<'a, T: Headers<'a>> { pub struct Deserializer<'a, T: Headers<'a>> {

View File

@ -1,5 +1,7 @@
use http::header::{AsHeaderName, HeaderMap, HeaderName, HeaderValue}; use http::header::{AsHeaderName, HeaderMap, HeaderName, HeaderValue};
use http;
/// Any http library should be able to use the /// Any http library should be able to use the
/// [`to_event`](super::to_event) function with an implementation of /// [`to_event`](super::to_event) function with an implementation of
/// this trait. /// this trait.

View File

@ -13,6 +13,8 @@ mod serializer;
pub use builder::Builder; pub use builder::Builder;
use core::convert::TryFrom; use core::convert::TryFrom;
use http::Response; use http::Response;
use http;
pub use serializer::Serializer; pub use serializer::Serializer;
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt::Debug; use std::fmt::Debug;
@ -53,6 +55,8 @@ mod tests {
use core::convert::TryFrom; use core::convert::TryFrom;
use http::Response; use http::Response;
use http;
#[test] #[test]
fn test_response_to_event() { fn test_response_to_event() {
let event = fixtures::v10::minimal_string_extension(); let event = fixtures::v10::minimal_string_extension();

View File

@ -12,6 +12,8 @@ use crate::message::{
}; };
use crate::Event; use crate::Event;
use http::Request; use http::Request;
use http;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt::Debug; use std::fmt::Debug;
@ -131,6 +133,8 @@ mod tests {
use crate::test::fixtures; use crate::test::fixtures;
use bytes::Bytes; use bytes::Bytes;
use http::Request; use http::Request;
use http;
use std::convert::TryFrom; use std::convert::TryFrom;
#[test] #[test]

View File

@ -0,0 +1,44 @@
use http::Response;
use http_0_2 as http;
use hyper::body::Body;
use std::cell::Cell;
#[cfg(not(target_os = "wasi"))]
use hyper_0_14 as hyper;
#[cfg(target_os = "wasi")]
use hyper;
use crate::binding::http_0_2::{Builder, Serializer};
use crate::message::{BinaryDeserializer, Error, Result};
use crate::Event;
struct Adapter {
builder: Cell<http::response::Builder>,
}
impl Builder<Response<Body>> for Adapter {
fn header(&mut self, key: &str, value: http::header::HeaderValue) {
self.builder.set(self.builder.take().header(key, value));
}
fn body(&mut self, bytes: Vec<u8>) -> Result<Response<Body>> {
self.builder
.take()
.body(Body::from(bytes))
.map_err(|e| crate::message::Error::Other {
source: Box::new(e),
})
}
fn finish(&mut self) -> Result<Response<Body>> {
self.body(Vec::new())
}
}
pub fn to_response(event: Event) -> std::result::Result<Response<Body>, Error> {
BinaryDeserializer::deserialize_binary(
event,
Serializer::new(Adapter {
builder: Cell::new(http::Response::builder()),
}),
)
}

View File

@ -0,0 +1,11 @@
#[cfg(feature = "hyper-0-14")]
pub mod adapter;
use crate::message::Result;
use http_0_2 as http;
pub trait Builder<R> {
fn header(&mut self, key: &str, value: http::header::HeaderValue);
fn body(&mut self, bytes: Vec<u8>) -> Result<R>;
fn finish(&mut self) -> Result<R>;
}

View File

@ -0,0 +1,101 @@
use super::{Headers, SPEC_VERSION_HEADER};
use crate::{
binding::CLOUDEVENTS_JSON_HEADER,
event::SpecVersion,
header_value_to_str, message,
message::{
BinaryDeserializer, BinarySerializer, Encoding, MessageAttributeValue, MessageDeserializer,
Result, StructuredDeserializer, StructuredSerializer,
},
};
use http_0_2 as http;
use std::convert::TryFrom;
pub struct Deserializer<'a, T: Headers<'a>> {
headers: &'a T,
body: Vec<u8>,
}
impl<'a, T: Headers<'a>> Deserializer<'a, T> {
pub fn new(headers: &'a T, body: Vec<u8>) -> Deserializer<'a, T> {
Deserializer { headers, body }
}
}
impl<'a, T: Headers<'a>> BinaryDeserializer for Deserializer<'a, T> {
fn deserialize_binary<R: Sized, V: BinarySerializer<R>>(self, mut visitor: V) -> Result<R> {
if self.encoding() != Encoding::BINARY {
return Err(message::Error::WrongEncoding {});
}
let spec_version = SpecVersion::try_from(
self.headers
.get(SPEC_VERSION_HEADER)
.map(|a| header_value_to_str!(a))
.unwrap()?,
)?;
let attributes = spec_version.attribute_names();
visitor = visitor.set_spec_version(spec_version)?;
for (hn, hv) in self.headers.iter().filter(|(hn, _)| {
let key = hn.as_str();
SPEC_VERSION_HEADER.ne(key) && key.starts_with("ce-")
}) {
let name = &hn.as_str()["ce-".len()..];
if attributes.contains(&name) {
visitor = visitor.set_attribute(
name,
MessageAttributeValue::String(String::from(header_value_to_str!(hv)?)),
)?
} else {
visitor = visitor.set_extension(
name,
MessageAttributeValue::String(String::from(header_value_to_str!(hv)?)),
)?
}
}
if let Some(hv) = self.headers.get(http::header::CONTENT_TYPE) {
visitor = visitor.set_attribute(
"datacontenttype",
MessageAttributeValue::String(String::from(header_value_to_str!(hv)?)),
)?
}
if !self.body.is_empty() {
visitor.end_with_data(self.body)
} else {
visitor.end()
}
}
}
impl<'a, T: Headers<'a>> StructuredDeserializer for Deserializer<'a, T> {
fn deserialize_structured<R: Sized, V: StructuredSerializer<R>>(self, visitor: V) -> Result<R> {
if self.encoding() != Encoding::STRUCTURED {
return Err(message::Error::WrongEncoding {});
}
visitor.set_structured_event(self.body)
}
}
impl<'a, T: Headers<'a>> MessageDeserializer for Deserializer<'a, T> {
fn encoding(&self) -> Encoding {
if self
.headers
.get(http::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.filter(|&v| v.starts_with(CLOUDEVENTS_JSON_HEADER))
.is_some()
{
Encoding::STRUCTURED
} else if self.headers.get(SPEC_VERSION_HEADER).is_some() {
Encoding::BINARY
} else {
Encoding::UNKNOWN
}
}
}

View File

@ -0,0 +1,22 @@
use http::header::{AsHeaderName, HeaderMap, HeaderName, HeaderValue};
use http_0_2 as http;
/// Any http library should be able to use the
/// [`to_event`](super::to_event) function with an implementation of
/// this trait.
pub trait Headers<'a> {
type Iterator: Iterator<Item = (&'a HeaderName, &'a HeaderValue)>;
fn get<K: AsHeaderName>(&self, name: K) -> Option<&HeaderValue>;
fn iter(&'a self) -> Self::Iterator;
}
/// Implemention for the HeaderMap used by warp/reqwest
impl<'a> Headers<'a> for HeaderMap<HeaderValue> {
type Iterator = http::header::Iter<'a, HeaderValue>;
fn get<K: AsHeaderName>(&self, name: K) -> Option<&HeaderValue> {
self.get(name)
}
fn iter(&'a self) -> Self::Iterator {
self.iter()
}
}

View File

@ -0,0 +1,73 @@
pub mod builder;
pub mod deserializer;
mod headers;
use crate::{
message::{Error, MessageDeserializer},
Event,
};
use deserializer::Deserializer;
pub use headers::Headers;
mod serializer;
pub use builder::Builder;
use core::convert::TryFrom;
use http::Response;
use http_0_2 as http;
pub use serializer::Serializer;
use std::convert::TryInto;
use std::fmt::Debug;
pub static SPEC_VERSION_HEADER: &str = "ce-specversion";
/// Turn a pile of HTTP headers and a body into a CloudEvent
pub fn to_event<'a, T: Headers<'a>>(
headers: &'a T,
body: Vec<u8>,
) -> std::result::Result<Event, Error> {
MessageDeserializer::into_event(Deserializer::new(headers, body))
}
pub fn header_prefix(name: &str) -> String {
super::header_prefix("ce-", name)
}
impl<T> TryFrom<Response<T>> for Event
where
T: TryInto<Vec<u8>>,
<T as TryInto<Vec<u8>>>::Error: Debug,
{
type Error = crate::message::Error;
fn try_from(response: Response<T>) -> Result<Self, Self::Error> {
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;
use http_0_2 as http;
#[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());
}
}

View File

@ -0,0 +1,159 @@
use std::{cell::RefCell, rc::Rc};
use crate::binding::http_0_2::builder::Builder;
use crate::binding::{
http_0_2::{header_prefix, SPEC_VERSION_HEADER},
CLOUDEVENTS_JSON_HEADER,
};
use crate::event::SpecVersion;
use crate::message::BinaryDeserializer;
use crate::message::{
BinarySerializer, Error, MessageAttributeValue, Result, StructuredSerializer,
};
use crate::Event;
use http::Request;
use http_0_2 as http;
use std::convert::TryFrom;
use std::fmt::Debug;
macro_rules! str_to_header_value {
($header_value:expr) => {
http::header::HeaderValue::from_str(&$header_value.to_string()).map_err(|e| {
crate::message::Error::Other {
source: Box::new(e),
}
})
};
}
pub struct Serializer<T> {
builder: Rc<RefCell<dyn Builder<T>>>,
}
impl<T> Serializer<T> {
pub fn new<B: Builder<T> + 'static>(delegate: B) -> Serializer<T> {
let builder = Rc::new(RefCell::new(delegate));
Serializer { builder }
}
}
impl<T> BinarySerializer<T> for Serializer<T> {
fn set_spec_version(self, spec_version: SpecVersion) -> Result<Self> {
self.builder
.borrow_mut()
.header(SPEC_VERSION_HEADER, str_to_header_value!(spec_version)?);
Ok(self)
}
fn set_attribute(self, name: &str, value: MessageAttributeValue) -> Result<Self> {
self.builder
.borrow_mut()
.header(&header_prefix(name), str_to_header_value!(value)?);
Ok(self)
}
fn set_extension(self, name: &str, value: MessageAttributeValue) -> Result<Self> {
self.builder
.borrow_mut()
.header(&header_prefix(name), str_to_header_value!(value)?);
Ok(self)
}
fn end_with_data(self, bytes: Vec<u8>) -> Result<T> {
self.builder.borrow_mut().body(bytes)
}
fn end(self) -> Result<T> {
self.builder.borrow_mut().finish()
}
}
impl<T> StructuredSerializer<T> for Serializer<T> {
fn set_structured_event(self, bytes: Vec<u8>) -> Result<T> {
let mut builder = self.builder.borrow_mut();
builder.header(
http::header::CONTENT_TYPE.as_str(),
http::HeaderValue::from_static(CLOUDEVENTS_JSON_HEADER),
);
builder.body(bytes)
}
}
impl<T> BinarySerializer<http::request::Request<Option<T>>> for http::request::Builder
where
T: TryFrom<Vec<u8>>,
<T as TryFrom<Vec<u8>>>::Error: Debug,
{
fn set_spec_version(mut self, sv: SpecVersion) -> Result<Self> {
self = self.header(SPEC_VERSION_HEADER, &sv.to_string());
Ok(self)
}
fn set_attribute(mut self, name: &str, value: MessageAttributeValue) -> Result<Self> {
let key = &header_prefix(name);
self = self.header(key, &value.to_string());
Ok(self)
}
fn set_extension(mut self, name: &str, value: MessageAttributeValue) -> Result<Self> {
let key = &header_prefix(name);
self = self.header(key, &value.to_string());
Ok(self)
}
fn end_with_data(self, bytes: Vec<u8>) -> Result<http::request::Request<Option<T>>> {
let body = T::try_from(bytes).unwrap();
self.body(Some(body)).map_err(|e| Error::Other {
source: Box::new(e),
})
}
fn end(self) -> Result<http::request::Request<Option<T>>> {
self.body(None).map_err(|e| Error::Other {
source: Box::new(e),
})
}
}
impl<T> TryFrom<Event> for Request<Option<T>>
where
T: TryFrom<Vec<u8>>,
<T as TryFrom<Vec<u8>>>::Error: Debug,
{
type Error = crate::message::Error;
fn try_from(event: Event) -> Result<Self> {
BinaryDeserializer::deserialize_binary(event, http::request::Builder::new())
}
}
#[cfg(test)]
mod tests {
use crate::test::fixtures;
use bytes::Bytes;
use http::Request;
use http_0_2 as http;
use std::convert::TryFrom;
#[test]
fn test_event_to_http_request() {
let event = fixtures::v10::minimal_string_extension();
let request: Request<Option<Vec<u8>>> = 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<Option<Vec<u8>>> = 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())
);
}
}

View File

@ -11,8 +11,6 @@ pub mod axum;
docsrs, docsrs,
doc(cfg(any( doc(cfg(any(
feature = "http-binding", feature = "http-binding",
feature = "actix",
feature = "warp",
feature = "reqwest", feature = "reqwest",
feature = "axum", feature = "axum",
feature = "poem" feature = "poem"
@ -20,13 +18,19 @@ pub mod axum;
)] )]
#[cfg(any( #[cfg(any(
feature = "http-binding", feature = "http-binding",
feature = "actix",
feature = "warp",
feature = "reqwest", feature = "reqwest",
feature = "axum", feature = "axum",
feature = "poem" feature = "poem"
))] ))]
pub mod http; pub mod http;
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "http-0-2-binding", feature = "actix", feature = "warp",)))
)]
#[cfg(any(feature = "http-0-2-binding", feature = "actix", feature = "warp",))]
pub mod http_0_2;
#[cfg_attr(docsrs, doc(cfg(feature = "nats")))] #[cfg_attr(docsrs, doc(cfg(feature = "nats")))]
#[cfg(feature = "nats")] #[cfg(feature = "nats")]
pub mod nats; pub mod nats;

View File

@ -1,18 +1,16 @@
use async_trait::async_trait; use crate::binding::http::to_event;
use crate::Event;
use poem_lib::error::ResponseError; use poem_lib::error::ResponseError;
use poem_lib::http::StatusCode; use poem_lib::http::StatusCode;
use poem_lib::{FromRequest, Request, RequestBody, Result}; use poem_lib::{FromRequest, Request, RequestBody, Result};
use crate::binding::http::to_event;
use crate::Event;
impl ResponseError for crate::message::Error { impl ResponseError for crate::message::Error {
fn status(&self) -> StatusCode { fn status(&self) -> StatusCode {
StatusCode::BAD_REQUEST StatusCode::BAD_REQUEST
} }
} }
#[async_trait]
impl<'a> FromRequest<'a> for Event { impl<'a> FromRequest<'a> for Event {
async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result<Self> { async fn from_request(req: &'a Request, body: &mut RequestBody) -> Result<Self> {
Ok(to_event(req.headers(), body.take()?.into_vec().await?)?) Ok(to_event(req.headers(), body.take()?.into_vec().await?)?)
@ -57,7 +55,7 @@ mod tests {
let (req, mut body) = req.split(); let (req, mut body) = req.split();
let resp = Event::from_request(&req, &mut body).await.err().unwrap(); let resp = Event::from_request(&req, &mut body).await.err().unwrap();
assert_eq!(resp.as_response().status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
assert_eq!(resp.to_string(), "Invalid specversion BAD SPECIFICATION"); assert_eq!(resp.to_string(), "Invalid specversion BAD SPECIFICATION");
} }

View File

@ -1,14 +1,33 @@
use crate::{AttributesReader, Data, Event};
use bytes::Bytes;
use poem_lib::http::StatusCode; use poem_lib::http::StatusCode;
use poem_lib::{IntoResponse, Response}; use poem_lib::{IntoResponse, Response};
use crate::binding::http::builder::adapter::to_response;
use crate::Event;
impl IntoResponse for Event { impl IntoResponse for Event {
fn into_response(self) -> Response { fn into_response(self) -> Response {
match to_response(self) { let mut builder = Response::builder().status(StatusCode::OK);
Ok(resp) => resp.into(),
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into(), if let Some(dct) = self.datacontenttype() {
builder = builder.content_type(dct);
}
for (key, value) in self.iter() {
builder = builder.header(format!("ce-{key}").as_str(), value.to_string());
}
match self.data {
Some(data) => match data {
Data::Binary(v) => builder.body(Bytes::copy_from_slice(v.as_slice())),
Data::String(s) => builder.body(s.clone()),
Data::Json(j) => match serde_json::to_string(&j) {
Ok(s) => builder.body(s),
Err(e) => Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(e.to_string()),
},
},
None => builder.finish(),
} }
} }
} }

View File

@ -114,7 +114,7 @@ mod private {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use mockito::{mock, Matcher}; use mockito::Matcher;
use reqwest_lib as reqwest; use reqwest_lib as reqwest;
use crate::message::StructuredDeserializer; use crate::message::StructuredDeserializer;
@ -123,7 +123,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_request() { async fn test_request() {
let url = mockito::server_url(); let url = mockito::server_url();
let m = mock("POST", "/") let m = mockito::mock("POST", "/")
.match_header("ce-specversion", "1.0") .match_header("ce-specversion", "1.0")
.match_header("ce-id", "0001") .match_header("ce-id", "0001")
.match_header("ce-type", "test_event.test_application") .match_header("ce-type", "test_event.test_application")
@ -149,7 +149,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_request_with_full_data() { async fn test_request_with_full_data() {
let url = mockito::server_url(); let url = mockito::server_url();
let m = mock("POST", "/") let m = mockito::mock("POST", "/")
.match_header("ce-specversion", "1.0") .match_header("ce-specversion", "1.0")
.match_header("ce-id", "0001") .match_header("ce-id", "0001")
.with_header("ce-type", "test_event.test_application") .with_header("ce-type", "test_event.test_application")
@ -183,7 +183,7 @@ mod tests {
let input = fixtures::v10::full_json_data_string_extension(); let input = fixtures::v10::full_json_data_string_extension();
let url = mockito::server_url(); let url = mockito::server_url();
let m = mock("POST", "/") let m = mockito::mock("POST", "/")
.match_header("content-type", "application/cloudevents+json") .match_header("content-type", "application/cloudevents+json")
.match_body(Matcher::Exact(serde_json::to_string(&input).unwrap())) .match_body(Matcher::Exact(serde_json::to_string(&input).unwrap()))
.create(); .create();
@ -204,8 +204,9 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_batched_request() { async fn test_batched_request() {
let input = vec![fixtures::v10::full_json_data_string_extension()]; let input = vec![fixtures::v10::full_json_data_string_extension()];
let url = mockito::server_url(); let url = mockito::server_url();
let m = mock("POST", "/") let m = mockito::mock("POST", "/")
.match_header("content-type", "application/cloudevents-batch+json") .match_header("content-type", "application/cloudevents-batch+json")
.match_body(Matcher::Exact(serde_json::to_string(&input).unwrap())) .match_body(Matcher::Exact(serde_json::to_string(&input).unwrap()))
.create(); .create();

View File

@ -4,6 +4,7 @@ use crate::binding;
use crate::message::{Error, Result}; use crate::message::{Error, Result};
use crate::Event; use crate::Event;
use async_trait::async_trait; use async_trait::async_trait;
use http;
use http::header; use http::header;
use reqwest::Response; use reqwest::Response;
@ -68,7 +69,6 @@ mod private {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use mockito::mock;
use reqwest_lib as reqwest; use reqwest_lib as reqwest;
use std::vec; use std::vec;
@ -77,7 +77,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_response() { async fn test_response() {
let url = mockito::server_url(); let url = mockito::server_url();
let _m = mock("GET", "/") let _m = mockito::mock("GET", "/")
.with_status(200) .with_status(200)
.with_header("ce-specversion", "1.0") .with_header("ce-specversion", "1.0")
.with_header("ce-id", "0001") .with_header("ce-id", "0001")
@ -104,7 +104,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_response_with_full_data() { async fn test_response_with_full_data() {
let url = mockito::server_url(); let url = mockito::server_url();
let _m = mock("GET", "/") let _m = mockito::mock("GET", "/")
.with_status(200) .with_status(200)
.with_header("ce-specversion", "1.0") .with_header("ce-specversion", "1.0")
.with_header("ce-id", "0001") .with_header("ce-id", "0001")
@ -139,7 +139,7 @@ mod tests {
let expected = fixtures::v10::full_json_data_string_extension(); let expected = fixtures::v10::full_json_data_string_extension();
let url = mockito::server_url(); let url = mockito::server_url();
let _m = mock("GET", "/") let _m = mockito::mock("GET", "/")
.with_status(200) .with_status(200)
.with_header( .with_header(
"content-type", "content-type",
@ -166,7 +166,7 @@ mod tests {
let expected = vec![fixtures::v10::full_json_data_string_extension()]; let expected = vec![fixtures::v10::full_json_data_string_extension()];
let url = mockito::server_url(); let url = mockito::server_url();
let _m = mock("GET", "/") let _m = mockito::mock("GET", "/")
.with_status(200) .with_status(200)
.with_header( .with_header(
"content-type", "content-type",

View File

@ -1,6 +1,6 @@
use warp_lib as warp; use warp_lib as warp;
use crate::binding::http; use crate::binding::http_0_2 as http;
use crate::Event; use crate::Event;
use warp::http::HeaderMap; use warp::http::HeaderMap;

View File

@ -24,6 +24,7 @@
//! //!
//! ``` //! ```
//! # use warp_lib as warp; //! # use warp_lib as warp;
//! # use http_0_2 as http;
//! use cloudevents::{Event, EventBuilder, EventBuilderV10}; //! use cloudevents::{Event, EventBuilder, EventBuilderV10};
//! use http::StatusCode; //! use http::StatusCode;
//! use serde_json::json; //! use serde_json::json;

View File

@ -1,11 +1,12 @@
use warp_lib as warp; use warp_lib as warp;
use crate::binding::http::builder::adapter::to_response; use crate::binding::http_0_2::builder::adapter::to_response;
use crate::Event; use crate::Event;
use http::StatusCode; use http::StatusCode;
use http_0_2 as http;
use hyper_0_14 as hyper;
use warp::reply::Response; use warp::reply::Response;
/// ///
/// # Serializes [`crate::Event`] as a http response /// # Serializes [`crate::Event`] as a http response
/// ///
@ -32,6 +33,7 @@ pub fn from_event(event: Event) -> Response {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::test::fixtures; use crate::test::fixtures;
use hyper_0_14 as hyper;
#[test] #[test]
fn test_response() { fn test_response() {

View File

@ -3,6 +3,7 @@ use super::{
EventFormatSerializerV03, EventFormatSerializerV10, EventFormatSerializerV03, EventFormatSerializerV10,
}; };
use crate::event::{AttributesReader, ExtensionValue}; use crate::event::{AttributesReader, ExtensionValue};
use base64::prelude::*;
use serde::de::{Error, IntoDeserializer}; use serde::de::{Error, IntoDeserializer};
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::{Map, Value}; use serde_json::{Map, Value};
@ -58,7 +59,9 @@ pub fn parse_data_string<E: serde::de::Error>(v: Value) -> Result<String, E> {
pub fn parse_data_base64<E: serde::de::Error>(v: Value) -> Result<Vec<u8>, E> { pub fn parse_data_base64<E: serde::de::Error>(v: Value) -> Result<Vec<u8>, E> {
parse_field!(v, String, E).and_then(|s| { parse_field!(v, String, E).and_then(|s| {
base64::decode(s).map_err(|e| E::custom(format_args!("decode error `{}`", e))) BASE64_STANDARD
.decode(s)
.map_err(|e| E::custom(format_args!("decode error `{}`", e)))
}) })
} }

View File

@ -84,26 +84,29 @@ pub struct Event {
#[delegate(self.attributes)] #[delegate(self.attributes)]
impl AttributesReader for Event { impl AttributesReader for Event {
fn id(&self) -> &str; fn id(&self) -> &str {}
fn source(&self) -> &UriReference; fn source(&self) -> &UriReference {}
fn specversion(&self) -> SpecVersion; fn specversion(&self) -> SpecVersion {}
fn ty(&self) -> &str; fn ty(&self) -> &str {}
fn datacontenttype(&self) -> Option<&str>; fn datacontenttype(&self) -> Option<&str> {}
fn dataschema(&self) -> Option<&Url>; fn dataschema(&self) -> Option<&Url> {}
fn subject(&self) -> Option<&str>; fn subject(&self) -> Option<&str> {}
fn time(&self) -> Option<&DateTime<Utc>>; fn time(&self) -> Option<&DateTime<Utc>> {}
} }
#[delegate(self.attributes)] #[delegate(self.attributes)]
impl AttributesWriter for Event { impl AttributesWriter for Event {
fn set_id(&mut self, id: impl Into<String>) -> String; fn set_id(&mut self, id: impl Into<String>) -> String {}
fn set_source(&mut self, source: impl Into<UriReference>) -> UriReference; fn set_source(&mut self, source: impl Into<UriReference>) -> UriReference {}
fn set_type(&mut self, ty: impl Into<String>) -> String; fn set_type(&mut self, ty: impl Into<String>) -> String {}
fn set_subject(&mut self, subject: Option<impl Into<String>>) -> Option<String>; fn set_subject(&mut self, subject: Option<impl Into<String>>) -> Option<String> {}
fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>) -> Option<DateTime<Utc>>; fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>) -> Option<DateTime<Utc>> {}
fn set_datacontenttype(&mut self, datacontenttype: Option<impl Into<String>>) fn set_datacontenttype(
-> Option<String>; &mut self,
fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) -> Option<Url>; datacontenttype: Option<impl Into<String>>,
) -> Option<String> {
}
fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) -> Option<Url> {}
} }
impl Default for Event { impl Default for Event {

View File

@ -221,7 +221,7 @@ impl crate::event::message::AttributesDeserializer for super::Attributes {
mod tests { mod tests {
use super::*; use super::*;
use crate::test::fixtures; use crate::test::fixtures;
use chrono::NaiveDateTime; use chrono::DateTime;
#[test] #[test]
fn iter_v03_test() { fn iter_v03_test() {
@ -243,13 +243,10 @@ mod tests {
datacontenttype: None, datacontenttype: None,
schemaurl: None, schemaurl: None,
subject: None, subject: None,
time: Some(DateTime::<Utc>::from_utc( time: DateTime::from_timestamp(61, 0),
NaiveDateTime::from_timestamp(61, 0),
Utc,
)),
}; };
let b = &mut a.into_iter(); let b = &mut a.into_iter();
let time = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc); let time = DateTime::from_timestamp(61, 0).unwrap();
assert_eq!( assert_eq!(
("specversion", AttributeValue::SpecVersion(SpecVersion::V03)), ("specversion", AttributeValue::SpecVersion(SpecVersion::V03)),

View File

@ -4,6 +4,7 @@ use crate::event::format::{
parse_data_base64, parse_data_base64_json, parse_data_json, parse_data_string, parse_data_base64, parse_data_base64_json, parse_data_json, parse_data_string,
}; };
use crate::event::{Data, ExtensionValue}; use crate::event::{Data, ExtensionValue};
use base64::prelude::*;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::de::IntoDeserializer; use serde::de::IntoDeserializer;
use serde::ser::SerializeMap; use serde::ser::SerializeMap;
@ -102,7 +103,7 @@ impl<S: serde::Serializer> crate::event::format::EventFormatSerializer<S, Attrib
Some(Data::Json(j)) => state.serialize_entry("data", j)?, Some(Data::Json(j)) => state.serialize_entry("data", j)?,
Some(Data::String(s)) => state.serialize_entry("data", s)?, Some(Data::String(s)) => state.serialize_entry("data", s)?,
Some(Data::Binary(v)) => { Some(Data::Binary(v)) => {
state.serialize_entry("data", &base64::encode(v))?; state.serialize_entry("data", &BASE64_STANDARD.encode(v))?;
state.serialize_entry("datacontentencoding", "base64")?; state.serialize_entry("datacontentencoding", "base64")?;
} }
_ => (), _ => (),

View File

@ -222,7 +222,6 @@ impl AttributesConverter for Attributes {
mod tests { mod tests {
use super::*; use super::*;
use crate::test::fixtures; use crate::test::fixtures;
use chrono::NaiveDateTime;
#[test] #[test]
fn iter_v10_test() { fn iter_v10_test() {
@ -244,13 +243,10 @@ mod tests {
datacontenttype: None, datacontenttype: None,
dataschema: None, dataschema: None,
subject: None, subject: None,
time: Some(DateTime::<Utc>::from_utc( time: DateTime::from_timestamp(61, 0),
NaiveDateTime::from_timestamp(61, 0),
Utc,
)),
}; };
let b = &mut a.into_iter(); let b = &mut a.into_iter();
let time = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc); let time = DateTime::from_timestamp(61, 0).unwrap();
assert_eq!( assert_eq!(
("specversion", AttributeValue::SpecVersion(SpecVersion::V10)), ("specversion", AttributeValue::SpecVersion(SpecVersion::V10)),

View File

@ -4,6 +4,7 @@ use crate::event::format::{
parse_data_base64, parse_data_base64_json, parse_data_json, parse_data_string, parse_data_base64, parse_data_base64_json, parse_data_json, parse_data_string,
}; };
use crate::event::{Data, ExtensionValue}; use crate::event::{Data, ExtensionValue};
use base64::prelude::*;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::de::IntoDeserializer; use serde::de::IntoDeserializer;
use serde::ser::SerializeMap; use serde::ser::SerializeMap;
@ -102,7 +103,9 @@ impl<S: serde::Serializer> crate::event::format::EventFormatSerializer<S, Attrib
match data { match data {
Some(Data::Json(j)) => state.serialize_entry("data", j)?, Some(Data::Json(j)) => state.serialize_entry("data", j)?,
Some(Data::String(s)) => state.serialize_entry("data", s)?, Some(Data::String(s)) => state.serialize_entry("data", s)?,
Some(Data::Binary(v)) => state.serialize_entry("data_base64", &base64::encode(v))?, Some(Data::Binary(v)) => {
state.serialize_entry("data_base64", &BASE64_STANDARD.encode(v))?
}
_ => (), _ => (),
}; };
for (k, v) in extensions { for (k, v) in extensions {

View File

@ -54,7 +54,7 @@
//! [Extractors]: https://actix.rs/docs/extractors/ //! [Extractors]: https://actix.rs/docs/extractors/
//! [Responders]: https://actix.rs/docs/handlers/ //! [Responders]: https://actix.rs/docs/handlers/
#![doc(html_root_url = "https://docs.rs/cloudevents-sdk/0.7.0")] #![doc(html_root_url = "https://docs.rs/cloudevents-sdk/0.8.0")]
#![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))] // Show feature gate in doc #![cfg_attr(docsrs, feature(doc_cfg))] // Show feature gate in doc

View File

@ -1,4 +1,5 @@
use crate::event::{ExtensionValue, UriReference}; use crate::event::{ExtensionValue, UriReference};
use base64::prelude::*;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt; use std::fmt;
@ -46,10 +47,14 @@ impl fmt::Display for MessageAttributeValue {
MessageAttributeValue::Boolean(b) => write!(f, "{}", b), MessageAttributeValue::Boolean(b) => write!(f, "{}", b),
MessageAttributeValue::Integer(i) => write!(f, "{}", i), MessageAttributeValue::Integer(i) => write!(f, "{}", i),
MessageAttributeValue::String(s) => write!(f, "{}", s), MessageAttributeValue::String(s) => write!(f, "{}", s),
MessageAttributeValue::Binary(v) => write!(f, "{}", base64::encode(v)), MessageAttributeValue::Binary(v) => {
write!(f, "{}", BASE64_STANDARD.encode(v))
}
MessageAttributeValue::Uri(u) => write!(f, "{}", u), MessageAttributeValue::Uri(u) => write!(f, "{}", u),
MessageAttributeValue::UriRef(u) => write!(f, "{}", u), MessageAttributeValue::UriRef(u) => write!(f, "{}", u),
MessageAttributeValue::DateTime(d) => write!(f, "{}", d.to_rfc3339()), MessageAttributeValue::DateTime(d) => {
write!(f, "{}", d.to_rfc3339())
}
} }
} }
} }

View File

@ -45,7 +45,7 @@ pub fn subject() -> String {
} }
pub fn time() -> DateTime<Utc> { pub fn time() -> DateTime<Utc> {
Utc.ymd(2020, 3, 16).and_hms(11, 50, 00) Utc.with_ymd_and_hms(2020, 3, 16, 11, 50, 00).unwrap()
} }
pub fn string_extension() -> (String, String) { pub fn string_extension() -> (String, String) {

View File

@ -1,5 +1,6 @@
use crate::test::fixtures::*; use crate::test::fixtures::*;
use crate::{Event, EventBuilder, EventBuilderV03}; use crate::{Event, EventBuilder, EventBuilderV03};
use base64::prelude::*;
use serde_json::{json, Value}; use serde_json::{json, Value};
use url::Url; use url::Url;
@ -120,7 +121,7 @@ pub fn full_json_base64_data_json() -> Value {
"datacontenttype": json_datacontenttype(), "datacontenttype": json_datacontenttype(),
"schemaurl": dataschema(), "schemaurl": dataschema(),
"datacontentencoding": "base64", "datacontentencoding": "base64",
"data": base64::encode(json_data_binary()) "data": BASE64_STANDARD.encode(json_data_binary())
}) })
} }
@ -199,6 +200,6 @@ pub fn full_xml_base64_data_json() -> Value {
int_ext_name: int_ext_value, int_ext_name: int_ext_value,
"datacontenttype": xml_datacontenttype(), "datacontenttype": xml_datacontenttype(),
"datacontentencoding": "base64", "datacontentencoding": "base64",
"data": base64::encode(Vec::from(xml_data())) "data": BASE64_STANDARD.encode(Vec::from(xml_data()))
}) })
} }

View File

@ -1,5 +1,6 @@
use crate::test::fixtures::*; use crate::test::fixtures::*;
use crate::{Event, EventBuilder, EventBuilderV10}; use crate::{Event, EventBuilder, EventBuilderV10};
use base64::prelude::*;
use serde_json::{json, Value}; use serde_json::{json, Value};
use url::Url; use url::Url;
@ -166,7 +167,7 @@ pub fn full_json_base64_data_json() -> Value {
int_ext_name: int_ext_value, int_ext_name: int_ext_value,
"datacontenttype": json_datacontenttype(), "datacontenttype": json_datacontenttype(),
"dataschema": dataschema(), "dataschema": dataschema(),
"data_base64": base64::encode(json_data_binary()) "data_base64": BASE64_STANDARD.encode(json_data_binary())
}) })
} }
@ -175,7 +176,7 @@ pub fn full_non_json_base64_data() -> Value {
Value::Object(mut m) => { Value::Object(mut m) => {
m.insert( m.insert(
"data_base64".to_string(), "data_base64".to_string(),
Value::String(base64::encode(b"hello world")), Value::String(BASE64_STANDARD.encode(b"hello world")),
); );
Value::Object(m) Value::Object(m)
} }
@ -187,7 +188,11 @@ pub fn full_non_json_data() -> Event {
let mut event = full_json_data(); let mut event = full_json_data();
let value = full_non_json_base64_data(); let value = full_non_json_base64_data();
if let Value::Object(m) = value { if let Value::Object(m) = value {
event.set_data_unchecked(base64::decode(m["data_base64"].as_str().unwrap()).unwrap()); event.set_data_unchecked(
BASE64_STANDARD
.decode(m["data_base64"].as_str().unwrap())
.unwrap(),
);
} }
event event
} }
@ -266,6 +271,6 @@ pub fn full_xml_base64_data_json() -> Value {
bool_ext_name: bool_ext_value, bool_ext_name: bool_ext_value,
int_ext_name: int_ext_value, int_ext_name: int_ext_value,
"datacontenttype": xml_datacontenttype(), "datacontenttype": xml_datacontenttype(),
"data_base64": base64::encode(Vec::from(xml_data())) "data_base64": BASE64_STANDARD.encode(Vec::from(xml_data()))
}) })
} }