use crate::{HttpFgbReader, Result};
use bytes::Bytes;
use http_range_client::AsyncHttpRangeClient;
use std::fs::File;
use std::io::{BufReader, Read, Seek, SeekFrom};
use std::ops::Range;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
impl HttpFgbReader<MockHttpRangeClient> {
pub async fn mock_from_file(
path: &str,
) -> Result<(
HttpFgbReader<MockHttpRangeClient>,
Arc<RwLock<RequestStats>>,
)> {
trace!("starting: opening http reader, reading header");
let stats = Arc::new(RwLock::new(RequestStats::new()));
let http_client = MockHttpRangeClient::new(path, stats.clone());
let client = http_range_client::AsyncBufferedHttpRangeClient::with(http_client, path);
Ok((Self::_open(client).await?, stats))
}
}
pub(crate) struct MockHttpRangeClient {
path: PathBuf,
stats: Arc<RwLock<RequestStats>>,
}
pub(crate) struct RequestStats {
pub request_count: u64,
pub bytes_requested: u64,
}
impl RequestStats {
fn new() -> Self {
Self {
request_count: 0,
bytes_requested: 0,
}
}
}
#[async_trait::async_trait]
impl AsyncHttpRangeClient for MockHttpRangeClient {
async fn get_range(&self, url: &str, range: &str) -> http_range_client::Result<Bytes> {
assert_eq!(url, self.path.to_str().unwrap());
fn parse_range_header(range: &str) -> Range<u64> {
let bytes = range.strip_prefix("bytes=").unwrap();
let parts: Vec<&str> = bytes.split('-').collect();
assert!(parts.len() == 2);
let start = parts[0].parse().expect("should have valid start range");
let end: u64 = parts[1].parse().expect("should have valid end range");
start..(end + 1)
}
let range = parse_range_header(range);
let request_length = range.end - range.start;
let mut stats = self
.stats
.write()
.expect("test code does not handle actual concurrency");
stats.request_count += 1;
stats.bytes_requested += request_length;
let mut file_reader = BufReader::new(File::open(&self.path).unwrap());
file_reader
.seek(SeekFrom::Start(range.start))
.expect("unable to seek test reader");
let mut output = vec![0; request_length as usize];
file_reader
.read_exact(&mut output)
.expect("failed to read from test reader");
Ok(Bytes::from(output))
}
async fn head_response_header(
&self,
_url: &str,
_header: &str,
) -> http_range_client::Result<Option<String>> {
unimplemented!()
}
}
impl MockHttpRangeClient {
fn new(path: &str, stats: Arc<RwLock<RequestStats>>) -> Self {
Self {
path: path.into(),
stats,
}
}
}