diff options
author | Fernando Jiménez Moreno <ferjmoreno@gmail.com> | 2018-11-15 06:45:13 +0100 |
---|---|---|
committer | Fernando Jiménez Moreno <ferjmoreno@gmail.com> | 2018-11-26 09:34:14 +0100 |
commit | 79d27cb7ca0267092496dbbef24844ca5ab14fb4 (patch) | |
tree | 7c53a594436f5025150e55b400562caf1d65df55 /components/net/fetch/methods.rs | |
parent | b96e5681aaabf7b49411aa3c1b56f86c7e8570e8 (diff) | |
download | servo-79d27cb7ca0267092496dbbef24844ca5ab14fb4.tar.gz servo-79d27cb7ca0267092496dbbef24844ca5ab14fb4.zip |
Set response status for range requests to file and blob urls
Diffstat (limited to 'components/net/fetch/methods.rs')
-rw-r--r-- | components/net/fetch/methods.rs | 70 |
1 files changed, 59 insertions, 11 deletions
diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index a15045bef4b..19736de1136 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -18,7 +18,7 @@ use hyper::StatusCode; use ipc_channel::ipc::IpcReceiver; use mime::{self, Mime}; use mime_guess::guess_mime_type; -use net_traits::blob_url_store::parse_blob_url; +use net_traits::blob_url_store::{parse_blob_url, BlobURLStoreError}; use net_traits::filemanager_thread::RelativePos; use net_traits::request::{CredentialsMode, Destination, Referrer, Request, RequestMode}; use net_traits::request::{Origin, ResponseTainting, Window}; @@ -500,17 +500,24 @@ pub enum RangeRequestBounds { } impl RangeRequestBounds { - pub fn get_final(&self, len: Option<u64>) -> RelativePos { + pub fn get_final(&self, len: Option<u64>) -> Result<RelativePos, ()> { match self { - RangeRequestBounds::Final(pos) => pos.clone(), - RangeRequestBounds::Pending(offset) => RelativePos::from_opts( + RangeRequestBounds::Final(pos) => { + if let Some(len) = len { + if pos.start <= len as i64 { + return Ok(pos.clone()); + } + } + Err(()) + }, + RangeRequestBounds::Pending(offset) => Ok(RelativePos::from_opts( if let Some(len) = len { Some((len - u64::min(len, *offset)) as i64) } else { Some(0) }, None, - ), + )), } } } @@ -539,6 +546,18 @@ fn get_range_request_bounds(range: Option<Range>) -> RangeRequestBounds { } } +fn partial_content(response: &mut Response) { + let reason = "Partial Content".to_owned(); + response.status = Some((StatusCode::PARTIAL_CONTENT, reason.clone())); + response.raw_status = Some((StatusCode::PARTIAL_CONTENT.as_u16(), reason.into())); +} + +fn range_not_satisfiable_error(response: &mut Response) { + let reason = "Range Not Satisfiable".to_owned(); + response.status = Some((StatusCode::RANGE_NOT_SATISFIABLE, reason.clone())); + response.raw_status = Some((StatusCode::RANGE_NOT_SATISFIABLE.as_u16(), reason.into())); +} + /// [Scheme fetch](https://fetch.spec.whatwg.org#scheme-fetch) fn scheme_fetch( request: &mut Request, @@ -584,16 +603,24 @@ fn scheme_fetch( } if let Ok(file_path) = url.to_file_path() { if let Ok(file) = File::open(file_path.clone()) { - let mut response = Response::new(url, ResourceFetchTiming::new(request.timing_type())); - // 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 range = get_range_request_bounds(request.headers.typed_get::<Range>()); - let range = range.get_final(file_size); + + 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 range = match get_range_request_bounds(range_header).get_final(file_size) { + Ok(range) => range, + Err(_) => { + range_not_satisfiable_error(&mut response); + return response; + }, + }; let mut reader = BufReader::with_capacity(FILE_CHUNK_SIZE, file); if reader.seek(SeekFrom::Start(range.start as u64)).is_err() { return Response::network_error(NetworkError::Internal( @@ -601,6 +628,12 @@ fn scheme_fetch( )); } + // 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 = guess_mime_type(file_path); response.headers.typed_insert(ContentType::from(mime)); @@ -631,7 +664,7 @@ fn scheme_fetch( }, "blob" => { - println!("Loading blob {}", url.as_str()); + debug!("Loading blob {}", url.as_str()); // Step 2. if request.method != Method::GET { return Response::network_error(NetworkError::Internal( @@ -639,7 +672,11 @@ fn scheme_fetch( )); } - let range = get_range_request_bounds(request.headers.typed_get::<Range>()); + 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), @@ -651,6 +688,10 @@ fn scheme_fetch( }; let mut response = Response::new(url); + if is_range_request { + partial_content(&mut response); + } + let (done_sender, done_receiver) = channel(); *done_chan = Some((done_sender.clone(), done_receiver)); *response.body.lock().unwrap() = ResponseBody::Receiving(vec![]); @@ -665,6 +706,13 @@ fn scheme_fetch( range, ) { let _ = done_sender.send(Data::Done); + let err = match err { + BlobURLStoreError::InvalidRange => { + range_not_satisfiable_error(&mut response); + return response; + }, + _ => format!("{:?}", err), + }; return Response::network_error(NetworkError::Internal(err)); }; |