net/protocols/
blob.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::future::{Future, ready};
6use std::pin::Pin;
7
8use headers::{HeaderMapExt, Range};
9use http::Method;
10use log::debug;
11use net_traits::blob_url_store::{BlobURLStoreError, parse_blob_url};
12use net_traits::http_status::HttpStatus;
13use net_traits::request::Request;
14use net_traits::response::{Response, ResponseBody};
15use net_traits::{NetworkError, ResourceFetchTiming};
16use tokio::sync::mpsc::unbounded_channel;
17
18use crate::fetch::methods::{Data, DoneChannel, FetchContext};
19use crate::protocols::{ProtocolHandler, partial_content, range_not_satisfiable_error};
20
21#[derive(Default)]
22pub struct BlobProtocolHander {}
23
24impl ProtocolHandler for BlobProtocolHander {
25    fn load(
26        &self,
27        request: &mut Request,
28        done_chan: &mut DoneChannel,
29        context: &FetchContext,
30    ) -> Pin<Box<dyn Future<Output = Response> + Send>> {
31        let url_and_blob_claim = request.current_url_with_blob_claim();
32        debug!("Loading blob {}", url_and_blob_claim.as_str());
33
34        // Step 2.
35        if request.method != Method::GET {
36            return Box::pin(ready(Response::network_error(NetworkError::InvalidMethod)));
37        }
38
39        let range_header = request.headers.typed_get::<Range>();
40        let is_range_request = range_header.is_some();
41
42        let (file_id, origin) = if let Some(token) = url_and_blob_claim.token() {
43            (token.file_id, token.origin.clone())
44        } else {
45            // FIXME: This should never happen, we should have acquired a token beforehand
46            let Ok((id, _)) = parse_blob_url(&url_and_blob_claim.url()) else {
47                return Box::pin(ready(Response::network_error(
48                    NetworkError::ResourceLoadError("Invalid blob URL".into()),
49                )));
50            };
51            (id, url_and_blob_claim.url().origin())
52        };
53
54        let mut response = Response::new(
55            url_and_blob_claim.url(),
56            ResourceFetchTiming::new(request.timing_type()),
57        );
58        response.status = HttpStatus::default();
59
60        if is_range_request {
61            response.range_requested = true;
62            partial_content(&mut response);
63        }
64
65        let (mut done_sender, done_receiver) = unbounded_channel();
66        *done_chan = Some((done_sender.clone(), done_receiver));
67        *response.body.lock() = ResponseBody::Receiving(vec![]);
68
69        if let Err(err) = context.filemanager.fetch_file(
70            &mut done_sender,
71            context.cancellation_listener.clone(),
72            file_id,
73            &context.file_token,
74            origin,
75            &mut response,
76            range_header,
77        ) {
78            let _ = done_sender.send(Data::Done);
79            let err = match err {
80                BlobURLStoreError::InvalidRange => {
81                    range_not_satisfiable_error(&mut response);
82                    return Box::pin(ready(response));
83                },
84                _ => format!("{:?}", err),
85            };
86            return Box::pin(ready(Response::network_error(
87                NetworkError::BlobURLStoreError(err),
88            )));
89        };
90
91        Box::pin(ready(response))
92    }
93}