/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::future::{Future, ready}; use std::pin::Pin; use headers::{HeaderMapExt, Range}; use http::Method; use log::debug; use net_traits::blob_url_store::{BlobURLStoreError, parse_blob_url}; use net_traits::http_status::HttpStatus; use net_traits::request::Request; use net_traits::response::{Response, ResponseBody}; use net_traits::{NetworkError, ResourceFetchTiming}; use tokio::sync::mpsc::unbounded_channel; use crate::fetch::methods::{Data, DoneChannel, FetchContext}; use crate::protocols::{ProtocolHandler, partial_content, range_not_satisfiable_error}; #[derive(Default)] pub struct BlobProtocolHander {} impl ProtocolHandler for BlobProtocolHander { fn load( &self, request: &mut Request, done_chan: &mut DoneChannel, context: &FetchContext, ) -> Pin + Send>> { let url = request.current_url(); debug!("Loading blob {}", url.as_str()); // Step 2. if request.method != Method::GET { return Box::pin(ready(Response::network_error(NetworkError::Internal( "Unexpected method for blob".into(), )))); } let range_header = request.headers.typed_get::(); let is_range_request = range_header.is_some(); let (id, origin) = match parse_blob_url(&url) { Ok((id, origin)) => (id, origin), Err(error) => { return Box::pin(ready(Response::network_error(NetworkError::Internal( format!("Invalid blob URL ({error})"), )))); }, }; let mut response = Response::new(url, ResourceFetchTiming::new(request.timing_type())); response.status = HttpStatus::default(); if is_range_request { response.range_requested = true; partial_content(&mut response); } let (mut done_sender, done_receiver) = unbounded_channel(); *done_chan = Some((done_sender.clone(), done_receiver)); *response.body.lock().unwrap() = ResponseBody::Receiving(vec![]); if let Err(err) = context.filemanager.lock().unwrap().fetch_file( &mut done_sender, context.cancellation_listener.clone(), id, &context.file_token, origin, &mut response, range_header, ) { let _ = done_sender.send(Data::Done); let err = match err { BlobURLStoreError::InvalidRange => { range_not_satisfiable_error(&mut response); return Box::pin(ready(response)); }, _ => format!("{:?}", err), }; return Box::pin(ready(Response::network_error(NetworkError::Internal(err)))); }; Box::pin(ready(response)) } }