use crate::ServeEntry;
use crate::config::{StaticFileConfig};
use std::path::Path;
use futures::Stream;
use actix_web::{HttpRequest, HttpResponse};
use actix_web::dev::{AsyncResult, Handler};
use actix_web::error::Error;
impl<S: 'static, C: 'static + StaticFileConfig> Handler<S> for crate::StaticFiles<C> {
type Result = Result<AsyncResult<HttpResponse>, Error>;
fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
if !C::is_method_allowed(req.method()) {
return Ok(HttpResponse::MethodNotAllowed().header(http::header::CONTENT_TYPE, "text/plain")
.header(http::header::ALLOW, "GET, HEAD")
.body("This resource only supports GET and HEAD.")
.into());
}
let path = req.match_info().get_decoded("tail").unwrap_or_else(|| "".to_string());
let path = Path::new(path.trim_start_matches("/"));
let result = self.serve(path);
match result {
ServeEntry::NotFound | ServeEntry::IoError(_) => {
let mut response = HttpResponse::new(http::StatusCode::OK);
let (code, body) = self.handle_not_found(&path, response.headers_mut());
*response.status_mut() = code;
if body.len() > 0 {
response.set_body(body);
}
Ok(response.into())
},
ServeEntry::Directory(path, dir) => Ok(HttpResponse::Ok().content_type("text/html; charset=utf-8").body(self.list_dir(&path, dir)).into()),
ServeEntry::File(file, meta) => {
let mut response = HttpResponse::new(http::StatusCode::OK);
let (code, body) = self.serve_file(&path, file, meta, req.method().clone(), req.headers(), response.headers_mut());
*response.status_mut() = code;
if response.headers().contains_key(http::header::CONTENT_RANGE) {
match code {
http::StatusCode::OK | http::StatusCode::PARTIAL_CONTENT => {
response.set_content_encoding(actix_web::http::ContentEncoding::Identity);
},
_ => ()
}
}
if let Some(body) = body {
response.set_body(actix_web::Body::Streaming(Box::new(body.map_err(|e| e.into()))))
}
Ok(response.into())
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::{Async, Future};
use actix_web::test::TestRequest;
use std::time::{Duration, SystemTime};
use crate::utils::display_to_header;
pub struct DirectoryConfig;
impl StaticFileConfig for DirectoryConfig {
type FileService = crate::config::DefaultConfig;
type DirService = crate::config::DefaultConfig;
fn handle_directory(&self, _path: &Path) -> bool {
true
}
fn thread_pool_builder() -> threadpool::ThreadPool {
threadpool::Builder::new().num_threads(1).build()
}
}
macro_rules! from_async_result {
($resp:expr) => {
match $resp.poll().unwrap() {
Async::Ready(res) => res,
Async::NotReady => panic!("Future is not ready!?")
}
}
}
#[test]
fn test_if_modified_since_without_if_none_match() {
let files = crate::StaticFiles::new(DirectoryConfig);
let since = SystemTime::now() + Duration::from_secs(60);
let since = httpdate::HttpDate::from(since);
let req = TestRequest::with_uri("/Cargo.toml").header(http::header::IF_MODIFIED_SINCE, display_to_header(&since)).finish();
let mut resp = files.handle(&req).expect("To get response");
let resp = from_async_result!(resp);
assert_eq!(resp.status(), http::StatusCode::NOT_MODIFIED);
}
#[test]
fn test_if_modified_since_with_if_none_match() {
let files = crate::StaticFiles::new(DirectoryConfig);
let since = SystemTime::now() + Duration::from_secs(60);
let since = httpdate::HttpDate::from(since);
let req = TestRequest::with_uri("/Cargo.toml").header(http::header::IF_MODIFIED_SINCE, display_to_header(&since))
.header(http::header::IF_NONE_MATCH, "wrong etag")
.finish();
let mut resp = files.handle(&req).expect("To get response");
let resp = from_async_result!(resp);
assert_ne!(resp.status(), http::StatusCode::NOT_MODIFIED);
}
}