net/fetch/
fetch_params.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::sync::{Arc, Mutex};
6
7use content_security_policy as csp;
8use net_traits::request::{PreloadEntry, PreloadId, PreloadKey, Request, RequestClient};
9use net_traits::response::Response;
10use rustc_hash::FxHashMap;
11use tokio::sync::oneshot::Receiver as TokioReceiver;
12
13/// <https://fetch.spec.whatwg.org/#fetch-params-preloaded-response-candidate>
14pub enum PreloadResponseCandidate {
15    None,
16    Pending(TokioReceiver<Response>, PreloadId),
17    Response(Box<Response>, PreloadId),
18}
19
20impl PreloadResponseCandidate {
21    /// Part of Step 12 of <https://fetch.spec.whatwg.org/#concept-main-fetch>
22    pub(crate) async fn response(&mut self) -> Option<(Response, PreloadId)> {
23        // Step 3. Return fetchParams’s preloaded response candidate.
24        match self {
25            PreloadResponseCandidate::None => None,
26            // Step 2. Assert: fetchParams’s preloaded response candidate is a response.
27            PreloadResponseCandidate::Response(response, preload_id) => {
28                Some((*response.clone(), preload_id.clone()))
29            },
30            // Step 1. Wait until fetchParams’s preloaded response candidate is not "pending".
31            PreloadResponseCandidate::Pending(receiver, preload_id) => receiver
32                .await
33                .ok()
34                .map(|response| (response, preload_id.clone())),
35        }
36    }
37}
38
39/// <https://fetch.spec.whatwg.org/#fetch-params>
40pub struct FetchParams {
41    /// <https://fetch.spec.whatwg.org/#fetch-params-request>
42    pub request: Request,
43    /// <https://fetch.spec.whatwg.org/#fetch-params-preloaded-response-candidate>
44    pub preload_response_candidate: PreloadResponseCandidate,
45}
46
47impl FetchParams {
48    pub fn new(request: Request) -> FetchParams {
49        FetchParams {
50            request,
51            preload_response_candidate: PreloadResponseCandidate::None,
52        }
53    }
54}
55
56pub type SharedPreloadedResources = Arc<Mutex<FxHashMap<PreloadId, PreloadEntry>>>;
57
58pub trait ConsumePreloadedResources {
59    fn consume_preloaded_resource(
60        &self,
61        request: &Request,
62        preloaded_resources: SharedPreloadedResources,
63    ) -> Option<PreloadResponseCandidate>;
64}
65
66impl ConsumePreloadedResources for RequestClient {
67    /// <https://html.spec.whatwg.org/multipage/#consume-a-preloaded-resource>
68    fn consume_preloaded_resource(
69        &self,
70        request: &Request,
71        preloaded_resources: SharedPreloadedResources,
72    ) -> Option<PreloadResponseCandidate> {
73        // Step 1. Let key be a preload key whose URL is url,
74        // destination is destination, mode is mode, and credentials mode is credentialsMode.
75        let key = PreloadKey {
76            url: request.url().clone(),
77            destination: request.destination,
78            mode: request.mode.clone(),
79            credentials_mode: request.credentials_mode,
80        };
81        // Step 2. Let preloads be window's associated Document's map of preloaded resources.
82        // Step 3. If key does not exist in preloads, then return false.
83        let preload_id = self.preloaded_resources.get(&key)?;
84
85        // Step 4. Let entry be preloads[key].
86        let mut preloaded_resources_lock = preloaded_resources.lock();
87        let preloads = preloaded_resources_lock.as_mut().unwrap();
88        let preload_entry = preloads.get_mut(preload_id)?;
89
90        // Step 5. Let consumerIntegrityMetadata be the result of parsing integrityMetadata.
91        let consumer_integrity_metadata =
92            csp::parse_subresource_integrity_metadata(&request.integrity_metadata);
93        // Step 6. Let preloadIntegrityMetadata be the result of parsing entry's integrity metadata.
94        let preload_integrity_metadata =
95            csp::parse_subresource_integrity_metadata(&preload_entry.integrity_metadata);
96        // Step 7. If none of the following conditions apply:
97        if !(
98            // consumerIntegrityMetadata is no metadata;
99            consumer_integrity_metadata == csp::SubresourceIntegrityMetadata::NoMetadata
100            // consumerIntegrityMetadata is equal to preloadIntegrityMetadata; or
101            || consumer_integrity_metadata == preload_integrity_metadata
102        ) {
103            // then return false.
104            return None;
105        }
106
107        // Step 8. Remove preloads[key].
108        // Note: `self.preloads` is an immutable copy of the list of preloads
109        //       that is not shared with any other running fetch.
110        //       Instead, we defer removing the associated PreloadId->PreloadEntry
111        //       map entry until the entire response has been preloaded, since
112        //       it stores the Sender which will be used to transmit the complete
113        //       response.
114
115        // Step 10. Otherwise, call onResponseAvailable with entry's response.
116        if let Some(response) = preload_entry.response.as_ref() {
117            Some(PreloadResponseCandidate::Response(
118                Box::new(response.clone()),
119                preload_id.clone(),
120            ))
121        } else {
122            // Step 9. If entry's response is null, then set entry's on response available to onResponseAvailable.
123            let (sender, receiver) = tokio::sync::oneshot::channel();
124            preload_entry.on_response_available = Some(sender);
125            // Step 11. Return true.
126            Some(PreloadResponseCandidate::Pending(
127                receiver,
128                preload_id.clone(),
129            ))
130        }
131    }
132}