Fix http header accept parsing problem (#3185)

## Issue Addressed

Which issue # does this PR address?
#3114 

## Proposed Changes

1. introduce `mime` package 
2. Parse `Accept` field in the header with `mime`

## Additional Info

Please provide any additional information. For example, future considerations
or information useful for reviewers.
This commit is contained in:
will 2022-05-18 06:50:50 +00:00
parent def9bc660e
commit 0428018cc1
3 changed files with 51 additions and 6 deletions

1
Cargo.lock generated
View File

@ -1586,6 +1586,7 @@ dependencies = [
"futures-util", "futures-util",
"libsecp256k1", "libsecp256k1",
"lighthouse_network", "lighthouse_network",
"mime",
"procinfo", "procinfo",
"proto_array", "proto_array",
"psutil", "psutil",

View File

@ -26,6 +26,7 @@ futures-util = "0.3.8"
futures = "0.3.8" futures = "0.3.8"
store = { path = "../../beacon_node/store", optional = true } store = { path = "../../beacon_node/store", optional = true }
slashing_protection = { path = "../../validator_client/slashing_protection", optional = true } slashing_protection = { path = "../../validator_client/slashing_protection", optional = true }
mime = "0.3.16"
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
psutil = { version = "3.2.2", optional = true } psutil = { version = "3.2.2", optional = true }

View File

@ -3,7 +3,9 @@
use crate::Error as ServerError; use crate::Error as ServerError;
use lighthouse_network::{ConnectionDirection, Enr, Multiaddr, PeerConnectionStatus}; use lighthouse_network::{ConnectionDirection, Enr, Multiaddr, PeerConnectionStatus};
use mime::{Mime, APPLICATION, JSON, OCTET_STREAM, STAR};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Reverse;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt; use std::fmt;
use std::str::{from_utf8, FromStr}; use std::str::{from_utf8, FromStr};
@ -1008,15 +1010,37 @@ impl FromStr for Accept {
type Err = String; type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { let mut mimes = parse_accept(s)?;
"application/octet-stream" => Ok(Accept::Ssz),
"application/json" => Ok(Accept::Json), // [q-factor weighting]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2
"*/*" => Ok(Accept::Any), // find the highest q-factor supported accept type
_ => Err("accept header cannot be parsed.".to_string()), mimes.sort_by_key(|m| {
} Reverse(m.get_param("q").map_or(1000_u16, |n| {
(n.as_ref().parse::<f32>().unwrap_or(0_f32) * 1000_f32) as u16
}))
});
mimes
.into_iter()
.find_map(|m| match (m.type_(), m.subtype()) {
(APPLICATION, OCTET_STREAM) => Some(Accept::Ssz),
(APPLICATION, JSON) => Some(Accept::Json),
(STAR, STAR) => Some(Accept::Any),
_ => None,
})
.ok_or_else(|| "accept header is not supported".to_string())
} }
} }
fn parse_accept(accept: &str) -> Result<Vec<Mime>, String> {
accept
.split(',')
.map(|part| {
part.parse()
.map_err(|e| format!("error parsing Accept header: {}", e))
})
.collect()
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct LivenessRequestData { pub struct LivenessRequestData {
pub epoch: Epoch, pub epoch: Epoch,
@ -1045,4 +1069,23 @@ mod tests {
} }
); );
} }
#[test]
fn parse_accept_header_content() {
assert_eq!(
Accept::from_str("application/json; charset=utf-8").unwrap(),
Accept::Json
);
assert_eq!(
Accept::from_str("text/plain,application/octet-stream;q=0.3,application/json;q=0.9")
.unwrap(),
Accept::Json
);
assert_eq!(
Accept::from_str("text/plain"),
Err("accept header is not supported".to_string())
)
}
} }