diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr index 33beae6bcb..64d05937b1 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `::Hash: Serialize` is not satisfi --> tests/ui/incorrect/rpc/rpc_empty_bounds.rs:9:1 | 9 | #[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Serialize` is not implemented for `::Hash`, which is required by `Result<::Hash, ErrorObject<'_>>: IntoResponse` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Serialize` is not implemented for `::Hash` | = note: for local types consider adding `#[derive(serde::Serialize)]` to your `::Hash` type = note: for types from other crates check whether the crate offers a `serde` feature flag @@ -13,7 +13,7 @@ error[E0277]: the trait bound `::Hash: Clone` is not satisfied --> tests/ui/incorrect/rpc/rpc_empty_bounds.rs:9:1 | 9 | #[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `::Hash`, which is required by `Result<::Hash, ErrorObject<'_>>: IntoResponse` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `::Hash` | = note: required for `Result<::Hash, ErrorObject<'_>>` to implement `IntoResponse` = note: this error originates in the attribute macro `rpc` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -22,7 +22,7 @@ error[E0277]: the trait bound `::Hash: DeserializeOwned` is not --> tests/ui/incorrect/rpc/rpc_empty_bounds.rs:9:1 | 9 | #[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'de> Deserialize<'de>` is not implemented for `::Hash`, which is required by `::Hash: DeserializeOwned` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'de> Deserialize<'de>` is not implemented for `::Hash` | = note: required for `::Hash` to implement `DeserializeOwned` note: required by a bound in `request` diff --git a/server/src/middleware/http/proxy_get_request.rs b/server/src/middleware/http/proxy_get_request.rs index 0ef6cdb5dd..568ef0d2c5 100644 --- a/server/src/middleware/http/proxy_get_request.rs +++ b/server/src/middleware/http/proxy_get_request.rs @@ -24,8 +24,7 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -//! Middleware that proxies requests at a specified URI to internal -//! RPC method calls. +//! Middleware that proxies HTTP GET requests at a specified URI to an internal RPC method call. use crate::transport::http; use crate::{HttpBody, HttpRequest, HttpResponse}; @@ -34,9 +33,9 @@ use http_body_util::BodyExt; use hyper::body::Bytes; use hyper::header::{ACCEPT, CONTENT_TYPE}; use hyper::http::HeaderValue; -use hyper::{Method, Uri}; +use hyper::{Method, StatusCode, Uri}; use jsonrpsee_core::BoxError; -use jsonrpsee_types::{Id, RequestSer}; +use jsonrpsee_types::{ErrorCode, ErrorObject, Id, RequestSer}; use std::collections::HashMap; use std::future::Future; use std::pin::Pin; @@ -181,19 +180,19 @@ where } #[derive(serde::Deserialize)] - struct RpcPayload<'a> { + struct SuccessResponse<'a> { #[serde(borrow)] result: &'a serde_json::value::RawValue, } - let response = if let Ok(payload) = serde_json::from_slice::(&bytes) { - let mut rp = http::response::ok_response(payload.result.to_string()); - rp.extensions_mut().extend(parts.extensions); - rp + let mut response = if let Ok(payload) = serde_json::from_slice::(&bytes) { + http::response::ok_response(payload.result.to_string()) } else { - http::response::internal_error() + internal_proxy_error(&bytes) }; + response.extensions_mut().extend(parts.extensions); + Ok(response) } .boxed() @@ -206,3 +205,21 @@ where } } } + +fn internal_proxy_error(bytes: &[u8]) -> HttpResponse { + #[derive(serde::Deserialize)] + struct ErrorResponse<'a> { + #[serde(borrow)] + error: ErrorObject<'a>, + } + + let error = serde_json::from_slice::(&bytes) + .map(|payload| payload.error) + .unwrap_or_else(|_| ErrorObject::from(ErrorCode::InternalError)); + + http::response::from_template( + StatusCode::INTERNAL_SERVER_ERROR, + serde_json::to_string(&error).expect("JSON serialization infallible; qed"), + "application/json; charset=utf-8", + ) +} diff --git a/server/src/transport/http.rs b/server/src/transport/http.rs index 1cb89e42f4..c60e506899 100644 --- a/server/src/transport/http.rs +++ b/server/src/transport/http.rs @@ -115,7 +115,7 @@ where pub mod response { use jsonrpsee_core::server::MethodResponse; use jsonrpsee_types::error::{reject_too_big_request, ErrorCode}; - use jsonrpsee_types::{ErrorObjectOwned, Id, Response, ResponsePayload}; + use jsonrpsee_types::{ErrorObject, ErrorObjectOwned, Id, Response, ResponsePayload}; use crate::{HttpBody, HttpResponse}; @@ -131,6 +131,12 @@ pub mod response { from_template(hyper::StatusCode::INTERNAL_SERVER_ERROR, error, JSON) } + /// Create a json response for general errors returned by the called method. + pub fn error_response(error: ErrorObject) -> HttpResponse { + let error = serde_json::to_string(&error).expect("JSON serialization infallible; qed"); + from_template(hyper::StatusCode::INTERNAL_SERVER_ERROR, error, JSON) + } + /// Create a text/plain response for not allowed hosts. pub fn host_not_allowed() -> HttpResponse { from_template(hyper::StatusCode::FORBIDDEN, "Provided Host header is not whitelisted.\n", TEXT) @@ -163,7 +169,11 @@ pub mod response { } /// Create a response body. - fn from_template(status: hyper::StatusCode, body: impl Into, content_type: &'static str) -> HttpResponse { + pub(crate) fn from_template( + status: hyper::StatusCode, + body: impl Into, + content_type: &'static str, + ) -> HttpResponse { HttpResponse::builder() .status(status) .header("content-type", hyper::header::HeaderValue::from_static(content_type))