mirror of https://github.com/fafhrd91/actix-web
I <3 vertical whitespace
This commit is contained in:
parent
3046980540
commit
c6e2319f71
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -32,17 +32,15 @@ pub struct FilesService {
|
||||||
pub(crate) guards: Option<Rc<dyn Guard>>,
|
pub(crate) guards: Option<Rc<dyn Guard>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FilesServiceFuture = Either<
|
||||||
|
Ready<Result<ServiceResponse, Error>>,
|
||||||
|
LocalBoxFuture<'static, Result<ServiceResponse, Error>>,
|
||||||
|
>;
|
||||||
|
|
||||||
impl FilesService {
|
impl FilesService {
|
||||||
#[allow(clippy::type_complexity)]
|
fn handle_err(&mut self, e: io::Error, req: ServiceRequest) -> FilesServiceFuture {
|
||||||
fn handle_err(
|
log::debug!("Failed to handle {}: {}", req.path(), e);
|
||||||
&mut self,
|
|
||||||
e: io::Error,
|
|
||||||
req: ServiceRequest,
|
|
||||||
) -> Either<
|
|
||||||
Ready<Result<ServiceResponse, Error>>,
|
|
||||||
LocalBoxFuture<'static, Result<ServiceResponse, Error>>,
|
|
||||||
> {
|
|
||||||
log::debug!("Files: 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) => {
|
||||||
|
|
Loading…
Reference in New Issue