use alloc::collections::VecDeque;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use super::MockHost;
use crate::daemon::http::{Request, Response, Target, too_long};
use crate::error::{Error, Result};
#[derive(Clone, Debug)]
struct Scripted {
method: String,
prefix: String,
answer: Result<Response>,
}
#[derive(Clone, Debug, Default)]
pub(crate) struct MockHttp {
queue: VecDeque<Scripted>,
requests: Vec<Request>,
}
impl MockHost {
pub fn http(mut self, method: &str, url_prefix: &str, response: Response) -> Self {
self.queue_http(method, url_prefix, Ok(response));
self
}
pub fn queue_http(&mut self, method: &str, url_prefix: &str, answer: Result<Response>) {
self.http.queue.push_back(Scripted {
method: method.to_ascii_uppercase(),
prefix: url_prefix.to_string(),
answer,
});
}
pub fn http_requests(&self) -> &[Request] {
&self.http.requests
}
pub fn http_pending(&self) -> usize {
self.http.queue.len()
}
fn send_http(&mut self, request: &Request, target: &Target<'_>) -> Result<Response> {
if !self
.grants
.allows_fetch(target.scheme, &target.host(), target.port())
{
let Error::GrantDenied { grant } = target.denied() else {
unreachable!("a fetch refusal names its grant")
};
self.denials.push(grant.clone());
return Err(Error::GrantDenied { grant });
}
self.http.requests.push(request.clone());
let method = request.method.to_ascii_uppercase();
let index = self.http.queue.iter().position(|scripted| {
(scripted.method == "*" || scripted.method == method)
&& request.url.starts_with(&scripted.prefix)
});
let Some(scripted) = index.and_then(|index| self.http.queue.remove(index)) else {
return Err(Error::Unavailable(alloc::format!(
"no response queued for {method} {}",
request.url
)));
};
let response = scripted.answer?;
if response.body.len() > request.max_body {
return Err(too_long(request.max_body));
}
Ok(response)
}
}
pub(crate) fn send(request: &Request, target: &Target<'_>) -> Result<Response> {
super::with_host(|host| host.send_http(request, target))
}