diff --git a/.travis.yml b/.travis.yml
index 1d3c227a9..55a03ec8c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -35,7 +35,7 @@ before_script:
script:
- cargo clean
- - cargo test -- --nocapture
+ - cargo test --all -- --nocapture
# Upload docs
after_success:
@@ -49,7 +49,7 @@ after_success:
fi
- |
if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-03-02" ]]; then
- cargo tarpaulin --out Xml
+ cargo tarpaulin --out Xml --all
bash <(curl -s https://codecov.io/bash)
echo "Uploaded code coverage"
fi
diff --git a/Cargo.toml b/Cargo.toml
index 2f50b210a..acacb2f21 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,7 +28,7 @@ path = "src/lib.rs"
members = [
".",
"actix-session",
- "staticfiles",
+ "actix-staticfiles",
]
[package.metadata.docs.rs]
diff --git a/staticfiles/CHANGES.md b/actix-staticfiles/CHANGES.md
similarity index 100%
rename from staticfiles/CHANGES.md
rename to actix-staticfiles/CHANGES.md
diff --git a/staticfiles/Cargo.toml b/actix-staticfiles/Cargo.toml
similarity index 93%
rename from staticfiles/Cargo.toml
rename to actix-staticfiles/Cargo.toml
index 0aa589701..0a5517920 100644
--- a/staticfiles/Cargo.toml
+++ b/actix-staticfiles/Cargo.toml
@@ -20,7 +20,8 @@ path = "src/lib.rs"
[dependencies]
actix-web = { path=".." }
actix-http = { git = "https://github.com/actix/actix-http.git" }
-actix-service = "0.3.0"
+actix-service = { git = "https://github.com/actix/actix-net.git" }
+#actix-service = "0.3.0"
bytes = "0.4"
futures = "0.1"
diff --git a/staticfiles/README.md b/actix-staticfiles/README.md
similarity index 100%
rename from staticfiles/README.md
rename to actix-staticfiles/README.md
diff --git a/actix-staticfiles/src/config.rs b/actix-staticfiles/src/config.rs
new file mode 100644
index 000000000..da72da201
--- /dev/null
+++ b/actix-staticfiles/src/config.rs
@@ -0,0 +1,70 @@
+use actix_http::http::header::DispositionType;
+use actix_web::http::Method;
+use mime;
+
+/// Describes `StaticFiles` configiration
+///
+/// To configure actix's static resources you need
+/// to define own configiration type and implement any method
+/// you wish to customize.
+/// As trait implements reasonable defaults for Actix.
+///
+/// ## Example
+///
+/// ```rust,ignore
+/// extern crate mime;
+/// extern crate actix_web;
+/// use actix_web::http::header::DispositionType;
+/// use actix_web::fs::{StaticFileConfig, NamedFile};
+///
+/// #[derive(Default)]
+/// struct MyConfig;
+///
+/// impl StaticFileConfig for MyConfig {
+/// fn content_disposition_map(typ: mime::Name) -> DispositionType {
+/// DispositionType::Attachment
+/// }
+/// }
+///
+/// let file = NamedFile::open_with_config("foo.txt", MyConfig);
+/// ```
+pub trait StaticFileConfig: Default {
+ ///Describes mapping for mime type to content disposition header
+ ///
+ ///By default `IMAGE`, `TEXT` and `VIDEO` are mapped to Inline.
+ ///Others are mapped to Attachment
+ fn content_disposition_map(typ: mime::Name) -> DispositionType {
+ match typ {
+ mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline,
+ _ => DispositionType::Attachment,
+ }
+ }
+
+ ///Describes whether Actix should attempt to calculate `ETag`
+ ///
+ ///Defaults to `true`
+ fn is_use_etag() -> bool {
+ true
+ }
+
+ ///Describes whether Actix should use last modified date of file.
+ ///
+ ///Defaults to `true`
+ fn is_use_last_modifier() -> bool {
+ true
+ }
+
+ ///Describes allowed methods to access static resources.
+ ///
+ ///By default all methods are allowed
+ fn is_method_allowed(_method: &Method) -> bool {
+ true
+ }
+}
+
+///Default content disposition as described in
+///[StaticFileConfig](trait.StaticFileConfig.html)
+#[derive(Default)]
+pub struct DefaultConfig;
+
+impl StaticFileConfig for DefaultConfig {}
diff --git a/actix-staticfiles/src/error.rs b/actix-staticfiles/src/error.rs
new file mode 100644
index 000000000..f165a618a
--- /dev/null
+++ b/actix-staticfiles/src/error.rs
@@ -0,0 +1,41 @@
+use actix_web::{http::StatusCode, HttpResponse, ResponseError};
+use derive_more::Display;
+
+/// Errors which can occur when serving static files.
+#[derive(Display, Debug, PartialEq)]
+pub enum StaticFilesError {
+ /// Path is not a directory
+ #[display(fmt = "Path is not a directory. Unable to serve static files")]
+ IsNotDirectory,
+
+ /// Cannot render directory
+ #[display(fmt = "Unable to render directory without index file")]
+ IsDirectory,
+}
+
+/// Return `NotFound` for `StaticFilesError`
+impl ResponseError for StaticFilesError {
+ fn error_response(&self) -> HttpResponse {
+ HttpResponse::new(StatusCode::NOT_FOUND)
+ }
+}
+
+#[derive(Display, Debug, PartialEq)]
+pub enum UriSegmentError {
+ /// The segment started with the wrapped invalid character.
+ #[display(fmt = "The segment started with the wrapped invalid character")]
+ BadStart(char),
+ /// The segment contained the wrapped invalid character.
+ #[display(fmt = "The segment contained the wrapped invalid character")]
+ BadChar(char),
+ /// The segment ended with the wrapped invalid character.
+ #[display(fmt = "The segment ended with the wrapped invalid character")]
+ BadEnd(char),
+}
+
+/// Return `BadRequest` for `UriSegmentError`
+impl ResponseError for UriSegmentError {
+ fn error_response(&self) -> HttpResponse {
+ HttpResponse::new(StatusCode::BAD_REQUEST)
+ }
+}
diff --git a/actix-staticfiles/src/lib.rs b/actix-staticfiles/src/lib.rs
new file mode 100644
index 000000000..01306d4b3
--- /dev/null
+++ b/actix-staticfiles/src/lib.rs
@@ -0,0 +1,1482 @@
+//! Static files support
+use std::cell::RefCell;
+use std::fmt::Write;
+use std::fs::{DirEntry, File};
+use std::io::{Read, Seek};
+use std::marker::PhantomData;
+use std::path::{Path, PathBuf};
+use std::rc::Rc;
+use std::{cmp, io};
+
+use bytes::Bytes;
+use futures::{Async, Future, Poll, Stream};
+use mime;
+use mime_guess::get_mime_type;
+use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
+use v_htmlescape::escape as escape_html_entity;
+
+use actix_http::error::{Error, ErrorInternalServerError};
+use actix_service::{boxed::BoxedNewService, NewService, Service};
+use actix_web::dev::{self, HttpServiceFactory, ResourceDef, Url};
+use actix_web::{
+ blocking, FromRequest, HttpRequest, HttpResponse, Responder, ServiceFromRequest,
+ ServiceRequest, ServiceResponse,
+};
+use futures::future::{ok, FutureResult};
+
+mod config;
+mod error;
+mod named;
+
+use self::error::{StaticFilesError, UriSegmentError};
+pub use crate::config::{DefaultConfig, StaticFileConfig};
+pub use crate::named::NamedFile;
+
+type HttpNewService
= BoxedNewService<(), ServiceRequest
, ServiceResponse, (), ()>;
+
+/// 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 {
+ get_mime_type(ext)
+}
+
+#[doc(hidden)]
+/// A helper created from a `std::fs::File` which reads the file
+/// chunk-by-chunk on a `ThreadPool`.
+pub struct ChunkedReadFile {
+ size: u64,
+ offset: u64,
+ file: Option,
+ fut: Option>,
+ counter: u64,
+}
+
+fn handle_error(err: blocking::BlockingError) -> Error {
+ match err {
+ blocking::BlockingError::Error(err) => err.into(),
+ blocking::BlockingError::Canceled => {
+ ErrorInternalServerError("Unexpected error").into()
+ }
+ }
+}
+
+impl Stream for ChunkedReadFile {
+ type Item = Bytes;
+ type Error = Error;
+
+ fn poll(&mut self) -> Poll