use bytes::BytesMut;
use log::error;
use pingora_core::{Error, ErrorType};
use pingora_http::StatusCode;
use pingora_proxy::Session;
use std::cmp::min;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
use crate::compression::Compression;
const BUFFER_SIZE: usize = 64 * 1024;
pub(crate) async fn file_response(
session: &mut Session,
path: &Path,
start: u64,
end: u64,
compression: &Compression<'_>,
) -> Result<(), Box<Error>> {
let mut file = File::open(path).map_err(|err| {
error!("failed opening file {path:?}: {err}");
Error::new(ErrorType::HTTPStatus(
StatusCode::INTERNAL_SERVER_ERROR.into(),
))
})?;
if start != 0 {
file.seek(SeekFrom::Start(start)).map_err(|err| {
error!("failed seeking in file {path:?}: {err}");
Error::new(ErrorType::HTTPStatus(
StatusCode::INTERNAL_SERVER_ERROR.into(),
))
})?;
}
let mut remaining = (end - start + 1) as usize;
while remaining > 0 {
let mut buf = BytesMut::zeroed(min(remaining, BUFFER_SIZE));
let len = file.read(buf.as_mut()).map_err(|err| {
error!("failed reading data from {path:?}: {err}");
Error::new(ErrorType::HTTPStatus(
StatusCode::INTERNAL_SERVER_ERROR.into(),
))
})?;
if len == 0 {
error!("file ended with {remaining} bytes left to be written");
return Err(Error::new(ErrorType::ReadError));
}
buf.truncate(len);
if let Some(bytes) = compression.transform_body(session, Some(buf.into())) {
session.write_response_body(bytes).await?;
}
remaining -= len;
}
if let Some(bytes) = compression.transform_body(session, None) {
session.write_response_body(bytes).await?;
}
Ok(())
}