mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
make protocol handlers registrable (#33104)
Signed-off-by: webbeef <me@webbeef.org>
This commit is contained in:
parent
562d32c051
commit
663a92a5df
19 changed files with 516 additions and 219 deletions
109
components/net/protocols/file.rs
Normal file
109
components/net/protocols/file.rs
Normal file
|
@ -0,0 +1,109 @@
|
|||
/* 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::fs::File;
|
||||
use std::future::{ready, Future};
|
||||
use std::io::{BufReader, Seek, SeekFrom};
|
||||
use std::pin::Pin;
|
||||
|
||||
use headers::{ContentType, HeaderMapExt, Range};
|
||||
use http::Method;
|
||||
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::{DoneChannel, FetchContext};
|
||||
use crate::filemanager_thread::FILE_CHUNK_SIZE;
|
||||
use crate::local_directory_listing;
|
||||
use crate::protocols::{
|
||||
get_range_request_bounds, partial_content, range_not_satisfiable_error, ProtocolHandler,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FileProtocolHander {}
|
||||
|
||||
impl ProtocolHandler for FileProtocolHander {
|
||||
fn load(
|
||||
&self,
|
||||
request: &mut Request,
|
||||
done_chan: &mut DoneChannel,
|
||||
context: &FetchContext,
|
||||
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let url = request.current_url();
|
||||
|
||||
if request.method != Method::GET {
|
||||
return Box::pin(ready(Response::network_error(NetworkError::Internal(
|
||||
"Unexpected method for file".into(),
|
||||
))));
|
||||
}
|
||||
let response = if let Ok(file_path) = url.to_file_path() {
|
||||
if file_path.is_dir() {
|
||||
return Box::pin(ready(local_directory_listing::fetch(
|
||||
request, url, file_path,
|
||||
)));
|
||||
}
|
||||
|
||||
if let Ok(file) = File::open(file_path.clone()) {
|
||||
// Get range bounds (if any) and try to seek to the requested offset.
|
||||
// If seeking fails, bail out with a NetworkError.
|
||||
let file_size = match file.metadata() {
|
||||
Ok(metadata) => Some(metadata.len()),
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let mut response =
|
||||
Response::new(url, ResourceFetchTiming::new(request.timing_type()));
|
||||
|
||||
let range_header = request.headers.typed_get::<Range>();
|
||||
let is_range_request = range_header.is_some();
|
||||
let Ok(range) = get_range_request_bounds(range_header).get_final(file_size) else {
|
||||
range_not_satisfiable_error(&mut response);
|
||||
return Box::pin(ready(response));
|
||||
};
|
||||
let mut reader = BufReader::with_capacity(FILE_CHUNK_SIZE, file);
|
||||
if reader.seek(SeekFrom::Start(range.start as u64)).is_err() {
|
||||
return Box::pin(ready(Response::network_error(NetworkError::Internal(
|
||||
"Unexpected method for file".into(),
|
||||
))));
|
||||
}
|
||||
|
||||
// Set response status to 206 if Range header is present.
|
||||
// At this point we should have already validated the header.
|
||||
if is_range_request {
|
||||
partial_content(&mut response);
|
||||
}
|
||||
|
||||
// Set Content-Type header.
|
||||
let mime = mime_guess::from_path(file_path).first_or_octet_stream();
|
||||
response.headers.typed_insert(ContentType::from(mime));
|
||||
|
||||
// Setup channel to receive cross-thread messages about the file fetch
|
||||
// operation.
|
||||
let (mut done_sender, done_receiver) = unbounded_channel();
|
||||
*done_chan = Some((done_sender.clone(), done_receiver));
|
||||
|
||||
*response.body.lock().unwrap() = ResponseBody::Receiving(vec![]);
|
||||
|
||||
context.filemanager.lock().unwrap().fetch_file_in_chunks(
|
||||
&mut done_sender,
|
||||
reader,
|
||||
response.body.clone(),
|
||||
context.cancellation_listener.clone(),
|
||||
range,
|
||||
);
|
||||
|
||||
response
|
||||
} else {
|
||||
Response::network_error(NetworkError::Internal("Opening file failed".into()))
|
||||
}
|
||||
} else {
|
||||
Response::network_error(NetworkError::Internal(
|
||||
"Constructing file path failed".into(),
|
||||
))
|
||||
};
|
||||
|
||||
Box::pin(ready(response))
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue