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