use crate::error::{FeroxFuzzError, RequestErrorKind};
use crate::input::Data;
use crate::std_ext::convert::AsInner;
use reqwest::Version;
use tracing::{error, instrument};
#[instrument(skip_all, level = "trace")]
pub(super) fn parse_version(data: &Data) -> Result<Version, FeroxFuzzError> {
match data.inner().get(5..) {
Some([0x30, 0x2e, 0x39]) => Ok(Version::HTTP_09),
Some([0x31, 0x2e, 0x30]) => Ok(Version::HTTP_10),
Some([0x31, 0x2e, 0x31]) => Ok(Version::HTTP_11),
Some([0x32, 0x2e, 0x30]) => Ok(Version::HTTP_2),
Some([0x33, 0x2e, 0x30]) => Ok(Version::HTTP_3),
_ => {
error!(%data, "failed to parse http version; must be a valid http version when using a reqwest client");
Err(FeroxFuzzError::InvalidVersionError {
version: format!("{data}"),
})
}
}
}
#[allow(clippy::needless_pass_by_value)]
#[instrument(skip_all, level = "trace")]
pub(super) fn reqwest_to_ferox_error(source: reqwest::Error) -> FeroxFuzzError {
let status = source.status().map(|status_code| status_code.as_u16());
let kind = if source.is_body() {
RequestErrorKind::Body(status)
} else if source.is_connect() {
RequestErrorKind::Connect(status)
} else if source.is_decode() {
RequestErrorKind::Decode(status)
} else if source.is_redirect() {
RequestErrorKind::Redirect(status)
} else if source.is_timeout() {
RequestErrorKind::Timeout(status)
} else if source.is_request() {
RequestErrorKind::Request(status)
} else {
RequestErrorKind::Unknown
};
error!(?kind, "error occurred while sending request: {}", source);
FeroxFuzzError::RequestError {
kind,
message: source.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parsed_versions_are_correct() {
let versions = [
b"HTTP/0.9",
b"HTTP/1.0",
b"HTTP/1.1",
b"HTTP/2.0",
b"HTTP/3.0",
];
for version in versions {
let data = Data::Static(version.to_vec());
assert!(parse_version(&data).is_ok());
}
assert!(parse_version(&Data::Static(b"not valid".to_vec())).is_err());
}
}