use std::sync::{Arc, Mutex};
use content_security_policy as csp;
use net_traits::request::{PreloadEntry, PreloadId, PreloadKey, Request, RequestClient};
use net_traits::response::Response;
use rustc_hash::FxHashMap;
use tokio::sync::oneshot::Receiver as TokioReceiver;
pub enum PreloadResponseCandidate {
None,
Pending(TokioReceiver<Response>, PreloadId),
Response(Box<Response>, PreloadId),
}
impl PreloadResponseCandidate {
pub(crate) async fn response(&mut self) -> Option<(Response, PreloadId)> {
match self {
PreloadResponseCandidate::None => None,
PreloadResponseCandidate::Response(response, preload_id) => {
Some((*response.clone(), preload_id.clone()))
},
PreloadResponseCandidate::Pending(receiver, preload_id) => receiver
.await
.ok()
.map(|response| (response, preload_id.clone())),
}
}
}
pub struct FetchParams {
pub request: Request,
pub preload_response_candidate: PreloadResponseCandidate,
}
impl FetchParams {
pub fn new(request: Request) -> FetchParams {
FetchParams {
request,
preload_response_candidate: PreloadResponseCandidate::None,
}
}
}
pub type SharedPreloadedResources = Arc<Mutex<FxHashMap<PreloadId, PreloadEntry>>>;
pub trait ConsumePreloadedResources {
fn consume_preloaded_resource(
&self,
request: &Request,
preloaded_resources: SharedPreloadedResources,
) -> Option<PreloadResponseCandidate>;
}
impl ConsumePreloadedResources for RequestClient {
fn consume_preloaded_resource(
&self,
request: &Request,
preloaded_resources: SharedPreloadedResources,
) -> Option<PreloadResponseCandidate> {
let key = PreloadKey {
url: request.url(),
destination: request.destination,
mode: request.mode.clone(),
credentials_mode: request.credentials_mode,
};
let preload_id = self.preloaded_resources.get(&key)?;
let mut preloaded_resources_lock = preloaded_resources.lock();
let preloads = preloaded_resources_lock.as_mut().unwrap();
let preload_entry = preloads.get_mut(preload_id)?;
let consumer_integrity_metadata =
csp::parse_subresource_integrity_metadata(&request.integrity_metadata);
let preload_integrity_metadata =
csp::parse_subresource_integrity_metadata(&preload_entry.integrity_metadata);
if !(
consumer_integrity_metadata == csp::SubresourceIntegrityMetadata::NoMetadata
|| consumer_integrity_metadata == preload_integrity_metadata
) {
return None;
}
if let Some(response) = preload_entry.response.as_ref() {
Some(PreloadResponseCandidate::Response(
Box::new(response.clone()),
preload_id.clone(),
))
} else {
let (sender, receiver) = tokio::sync::oneshot::channel();
preload_entry.on_response_available = Some(sender);
Some(PreloadResponseCandidate::Pending(
receiver,
preload_id.clone(),
))
}
}
}