mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-10 04:01:51 +00:00
Add first Server Sent Events API endpoint (#1107)
* Add Server Sent Events API endpoint * Support both event handlers as a transitory measure * Fix merge conflicts
This commit is contained in:
@@ -3,16 +3,22 @@ use crate::response_builder::ResponseBuilder;
|
||||
use crate::validator::get_state_for_epoch;
|
||||
use crate::{ApiError, ApiResult, UrlQuery};
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, StateSkipConfig};
|
||||
use hyper::{Body, Request};
|
||||
use bus::BusReader;
|
||||
use futures::executor::block_on;
|
||||
use hyper::body::Bytes;
|
||||
use hyper::{Body, Request, Response};
|
||||
use rest_types::{
|
||||
BlockResponse, CanonicalHeadResponse, Committee, HeadBeaconBlock, StateResponse,
|
||||
ValidatorRequest, ValidatorResponse,
|
||||
};
|
||||
use std::io::Write;
|
||||
use std::sync::Arc;
|
||||
use store::Store;
|
||||
|
||||
use slog::{error, Logger};
|
||||
use types::{
|
||||
AttesterSlashing, BeaconState, EthSpec, Hash256, ProposerSlashing, PublicKeyBytes,
|
||||
RelativeEpoch, Slot,
|
||||
RelativeEpoch, SignedBeaconBlockHash, Slot,
|
||||
};
|
||||
|
||||
/// HTTP handler to return a `BeaconBlock` at a given `root` or `slot`.
|
||||
@@ -122,6 +128,48 @@ pub fn get_block_root<T: BeaconChainTypes>(
|
||||
ResponseBuilder::new(&req)?.body(&root)
|
||||
}
|
||||
|
||||
fn make_sse_response_chunk(new_head_hash: SignedBeaconBlockHash) -> std::io::Result<Bytes> {
|
||||
let mut buffer = Vec::new();
|
||||
{
|
||||
let mut sse_message = uhttp_sse::SseMessage::new(&mut buffer);
|
||||
let untyped_hash: Hash256 = new_head_hash.into();
|
||||
write!(sse_message.data()?, "{:?}", untyped_hash)?;
|
||||
}
|
||||
let bytes: Bytes = buffer.into();
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
pub fn stream_forks<T: BeaconChainTypes>(
|
||||
log: Logger,
|
||||
mut events: BusReader<SignedBeaconBlockHash>,
|
||||
) -> ApiResult {
|
||||
let (mut sender, body) = Body::channel();
|
||||
std::thread::spawn(move || {
|
||||
while let Ok(new_head_hash) = events.recv() {
|
||||
let chunk = match make_sse_response_chunk(new_head_hash) {
|
||||
Ok(chunk) => chunk,
|
||||
Err(e) => {
|
||||
error!(log, "Failed to make SSE chunk"; "error" => e.to_string());
|
||||
sender.abort();
|
||||
break;
|
||||
}
|
||||
};
|
||||
if let Err(bytes) = block_on(sender.send_data(chunk)) {
|
||||
error!(log, "Couldn't stream piece {:?}", bytes);
|
||||
}
|
||||
}
|
||||
});
|
||||
let response = Response::builder()
|
||||
.status(200)
|
||||
.header("Content-Type", "text/event-stream")
|
||||
.header("Connection", "Keep-Alive")
|
||||
.header("Cache-Control", "no-cache")
|
||||
.header("Access-Control-Allow-Origin", "*")
|
||||
.body(body)
|
||||
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// HTTP handler to return the `Fork` of the current head.
|
||||
pub fn get_fork<T: BeaconChainTypes>(
|
||||
req: Request<Body>,
|
||||
|
||||
@@ -71,6 +71,12 @@ impl From<hyper::error::Error> for ApiError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ApiError {
|
||||
fn from(e: std::io::Error) -> ApiError {
|
||||
ApiError::ServerError(format!("IO error: {:?}", e))
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for ApiError {
|
||||
fn cause(&self) -> Option<&dyn StdError> {
|
||||
None
|
||||
|
||||
@@ -21,6 +21,7 @@ mod url_query;
|
||||
mod validator;
|
||||
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
use bus::Bus;
|
||||
use client_network::NetworkMessage;
|
||||
pub use config::ApiEncodingFormat;
|
||||
use error::{ApiError, ApiResult};
|
||||
@@ -30,12 +31,13 @@ use futures::future::TryFutureExt;
|
||||
use hyper::server::conn::AddrStream;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Request, Server};
|
||||
use parking_lot::Mutex;
|
||||
use slog::{info, warn};
|
||||
use std::net::SocketAddr;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
use types::SignedBeaconBlockHash;
|
||||
use url_query::UrlQuery;
|
||||
|
||||
pub use crate::helpers::parse_pubkey_bytes;
|
||||
@@ -58,6 +60,7 @@ pub fn start_server<T: BeaconChainTypes>(
|
||||
db_path: PathBuf,
|
||||
freezer_db_path: PathBuf,
|
||||
eth2_config: Eth2Config,
|
||||
events: Arc<Mutex<Bus<SignedBeaconBlockHash>>>,
|
||||
) -> Result<SocketAddr, hyper::Error> {
|
||||
let log = executor.log();
|
||||
let inner_log = log.clone();
|
||||
@@ -72,6 +75,7 @@ pub fn start_server<T: BeaconChainTypes>(
|
||||
let network_channel = network_info.network_chan.clone();
|
||||
let db_path = db_path.clone();
|
||||
let freezer_db_path = freezer_db_path.clone();
|
||||
let events = events.clone();
|
||||
|
||||
async move {
|
||||
Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
|
||||
@@ -84,6 +88,7 @@ pub fn start_server<T: BeaconChainTypes>(
|
||||
log.clone(),
|
||||
db_path.clone(),
|
||||
freezer_db_path.clone(),
|
||||
events.clone(),
|
||||
)
|
||||
}))
|
||||
}
|
||||
@@ -131,14 +136,3 @@ pub fn start_server<T: BeaconChainTypes>(
|
||||
|
||||
Ok(actual_listen_addr)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DBPath(PathBuf);
|
||||
|
||||
impl Deref for DBPath {
|
||||
type Target = PathBuf;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,16 @@ use crate::{
|
||||
spec, validator, NetworkChannel,
|
||||
};
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
use bus::Bus;
|
||||
use eth2_config::Eth2Config;
|
||||
use eth2_libp2p::NetworkGlobals;
|
||||
use hyper::{Body, Error, Method, Request, Response};
|
||||
use parking_lot::Mutex;
|
||||
use slog::debug;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use types::Slot;
|
||||
use types::{SignedBeaconBlockHash, Slot};
|
||||
|
||||
// Allowing more than 7 arguments.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -23,6 +25,7 @@ pub async fn route<T: BeaconChainTypes>(
|
||||
local_log: slog::Logger,
|
||||
db_path: PathBuf,
|
||||
freezer_db_path: PathBuf,
|
||||
events: Arc<Mutex<Bus<SignedBeaconBlockHash>>>,
|
||||
) -> Result<Response<Body>, Error> {
|
||||
metrics::inc_counter(&metrics::REQUEST_COUNT);
|
||||
let timer = metrics::start_timer(&metrics::REQUEST_RESPONSE_TIME);
|
||||
@@ -63,6 +66,10 @@ pub async fn route<T: BeaconChainTypes>(
|
||||
(&Method::GET, "/beacon/block") => beacon::get_block::<T>(req, beacon_chain),
|
||||
(&Method::GET, "/beacon/block_root") => beacon::get_block_root::<T>(req, beacon_chain),
|
||||
(&Method::GET, "/beacon/fork") => beacon::get_fork::<T>(req, beacon_chain),
|
||||
(&Method::GET, "/beacon/fork/stream") => {
|
||||
let reader = events.lock().add_rx();
|
||||
beacon::stream_forks::<T>(log, reader)
|
||||
}
|
||||
(&Method::GET, "/beacon/genesis_time") => beacon::get_genesis_time::<T>(req, beacon_chain),
|
||||
(&Method::GET, "/beacon/genesis_validators_root") => {
|
||||
beacon::get_genesis_validators_root::<T>(req, beacon_chain)
|
||||
|
||||
Reference in New Issue
Block a user