jsonrpsee_server/transport/
http.rs1use crate::{
2 middleware::rpc::{RpcService, RpcServiceBuilder, RpcServiceCfg, RpcServiceT},
3 server::{handle_rpc_call, ServerConfig},
4 BatchRequestConfig, ConnectionState, HttpRequest, HttpResponse, LOG_TARGET,
5};
6use http::Method;
7use hyper::body::{Body, Bytes};
8use jsonrpsee_core::{
9 http_helpers::{read_body, HttpError},
10 server::Methods,
11 BoxError,
12};
13
14pub fn content_type_is_json<T: Body>(request: &HttpRequest<T>) -> bool {
16 is_json(request.headers().get(hyper::header::CONTENT_TYPE))
17}
18
19pub fn is_json(content_type: Option<&hyper::header::HeaderValue>) -> bool {
21 content_type.and_then(|val| val.to_str().ok()).map_or(false, |content| {
22 content.eq_ignore_ascii_case("application/json")
23 || content.eq_ignore_ascii_case("application/json; charset=utf-8")
24 || content.eq_ignore_ascii_case("application/json;charset=utf-8")
25 || content.eq_ignore_ascii_case("application/json-rpc")
26 || content.eq_ignore_ascii_case("application/json-rpc;charset=utf-8")
27 || content.eq_ignore_ascii_case("application/json-rpc; charset=utf-8")
28 })
29}
30
31pub async fn call_with_service_builder<L, B>(
35 request: HttpRequest<B>,
36 server_cfg: ServerConfig,
37 conn: ConnectionState,
38 methods: impl Into<Methods>,
39 rpc_service: RpcServiceBuilder<L>,
40) -> HttpResponse
41where
42 B: http_body::Body<Data = Bytes> + Send + 'static,
43 B::Data: Send,
44 B::Error: Into<BoxError>,
45 L: for<'a> tower::Layer<RpcService>,
46 <L as tower::Layer<RpcService>>::Service: Send + Sync + 'static,
47 for<'a> <L as tower::Layer<RpcService>>::Service: RpcServiceT<'a>,
48{
49 let ServerConfig { max_response_body_size, batch_requests_config, max_request_body_size, .. } = server_cfg;
50
51 let rpc_service = rpc_service.service(RpcService::new(
52 methods.into(),
53 max_response_body_size as usize,
54 conn.conn_id.into(),
55 RpcServiceCfg::OnlyCalls,
56 ));
57
58 let rp =
59 call_with_service(request, batch_requests_config, max_request_body_size, rpc_service, max_response_body_size)
60 .await;
61
62 drop(conn);
63
64 rp
65}
66
67pub async fn call_with_service<S, B>(
71 request: HttpRequest<B>,
72 batch_config: BatchRequestConfig,
73 max_request_size: u32,
74 rpc_service: S,
75 max_response_size: u32,
76) -> HttpResponse
77where
78 B: http_body::Body<Data = Bytes> + Send + 'static,
79 B::Data: Send,
80 B::Error: Into<BoxError>,
81 for<'a> S: RpcServiceT<'a> + Send,
82{
83 match *request.method() {
85 Method::POST if content_type_is_json(&request) => {
86 let (parts, body) = request.into_parts();
87
88 let (body, is_single) = match read_body(&parts.headers, body, max_request_size).await {
89 Ok(r) => r,
90 Err(HttpError::TooLarge) => return response::too_large(max_request_size),
91 Err(HttpError::Malformed) => return response::malformed(),
92 Err(HttpError::Stream(e)) => {
93 tracing::warn!(target: LOG_TARGET, "Internal error reading request body: {}", e);
94 return response::internal_error();
95 }
96 };
97
98 let rp = handle_rpc_call(&body, is_single, batch_config, max_response_size, &rpc_service, parts.extensions)
99 .await;
100
101 response::ok_response(rp.map_or(String::new(), |r| r.into_result()))
104 }
105 Method::POST => response::unsupported_content_type(),
107 _ => response::method_not_allowed(),
108 }
109}
110
111pub mod response {
113 use jsonrpsee_types::error::{reject_too_big_request, ErrorCode};
114 use jsonrpsee_types::{ErrorObjectOwned, Id, Response, ResponsePayload};
115
116 use crate::{HttpBody, HttpResponse};
117
118 const JSON: &str = "application/json; charset=utf-8";
119 const TEXT: &str = "text/plain";
120
121 pub fn internal_error() -> HttpResponse {
123 let err = ResponsePayload::<()>::error(ErrorObjectOwned::from(ErrorCode::InternalError));
124 let rp = Response::new(err, Id::Null);
125 let error = serde_json::to_string(&rp).expect("built from known-good data; qed");
126
127 from_template(hyper::StatusCode::INTERNAL_SERVER_ERROR, error, JSON)
128 }
129
130 pub fn host_not_allowed() -> HttpResponse {
132 from_template(hyper::StatusCode::FORBIDDEN, "Provided Host header is not whitelisted.\n", TEXT)
133 }
134
135 pub fn method_not_allowed() -> HttpResponse {
137 from_template(
138 hyper::StatusCode::METHOD_NOT_ALLOWED,
139 "Used HTTP Method is not allowed. POST is required\n",
140 TEXT,
141 )
142 }
143
144 pub fn too_large(limit: u32) -> HttpResponse {
146 let err = ResponsePayload::<()>::error(reject_too_big_request(limit));
147 let rp = Response::new(err, Id::Null);
148 let error = serde_json::to_string(&rp).expect("JSON serialization infallible; qed");
149
150 from_template(hyper::StatusCode::PAYLOAD_TOO_LARGE, error, JSON)
151 }
152
153 pub fn malformed() -> HttpResponse {
155 let rp = Response::new(ResponsePayload::<()>::error(ErrorCode::ParseError), Id::Null);
156 let error = serde_json::to_string(&rp).expect("JSON serialization infallible; qed");
157
158 from_template(hyper::StatusCode::BAD_REQUEST, error, JSON)
159 }
160
161 fn from_template(status: hyper::StatusCode, body: impl Into<HttpBody>, content_type: &'static str) -> HttpResponse {
163 HttpResponse::builder()
164 .status(status)
165 .header("content-type", hyper::header::HeaderValue::from_static(content_type))
166 .body(body.into())
167 .expect("Unable to parse response body for type conversion")
170 }
171
172 pub fn ok_response(body: impl Into<HttpBody>) -> HttpResponse {
174 from_template(hyper::StatusCode::OK, body, JSON)
175 }
176
177 pub fn unsupported_content_type() -> HttpResponse {
179 from_template(
180 hyper::StatusCode::UNSUPPORTED_MEDIA_TYPE,
181 "Supplied content type is not allowed. Content-Type: application/json is required\n",
182 TEXT,
183 )
184 }
185
186 pub fn too_many_requests() -> HttpResponse {
188 from_template(hyper::StatusCode::TOO_MANY_REQUESTS, "Too many connections. Please try again later.", TEXT)
189 }
190
191 pub fn denied() -> HttpResponse {
193 from_template(hyper::StatusCode::FORBIDDEN, HttpBody::default(), TEXT)
194 }
195}