extern crate alloc;
use crate::body::Body;
use crate::error::ParseError;
use crate::headers::Headers;
use crate::parser::framing::FramingDetector;
use crate::parser::message::{BodyReadStrategy, Response};
use alloc::vec::Vec;
#[allow(dead_code)]
pub struct ResponseReader {
state: ReaderState,
buffer: Vec<u8>,
max_header_size: usize,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ReaderState {
ReadingHeaders,
ReadingBody {
status_code: u16,
strategy: BodyReadStrategy,
},
}
#[allow(dead_code)]
impl ResponseReader {
pub const fn new() -> Self {
Self::with_max_header_size(8192)
}
pub const fn with_max_header_size(max_header_size: usize) -> Self {
Self {
state: ReaderState::ReadingHeaders,
buffer: Vec::new(),
max_header_size,
}
}
pub fn feed(
&mut self,
data: &[u8],
) -> Result<(), ParseError> {
self.buffer.extend_from_slice(data);
if matches!(self.state, ReaderState::ReadingHeaders) && self.buffer.len() > self.max_header_size {
return Err(ParseError::HeaderTooLarge);
}
Ok(())
}
pub fn has_complete_headers(&self) -> bool {
matches!(self.state, ReaderState::ReadingHeaders) && FramingDetector::has_complete_headers(&self.buffer)
}
pub fn parse_headers(&mut self) -> Result<(u16, alloc::string::String, Headers, BodyReadStrategy), ParseError> {
if !matches!(self.state, ReaderState::ReadingHeaders) {
return Err(ParseError::InvalidState);
}
let (status_code, reason, headers, remaining) = Response::parse_headers_only(&self.buffer)?;
let strategy = Response::body_read_strategy(&headers, status_code);
self.buffer = remaining.to_vec();
self.state = ReaderState::ReadingBody { status_code, strategy };
Ok((status_code, reason, headers, strategy))
}
pub fn is_body_complete(&self) -> bool {
if let ReaderState::ReadingBody { strategy, .. } = self.state {
match strategy {
BodyReadStrategy::NoBody => true,
BodyReadStrategy::ContentLength(expected) => self.buffer.len() >= expected,
BodyReadStrategy::Chunked => FramingDetector::has_chunked_terminator(&self.buffer),
BodyReadStrategy::UntilClose => false,
}
} else {
false
}
}
pub const fn bytes_needed(&self) -> Option<usize> {
if let ReaderState::ReadingBody {
strategy: BodyReadStrategy::ContentLength(expected),
..
} = self.state
{
Some(expected.saturating_sub(self.buffer.len()))
} else {
None
}
}
pub fn finish(
self,
headers: &Headers,
status_code: u16,
) -> Result<Body, ParseError> {
if !matches!(
self.state,
ReaderState::ReadingHeaders | ReaderState::ReadingBody { .. }
) {
return Err(ParseError::InvalidState);
}
Response::parse_body_from_bytes(&self.buffer, headers, status_code)
}
}
impl Default for ResponseReader {
fn default() -> Self {
Self::new()
}
}