Upgrade axum.

Breaks docs.

Signed-off-by: Omar Zabala-Ferrera <73452461+ozabalaferrera@users.noreply.github.com>
This commit is contained in:
Omar Zabala-Ferrera 2024-07-13 23:40:55 -04:00
parent bf45f01602
commit 03021f7242
No known key found for this signature in database
11 changed files with 121 additions and 43 deletions

View File

@ -27,7 +27,7 @@ actix = ["actix-web", "actix-http", "async-trait", "bytes", "futures", "http"]
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", "hyper"]
axum = ["bytes", "http", "hyper", "axum-lib", "http-body", "async-trait"] axum = ["bytes", "http-1-1", "hyper-1-3", "axum-lib-0-7", "http-body-1-0", "http-body-util", "async-trait"]
poem = ["bytes", "http", "poem-lib", "hyper", "async-trait"] poem = ["bytes", "http", "poem-lib", "hyper", "async-trait"]
nats = ["nats-lib"] nats = ["nats-lib"]
@ -50,10 +50,15 @@ rdkafka-lib = { version = "^0.36", features = ["cmake-build"], optional = true,
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.33", optional = true }
bytes = { version = "^1.0", optional = true } bytes = { version = "^1.0", optional = true }
# bytes-1-6 = { version = "^1.6", optional = true, package="bytes" }
futures = { version = "^0.3", optional = true } futures = { version = "^0.3", optional = true }
http = { version = "0.2", optional = true } http = { version = "0.2", optional = true }
http-1-1 = { version = "1.1", optional = true, package = "http"}
axum-lib = { version = "^0.6", optional = true, package="axum"} axum-lib = { version = "^0.6", optional = true, package="axum"}
axum-lib-0-7 = { version = "^0.7", optional = true, package="axum"}
http-body = { version = "^0.4", optional = true } http-body = { version = "^0.4", optional = true }
http-body-1-0 = { version = "^1.0", optional = true, package="http-body" }
http-body-util = {version = "^0.1", optional = true}
poem-lib = { version = "=1.2.34", optional = true, package = "poem" } poem-lib = { version = "=1.2.34", optional = true, package = "poem" }
nats-lib = { version = "0.21.0", optional = true, package = "nats" } nats-lib = { version = "0.21.0", optional = true, package = "nats" }
@ -65,6 +70,7 @@ 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 = "^0.14", optional = true }
hyper-1-3 = { version = "^1.3", 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 }

View File

