Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Replace serde_json with sonic_rs for poem crate #819

Merged
merged 7 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ quote = "1.0.9"
syn = { version = "2.0" }
tokio = "1.17.0"
serde_json = "1.0.68"
sonic-rs = "0.3.5"
serde = { version = "1.0.130", features = ["derive"] }
thiserror = "1.0.30"
regex = "1.5.5"
Expand Down
1 change: 1 addition & 0 deletions poem-openapi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ hostname = ["hostname-validator"]
static-files = ["poem/static-files"]
websocket = ["poem/websocket"]
geo = ["dep:geo-types", "dep:geojson"]
sonic-rs = ["poem/sonic-rs"]

[dependencies]
poem-openapi-derive.workspace = true
Expand Down
1 change: 1 addition & 0 deletions poem-openapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ To avoid compiling unused dependencies, Poem gates certain features, some of whi
| prost-wkt-types | Integrate with the [`prost-wkt-types` crate](https://crates.io/crates/prost-wkt-types) |
| static-files | Support for static file response |
| websocket | Support for websocket |
|sonic-rs | Uses [`sonic-rs`](https://github.com/cloudwego/sonic-rs) instead of `serde_json`. Pls, checkout `sonic-rs` requirements to properly enable `sonic-rs` capabilities |

## Safety

Expand Down
1 change: 1 addition & 0 deletions poem-openapi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
//! | prost-wkt-types | Integrate with the [`prost-wkt-types` crate](https://crates.io/crates/prost-wkt-types) |
//! | static-files | Support for static file response |
//! | websocket | Support for websocket |
//! |sonic-rs | Uses [`sonic-rs`](https://github.com/cloudwego/sonic-rs) instead of `serde_json`. Pls, checkout `sonic-rs` requirements to properly enable `sonic-rs` capabilities |

#![doc(html_favicon_url = "https://raw.githubusercontent.com/poem-web/poem/master/favicon.ico")]
#![doc(html_logo_url = "https://raw.githubusercontent.com/poem-web/poem/master/logo.png")]
Expand Down
2 changes: 2 additions & 0 deletions poem/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ embed = ["rust-embed", "hex", "mime_guess"]
xml = ["quick-xml"]
yaml = ["serde_yaml"]
requestid = ["dep:uuid"]
sonic-rs = ["dep:sonic-rs"]

[dependencies]
poem-derive.workspace = true
Expand All @@ -80,6 +81,7 @@ http-body-util = "0.1.0"
tokio = { workspace = true, features = ["sync", "time", "macros", "net"] }
tokio-util = { version = "0.7.0", features = ["io"] }
serde.workspace = true
sonic-rs = { workspace = true, optional = true }
serde_json.workspace = true
serde_urlencoded.workspace = true
parking_lot = "0.12.0"
Expand Down
2 changes: 1 addition & 1 deletion poem/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ which are disabled by default:
| xml | Integrate with [`quick-xml`](https://crates.io/crates/quick-xml) crate. |
| yaml | Integrate with [`serde-yaml`](https://crates.io/crates/serde-yaml) crate. |
|requestid |Associates an unique ID with each incoming request |

|sonic-rs | Uses [`sonic-rs`](https://github.com/cloudwego/sonic-rs) instead of `serde_json`. Pls, checkout `sonic-rs` requirements to properly enable `sonic-rs` capabilities |
## Safety

This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% Safe Rust.
Expand Down
15 changes: 14 additions & 1 deletion poem/src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,16 @@ impl Body {
}

/// Create a body object from JSON.
#[cfg(not(feature = "sonic-rc"))]
pub fn from_json(body: impl Serialize) -> serde_json::Result<Self> {
Ok(serde_json::to_vec(&body)?.into())
}

#[cfg(feature = "sonic-rc")]
pub fn from_json(body: impl Serialize) -> sonic_rs::Result<Self> {
Ok(sonic_rs::to_vec(&body)?.into())
}

/// Create an empty body.
#[inline]
pub fn empty() -> Self {
Expand Down Expand Up @@ -234,7 +240,14 @@ impl Body {
/// - [`ReadBodyError`]
/// - [`ParseJsonError`]
pub async fn into_json<T: DeserializeOwned>(self) -> Result<T> {
Ok(serde_json::from_slice(&self.into_vec().await?).map_err(ParseJsonError::Parse)?)
#[cfg(not(feature = "sonic-rs"))]
{
Ok(serde_json::from_slice(&self.into_vec().await?).map_err(ParseJsonError::Parse)?)
}
#[cfg(feature = "sonic-rs")]
{
Ok(sonic_rs::from_slice(&self.into_vec().await?).map_err(ParseJsonError::Parse)?)
}
}

/// Consumes this body object and parse it as `T`.
Expand Down
12 changes: 12 additions & 0 deletions poem/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,13 @@ pub enum ParseCookieError {

/// Cookie value is illegal.
#[error("cookie is illegal: {0}")]
#[cfg(not(feature = "sonic-rs"))]
ParseJsonValue(#[from] serde_json::Error),

/// Cookie value is illegal.
#[error("cookie is illegal: {0}")]
#[cfg(feature = "sonic-rs")]
ParseJsonValue(#[from] sonic_rs::Error),
}

#[cfg(feature = "cookie")]
Expand Down Expand Up @@ -749,7 +755,13 @@ pub enum ParseJsonError {

/// Url decode error.
#[error("parse error: {0}")]
#[cfg(not(feature = "sonic-rs"))]
Parse(#[from] serde_json::Error),

/// Url decode error.
#[error("parse error: {0}")]
#[cfg(feature = "sonic-rs")]
Parse(#[from] sonic_rs::Error),
}

impl ResponseError for ParseJsonError {
Expand Down
1 change: 1 addition & 0 deletions poem/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@
//! | embed | Integrate with [`rust-embed`](https://crates.io/crates/rust-embed) crate. |
//! | xml | Integrate with [`quick-xml`](https://crates.io/crates/quick-xml) crate. |
//! | yaml | Integrate with [`serde-yaml`](https://crates.io/crates/serde-yaml) crate. |
//! |sonic-rs | Uses [`sonic-rs`](https://github.com/cloudwego/sonic-rs) instead of `serde_json`. Pls, checkout `sonic-rs` requirements to properly enable `sonic-rs` capabilities |

#![doc(html_favicon_url = "https://raw.githubusercontent.com/poem-web/poem/master/favicon.ico")]
#![doc(html_logo_url = "https://raw.githubusercontent.com/poem-web/poem/master/logo.png")]
Expand Down
36 changes: 31 additions & 5 deletions poem/src/listener/acme/jose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ impl<'a> Protected<'a> {
nonce,
url,
};
#[cfg(not(feature = "sonic-rs"))]
let protected = serde_json::to_vec(&protected).map_err(|err| {
IoError::new(ErrorKind::Other, format!("failed to encode jwt: {err}"))
})?;
#[cfg(feature = "sonic-rs")]
let protected = sonic_rs::to_vec(&protected).map_err(|err| {
IoError::new(ErrorKind::Other, format!("failed to encode jwt: {err}"))
})?;
Ok(URL_SAFE_NO_PAD.encode(protected))
}
}
Expand Down Expand Up @@ -78,9 +83,14 @@ impl Jwk {
x: &self.x,
y: &self.y,
};
#[cfg(not(feature = "sonic-rs"))]
let json = serde_json::to_vec(&jwk_thumb).map_err(|err| {
IoError::new(ErrorKind::Other, format!("failed to encode jwt: {err}"))
})?;
#[cfg(feature = "sonic-rs")]
let json = sonic_rs::to_vec(&jwk_thumb).map_err(|err| {
IoError::new(ErrorKind::Other, format!("failed to encode jwt: {err}"))
})?;
let hash = sha256(json);
Ok(URL_SAFE_NO_PAD.encode(hash))
}
Expand Down Expand Up @@ -111,9 +121,17 @@ pub(crate) async fn request(
};
let protected = Protected::base64(jwk, kid, nonce, uri)?;
let payload = match payload {
Some(payload) => serde_json::to_vec(&payload).map_err(|err| {
IoError::new(ErrorKind::Other, format!("failed to encode payload: {err}"))
})?,
Some(payload) => {
#[cfg(not(feature = "sonic-rs"))]
let res = serde_json::to_vec(&payload).map_err(|err| {
IoError::new(ErrorKind::Other, format!("failed to encode payload: {err}"))
})?;
#[cfg(feature = "sonic-rs")]
let res = sonic_rs::to_vec(&payload).map_err(|err| {
IoError::new(ErrorKind::Other, format!("failed to encode payload: {err}"))
})?;
res
}
None => Vec::new(),
};
let payload = URL_SAFE_NO_PAD.encode(payload);
Expand Down Expand Up @@ -166,8 +184,16 @@ where
.text()
.await
.map_err(|_| IoError::new(ErrorKind::Other, "failed to read response"))?;
serde_json::from_str(&data)
.map_err(|err| IoError::new(ErrorKind::Other, format!("bad response: {err}")))
#[cfg(not(feature = "sonic-rs"))]
{
serde_json::from_str(&data)
.map_err(|err| IoError::new(ErrorKind::Other, format!("bad response: {err}")))
}
#[cfg(feature = "sonic-rs")]
{
sonic_rs::from_str(&data)
.map_err(|err| IoError::new(ErrorKind::Other, format!("bad response: {err}")))
}
}

pub(crate) fn key_authorization(key: &KeyPair, token: &str) -> IoResult<String> {
Expand Down
15 changes: 12 additions & 3 deletions poem/src/middleware/tokio_metrics_mw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,18 @@ impl TokioMetrics {
pub fn exporter(&self) -> impl Endpoint {
let metrics = self.metrics.clone();
RouteMethod::new().get(make_sync(move |_| {
serde_json::to_string(&*metrics.lock())
.unwrap()
.with_content_type("application/json")
#[cfg(not(feature = "sonic-rs"))]
{
serde_json::to_string(&*metrics.lock())
.unwrap()
.with_content_type("application/json")
}
#[cfg(feature = "sonic-rs")]
{
sonic_rs::to_string(&*metrics.lock())
.unwrap()
.with_content_type("application/json")
}
}))
}
}
Expand Down
25 changes: 20 additions & 5 deletions poem/src/session/cookie_session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,16 @@ impl<E: Endpoint> Endpoint for CookieSessionEndpoint<E> {
let session = self
.config
.get_cookie_value(&cookie_jar)
.and_then(|value| serde_json::from_str::<BTreeMap<String, Value>>(&value).ok())
.and_then(|value| {
#[cfg(not(feature = "sonic-rs"))]
{
serde_json::from_str::<BTreeMap<String, Value>>(&value).ok()
}
#[cfg(feature = "sonic-rs")]
{
sonic_rs::from_str::<BTreeMap<String, Value>>(&value).ok()
}
})
.map(Session::new)
.unwrap_or_default();

Expand All @@ -59,10 +68,16 @@ impl<E: Endpoint> Endpoint for CookieSessionEndpoint<E> {

match session.status() {
SessionStatus::Changed | SessionStatus::Renewed => {
self.config.set_cookie_value(
&cookie_jar,
&serde_json::to_string(&session.entries()).unwrap_or_default(),
);
self.config.set_cookie_value(&cookie_jar, {
#[cfg(not(feature = "sonic-rs"))]
{
&serde_json::to_string(&session.entries()).unwrap_or_default()
}
#[cfg(feature = "sonic-rs")]
{
&sonic_rs::to_string(&session.entries()).unwrap_or_default()
}
});
}
SessionStatus::Purged => {
self.config.remove_cookie(&cookie_jar);
Expand Down
17 changes: 13 additions & 4 deletions poem/src/session/redis_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ impl<T: ConnectionLike + Clone + Sync + Send> SessionStorage for RedisStorage<T>
.map_err(RedisSessionError::Redis)?;

match data {
Some(data) => match serde_json::from_str::<BTreeMap<String, Value>>(&data) {
Ok(entries) => Ok(Some(entries)),
Err(_) => Ok(None),
},
Some(data) => {
#[cfg(not(feature = "sonic-rs"))]
let map = serde_json::from_str::<BTreeMap<String, Value>>(&data);
#[cfg(feature = "sonic-rs")]
let map = sonic_rs::from_str::<BTreeMap<String, Value>>(&data);
match map {
Ok(entries) => Ok(Some(entries)),
Err(_) => Ok(None),
}
}
None => Ok(None),
}
}
Expand All @@ -47,7 +53,10 @@ impl<T: ConnectionLike + Clone + Sync + Send> SessionStorage for RedisStorage<T>
entries: &'a BTreeMap<String, Value>,
expires: Option<Duration>,
) -> Result<()> {
#[cfg(not(feature = "sonic-rs"))]
let value = serde_json::to_string(entries).unwrap_or_default();
#[cfg(feature = "sonic-rs")]
let value = sonic_rs::to_string(entries).unwrap_or_default();
let cmd = match expires {
Some(expires) => Cmd::set_ex(session_id, value, expires.as_secs()),
None => Cmd::set(session_id, value),
Expand Down
34 changes: 28 additions & 6 deletions poem/src/web/cookie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,20 @@ impl Display for Cookie {
impl Cookie {
/// Creates a new Cookie with the given `name` and serialized `value`.
pub fn new(name: impl Into<String>, value: impl Serialize) -> Self {
Self(libcookie::Cookie::new(
name.into(),
serde_json::to_string(&value).unwrap_or_default(),
))
#[cfg(not(feature = "sonic-rs"))]
{
Self(libcookie::Cookie::new(
name.into(),
serde_json::to_string(&value).unwrap_or_default(),
))
}
#[cfg(feature = "sonic-rs")]
{
Self(libcookie::Cookie::new(
name.into(),
sonic_rs::to_string(&value).unwrap_or_default(),
))
}
}

/// Creates a new Cookie with the given `name` and `value`.
Expand Down Expand Up @@ -275,7 +285,12 @@ impl Cookie {

/// Sets the value of `self` to the serialized `value`.
pub fn set_value(&mut self, value: impl Serialize) {
if let Ok(value) = serde_json::to_string(&value) {
#[cfg(not(feature = "sonic-rs"))]
let json_string = serde_json::to_string(&value);
#[cfg(feature = "sonic-rs")]
let json_string = sonic_rs::to_string(&value);

if let Ok(value) = json_string {
self.0.set_value(value);
}
}
Expand All @@ -287,7 +302,14 @@ impl Cookie {

/// Returns the value of `self` to the deserialized `value`.
pub fn value<'de, T: Deserialize<'de>>(&'de self) -> Result<T, ParseCookieError> {
serde_json::from_str(self.0.value()).map_err(ParseCookieError::ParseJsonValue)
#[cfg(not(feature = "sonic-rs"))]
{
serde_json::from_str(self.0.value()).map_err(ParseCookieError::ParseJsonValue)
}
#[cfg(feature = "sonic-rs")]
{
sonic_rs::from_str(self.0.value()).map_err(ParseCookieError::ParseJsonValue)
}
}
}

Expand Down
Loading
Loading