actix-web: make cbor optional feature, add test

This commit is contained in:
Follpvosten 2022-01-17 16:09:16 +01:00
parent ce36e73639
commit 5898f63dac
3 changed files with 91 additions and 29 deletions

View File

@ -21,11 +21,16 @@ actix-web-actors = "4.0.0-beta.9"
async-channel = "1.6.1"
futures-util = { version = "0.3.0", default-features = false }
serde_json = "1.0.64"
serde_cbor = "0.11"
serde_urlencoded = "0.7.0"
futures-channel = "0.3.13"
thiserror = "1.0.30"
serde_cbor = { version = "0.11.2", optional = true }
[features]
default = []
cbor = ["serde_cbor"]
[dev-dependencies]
actix-rt = "2.2.0"
async-mutex = "1.4.0"
serde = { version = "1", features = ["derive"] }

View File

@ -1,6 +1,5 @@
use actix_http::body::BoxBody;
use actix_web::error::JsonPayloadError;
use core::fmt;
use std::future::Future;
use std::io::{self, ErrorKind};
use std::pin::Pin;
@ -8,9 +7,7 @@ use std::pin::Pin;
use actix_http::error::PayloadError;
use actix_web::dev::Payload;
use actix_web::http::{Method, StatusCode};
use actix_web::{
http, Error, FromRequest, HttpRequest, HttpResponse, Responder, ResponseError, Result,
};
use actix_web::{http, Error, FromRequest, HttpRequest, HttpResponse, Responder, Result};
use futures_util::future::{self, FutureExt};
use futures_util::{StreamExt, TryStreamExt};
@ -157,16 +154,22 @@ impl From<async_graphql::BatchResponse> for GraphQLResponse {
}
}
#[derive(Debug)]
struct CborSerializeError(serde_cbor::Error);
impl fmt::Display for CborSerializeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
#[cfg(feature = "cbor")]
mod cbor {
use actix_web::{http::StatusCode, ResponseError};
use core::fmt;
#[derive(Debug)]
pub struct Error(pub serde_cbor::Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
}
impl ResponseError for CborSerializeError {
fn status_code(&self) -> StatusCode {
StatusCode::INTERNAL_SERVER_ERROR
impl ResponseError for Error {
fn status_code(&self) -> StatusCode {
StatusCode::INTERNAL_SERVER_ERROR
}
}
}
@ -187,20 +190,26 @@ impl Responder for GraphQLResponse {
.headers()
.get(http::header::ACCEPT)
.and_then(|val| val.to_str().ok());
if accept == Some("application/cbor") {
res.content_type("application/cbor");
let body = match serde_cbor::to_vec(&self.0) {
Ok(body) => body,
Err(error) => return HttpResponse::from_error(CborSerializeError(error)),
};
res.append_header((http::header::CONTENT_LENGTH, body.len()));
res.body(body)
} else {
res.content_type("application/json");
res.body(match serde_json::to_vec(&self.0) {
Ok(body) => body,
Err(error) => return HttpResponse::from_error(JsonPayloadError::Serialize(error)),
})
}
let (ct, body) = match accept {
// optional cbor support
#[cfg(feature = "cbor")]
// this avoids copy-pasting the mime type
Some(ct @ "application/cbor") => (
ct,
match serde_cbor::to_vec(&self.0) {
Ok(body) => body,
Err(e) => return HttpResponse::from_error(cbor::Error(e)),
},
),
_ => (
"application/json",
match serde_json::to_vec(&self.0) {
Ok(body) => body,
Err(e) => return HttpResponse::from_error(JsonPayloadError::Serialize(e)),
},
),
};
res.content_type(ct);
res.body(body)
}
}

View File

@ -220,3 +220,51 @@ async fn test_count() {
.into_bytes()
);
}
#[cfg(feature = "cbor")]
#[actix_rt::test]
async fn test_cbor() {
let srv = test::init_service(
App::new()
.app_data(Data::new(Schema::new(
AddQueryRoot,
EmptyMutation,
EmptySubscription,
)))
.service(
web::resource("/")
.guard(guard::Post())
.to(gql_handle_schema::<AddQueryRoot, EmptyMutation, EmptySubscription>),
),
)
.await;
let response = srv
.call(
test::TestRequest::with_uri("/")
.method(Method::POST)
.set_payload(r#"{"query":"{ add(a: 10, b: 20) }"}"#)
.insert_header((actix_http::header::ACCEPT, "application/cbor"))
.to_request(),
)
.await
.unwrap();
assert!(response.status().is_success());
#[derive(Debug, serde::Deserialize, PartialEq)]
struct Response {
data: ResponseInner,
}
#[derive(Debug, serde::Deserialize, PartialEq)]
struct ResponseInner {
add: i32,
}
let body = actix_web::body::to_bytes(response.into_body())
.await
.unwrap();
let response: Response = serde_cbor::from_slice(&body).unwrap();
assert_eq!(
response,
Response {
data: ResponseInner { add: 30 }
}
);
}