use crate::{HttpError, MAXIMUM_RESPONSE_HEADERS, MAXIMUM_RESPONSE_HEADER_LENGTH};
use httparse::{Response, EMPTY_HEADER};
#[cfg(feature = "runtime-tokio")]
async fn get_response_tokio<IO>(stream: &mut tokio::io::BufStream<IO>) -> Result<String, HttpError>
where
IO: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin,
{
use tokio::io::AsyncBufReadExt;
let mut response = String::new();
loop {
if stream.read_line(&mut response).await? == 0 {
return Err(HttpError::EndOfFile);
}
if MAXIMUM_RESPONSE_HEADER_LENGTH < response.len() {
return Err(HttpError::MaximumResponseHeaderLengthExceeded(response));
}
if response.ends_with("\r\n\r\n") {
return Ok(response);
}
}
}
#[cfg(feature = "runtime-async-std")]
async fn get_response_async_std<R>(read: &mut R) -> Result<String, HttpError>
where
R: async_std::io::Read + Unpin,
{
use async_std::io::prelude::BufReadExt;
let mut reader = async_std::io::BufReader::new(read);
let mut response = String::new();
loop {
if reader.read_line(&mut response).await? == 0 {
return Err(HttpError::EndOfFile);
}
if MAXIMUM_RESPONSE_HEADER_LENGTH < response.len() {
return Err(HttpError::MaximumResponseHeaderLengthExceeded(response));
}
if response.ends_with("\r\n\r\n") {
return Ok(response);
}
}
}
fn check_code(response: &Response<'_, '_>) -> Result<(), HttpError> {
match response.code {
Some(code) => {
if code == 200 {
Ok(())
} else {
Err(HttpError::HttpCode200(code))
}
}
None => Err(HttpError::NoHttpCode),
}
}
fn parse_and_check(response_string: &str) -> Result<(), HttpError> {
let mut response_headers = [EMPTY_HEADER; MAXIMUM_RESPONSE_HEADERS];
let mut response = Response::new(&mut response_headers[..]);
response.parse(response_string.as_bytes())?;
check_code(&response)?;
Ok(())
}
#[cfg(feature = "runtime-tokio")]
pub(crate) async fn recv_and_check_response_tokio<IO>(
stream: &mut tokio::io::BufStream<IO>,
) -> Result<(), HttpError>
where
IO: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin,
{
let response_string = get_response_tokio(stream).await?;
parse_and_check(&response_string)?;
Ok(())
}
#[cfg(feature = "runtime-async-std")]
pub(crate) async fn recv_and_check_response_async_std<R>(read: &mut R) -> Result<(), HttpError>
where
R: async_std::io::Read + Unpin,
{
let response_string = get_response_async_std(read).await?;
parse_and_check(&response_string)?;
Ok(())
}