From bff6c2caf49a146efe4de2aa2fbfa46133ffdb8e Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 24 Jan 2018 22:54:08 +0100 Subject: [PATCH 1/5] Partial Content status functions added. --- src/httpcodes.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/httpcodes.rs b/src/httpcodes.rs index aaa5230a6..50eb90917 100644 --- a/src/httpcodes.rs +++ b/src/httpcodes.rs @@ -10,6 +10,7 @@ use httpresponse::{HttpResponse, HttpResponseBuilder}; pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK); pub const HTTPCreated: StaticResponse = StaticResponse(StatusCode::CREATED); pub const HTTPNoContent: StaticResponse = StaticResponse(StatusCode::NO_CONTENT); +pub const HTTPPartialContent: StaticResponse = StaticResponse(StatusCode::PARTIAL_CONTENT); pub const HTTPMultipleChoices: StaticResponse = StaticResponse(StatusCode::MULTIPLE_CHOICES); pub const HTTPMovedPermanenty: StaticResponse = StaticResponse(StatusCode::MOVED_PERMANENTLY); @@ -113,6 +114,7 @@ impl HttpResponse { STATIC_RESP!(Ok, StatusCode::OK); STATIC_RESP!(Created, StatusCode::CREATED); STATIC_RESP!(NoContent, StatusCode::NO_CONTENT); + STATIC_RESP!(PartialContent, StatusCode::PARTIAL_CONTENT); STATIC_RESP!(MultipleChoices, StatusCode::MULTIPLE_CHOICES); STATIC_RESP!(MovedPermanenty, StatusCode::MOVED_PERMANENTLY); From de7c05c45efbf31139ccd7737bbc539ed273dc40 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 14 Feb 2018 23:28:35 +0100 Subject: [PATCH 2/5] #60: Basic Ranges header support --- src/error.rs | 6 ++++++ src/fs.rs | 41 +++++++++++++++++++++++++++++------------ src/httpcodes.rs | 3 +++ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/error.rs b/src/error.rs index 084249217..f9bbca241 100644 --- a/src/error.rs +++ b/src/error.rs @@ -627,6 +627,12 @@ pub fn ErrorPreconditionFailed(err: T) -> InternalError { InternalError::new(err, StatusCode::PRECONDITION_FAILED) } +/// Helper function that creates wrapper of any error and generate *RANGE NOT SATISFIABLE* response. +#[allow(non_snake_case)] +pub fn ErrorRangeNotSatisfiable(err: T) -> InternalError { + InternalError::new(err, StatusCode::RANGE_NOT_SATISFIABLE) +} + /// Helper function that creates wrapper of any error and generate *EXPECTATION FAILED* response. #[allow(non_snake_case)] pub fn ErrorExpectationFailed(err: T) -> InternalError { diff --git a/src/fs.rs b/src/fs.rs index 7a4dd229c..02e9f0fa3 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -2,9 +2,9 @@ // //! TODO: needs to re-implement actual files handling, current impl blocks use std::io; -use std::io::Read; +use std::io::{Read,Seek,SeekFrom}; use std::fmt::Write; -use std::fs::{File, DirEntry}; +use std::fs::{File, DirEntry, metadata}; use std::path::{Path, PathBuf}; use std::ops::{Deref, DerefMut}; @@ -12,10 +12,11 @@ use mime_guess::get_mime_type; use param::FromParam; use handler::{Handler, Responder}; -use headers::ContentEncoding; +use headers::{ContentEncoding,HttpRange}; +use http::header; use httprequest::HttpRequest; use httpresponse::HttpResponse; -use httpcodes::HTTPOk; +use httpcodes::{HTTPOk,HTTPPartialContent,HTTPRangeNotSatisfiable}; /// A file with an associated name; responds with the Content-Type based on the /// file extension. @@ -83,16 +84,32 @@ impl Responder for NamedFile { type Item = HttpResponse; type Error = io::Error; - fn respond_to(mut self, _: HttpRequest) -> Result { - let mut resp = HTTPOk.build(); - resp.content_encoding(ContentEncoding::Identity); - if let Some(ext) = self.path().extension() { + fn respond_to(mut self, req: HttpRequest) -> Result { + if let Ok(rangeheader) = req.headers().get("range").unwrap().to_str() { + let file_metadata = metadata(self.0)?; + if let Ok(ranges) = HttpRange::parse(rangeheader, file_metadata.len()) { + let mut resp = HTTPPartialContent.build(); + let length: usize = ranges[0].length as usize; + let mut data: Vec = vec![0u8; length]; + let _ = &self.1.seek(SeekFrom::Start(ranges[0].start))?; + let _ = self.1.read_exact(&mut data); + Ok(resp.body(data).unwrap()) + } else { + Ok(HTTPRangeNotSatisfiable.build() + .header(header::CONTENT_RANGE, format!("0-{}", file_metadata.len()).as_str()) + .body("").unwrap()) + } + } else { + let mut resp = HTTPOk.build(); + resp.content_encoding(ContentEncoding::Identity); + if let Some(ext) = self.path().extension() { let mime = get_mime_type(&ext.to_string_lossy()); - resp.content_type(format!("{}", mime).as_str()); + resp.content_type(format!("{}", mime).as_str()); + } + let mut data = Vec::new(); + let _ = self.1.read_to_end(&mut data); + Ok(resp.body(data).unwrap()) } - let mut data = Vec::new(); - let _ = self.1.read_to_end(&mut data); - Ok(resp.body(data).unwrap()) } } diff --git a/src/httpcodes.rs b/src/httpcodes.rs index 50eb90917..edad519aa 100644 --- a/src/httpcodes.rs +++ b/src/httpcodes.rs @@ -42,6 +42,8 @@ pub const HTTPPreconditionFailed: StaticResponse = StaticResponse(StatusCode::PRECONDITION_FAILED); pub const HTTPPayloadTooLarge: StaticResponse = StaticResponse(StatusCode::PAYLOAD_TOO_LARGE); pub const HTTPUriTooLong: StaticResponse = StaticResponse(StatusCode::URI_TOO_LONG); +pub const HTTPRangeNotSatisfiable: StaticResponse = + StaticResponse(StatusCode::RANGE_NOT_SATISFIABLE); pub const HTTPExpectationFailed: StaticResponse = StaticResponse(StatusCode::EXPECTATION_FAILED); @@ -141,6 +143,7 @@ impl HttpResponse { STATIC_RESP!(PreconditionFailed, StatusCode::PRECONDITION_FAILED); STATIC_RESP!(PayloadTooLarge, StatusCode::PAYLOAD_TOO_LARGE); STATIC_RESP!(UriTooLong, StatusCode::URI_TOO_LONG); + STATIC_RESP!(RangeNotSatisfiable, StatusCode::RANGE_NOT_SATISFIABLE); STATIC_RESP!(ExpectationFailed, StatusCode::EXPECTATION_FAILED); STATIC_RESP!(InternalServerError, StatusCode::INTERNAL_SERVER_ERROR); From e21f01c41db00dc1d14818722b74a50a81cb23e5 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 15 Feb 2018 00:06:12 +0100 Subject: [PATCH 3/5] #60: Basic Ranges header support --- src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 02e9f0fa3..59bbe9e7f 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -85,9 +85,9 @@ impl Responder for NamedFile { type Error = io::Error; fn respond_to(mut self, req: HttpRequest) -> Result { - if let Ok(rangeheader) = req.headers().get("range").unwrap().to_str() { + if let Some(rangeheader) = req.headers().get("range") { let file_metadata = metadata(self.0)?; - if let Ok(ranges) = HttpRange::parse(rangeheader, file_metadata.len()) { + if let Ok(ranges) = HttpRange::parse(rangeheader.to_str().unwrap(), file_metadata.len()) { let mut resp = HTTPPartialContent.build(); let length: usize = ranges[0].length as usize; let mut data: Vec = vec![0u8; length]; From 9e9ae13f25c7761e3e90319fc238e780b57a7c54 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 18 Feb 2018 23:07:10 +0100 Subject: [PATCH 4/5] Add content-range and accept-ranges header --- src/fs.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 59bbe9e7f..36efcf0c0 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -85,7 +85,7 @@ impl Responder for NamedFile { type Error = io::Error; fn respond_to(mut self, req: HttpRequest) -> Result { - if let Some(rangeheader) = req.headers().get("range") { + if let Some(rangeheader) = req.headers().get(header::RANGE) { let file_metadata = metadata(self.0)?; if let Ok(ranges) = HttpRange::parse(rangeheader.to_str().unwrap(), file_metadata.len()) { let mut resp = HTTPPartialContent.build(); @@ -93,14 +93,19 @@ impl Responder for NamedFile { let mut data: Vec = vec![0u8; length]; let _ = &self.1.seek(SeekFrom::Start(ranges[0].start))?; let _ = self.1.read_exact(&mut data); - Ok(resp.body(data).unwrap()) + Ok(resp + .header(header::CONTENT_RANGE, format!("bytes {}-{}/{}", ranges[0].start, ranges[0].length, file_metadata.len()).as_str()) + .body(data).unwrap()) } else { Ok(HTTPRangeNotSatisfiable.build() - .header(header::CONTENT_RANGE, format!("0-{}", file_metadata.len()).as_str()) + .header(header::CONTENT_RANGE, format!("bytes */{}", file_metadata.len()).as_str()) + .header(header::ACCEPT_RANGES, "bytes") .body("").unwrap()) } } else { + //let file_metadata = metadata(self.0)?; let mut resp = HTTPOk.build(); + resp.header(header::ACCEPT_RANGES, "bytes"); resp.content_encoding(ContentEncoding::Identity); if let Some(ext) = self.path().extension() { let mime = get_mime_type(&ext.to_string_lossy()); From ac573406dfd989e6ac0bbcaec63d86dc3824ffc0 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 23 Feb 2018 01:32:52 +0100 Subject: [PATCH 5/5] #60 Ranges Header support --- src/fs.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 36efcf0c0..d92ad1e73 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -87,28 +87,27 @@ impl Responder for NamedFile { fn respond_to(mut self, req: HttpRequest) -> Result { if let Some(rangeheader) = req.headers().get(header::RANGE) { let file_metadata = metadata(self.0)?; - if let Ok(ranges) = HttpRange::parse(rangeheader.to_str().unwrap(), file_metadata.len()) { + if let Ok(ranges) = HttpRange::parse(try!(rangeheader.to_str()), file_metadata.len()) { let mut resp = HTTPPartialContent.build(); let length: usize = ranges[0].length as usize; let mut data: Vec = vec![0u8; length]; let _ = &self.1.seek(SeekFrom::Start(ranges[0].start))?; - let _ = self.1.read_exact(&mut data); + let _ = self.1.read_exact(&mut data)?; Ok(resp - .header(header::CONTENT_RANGE, format!("bytes {}-{}/{}", ranges[0].start, ranges[0].length, file_metadata.len()).as_str()) + .header(header::CONTENT_RANGE, format!("bytes {}-{}/{}", ranges[0].start, ranges[0].start + ranges[0].length, file_metadata.len()).as_str()) .body(data).unwrap()) } else { Ok(HTTPRangeNotSatisfiable.build() .header(header::CONTENT_RANGE, format!("bytes */{}", file_metadata.len()).as_str()) .header(header::ACCEPT_RANGES, "bytes") - .body("").unwrap()) + .finish().unwrap()) } } else { - //let file_metadata = metadata(self.0)?; let mut resp = HTTPOk.build(); resp.header(header::ACCEPT_RANGES, "bytes"); resp.content_encoding(ContentEncoding::Identity); if let Some(ext) = self.path().extension() { - let mime = get_mime_type(&ext.to_string_lossy()); + let mime = get_mime_type(&ext.to_string_lossy()); resp.content_type(format!("{}", mime).as_str()); } let mut data = Vec::new();