1use 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 = request.current_url();
32 debug!("Loading blob {}", url.as_str());
33
34 if request.method != Method::GET {
36 return Box::pin(ready(Response::network_error(NetworkError::Internal(
37 "Unexpected method for blob".into(),
38 ))));
39 }
40
41 let range_header = request.headers.typed_get::<Range>();
42 let is_range_request = range_header.is_some();
43
44 let (id, origin) = match parse_blob_url(&url) {
45 Ok((id, origin)) => (id, origin),
46 Err(error) => {
47 return Box::pin(ready(Response::network_error(NetworkError::Internal(
48 format!("Invalid blob URL ({error})"),
49 ))));
50 },
51 };
52
53 let mut response = Response::new(url, ResourceFetchTiming::new(request.timing_type()));
54 response.status = HttpStatus::default();
55
56 if is_range_request {
57 response.range_requested = true;
58 partial_content(&mut response);
59 }
60
61 let (mut done_sender, done_receiver) = unbounded_channel();
62 *done_chan = Some((done_sender.clone(), done_receiver));
63 *response.body.lock().unwrap() = ResponseBody::Receiving(vec![]);
64
65 if let Err(err) = context.filemanager.lock().unwrap().fetch_file(
66 &mut done_sender,
67 context.cancellation_listener.clone(),
68 id,
69 &context.file_token,
70 origin,
71 &mut response,
72 range_header,
73 ) {
74 let _ = done_sender.send(Data::Done);
75 let err = match err {
76 BlobURLStoreError::InvalidRange => {
77 range_not_satisfiable_error(&mut response);
78 return Box::pin(ready(response));
79 },
80 _ => format!("{:?}", err),
81 };
82 return Box::pin(ready(Response::network_error(NetworkError::Internal(err))));
83 };
84
85 Box::pin(ready(response))
86 }
87}