aboutsummaryrefslogtreecommitdiffstats
path: root/components/net/protocols/blob.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/net/protocols/blob.rs')
-rw-r--r--components/net/protocols/blob.rs91
1 files changed, 91 insertions, 0 deletions
diff --git a/components/net/protocols/blob.rs b/components/net/protocols/blob.rs
new file mode 100644
index 00000000000..c26f95bf8da
--- /dev/null
+++ b/components/net/protocols/blob.rs
@@ -0,0 +1,91 @@
+/* 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::{ready, Future};
+use std::pin::Pin;
+
+use headers::{HeaderMapExt, Range};
+use http::{Method, StatusCode};
+use log::debug;
+use net_traits::blob_url_store::{parse_blob_url, BlobURLStoreError};
+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::{
+ get_range_request_bounds, partial_content, range_not_satisfiable_error, ProtocolHandler,
+};
+
+#[derive(Default)]
+pub struct BlobProtocolHander {}
+
+impl ProtocolHandler for BlobProtocolHander {
+ fn load(
+ &self,
+ request: &mut Request,
+ done_chan: &mut DoneChannel,
+ context: &FetchContext,
+ ) -> Pin<Box<dyn Future<Output = Response> + 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::<Range>();
+ let is_range_request = range_header.is_some();
+ // We will get a final version of this range once we have
+ // the length of the data backing the blob.
+ let range = get_range_request_bounds(range_header);
+
+ 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 = Some((StatusCode::OK, "OK".to_string()));
+ response.raw_status = Some((StatusCode::OK.as_u16(), b"OK".to_vec()));
+
+ if is_range_request {
+ 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,
+ ) {
+ 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))
+ }
+}