casper_json_rpc/
lib.rs

1//! # casper-json-rpc
2//!
3//! A library suitable for use as the framework for a JSON-RPC server.
4//!
5//! # Usage
6//!
7//! Normally usage will involve two steps:
8//!   * construct a set of request handlers using a [`RequestHandlersBuilder`]
9//!   * call [`casper_json_rpc::route`](route) to construct a boxed warp filter ready to be passed
10//!     to [`warp::service`](https://docs.rs/warp/latest/warp/fn.service.html) for example
11//!
12//! # Example
13//!
14//! ```no_run
15//! use casper_json_rpc::{Error, Params, RequestHandlersBuilder};
16//! use std::{convert::Infallible, sync::Arc};
17//!
18//! # #[allow(unused)]
19//! async fn get(params: Option<Params>) -> Result<String, Error> {
20//!     // * parse params or return `ReservedErrorCode::InvalidParams` error
21//!     // * handle request and return result
22//!     Ok("got it".to_string())
23//! }
24//!
25//! # #[allow(unused)]
26//! async fn put(params: Option<Params>, other_input: &str) -> Result<String, Error> {
27//!     Ok(other_input.to_string())
28//! }
29//!
30//! #[tokio::main]
31//! async fn main() {
32//!     // Register handlers for methods "get" and "put".
33//!     let mut handlers = RequestHandlersBuilder::new();
34//!     handlers.register_handler("get", Arc::new(get));
35//!     let put_handler = move |params| async move { put(params, "other input").await };
36//!     handlers.register_handler("put", Arc::new(put_handler));
37//!     let handlers = handlers.build();
38//!
39//!     // Get the new route.
40//!     let path = "rpc";
41//!     let max_body_bytes = 1024;
42//!     let allow_unknown_fields = false;
43//!     let route = casper_json_rpc::route(path, max_body_bytes, handlers, allow_unknown_fields);
44//!
45//!     // Convert it into a `Service` and run it.
46//!     let make_svc = hyper::service::make_service_fn(move |_| {
47//!         let svc = warp::service(route.clone());
48//!         async move { Ok::<_, Infallible>(svc.clone()) }
49//!     });
50//!
51//!     hyper::Server::bind(&([127, 0, 0, 1], 3030).into())
52//!         .serve(make_svc)
53//!         .await
54//!         .unwrap();
55//! }
56//! ```
57//!
58//! # Errors
59//!
60//! To return a JSON-RPC response indicating an error, use [`Error::new`].  Most error conditions
61//! which require returning a reserved error are already handled in the provided warp filters.  The
62//! only exception is [`ReservedErrorCode::InvalidParams`] which should be returned by any RPC
63//! handler which deems the provided `params: Option<Params>` to be invalid for any reason.
64//!
65//! Generally a set of custom error codes should be provided.  These should all implement
66//! [`ErrorCodeT`].
67
68#![doc(html_root_url = "https://docs.rs/casper-json-rpc/1.1.0")]
69#![doc(
70    html_favicon_url = "https://raw.githubusercontent.com/casper-network/casper-node/master/images/CasperLabs_Logo_Favicon_RGB_50px.png",
71    html_logo_url = "https://raw.githubusercontent.com/casper-network/casper-node/master/images/CasperLabs_Logo_Symbol_RGB.png",
72    test(attr(deny(warnings)))
73)]
74#![warn(
75    missing_docs,
76    trivial_casts,
77    trivial_numeric_casts,
78    unused_qualifications
79)]
80
81mod error;
82pub mod filters;
83mod rejections;
84mod request;
85mod request_handlers;
86mod response;
87
88use http::{header::CONTENT_TYPE, Method};
89use warp::{filters::BoxedFilter, Filter, Reply};
90
91pub use error::{Error, ErrorCodeT, ReservedErrorCode};
92pub use request::Params;
93pub use request_handlers::{RequestHandlers, RequestHandlersBuilder};
94pub use response::Response;
95
96const JSON_RPC_VERSION: &str = "2.0";
97
98/// Specifies the CORS origin
99pub enum CorsOrigin {
100    /// Any (*) origin is allowed.
101    Any,
102    /// Only the specified origin is allowed.
103    Specified(String),
104}
105
106/// Constructs a set of warp filters suitable for use in a JSON-RPC server.
107///
108/// `path` specifies the exact HTTP path for JSON-RPC requests, e.g. "rpc" will match requests on
109/// exactly "/rpc", and not "/rpc/other".
110///
111/// `max_body_bytes` sets an upper limit for the number of bytes in the HTTP request body.  For
112/// further details, see
113/// [`warp::filters::body::content_length_limit`](https://docs.rs/warp/latest/warp/filters/body/fn.content_length_limit.html).
114///
115/// `handlers` is the map of functions to which incoming requests will be dispatched.  These are
116/// keyed by the JSON-RPC request's "method".
117///
118/// If `allow_unknown_fields` is `false`, requests with unknown fields will cause the server to
119/// respond with an error.
120///
121/// For further details, see the docs for the [`filters`] functions.
122pub fn route<P: AsRef<str>>(
123    path: P,
124    max_body_bytes: u32,
125    handlers: RequestHandlers,
126    allow_unknown_fields: bool,
127) -> BoxedFilter<(impl Reply,)> {
128    filters::base_filter(path, max_body_bytes)
129        .and(filters::main_filter(handlers, allow_unknown_fields))
130        .recover(filters::handle_rejection)
131        .boxed()
132}
133
134/// Constructs a set of warp filters suitable for use in a JSON-RPC server.
135///
136/// `path` specifies the exact HTTP path for JSON-RPC requests, e.g. "rpc" will match requests on
137/// exactly "/rpc", and not "/rpc/other".
138///
139/// `max_body_bytes` sets an upper limit for the number of bytes in the HTTP request body.  For
140/// further details, see
141/// [`warp::filters::body::content_length_limit`](https://docs.rs/warp/latest/warp/filters/body/fn.content_length_limit.html).
142///
143/// `handlers` is the map of functions to which incoming requests will be dispatched.  These are
144/// keyed by the JSON-RPC request's "method".
145///
146/// If `allow_unknown_fields` is `false`, requests with unknown fields will cause the server to
147/// respond with an error.
148///
149/// Note that this is a convenience function combining the lower-level functions in [`filters`]
150/// along with [a warp CORS filter](https://docs.rs/warp/latest/warp/filters/cors/index.html) which
151///   * allows any origin or specified origin
152///   * allows "content-type" as a header
153///   * allows the method "POST"
154///
155/// For further details, see the docs for the [`filters`] functions.
156pub fn route_with_cors<P: AsRef<str>>(
157    path: P,
158    max_body_bytes: u32,
159    handlers: RequestHandlers,
160    allow_unknown_fields: bool,
161    cors_header: &CorsOrigin,
162) -> BoxedFilter<(impl Reply,)> {
163    filters::base_filter(path, max_body_bytes)
164        .and(filters::main_filter(handlers, allow_unknown_fields))
165        .recover(filters::handle_rejection)
166        .with(match cors_header {
167            CorsOrigin::Any => warp::cors()
168                .allow_any_origin()
169                .allow_header(CONTENT_TYPE)
170                .allow_method(Method::POST),
171            CorsOrigin::Specified(origin) => warp::cors()
172                .allow_origin(origin.as_str())
173                .allow_header(CONTENT_TYPE)
174                .allow_method(Method::POST),
175        })
176        .boxed()
177}