api: Replace bytes::Buf by AsRef<u8> for reading
This allows us to switch back to serde_json::from_slice instead of serde_json::from_reader, because the latter is significantly slower. See https://github.com/serde-rs/json/issues/160
This commit is contained in:
parent
e4ae2a40ee
commit
c1693569f1
@ -10,7 +10,6 @@ impl Request {
|
||||
error_ty: &TokenStream,
|
||||
ruma_api: &TokenStream,
|
||||
) -> TokenStream {
|
||||
let bytes = quote! { #ruma_api::exports::bytes };
|
||||
let http = quote! { #ruma_api::exports::http };
|
||||
let percent_encoding = quote! { #ruma_api::exports::percent_encoding };
|
||||
let ruma_serde = quote! { #ruma_api::exports::ruma_serde };
|
||||
@ -159,15 +158,17 @@ impl Request {
|
||||
RequestBody #body_lifetimes
|
||||
as #ruma_serde::Outgoing
|
||||
>::Incoming = {
|
||||
let body = request.into_body();
|
||||
if #bytes::Buf::has_remaining(&body) {
|
||||
#serde_json::from_reader(#bytes::Buf::reader(body))?
|
||||
} else {
|
||||
let body = ::std::convert::AsRef::<[::std::primitive::u8]>::as_ref(
|
||||
request.body(),
|
||||
);
|
||||
|
||||
#serde_json::from_slice(match body {
|
||||
// If the request body is completely empty, pretend it is an empty JSON
|
||||
// object instead. This allows requests with only optional body parameters
|
||||
// to be deserialized in that case.
|
||||
#serde_json::from_str("{}")?
|
||||
}
|
||||
[] => b"{}",
|
||||
b => b,
|
||||
})?
|
||||
};
|
||||
}
|
||||
} else {
|
||||
@ -184,13 +185,8 @@ impl Request {
|
||||
} else if let Some(field) = self.newtype_raw_body_field() {
|
||||
let field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
||||
let parse = quote! {
|
||||
let #field_name = {
|
||||
let mut reader = #bytes::Buf::reader(request.into_body());
|
||||
let mut vec = ::std::vec::Vec::new();
|
||||
::std::io::Read::read_to_end(&mut reader, &mut vec)
|
||||
.expect("reading from a bytes::Buf never fails");
|
||||
vec
|
||||
};
|
||||
let #field_name =
|
||||
::std::convert::AsRef::<[u8]>::as_ref(request.body()).to_vec();
|
||||
};
|
||||
|
||||
(parse, quote! { #field_name, })
|
||||
|
@ -5,7 +5,6 @@ use super::{Response, ResponseField};
|
||||
|
||||
impl Response {
|
||||
pub fn expand_incoming(&self, error_ty: &TokenStream, ruma_api: &TokenStream) -> TokenStream {
|
||||
let bytes = quote! { #ruma_api::exports::bytes };
|
||||
let http = quote! { #ruma_api::exports::http };
|
||||
let ruma_serde = quote! { #ruma_api::exports::ruma_serde };
|
||||
let serde_json = quote! { #ruma_api::exports::serde_json };
|
||||
@ -25,15 +24,17 @@ impl Response {
|
||||
ResponseBody
|
||||
as #ruma_serde::Outgoing
|
||||
>::Incoming = {
|
||||
let body = response.into_body();
|
||||
if #bytes::Buf::has_remaining(&body) {
|
||||
#serde_json::from_reader(#bytes::Buf::reader(body))?
|
||||
} else {
|
||||
let body = ::std::convert::AsRef::<[::std::primitive::u8]>::as_ref(
|
||||
response.body(),
|
||||
);
|
||||
|
||||
#serde_json::from_slice(match body {
|
||||
// If the response body is completely empty, pretend it is an empty
|
||||
// JSON object instead. This allows responses with only optional body
|
||||
// parameters to be deserialized in that case.
|
||||
#serde_json::from_str("{}")?
|
||||
}
|
||||
[] => b"{}",
|
||||
b => b,
|
||||
})?
|
||||
};
|
||||
}
|
||||
} else {
|
||||
@ -93,11 +94,10 @@ impl Response {
|
||||
ResponseField::NewtypeRawBody(_) => {
|
||||
new_type_raw_body = Some(quote! {
|
||||
#field_name: {
|
||||
let mut reader = #bytes::Buf::reader(response.into_body());
|
||||
let mut vec = ::std::vec::Vec::new();
|
||||
::std::io::Read::read_to_end(&mut reader, &mut vec)
|
||||
.expect("reading from a bytes::Buf never fails");
|
||||
vec
|
||||
::std::convert::AsRef::<[::std::primitive::u8]>::as_ref(
|
||||
response.body(),
|
||||
)
|
||||
.to_vec()
|
||||
}
|
||||
});
|
||||
// skip adding to the vec
|
||||
@ -120,7 +120,7 @@ impl Response {
|
||||
impl #ruma_api::IncomingResponse for Response {
|
||||
type EndpointError = #error_ty;
|
||||
|
||||
fn try_from_http_response<T: #bytes::Buf>(
|
||||
fn try_from_http_response<T: ::std::convert::AsRef<[::std::primitive::u8]>>(
|
||||
response: #http::Response<T>,
|
||||
) -> ::std::result::Result<
|
||||
Self,
|
||||
@ -130,15 +130,17 @@ impl Response {
|
||||
#extract_response_headers
|
||||
#typed_response_body_decl
|
||||
|
||||
Ok(Self {
|
||||
::std::result::Result::Ok(Self {
|
||||
#response_init_fields
|
||||
})
|
||||
} else {
|
||||
match <#error_ty as #ruma_api::EndpointError>::try_from_http_response(
|
||||
response
|
||||
) {
|
||||
Ok(err) => Err(#ruma_api::error::ServerError::Known(err).into()),
|
||||
Err(response_err) => {
|
||||
::std::result::Result::Ok(err) => {
|
||||
Err(#ruma_api::error::ServerError::Known(err).into())
|
||||
}
|
||||
::std::result::Result::Err(response_err) => {
|
||||
Err(#ruma_api::error::ServerError::Unknown(response_err).into())
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.0.1"
|
||||
http = "0.2.2"
|
||||
percent-encoding = "2.1.0"
|
||||
ruma-api-macros = { version = "=0.17.0-alpha.2", path = "../ruma-api-macros" }
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
use std::{error::Error as StdError, fmt};
|
||||
|
||||
use bytes::Buf;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{EndpointError, OutgoingResponse};
|
||||
@ -21,7 +20,7 @@ impl OutgoingResponse for Void {
|
||||
}
|
||||
|
||||
impl EndpointError for Void {
|
||||
fn try_from_http_response<T: Buf>(
|
||||
fn try_from_http_response<T: AsRef<[u8]>>(
|
||||
_response: http::Response<T>,
|
||||
) -> Result<Self, ResponseDeserializationError> {
|
||||
Err(ResponseDeserializationError::none())
|
||||
|
@ -22,7 +22,6 @@ compile_error!("ruma_api's Cargo features only exist as a workaround are not mea
|
||||
|
||||
use std::{convert::TryInto as _, error::Error as StdError};
|
||||
|
||||
use bytes::Buf;
|
||||
use http::Method;
|
||||
use ruma_identifiers::UserId;
|
||||
|
||||
@ -204,7 +203,6 @@ pub mod error;
|
||||
/// It is not considered part of ruma-api's public API.
|
||||
#[doc(hidden)]
|
||||
pub mod exports {
|
||||
pub use bytes;
|
||||
pub use http;
|
||||
pub use percent_encoding;
|
||||
pub use ruma_serde;
|
||||
@ -246,7 +244,7 @@ pub trait IncomingResponse: Sized {
|
||||
type EndpointError: EndpointError;
|
||||
|
||||
/// Tries to convert the given `http::Response` into this response type.
|
||||
fn try_from_http_response<T: Buf>(
|
||||
fn try_from_http_response<T: AsRef<[u8]>>(
|
||||
response: http::Response<T>,
|
||||
) -> Result<Self, FromHttpResponseError<Self::EndpointError>>;
|
||||
}
|
||||
@ -301,7 +299,9 @@ pub trait IncomingRequest: Sized {
|
||||
const METADATA: Metadata;
|
||||
|
||||
/// Tries to turn the given `http::Request` into this request type.
|
||||
fn try_from_http_request<T: Buf>(req: http::Request<T>) -> Result<Self, FromHttpRequestError>;
|
||||
fn try_from_http_request<T: AsRef<[u8]>>(
|
||||
req: http::Request<T>,
|
||||
) -> Result<Self, FromHttpRequestError>;
|
||||
}
|
||||
|
||||
/// A request type for a Matrix API endpoint, used for sending responses.
|
||||
@ -319,7 +319,7 @@ pub trait EndpointError: OutgoingResponse + StdError + Sized + 'static {
|
||||
///
|
||||
/// This will always return `Err` variant when no `error` field is defined in
|
||||
/// the `ruma_api` macro.
|
||||
fn try_from_http_response<T: Buf>(
|
||||
fn try_from_http_response<T: AsRef<[u8]>>(
|
||||
response: http::Response<T>,
|
||||
) -> Result<Self, error::ResponseDeserializationError>;
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ fn request_serde() {
|
||||
};
|
||||
|
||||
let http_req = req.clone().try_into_http_request("https://homeserver.tld", None).unwrap();
|
||||
let req2 = Request::try_from_http_request(http_req.map(std::io::Cursor::new)).unwrap();
|
||||
let req2 = Request::try_from_http_request(http_req).unwrap();
|
||||
|
||||
assert_eq!(req.hello, req2.hello);
|
||||
assert_eq!(req.world, req2.world);
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use bytes::Buf;
|
||||
use http::{header::CONTENT_TYPE, method::Method};
|
||||
use ruma_api::{
|
||||
error::{FromHttpRequestError, FromHttpResponseError, IntoHttpError, ServerError, Void},
|
||||
@ -67,7 +66,7 @@ impl IncomingRequest for Request {
|
||||
|
||||
const METADATA: Metadata = METADATA;
|
||||
|
||||
fn try_from_http_request<T: Buf>(
|
||||
fn try_from_http_request<T: AsRef<[u8]>>(
|
||||
request: http::Request<T>,
|
||||
) -> Result<Self, FromHttpRequestError> {
|
||||
let path_segments: Vec<&str> = request.uri().path()[1..].split('/').collect();
|
||||
@ -78,7 +77,7 @@ impl IncomingRequest for Request {
|
||||
TryFrom::try_from(&*decoded)?
|
||||
};
|
||||
|
||||
let request_body: RequestBody = serde_json::from_reader(request.into_body().reader())?;
|
||||
let request_body: RequestBody = serde_json::from_slice(request.body().as_ref())?;
|
||||
|
||||
Ok(Request { room_id: request_body.room_id, room_alias })
|
||||
}
|
||||
@ -100,7 +99,7 @@ impl Outgoing for Response {
|
||||
impl IncomingResponse for Response {
|
||||
type EndpointError = Void;
|
||||
|
||||
fn try_from_http_response<T: Buf>(
|
||||
fn try_from_http_response<T: AsRef<[u8]>>(
|
||||
http_response: http::Response<T>,
|
||||
) -> Result<Self, FromHttpResponseError<Void>> {
|
||||
if http_response.status().as_u16() < 400 {
|
||||
|
@ -1,4 +1,3 @@
|
||||
use bytes::Buf;
|
||||
use ruma_api::{
|
||||
error::{FromHttpResponseError, IntoHttpError, Void},
|
||||
ruma_api, IncomingResponse, OutgoingResponse,
|
||||
@ -28,7 +27,7 @@ pub struct Response;
|
||||
impl IncomingResponse for Response {
|
||||
type EndpointError = Void;
|
||||
|
||||
fn try_from_http_response<T: Buf>(
|
||||
fn try_from_http_response<T: AsRef<[u8]>>(
|
||||
_: http::Response<T>,
|
||||
) -> Result<Self, FromHttpResponseError<Void>> {
|
||||
todo!()
|
||||
|
@ -17,7 +17,6 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
assign = "1.1.1"
|
||||
bytes = "1.0.1"
|
||||
http = "0.2.2"
|
||||
js_int = { version = "0.2.0", features = ["serde"] }
|
||||
maplit = "1.0.2"
|
||||
|
@ -2,14 +2,13 @@
|
||||
|
||||
use std::{collections::BTreeMap, fmt, time::Duration};
|
||||
|
||||
use bytes::Buf;
|
||||
use ruma_api::{
|
||||
error::{IntoHttpError, ResponseDeserializationError},
|
||||
EndpointError, OutgoingResponse,
|
||||
};
|
||||
use ruma_identifiers::RoomVersionId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{from_reader as from_json_reader, to_vec as to_json_vec, Value as JsonValue};
|
||||
use serde_json::{from_slice as from_json_slice, to_vec as to_json_vec, Value as JsonValue};
|
||||
|
||||
/// Deserialize and Serialize implementations for ErrorKind.
|
||||
/// Separate module because it's a lot of code.
|
||||
@ -206,11 +205,11 @@ pub struct Error {
|
||||
}
|
||||
|
||||
impl EndpointError for Error {
|
||||
fn try_from_http_response<T: Buf>(
|
||||
fn try_from_http_response<T: AsRef<[u8]>>(
|
||||
response: http::Response<T>,
|
||||
) -> Result<Self, ResponseDeserializationError> {
|
||||
let status = response.status();
|
||||
let error_body: ErrorBody = from_json_reader(response.into_body().reader())?;
|
||||
let error_body: ErrorBody = from_json_slice(response.body().as_ref())?;
|
||||
Ok(error_body.into_error(status))
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ impl ruma_api::IncomingRequest for IncomingRequest {
|
||||
|
||||
const METADATA: ruma_api::Metadata = METADATA;
|
||||
|
||||
fn try_from_http_request<T: bytes::Buf>(
|
||||
fn try_from_http_request<T: AsRef<[u8]>>(
|
||||
request: http::Request<T>,
|
||||
) -> Result<Self, ruma_api::error::FromHttpRequestError> {
|
||||
use std::convert::TryFrom;
|
||||
@ -129,7 +129,7 @@ impl ruma_api::IncomingRequest for IncomingRequest {
|
||||
let content = {
|
||||
let event_type =
|
||||
percent_encoding::percent_decode(path_segments[6].as_bytes()).decode_utf8()?;
|
||||
let body: Box<RawJsonValue> = serde_json::from_reader(body.reader())?;
|
||||
let body: Box<RawJsonValue> = serde_json::from_slice(body.as_ref())?;
|
||||
|
||||
AnyMessageEventContent::from_parts(&event_type, body)?
|
||||
};
|
||||
|
@ -77,9 +77,8 @@ mod tests {
|
||||
http::Request::builder()
|
||||
.method("PUT")
|
||||
.uri("https://bar.org/_matrix/client/r0/profile/@foo:bar.org/avatar_url")
|
||||
.body(std::io::Cursor::new(
|
||||
serde_json::to_vec(&serde_json::json!({ "avatar_url": "" })).unwrap(),
|
||||
)).unwrap(),
|
||||
.body(serde_json::to_vec(&serde_json::json!({ "avatar_url": "" })).unwrap())
|
||||
.unwrap(),
|
||||
).unwrap(),
|
||||
IncomingRequest { user_id, avatar_url: None } if user_id == "@foo:bar.org"
|
||||
);
|
||||
|
@ -108,7 +108,7 @@ impl ruma_api::IncomingRequest for IncomingRequest {
|
||||
|
||||
const METADATA: ruma_api::Metadata = METADATA;
|
||||
|
||||
fn try_from_http_request<T: bytes::Buf>(
|
||||
fn try_from_http_request<T: AsRef<[u8]>>(
|
||||
request: http::Request<T>,
|
||||
) -> Result<Self, ruma_api::error::FromHttpRequestError> {
|
||||
use std::convert::TryFrom;
|
||||
|
@ -108,7 +108,7 @@ impl ruma_api::IncomingRequest for IncomingRequest {
|
||||
|
||||
const METADATA: ruma_api::Metadata = METADATA;
|
||||
|
||||
fn try_from_http_request<T: bytes::Buf>(
|
||||
fn try_from_http_request<T: AsRef<[u8]>>(
|
||||
request: http::Request<T>,
|
||||
) -> Result<Self, ruma_api::error::FromHttpRequestError> {
|
||||
use std::{borrow::Cow, convert::TryFrom};
|
||||
@ -136,7 +136,7 @@ impl ruma_api::IncomingRequest for IncomingRequest {
|
||||
let content = {
|
||||
let event_type =
|
||||
percent_encoding::percent_decode(path_segments[6].as_bytes()).decode_utf8()?;
|
||||
let body: Box<RawJsonValue> = serde_json::from_reader(body.reader())?;
|
||||
let body: Box<RawJsonValue> = serde_json::from_slice(body.as_ref())?;
|
||||
|
||||
AnyStateEventContent::from_parts(&event_type, body)?
|
||||
};
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
use std::{collections::BTreeMap, fmt};
|
||||
|
||||
use bytes::Buf;
|
||||
use ruma_api::{
|
||||
error::{IntoHttpError, ResponseDeserializationError},
|
||||
EndpointError, OutgoingResponse,
|
||||
@ -10,7 +9,7 @@ use ruma_api::{
|
||||
use ruma_serde::Outgoing;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{
|
||||
from_reader as from_json_reader, to_vec as to_json_vec, value::RawValue as RawJsonValue,
|
||||
from_slice as from_json_slice, to_vec as to_json_vec, value::RawValue as RawJsonValue,
|
||||
Value as JsonValue,
|
||||
};
|
||||
|
||||
@ -137,11 +136,11 @@ impl From<MatrixError> for UiaaResponse {
|
||||
}
|
||||
|
||||
impl EndpointError for UiaaResponse {
|
||||
fn try_from_http_response<T: Buf>(
|
||||
fn try_from_http_response<T: AsRef<[u8]>>(
|
||||
response: http::Response<T>,
|
||||
) -> Result<Self, ResponseDeserializationError> {
|
||||
if response.status() == http::StatusCode::UNAUTHORIZED {
|
||||
Ok(UiaaResponse::AuthResponse(from_json_reader(response.into_body().reader())?))
|
||||
Ok(UiaaResponse::AuthResponse(from_json_slice(response.body().as_ref())?))
|
||||
} else {
|
||||
MatrixError::try_from_http_response(response).map(From::from)
|
||||
}
|
||||
|
@ -373,7 +373,9 @@ impl Client {
|
||||
let hyper_response = client.hyper.request(http_request.map(hyper::Body::from)).await?;
|
||||
let (head, body) = hyper_response.into_parts();
|
||||
|
||||
let full_body = hyper::body::aggregate(body).await?;
|
||||
// FIXME: Use aggregate instead of to_bytes once serde_json can parse from a reader at a
|
||||
// comparable speed as reading from a slice: https://github.com/serde-rs/json/issues/160
|
||||
let full_body = hyper::body::to_bytes(body).await?;
|
||||
let full_response = HttpResponse::from_parts(head, full_body);
|
||||
|
||||
Ok(ruma_api::IncomingResponse::try_from_http_response(full_response)?)
|
||||
|
@ -6,7 +6,7 @@ pub fn expand_display_as_ref_str(ident: &Ident) -> syn::Result<TokenStream> {
|
||||
#[automatically_derived]
|
||||
impl ::std::fmt::Display for #ident {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
f.write_str(<Self as ::std::convert::AsRef<::std::primitive::str>>::as_ref(self))
|
||||
f.write_str(::std::convert::AsRef::<::std::primitive::str>::as_ref(self))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -13,8 +13,7 @@ pub fn expand_serialize_as_ref_str(ident: &Ident) -> syn::Result<TokenStream> {
|
||||
where
|
||||
S: #ruma_serde::exports::serde::ser::Serializer,
|
||||
{
|
||||
<Self as ::std::convert::AsRef<::std::primitive::str>>::as_ref(self)
|
||||
.serialize(serializer)
|
||||
::std::convert::AsRef::<::std::primitive::str>::as_ref(self).serialize(serializer)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user