solana/core/src/rpc_service.rs

201 lines
6.6 KiB
Rust
Raw Normal View History

2019-02-17 09:29:08 -08:00
//! The `rpc_service` module implements the Solana JSON RPC service.
2019-03-18 14:18:43 -07:00
use crate::bank_forks::BankForks;
2019-02-17 09:29:08 -08:00
use crate::cluster_info::ClusterInfo;
use crate::rpc::*;
use crate::service::Service;
use crate::storage_stage::StorageState;
use jsonrpc_core::MetaIoHandler;
2019-07-30 22:43:47 -07:00
use jsonrpc_http_server::{
hyper, AccessControlAllowOrigin, DomainsValidation, RequestMiddleware, RequestMiddlewareAction,
ServerBuilder,
};
2019-02-17 09:29:08 -08:00
use std::net::SocketAddr;
2019-07-30 22:43:47 -07:00
use std::path::{Path, PathBuf};
2019-02-17 09:29:08 -08:00
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock};
use std::thread::{self, sleep, Builder, JoinHandle};
use std::time::Duration;
2019-07-30 22:43:47 -07:00
use tokio::prelude::Future;
2019-02-17 09:29:08 -08:00
pub struct JsonRpcService {
thread_hdl: JoinHandle<()>,
#[cfg(test)]
pub request_processor: Arc<RwLock<JsonRpcRequestProcessor>>, // Used only by test_rpc_new()...
2019-02-17 09:29:08 -08:00
}
2019-07-30 22:43:47 -07:00
#[derive(Default)]
struct RpcRequestMiddleware {
ledger_path: PathBuf,
}
impl RpcRequestMiddleware {
pub fn new(ledger_path: PathBuf) -> Self {
Self { ledger_path }
}
fn not_found() -> hyper::Response<hyper::Body> {
hyper::Response::builder()
.status(hyper::StatusCode::NOT_FOUND)
.body(hyper::Body::empty())
.unwrap()
}
fn internal_server_error() -> hyper::Response<hyper::Body> {
hyper::Response::builder()
.status(hyper::StatusCode::INTERNAL_SERVER_ERROR)
.body(hyper::Body::empty())
.unwrap()
}
fn get(&self, filename: &str) -> RequestMiddlewareAction {
let filename = self.ledger_path.join(filename);
RequestMiddlewareAction::Respond {
should_validate_hosts: true,
response: Box::new(
tokio_fs::file::File::open(filename)
.and_then(|file| {
let buf: Vec<u8> = Vec::new();
tokio_io::io::read_to_end(file, buf)
.and_then(|item| Ok(hyper::Response::new(item.1.into())))
.or_else(|_| Ok(RpcRequestMiddleware::internal_server_error()))
})
.or_else(|_| Ok(RpcRequestMiddleware::not_found())),
),
}
}
}
impl RequestMiddleware for RpcRequestMiddleware {
fn on_request(&self, request: hyper::Request<hyper::Body>) -> RequestMiddlewareAction {
trace!("request uri: {}", request.uri());
match request.uri().path() {
"/snapshot.tar.bz2" => self.get("snapshot.tar.bz2"),
"/genesis.tar.bz2" => self.get("genesis.tar.bz2"),
2019-07-30 22:43:47 -07:00
_ => RequestMiddlewareAction::Proceed {
should_continue_on_invalid_cors: false,
request,
},
}
}
}
2019-02-17 09:29:08 -08:00
impl JsonRpcService {
pub fn new(
cluster_info: &Arc<RwLock<ClusterInfo>>,
rpc_addr: SocketAddr,
storage_state: StorageState,
config: JsonRpcConfig,
2019-03-18 14:18:43 -07:00
bank_forks: Arc<RwLock<BankForks>>,
2019-07-30 22:43:47 -07:00
ledger_path: &Path,
2019-03-04 16:21:33 -08:00
exit: &Arc<AtomicBool>,
2019-02-17 09:29:08 -08:00
) -> Self {
info!("rpc bound to {:?}", rpc_addr);
2019-03-06 09:26:12 -08:00
info!("rpc configuration: {:?}", config);
let request_processor = Arc::new(RwLock::new(JsonRpcRequestProcessor::new(
storage_state,
config,
2019-03-18 14:18:43 -07:00
bank_forks,
2019-03-04 16:21:33 -08:00
exit,
)));
2019-02-17 09:29:08 -08:00
let request_processor_ = request_processor.clone();
let cluster_info = cluster_info.clone();
2019-02-17 09:29:08 -08:00
let exit_ = exit.clone();
2019-07-30 22:43:47 -07:00
let ledger_path = ledger_path.to_path_buf();
2019-02-17 09:29:08 -08:00
let thread_hdl = Builder::new()
.name("solana-jsonrpc".to_string())
.spawn(move || {
let mut io = MetaIoHandler::default();
let rpc = RpcSolImpl;
io.extend_with(rpc.to_delegate());
let server =
ServerBuilder::with_meta_extractor(io, move |_req: &hyper::Request<hyper::Body>| Meta {
request_processor: request_processor_.clone(),
cluster_info: cluster_info.clone(),
2019-02-17 09:29:08 -08:00
}).threads(4)
.cors(DomainsValidation::AllowOnly(vec![
AccessControlAllowOrigin::Any,
]))
2019-07-30 22:43:47 -07:00
.request_middleware(RpcRequestMiddleware::new(ledger_path))
2019-02-17 09:29:08 -08:00
.start_http(&rpc_addr);
if let Err(e) = server {
warn!("JSON RPC service unavailable error: {:?}. \nAlso, check that port {} is not already in use by another application", e, rpc_addr.port());
return;
}
2019-07-30 22:43:47 -07:00
2019-02-17 09:29:08 -08:00
while !exit_.load(Ordering::Relaxed) {
sleep(Duration::from_millis(100));
}
server.unwrap().close();
})
.unwrap();
Self {
thread_hdl,
#[cfg(test)]
2019-02-17 09:29:08 -08:00
request_processor,
}
}
}
impl Service for JsonRpcService {
type JoinReturnType = ();
fn join(self) -> thread::Result<()> {
self.thread_hdl.join()
}
}
#[cfg(test)]
mod tests {
use super::*;
2019-03-08 17:23:07 -08:00
use crate::contact_info::ContactInfo;
use crate::genesis_utils::{create_genesis_block, GenesisBlockInfo};
2019-02-20 21:23:44 -08:00
use solana_runtime::bank::Bank;
2019-02-17 09:29:08 -08:00
use solana_sdk::signature::KeypairUtil;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
#[test]
fn test_rpc_new() {
let GenesisBlockInfo {
genesis_block,
mint_keypair,
..
} = create_genesis_block(10_000);
let exit = Arc::new(AtomicBool::new(false));
let bank = Bank::new(&genesis_block);
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new_with_invalid_keypair(
2019-03-08 17:23:07 -08:00
ContactInfo::default(),
)));
2019-02-17 09:29:08 -08:00
let rpc_addr = SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
solana_netutil::find_available_port_in_range((10000, 65535)).unwrap(),
);
2019-03-18 14:18:43 -07:00
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank.slot(), bank)));
let rpc_service = JsonRpcService::new(
&cluster_info,
rpc_addr,
StorageState::default(),
JsonRpcConfig::default(),
2019-03-18 14:18:43 -07:00
bank_forks,
2019-07-30 22:43:47 -07:00
&PathBuf::from("farf"),
2019-03-04 16:21:33 -08:00
&exit,
);
2019-02-17 09:29:08 -08:00
let thread = rpc_service.thread_hdl.thread();
assert_eq!(thread.name().unwrap(), "solana-jsonrpc");
assert_eq!(
10_000,
rpc_service
.request_processor
.read()
.unwrap()
.get_balance(&mint_keypair.pubkey())
2019-02-17 09:29:08 -08:00
);
2019-03-04 16:21:33 -08:00
exit.store(true, Ordering::Relaxed);
rpc_service.join().unwrap();
2019-02-17 09:29:08 -08:00
}
}