use crate::rangeext::RangeExt;
use ehttpd::bytes::{Data, Source};
use ehttpd::err;
use ehttpd::error::Error;
use ehttpd::http::Response;
use std::fs::File;
use std::io::{BufReader, Read, Seek, SeekFrom};
use std::ops::{Range, RangeBounds, RangeInclusive};
pub trait RangeResponse
where
Self: Sized,
{
fn new_206_partial_content() -> Self;
fn set_accept_ranges_bytes(&mut self);
fn set_accept_ranges_none(&mut self);
fn set_content_range<T>(&mut self, range: T, total: u64) -> Result<(), Error>
where
T: RangeBounds<u64>;
fn set_body_data_range<T, R>(&mut self, data: T, range: R) -> Result<(), Error>
where
T: Into<Data>,
R: RangeBounds<usize>;
fn set_body_file_range<T, R>(&mut self, file: T, range: R) -> Result<(), Error>
where
T: Into<File>,
R: RangeBounds<u64>;
}
impl<const HEADER_SIZE_MAX: usize> RangeResponse for Response<HEADER_SIZE_MAX> {
fn new_206_partial_content() -> Self {
Self::new_status_reason(206, "Partial Content")
}
fn set_accept_ranges_bytes(&mut self) {
self.set_field("Accept-Ranges", "bytes")
}
fn set_accept_ranges_none(&mut self) {
self.set_field("Accept-Ranges", "none")
}
fn set_content_range<T>(&mut self, range: T, total: u64) -> Result<(), Error>
where
T: RangeBounds<u64>,
{
let range =
RangeInclusive::from_range_bounds(range, 0, total).ok_or_else(|| err!("Range would exceed total limit"))?;
let range_string = format!("bytes {}-{}/{total}", range.start(), range.end());
self.set_field("Content-Range", range_string);
Ok(())
}
fn set_body_data_range<T, R>(&mut self, data: T, range: R) -> Result<(), Error>
where
T: Into<Data>,
R: RangeBounds<usize>,
{
if !self.status.eq(b"206") {
return Err(err!("Response is not a 206 response"));
}
let data: Data = data.into();
let Range { start, end } =
Range::from_range_bounds(range, 0, data.len()).ok_or_else(|| err!("Range would exceed data size"))?;
let subdata = data.subcopy(start..end).expect("range would exceed data size");
self.set_content_range(start as u64..end as u64, data.len() as u64)?;
self.set_body_data(subdata);
Ok(())
}
fn set_body_file_range<T, R>(&mut self, file: T, range: R) -> Result<(), Error>
where
T: Into<File>,
R: RangeBounds<u64>,
{
if !self.status.eq(b"206") {
return Err(err!("Response is not a 206 response"));
}
let mut file: File = file.into();
let file_size = file.metadata()?.len();
let Range { start, end } =
Range::from_range_bounds(range, 0, file_size).ok_or_else(|| err!("Range would exceed file size"))?;
let len = end.saturating_sub(start);
file.seek(SeekFrom::Start(start))?;
let file = file.take(end.saturating_sub(start));
self.set_content_range(start..end, file_size)?;
self.set_content_length(len);
let file = BufReader::new(file);
self.body = Source::new(file);
Ok(())
}
}