diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 8841f7fb1..634296c56 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -18,7 +18,6 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "3.0.0", default-features = false } -actix-http = "2.0.0" actix-service = "1.0.6" bitflags = "1" bytes = "0.5.3" diff --git a/actix-files/src/chunked.rs b/actix-files/src/chunked.rs index ab65c5420..580b06787 100644 --- a/actix-files/src/chunked.rs +++ b/actix-files/src/chunked.rs @@ -1,5 +1,5 @@ use std::{ - cmp, + cmp, fmt, fs::File, future::Future, io::{self, Read, Seek}, @@ -31,6 +31,12 @@ pub struct ChunkedReadFile { pub(crate) counter: u64, } +impl fmt::Debug for ChunkedReadFile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("ChunkedReadFile") + } +} + impl Stream for ChunkedReadFile { type Item = Result; diff --git a/actix-files/src/files.rs b/actix-files/src/files.rs index 14d501b05..2b55e1aa9 100644 --- a/actix-files/src/files.rs +++ b/actix-files/src/files.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, io, path::PathBuf, rc::Rc}; +use std::{cell::RefCell, fmt, io, path::PathBuf, rc::Rc}; use actix_service::{boxed, IntoServiceFactory, ServiceFactory}; use actix_web::{ @@ -17,7 +17,7 @@ use crate::{ HttpNewService, MimeOverride, }; -/// Static files handling +/// Static files handling service. /// /// `Files` service must be registered with `App::service()` method. /// @@ -41,6 +41,12 @@ pub struct Files { guards: Option>, } +impl fmt::Debug for Files { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Files") + } +} + impl Clone for Files { fn clone(&self) -> Self { Self { @@ -193,11 +199,13 @@ impl HttpServiceFactory for Files { if self.default.borrow().is_none() { *self.default.borrow_mut() = Some(config.default_service()); } + let rdef = if config.is_root() { ResourceDef::root_prefix(&self.path) } else { ResourceDef::prefix(&self.path) }; + config.register_service(rdef, None, self, None) } } diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 0bcf4057f..1fc7cb3f3 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -1,6 +1,22 @@ -//! Static files support +//! Static files support for Actix Web. +//! +//! Provides a non-blocking service for serving static files from disk. +//! +//! # Example +//! ```rust +//! use actix_web::App; +//! use actix_files::Files; +//! +//! let app = App::new() +//! .service(Files::new("/static", ".")); +//! ``` +//! +//! # Implementation Quirks +//! - If a filename contains non-ascii characters, that file will be served with the `charset=utf-8` +//! extension on the Content-Type header. #![deny(rust_2018_idioms)] +#![warn(missing_docs, missing_debug_implementations)] use std::io; diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index 89107afc0..3caa4a809 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -7,17 +7,20 @@ use std::time::{SystemTime, UNIX_EPOCH}; #[cfg(unix)] use std::os::unix::fs::MetadataExt; -use bitflags::bitflags; -use mime_guess::from_path; - -use actix_http::body::SizedStream; -use actix_web::dev::BodyEncoding; -use actix_web::http::header::{ - self, Charset, ContentDisposition, DispositionParam, DispositionType, ExtendedValue, +use actix_web::{ + dev::{BodyEncoding, SizedStream}, + http::{ + header::{ + self, Charset, ContentDisposition, DispositionParam, DispositionType, + ExtendedValue, + }, + ContentEncoding, StatusCode, + }, + Error, HttpMessage, HttpRequest, HttpResponse, Responder, }; -use actix_web::http::{ContentEncoding, StatusCode}; -use actix_web::{Error, HttpMessage, HttpRequest, HttpResponse, Responder}; +use bitflags::bitflags; use futures_util::future::{ready, Ready}; +use mime_guess::from_path; use crate::range::HttpRange; use crate::ChunkedReadFile; @@ -247,6 +250,7 @@ impl NamedFile { let dur = mtime .duration_since(UNIX_EPOCH) .expect("modification time must be after epoch"); + header::EntityTag::strong(format!( "{:x}:{:x}:{:x}:{:x}", ino, @@ -261,6 +265,7 @@ impl NamedFile { self.modified.map(|mtime| mtime.into()) } + /// Creates an `HttpResponse` with file as a streaming body. pub fn into_response(self, req: &HttpRequest) -> Result { if self.status_code != StatusCode::OK { let mut resp = HttpResponse::build(self.status_code); @@ -364,10 +369,10 @@ impl NamedFile { // check for range header if let Some(ranges) = req.headers().get(header::RANGE) { - if let Ok(rangesheader) = ranges.to_str() { - if let Ok(rangesvec) = HttpRange::parse(rangesheader, length) { - length = rangesvec[0].length; - offset = rangesvec[0].start; + if let Ok(ranges_header) = ranges.to_str() { + if let Ok(ranges) = HttpRange::parse(ranges_header, length) { + length = ranges[0].length; + offset = ranges[0].start; resp.encoding(ContentEncoding::Identity); resp.header( diff --git a/actix-files/src/path_buf.rs b/actix-files/src/path_buf.rs index 6dd8dd429..2f3ae84d4 100644 --- a/actix-files/src/path_buf.rs +++ b/actix-files/src/path_buf.rs @@ -16,7 +16,7 @@ impl FromStr for PathBufWrap { fn from_str(path: &str) -> Result { let mut buf = PathBuf::new(); - + for segment in path.split('/') { if segment == ".." { buf.pop(); diff --git a/actix-files/src/range.rs b/actix-files/src/range.rs index 2cdeca795..e891ca7ec 100644 --- a/actix-files/src/range.rs +++ b/actix-files/src/range.rs @@ -1,7 +1,10 @@ /// HTTP Range header representation. #[derive(Debug, Clone, Copy)] pub struct HttpRange { + /// Start of range. pub start: u64, + + /// Length of range. pub length: u64, } diff --git a/actix-files/src/service.rs b/actix-files/src/service.rs index f16806b13..cbf4c2d3b 100644 --- a/actix-files/src/service.rs +++ b/actix-files/src/service.rs @@ -1,5 +1,5 @@ use std::{ - io, + fmt, io, path::PathBuf, rc::Rc, task::{Context, Poll}, @@ -20,6 +20,7 @@ use crate::{ NamedFile, PathBufWrap, }; +/// Assembled file serving service. pub struct FilesService { pub(crate) directory: PathBuf, pub(crate) index: Option, @@ -49,6 +50,12 @@ impl FilesService { } } +impl fmt::Debug for FilesService { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("FilesService") + } +} + impl Service for FilesService { type Request = ServiceRequest; type Response = ServiceResponse;