jsonrpsee_server/middleware/http/
proxy_get_request.rs

1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any
4// person obtaining a copy of this software and associated
5// documentation files (the "Software"), to deal in the
6// Software without restriction, including without
7// limitation the rights to use, copy, modify, merge,
8// publish, distribute, sublicense, and/or sell copies of
9// the Software, and to permit persons to whom the Software
10// is furnished to do so, subject to the following
11// conditions:
12//
13// The above copyright notice and this permission notice
14// shall be included in all copies or substantial portions
15// of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25// DEALINGS IN THE SOFTWARE.
26
27//! Middleware that proxies requests at a specified URI to internal
28//! RPC method calls.
29use crate::transport::http;
30use crate::{HttpBody, HttpRequest, HttpResponse};
31
32use http_body_util::BodyExt;
33use hyper::body::Bytes;
34use hyper::header::{ACCEPT, CONTENT_TYPE};
35use hyper::http::HeaderValue;
36use hyper::{Method, Uri};
37use jsonrpsee_core::BoxError;
38use jsonrpsee_types::{Id, RequestSer};
39use std::future::Future;
40use std::pin::Pin;
41use std::sync::Arc;
42use std::task::{Context, Poll};
43use tower::{Layer, Service};
44
45/// Error that occur if the specified path doesn't start with `/<path>`
46#[derive(Debug, thiserror::Error)]
47#[error("ProxyGetRequestLayer path must start with `/`, got `{0}`")]
48pub struct InvalidPath(String);
49
50/// Layer that applies [`ProxyGetRequest`] which proxies the `GET /path` requests to
51/// specific RPC method calls and that strips the response.
52///
53/// See [`ProxyGetRequest`] for more details.
54#[derive(Debug, Clone)]
55pub struct ProxyGetRequestLayer {
56	path: String,
57	method: String,
58}
59
60impl ProxyGetRequestLayer {
61	/// Creates a new [`ProxyGetRequestLayer`].
62	///
63	/// See [`ProxyGetRequest`] for more details.
64	pub fn new(path: impl Into<String>, method: impl Into<String>) -> Result<Self, InvalidPath> {
65		let path = path.into();
66		if !path.starts_with('/') {
67			return Err(InvalidPath(path));
68		}
69
70		Ok(Self { path, method: method.into() })
71	}
72}
73impl<S> Layer<S> for ProxyGetRequestLayer {
74	type Service = ProxyGetRequest<S>;
75
76	fn layer(&self, inner: S) -> Self::Service {
77		ProxyGetRequest::new(inner, &self.path, &self.method)
78			.expect("Path already validated in ProxyGetRequestLayer; qed")
79	}
80}
81
82/// Proxy `GET /path` requests to the specified RPC method calls.
83///
84/// # Request
85///
86/// The `GET /path` requests are modified into valid `POST` requests for
87/// calling the RPC method. This middleware adds appropriate headers to the
88/// request, and completely modifies the request `BODY`.
89///
90/// # Response
91///
92/// The response of the RPC method is stripped down to contain only the method's
93/// response, removing any RPC 2.0 spec logic regarding the response' body.
94#[derive(Debug, Clone)]
95pub struct ProxyGetRequest<S> {
96	inner: S,
97	path: Arc<str>,
98	method: Arc<str>,
99}
100
101impl<S> ProxyGetRequest<S> {
102	/// Creates a new [`ProxyGetRequest`].
103	///
104	/// The request `GET /path` is redirected to the provided method.
105	/// Fails if the path does not start with `/`.
106	pub fn new(inner: S, path: &str, method: &str) -> Result<Self, InvalidPath> {
107		if !path.starts_with('/') {
108			return Err(InvalidPath(path.to_string()));
109		}
110
111		Ok(Self { inner, path: Arc::from(path), method: Arc::from(method) })
112	}
113}
114
115impl<S, B> Service<HttpRequest<B>> for ProxyGetRequest<S>
116where
117	S: Service<HttpRequest, Response = HttpResponse>,
118	S::Response: 'static,
119	S::Error: Into<BoxError> + 'static,
120	S::Future: Send + 'static,
121	B: http_body::Body<Data = Bytes> + Send + 'static,
122	B::Data: Send,
123	B::Error: Into<BoxError>,
124{
125	type Response = S::Response;
126	type Error = BoxError;
127	type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
128
129	#[inline]
130	fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
131		self.inner.poll_ready(cx).map_err(Into::into)
132	}
133
134	fn call(&mut self, mut req: HttpRequest<B>) -> Self::Future {
135		let modify = self.path.as_ref() == req.uri() && req.method() == Method::GET;
136
137		// Proxy the request to the appropriate method call.
138		let req = if modify {
139			// RPC methods are accessed with `POST`.
140			*req.method_mut() = Method::POST;
141			// Precautionary remove the URI.
142			*req.uri_mut() = Uri::from_static("/");
143
144			// Requests must have the following headers:
145			req.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
146			req.headers_mut().insert(ACCEPT, HeaderValue::from_static("application/json"));
147
148			// Adjust the body to reflect the method call.
149			let bytes = serde_json::to_vec(&RequestSer::borrowed(&Id::Number(0), &self.method, None))
150				.expect("Valid request; qed");
151
152			let body = HttpBody::from(bytes);
153
154			req.map(|_| body)
155		} else {
156			req.map(HttpBody::new)
157		};
158
159		// Call the inner service and get a future that resolves to the response.
160		let fut = self.inner.call(req);
161
162		// Adjust the response if needed.
163		let res_fut = async move {
164			let res = fut.await.map_err(|err| err.into())?;
165
166			// Nothing to modify: return the response as is.
167			if !modify {
168				return Ok(res);
169			}
170
171			let mut body = http_body_util::BodyStream::new(res.into_body());
172			let mut bytes = Vec::new();
173
174			while let Some(frame) = body.frame().await {
175				let data = frame?.into_data().map_err(|e| format!("{e:?}"))?;
176				bytes.extend(data);
177			}
178
179			#[derive(serde::Deserialize, Debug)]
180			struct RpcPayload<'a> {
181				#[serde(borrow)]
182				result: &'a serde_json::value::RawValue,
183			}
184
185			let response = if let Ok(payload) = serde_json::from_slice::<RpcPayload>(&bytes) {
186				http::response::ok_response(payload.result.to_string())
187			} else {
188				http::response::internal_error()
189			};
190
191			Ok(response)
192		};
193
194		Box::pin(res_fut)
195	}
196}