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",
"libsecp256k1",
"lighthouse_network",
"mime",
"procinfo",
"proto_array",
"psutil",

View File

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

View File

@ -3,7 +3,9 @@
use crate::Error as ServerError;
use lighthouse_network::{ConnectionDirection, Enr, Multiaddr, PeerConnectionStatus};
use mime::{Mime, APPLICATION, JSON, OCTET_STREAM, STAR};
use serde::{Deserialize, Serialize};
use std::cmp::Reverse;
use std::convert::TryFrom;
use std::fmt;
use std::str::{from_utf8, FromStr};
@ -1008,15 +1010,37 @@ impl FromStr for Accept {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"application/octet-stream" => Ok(Accept::Ssz),
"application/json" => Ok(Accept::Json),
"*/*" => Ok(Accept::Any),
_ => Err("accept header cannot be parsed.".to_string()),
}
let mut mimes = parse_accept(s)?;
// [q-factor weighting]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2
// find the highest q-factor supported accept type
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)]
pub struct LivenessRequestData {
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())
)
}
}