/
githubmirror
/
servo
Обзор
Документация
Войти
/
githubmirror
/
servo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
components/net/protocols/file.rs
125 строк
5 KB
Xabier Rodríguez
media: support MP4 with moov atom at the end of the file (#45084)
28 май 2026, 09:12
Не верифицирован
28 май 2026, 09:12
81b7090
Код
Авторство
О чём код?
/* 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::{Future, ready}; use std::io::{BufReader, Seek, SeekFrom}; use std::pin::Pin; use headers::{ContentLength, ContentRange, 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::{ ProtocolHandler, get_range_request_bounds, partial_content, range_not_satisfiable_error, }; #[derive(Default)] pub struct FileProtocolHander {} impl ProtocolHandler for FileProtocolHander { fn load<'a>( &'a self, request: &'a mut Request, done_chan: &mut DoneChannel, context: &FetchContext, ) -> Pin<Box<dyn Future<Output = Response> + Send + 'a>> { let url = request.current_url(); if request.method != Method::GET { return Box::pin(ready(Response::network_error(NetworkError::InvalidMethod))); } let response = if let Ok(file_path) = url.to_file_path() { if file_path.is_dir() { return Box::pin(async move { local_directory_listing::fetch(request, url, file_path).await }); } 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, file_size.unwrap_or(0)) .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::InvalidMethod))); } // 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-Length and Content-Range headers when end is determinable. let end_byte = range.end.map(|e| e as u64).or(file_size); if let Some(end_byte) = end_byte { let start_byte = range.start as u64; response .headers .typed_insert(ContentLength(end_byte - start_byte)); if is_range_request && let Ok(content_range) = ContentRange::bytes(start_byte..end_byte, file_size) { response.headers.typed_insert(content_range); } } // 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() = ResponseBody::Receiving(vec![]); context.filemanager.fetch_file_in_chunks( &mut done_sender, reader, response.body.clone(), context.cancellation_listener.clone(), range, ); response } else { Response::network_error(NetworkError::ResourceLoadError( "Opening file failed".into(), )) } } else { Response::network_error(NetworkError::ResourceLoadError( "Constructing file path failed".into(), )) }; Box::pin(ready(response)) } }