Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
26 changes: 8 additions & 18 deletions http-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use crate::traits::Client;
use crate::transport::HttpTransportClient;
use crate::v2::request::{JsonRpcCallSer, JsonRpcNotificationSer};
use crate::v2::{
error::JsonRpcErrorAlloc,
error::JsonRpcError,
params::{Id, JsonRpcParams},
response::JsonRpcResponse,
};
use crate::{Error, JsonRawValue, TEN_MB_SIZE_BYTES};
use crate::{Error, TEN_MB_SIZE_BYTES};
use async_trait::async_trait;
use fnv::FnvHashMap;
use serde::de::DeserializeOwned;
Expand Down Expand Up @@ -76,12 +76,12 @@ impl Client for HttpClient {
let response: JsonRpcResponse<_> = match serde_json::from_slice(&body) {
Ok(response) => response,
Err(_) => {
let err: JsonRpcErrorAlloc = serde_json::from_slice(&body).map_err(Error::ParseError)?;
return Err(Error::Request(err));
let err: JsonRpcError = serde_json::from_slice(&body).map_err(Error::ParseError)?;
return Err(Error::Request(err.to_string()));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I changed the error type to a String in Error::Request to get rid of the alloc type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I noticed that. I don't think there much else we can do right?

}
};

let response_id = parse_request_id(response.id)?;
let response_id = response.id.as_number().copied().ok_or(Error::InvalidRequestId)?;

if response_id == id {
Ok(response.result)
Expand Down Expand Up @@ -115,15 +115,15 @@ impl Client for HttpClient {
let rps: Vec<JsonRpcResponse<_>> = match serde_json::from_slice(&body) {
Ok(response) => response,
Err(_) => {
let err: JsonRpcErrorAlloc = serde_json::from_slice(&body).map_err(Error::ParseError)?;
return Err(Error::Request(err));
let err: JsonRpcError = serde_json::from_slice(&body).map_err(Error::ParseError)?;
return Err(Error::Request(err.to_string()));
}
};

// NOTE: `R::default` is placeholder and will be replaced in loop below.
let mut responses = vec![R::default(); ordered_requests.len()];
for rp in rps {
let response_id = parse_request_id(rp.id)?;
let response_id = rp.id.as_number().copied().ok_or(Error::InvalidRequestId)?;
let pos = match request_set.get(&response_id) {
Some(pos) => *pos,
None => return Err(Error::InvalidRequestId),
Expand All @@ -133,13 +133,3 @@ impl Client for HttpClient {
Ok(responses)
}
}

fn parse_request_id(raw: Option<&JsonRawValue>) -> Result<u64, Error> {
match raw {
None => Err(Error::InvalidRequestId),
Some(id) => {
let id = serde_json::from_str(id.get()).map_err(Error::ParseError)?;
Ok(id)
}
}
}
15 changes: 10 additions & 5 deletions http-client/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::v2::{
error::{JsonRpcErrorCode, JsonRpcErrorObjectAlloc},
error::{JsonRpcError, JsonRpcErrorCode, JsonRpcErrorObject},
params::JsonRpcParams,
};
use crate::{traits::Client, Error, HttpClientBuilder, JsonValue};
Expand Down Expand Up @@ -107,9 +107,14 @@ async fn run_request_with_response(response: String) -> Result<JsonValue, Error>
client.request("say_hello", JsonRpcParams::NoParams).await
}

fn assert_jsonrpc_error_response(error: Error, code: JsonRpcErrorObjectAlloc) {
match &error {
Error::Request(e) => assert_eq!(e.error, code),
e => panic!("Expected error: \"{}\", got: {:?}", error, e),
fn assert_jsonrpc_error_response(err: Error, exp: JsonRpcErrorObject) {
match &err {
Error::Request(e) => {
let this: JsonRpcError = serde_json::from_str(&e).unwrap();
// NOTE: `RawValue` doesn't implement PartialEq.
assert_eq!(this.error.code, exp.code);
assert_eq!(this.error.message, exp.message);
}
e => panic!("Expected error: \"{}\", got: {:?}", err, e),
};
}
13 changes: 7 additions & 6 deletions http-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ use hyper::{
Error as HyperError,
};
use jsonrpsee_types::error::{CallError, Error, GenericTransportError};
use jsonrpsee_types::v2::error::JsonRpcErrorCode;
use jsonrpsee_types::v2::params::{Id, RpcParams};
use jsonrpsee_types::v2::request::{JsonRpcInvalidRequest, JsonRpcRequest};
use jsonrpsee_types::v2::{error::JsonRpcErrorCode, params::RpcParams};
use jsonrpsee_utils::hyper_helpers::read_response_to_body;
use jsonrpsee_utils::server::helpers::{collect_batch_response, send_error};
use jsonrpsee_utils::server::rpc_module::{MethodSink, RpcModule};
Expand Down Expand Up @@ -162,17 +163,17 @@ impl Server {
if let Some(method) = methods.get(&*req.method) {
let params = RpcParams::new(req.params.map(|params| params.get()));
// NOTE(niklasad1): connection ID is unused thus hardcoded to `0`.
if let Err(err) = (method)(req.id, params, &tx, 0) {
if let Err(err) = (method)(req.id.clone(), params, &tx, 0) {
log::error!(
"execution of method call '{}' failed: {:?}, request id={:?}",
req.method,
err,
req.id
);
send_error(req.id, &tx, JsonRpcErrorCode::ServerError(-1).into());
send_error(req.id.clone(), &tx, JsonRpcErrorCode::ServerError(-1).into());
}
} else {
send_error(req.id, &tx, JsonRpcErrorCode::MethodNotFound.into());
send_error(req.id.clone(), &tx, JsonRpcErrorCode::MethodNotFound.into());
}
};

Expand Down Expand Up @@ -218,7 +219,7 @@ impl Server {
execute(&tx, req);
}
} else {
send_error(None, &tx, JsonRpcErrorCode::InvalidRequest.into());
send_error(Id::Null, &tx, JsonRpcErrorCode::InvalidRequest.into());
}
} else {
log::error!(
Expand All @@ -227,7 +228,7 @@ impl Server {
);
let (id, code) = match serde_json::from_slice::<JsonRpcInvalidRequest>(&body) {
Ok(req) => (req.id, JsonRpcErrorCode::InvalidRequest),
Err(_) => (None, JsonRpcErrorCode::ParseError),
Err(_) => (Id::Null, JsonRpcErrorCode::ParseError),
};
send_error(id, &tx, code.into());
}
Expand Down
2 changes: 1 addition & 1 deletion http-server/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ async fn invalid_single_method_call() {
let req = r#"{"jsonrpc":"2.0","method":1, "params": "bar"}"#;
let response = http_request(req.into(), uri.clone()).await.unwrap();
assert_eq!(response.status, StatusCode::OK);
assert_eq!(response.body, invalid_request(Id::Null));
assert_eq!(response.body, parse_error(Id::Null));
}

#[tokio::test]
Expand Down
4 changes: 1 addition & 3 deletions types/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use crate::v2::error::JsonRpcErrorAlloc;
use std::fmt;

/// Convenience type for displaying errors.
#[derive(Clone, Debug, PartialEq)]
pub struct Mismatch<T> {
Expand Down Expand Up @@ -48,7 +46,7 @@ pub enum Error {
Transport(#[source] Box<dyn std::error::Error + Send + Sync>),
/// JSON-RPC request error.
#[error("JSON-RPC request error: {0:?}")]
Request(#[source] JsonRpcErrorAlloc),
Request(String),
/// Frontend/backend channel error.
#[error("Frontend/backend channel error: {0}")]
Internal(#[source] futures_channel::mpsc::SendError),
Expand Down
80 changes: 11 additions & 69 deletions types/src/v2/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,30 @@ use crate::v2::params::{Id, TwoPointZero};
use serde::de::Deserializer;
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use serde_json::value::{RawValue, Value as JsonValue};
use serde_json::value::RawValue;
use std::fmt;
use thiserror::Error;

/// [Failed JSON-RPC response object](https://www.jsonrpc.org/specification#response_object).
#[derive(Serialize, Debug)]
#[derive(Serialize, Deserialize, Debug)]
pub struct JsonRpcError<'a> {
/// JSON-RPC version.
pub jsonrpc: TwoPointZero,
/// Error.
#[serde(borrow)]
pub error: JsonRpcErrorObject<'a>,
/// Request ID
pub id: Option<&'a RawValue>,
}
/// [Failed JSON-RPC response object with allocations](https://www.jsonrpc.org/specification#response_object).
#[derive(Error, Debug, Deserialize, PartialEq)]
pub struct JsonRpcErrorAlloc {
/// JSON-RPC version.
pub jsonrpc: TwoPointZero,
/// JSON-RPC error object.
pub error: JsonRpcErrorObjectAlloc,
/// Request ID.
pub id: Id,
}

impl fmt::Display for JsonRpcErrorAlloc {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}: {:?}: {:?}", self.jsonrpc, self.error, self.id)
}
pub id: Id<'a>,
}

/// JSON-RPC error object.
#[derive(Debug, PartialEq, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct JsonRpcErrorObjectAlloc {
/// Code
pub code: JsonRpcErrorCode,
/// Message
pub message: String,
/// Optional data
pub data: Option<JsonValue>,
}

impl From<JsonRpcErrorCode> for JsonRpcErrorObjectAlloc {
fn from(code: JsonRpcErrorCode) -> Self {
Self { code, message: code.message().to_owned(), data: None }
impl<'a> fmt::Display for JsonRpcError<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", serde_json::to_string(&self).expect("infallible; qed"))
}
}

/// JSON-RPC error object with no extra allocations.
Comment thread
niklasad1 marked this conversation as resolved.
Outdated
#[derive(Debug, Serialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct JsonRpcErrorObject<'a> {
/// Code
Expand All @@ -61,6 +34,7 @@ pub struct JsonRpcErrorObject<'a> {
pub message: &'a str,
/// Optional data
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(borrow)]
pub data: Option<&'a RawValue>,
}

Expand Down Expand Up @@ -180,47 +154,15 @@ impl serde::Serialize for JsonRpcErrorCode {

#[cfg(test)]
mod tests {
use super::{
Id, JsonRpcError, JsonRpcErrorAlloc, JsonRpcErrorCode, JsonRpcErrorObject, JsonRpcErrorObjectAlloc,
TwoPointZero,
};

#[test]
fn deserialize_works() {
Comment thread
niklasad1 marked this conversation as resolved.
let ser = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#;
let err: JsonRpcErrorAlloc = serde_json::from_str(ser).unwrap();
assert_eq!(err.jsonrpc, TwoPointZero);
assert_eq!(
err.error,
JsonRpcErrorObjectAlloc { code: JsonRpcErrorCode::ParseError, message: "Parse error".into(), data: None }
);
assert_eq!(err.id, Id::Null);
}

#[test]
fn deserialize_with_optional_data() {
let ser = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error", "data":"vegan"},"id":null}"#;
let err: JsonRpcErrorAlloc = serde_json::from_str(ser).unwrap();
assert_eq!(err.jsonrpc, TwoPointZero);
assert_eq!(
err.error,
JsonRpcErrorObjectAlloc {
code: JsonRpcErrorCode::ParseError,
message: "Parse error".into(),
data: Some("vegan".into())
}
);
assert_eq!(err.id, Id::Null);
}
use super::{Id, JsonRpcError, JsonRpcErrorCode, JsonRpcErrorObject, TwoPointZero};

#[test]
fn serialize_works() {
let exp = r#"{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error"},"id":1337}"#;
let raw_id = serde_json::value::to_raw_value(&1337).unwrap();
let err = JsonRpcError {
jsonrpc: TwoPointZero,
error: JsonRpcErrorObject { code: JsonRpcErrorCode::InternalError, message: "Internal error", data: None },
id: Some(&*raw_id),
id: Id::Number(1337),
};
let ser = serde_json::to_string(&err).unwrap();
assert_eq!(exp, ser);
Expand Down
58 changes: 51 additions & 7 deletions types/src/v2/params.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::error::InvalidParams;
use alloc::collections::BTreeMap;
use std::borrow::Cow;
Comment thread
niklasad1 marked this conversation as resolved.
Outdated
use serde::de::{self, Deserializer, Unexpected, Visitor};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use serde_json::{value::RawValue, Value as JsonValue};

Comment thread
niklasad1 marked this conversation as resolved.
Outdated
use std::fmt;

/// JSON-RPC parameter values for subscriptions.
Expand All @@ -26,7 +28,7 @@ pub struct JsonRpcNotificationParamsAlloc<T> {
}

/// JSON-RPC v2 marker type.
#[derive(Debug, Default, PartialEq)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct TwoPointZero;

struct TwoPointZeroVisitor;
Expand Down Expand Up @@ -157,16 +159,17 @@ impl From<SubscriptionId> for JsonValue {
#[derive(Debug, PartialEq, Clone, Hash, Eq, Deserialize, Serialize)]
Copy link
Copy Markdown
Contributor Author

@niklasad1 niklasad1 May 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clone should be cheap as deserialize just borrows the String AFAIU.

Then in the client code we just use Id::Number for now.

Thus, should never allocate in server at least?!

#[serde(deny_unknown_fields)]
#[serde(untagged)]
pub enum Id {
pub enum Id<'a> {
/// Null
Null,
/// Numeric id
Number(u64),
/// String id
Str(String),
#[serde(borrow)]
Str(Cow<'a, str>),
}

impl Id {
impl<'a> Id<'a> {
/// If the Id is a number, returns the associated number. Returns None otherwise.
pub fn as_number(&self) -> Option<&u64> {
match self {
Expand All @@ -190,8 +193,49 @@ impl Id {
_ => None,
}
}

/// Creates owned data from borrowed data, allocates only for Strings.
pub fn to_owned(&self) -> Id<'static> {
Comment thread
niklasad1 marked this conversation as resolved.
Outdated
match self {
Id::Null => Id::Null,
Id::Number(n) => Id::Number(*n),
Id::Str(Cow::Borrowed(s)) => Id::Str(Cow::Owned(s.to_string())),
Id::Str(Cow::Owned(s)) => Id::Str(Cow::Owned(s.clone())),
}
}
}

/// Untyped JSON-RPC ID.
// TODO(niklasad1): this should be enforced to only accept: String, Number, or Null.
pub type JsonRpcRawId<'a> = Option<&'a serde_json::value::RawValue>;
#[cfg(test)]
mod test {
use super::{Cow, Id};

#[test]
fn id_deserialization() {
let s = r#""2""#;
let deserialized: Id = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, Id::Str("2".into()));

let s = r#"2"#;
let deserialized: Id = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, Id::Number(2));

let s = r#""2x""#;
let deserialized: Id = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, Id::Str(Cow::Borrowed("2x")));

let s = r#"[1337]"#;
assert!(serde_json::from_str::<Id>(s).is_err());

let s = r#"[null, 0, 2, "3"]"#;
let deserialized: Vec<Id> = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, vec![Id::Null, Id::Number(0), Id::Number(2), Id::Str("3".into())]);
}

#[test]
fn id_serialization() {
let d =
vec![Id::Null, Id::Number(0), Id::Number(2), Id::Number(3), Id::Str("3".into()), Id::Str("test".into())];
let serialized = serde_json::to_string(&d).unwrap();
assert_eq!(serialized, r#"[null,0,2,3,"3","test"]"#);
}
}
Loading