use bytes::Bytes;
use http::{HeaderName, Request, Response, Uri, Version, request, response};
use snafu::OptionExt;
use super::{
FieldLine, FieldSection, MalformedHeaderSection, Protocol, PseudoHeaders,
malformed_header_section,
};
impl From<hyper::ext::Protocol> for FieldLine {
fn from(protocol: hyper::ext::Protocol) -> Self {
FieldLine {
name: Bytes::from_static(PseudoHeaders::PROTOOCL.as_bytes()),
value: Bytes::from_owner(protocol),
}
}
}
pub fn header_map_to_field_lines(headers: http::HeaderMap) -> impl Iterator<Item = FieldLine> {
headers
.into_iter()
.scan(None::<HeaderName>, |last_name, (name, value)| {
let name = match name {
Some(name) => {
*last_name = Some(name.clone());
name
}
None => match last_name.clone() {
Some(name) => name,
None => return Some(None),
},
};
Some(Some(FieldLine {
name: Bytes::from_owner(name),
value: Bytes::from_owner(value),
}))
})
.flatten()
}
pub fn hyper_request_parts_to_field_lines(
mut parts: http::request::Parts,
) -> impl Iterator<Item = FieldLine> {
let uri_parts = parts.uri.into_parts();
let pseudo_headers = [
Some(parts.method.into()),
uri_parts.scheme.map(FieldLine::from),
uri_parts.authority.map(FieldLine::from),
uri_parts.path_and_query.map(FieldLine::from),
];
let protocol = parts
.extensions
.remove::<Protocol>()
.map(FieldLine::from)
.or_else(|| {
parts
.extensions
.remove::<::hyper::ext::Protocol>()
.map(FieldLine::from)
});
pseudo_headers
.into_iter()
.flatten()
.chain(protocol)
.chain(header_map_to_field_lines(parts.headers))
}
pub fn hyper_response_parts_to_field_lines(
parts: http::response::Parts,
) -> impl Iterator<Item = FieldLine> {
let pseudo_headers = [Some(FieldLine::from(parts.status))];
pseudo_headers
.into_iter()
.flatten()
.chain(header_map_to_field_lines(parts.headers))
}
impl From<request::Parts> for FieldSection {
fn from(mut request: request::Parts) -> Self {
let mut pseudo = PseudoHeaders::request(request.method, request.uri);
if let Some(p) = request.extensions.remove::<Protocol>()
&& let PseudoHeaders::Request { protocol, .. } = &mut pseudo
{
protocol.replace(p);
} else if let Some(p) = request.extensions.remove::<::hyper::ext::Protocol>()
&& let PseudoHeaders::Request { protocol, .. } = &mut pseudo
{
protocol.replace(Protocol {
token: Bytes::from_owner(p),
});
}
Self {
pseudo_headers: Some(pseudo),
header_map: request.headers,
}
}
}
impl TryFrom<FieldSection> for request::Parts {
type Error = MalformedHeaderSection;
fn try_from(value: FieldSection) -> Result<Self, Self::Error> {
let PseudoHeaders::Request {
method,
scheme,
authority,
path,
protocol,
} = value
.pseudo_headers
.context(malformed_header_section::AbsenceOfMandatoryPseudoHeadersSnafu)?
else {
return Err(MalformedHeaderSection::ResponsePseudoHeaderInRequest);
};
let mut uri = Uri::builder();
if let Some(scheme) = scheme {
uri = uri.scheme(scheme);
}
if let Some(authority) = authority {
uri = uri.authority(authority)
}
if let Some(path) = path {
uri = uri.path_and_query(path);
}
let uri = uri.build()?;
let method =
method.context(malformed_header_section::AbsenceOfMandatoryPseudoHeadersSnafu)?;
let mut request = Request::builder()
.uri(uri)
.method(method)
.version(Version::HTTP_3)
.body(())?;
*request.headers_mut() = value.header_map;
if let Some(protocol) = protocol {
request.extensions_mut().insert(protocol);
}
Ok(request.into_parts().0)
}
}
impl From<response::Parts> for FieldSection {
fn from(response: response::Parts) -> Self {
Self {
pseudo_headers: Some(PseudoHeaders::response(response.status)),
header_map: response.headers,
}
}
}
impl TryFrom<FieldSection> for response::Parts {
type Error = MalformedHeaderSection;
fn try_from(value: FieldSection) -> Result<Self, Self::Error> {
let PseudoHeaders::Response { status } = value
.pseudo_headers
.context(malformed_header_section::AbsenceOfMandatoryPseudoHeadersSnafu)?
else {
return Err(MalformedHeaderSection::RequestPseudoHeaderInResponse);
};
let status =
status.context(malformed_header_section::AbsenceOfMandatoryPseudoHeadersSnafu)?;
let mut response = Response::builder()
.status(status)
.version(Version::HTTP_3)
.body(())?;
*response.headers_mut() = value.header_map;
Ok(response.into_parts().0)
}
}