diff --git a/beacon_node/rest_api/Cargo.toml b/beacon_node/rest_api/Cargo.toml index 057de4f94..863ea04da 100644 --- a/beacon_node/rest_api/Cargo.toml +++ b/beacon_node/rest_api/Cargo.toml @@ -14,9 +14,12 @@ store = { path = "../store" } version = { path = "../version" } serde = { version = "1.0", features = ["derive"] } serde_json = "^1.0" +serde_yaml = "0.8" slog = "^2.2.3" slog-term = "^2.4.0" slog-async = "^2.3.0" +eth2_ssz = { path = "../../eth2/utils/ssz" } +eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } state_processing = { path = "../../eth2/state_processing" } types = { path = "../../eth2/types" } clap = "2.32.0" diff --git a/beacon_node/rest_api/src/beacon.rs b/beacon_node/rest_api/src/beacon.rs index 1c66a2819..85f20294d 100644 --- a/beacon_node/rest_api/src/beacon.rs +++ b/beacon_node/rest_api/src/beacon.rs @@ -1,8 +1,9 @@ -use super::{success_response, ApiResult}; +use super::{success_response, ApiResult, ResponseBuilder}; use crate::{helpers::*, ApiError, UrlQuery}; use beacon_chain::{BeaconChain, BeaconChainTypes}; use hyper::{Body, Request}; use serde::Serialize; +use ssz_derive::Encode; use std::sync::Arc; use store::Store; use types::{BeaconBlock, BeaconState, EthSpec, Hash256, Slot}; @@ -33,7 +34,7 @@ pub fn get_head(req: Request) -> ApiResult Ok(success_response(Body::from(json))) } -#[derive(Serialize)] +#[derive(Serialize, Encode)] #[serde(bound = "T: EthSpec")] pub struct BlockResponse { pub root: Hash256, @@ -77,11 +78,7 @@ pub fn get_block(req: Request) -> ApiResult beacon_block: block, }; - let json: String = serde_json::to_string(&response).map_err(|e| { - ApiError::ServerError(format!("Unable to serialize BlockResponse: {:?}", e)) - })?; - - Ok(success_response(Body::from(json))) + ResponseBuilder::new(&req).body(&response) } /// HTTP handler to return a `BeaconBlock` root at a given `slot`. @@ -104,7 +101,7 @@ pub fn get_block_root(req: Request) -> ApiR Ok(success_response(Body::from(json))) } -#[derive(Serialize)] +#[derive(Serialize, Encode)] #[serde(bound = "T: EthSpec")] pub struct StateResponse { pub root: Hash256, @@ -144,11 +141,7 @@ pub fn get_state(req: Request) -> ApiResult beacon_state: state, }; - let json: String = serde_json::to_string(&response).map_err(|e| { - ApiError::ServerError(format!("Unable to serialize StateResponse: {:?}", e)) - })?; - - Ok(success_response(Body::from(json))) + ResponseBuilder::new(&req).body(&response) } /// HTTP handler to return a `BeaconState` root at a given `slot`. diff --git a/beacon_node/rest_api/src/lib.rs b/beacon_node/rest_api/src/lib.rs index 7c5ab30ef..1b5a2d6ee 100644 --- a/beacon_node/rest_api/src/lib.rs +++ b/beacon_node/rest_api/src/lib.rs @@ -8,6 +8,7 @@ mod helpers; mod metrics; mod network; mod node; +mod response_builder; mod spec; mod url_query; mod validator; @@ -18,6 +19,7 @@ use eth2_config::Eth2Config; use hyper::rt::Future; use hyper::service::service_fn_ok; use hyper::{Body, Method, Response, Server, StatusCode}; +use response_builder::ResponseBuilder; use slog::{info, o, warn}; use std::ops::Deref; use std::path::PathBuf; diff --git a/beacon_node/rest_api/src/response_builder.rs b/beacon_node/rest_api/src/response_builder.rs new file mode 100644 index 000000000..9b8819996 --- /dev/null +++ b/beacon_node/rest_api/src/response_builder.rs @@ -0,0 +1,50 @@ +use super::{ApiError, ApiResult}; +use http::header; +use hyper::{Body, Request, Response, StatusCode}; +use serde::Serialize; +use ssz::Encode; + +pub enum Encoding { + JSON, + SSZ, + YAML, +} + +pub struct ResponseBuilder { + encoding: Encoding, +} + +impl ResponseBuilder { + pub fn new(req: &Request) -> Self { + let encoding = match req.headers().get(header::CONTENT_TYPE) { + Some(h) if h == "application/ssz" => Encoding::SSZ, + Some(h) if h == "application/yaml" => Encoding::YAML, + _ => Encoding::JSON, + }; + + Self { encoding } + } + + pub fn body(self, item: &T) -> ApiResult { + let body: Body = match self.encoding { + Encoding::JSON => Body::from(serde_json::to_string(&item).map_err(|e| { + ApiError::ServerError(format!( + "Unable to serialize response body as JSON: {:?}", + e + )) + })?), + Encoding::SSZ => Body::from(item.as_ssz_bytes()), + Encoding::YAML => Body::from(serde_yaml::to_string(&item).map_err(|e| { + ApiError::ServerError(format!( + "Unable to serialize response body as YAML: {:?}", + e + )) + })?), + }; + + Response::builder() + .status(StatusCode::OK) + .body(Body::from(body)) + .map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e))) + } +}