2020-10-06 21:08:33 +00:00
|
|
|
//! Static file serving for Actix Web.
|
2020-09-20 22:18:25 +00:00
|
|
|
//!
|
|
|
|
//! Provides a non-blocking service for serving static files from disk.
|
|
|
|
//!
|
2022-01-21 16:56:33 +00:00
|
|
|
//! # Examples
|
2021-03-25 08:45:52 +00:00
|
|
|
//! ```
|
2020-09-20 22:18:25 +00:00
|
|
|
//! use actix_web::App;
|
|
|
|
//! use actix_files::Files;
|
|
|
|
//!
|
|
|
|
//! let app = App::new()
|
2020-10-06 20:56:28 +00:00
|
|
|
//! .service(Files::new("/static", ".").prefer_utf8(true));
|
2020-09-20 22:18:25 +00:00
|
|
|
//! ```
|
2020-09-10 13:46:35 +00:00
|
|
|
|
2021-12-08 06:09:56 +00:00
|
|
|
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
|
|
#![warn(future_incompatible, missing_docs, missing_debug_implementations)]
|
2023-02-26 21:55:25 +00:00
|
|
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
|
|
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
|
|
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
2020-09-20 22:18:25 +00:00
|
|
|
|
2023-07-17 01:38:12 +00:00
|
|
|
use std::path::Path;
|
|
|
|
|
2020-09-20 22:18:25 +00:00
|
|
|
use actix_service::boxed::{BoxService, BoxServiceFactory};
|
|
|
|
use actix_web::{
|
2021-06-25 13:21:57 +00:00
|
|
|
dev::{RequestHead, ServiceRequest, ServiceResponse},
|
2021-01-09 16:04:19 +00:00
|
|
|
error::Error,
|
2020-09-20 22:18:25 +00:00
|
|
|
http::header::DispositionType,
|
2019-04-01 01:19:18 +00:00
|
|
|
};
|
2019-08-11 20:43:29 +00:00
|
|
|
use mime_guess::from_ext;
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2020-09-20 22:18:25 +00:00
|
|
|
mod chunked;
|
|
|
|
mod directory;
|
2020-10-06 20:56:28 +00:00
|
|
|
mod encoding;
|
2019-03-06 06:10:08 +00:00
|
|
|
mod error;
|
2020-09-20 22:18:25 +00:00
|
|
|
mod files;
|
2019-03-06 06:10:08 +00:00
|
|
|
mod named;
|
2020-09-20 22:18:25 +00:00
|
|
|
mod path_buf;
|
2019-03-06 17:27:02 +00:00
|
|
|
mod range;
|
2020-09-20 22:18:25 +00:00
|
|
|
mod service;
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2023-07-17 01:38:12 +00:00
|
|
|
pub use self::{
|
|
|
|
chunked::ChunkedReadFile, directory::Directory, files::Files, named::NamedFile,
|
|
|
|
range::HttpRange, service::FilesService,
|
|
|
|
};
|
|
|
|
use self::{
|
|
|
|
directory::{directory_listing, DirectoryRenderer},
|
|
|
|
error::FilesError,
|
|
|
|
path_buf::PathBufWrap,
|
|
|
|
};
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
type HttpService = BoxService<ServiceRequest, ServiceResponse, Error>;
|
|
|
|
type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>;
|
2019-03-06 06:10:08 +00:00
|
|
|
|
|
|
|
/// Return the MIME type associated with a filename extension (case-insensitive).
|
|
|
|
/// If `ext` is empty or no associated type for the extension was found, returns
|
|
|
|
/// the type `application/octet-stream`.
|
|
|
|
#[inline]
|
|
|
|
pub fn file_extension_to_mime(ext: &str) -> mime::Mime {
|
2019-08-11 20:43:29 +00:00
|
|
|
from_ext(ext).first_or_octet_stream()
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2020-09-10 13:46:35 +00:00
|
|
|
type MimeOverride = dyn Fn(&mime::Name<'_>) -> DispositionType;
|
2019-03-25 20:02:37 +00:00
|
|
|
|
2021-06-25 13:21:57 +00:00
|
|
|
type PathFilter = dyn Fn(&Path, &RequestHead) -> bool;
|
|
|
|
|
2019-03-06 06:10:08 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2020-09-20 22:18:25 +00:00
|
|
|
use std::{
|
2023-08-29 00:11:11 +00:00
|
|
|
fmt::Write as _,
|
2021-11-22 01:19:09 +00:00
|
|
|
fs::{self},
|
2020-09-20 22:18:25 +00:00
|
|
|
ops::Add,
|
|
|
|
time::{Duration, SystemTime},
|
|
|
|
};
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2020-09-20 22:18:25 +00:00
|
|
|
use actix_web::{
|
2022-01-12 17:53:36 +00:00
|
|
|
dev::ServiceFactory,
|
2020-09-20 22:18:25 +00:00
|
|
|
guard,
|
|
|
|
http::{
|
2024-02-19 12:31:10 +00:00
|
|
|
header::{self, ContentDisposition, DispositionParam},
|
2020-09-20 22:18:25 +00:00
|
|
|
Method, StatusCode,
|
|
|
|
},
|
|
|
|
middleware::Compress,
|
|
|
|
test::{self, TestRequest},
|
2021-02-07 01:00:40 +00:00
|
|
|
web::{self, Bytes},
|
|
|
|
App, HttpResponse, Responder,
|
2019-04-01 17:26:09 +00:00
|
|
|
};
|
2020-09-20 22:18:25 +00:00
|
|
|
|
|
|
|
use super::*;
|
2021-11-22 01:19:09 +00:00
|
|
|
use crate::named::File;
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2021-10-19 16:30:32 +00:00
|
|
|
#[actix_web::test]
|
2019-11-26 05:25:50 +00:00
|
|
|
async fn test_file_extension_to_mime() {
|
2020-10-06 20:56:28 +00:00
|
|
|
let m = file_extension_to_mime("");
|
|
|
|
assert_eq!(m, mime::APPLICATION_OCTET_STREAM);
|
|
|
|
|
2019-03-06 06:10:08 +00:00
|
|
|
let m = file_extension_to_mime("jpg");
|
|
|
|
assert_eq!(m, mime::IMAGE_JPEG);
|
|
|
|
|
|
|
|
let m = file_extension_to_mime("invalid extension!!");
|
|
|
|
assert_eq!(m, mime::APPLICATION_OCTET_STREAM);
|
|
|
|
|
|
|
|
let m = file_extension_to_mime("");
|
|
|
|
assert_eq!(m, mime::APPLICATION_OCTET_STREAM);
|
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_if_modified_since_without_if_none_match() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let file = NamedFile::open_async("Cargo.toml").await.unwrap();
|
2021-02-11 23:03:17 +00:00
|
|
|
let since = header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60)));
|
2019-11-26 05:25:50 +00:00
|
|
|
|
|
|
|
let req = TestRequest::default()
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::IF_MODIFIED_SINCE, since))
|
2019-11-26 05:25:50 +00:00
|
|
|
.to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::NOT_MODIFIED);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2021-01-11 18:18:23 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_if_modified_since_without_if_none_match_same() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let file = NamedFile::open_async("Cargo.toml").await.unwrap();
|
2021-01-11 18:18:23 +00:00
|
|
|
let since = file.last_modified().unwrap();
|
|
|
|
|
|
|
|
let req = TestRequest::default()
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::IF_MODIFIED_SINCE, since))
|
2021-01-11 18:18:23 +00:00
|
|
|
.to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2021-01-11 18:18:23 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::NOT_MODIFIED);
|
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_if_modified_since_with_if_none_match() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let file = NamedFile::open_async("Cargo.toml").await.unwrap();
|
2021-02-11 23:03:17 +00:00
|
|
|
let since = header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60)));
|
2019-11-26 05:25:50 +00:00
|
|
|
|
|
|
|
let req = TestRequest::default()
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::IF_NONE_MATCH, "miss_etag"))
|
|
|
|
.insert_header((header::IF_MODIFIED_SINCE, since))
|
2019-11-26 05:25:50 +00:00
|
|
|
.to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_ne!(resp.status(), StatusCode::NOT_MODIFIED);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2021-01-11 18:18:23 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_if_unmodified_since() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let file = NamedFile::open_async("Cargo.toml").await.unwrap();
|
2021-01-11 18:18:23 +00:00
|
|
|
let since = file.last_modified().unwrap();
|
|
|
|
|
|
|
|
let req = TestRequest::default()
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::IF_UNMODIFIED_SINCE, since))
|
2021-01-11 18:18:23 +00:00
|
|
|
.to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2021-01-11 18:18:23 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_if_unmodified_since_failed() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let file = NamedFile::open_async("Cargo.toml").await.unwrap();
|
2021-01-11 18:18:23 +00:00
|
|
|
let since = header::HttpDate::from(SystemTime::UNIX_EPOCH);
|
|
|
|
|
|
|
|
let req = TestRequest::default()
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::IF_UNMODIFIED_SINCE, since))
|
2021-01-11 18:18:23 +00:00
|
|
|
.to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2021-01-11 18:18:23 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::PRECONDITION_FAILED);
|
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_text() {
|
2021-11-22 01:19:09 +00:00
|
|
|
assert!(NamedFile::open_async("test--").await.is_err());
|
|
|
|
let mut file = NamedFile::open_async("Cargo.toml").await.unwrap();
|
2019-11-26 05:25:50 +00:00
|
|
|
{
|
|
|
|
file.file();
|
|
|
|
let _f: &File = &file;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let _f: &mut File = &mut file;
|
|
|
|
}
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
let req = TestRequest::default().to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
|
|
|
"text/x-toml"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
|
|
|
"inline; filename=\"Cargo.toml\""
|
|
|
|
);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_content_disposition() {
|
2021-11-22 01:19:09 +00:00
|
|
|
assert!(NamedFile::open_async("test--").await.is_err());
|
|
|
|
let mut file = NamedFile::open_async("Cargo.toml").await.unwrap();
|
2019-11-26 05:25:50 +00:00
|
|
|
{
|
|
|
|
file.file();
|
|
|
|
let _f: &File = &file;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let _f: &mut File = &mut file;
|
|
|
|
}
|
2019-07-20 05:43:49 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
let req = TestRequest::default().to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
|
|
|
"inline; filename=\"Cargo.toml\""
|
|
|
|
);
|
2019-07-20 05:43:49 +00:00
|
|
|
|
2021-11-22 01:19:09 +00:00
|
|
|
let file = NamedFile::open_async("Cargo.toml")
|
|
|
|
.await
|
2019-11-26 05:25:50 +00:00
|
|
|
.unwrap()
|
|
|
|
.disable_content_disposition();
|
|
|
|
let req = TestRequest::default().to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert!(resp.headers().get(header::CONTENT_DISPOSITION).is_none());
|
2019-07-20 05:43:49 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_non_ascii_file_name() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let file = {
|
|
|
|
#[cfg(feature = "experimental-io-uring")]
|
|
|
|
{
|
|
|
|
crate::named::File::open("Cargo.toml").await.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "experimental-io-uring"))]
|
|
|
|
{
|
|
|
|
crate::named::File::open("Cargo.toml").unwrap()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut file = NamedFile::from_file(file, "貨物.toml").unwrap();
|
2019-11-26 05:25:50 +00:00
|
|
|
{
|
|
|
|
file.file();
|
|
|
|
let _f: &File = &file;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let _f: &mut File = &mut file;
|
|
|
|
}
|
2019-11-06 14:08:37 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
let req = TestRequest::default().to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
|
|
|
"text/x-toml"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2019-11-06 14:08:37 +00:00
|
|
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
|
|
|
"inline; filename=\"貨物.toml\"; filename*=UTF-8''%E8%B2%A8%E7%89%A9.toml"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_set_content_type() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let mut file = NamedFile::open_async("Cargo.toml")
|
|
|
|
.await
|
2019-11-26 05:25:50 +00:00
|
|
|
.unwrap()
|
|
|
|
.set_content_type(mime::TEXT_XML);
|
|
|
|
{
|
|
|
|
file.file();
|
|
|
|
let _f: &File = &file;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let _f: &mut File = &mut file;
|
|
|
|
}
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
let req = TestRequest::default().to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
|
|
|
"text/xml"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
|
|
|
"inline; filename=\"Cargo.toml\""
|
|
|
|
);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_image() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let mut file = NamedFile::open_async("tests/test.png").await.unwrap();
|
2019-11-26 05:25:50 +00:00
|
|
|
{
|
|
|
|
file.file();
|
|
|
|
let _f: &File = &file;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let _f: &mut File = &mut file;
|
|
|
|
}
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
let req = TestRequest::default().to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
|
|
|
"image/png"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
|
|
|
"inline; filename=\"test.png\""
|
|
|
|
);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2021-06-16 19:33:22 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_javascript() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let file = NamedFile::open_async("tests/test.js").await.unwrap();
|
2021-06-16 19:33:22 +00:00
|
|
|
|
|
|
|
let req = TestRequest::default().to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2021-06-16 19:33:22 +00:00
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
2024-06-30 19:28:11 +00:00
|
|
|
"text/javascript",
|
2021-06-16 19:33:22 +00:00
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
2024-06-30 19:28:11 +00:00
|
|
|
"inline; filename=\"test.js\"",
|
2021-06-16 19:33:22 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_image_attachment() {
|
|
|
|
let cd = ContentDisposition {
|
|
|
|
disposition: DispositionType::Attachment,
|
|
|
|
parameters: vec![DispositionParam::Filename(String::from("test.png"))],
|
|
|
|
};
|
2021-11-22 01:19:09 +00:00
|
|
|
let mut file = NamedFile::open_async("tests/test.png")
|
|
|
|
.await
|
2019-11-26 05:25:50 +00:00
|
|
|
.unwrap()
|
|
|
|
.set_content_disposition(cd);
|
|
|
|
{
|
|
|
|
file.file();
|
|
|
|
let _f: &File = &file;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let _f: &mut File = &mut file;
|
|
|
|
}
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
let req = TestRequest::default().to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
|
|
|
"image/png"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
|
|
|
"attachment; filename=\"test.png\""
|
|
|
|
);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_binary() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let mut file = NamedFile::open_async("tests/test.binary").await.unwrap();
|
2019-11-26 05:25:50 +00:00
|
|
|
{
|
|
|
|
file.file();
|
|
|
|
let _f: &File = &file;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let _f: &mut File = &mut file;
|
|
|
|
}
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
let req = TestRequest::default().to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
|
|
|
"application/octet-stream"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
|
|
|
"attachment; filename=\"test.binary\""
|
|
|
|
);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2022-03-10 02:54:06 +00:00
|
|
|
#[allow(deprecated)]
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
2022-03-10 02:54:06 +00:00
|
|
|
async fn status_code_customize_same_output() {
|
|
|
|
let file1 = NamedFile::open_async("Cargo.toml")
|
2021-11-22 01:19:09 +00:00
|
|
|
.await
|
2019-11-26 05:25:50 +00:00
|
|
|
.unwrap()
|
|
|
|
.set_status_code(StatusCode::NOT_FOUND);
|
2022-03-10 02:54:06 +00:00
|
|
|
|
|
|
|
let file2 = NamedFile::open_async("Cargo.toml")
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.customize()
|
|
|
|
.with_status(StatusCode::NOT_FOUND);
|
|
|
|
|
|
|
|
let req = TestRequest::default().to_http_request();
|
|
|
|
let res1 = file1.respond_to(&req);
|
|
|
|
let res2 = file2.respond_to(&req);
|
|
|
|
|
|
|
|
assert_eq!(res1.status(), StatusCode::NOT_FOUND);
|
|
|
|
assert_eq!(res2.status(), StatusCode::NOT_FOUND);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_status_code_text() {
|
|
|
|
let mut file = NamedFile::open_async("Cargo.toml").await.unwrap();
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
{
|
|
|
|
file.file();
|
|
|
|
let _f: &File = &file;
|
|
|
|
}
|
2022-03-10 02:54:06 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
{
|
|
|
|
let _f: &mut File = &mut file;
|
|
|
|
}
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2022-03-10 02:54:06 +00:00
|
|
|
let file = file.customize().with_status(StatusCode::NOT_FOUND);
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
let req = TestRequest::default().to_http_request();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
|
|
|
"text/x-toml"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
|
|
|
"inline; filename=\"Cargo.toml\""
|
|
|
|
);
|
|
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_mime_override() {
|
2020-09-10 13:46:35 +00:00
|
|
|
fn all_attachment(_: &mime::Name<'_>) -> DispositionType {
|
2019-11-26 05:25:50 +00:00
|
|
|
DispositionType::Attachment
|
|
|
|
}
|
2019-03-25 20:02:37 +00:00
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
let srv = test::init_service(
|
2019-11-26 05:25:50 +00:00
|
|
|
App::new().service(
|
|
|
|
Files::new("/", ".")
|
|
|
|
.mime_override(all_attachment)
|
|
|
|
.index_file("Cargo.toml"),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let request = TestRequest::get().uri("/").to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let response = test::call_service(&srv, request).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
|
|
|
|
let content_disposition = response
|
|
|
|
.headers()
|
|
|
|
.get(header::CONTENT_DISPOSITION)
|
|
|
|
.expect("To have CONTENT_DISPOSITION");
|
|
|
|
let content_disposition = content_disposition
|
|
|
|
.to_str()
|
|
|
|
.expect("Convert CONTENT_DISPOSITION to str");
|
|
|
|
assert_eq!(content_disposition, "attachment; filename=\"Cargo.toml\"");
|
2019-03-25 20:02:37 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_ranges_status_code() {
|
2021-02-07 01:00:40 +00:00
|
|
|
let srv = test::init_service(
|
2019-11-26 05:25:50 +00:00
|
|
|
App::new().service(Files::new("/test", ".").index_file("Cargo.toml")),
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
// Valid range header
|
|
|
|
let request = TestRequest::get()
|
|
|
|
.uri("/t%65st/Cargo.toml")
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::RANGE, "bytes=10-20"))
|
2019-11-26 05:25:50 +00:00
|
|
|
.to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let response = test::call_service(&srv, request).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
|
|
|
|
|
|
|
|
// Invalid range header
|
|
|
|
let request = TestRequest::get()
|
|
|
|
.uri("/t%65st/Cargo.toml")
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::RANGE, "bytes=1-0"))
|
2019-11-26 05:25:50 +00:00
|
|
|
.to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let response = test::call_service(&srv, request).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
|
|
|
|
assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_content_range_headers() {
|
2021-04-02 07:26:59 +00:00
|
|
|
let srv = actix_test::start(|| App::new().service(Files::new("/", ".")));
|
2019-11-26 05:25:50 +00:00
|
|
|
|
|
|
|
// Valid range header
|
2020-05-17 21:54:42 +00:00
|
|
|
let response = srv
|
|
|
|
.get("/tests/test.binary")
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::RANGE, "bytes=10-20"))
|
2020-05-17 21:54:42 +00:00
|
|
|
.send()
|
|
|
|
.await
|
2019-11-26 05:25:50 +00:00
|
|
|
.unwrap();
|
2020-05-17 21:54:42 +00:00
|
|
|
let content_range = response.headers().get(header::CONTENT_RANGE).unwrap();
|
|
|
|
assert_eq!(content_range.to_str().unwrap(), "bytes 10-20/100");
|
2019-11-26 05:25:50 +00:00
|
|
|
|
|
|
|
// Invalid range header
|
2020-05-17 21:54:42 +00:00
|
|
|
let response = srv
|
|
|
|
.get("/tests/test.binary")
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::RANGE, "bytes=10-5"))
|
2020-05-17 21:54:42 +00:00
|
|
|
.send()
|
|
|
|
.await
|
2019-11-26 05:25:50 +00:00
|
|
|
.unwrap();
|
2020-05-17 21:54:42 +00:00
|
|
|
let content_range = response.headers().get(header::CONTENT_RANGE).unwrap();
|
|
|
|
assert_eq!(content_range.to_str().unwrap(), "bytes */100");
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_content_length_headers() {
|
2021-04-02 07:26:59 +00:00
|
|
|
let srv = actix_test::start(|| App::new().service(Files::new("/", ".")));
|
2019-11-26 05:25:50 +00:00
|
|
|
|
|
|
|
// Valid range header
|
2020-05-17 21:54:42 +00:00
|
|
|
let response = srv
|
|
|
|
.get("/tests/test.binary")
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::RANGE, "bytes=10-20"))
|
2020-05-17 21:54:42 +00:00
|
|
|
.send()
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
|
|
|
assert_eq!(content_length.to_str().unwrap(), "11");
|
|
|
|
|
|
|
|
// Valid range header, starting from 0
|
|
|
|
let response = srv
|
|
|
|
.get("/tests/test.binary")
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::RANGE, "bytes=0-20"))
|
2020-05-17 21:54:42 +00:00
|
|
|
.send()
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
|
|
|
assert_eq!(content_length.to_str().unwrap(), "21");
|
2019-11-26 05:25:50 +00:00
|
|
|
|
|
|
|
// Without range header
|
2020-05-17 21:54:42 +00:00
|
|
|
let mut response = srv.get("/tests/test.binary").send().await.unwrap();
|
|
|
|
let content_length = response.headers().get(header::CONTENT_LENGTH).unwrap();
|
|
|
|
assert_eq!(content_length.to_str().unwrap(), "100");
|
2019-11-26 05:25:50 +00:00
|
|
|
|
2020-05-17 21:54:42 +00:00
|
|
|
// Should be no transfer-encoding
|
|
|
|
let transfer_encoding = response.headers().get(header::TRANSFER_ENCODING);
|
|
|
|
assert!(transfer_encoding.is_none());
|
2019-11-26 05:25:50 +00:00
|
|
|
|
2020-05-17 21:54:42 +00:00
|
|
|
// Check file contents
|
|
|
|
let bytes = response.body().await.unwrap();
|
2020-09-20 22:18:25 +00:00
|
|
|
let data = web::Bytes::from(fs::read("tests/test.binary").unwrap());
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(bytes, data);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_head_content_length_headers() {
|
2021-04-02 07:26:59 +00:00
|
|
|
let srv = actix_test::start(|| App::new().service(Files::new("/", ".")));
|
2020-05-17 21:54:42 +00:00
|
|
|
|
2020-07-21 23:28:33 +00:00
|
|
|
let response = srv.head("/tests/test.binary").send().await.unwrap();
|
2019-11-26 05:25:50 +00:00
|
|
|
|
2020-05-17 21:54:42 +00:00
|
|
|
let content_length = response
|
|
|
|
.headers()
|
|
|
|
.get(header::CONTENT_LENGTH)
|
|
|
|
.unwrap()
|
|
|
|
.to_str()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(content_length, "100");
|
2019-06-13 09:27:21 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_static_files_with_spaces() {
|
2023-07-17 01:38:12 +00:00
|
|
|
let srv =
|
|
|
|
test::init_service(App::new().service(Files::new("/", ".").index_file("Cargo.toml")))
|
|
|
|
.await;
|
2019-11-26 05:25:50 +00:00
|
|
|
let request = TestRequest::get()
|
|
|
|
.uri("/tests/test%20space.binary")
|
|
|
|
.to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let response = test::call_service(&srv, request).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
|
|
|
|
let bytes = test::read_body(response).await;
|
2020-09-20 22:18:25 +00:00
|
|
|
let data = web::Bytes::from(fs::read("tests/test space.binary").unwrap());
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(bytes, data);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2024-02-03 23:55:01 +00:00
|
|
|
#[cfg(not(target_os = "windows"))]
|
2024-01-06 10:11:40 +00:00
|
|
|
#[actix_rt::test]
|
2024-01-10 03:56:15 +00:00
|
|
|
async fn test_static_files_with_special_characters() {
|
2024-01-06 10:11:40 +00:00
|
|
|
// Create the file we want to test against ad-hoc. We can't check it in as otherwise
|
|
|
|
// Windows can't even checkout this repository.
|
2024-01-06 10:19:15 +00:00
|
|
|
let temp_dir = tempfile::tempdir().unwrap();
|
2024-01-10 03:56:15 +00:00
|
|
|
let file_with_newlines = temp_dir.path().join("test\n\x0B\x0C\rnewline.text");
|
2024-01-06 10:11:40 +00:00
|
|
|
fs::write(&file_with_newlines, "Look at my newlines").unwrap();
|
2024-01-06 10:19:15 +00:00
|
|
|
|
2024-01-06 10:11:40 +00:00
|
|
|
let srv = test::init_service(
|
2024-01-06 10:19:15 +00:00
|
|
|
App::new().service(Files::new("/", temp_dir.path()).index_file("Cargo.toml")),
|
2024-01-06 10:11:40 +00:00
|
|
|
)
|
|
|
|
.await;
|
2024-01-10 03:56:15 +00:00
|
|
|
let request = TestRequest::get()
|
|
|
|
.uri("/test%0A%0B%0C%0Dnewline.text")
|
|
|
|
.to_request();
|
2024-01-06 10:11:40 +00:00
|
|
|
let response = test::call_service(&srv, request).await;
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
|
|
|
|
let bytes = test::read_body(response).await;
|
|
|
|
let data = web::Bytes::from(fs::read(file_with_newlines).unwrap());
|
|
|
|
assert_eq!(bytes, data);
|
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_files_not_allowed() {
|
2021-02-07 01:00:40 +00:00
|
|
|
let srv = test::init_service(App::new().service(Files::new("/", "."))).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
|
|
|
|
let req = TestRequest::default()
|
|
|
|
.uri("/Cargo.toml")
|
|
|
|
.method(Method::POST)
|
|
|
|
.to_request();
|
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
let resp = test::call_service(&srv, req).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
let srv = test::init_service(App::new().service(Files::new("/", "."))).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
let req = TestRequest::default()
|
|
|
|
.method(Method::PUT)
|
|
|
|
.uri("/Cargo.toml")
|
|
|
|
.to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let resp = test::call_service(&srv, req).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_files_guards() {
|
2021-02-07 01:00:40 +00:00
|
|
|
let srv = test::init_service(
|
2021-04-22 17:31:21 +00:00
|
|
|
App::new().service(Files::new("/", ".").method_guard(guard::Post())),
|
2019-11-26 05:25:50 +00:00
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let req = TestRequest::default()
|
|
|
|
.uri("/Cargo.toml")
|
|
|
|
.method(Method::POST)
|
|
|
|
.to_request();
|
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
let resp = test::call_service(&srv, req).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
2019-10-08 04:09:40 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_content_encoding() {
|
2021-02-07 01:00:40 +00:00
|
|
|
let srv = test::init_service(App::new().wrap(Compress::default()).service(
|
2020-07-21 23:28:33 +00:00
|
|
|
web::resource("/").to(|| async {
|
2021-11-22 01:19:09 +00:00
|
|
|
NamedFile::open_async("Cargo.toml")
|
|
|
|
.await
|
2020-07-21 23:28:33 +00:00
|
|
|
.unwrap()
|
|
|
|
.set_content_encoding(header::ContentEncoding::Identity)
|
2019-11-26 05:25:50 +00:00
|
|
|
}),
|
|
|
|
))
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let request = TestRequest::get()
|
|
|
|
.uri("/")
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::ACCEPT_ENCODING, "gzip"))
|
2019-11-26 05:25:50 +00:00
|
|
|
.to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let res = test::call_service(&srv, request).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(res.status(), StatusCode::OK);
|
2022-01-03 18:46:04 +00:00
|
|
|
assert!(res.headers().contains_key(header::CONTENT_ENCODING));
|
|
|
|
assert!(!test::read_body(res).await.is_empty());
|
2019-04-04 21:00:56 +00:00
|
|
|
}
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_content_encoding_gzip() {
|
2021-02-07 01:00:40 +00:00
|
|
|
let srv = test::init_service(App::new().wrap(Compress::default()).service(
|
2020-07-21 23:28:33 +00:00
|
|
|
web::resource("/").to(|| async {
|
2021-11-22 01:19:09 +00:00
|
|
|
NamedFile::open_async("Cargo.toml")
|
|
|
|
.await
|
2020-07-21 23:28:33 +00:00
|
|
|
.unwrap()
|
|
|
|
.set_content_encoding(header::ContentEncoding::Gzip)
|
2019-11-26 05:25:50 +00:00
|
|
|
}),
|
|
|
|
))
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let request = TestRequest::get()
|
|
|
|
.uri("/")
|
2021-01-15 02:11:10 +00:00
|
|
|
.insert_header((header::ACCEPT_ENCODING, "gzip"))
|
2019-11-26 05:25:50 +00:00
|
|
|
.to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let res = test::call_service(&srv, request).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(res.status(), StatusCode::OK);
|
|
|
|
assert_eq!(
|
|
|
|
res.headers()
|
|
|
|
.get(header::CONTENT_ENCODING)
|
|
|
|
.unwrap()
|
|
|
|
.to_str()
|
|
|
|
.unwrap(),
|
|
|
|
"gzip"
|
|
|
|
);
|
2019-04-01 01:19:18 +00:00
|
|
|
}
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_allowed_method() {
|
|
|
|
let req = TestRequest::default().method(Method::GET).to_http_request();
|
2021-11-22 01:19:09 +00:00
|
|
|
let file = NamedFile::open_async("Cargo.toml").await.unwrap();
|
2022-01-24 11:56:01 +00:00
|
|
|
let resp = file.respond_to(&req);
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_static_files() {
|
2021-02-11 23:03:17 +00:00
|
|
|
let srv =
|
2023-07-17 01:38:12 +00:00
|
|
|
test::init_service(App::new().service(Files::new("/", ".").show_files_listing())).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
let req = TestRequest::with_uri("/missing").to_request();
|
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
let resp = test::call_service(&srv, req).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
let srv = test::init_service(App::new().service(Files::new("/", "."))).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
|
|
|
|
let req = TestRequest::default().to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let resp = test::call_service(&srv, req).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
|
|
|
|
2021-02-11 23:03:17 +00:00
|
|
|
let srv =
|
2023-07-17 01:38:12 +00:00
|
|
|
test::init_service(App::new().service(Files::new("/", ".").show_files_listing())).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
let req = TestRequest::with_uri("/tests").to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let resp = test::call_service(&srv, req).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
|
|
|
"text/html; charset=utf-8"
|
|
|
|
);
|
2019-11-21 05:31:31 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
let bytes = test::read_body(resp).await;
|
|
|
|
assert!(format!("{:?}", bytes).contains("/tests/test.png"));
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_redirect_to_slash_directory() {
|
2021-05-26 09:42:29 +00:00
|
|
|
// should not redirect if no index and files listing is disabled
|
2021-02-07 01:00:40 +00:00
|
|
|
let srv = test::init_service(
|
2019-11-26 05:25:50 +00:00
|
|
|
App::new().service(Files::new("/", ".").redirect_to_slash_directory()),
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
let req = TestRequest::with_uri("/tests").to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let resp = test::call_service(&srv, req).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
|
|
|
|
|
|
|
// should redirect if index present
|
2021-02-07 01:00:40 +00:00
|
|
|
let srv = test::init_service(
|
2019-11-26 05:25:50 +00:00
|
|
|
App::new().service(
|
|
|
|
Files::new("/", ".")
|
|
|
|
.index_file("test.png")
|
|
|
|
.redirect_to_slash_directory(),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
let req = TestRequest::with_uri("/tests").to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let resp = test::call_service(&srv, req).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::FOUND);
|
|
|
|
|
2021-05-26 09:42:29 +00:00
|
|
|
// should redirect if files listing is enabled
|
|
|
|
let srv = test::init_service(
|
|
|
|
App::new().service(
|
|
|
|
Files::new("/", ".")
|
|
|
|
.show_files_listing()
|
|
|
|
.redirect_to_slash_directory(),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
let req = TestRequest::with_uri("/tests").to_request();
|
|
|
|
let resp = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(resp.status(), StatusCode::FOUND);
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
// should not redirect if the path is wrong
|
|
|
|
let req = TestRequest::with_uri("/not_existing").to_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let resp = test::call_service(&srv, req).await;
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
2019-10-14 15:23:15 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_static_files_bad_directory() {
|
2021-02-28 23:01:59 +00:00
|
|
|
let service = Files::new("/", "./missing").new_service(()).await.unwrap();
|
|
|
|
|
|
|
|
let req = TestRequest::with_uri("/").to_srv_request();
|
|
|
|
let resp = test::call_service(&service, req).await;
|
|
|
|
|
|
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_default_handler_file_missing() {
|
2021-02-07 01:00:40 +00:00
|
|
|
let st = Files::new("/", ".")
|
2021-11-22 01:19:09 +00:00
|
|
|
.default_handler(|req: ServiceRequest| async {
|
|
|
|
Ok(req.into_response(HttpResponse::Ok().body("default content")))
|
2019-11-26 05:25:50 +00:00
|
|
|
})
|
2019-12-02 15:37:13 +00:00
|
|
|
.new_service(())
|
2019-11-26 05:25:50 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let req = TestRequest::with_uri("/missing").to_srv_request();
|
2021-02-07 01:00:40 +00:00
|
|
|
let resp = test::call_service(&st, req).await;
|
2021-02-28 23:01:59 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
|
|
let bytes = test::read_body(resp).await;
|
2020-09-20 22:18:25 +00:00
|
|
|
assert_eq!(bytes, web::Bytes::from_static(b"default content"));
|
2019-04-01 01:19:18 +00:00
|
|
|
}
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2021-02-28 23:01:59 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_serve_index_nested() {
|
|
|
|
let service = Files::new(".", ".")
|
|
|
|
.index_file("lib.rs")
|
|
|
|
.new_service(())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let req = TestRequest::default().uri("/src").to_srv_request();
|
|
|
|
let resp = test::call_service(&service, req).await;
|
|
|
|
|
|
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
|
|
|
"text/x-rust"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
|
|
|
"inline; filename=\"lib.rs\""
|
|
|
|
);
|
|
|
|
}
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn integration_serve_index() {
|
|
|
|
let srv = test::init_service(
|
|
|
|
App::new().service(Files::new("test", ".").index_file("Cargo.toml")),
|
|
|
|
)
|
|
|
|
.await;
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
let req = TestRequest::get().uri("/test").to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::OK);
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
let bytes = test::read_body(res).await;
|
2019-03-06 06:10:08 +00:00
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
let data = Bytes::from(fs::read("Cargo.toml").unwrap());
|
|
|
|
assert_eq!(bytes, data);
|
|
|
|
|
|
|
|
let req = TestRequest::get().uri("/test/").to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::OK);
|
|
|
|
|
|
|
|
let bytes = test::read_body(res).await;
|
|
|
|
let data = Bytes::from(fs::read("Cargo.toml").unwrap());
|
|
|
|
assert_eq!(bytes, data);
|
|
|
|
|
|
|
|
// nonexistent index file
|
|
|
|
let req = TestRequest::get().uri("/test/unknown").to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
|
|
|
|
|
|
|
let req = TestRequest::get().uri("/test/unknown/").to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[actix_rt::test]
|
|
|
|
async fn integration_percent_encoded() {
|
|
|
|
let srv = test::init_service(
|
|
|
|
App::new().service(Files::new("test", ".").index_file("Cargo.toml")),
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let req = TestRequest::get().uri("/test/%43argo.toml").to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::OK);
|
2022-01-04 12:54:11 +00:00
|
|
|
|
|
|
|
// `%2F` == `/`
|
|
|
|
let req = TestRequest::get().uri("/test%2Ftest.binary").to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
|
|
|
|
|
|
|
let req = TestRequest::get().uri("/test/Cargo.toml%00").to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_percent_encoding_2() {
|
2024-01-06 10:19:15 +00:00
|
|
|
let temp_dir = tempfile::tempdir().unwrap();
|
2022-01-04 12:54:11 +00:00
|
|
|
let filename = match cfg!(unix) {
|
2024-01-06 10:11:40 +00:00
|
|
|
true => "ض:?#[]{}<>()@!$&'`|*+,;= %20\n.test",
|
2022-01-04 12:54:11 +00:00
|
|
|
false => "ض#[]{}()@!$&'`+,;= %20.test",
|
|
|
|
};
|
|
|
|
let filename_encoded = filename
|
|
|
|
.as_bytes()
|
|
|
|
.iter()
|
2023-08-29 00:11:11 +00:00
|
|
|
.fold(String::new(), |mut buf, c| {
|
|
|
|
write!(&mut buf, "%{:02X}", c).unwrap();
|
|
|
|
buf
|
|
|
|
});
|
2024-01-06 10:19:15 +00:00
|
|
|
std::fs::File::create(temp_dir.path().join(filename)).unwrap();
|
2022-01-04 12:54:11 +00:00
|
|
|
|
2024-01-06 10:19:15 +00:00
|
|
|
let srv = test::init_service(App::new().service(Files::new("/", temp_dir.path()))).await;
|
2022-01-04 12:54:11 +00:00
|
|
|
|
|
|
|
let req = TestRequest::get()
|
|
|
|
.uri(&format!("/{}", filename_encoded))
|
|
|
|
.to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::OK);
|
2021-02-07 01:00:40 +00:00
|
|
|
}
|
2021-04-13 04:28:30 +00:00
|
|
|
|
2021-04-18 22:34:51 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_serve_named_file() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let factory = NamedFile::open_async("Cargo.toml").await.unwrap();
|
|
|
|
let srv = test::init_service(App::new().service(factory)).await;
|
2021-04-18 22:34:51 +00:00
|
|
|
|
|
|
|
let req = TestRequest::get().uri("/Cargo.toml").to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::OK);
|
|
|
|
|
|
|
|
let bytes = test::read_body(res).await;
|
|
|
|
let data = Bytes::from(fs::read("Cargo.toml").unwrap());
|
|
|
|
assert_eq!(bytes, data);
|
|
|
|
|
|
|
|
let req = TestRequest::get().uri("/test/unknown").to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_serve_named_file_prefix() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let factory = NamedFile::open_async("Cargo.toml").await.unwrap();
|
|
|
|
let srv =
|
|
|
|
test::init_service(App::new().service(web::scope("/test").service(factory))).await;
|
2021-04-18 22:34:51 +00:00
|
|
|
|
|
|
|
let req = TestRequest::get().uri("/test/Cargo.toml").to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::OK);
|
|
|
|
|
|
|
|
let bytes = test::read_body(res).await;
|
|
|
|
let data = Bytes::from(fs::read("Cargo.toml").unwrap());
|
|
|
|
assert_eq!(bytes, data);
|
|
|
|
|
|
|
|
let req = TestRequest::get().uri("/Cargo.toml").to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_named_file_default_service() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let factory = NamedFile::open_async("Cargo.toml").await.unwrap();
|
|
|
|
let srv = test::init_service(App::new().default_service(factory)).await;
|
2021-04-18 22:34:51 +00:00
|
|
|
|
|
|
|
for route in ["/foobar", "/baz", "/"].iter() {
|
|
|
|
let req = TestRequest::get().uri(route).to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::OK);
|
|
|
|
|
|
|
|
let bytes = test::read_body(res).await;
|
|
|
|
let data = Bytes::from(fs::read("Cargo.toml").unwrap());
|
|
|
|
assert_eq!(bytes, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_default_handler_named_file() {
|
2021-11-22 01:19:09 +00:00
|
|
|
let factory = NamedFile::open_async("Cargo.toml").await.unwrap();
|
2021-04-18 22:34:51 +00:00
|
|
|
let st = Files::new("/", ".")
|
2021-11-22 01:19:09 +00:00
|
|
|
.default_handler(factory)
|
2021-04-18 22:34:51 +00:00
|
|
|
.new_service(())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let req = TestRequest::with_uri("/missing").to_srv_request();
|
|
|
|
let resp = test::call_service(&st, req).await;
|
|
|
|
|
|
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
|
|
let bytes = test::read_body(resp).await;
|
|
|
|
let data = Bytes::from(fs::read("Cargo.toml").unwrap());
|
|
|
|
assert_eq!(bytes, data);
|
|
|
|
}
|
|
|
|
|
2021-04-13 04:28:30 +00:00
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_symlinks() {
|
|
|
|
let srv = test::init_service(App::new().service(Files::new("test", "."))).await;
|
|
|
|
|
|
|
|
let req = TestRequest::get()
|
|
|
|
.uri("/test/tests/symlink-test.png")
|
|
|
|
.to_request();
|
|
|
|
let res = test::call_service(&srv, req).await;
|
|
|
|
assert_eq!(res.status(), StatusCode::OK);
|
|
|
|
assert_eq!(
|
|
|
|
res.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
|
|
|
"inline; filename=\"symlink-test.png\""
|
|
|
|
);
|
|
|
|
}
|
2021-06-19 20:00:31 +00:00
|
|
|
|
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_index_with_show_files_listing() {
|
|
|
|
let service = Files::new(".", ".")
|
|
|
|
.index_file("lib.rs")
|
|
|
|
.show_files_listing()
|
|
|
|
.new_service(())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Serve the index if exists
|
|
|
|
let req = TestRequest::default().uri("/src").to_srv_request();
|
|
|
|
let resp = test::call_service(&service, req).await;
|
|
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
|
|
|
"text/x-rust"
|
|
|
|
);
|
|
|
|
|
|
|
|
// Show files listing, otherwise.
|
|
|
|
let req = TestRequest::default().uri("/tests").to_srv_request();
|
|
|
|
let resp = test::call_service(&service, req).await;
|
|
|
|
assert_eq!(
|
|
|
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
|
|
|
"text/html; charset=utf-8"
|
|
|
|
);
|
|
|
|
let bytes = test::read_body(resp).await;
|
|
|
|
assert!(format!("{:?}", bytes).contains("/tests/test.png"));
|
|
|
|
}
|
2021-06-25 13:21:57 +00:00
|
|
|
|
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_path_filter() {
|
|
|
|
// prevent searching subdirectories
|
|
|
|
let st = Files::new("/", ".")
|
|
|
|
.path_filter(|path, _| path.components().count() == 1)
|
|
|
|
.new_service(())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let req = TestRequest::with_uri("/Cargo.toml").to_srv_request();
|
|
|
|
let resp = test::call_service(&st, req).await;
|
|
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
|
|
|
|
|
|
let req = TestRequest::with_uri("/src/lib.rs").to_srv_request();
|
|
|
|
let resp = test::call_service(&st, req).await;
|
|
|
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[actix_rt::test]
|
|
|
|
async fn test_default_handler_filter() {
|
|
|
|
let st = Files::new("/", ".")
|
2021-11-22 01:19:09 +00:00
|
|
|
.default_handler(|req: ServiceRequest| async {
|
|
|
|
Ok(req.into_response(HttpResponse::Ok().body("default content")))
|
2021-06-25 13:21:57 +00:00
|
|
|
})
|
|
|
|
.path_filter(|path, _| path.extension() == Some("png".as_ref()))
|
|
|
|
.new_service(())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let req = TestRequest::with_uri("/Cargo.toml").to_srv_request();
|
|
|
|
let resp = test::call_service(&st, req).await;
|
|
|
|
|
|
|
|
assert_eq!(resp.status(), StatusCode::OK);
|
|
|
|
let bytes = test::read_body(resp).await;
|
|
|
|
assert_eq!(bytes, web::Bytes::from_static(b"default content"));
|
|
|
|
}
|
2019-03-06 06:10:08 +00:00
|
|
|
}
|