diff --git a/src/fs.rs b/src/fs.rs index 7a4dd229c..d92ad1e73 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,36 @@ 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() { - let mime = get_mime_type(&ext.to_string_lossy()); - resp.content_type(format!("{}", mime).as_str()); + 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(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)?; + Ok(resp + .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") + .finish().unwrap()) + } + } else { + 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()); + 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 aaa5230a6..edad519aa 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); @@ -41,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); @@ -113,6 +116,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); @@ -139,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);