@ -1,45 +1,52 @@
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_0_7 as axum;
use http::StatusCode; use http::StatusCode;
use http_body::Body; use http_1_1 as http;
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(
let (Parts { headers, .. }, req_body) = req.into_parts(); req: Request,
let buf = body::to_bytes(req_body) _state: &S,
.await ) -> Result<Self, Self::Rejection> {
.map_err(|e| (StatusCode::BAD_REQUEST, format!("{}", e.into())))? let (parts, body) = req.into_parts();
.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 axum_lib_0_7 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 +87,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

@ -6,13 +6,14 @@
//! To echo events: //! To echo events:
//! //!
//! ``` //! ```
//! use axum_lib as axum; //! # use axum_lib_0_7 as axum;
//! use axum::{ //! use axum::{
//! routing::{get, post}, //! routing::{get, post},
//! Router, //! Router,
//! }; //! };
//! use cloudevents::Event; //! use cloudevents::Event;
//! use http::StatusCode; //! use http::StatusCode;
//! # use http_1_1 as http;
//! //!
//! fn app() -> Router { //! fn app() -> Router {
//! Router::new() //! Router::new()
@ -31,13 +32,14 @@
//! To create event inside request handlers and send them as responses: //! To create event inside request handlers and send them as responses:
//! //!
//! ``` //! ```
//! use axum_lib as axum; //! # use axum_lib_0_7 as axum;
//! use axum::{ //! use axum::{
//! routing::{get, post}, //! routing::{get, post},
//! Router, //! Router,
//! }; //! };
//! use cloudevents::{Event, EventBuilder, EventBuilderV10}; //! use cloudevents::{Event, EventBuilder, EventBuilderV10};
//! use http::StatusCode; //! use http::StatusCode;
//! # use http_1_1 as http;
//! use serde_json::json; //! use serde_json::json;
//! //!
//! fn app() -> Router { //! fn app() -> Router {
@ -77,7 +79,7 @@ pub mod response;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use axum_lib as axum; use axum_lib_0_7 as axum;
use axum::{ use axum::{
body::Body, body::Body,
@ -155,7 +157,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_0_7 as axum;
use http::{header, StatusCode};
use http_1_1 as http;
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,15 +1,27 @@
#[cfg(feature = "axum")]
use bytes::Bytes;
use http::Response; use http::Response;
#[cfg(feature = "axum")]
use http_1_1 as http;
#[cfg(feature = "axum")]
use http_body_util::Full;
#[cfg(not(feature = "axum"))]
use hyper::body::Body; use hyper::body::Body;
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;
#[cfg(feature = "axum")]
use std::convert::Infallible;
#[cfg(feature = "axum")]
type BoxBody = http_body_util::combinators::UnsyncBoxBody<Bytes, Infallible>;
struct Adapter { struct Adapter {
builder: Cell<http::response::Builder>, builder: Cell<http::response::Builder>,
} }
#[cfg(not(feature = "axum"))]
impl Builder<Response<Body>> for Adapter { impl Builder<Response<Body>> 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));
@ -27,6 +39,27 @@ impl Builder<Response<Body>> for Adapter {
} }
} }
#[cfg(feature = "axum")]
impl Builder<Response<BoxBody>> 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<BoxBody>> {
self.builder
.take()
.body(BoxBody::new(Full::from(bytes)))
.map_err(|e| crate::message::Error::Other {
source: Box::new(e),
})
}
fn finish(&mut self) -> Result<Response<BoxBody>> {
self.body(Vec::new())
}
}
#[cfg(not(feature = "axum"))]
pub fn to_response(event: Event) -> std::result::Result<Response<Body>, Error> { pub fn to_response(event: Event) -> std::result::Result<Response<Body>, Error> {
BinaryDeserializer::deserialize_binary( BinaryDeserializer::deserialize_binary(
event, event,
@ -35,3 +68,13 @@ pub fn to_response(event: Event) -> std::result::Result<Response<Body>, Error> {
}), }),
) )
} }
#[cfg(feature = "axum")]
pub fn to_response(event: Event) -> std::result::Result<Response<BoxBody>, Error> {
BinaryDeserializer::deserialize_binary(
event,
Serializer::new(Adapter {
builder: Cell::new(http::Response::builder()),
}),
)
}

View File

@ -1,7 +1,9 @@
#[cfg(feature = "hyper")] #[cfg(any(feature = "hyper", feature = "hyper-1-3"))]
pub mod adapter; pub mod adapter;
use crate::message::Result; use crate::message::Result;
#[cfg(feature = "axum")]
use http_1_1 as 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);

View File

@ -8,6 +8,8 @@ use crate::{
Result, StructuredDeserializer, StructuredSerializer, Result, StructuredDeserializer, StructuredSerializer,
}, },
}; };
#[cfg(feature = "axum")]
use http_1_1 as 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,4 +1,6 @@
use http::header::{AsHeaderName, HeaderMap, HeaderName, HeaderValue}; use http::header::{AsHeaderName, HeaderMap, HeaderName, HeaderValue};
#[cfg(feature = "axum")]
use http_1_1 as 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

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;
#[cfg(feature = "axum")]
use http_1_1 as 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;
@ -52,6 +54,8 @@ mod tests {
use crate::Event; use crate::Event;
use core::convert::TryFrom; use core::convert::TryFrom;
use http::Response; use http::Response;
#[cfg(feature = "axum")]
use http_1_1 as http;
#[test] #[test]
fn test_response_to_event() { fn test_response_to_event() {

View File

@ -12,6 +12,8 @@ use crate::message::{
}; };
use crate::Event; use crate::Event;
use http::Request; use http::Request;
#[cfg(feature = "axum")]
use http_1_1 as 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;
#[cfg(feature = "axum")]
use http_1_1 as http;
use std::convert::TryFrom; use std::convert::TryFrom;
#[test] #[test]

View File

@ -2,6 +2,18 @@
//! //!
//! Note: these APIs should be considered unstable and subject to changes. //! Note: these APIs should be considered unstable and subject to changes.
#[cfg(all(
feature = "axum",
any(
feature = "http-binding",
feature = "actix",
feature = "reqwest",
feature = "warp",
feature = "poem"
)
))]
compile_error!("feature `axum` cannot be used with features `http-binding`, `actix`, `reqwest`, `warp`, or `poem`");
mod deserializer; mod deserializer;
mod encoding; mod encoding;
mod error; mod error;