I <3 vertical whitespace

This commit is contained in:
Rob Ede 2020-09-16 00:25:28 +01:00
parent 3046980540
commit c6e2319f71
No known key found for this signature in database
GPG Key ID: C2A3B36E841A91E6
5 changed files with 51 additions and 27 deletions

View File

@ -12,7 +12,7 @@ use actix_web::{
web, web,
}; };
use bytes::Bytes; use bytes::Bytes;
use futures_core::Stream; use futures_core::{ready, Stream};
use futures_util::future::{FutureExt, LocalBoxFuture}; use futures_util::future::{FutureExt, LocalBoxFuture};
use crate::handle_error; use crate::handle_error;
@ -39,16 +39,17 @@ impl Stream for ChunkedReadFile {
cx: &mut Context<'_>, cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> { ) -> Poll<Option<Self::Item>> {
if let Some(ref mut fut) = self.fut { if let Some(ref mut fut) = self.fut {
return match Pin::new(fut).poll(cx) { return match ready!(Pin::new(fut).poll(cx)) {
Poll::Ready(Ok((file, bytes))) => { Ok((file, bytes)) => {
self.fut.take(); self.fut.take();
self.file = Some(file); self.file = Some(file);
self.offset += bytes.len() as u64; self.offset += bytes.len() as u64;
self.counter += bytes.len() as u64; self.counter += bytes.len() as u64;
Poll::Ready(Some(Ok(bytes))) Poll::Ready(Some(Ok(bytes)))
} }
Poll::Ready(Err(e)) => Poll::Ready(Some(Err(handle_error(e)))), Err(e) => Poll::Ready(Some(Err(handle_error(e)))),
Poll::Pending => Poll::Pending,
}; };
} }
@ -60,21 +61,27 @@ impl Stream for ChunkedReadFile {
Poll::Ready(None) Poll::Ready(None)
} else { } else {
let mut file = self.file.take().expect("Use after completion"); let mut file = self.file.take().expect("Use after completion");
self.fut = Some( self.fut = Some(
web::block(move || { web::block(move || {
let max_bytes: usize; let max_bytes =
max_bytes = cmp::min(size.saturating_sub(counter), 65_536) as usize; cmp::min(size.saturating_sub(counter), 65_536) as usize;
let mut buf = Vec::with_capacity(max_bytes); let mut buf = Vec::with_capacity(max_bytes);
file.seek(io::SeekFrom::Start(offset))?; file.seek(io::SeekFrom::Start(offset))?;
let nbytes =
let n_bytes =
file.by_ref().take(max_bytes as u64).read_to_end(&mut buf)?; file.by_ref().take(max_bytes as u64).read_to_end(&mut buf)?;
if nbytes == 0 {
if n_bytes == 0 {
return Err(io::ErrorKind::UnexpectedEof.into()); return Err(io::ErrorKind::UnexpectedEof.into());
} }
Ok((file, Bytes::from(buf))) Ok((file, Bytes::from(buf)))
}) })
.boxed_local(), .boxed_local(),
); );
self.poll_next(cx) self.poll_next(cx)
} }
} }

View File

