use super::*;
pub fn find_head_end(buffer: &[u8]) -> Option<usize> {
let mut index: usize = 0;
while index < buffer.len() {
let rest: &[u8] = &buffer[index..];
let line_len: usize = find_line_end(rest)?;
let is_blank: bool = rest[..line_len]
.iter()
.all(|byte: &u8| *byte == CR_U8 || *byte == LF_U8);
if is_blank {
return Some(index + line_len);
}
index += line_len;
}
None
}
pub fn find_line_end(bytes: &[u8]) -> Option<usize> {
bytes
.iter()
.position(|byte: &u8| *byte == LF_U8)
.map(|index: usize| index + 1)
}
pub fn parse_response_head(head: &[u8]) -> Result<(usize, ProtocolHeaders), ResponseError> {
let text: &str =
from_utf8(head).map_err(|error: Utf8Error| ResponseError::Send(error.to_string()))?;
let mut lines: SplitLines<'_> = text.split(CRLF);
let status_line: &str = lines.next().unwrap_or(EMPTY_STR);
let mut fields: SplitWhitespace<'_> = status_line.split_whitespace();
let version: &str = fields.next().unwrap_or(EMPTY_STR);
if !version.starts_with(HTTP_VERSION_PREFIX) {
return Err(ResponseError::Send(MISSING_RESPONSE_VERSION.to_owned()));
}
let status_text: &str = fields.next().unwrap_or(EMPTY_STR);
if status_text.is_empty() {
return Err(ResponseError::Send(MISSING_RESPONSE_STATUS.to_owned()));
}
let status_code: usize = status_text
.parse::<usize>()
.map_err(|error: ParseIntError| ResponseError::Send(error.to_string()))?;
let mut headers: ProtocolHeaders = http::HeaderMap::new();
for line in lines {
let line: &str = line.trim_end_matches(LF_CHAR);
if line.is_empty() {
continue;
}
let Some((name, value)) = line.split_once(COLON) else {
return Err(ResponseError::Send(MALFORMED_RESPONSE_HEADER.to_owned()));
};
let name: &str = name.trim();
if name.is_empty() {
return Err(ResponseError::Send(MALFORMED_RESPONSE_HEADER.to_owned()));
}
let Ok(header_name) = http::HeaderName::from_bytes(name.as_bytes()) else {
return Err(ResponseError::Send(MALFORMED_RESPONSE_HEADER.to_owned()));
};
let Ok(header_value) = http::HeaderValue::from_bytes(value.trim().as_bytes()) else {
return Err(ResponseError::Send(MALFORMED_RESPONSE_HEADER.to_owned()));
};
headers.append(header_name, header_value);
}
Ok((status_code, headers))
}
pub fn has_transfer_encoding(headers: &ProtocolHeaders) -> bool {
headers.contains_key(TRANSFER_ENCODING)
}
pub fn parse_content_length(headers: &ProtocolHeaders) -> Result<Option<usize>, ResponseError> {
let mut announced: Option<usize> = None;
for value in headers.get_all(CONTENT_LENGTH).iter() {
let Ok(value) = value.to_str() else {
return Err(ResponseError::Send(MALFORMED_RESPONSE_HEADER.to_owned()));
};
let length: usize = parse_ascii_length(value)
.map_err(|error: RequestError| ResponseError::Send(error.to_string()))?;
match announced {
Some(previous) if previous != length => {
return Err(ResponseError::Send(OVERFLOWING_RESPONSE_BODY.to_owned()));
}
_ => announced = Some(length),
}
}
Ok(announced)
}
pub fn connection_nominates(headers: &ProtocolHeaders, name: &str) -> bool {
if name.eq_ignore_ascii_case(CONNECTION) {
return true;
}
for value in headers.get_all(CONNECTION).iter() {
let Ok(value) = value.to_str() else {
continue;
};
for token in value.split(COMMA_STR) {
if token.trim().eq_ignore_ascii_case(name) {
return true;
}
}
}
false
}
pub(crate) async fn next_h2_capacity(
send_stream: &mut h2::SendStream<Bytes>,
wanted: usize,
) -> Result<usize, ResponseError> {
send_stream.reserve_capacity(wanted);
loop {
let granted: Option<Result<usize, h2::Error>> =
poll_fn(|context: &mut task::Context<'_>| send_stream.poll_capacity(context)).await;
match granted {
Some(Ok(capacity)) => {
if capacity > 0 {
return Ok(capacity.min(wanted));
}
}
Some(Err(error)) => return Err(ResponseError::Send(error.to_string())),
None => return Err(ResponseError::Send(MISSING_SEND_CAPACITY.to_owned())),
}
}
}