mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-27 17:22:57 +01:00
migrate actix-files
This commit is contained in:
parent
6ac4ac66b9
commit
69cadcdedb
@ -32,7 +32,7 @@ members = [
|
||||
"awc",
|
||||
"actix-http",
|
||||
"actix-cors",
|
||||
#"actix-files",
|
||||
"actix-files",
|
||||
#"actix-framed",
|
||||
#"actix-session",
|
||||
"actix-identity",
|
||||
@ -126,7 +126,7 @@ actix-http-test = { path = "test-server" }
|
||||
actix-web-codegen = { path = "actix-web-codegen" }
|
||||
# actix-web-actors = { path = "actix-web-actors" }
|
||||
# actix-session = { path = "actix-session" }
|
||||
# actix-files = { path = "actix-files" }
|
||||
actix-files = { path = "actix-files" }
|
||||
# actix-multipart = { path = "actix-multipart" }
|
||||
awc = { path = "awc" }
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-files"
|
||||
version = "0.1.7"
|
||||
version = "0.2.0-alpha.1"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Static files support for actix web."
|
||||
readme = "README.md"
|
||||
@ -11,19 +11,19 @@ documentation = "https://docs.rs/actix-files/"
|
||||
categories = ["asynchronous", "web-programming::http-server"]
|
||||
license = "MIT/Apache-2.0"
|
||||
edition = "2018"
|
||||
# workspace = ".."
|
||||
workspace = ".."
|
||||
|
||||
[lib]
|
||||
name = "actix_files"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
actix-web = { version = "1.0.9", default-features = false }
|
||||
actix-http = "0.2.11"
|
||||
actix-service = "0.4.2"
|
||||
actix-web = { version = "2.0.0-alpha.1", default-features = false }
|
||||
actix-http = "0.3.0-alpha.1"
|
||||
actix-service = "1.0.0-alpha.1"
|
||||
bitflags = "1"
|
||||
bytes = "0.4"
|
||||
futures = "0.1.24"
|
||||
futures = "0.3.1"
|
||||
derive_more = "0.15.0"
|
||||
log = "0.4"
|
||||
mime = "0.3"
|
||||
@ -32,4 +32,4 @@ percent-encoding = "2.1"
|
||||
v_htmlescape = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
actix-web = { version = "1.0.9", features=["ssl"] }
|
||||
actix-web = { version = "2.0.0-alpha.1", features=["openssl"] }
|
||||
|
@ -4,25 +4,28 @@
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::Write;
|
||||
use std::fs::{DirEntry, File};
|
||||
use std::future::Future;
|
||||
use std::io::{Read, Seek};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{cmp, io};
|
||||
|
||||
use actix_service::boxed::{self, BoxedNewService, BoxedService};
|
||||
use actix_service::{IntoNewService, NewService, Service};
|
||||
use actix_service::{IntoServiceFactory, Service, ServiceFactory};
|
||||
use actix_web::dev::{
|
||||
AppService, HttpServiceFactory, Payload, ResourceDef, ServiceRequest,
|
||||
ServiceResponse,
|
||||
};
|
||||
use actix_web::error::{BlockingError, Error, ErrorInternalServerError};
|
||||
use actix_web::error::{Canceled, Error, ErrorInternalServerError};
|
||||
use actix_web::guard::Guard;
|
||||
use actix_web::http::header::{self, DispositionType};
|
||||
use actix_web::http::Method;
|
||||
use actix_web::{web, FromRequest, HttpRequest, HttpResponse, Responder};
|
||||
use actix_web::{web, FromRequest, HttpRequest, HttpResponse};
|
||||
use bytes::Bytes;
|
||||
use futures::future::{ok, Either, FutureResult};
|
||||
use futures::{Async, Future, Poll, Stream};
|
||||
use futures::future::{ok, ready, Either, FutureExt, LocalBoxFuture, Ready};
|
||||
use futures::Stream;
|
||||
use mime;
|
||||
use mime_guess::from_ext;
|
||||
use percent_encoding::{utf8_percent_encode, CONTROLS};
|
||||
@ -54,32 +57,34 @@ pub struct ChunkedReadFile {
|
||||
size: u64,
|
||||
offset: u64,
|
||||
file: Option<File>,
|
||||
fut: Option<Box<dyn Future<Item = (File, Bytes), Error = BlockingError<io::Error>>>>,
|
||||
fut: Option<
|
||||
LocalBoxFuture<'static, Result<Result<(File, Bytes), io::Error>, Canceled>>,
|
||||
>,
|
||||
counter: u64,
|
||||
}
|
||||
|
||||
fn handle_error(err: BlockingError<io::Error>) -> Error {
|
||||
match err {
|
||||
BlockingError::Error(err) => err.into(),
|
||||
BlockingError::Canceled => ErrorInternalServerError("Unexpected error"),
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for ChunkedReadFile {
|
||||
type Item = Bytes;
|
||||
type Error = Error;
|
||||
type Item = Result<Bytes, Error>;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Bytes>, Error> {
|
||||
if self.fut.is_some() {
|
||||
return match self.fut.as_mut().unwrap().poll().map_err(handle_error)? {
|
||||
Async::Ready((file, bytes)) => {
|
||||
fn poll_next(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context,
|
||||
) -> Poll<Option<Self::Item>> {
|
||||
if let Some(ref mut fut) = self.fut {
|
||||
return match Pin::new(fut).poll(cx) {
|
||||
Poll::Ready(Err(_)) => Poll::Ready(Some(Err(ErrorInternalServerError(
|
||||
"Unexpected error",
|
||||
)
|
||||
.into()))),
|
||||
Poll::Ready(Ok(Ok((file, bytes)))) => {
|
||||
self.fut.take();
|
||||
self.file = Some(file);
|
||||
self.offset += bytes.len() as u64;
|
||||
self.counter += bytes.len() as u64;
|
||||
Ok(Async::Ready(Some(bytes)))
|
||||
Poll::Ready(Some(Ok(bytes)))
|
||||
}
|
||||
Async::NotReady => Ok(Async::NotReady),
|
||||
Poll::Ready(Ok(Err(e))) => Poll::Ready(Some(Err(e.into()))),
|
||||
Poll::Pending => Poll::Pending,
|
||||
};
|
||||
}
|
||||
|
||||
@ -88,10 +93,11 @@ impl Stream for ChunkedReadFile {
|
||||
let counter = self.counter;
|
||||
|
||||
if size == counter {
|
||||
Ok(Async::Ready(None))
|
||||
Poll::Ready(None)
|
||||
} else {
|
||||
let mut file = self.file.take().expect("Use after completion");
|
||||
self.fut = Some(Box::new(web::block(move || {
|
||||
self.fut = Some(
|
||||
web::block(move || {
|
||||
let max_bytes: usize;
|
||||
max_bytes = cmp::min(size.saturating_sub(counter), 65_536) as usize;
|
||||
let mut buf = Vec::with_capacity(max_bytes);
|
||||
@ -102,8 +108,10 @@ impl Stream for ChunkedReadFile {
|
||||
return Err(io::ErrorKind::UnexpectedEof.into());
|
||||
}
|
||||
Ok((file, Bytes::from(buf)))
|
||||
})));
|
||||
self.poll()
|
||||
})
|
||||
.boxed_local(),
|
||||
);
|
||||
self.poll_next(cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -367,8 +375,8 @@ impl Files {
|
||||
/// Sets default handler which is used when no matched file could be found.
|
||||
pub fn default_handler<F, U>(mut self, f: F) -> Self
|
||||
where
|
||||
F: IntoNewService<U>,
|
||||
U: NewService<
|
||||
F: IntoServiceFactory<U>,
|
||||
U: ServiceFactory<
|
||||
Config = (),
|
||||
Request = ServiceRequest,
|
||||
Response = ServiceResponse,
|
||||
@ -376,8 +384,8 @@ impl Files {
|
||||
> + 'static,
|
||||
{
|
||||
// create and configure default resource
|
||||
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service(
|
||||
f.into_new_service().map_init_err(|_| ()),
|
||||
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::factory(
|
||||
f.into_factory().map_init_err(|_| ()),
|
||||
)))));
|
||||
|
||||
self
|
||||
@ -398,14 +406,14 @@ impl HttpServiceFactory for Files {
|
||||
}
|
||||
}
|
||||
|
||||
impl NewService for Files {
|
||||
impl ServiceFactory for Files {
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse;
|
||||
type Error = Error;
|
||||
type Config = ();
|
||||
type Service = FilesService;
|
||||
type InitError = ();
|
||||
type Future = Box<dyn Future<Item = Self::Service, Error = Self::InitError>>;
|
||||
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
|
||||
|
||||
fn new_service(&self, _: &()) -> Self::Future {
|
||||
let mut srv = FilesService {
|
||||
@ -421,17 +429,18 @@ impl NewService for Files {
|
||||
};
|
||||
|
||||
if let Some(ref default) = *self.default.borrow() {
|
||||
Box::new(
|
||||
default
|
||||
.new_service(&())
|
||||
.map(move |default| {
|
||||
.map(move |result| match result {
|
||||
Ok(default) => {
|
||||
srv.default = Some(default);
|
||||
srv
|
||||
Ok(srv)
|
||||
}
|
||||
Err(_) => Err(()),
|
||||
})
|
||||
.map_err(|_| ()),
|
||||
)
|
||||
.boxed_local()
|
||||
} else {
|
||||
Box::new(ok(srv))
|
||||
ok(srv).boxed_local()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -454,14 +463,14 @@ impl FilesService {
|
||||
e: io::Error,
|
||||
req: ServiceRequest,
|
||||
) -> Either<
|
||||
FutureResult<ServiceResponse, Error>,
|
||||
Box<dyn Future<Item = ServiceResponse, Error = Error>>,
|
||||
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 {
|
||||
default.call(req)
|
||||
Either::Right(default.call(req))
|
||||
} else {
|
||||
Either::A(ok(req.error_response(e)))
|
||||
Either::Left(ok(req.error_response(e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -471,17 +480,15 @@ impl Service for FilesService {
|
||||
type Response = ServiceResponse;
|
||||
type Error = Error;
|
||||
type Future = Either<
|
||||
FutureResult<Self::Response, Self::Error>,
|
||||
Box<dyn Future<Item = Self::Response, Error = Self::Error>>,
|
||||
Ready<Result<Self::Response, Self::Error>>,
|
||||
LocalBoxFuture<'static, Result<Self::Response, Self::Error>>,
|
||||
>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
Ok(Async::Ready(()))
|
||||
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||
// let (req, pl) = req.into_parts();
|
||||
|
||||
let is_method_valid = if let Some(guard) = &self.guards {
|
||||
// execute user defined guards
|
||||
(**guard).check(req.head())
|
||||
@ -494,7 +501,7 @@ impl Service for FilesService {
|
||||
};
|
||||
|
||||
if !is_method_valid {
|
||||
return Either::A(ok(req.into_response(
|
||||
return Either::Left(ok(req.into_response(
|
||||
actix_web::HttpResponse::MethodNotAllowed()
|
||||
.header(header::CONTENT_TYPE, "text/plain")
|
||||
.body("Request did not meet this resource's requirements."),
|
||||
@ -503,7 +510,7 @@ impl Service for FilesService {
|
||||
|
||||
let real_path = match PathBufWrp::get_pathbuf(req.match_info().path()) {
|
||||
Ok(item) => item,
|
||||
Err(e) => return Either::A(ok(req.error_response(e))),
|
||||
Err(e) => return Either::Left(ok(req.error_response(e))),
|
||||
};
|
||||
|
||||
// full filepath
|
||||
@ -516,7 +523,7 @@ impl Service for FilesService {
|
||||
if let Some(ref redir_index) = self.index {
|
||||
if self.redirect_to_slash && !req.path().ends_with('/') {
|
||||
let redirect_to = format!("{}/", req.path());
|
||||
return Either::A(ok(req.into_response(
|
||||
return Either::Left(ok(req.into_response(
|
||||
HttpResponse::Found()
|
||||
.header(header::LOCATION, redirect_to)
|
||||
.body("")
|
||||
@ -536,7 +543,7 @@ impl Service for FilesService {
|
||||
|
||||
named_file.flags = self.file_flags;
|
||||
let (req, _) = req.into_parts();
|
||||
Either::A(ok(match named_file.respond_to(&req) {
|
||||
Either::Left(ok(match named_file.into_response(&req) {
|
||||
Ok(item) => ServiceResponse::new(req, item),
|
||||
Err(e) => ServiceResponse::from_err(e, req),
|
||||
}))
|
||||
@ -548,11 +555,11 @@ impl Service for FilesService {
|
||||
let (req, _) = req.into_parts();
|
||||
let x = (self.renderer)(&dir, &req);
|
||||
match x {
|
||||
Ok(resp) => Either::A(ok(resp)),
|
||||
Err(e) => Either::A(ok(ServiceResponse::from_err(e, req))),
|
||||
Ok(resp) => Either::Left(ok(resp)),
|
||||
Err(e) => Either::Left(ok(ServiceResponse::from_err(e, req))),
|
||||
}
|
||||
} else {
|
||||
Either::A(ok(ServiceResponse::from_err(
|
||||
Either::Left(ok(ServiceResponse::from_err(
|
||||
FilesError::IsDirectory,
|
||||
req.into_parts().0,
|
||||
)))
|
||||
@ -568,11 +575,11 @@ impl Service for FilesService {
|
||||
|
||||
named_file.flags = self.file_flags;
|
||||
let (req, _) = req.into_parts();
|
||||
match named_file.respond_to(&req) {
|
||||
match named_file.into_response(&req) {
|
||||
Ok(item) => {
|
||||
Either::A(ok(ServiceResponse::new(req.clone(), item)))
|
||||
Either::Left(ok(ServiceResponse::new(req.clone(), item)))
|
||||
}
|
||||
Err(e) => Either::A(ok(ServiceResponse::from_err(e, req))),
|
||||
Err(e) => Either::Left(ok(ServiceResponse::from_err(e, req))),
|
||||
}
|
||||
}
|
||||
Err(e) => self.handle_err(e, req),
|
||||
@ -615,11 +622,11 @@ impl PathBufWrp {
|
||||
|
||||
impl FromRequest for PathBufWrp {
|
||||
type Error = UriSegmentError;
|
||||
type Future = Result<Self, Self::Error>;
|
||||
type Future = Ready<Result<Self, Self::Error>>;
|
||||
type Config = ();
|
||||
|
||||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||
PathBufWrp::get_pathbuf(req.match_info().path())
|
||||
ready(PathBufWrp::get_pathbuf(req.match_info().path()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -630,8 +637,6 @@ mod tests {
|
||||
use std::ops::Add;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use bytes::BytesMut;
|
||||
|
||||
use super::*;
|
||||
use actix_web::guard;
|
||||
use actix_web::http::header::{
|
||||
@ -639,8 +644,8 @@ mod tests {
|
||||
};
|
||||
use actix_web::http::{Method, StatusCode};
|
||||
use actix_web::middleware::Compress;
|
||||
use actix_web::test::{self, TestRequest};
|
||||
use actix_web::App;
|
||||
use actix_web::test::{self, block_on, TestRequest};
|
||||
use actix_web::{App, Responder};
|
||||
|
||||
#[test]
|
||||
fn test_file_extension_to_mime() {
|
||||
@ -656,6 +661,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_if_modified_since_without_if_none_match() {
|
||||
block_on(async {
|
||||
let file = NamedFile::open("Cargo.toml").unwrap();
|
||||
let since =
|
||||
header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60)));
|
||||
@ -663,12 +669,14 @@ mod tests {
|
||||
let req = TestRequest::default()
|
||||
.header(header::IF_MODIFIED_SINCE, since)
|
||||
.to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::NOT_MODIFIED);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_modified_since_with_if_none_match() {
|
||||
block_on(async {
|
||||
let file = NamedFile::open("Cargo.toml").unwrap();
|
||||
let since =
|
||||
header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60)));
|
||||
@ -677,12 +685,14 @@ mod tests {
|
||||
.header(header::IF_NONE_MATCH, "miss_etag")
|
||||
.header(header::IF_MODIFIED_SINCE, since)
|
||||
.to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_ne!(resp.status(), StatusCode::NOT_MODIFIED);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_text() {
|
||||
block_on(async {
|
||||
assert!(NamedFile::open("test--").is_err());
|
||||
let mut file = NamedFile::open("Cargo.toml").unwrap();
|
||||
{
|
||||
@ -694,7 +704,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"text/x-toml"
|
||||
@ -703,10 +713,12 @@ mod tests {
|
||||
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
||||
"inline; filename=\"Cargo.toml\""
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_content_disposition() {
|
||||
block_on(async {
|
||||
assert!(NamedFile::open("test--").is_err());
|
||||
let mut file = NamedFile::open("Cargo.toml").unwrap();
|
||||
{
|
||||
@ -718,7 +730,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
||||
"inline; filename=\"Cargo.toml\""
|
||||
@ -728,12 +740,14 @@ mod tests {
|
||||
.unwrap()
|
||||
.disable_content_disposition();
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert!(resp.headers().get(header::CONTENT_DISPOSITION).is_none());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_non_ascii_file_name() {
|
||||
block_on(async {
|
||||
let mut file =
|
||||
NamedFile::from_file(File::open("Cargo.toml").unwrap(), "貨物.toml")
|
||||
.unwrap();
|
||||
@ -746,7 +760,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"text/x-toml"
|
||||
@ -755,10 +769,12 @@ mod tests {
|
||||
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
||||
"inline; filename=\"貨物.toml\"; filename*=UTF-8''%E8%B2%A8%E7%89%A9.toml"
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_set_content_type() {
|
||||
block_on(async {
|
||||
let mut file = NamedFile::open("Cargo.toml")
|
||||
.unwrap()
|
||||
.set_content_type(mime::TEXT_XML);
|
||||
@ -771,7 +787,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"text/xml"
|
||||
@ -780,10 +796,12 @@ mod tests {
|
||||
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
||||
"inline; filename=\"Cargo.toml\""
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_image() {
|
||||
block_on(async {
|
||||
let mut file = NamedFile::open("tests/test.png").unwrap();
|
||||
{
|
||||
file.file();
|
||||
@ -794,7 +812,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"image/png"
|
||||
@ -803,10 +821,12 @@ mod tests {
|
||||
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
||||
"inline; filename=\"test.png\""
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_image_attachment() {
|
||||
block_on(async {
|
||||
let cd = ContentDisposition {
|
||||
disposition: DispositionType::Attachment,
|
||||
parameters: vec![DispositionParam::Filename(String::from("test.png"))],
|
||||
@ -823,7 +843,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"image/png"
|
||||
@ -832,10 +852,12 @@ mod tests {
|
||||
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
||||
"attachment; filename=\"test.png\""
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_binary() {
|
||||
block_on(async {
|
||||
let mut file = NamedFile::open("tests/test.binary").unwrap();
|
||||
{
|
||||
file.file();
|
||||
@ -846,7 +868,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"application/octet-stream"
|
||||
@ -855,10 +877,12 @@ mod tests {
|
||||
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
||||
"attachment; filename=\"test.binary\""
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_status_code_text() {
|
||||
block_on(async {
|
||||
let mut file = NamedFile::open("Cargo.toml")
|
||||
.unwrap()
|
||||
.set_status_code(StatusCode::NOT_FOUND);
|
||||
@ -871,7 +895,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"text/x-toml"
|
||||
@ -881,10 +905,12 @@ mod tests {
|
||||
"inline; filename=\"Cargo.toml\""
|
||||
);
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mime_override() {
|
||||
block_on(async {
|
||||
fn all_attachment(_: &mime::Name) -> DispositionType {
|
||||
DispositionType::Attachment
|
||||
}
|
||||
@ -895,10 +921,11 @@ mod tests {
|
||||
.mime_override(all_attachment)
|
||||
.index_file("Cargo.toml"),
|
||||
),
|
||||
);
|
||||
)
|
||||
.await;
|
||||
|
||||
let request = TestRequest::get().uri("/").to_request();
|
||||
let response = test::call_service(&mut srv, request);
|
||||
let response = test::call_service(&mut srv, request).await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let content_disposition = response
|
||||
@ -909,20 +936,23 @@ mod tests {
|
||||
.to_str()
|
||||
.expect("Convert CONTENT_DISPOSITION to str");
|
||||
assert_eq!(content_disposition, "attachment; filename=\"Cargo.toml\"");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_ranges_status_code() {
|
||||
block_on(async {
|
||||
let mut srv = test::init_service(
|
||||
App::new().service(Files::new("/test", ".").index_file("Cargo.toml")),
|
||||
);
|
||||
)
|
||||
.await;
|
||||
|
||||
// Valid range header
|
||||
let request = TestRequest::get()
|
||||
.uri("/t%65st/Cargo.toml")
|
||||
.header(header::RANGE, "bytes=10-20")
|
||||
.to_request();
|
||||
let response = test::call_service(&mut srv, request);
|
||||
let response = test::call_service(&mut srv, request).await;
|
||||
assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
|
||||
|
||||
// Invalid range header
|
||||
@ -930,16 +960,20 @@ mod tests {
|
||||
.uri("/t%65st/Cargo.toml")
|
||||
.header(header::RANGE, "bytes=1-0")
|
||||
.to_request();
|
||||
let response = test::call_service(&mut srv, request);
|
||||
let response = test::call_service(&mut srv, request).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_content_range_headers() {
|
||||
block_on(async {
|
||||
let mut srv = test::init_service(
|
||||
App::new().service(Files::new("/test", ".").index_file("tests/test.binary")),
|
||||
);
|
||||
App::new()
|
||||
.service(Files::new("/test", ".").index_file("tests/test.binary")),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Valid range header
|
||||
let request = TestRequest::get()
|
||||
@ -947,7 +981,7 @@ mod tests {
|
||||
.header(header::RANGE, "bytes=10-20")
|
||||
.to_request();
|
||||
|
||||
let response = test::call_service(&mut srv, request);
|
||||
let response = test::call_service(&mut srv, request).await;
|
||||
let contentrange = response
|
||||
.headers()
|
||||
.get(header::CONTENT_RANGE)
|
||||
@ -962,7 +996,7 @@ mod tests {
|
||||
.uri("/t%65st/tests/test.binary")
|
||||
.header(header::RANGE, "bytes=10-5")
|
||||
.to_request();
|
||||
let response = test::call_service(&mut srv, request);
|
||||
let response = test::call_service(&mut srv, request).await;
|
||||
|
||||
let contentrange = response
|
||||
.headers()
|
||||
@ -972,22 +1006,26 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(contentrange, "bytes */100");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_content_length_headers() {
|
||||
block_on(async {
|
||||
// use actix_web::body::{MessageBody, ResponseBody};
|
||||
|
||||
let mut srv = test::init_service(
|
||||
App::new().service(Files::new("test", ".").index_file("tests/test.binary")),
|
||||
);
|
||||
App::new()
|
||||
.service(Files::new("test", ".").index_file("tests/test.binary")),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Valid range header
|
||||
let request = TestRequest::get()
|
||||
.uri("/t%65st/tests/test.binary")
|
||||
.header(header::RANGE, "bytes=10-20")
|
||||
.to_request();
|
||||
let _response = test::call_service(&mut srv, request);
|
||||
let _response = test::call_service(&mut srv, request).await;
|
||||
|
||||
// let contentlength = response
|
||||
// .headers()
|
||||
@ -1002,7 +1040,7 @@ mod tests {
|
||||
.uri("/t%65st/tests/test.binary")
|
||||
.header(header::RANGE, "bytes=10-8")
|
||||
.to_request();
|
||||
let response = test::call_service(&mut srv, request);
|
||||
let response = test::call_service(&mut srv, request).await;
|
||||
assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
|
||||
|
||||
// Without range header
|
||||
@ -1010,7 +1048,7 @@ mod tests {
|
||||
.uri("/t%65st/tests/test.binary")
|
||||
// .no_default_headers()
|
||||
.to_request();
|
||||
let _response = test::call_service(&mut srv, request);
|
||||
let _response = test::call_service(&mut srv, request).await;
|
||||
|
||||
// let contentlength = response
|
||||
// .headers()
|
||||
@ -1024,7 +1062,7 @@ mod tests {
|
||||
let request = TestRequest::get()
|
||||
.uri("/t%65st/tests/test.binary")
|
||||
.to_request();
|
||||
let mut response = test::call_service(&mut srv, request);
|
||||
let response = test::call_service(&mut srv, request).await;
|
||||
|
||||
// with enabled compression
|
||||
// {
|
||||
@ -1037,28 +1075,27 @@ mod tests {
|
||||
// assert_eq!(te, "chunked");
|
||||
// }
|
||||
|
||||
let bytes =
|
||||
test::block_on(response.take_body().fold(BytesMut::new(), |mut b, c| {
|
||||
b.extend(c);
|
||||
Ok::<_, Error>(b)
|
||||
}))
|
||||
.unwrap();
|
||||
let bytes = test::read_body(response).await;
|
||||
let data = Bytes::from(fs::read("tests/test.binary").unwrap());
|
||||
assert_eq!(bytes.freeze(), data);
|
||||
assert_eq!(bytes, data);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_head_content_length_headers() {
|
||||
block_on(async {
|
||||
let mut srv = test::init_service(
|
||||
App::new().service(Files::new("test", ".").index_file("tests/test.binary")),
|
||||
);
|
||||
App::new()
|
||||
.service(Files::new("test", ".").index_file("tests/test.binary")),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Valid range header
|
||||
let request = TestRequest::default()
|
||||
.method(Method::HEAD)
|
||||
.uri("/t%65st/tests/test.binary")
|
||||
.to_request();
|
||||
let _response = test::call_service(&mut srv, request);
|
||||
let _response = test::call_service(&mut srv, request).await;
|
||||
|
||||
// TODO: fix check
|
||||
// let contentlength = response
|
||||
@ -1068,100 +1105,112 @@ mod tests {
|
||||
// .to_str()
|
||||
// .unwrap();
|
||||
// assert_eq!(contentlength, "100");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_static_files_with_spaces() {
|
||||
block_on(async {
|
||||
let mut srv = test::init_service(
|
||||
App::new().service(Files::new("/", ".").index_file("Cargo.toml")),
|
||||
);
|
||||
)
|
||||
.await;
|
||||
let request = TestRequest::get()
|
||||
.uri("/tests/test%20space.binary")
|
||||
.to_request();
|
||||
let mut response = test::call_service(&mut srv, request);
|
||||
let response = test::call_service(&mut srv, request).await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let bytes =
|
||||
test::block_on(response.take_body().fold(BytesMut::new(), |mut b, c| {
|
||||
b.extend(c);
|
||||
Ok::<_, Error>(b)
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let bytes = test::read_body(response).await;
|
||||
let data = Bytes::from(fs::read("tests/test space.binary").unwrap());
|
||||
assert_eq!(bytes.freeze(), data);
|
||||
assert_eq!(bytes, data);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_files_not_allowed() {
|
||||
let mut srv = test::init_service(App::new().service(Files::new("/", ".")));
|
||||
block_on(async {
|
||||
let mut srv =
|
||||
test::init_service(App::new().service(Files::new("/", "."))).await;
|
||||
|
||||
let req = TestRequest::default()
|
||||
.uri("/Cargo.toml")
|
||||
.method(Method::POST)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&mut srv, req);
|
||||
let resp = test::call_service(&mut srv, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||
|
||||
let mut srv = test::init_service(App::new().service(Files::new("/", ".")));
|
||||
let mut srv =
|
||||
test::init_service(App::new().service(Files::new("/", "."))).await;
|
||||
let req = TestRequest::default()
|
||||
.method(Method::PUT)
|
||||
.uri("/Cargo.toml")
|
||||
.to_request();
|
||||
let resp = test::call_service(&mut srv, req);
|
||||
let resp = test::call_service(&mut srv, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_files_guards() {
|
||||
block_on(async {
|
||||
let mut srv = test::init_service(
|
||||
App::new().service(Files::new("/", ".").use_guards(guard::Post())),
|
||||
);
|
||||
)
|
||||
.await;
|
||||
|
||||
let req = TestRequest::default()
|
||||
.uri("/Cargo.toml")
|
||||
.method(Method::POST)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&mut srv, req);
|
||||
let resp = test::call_service(&mut srv, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_content_encoding() {
|
||||
let mut srv = test::init_service(App::new().wrap(Compress::default()).service(
|
||||
block_on(async {
|
||||
let mut srv =
|
||||
test::init_service(App::new().wrap(Compress::default()).service(
|
||||
web::resource("/").to(|| {
|
||||
NamedFile::open("Cargo.toml")
|
||||
.unwrap()
|
||||
.set_content_encoding(header::ContentEncoding::Identity)
|
||||
}),
|
||||
));
|
||||
))
|
||||
.await;
|
||||
|
||||
let request = TestRequest::get()
|
||||
.uri("/")
|
||||
.header(header::ACCEPT_ENCODING, "gzip")
|
||||
.to_request();
|
||||
let res = test::call_service(&mut srv, request);
|
||||
let res = test::call_service(&mut srv, request).await;
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert!(!res.headers().contains_key(header::CONTENT_ENCODING));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_content_encoding_gzip() {
|
||||
let mut srv = test::init_service(App::new().wrap(Compress::default()).service(
|
||||
block_on(async {
|
||||
let mut srv =
|
||||
test::init_service(App::new().wrap(Compress::default()).service(
|
||||
web::resource("/").to(|| {
|
||||
NamedFile::open("Cargo.toml")
|
||||
.unwrap()
|
||||
.set_content_encoding(header::ContentEncoding::Gzip)
|
||||
}),
|
||||
));
|
||||
))
|
||||
.await;
|
||||
|
||||
let request = TestRequest::get()
|
||||
.uri("/")
|
||||
.header(header::ACCEPT_ENCODING, "gzip")
|
||||
.to_request();
|
||||
let res = test::call_service(&mut srv, request);
|
||||
let res = test::call_service(&mut srv, request).await;
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
res.headers()
|
||||
@ -1171,59 +1220,64 @@ mod tests {
|
||||
.unwrap(),
|
||||
"gzip"
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_allowed_method() {
|
||||
block_on(async {
|
||||
let req = TestRequest::default().method(Method::GET).to_http_request();
|
||||
let file = NamedFile::open("Cargo.toml").unwrap();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_static_files() {
|
||||
block_on(async {
|
||||
let mut srv = test::init_service(
|
||||
App::new().service(Files::new("/", ".").show_files_listing()),
|
||||
);
|
||||
)
|
||||
.await;
|
||||
let req = TestRequest::with_uri("/missing").to_request();
|
||||
|
||||
let resp = test::call_service(&mut srv, req);
|
||||
let resp = test::call_service(&mut srv, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let mut srv = test::init_service(App::new().service(Files::new("/", ".")));
|
||||
let mut srv =
|
||||
test::init_service(App::new().service(Files::new("/", "."))).await;
|
||||
|
||||
let req = TestRequest::default().to_request();
|
||||
let resp = test::call_service(&mut srv, req);
|
||||
let resp = test::call_service(&mut srv, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let mut srv = test::init_service(
|
||||
App::new().service(Files::new("/", ".").show_files_listing()),
|
||||
);
|
||||
)
|
||||
.await;
|
||||
let req = TestRequest::with_uri("/tests").to_request();
|
||||
let mut resp = test::call_service(&mut srv, req);
|
||||
let resp = test::call_service(&mut srv, req).await;
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"text/html; charset=utf-8"
|
||||
);
|
||||
|
||||
let bytes =
|
||||
test::block_on(resp.take_body().fold(BytesMut::new(), |mut b, c| {
|
||||
b.extend(c);
|
||||
Ok::<_, Error>(b)
|
||||
}))
|
||||
.unwrap();
|
||||
let bytes = test::read_body(resp).await;
|
||||
assert!(format!("{:?}", bytes).contains("/tests/test.png"));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_redirect_to_slash_directory() {
|
||||
block_on(async {
|
||||
// should not redirect if no index
|
||||
let mut srv = test::init_service(
|
||||
App::new().service(Files::new("/", ".").redirect_to_slash_directory()),
|
||||
);
|
||||
)
|
||||
.await;
|
||||
let req = TestRequest::with_uri("/tests").to_request();
|
||||
let resp = test::call_service(&mut srv, req);
|
||||
let resp = test::call_service(&mut srv, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
// should redirect if index present
|
||||
@ -1233,15 +1287,17 @@ mod tests {
|
||||
.index_file("test.png")
|
||||
.redirect_to_slash_directory(),
|
||||
),
|
||||
);
|
||||
)
|
||||
.await;
|
||||
let req = TestRequest::with_uri("/tests").to_request();
|
||||
let resp = test::call_service(&mut srv, req);
|
||||
let resp = test::call_service(&mut srv, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::FOUND);
|
||||
|
||||
// should not redirect if the path is wrong
|
||||
let req = TestRequest::with_uri("/not_existing").to_request();
|
||||
let resp = test::call_service(&mut srv, req);
|
||||
let resp = test::call_service(&mut srv, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1252,26 +1308,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_default_handler_file_missing() {
|
||||
let mut st = test::block_on(
|
||||
Files::new("/", ".")
|
||||
block_on(async {
|
||||
let mut st = Files::new("/", ".")
|
||||
.default_handler(|req: ServiceRequest| {
|
||||
Ok(req.into_response(HttpResponse::Ok().body("default content")))
|
||||
ok(req.into_response(HttpResponse::Ok().body("default content")))
|
||||
})
|
||||
.new_service(&()),
|
||||
)
|
||||
.new_service(&())
|
||||
.await
|
||||
.unwrap();
|
||||
let req = TestRequest::with_uri("/missing").to_srv_request();
|
||||
|
||||
let mut resp = test::call_service(&mut st, req);
|
||||
let resp = test::call_service(&mut st, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
let bytes =
|
||||
test::block_on(resp.take_body().fold(BytesMut::new(), |mut b, c| {
|
||||
b.extend(c);
|
||||
Ok::<_, Error>(b)
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(bytes.freeze(), Bytes::from_static(b"default content"));
|
||||
let bytes = test::read_body(resp).await;
|
||||
assert_eq!(bytes, Bytes::from_static(b"default content"));
|
||||
})
|
||||
}
|
||||
|
||||
// #[test]
|
||||
|
@ -18,6 +18,7 @@ use actix_web::http::header::{
|
||||
use actix_web::http::{ContentEncoding, StatusCode};
|
||||
use actix_web::middleware::BodyEncoding;
|
||||
use actix_web::{Error, HttpMessage, HttpRequest, HttpResponse, Responder};
|
||||
use futures::future::{ready, Ready};
|
||||
|
||||
use crate::range::HttpRange;
|
||||
use crate::ChunkedReadFile;
|
||||
@ -255,62 +256,8 @@ impl NamedFile {
|
||||
pub(crate) fn last_modified(&self) -> Option<header::HttpDate> {
|
||||
self.modified.map(|mtime| mtime.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for NamedFile {
|
||||
type Target = File;
|
||||
|
||||
fn deref(&self) -> &File {
|
||||
&self.file
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for NamedFile {
|
||||
fn deref_mut(&mut self) -> &mut File {
|
||||
&mut self.file
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `req` has no `If-Match` header or one which matches `etag`.
|
||||
fn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
|
||||
match req.get_header::<header::IfMatch>() {
|
||||
None | Some(header::IfMatch::Any) => true,
|
||||
Some(header::IfMatch::Items(ref items)) => {
|
||||
if let Some(some_etag) = etag {
|
||||
for item in items {
|
||||
if item.strong_eq(some_etag) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `req` doesn't have an `If-None-Match` header matching `req`.
|
||||
fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
|
||||
match req.get_header::<header::IfNoneMatch>() {
|
||||
Some(header::IfNoneMatch::Any) => false,
|
||||
Some(header::IfNoneMatch::Items(ref items)) => {
|
||||
if let Some(some_etag) = etag {
|
||||
for item in items {
|
||||
if item.weak_eq(some_etag) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
impl Responder for NamedFile {
|
||||
type Error = Error;
|
||||
type Future = Result<HttpResponse, Error>;
|
||||
|
||||
fn respond_to(self, req: &HttpRequest) -> Self::Future {
|
||||
pub fn into_response(self, req: &HttpRequest) -> Result<HttpResponse, Error> {
|
||||
if self.status_code != StatusCode::OK {
|
||||
let mut resp = HttpResponse::build(self.status_code);
|
||||
resp.set(header::ContentType(self.content_type.clone()))
|
||||
@ -442,8 +389,67 @@ impl Responder for NamedFile {
|
||||
counter: 0,
|
||||
};
|
||||
if offset != 0 || length != self.md.len() {
|
||||
return Ok(resp.status(StatusCode::PARTIAL_CONTENT).streaming(reader));
|
||||
};
|
||||
Ok(resp.status(StatusCode::PARTIAL_CONTENT).streaming(reader))
|
||||
} else {
|
||||
Ok(resp.body(SizedStream::new(length, reader)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for NamedFile {
|
||||
type Target = File;
|
||||
|
||||
fn deref(&self) -> &File {
|
||||
&self.file
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for NamedFile {
|
||||
fn deref_mut(&mut self) -> &mut File {
|
||||
&mut self.file
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `req` has no `If-Match` header or one which matches `etag`.
|
||||
fn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
|
||||
match req.get_header::<header::IfMatch>() {
|
||||
None | Some(header::IfMatch::Any) => true,
|
||||
Some(header::IfMatch::Items(ref items)) => {
|
||||
if let Some(some_etag) = etag {
|
||||
for item in items {
|
||||
if item.strong_eq(some_etag) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `req` doesn't have an `If-None-Match` header matching `req`.
|
||||
fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
|
||||
match req.get_header::<header::IfNoneMatch>() {
|
||||
Some(header::IfNoneMatch::Any) => false,
|
||||
Some(header::IfNoneMatch::Items(ref items)) => {
|
||||
if let Some(some_etag) = etag {
|
||||
for item in items {
|
||||
if item.weak_eq(some_etag) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
impl Responder for NamedFile {
|
||||
type Error = Error;
|
||||
type Future = Ready<Result<HttpResponse, Error>>;
|
||||
|
||||
fn respond_to(self, req: &HttpRequest) -> Self::Future {
|
||||
ready(self.into_response(req))
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use std::{fmt, io, result};
|
||||
use actix_utils::timeout::TimeoutError;
|
||||
use bytes::BytesMut;
|
||||
use derive_more::{Display, From};
|
||||
use futures::channel::oneshot::Canceled;
|
||||
pub use futures::channel::oneshot::Canceled;
|
||||
use http::uri::InvalidUri;
|
||||
use http::{header, Error as HttpError, StatusCode};
|
||||
use httparse;
|
||||
|
Loading…
Reference in New Issue
Block a user