Restructured response builder to give YAML or JSON when SSZ is not available, not just JSON.
This commit is contained in:
parent
1dd86baf1a
commit
f48311900e
@ -55,7 +55,7 @@ pub fn get_head<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult
|
|||||||
previous_justified_block_root: chain_head.beacon_state.previous_justified_checkpoint.root,
|
previous_justified_block_root: chain_head.beacon_state.previous_justified_checkpoint.root,
|
||||||
};
|
};
|
||||||
|
|
||||||
ResponseBuilder::new(&req).body(&head)
|
ResponseBuilder::new(&req)?.body(&head)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Encode)]
|
#[derive(Serialize, Encode)]
|
||||||
@ -102,7 +102,7 @@ pub fn get_block<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult
|
|||||||
beacon_block: block,
|
beacon_block: block,
|
||||||
};
|
};
|
||||||
|
|
||||||
ResponseBuilder::new(&req).body(&response)
|
ResponseBuilder::new(&req)?.body(&response)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return a `BeaconBlock` root at a given `slot`.
|
/// HTTP handler to return a `BeaconBlock` root at a given `slot`.
|
||||||
@ -116,13 +116,13 @@ pub fn get_block_root<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiR
|
|||||||
ApiError::NotFound(format!("Unable to find BeaconBlock for slot {:?}", target))
|
ApiError::NotFound(format!("Unable to find BeaconBlock for slot {:?}", target))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
ResponseBuilder::new(&req).body(&root)
|
ResponseBuilder::new(&req)?.body(&root)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return the `Fork` of the current head.
|
/// HTTP handler to return the `Fork` of the current head.
|
||||||
pub fn get_fork<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult {
|
pub fn get_fork<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult {
|
||||||
let beacon_chain = get_beacon_chain_from_request::<T>(&req)?;
|
let beacon_chain = get_beacon_chain_from_request::<T>(&req)?;
|
||||||
ResponseBuilder::new(&req).body(&beacon_chain.head().beacon_state.fork)
|
ResponseBuilder::new(&req)?.body(&beacon_chain.head().beacon_state.fork)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return the set of validators for an `Epoch`
|
/// HTTP handler to return the set of validators for an `Epoch`
|
||||||
@ -157,7 +157,7 @@ pub fn get_validators<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiR
|
|||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
ResponseBuilder::new(&req).body(&active_vals)
|
ResponseBuilder::new(&req)?.body(&active_vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Encode)]
|
#[derive(Serialize, Encode)]
|
||||||
@ -210,7 +210,7 @@ pub fn get_state<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult
|
|||||||
beacon_state: state,
|
beacon_state: state,
|
||||||
};
|
};
|
||||||
|
|
||||||
ResponseBuilder::new(&req).body(&response)
|
ResponseBuilder::new(&req)?.body(&response)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return a `BeaconState` root at a given `slot`.
|
/// HTTP handler to return a `BeaconState` root at a given `slot`.
|
||||||
@ -225,7 +225,7 @@ pub fn get_state_root<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiR
|
|||||||
|
|
||||||
let root = state_root_at_slot(&beacon_chain, slot)?;
|
let root = state_root_at_slot(&beacon_chain, slot)?;
|
||||||
|
|
||||||
ResponseBuilder::new(&req).body(&root)
|
ResponseBuilder::new(&req)?.body(&root)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return the highest finalized slot.
|
/// HTTP handler to return the highest finalized slot.
|
||||||
@ -237,7 +237,7 @@ pub fn get_current_finalized_checkpoint<T: BeaconChainTypes + 'static>(
|
|||||||
|
|
||||||
let checkpoint = head_state.finalized_checkpoint.clone();
|
let checkpoint = head_state.finalized_checkpoint.clone();
|
||||||
|
|
||||||
ResponseBuilder::new(&req).body(&checkpoint)
|
ResponseBuilder::new(&req)?.body(&checkpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return a `BeaconState` at the genesis block.
|
/// HTTP handler to return a `BeaconState` at the genesis block.
|
||||||
@ -246,5 +246,5 @@ pub fn get_genesis_state<T: BeaconChainTypes + 'static>(req: Request<Body>) -> A
|
|||||||
|
|
||||||
let (_root, state) = state_at_slot(&beacon_chain, Slot::new(0))?;
|
let (_root, state) = state_at_slot(&beacon_chain, Slot::new(0))?;
|
||||||
|
|
||||||
ResponseBuilder::new(&req).body(&state)
|
ResponseBuilder::new(&req)?.body(&state)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ pub enum ApiError {
|
|||||||
NotImplemented(String),
|
NotImplemented(String),
|
||||||
InvalidQueryParams(String),
|
InvalidQueryParams(String),
|
||||||
NotFound(String),
|
NotFound(String),
|
||||||
|
UnsupportedType(String),
|
||||||
ImATeapot(String), // Just in case.
|
ImATeapot(String), // Just in case.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ impl ApiError {
|
|||||||
ApiError::NotImplemented(desc) => (StatusCode::NOT_IMPLEMENTED, desc),
|
ApiError::NotImplemented(desc) => (StatusCode::NOT_IMPLEMENTED, desc),
|
||||||
ApiError::InvalidQueryParams(desc) => (StatusCode::BAD_REQUEST, desc),
|
ApiError::InvalidQueryParams(desc) => (StatusCode::BAD_REQUEST, desc),
|
||||||
ApiError::NotFound(desc) => (StatusCode::NOT_FOUND, desc),
|
ApiError::NotFound(desc) => (StatusCode::NOT_FOUND, desc),
|
||||||
|
ApiError::UnsupportedType(desc) => (StatusCode::UNSUPPORTED_MEDIA_TYPE, desc),
|
||||||
ApiError::ImATeapot(desc) => (StatusCode::IM_A_TEAPOT, desc),
|
ApiError::ImATeapot(desc) => (StatusCode::IM_A_TEAPOT, desc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,6 @@ pub fn get_prometheus<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiR
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
String::from_utf8(buffer)
|
String::from_utf8(buffer)
|
||||||
.map(|string| ResponseBuilder::new(&req).body_text(string))
|
.map(|string| ResponseBuilder::new(&req)?.body_text(string))
|
||||||
.map_err(|e| ApiError::ServerError(format!("Failed to encode prometheus info: {:?}", e)))?
|
.map_err(|e| ApiError::ServerError(format!("Failed to encode prometheus info: {:?}", e)))?
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ pub fn get_listen_addresses<T: BeaconChainTypes>(req: Request<Body>) -> ApiResul
|
|||||||
.get::<Arc<NetworkService<T>>>()
|
.get::<Arc<NetworkService<T>>>()
|
||||||
.expect("The network service should always be there, we put it there");
|
.expect("The network service should always be there, we put it there");
|
||||||
let multiaddresses: Vec<Multiaddr> = network.listen_multiaddrs();
|
let multiaddresses: Vec<Multiaddr> = network.listen_multiaddrs();
|
||||||
ResponseBuilder::new(&req).body_json(&multiaddresses)
|
ResponseBuilder::new(&req)?.body_no_ssz(&multiaddresses)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return the network port the client is listening on.
|
/// HTTP handler to return the network port the client is listening on.
|
||||||
@ -27,7 +27,7 @@ pub fn get_listen_port<T: BeaconChainTypes>(req: Request<Body>) -> ApiResult {
|
|||||||
.get::<Arc<NetworkService<T>>>()
|
.get::<Arc<NetworkService<T>>>()
|
||||||
.expect("The network service should always be there, we put it there")
|
.expect("The network service should always be there, we put it there")
|
||||||
.clone();
|
.clone();
|
||||||
ResponseBuilder::new(&req).body(&network.listen_port())
|
ResponseBuilder::new(&req)?.body(&network.listen_port())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return the Discv5 ENR from the client's libp2p service.
|
/// HTTP handler to return the Discv5 ENR from the client's libp2p service.
|
||||||
@ -38,7 +38,7 @@ pub fn get_enr<T: BeaconChainTypes>(req: Request<Body>) -> ApiResult {
|
|||||||
.extensions()
|
.extensions()
|
||||||
.get::<Arc<NetworkService<T>>>()
|
.get::<Arc<NetworkService<T>>>()
|
||||||
.expect("The network service should always be there, we put it there");
|
.expect("The network service should always be there, we put it there");
|
||||||
ResponseBuilder::new(&req).body_json(&network.local_enr().to_base64())
|
ResponseBuilder::new(&req)?.body_no_ssz(&network.local_enr().to_base64())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return the `PeerId` from the client's libp2p service.
|
/// HTTP handler to return the `PeerId` from the client's libp2p service.
|
||||||
@ -49,7 +49,7 @@ pub fn get_peer_id<T: BeaconChainTypes>(req: Request<Body>) -> ApiResult {
|
|||||||
.extensions()
|
.extensions()
|
||||||
.get::<Arc<NetworkService<T>>>()
|
.get::<Arc<NetworkService<T>>>()
|
||||||
.expect("The network service should always be there, we put it there");
|
.expect("The network service should always be there, we put it there");
|
||||||
ResponseBuilder::new(&req).body_json(&network.local_peer_id().to_base58())
|
ResponseBuilder::new(&req)?.body_no_ssz(&network.local_peer_id().to_base58())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return the number of peers connected in the client's libp2p service.
|
/// HTTP handler to return the number of peers connected in the client's libp2p service.
|
||||||
@ -58,7 +58,7 @@ pub fn get_peer_count<T: BeaconChainTypes>(req: Request<Body>) -> ApiResult {
|
|||||||
.extensions()
|
.extensions()
|
||||||
.get::<Arc<NetworkService<T>>>()
|
.get::<Arc<NetworkService<T>>>()
|
||||||
.expect("The network service should always be there, we put it there");
|
.expect("The network service should always be there, we put it there");
|
||||||
ResponseBuilder::new(&req).body(&network.connected_peers())
|
ResponseBuilder::new(&req)?.body(&network.connected_peers())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return the list of peers connected to the client's libp2p service.
|
/// HTTP handler to return the list of peers connected to the client's libp2p service.
|
||||||
@ -74,5 +74,5 @@ pub fn get_peer_list<T: BeaconChainTypes>(req: Request<Body>) -> ApiResult {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(PeerId::to_string)
|
.map(PeerId::to_string)
|
||||||
.collect();
|
.collect();
|
||||||
ResponseBuilder::new(&req).body_json(&connected_peers)
|
ResponseBuilder::new(&req)?.body_no_ssz(&connected_peers)
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,11 @@ use version;
|
|||||||
|
|
||||||
/// Read the version string from the current Lighthouse build.
|
/// Read the version string from the current Lighthouse build.
|
||||||
pub fn get_version(req: Request<Body>) -> ApiResult {
|
pub fn get_version(req: Request<Body>) -> ApiResult {
|
||||||
ResponseBuilder::new(&req).body_json(&version::version())
|
ResponseBuilder::new(&req)?.body_no_ssz(&version::version())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the genesis time from the current beacon chain state.
|
/// Read the genesis time from the current beacon chain state.
|
||||||
pub fn get_genesis_time<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult {
|
pub fn get_genesis_time<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult {
|
||||||
let beacon_chain = get_beacon_chain_from_request::<T>(&req)?;
|
let beacon_chain = get_beacon_chain_from_request::<T>(&req)?;
|
||||||
ResponseBuilder::new(&req).body(&beacon_chain.head().beacon_state.genesis_time)
|
ResponseBuilder::new(&req)?.body(&beacon_chain.head().beacon_state.genesis_time)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ pub enum Encoding {
|
|||||||
JSON,
|
JSON,
|
||||||
SSZ,
|
SSZ,
|
||||||
YAML,
|
YAML,
|
||||||
|
TEXT,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ResponseBuilder {
|
pub struct ResponseBuilder {
|
||||||
@ -15,22 +16,55 @@ pub struct ResponseBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseBuilder {
|
impl ResponseBuilder {
|
||||||
pub fn new(req: &Request<Body>) -> Self {
|
pub fn new(req: &Request<Body>) -> Result<Self, ApiError> {
|
||||||
let encoding = match req.headers().get(header::CONTENT_TYPE) {
|
let content_header = req
|
||||||
Some(h) if h == "application/ssz" => Encoding::SSZ,
|
.headers()
|
||||||
Some(h) if h == "application/yaml" => Encoding::YAML,
|
.get(header::CONTENT_TYPE)
|
||||||
|
.map_or(Ok(""), |h| h.to_str())
|
||||||
|
.map_err(|e| {
|
||||||
|
ApiError::InvalidQueryParams(format!(
|
||||||
|
"The content-type header contains invalid characters: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.map(|h| String::from(h))?;
|
||||||
|
|
||||||
|
let encoding = match content_header {
|
||||||
|
ref h if h.starts_with("application/ssz") => Encoding::SSZ,
|
||||||
|
ref h if h.starts_with("application/yaml") => Encoding::YAML,
|
||||||
|
ref h if h.starts_with("text/plain") => Encoding::TEXT,
|
||||||
_ => Encoding::JSON,
|
_ => Encoding::JSON,
|
||||||
};
|
};
|
||||||
|
Ok(Self { encoding })
|
||||||
Self { encoding }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn body<T: Serialize + Encode>(self, item: &T) -> ApiResult {
|
pub fn body<T: Serialize + Encode>(self, item: &T) -> ApiResult {
|
||||||
|
match self.encoding {
|
||||||
|
Encoding::SSZ => Response::builder()
|
||||||
|
.status(StatusCode::OK)
|
||||||
|
.header("content-type", "application/ssz")
|
||||||
|
.body(Body::from(item.as_ssz_bytes()))
|
||||||
|
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e))),
|
||||||
|
_ => self.body_no_ssz(item),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body_no_ssz<T: Serialize>(self, item: &T) -> ApiResult {
|
||||||
let (body, content_type) = match self.encoding {
|
let (body, content_type) = match self.encoding {
|
||||||
Encoding::JSON => {
|
Encoding::JSON => (
|
||||||
return self.body_json(item);
|
Body::from(serde_json::to_string(&item).map_err(|e| {
|
||||||
|
ApiError::ServerError(format!(
|
||||||
|
"Unable to serialize response body as JSON: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?),
|
||||||
|
"application/json",
|
||||||
|
),
|
||||||
|
Encoding::SSZ => {
|
||||||
|
return Err(ApiError::UnsupportedType(
|
||||||
|
"Response cannot be encoded as SSZ.".into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Encoding::SSZ => (Body::from(item.as_ssz_bytes()), "application/ssz"),
|
|
||||||
Encoding::YAML => (
|
Encoding::YAML => (
|
||||||
Body::from(serde_yaml::to_string(&item).map_err(|e| {
|
Body::from(serde_yaml::to_string(&item).map_err(|e| {
|
||||||
ApiError::ServerError(format!(
|
ApiError::ServerError(format!(
|
||||||
@ -38,8 +72,13 @@ impl ResponseBuilder {
|
|||||||
e
|
e
|
||||||
))
|
))
|
||||||
})?),
|
})?),
|
||||||
"application/ssz",
|
"application/yaml",
|
||||||
),
|
),
|
||||||
|
Encoding::TEXT => {
|
||||||
|
return Err(ApiError::UnsupportedType(
|
||||||
|
"Response cannot be encoded as plain text.".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Response::builder()
|
Response::builder()
|
||||||
@ -49,19 +88,6 @@ impl ResponseBuilder {
|
|||||||
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn body_json<T: Serialize>(self, item: &T) -> ApiResult {
|
|
||||||
Response::builder()
|
|
||||||
.status(StatusCode::OK)
|
|
||||||
.header("content-type", "application/json")
|
|
||||||
.body(Body::from(serde_json::to_string(&item).map_err(|e| {
|
|
||||||
ApiError::ServerError(format!(
|
|
||||||
"Unable to serialize response body as JSON: {:?}",
|
|
||||||
e
|
|
||||||
))
|
|
||||||
})?))
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn body_text(self, text: String) -> ApiResult {
|
pub fn body_text(self, text: String) -> ApiResult {
|
||||||
Response::builder()
|
Response::builder()
|
||||||
.status(StatusCode::OK)
|
.status(StatusCode::OK)
|
||||||
|
@ -11,7 +11,7 @@ use types::EthSpec;
|
|||||||
/// HTTP handler to return the full spec object.
|
/// HTTP handler to return the full spec object.
|
||||||
pub fn get_spec<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult {
|
pub fn get_spec<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult {
|
||||||
let beacon_chain = get_beacon_chain_from_request::<T>(&req)?;
|
let beacon_chain = get_beacon_chain_from_request::<T>(&req)?;
|
||||||
ResponseBuilder::new(&req).body_json(&beacon_chain.spec)
|
ResponseBuilder::new(&req)?.body_no_ssz(&beacon_chain.spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return the full Eth2Config object.
|
/// HTTP handler to return the full Eth2Config object.
|
||||||
@ -21,10 +21,10 @@ pub fn get_eth2_config<T: BeaconChainTypes + 'static>(req: Request<Body>) -> Api
|
|||||||
.get::<Arc<Eth2Config>>()
|
.get::<Arc<Eth2Config>>()
|
||||||
.ok_or_else(|| ApiError::ServerError("Eth2Config extension missing".to_string()))?;
|
.ok_or_else(|| ApiError::ServerError("Eth2Config extension missing".to_string()))?;
|
||||||
|
|
||||||
ResponseBuilder::new(&req).body_json(eth2_config.as_ref())
|
ResponseBuilder::new(&req)?.body_no_ssz(eth2_config.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return the full spec object.
|
/// HTTP handler to return the full spec object.
|
||||||
pub fn get_slots_per_epoch<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult {
|
pub fn get_slots_per_epoch<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiResult {
|
||||||
ResponseBuilder::new(&req).body(&T::EthSpec::slots_per_epoch())
|
ResponseBuilder::new(&req)?.body(&T::EthSpec::slots_per_epoch())
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ pub fn get_validator_duties<T: BeaconChainTypes + 'static>(req: Request<Body>) -
|
|||||||
|
|
||||||
duties.append(&mut vec![duty]);
|
duties.append(&mut vec![duty]);
|
||||||
}
|
}
|
||||||
ResponseBuilder::new(&req).body_json(&duties)
|
ResponseBuilder::new(&req)?.body_no_ssz(&duties)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Handler to produce a new BeaconBlock from the current state, ready to be signed by a validator.
|
/// HTTP Handler to produce a new BeaconBlock from the current state, ready to be signed by a validator.
|
||||||
@ -188,7 +188,7 @@ pub fn get_new_beacon_block<T: BeaconChainTypes + 'static>(req: Request<Body>) -
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
ResponseBuilder::new(&req).body(&new_block)
|
ResponseBuilder::new(&req)?.body(&new_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Handler to publish a BeaconBlock, which has been signed by a validator.
|
/// HTTP Handler to publish a BeaconBlock, which has been signed by a validator.
|
||||||
@ -246,7 +246,7 @@ pub fn publish_beacon_block<T: BeaconChainTypes + 'static>(req: Request<Body>) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).and_then(|_| {
|
}).and_then(|_| {
|
||||||
response_builder.body_json(&())
|
response_builder?.body_no_ssz(&())
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,5 +354,5 @@ pub fn get_new_attestation<T: BeaconChainTypes + 'static>(req: Request<Body>) ->
|
|||||||
signature: AggregateSignature::new(),
|
signature: AggregateSignature::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
ResponseBuilder::new(&req).body(&attestation)
|
ResponseBuilder::new(&req)?.body(&attestation)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user