Add node/fork endpoint to HTTP API, tidy

This commit is contained in:
Paul Hauner 2019-05-27 11:34:22 +10:00
parent 705edf0e45
commit 255590ef3b
No known key found for this signature in database
GPG Key ID: 5E2CFF9B75FA63DF
6 changed files with 158 additions and 69 deletions

View File

@ -18,12 +18,16 @@ slot_clock = { path = "../../eth2/utils/slot_clock" }
protos = { path = "../../protos" } protos = { path = "../../protos" }
fork_choice = { path = "../../eth2/fork_choice" } fork_choice = { path = "../../eth2/fork_choice" }
grpcio = { version = "0.4", default-features = false, features = ["protobuf-codec"] } grpcio = { version = "0.4", default-features = false, features = ["protobuf-codec"] }
persistent = "^0.4"
protobuf = "2.0.2" protobuf = "2.0.2"
prometheus = "^0.6" prometheus = "^0.6"
clap = "2.32.0" clap = "2.32.0"
store = { path = "../store" } store = { path = "../store" }
dirs = "1.0.3" dirs = "1.0.3"
futures = "0.1.23" futures = "0.1.23"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
slog = "^2.2.3" slog = "^2.2.3"
slog-term = "^2.4.0" slog-term = "^2.4.0"
slog-async = "^2.3.0" slog-async = "^2.3.0"

View File

@ -0,0 +1,69 @@
use crate::key::BeaconChainKey;
use beacon_chain::{BeaconChain, BeaconChainTypes};
use iron::prelude::*;
use iron::{
headers::{CacheControl, CacheDirective, ContentType},
status::Status,
AfterMiddleware, Handler, IronResult, Request, Response,
};
use persistent::Read;
use router::Router;
use serde_json::json;
use std::sync::Arc;
pub fn build_handler<T: BeaconChainTypes + 'static>(
beacon_chain: Arc<BeaconChain<T>>,
) -> impl Handler {
let mut router = Router::new();
router.get("/node/fork", handle_fork::<T>, "fork");
let mut chain = Chain::new(router);
// Insert `BeaconChain` so it may be accessed in a request.
chain.link(Read::<BeaconChainKey<T>>::both(beacon_chain.clone()));
// Set the content-type headers.
chain.link_after(SetJsonContentType);
// Set the cache headers.
chain.link_after(SetCacheDirectives);
chain
}
/// Sets the `cache-control` headers on _all_ responses, unless they are already set.
struct SetCacheDirectives;
impl AfterMiddleware for SetCacheDirectives {
fn after(&self, _req: &mut Request, mut resp: Response) -> IronResult<Response> {
// This is run for every requests, AFTER all handlers have been executed
if resp.headers.get::<CacheControl>() == None {
resp.headers.set(CacheControl(vec![
CacheDirective::NoCache,
CacheDirective::NoStore,
]));
}
Ok(resp)
}
}
/// Sets the `content-type` headers on _all_ responses, unless they are already set.
struct SetJsonContentType;
impl AfterMiddleware for SetJsonContentType {
fn after(&self, _req: &mut Request, mut resp: Response) -> IronResult<Response> {
if resp.headers.get::<ContentType>() == None {
resp.headers.set(ContentType::json());
}
Ok(resp)
}
}
fn handle_fork<T: BeaconChainTypes + 'static>(req: &mut Request) -> IronResult<Response> {
// TODO: investigate unwrap - I'm _guessing_ we'll never hit it but we should check to be sure.
let beacon_chain = req.get::<Read<BeaconChainKey<T>>>().unwrap();
let response = json!({
"fork": beacon_chain.head().beacon_state.fork,
"chain_id": beacon_chain.spec.chain_id
});
Ok(Response::with((Status::Ok, response.to_string())))
}

View File

@ -0,0 +1,12 @@
use beacon_chain::{BeaconChain, BeaconChainTypes};
use iron::typemap::Key;
use std::marker::PhantomData;
use std::sync::Arc;
pub struct BeaconChainKey<T> {
_phantom: PhantomData<T>,
}
impl<T: BeaconChainTypes + 'static> Key for BeaconChainKey<T> {
type Value = Arc<BeaconChain<T>>;
}

View File

