From 255590ef3bca45d8d2306b707a22c58d01858c63 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 27 May 2019 11:34:22 +1000 Subject: [PATCH] Add `node/fork` endpoint to HTTP API, tidy --- beacon_node/http_server/Cargo.toml | 4 ++ beacon_node/http_server/src/api.rs | 69 +++++++++++++++++++ beacon_node/http_server/src/key.rs | 12 ++++ beacon_node/http_server/src/lib.rs | 38 +++++----- beacon_node/http_server/src/metrics.rs | 57 +++++++++++++++ .../http_server/src/prometheus_handler.rs | 47 ------------- 6 files changed, 158 insertions(+), 69 deletions(-) create mode 100644 beacon_node/http_server/src/api.rs create mode 100644 beacon_node/http_server/src/key.rs create mode 100644 beacon_node/http_server/src/metrics.rs delete mode 100644 beacon_node/http_server/src/prometheus_handler.rs diff --git a/beacon_node/http_server/Cargo.toml b/beacon_node/http_server/Cargo.toml index 6f4579d17..fb8bf9f4b 100644 --- a/beacon_node/http_server/Cargo.toml +++ b/beacon_node/http_server/Cargo.toml @@ -18,12 +18,16 @@ slot_clock = { path = "../../eth2/utils/slot_clock" } protos = { path = "../../protos" } fork_choice = { path = "../../eth2/fork_choice" } grpcio = { version = "0.4", default-features = false, features = ["protobuf-codec"] } +persistent = "^0.4" protobuf = "2.0.2" prometheus = "^0.6" clap = "2.32.0" store = { path = "../store" } dirs = "1.0.3" futures = "0.1.23" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" slog = "^2.2.3" slog-term = "^2.4.0" slog-async = "^2.3.0" diff --git a/beacon_node/http_server/src/api.rs b/beacon_node/http_server/src/api.rs new file mode 100644 index 000000000..c89cacd9a --- /dev/null +++ b/beacon_node/http_server/src/api.rs @@ -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( + beacon_chain: Arc>, +) -> impl Handler { + let mut router = Router::new(); + + router.get("/node/fork", handle_fork::, "fork"); + + let mut chain = Chain::new(router); + + // Insert `BeaconChain` so it may be accessed in a request. + chain.link(Read::>::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 { + // This is run for every requests, AFTER all handlers have been executed + if resp.headers.get::() == 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 { + if resp.headers.get::() == None { + resp.headers.set(ContentType::json()); + } + Ok(resp) + } +} + +fn handle_fork(req: &mut Request) -> IronResult { + // TODO: investigate unwrap - I'm _guessing_ we'll never hit it but we should check to be sure. + let beacon_chain = req.get::>>().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()))) +} diff --git a/beacon_node/http_server/src/key.rs b/beacon_node/http_server/src/key.rs new file mode 100644 index 000000000..2d27ce9f0 --- /dev/null +++ b/beacon_node/http_server/src/key.rs @@ -0,0 +1,12 @@ +use beacon_chain::{BeaconChain, BeaconChainTypes}; +use iron::typemap::Key; +use std::marker::PhantomData; +use std::sync::Arc; + +pub struct BeaconChainKey { + _phantom: PhantomData, +} + +impl Key for BeaconChainKey { + type Value = Arc>; +} diff --git a/beacon_node/http_server/src/lib.rs b/beacon_node/http_server/src/lib.rs index 6bc69e72d..8f6378e9a 100644 --- a/beacon_node/http_server/src/lib.rs +++ b/beacon_node/http_server/src/lib.rs @@ -1,11 +1,11 @@ -mod prometheus_handler; +mod api; +mod key; +mod metrics; use beacon_chain::{BeaconChain, BeaconChainTypes}; use futures::Future; use iron::prelude::*; -use iron::{status::Status, Handler, IronResult, Request, Response}; use network::NetworkMessage; -use prometheus_handler::PrometheusHandler; use router::Router; use slog::{info, o, warn}; use std::sync::Arc; @@ -26,32 +26,26 @@ impl Default for HttpServerConfig { } } -pub struct IndexHandler { - message: String, -} - -impl Handler for IndexHandler { - fn handle(&self, _: &mut Request) -> IronResult { - Ok(Response::with((Status::Ok, self.message.clone()))) - } -} - +/// Build the `iron` HTTP server, defining the core routes. pub fn create_iron_http_server( beacon_chain: Arc>, ) -> Iron { - let index_handler = IndexHandler { - message: "Hello world".to_string(), - }; - let prom_handler = PrometheusHandler { - beacon_chain: beacon_chain, - }; - 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) } +/// Start the HTTP service on the tokio `TaskExecutor`. pub fn start_service( config: &HttpServerConfig, executor: &TaskExecutor, diff --git a/beacon_node/http_server/src/metrics.rs b/beacon_node/http_server/src/metrics.rs new file mode 100644 index 000000000..57fa70623 --- /dev/null +++ b/beacon_node/http_server/src/metrics.rs @@ -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( + beacon_chain: Arc>, +) -> impl Handler { + let mut chain = Chain::new(handle_metrics::); + + chain.link(Read::>::both(beacon_chain)); + + chain +} + +/// Handle a request for Prometheus metrics. +/// +/// Returns a text string containing all metrics. +fn handle_metrics(req: &mut Request) -> IronResult { + let beacon_chain = req.get::>>().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); +} diff --git a/beacon_node/http_server/src/prometheus_handler.rs b/beacon_node/http_server/src/prometheus_handler.rs deleted file mode 100644 index 60f56084c..000000000 --- a/beacon_node/http_server/src/prometheus_handler.rs +++ /dev/null @@ -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 { - pub beacon_chain: Arc>, -} - -impl PrometheusHandler {} - -impl Handler for PrometheusHandler { - fn handle(&self, _: &mut Request) -> IronResult { - 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); -}