pub struct ServerBuilder<HttpMiddleware, RpcMiddleware> { /* private fields */ }
Expand description
Builder to configure and create a JSON-RPC server.
Implementations§
Source§impl Builder<Identity, Identity>
impl Builder<Identity, Identity>
Sourcepub fn with_config(config: ServerConfig) -> Self
pub fn with_config(config: ServerConfig) -> Self
Create a server builder with the given ServerConfig
.
Source§impl<HttpMiddleware, RpcMiddleware> Builder<HttpMiddleware, RpcMiddleware>
impl<HttpMiddleware, RpcMiddleware> Builder<HttpMiddleware, RpcMiddleware>
Sourcepub fn set_config(self, cfg: ServerConfig) -> Self
pub fn set_config(self, cfg: ServerConfig) -> Self
Configure the ServerConfig
.
Sourcepub fn set_rpc_middleware<T>(
self,
rpc_middleware: RpcServiceBuilder<T>,
) -> Builder<HttpMiddleware, T>
pub fn set_rpc_middleware<T>( self, rpc_middleware: RpcServiceBuilder<T>, ) -> Builder<HttpMiddleware, T>
Enable middleware that is invoked on every JSON-RPC call.
The middleware itself is very similar to the tower middleware
but
it has a different service trait which takes &self instead &mut self
which means that you can’t use built-in middleware from tower.
Another consequence of &self
is that you must wrap any of the middleware state in
a type which is Send and provides interior mutability such Arc<Mutex>
.
The builder itself exposes a similar API as the tower::ServiceBuilder
where it is possible to compose layers to the middleware.
To add a middleware crate::middleware::rpc::RpcServiceBuilder
exposes a few different layer APIs that
is wrapped on top of the tower::ServiceBuilder
.
When the server is started these layers are wrapped in the crate::middleware::rpc::RpcService
and
that’s why the service APIs is not exposed.
use std::{time::Instant, net::SocketAddr, sync::Arc};
use std::sync::atomic::{Ordering, AtomicUsize};
use jsonrpsee_server::middleware::rpc::{RpcService, RpcServiceBuilder, RpcServiceT, MethodResponse, Notification, Request, Batch};
use jsonrpsee_server::ServerBuilder;
#[derive(Clone)]
struct MyMiddleware<S> {
service: S,
count: Arc<AtomicUsize>,
}
impl<S> RpcServiceT for MyMiddleware<S>
where S: RpcServiceT + Send + Sync + Clone + 'static,
{
type MethodResponse = S::MethodResponse;
type BatchResponse = S::BatchResponse;
type NotificationResponse = S::NotificationResponse;
fn call<'a>(&self, req: Request<'a>) -> impl Future<Output = Self::MethodResponse> + Send + 'a {
tracing::info!("MyMiddleware processed call {}", req.method);
let count = self.count.clone();
let service = self.service.clone();
async move {
let rp = service.call(req).await;
// Modify the state.
count.fetch_add(1, Ordering::Relaxed);
rp
}
}
fn batch<'a>(&self, batch: Batch<'a>) -> impl Future<Output = Self::BatchResponse> + Send + 'a {
self.service.batch(batch)
}
fn notification<'a>(&self, notif: Notification<'a>) -> impl Future<Output = Self::NotificationResponse> + Send + 'a {
self.service.notification(notif)
}
}
// Create a state per connection
// NOTE: The service type can be omitted once `start` is called on the server.
let m = RpcServiceBuilder::new().layer_fn(move |service: ()| MyMiddleware { service, count: Arc::new(AtomicUsize::new(0)) });
let builder = ServerBuilder::default().set_rpc_middleware(m);
Sourcepub fn set_http_middleware<T>(
self,
http_middleware: ServiceBuilder<T>,
) -> Builder<T, RpcMiddleware>
pub fn set_http_middleware<T>( self, http_middleware: ServiceBuilder<T>, ) -> Builder<T, RpcMiddleware>
Configure a custom tower::ServiceBuilder
middleware for composing layers to be applied to the RPC service.
Default: No tower layers are applied to the RPC service.
§Examples
use std::time::Duration;
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
let builder = tower::ServiceBuilder::new().timeout(Duration::from_secs(2));
let server = jsonrpsee_server::ServerBuilder::new()
.set_http_middleware(builder)
.build("127.0.0.1:0".parse::<SocketAddr>().unwrap())
.await
.unwrap();
}
Sourcepub fn to_service_builder(
self,
) -> TowerServiceBuilder<RpcMiddleware, HttpMiddleware>
pub fn to_service_builder( self, ) -> TowerServiceBuilder<RpcMiddleware, HttpMiddleware>
Convert the server builder to a TowerServiceBuilder
.
This can be used to utilize the TowerService
from jsonrpsee.
§Examples
use jsonrpsee_server::{Methods, ServerConfig, ServerHandle, ws, stop_channel, serve_with_graceful_shutdown};
use tower::Service;
use std::{error::Error as StdError, net::SocketAddr};
use futures_util::future::{self, Either};
use hyper_util::rt::{TokioIo, TokioExecutor};
fn run_server() -> ServerHandle {
let (stop_handle, server_handle) = stop_channel();
let svc_builder = jsonrpsee_server::Server::builder()
.set_config(ServerConfig::builder().max_connections(33).build())
.to_service_builder();
let methods = Methods::new();
let stop_handle = stop_handle.clone();
tokio::spawn(async move {
let listener = tokio::net::TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0))).await.unwrap();
loop {
// The `tokio::select!` macro is used to wait for either of the
// listeners to accept a new connection or for the server to be
// stopped.
let (sock, remote_addr) = tokio::select! {
res = listener.accept() => {
match res {
Ok(sock) => sock,
Err(e) => {
tracing::error!("failed to accept v4 connection: {:?}", e);
continue;
}
}
}
_ = stop_handle.clone().shutdown() => break,
};
let stop_handle2 = stop_handle.clone();
let svc_builder2 = svc_builder.clone();
let methods2 = methods.clone();
let svc = tower::service_fn(move |req| {
let stop_handle = stop_handle2.clone();
let svc_builder = svc_builder2.clone();
let methods = methods2.clone();
let mut svc = svc_builder.build(methods, stop_handle.clone());
// It's not possible to know whether the websocket upgrade handshake failed or not here.
let is_websocket = ws::is_upgrade_request(&req);
if is_websocket {
println!("websocket")
} else {
println!("http")
}
// Call the jsonrpsee service which
// may upgrade it to a WebSocket connection
// or treat it as "ordinary HTTP request".
async move { svc.call(req).await }
});
// Upgrade the connection to a HTTP service with graceful shutdown.
tokio::spawn(serve_with_graceful_shutdown(sock, svc, stop_handle.clone().shutdown()));
}
});
server_handle
}
Sourcepub async fn build(
self,
addrs: impl ToSocketAddrs,
) -> Result<Server<HttpMiddleware, RpcMiddleware>>
pub async fn build( self, addrs: impl ToSocketAddrs, ) -> Result<Server<HttpMiddleware, RpcMiddleware>>
Finalize the configuration of the server. Consumes the Builder
.
#[tokio::main]
async fn main() {
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let occupied_addr = listener.local_addr().unwrap();
let addrs: &[std::net::SocketAddr] = &[
occupied_addr,
"127.0.0.1:0".parse().unwrap(),
];
assert!(jsonrpsee_server::ServerBuilder::default().build(occupied_addr).await.is_err());
assert!(jsonrpsee_server::ServerBuilder::default().build(addrs).await.is_ok());
}
Sourcepub fn build_from_tcp(
self,
listener: impl Into<StdTcpListener>,
) -> Result<Server<HttpMiddleware, RpcMiddleware>>
pub fn build_from_tcp( self, listener: impl Into<StdTcpListener>, ) -> Result<Server<HttpMiddleware, RpcMiddleware>>
Finalizes the configuration of the server with customized TCP settings on the socket.
use jsonrpsee_server::Server;
use socket2::{Domain, Socket, Type};
use std::time::Duration;
#[tokio::main]
async fn main() {
let addr = "127.0.0.1:0".parse().unwrap();
let domain = Domain::for_address(addr);
let socket = Socket::new(domain, Type::STREAM, None).unwrap();
socket.set_nonblocking(true).unwrap();
let address = addr.into();
socket.bind(&address).unwrap();
socket.listen(4096).unwrap();
let server = Server::builder().build_from_tcp(socket).unwrap();
}