@ -1,11 +1,11 @@
mod prometheus_handler; mod api;
mod key;
mod metrics;
use beacon_chain::{BeaconChain, BeaconChainTypes}; use beacon_chain::{BeaconChain, BeaconChainTypes};
use futures::Future; use futures::Future;
use iron::prelude::*; use iron::prelude::*;
use iron::{status::Status, Handler, IronResult, Request, Response};
use network::NetworkMessage; use network::NetworkMessage;
use prometheus_handler::PrometheusHandler;
use router::Router; use router::Router;
use slog::{info, o, warn}; use slog::{info, o, warn};
use std::sync::Arc; use std::sync::Arc;
@ -26,32 +26,26 @@ impl Default for HttpServerConfig {
} }
} }
pub struct IndexHandler { /// Build the `iron` HTTP server, defining the core routes.
message: String,
}
impl Handler for IndexHandler {
fn handle(&self, _: &mut Request) -> IronResult<Response> {
Ok(Response::with((Status::Ok, self.message.clone())))
}
}
pub fn create_iron_http_server<T: BeaconChainTypes + 'static>( pub fn create_iron_http_server<T: BeaconChainTypes + 'static>(
beacon_chain: Arc<BeaconChain<T>>, beacon_chain: Arc<BeaconChain<T>>,
) -> Iron<Router> { ) -> Iron<Router> {
let index_handler = IndexHandler {
message: "Hello world".to_string(),
};
let prom_handler = PrometheusHandler {
beacon_chain: beacon_chain,
};
let mut router = Router::new(); let mut router = Router::new();
router.get("/", index_handler, "index");
router.get("/metrics", prom_handler, "metrics"); // A `GET` request to `/metrics` is handled by the `metrics` module.
router.get(
"/metrics",
metrics::build_handler(beacon_chain.clone()),
"metrics",
);
// Any request to all other endpoints is handled by the `api` module.
router.any("/*", api::build_handler(beacon_chain.clone()), "api");
Iron::new(router) Iron::new(router)
} }
/// Start the HTTP service on the tokio `TaskExecutor`.
pub fn start_service<T: BeaconChainTypes + 'static>( pub fn start_service<T: BeaconChainTypes + 'static>(
config: &HttpServerConfig, config: &HttpServerConfig,
executor: &TaskExecutor, executor: &TaskExecutor,

View File

@ -0,0 +1,57 @@
use crate::key::BeaconChainKey;
use beacon_chain::{BeaconChain, BeaconChainTypes};
use iron::prelude::*;
use iron::{status::Status, Handler, IronResult, Request, Response};
use persistent::Read;
use prometheus::{Encoder, IntCounter, Opts, Registry, TextEncoder};
use slot_clock::SlotClock;
use std::sync::Arc;
use types::Slot;
pub fn build_handler<T: BeaconChainTypes + 'static>(
beacon_chain: Arc<BeaconChain<T>>,
) -> impl Handler {
let mut chain = Chain::new(handle_metrics::<T>);
chain.link(Read::<BeaconChainKey<T>>::both(beacon_chain));
chain
}
/// Handle a request for Prometheus metrics.
///
/// Returns a text string containing all metrics.
fn handle_metrics<T: BeaconChainTypes + 'static>(req: &mut Request) -> IronResult<Response> {
let beacon_chain = req.get::<Read<BeaconChainKey<T>>>().unwrap();
let r = Registry::new();
let present_slot = if let Ok(Some(slot)) = beacon_chain.slot_clock.present_slot() {
slot
} else {
Slot::new(0)
};
register_and_set_slot(
&r,
"present_slot",
"direct_slock_clock_reading",
present_slot,
);
// Gather the metrics.
let mut buffer = vec![];
let encoder = TextEncoder::new();
let metric_families = r.gather();
encoder.encode(&metric_families, &mut buffer).unwrap();
let prom_string = String::from_utf8(buffer).unwrap();
Ok(Response::with((Status::Ok, prom_string)))
}
fn register_and_set_slot(registry: &Registry, name: &str, help: &str, slot: Slot) {
let counter_opts = Opts::new(name, help);
let counter = IntCounter::with_opts(counter_opts).unwrap();
registry.register(Box::new(counter.clone())).unwrap();
counter.inc_by(slot.as_u64() as i64);
}

View File

@ -1,47 +0,0 @@
use beacon_chain::{BeaconChain, BeaconChainTypes};
use iron::{status::Status, Handler, IronResult, Request, Response};
use prometheus::{Encoder, IntCounter, Opts, Registry, TextEncoder};
use slot_clock::SlotClock;
use std::sync::Arc;
use types::Slot;
pub struct PrometheusHandler<T: BeaconChainTypes> {
pub beacon_chain: Arc<BeaconChain<T>>,
}
impl<T: BeaconChainTypes> PrometheusHandler<T> {}
impl<T: BeaconChainTypes + 'static> Handler for PrometheusHandler<T> {
fn handle(&self, _: &mut Request) -> IronResult<Response> {
let r = Registry::new();
let present_slot = if let Ok(Some(slot)) = self.beacon_chain.slot_clock.present_slot() {
slot
} else {
Slot::new(0)
};
register_and_set_slot(
&r,
"present_slot",
"direct_slock_clock_reading",
present_slot,
);
// Gather the metrics.
let mut buffer = vec![];
let encoder = TextEncoder::new();
let metric_families = r.gather();
encoder.encode(&metric_families, &mut buffer).unwrap();
let prom_string = String::from_utf8(buffer).unwrap();
Ok(Response::with((Status::Ok, prom_string)))
}
}
fn register_and_set_slot(registry: &Registry, name: &str, help: &str, slot: Slot) {
let counter_opts = Opts::new(name, help);
let counter = IntCounter::with_opts(counter_opts).unwrap();
registry.register(Box::new(counter.clone())).unwrap();
counter.inc_by(slot.as_u64() as i64);
}