@ -93,8 +93,10 @@ impl NamedFile {
mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline, mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline,
_ => DispositionType::Attachment, _ => DispositionType::Attachment,
}; };
let mut parameters = let mut parameters =
vec![DispositionParam::Filename(String::from(filename.as_ref()))]; vec![DispositionParam::Filename(String::from(filename.as_ref()))];
if !filename.is_ascii() { if !filename.is_ascii() {
parameters.push(DispositionParam::FilenameExt(ExtendedValue { parameters.push(DispositionParam::FilenameExt(ExtendedValue {
charset: Charset::Ext(String::from("UTF-8")), charset: Charset::Ext(String::from("UTF-8")),
@ -102,16 +104,19 @@ impl NamedFile {
value: filename.into_owned().into_bytes(), value: filename.into_owned().into_bytes(),
})) }))
} }
let cd = ContentDisposition { let cd = ContentDisposition {
disposition, disposition,
parameters, parameters,
}; };
(ct, cd) (ct, cd)
}; };
let md = file.metadata()?; let md = file.metadata()?;
let modified = md.modified().ok(); let modified = md.modified().ok();
let encoding = None; let encoding = None;
Ok(NamedFile { Ok(NamedFile {
path, path,
file, file,
@ -259,6 +264,7 @@ impl NamedFile {
pub fn into_response(self, req: &HttpRequest) -> Result<HttpResponse, Error> { pub fn into_response(self, req: &HttpRequest) -> Result<HttpResponse, Error> {
if self.status_code != StatusCode::OK { if self.status_code != StatusCode::OK {
let mut resp = HttpResponse::build(self.status_code); let mut resp = HttpResponse::build(self.status_code);
resp.set(header::ContentType(self.content_type.clone())) resp.set(header::ContentType(self.content_type.clone()))
.if_true(self.flags.contains(Flags::CONTENT_DISPOSITION), |res| { .if_true(self.flags.contains(Flags::CONTENT_DISPOSITION), |res| {
res.header( res.header(
@ -266,9 +272,11 @@ impl NamedFile {
self.content_disposition.to_string(), self.content_disposition.to_string(),
); );
}); });
if let Some(current_encoding) = self.encoding { if let Some(current_encoding) = self.encoding {
resp.encoding(current_encoding); resp.encoding(current_encoding);
} }
let reader = ChunkedReadFile { let reader = ChunkedReadFile {
size: self.md.len(), size: self.md.len(),
offset: 0, offset: 0,
@ -276,6 +284,7 @@ impl NamedFile {
fut: None, fut: None,
counter: 0, counter: 0,
}; };
return Ok(resp.streaming(reader)); return Ok(resp.streaming(reader));
} }
@ -284,6 +293,7 @@ impl NamedFile {
} else { } else {
None None
}; };
let last_modified = if self.flags.contains(Flags::LAST_MD) { let last_modified = if self.flags.contains(Flags::LAST_MD) {
self.last_modified() self.last_modified()
} else { } else {
@ -298,6 +308,7 @@ impl NamedFile {
{ {
let t1: SystemTime = m.clone().into(); let t1: SystemTime = m.clone().into();
let t2: SystemTime = since.clone().into(); let t2: SystemTime = since.clone().into();
match (t1.duration_since(UNIX_EPOCH), t2.duration_since(UNIX_EPOCH)) { match (t1.duration_since(UNIX_EPOCH), t2.duration_since(UNIX_EPOCH)) {
(Ok(t1), Ok(t2)) => t1 > t2, (Ok(t1), Ok(t2)) => t1 > t2,
_ => false, _ => false,
@ -316,6 +327,7 @@ impl NamedFile {
{ {
let t1: SystemTime = m.clone().into(); let t1: SystemTime = m.clone().into();
let t2: SystemTime = since.clone().into(); let t2: SystemTime = since.clone().into();
match (t1.duration_since(UNIX_EPOCH), t2.duration_since(UNIX_EPOCH)) { match (t1.duration_since(UNIX_EPOCH), t2.duration_since(UNIX_EPOCH)) {
(Ok(t1), Ok(t2)) => t1 <= t2, (Ok(t1), Ok(t2)) => t1 <= t2,
_ => false, _ => false,
@ -332,6 +344,7 @@ impl NamedFile {
self.content_disposition.to_string(), self.content_disposition.to_string(),
); );
}); });
// default compressing // default compressing
if let Some(current_encoding) = self.encoding { if let Some(current_encoding) = self.encoding {
resp.encoding(current_encoding); resp.encoding(current_encoding);
@ -355,6 +368,7 @@ impl NamedFile {
if let Ok(rangesvec) = HttpRange::parse(rangesheader, length) { if let Ok(rangesvec) = HttpRange::parse(rangesheader, length) {
length = rangesvec[0].length; length = rangesvec[0].length;
offset = rangesvec[0].start; offset = rangesvec[0].start;
resp.encoding(ContentEncoding::Identity); resp.encoding(ContentEncoding::Identity);
resp.header( resp.header(
header::CONTENT_RANGE, header::CONTENT_RANGE,
@ -414,6 +428,7 @@ impl DerefMut for NamedFile {
fn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool { fn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
match req.get_header::<header::IfMatch>() { match req.get_header::<header::IfMatch>() {
None | Some(header::IfMatch::Any) => true, None | Some(header::IfMatch::Any) => true,
Some(header::IfMatch::Items(ref items)) => { Some(header::IfMatch::Items(ref items)) => {
if let Some(some_etag) = etag { if let Some(some_etag) = etag {
for item in items { for item in items {
@ -422,6 +437,7 @@ fn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
} }
} }
} }
false false
} }
} }
@ -431,6 +447,7 @@ fn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool { fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
match req.get_header::<header::IfNoneMatch>() { match req.get_header::<header::IfNoneMatch>() {
Some(header::IfNoneMatch::Any) => false, Some(header::IfNoneMatch::Any) => false,
Some(header::IfNoneMatch::Items(ref items)) => { Some(header::IfNoneMatch::Items(ref items)) => {
if let Some(some_etag) = etag { if let Some(some_etag) = etag {
for item in items { for item in items {
@ -439,8 +456,10 @@ fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
} }
} }
} }
true true
} }
None => true, None => true,
} }
} }

View File

@ -16,6 +16,7 @@ impl FromStr for PathBufWrap {
fn from_str(path: &str) -> Result<Self, Self::Err> { fn from_str(path: &str) -> Result<Self, Self::Err> {
let mut buf = PathBuf::new(); let mut buf = PathBuf::new();
for segment in path.split('/') { for segment in path.split('/') {
if segment == ".." { if segment == ".." {
buf.pop(); buf.pop();

View File

@ -5,7 +5,7 @@ pub struct HttpRange {
pub length: u64, pub length: u64,
} }
static PREFIX: &str = "bytes="; const PREFIX: &str = "bytes=";
const PREFIX_LEN: usize = 6; const PREFIX_LEN: usize = 6;
impl HttpRange { impl HttpRange {

View File

@ -32,17 +32,15 @@ pub struct FilesService {
pub(crate) guards: Option<Rc<dyn Guard>>, pub(crate) guards: Option<Rc<dyn Guard>>,
} }
impl FilesService { type FilesServiceFuture = Either<
#[allow(clippy::type_complexity)]
fn handle_err(
&mut self,
e: io::Error,
req: ServiceRequest,
) -> Either<
Ready<Result<ServiceResponse, Error>>, Ready<Result<ServiceResponse, Error>>,
LocalBoxFuture<'static, Result<ServiceResponse, Error>>, LocalBoxFuture<'static, Result<ServiceResponse, Error>>,
> { >;
log::debug!("Files: Failed to handle {}: {}", req.path(), e);
impl FilesService {
fn handle_err(&mut self, e: io::Error, req: ServiceRequest) -> FilesServiceFuture {
log::debug!("Failed to handle {}: {}", req.path(), e);
if let Some(ref mut default) = self.default { if let Some(ref mut default) = self.default {
Either::Right(default.call(req)) Either::Right(default.call(req))
} else { } else {
@ -55,11 +53,7 @@ impl Service for FilesService {
type Request = ServiceRequest; type Request = ServiceRequest;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
#[allow(clippy::type_complexity)] type Future = FilesServiceFuture;
type Future = Either<
Ready<Result<Self::Response, Self::Error>>,
LocalBoxFuture<'static, Result<Self::Response, Self::Error>>,
>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(())) Poll::Ready(Ok(()))
@ -97,6 +91,7 @@ impl Service for FilesService {
if let Some(ref redir_index) = self.index { if let Some(ref redir_index) = self.index {
if self.redirect_to_slash && !req.path().ends_with('/') { if self.redirect_to_slash && !req.path().ends_with('/') {
let redirect_to = format!("{}/", req.path()); let redirect_to = format!("{}/", req.path());
return Either::Left(ok(req.into_response( return Either::Left(ok(req.into_response(
HttpResponse::Found() HttpResponse::Found()
.header(header::LOCATION, redirect_to) .header(header::LOCATION, redirect_to)
@ -114,8 +109,8 @@ impl Service for FilesService {
mime_override(&named_file.content_type.type_()); mime_override(&named_file.content_type.type_());
named_file.content_disposition.disposition = new_disposition; named_file.content_disposition.disposition = new_disposition;
} }
named_file.flags = self.file_flags; named_file.flags = self.file_flags;
let (req, _) = req.into_parts(); let (req, _) = req.into_parts();
Either::Left(ok(match named_file.into_response(&req) { Either::Left(ok(match named_file.into_response(&req) {
Ok(item) => ServiceResponse::new(req, item), Ok(item) => ServiceResponse::new(req, item),
@ -126,8 +121,10 @@ impl Service for FilesService {
} }
} else if self.show_index { } else if self.show_index {
let dir = Directory::new(self.directory.clone(), path); let dir = Directory::new(self.directory.clone(), path);
let (req, _) = req.into_parts(); let (req, _) = req.into_parts();
let x = (self.renderer)(&dir, &req); let x = (self.renderer)(&dir, &req);
match x { match x {
Ok(resp) => Either::Left(ok(resp)), Ok(resp) => Either::Left(ok(resp)),
Err(e) => Either::Left(ok(ServiceResponse::from_err(e, req))), Err(e) => Either::Left(ok(ServiceResponse::from_err(e, req))),
@ -146,8 +143,8 @@ impl Service for FilesService {
mime_override(&named_file.content_type.type_()); mime_override(&named_file.content_type.type_());
named_file.content_disposition.disposition = new_disposition; named_file.content_disposition.disposition = new_disposition;
} }
named_file.flags = self.file_flags; named_file.flags = self.file_flags;
let (req, _) = req.into_parts(); let (req, _) = req.into_parts();
match named_file.into_response(&req) { match named_file.into_response(&req) {
Ok(item) => { Ok(item) => {