1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-09-01 01:16:59 +02:00

add rustfmt config

This commit is contained in:
Nikolay Kim
2018-04-13 16:02:01 -07:00
parent 95f6277007
commit 113f5ad1a8
91 changed files with 8057 additions and 5509 deletions

318
src/fs.rs
View File

@@ -1,30 +1,30 @@
//! Static files support
use std::{io, cmp};
use std::io::{Read, Seek};
use std::fmt::Write;
use std::fs::{File, DirEntry, Metadata};
use std::path::{Path, PathBuf};
use std::fs::{DirEntry, File, Metadata};
use std::io::{Read, Seek};
use std::ops::{Deref, DerefMut};
use std::time::{SystemTime, UNIX_EPOCH};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};
use std::{cmp, io};
#[cfg(unix)]
use std::os::unix::fs::MetadataExt;
use bytes::{Bytes, BytesMut, BufMut};
use futures::{Async, Poll, Future, Stream};
use futures_cpupool::{CpuPool, CpuFuture};
use bytes::{BufMut, Bytes, BytesMut};
use futures::{Async, Future, Poll, Stream};
use futures_cpupool::{CpuFuture, CpuPool};
use mime_guess::get_mime_type;
use percent_encoding::percent_decode;
use header;
use error::Error;
use param::FromParam;
use handler::{Handler, RouteHandler, WrapHandler, Responder, Reply};
use handler::{Handler, Reply, Responder, RouteHandler, WrapHandler};
use header;
use http::{Method, StatusCode};
use httpmessage::HttpMessage;
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use param::FromParam;
/// A file with an associated name; responds with the Content-Type based on the
/// file extension.
@@ -55,9 +55,15 @@ impl NamedFile {
let path = path.as_ref().to_path_buf();
let modified = md.modified().ok();
let cpu_pool = None;
Ok(NamedFile{path, file, md, modified, cpu_pool,
only_get: false,
status_code: StatusCode::OK})
Ok(NamedFile {
path,
file,
md,
modified,
cpu_pool,
only_get: false,
status_code: StatusCode::OK,
})
}
/// Allow only GET and HEAD methods
@@ -110,17 +116,25 @@ impl NamedFile {
self.modified.as_ref().map(|mtime| {
let ino = {
#[cfg(unix)]
{ self.md.ino() }
{
self.md.ino()
}
#[cfg(not(unix))]
{ 0 }
{
0
}
};
let dur = mtime.duration_since(UNIX_EPOCH)
let dur = mtime
.duration_since(UNIX_EPOCH)
.expect("modification time must be after epoch");
header::EntityTag::strong(
format!("{:x}:{:x}:{:x}:{:x}",
ino, self.md.len(), dur.as_secs(),
dur.subsec_nanos()))
header::EntityTag::strong(format!(
"{:x}:{:x}:{:x}:{:x}",
ino,
self.md.len(),
dur.as_secs(),
dur.subsec_nanos()
))
})
}
@@ -178,7 +192,6 @@ fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
}
}
impl Responder for NamedFile {
type Item = HttpResponse;
type Error = io::Error;
@@ -187,23 +200,27 @@ impl Responder for NamedFile {
if self.status_code != StatusCode::OK {
let mut resp = HttpResponse::build(self.status_code);
resp.if_some(self.path().extension(), |ext, resp| {
resp.set(header::ContentType(get_mime_type(&ext.to_string_lossy())));
resp.set(header::ContentType(get_mime_type(
&ext.to_string_lossy(),
)));
});
let reader = ChunkedReadFile {
size: self.md.len(),
offset: 0,
cpu_pool: self.cpu_pool.unwrap_or_else(|| req.cpu_pool().clone()),
cpu_pool: self.cpu_pool
.unwrap_or_else(|| req.cpu_pool().clone()),
file: Some(self.file),
fut: None,
};
return Ok(resp.streaming(reader))
return Ok(resp.streaming(reader));
}
if self.only_get && *req.method() != Method::GET && *req.method() != Method::HEAD {
if self.only_get && *req.method() != Method::GET && *req.method() != Method::HEAD
{
return Ok(HttpResponse::MethodNotAllowed()
.header(header::CONTENT_TYPE, "text/plain")
.header(header::ALLOW, "GET, HEAD")
.body("This resource only supports GET and HEAD."))
.header(header::CONTENT_TYPE, "text/plain")
.header(header::ALLOW, "GET, HEAD")
.body("This resource only supports GET and HEAD."));
}
let etag = self.etag();
@@ -233,17 +250,21 @@ impl Responder for NamedFile {
let mut resp = HttpResponse::build(self.status_code);
resp
.if_some(self.path().extension(), |ext, resp| {
resp.set(header::ContentType(get_mime_type(&ext.to_string_lossy())));
resp.if_some(self.path().extension(), |ext, resp| {
resp.set(header::ContentType(get_mime_type(
&ext.to_string_lossy(),
)));
}).if_some(last_modified, |lm, resp| {
resp.set(header::LastModified(lm));
})
.if_some(last_modified, |lm, resp| {resp.set(header::LastModified(lm));})
.if_some(etag, |etag, resp| {resp.set(header::ETag(etag));});
.if_some(etag, |etag, resp| {
resp.set(header::ETag(etag));
});
if precondition_failed {
return Ok(resp.status(StatusCode::PRECONDITION_FAILED).finish())
return Ok(resp.status(StatusCode::PRECONDITION_FAILED).finish());
} else if not_modified {
return Ok(resp.status(StatusCode::NOT_MODIFIED).finish())
return Ok(resp.status(StatusCode::NOT_MODIFIED).finish());
}
if *req.method() == Method::HEAD {
@@ -252,7 +273,8 @@ impl Responder for NamedFile {
let reader = ChunkedReadFile {
size: self.md.len(),
offset: 0,
cpu_pool: self.cpu_pool.unwrap_or_else(|| req.cpu_pool().clone()),
cpu_pool: self.cpu_pool
.unwrap_or_else(|| req.cpu_pool().clone()),
file: Some(self.file),
fut: None,
};
@@ -273,7 +295,7 @@ pub struct ChunkedReadFile {
impl Stream for ChunkedReadFile {
type Item = Bytes;
type Error= Error;
type Error = Error;
fn poll(&mut self) -> Poll<Option<Bytes>, Error> {
if self.fut.is_some() {
@@ -283,7 +305,7 @@ impl Stream for ChunkedReadFile {
self.file = Some(file);
self.offset += bytes.len() as u64;
Ok(Async::Ready(Some(bytes)))
},
}
Async::NotReady => Ok(Async::NotReady),
};
}
@@ -299,11 +321,11 @@ impl Stream for ChunkedReadFile {
let max_bytes = cmp::min(size.saturating_sub(offset), 65_536) as usize;
let mut buf = BytesMut::with_capacity(max_bytes);
file.seek(io::SeekFrom::Start(offset))?;
let nbytes = file.read(unsafe{buf.bytes_mut()})?;
let nbytes = file.read(unsafe { buf.bytes_mut() })?;
if nbytes == 0 {
return Err(io::ErrorKind::UnexpectedEof.into())
return Err(io::ErrorKind::UnexpectedEof.into());
}
unsafe{buf.advance_mut(nbytes)};
unsafe { buf.advance_mut(nbytes) };
Ok((file, buf.freeze()))
}));
self.poll()
@@ -313,9 +335,9 @@ impl Stream for ChunkedReadFile {
/// A directory; responds with the generated directory listing.
#[derive(Debug)]
pub struct Directory{
pub struct Directory {
base: PathBuf,
path: PathBuf
path: PathBuf,
}
impl Directory {
@@ -327,12 +349,12 @@ impl Directory {
if let Ok(ref entry) = *entry {
if let Some(name) = entry.file_name().to_str() {
if name.starts_with('.') {
return false
return false;
}
}
if let Ok(ref md) = entry.metadata() {
let ft = md.file_type();
return ft.is_dir() || ft.is_file() || ft.is_symlink()
return ft.is_dir() || ft.is_file() || ft.is_symlink();
}
}
false
@@ -353,7 +375,7 @@ impl Responder for Directory {
let entry = entry.unwrap();
let p = match entry.path().strip_prefix(&self.path) {
Ok(p) => base.join(p),
Err(_) => continue
Err(_) => continue,
};
// show file url as relative to static path
let file_url = format!("{}", p.to_string_lossy());
@@ -361,27 +383,38 @@ impl Responder for Directory {
// if file is a directory, add '/' to the end of the name
if let Ok(metadata) = entry.metadata() {
if metadata.is_dir() {
let _ = write!(body, "<li><a href=\"{}\">{}/</a></li>",
file_url, entry.file_name().to_string_lossy());
let _ = write!(
body,
"<li><a href=\"{}\">{}/</a></li>",
file_url,
entry.file_name().to_string_lossy()
);
} else {
let _ = write!(body, "<li><a href=\"{}\">{}</a></li>",
file_url, entry.file_name().to_string_lossy());
let _ = write!(
body,
"<li><a href=\"{}\">{}</a></li>",
file_url,
entry.file_name().to_string_lossy()
);
}
} else {
continue
continue;
}
}
}
let html = format!("<html>\
<head><title>{}</title></head>\
<body><h1>{}</h1>\
<ul>\
{}\
</ul></body>\n</html>", index_of, index_of, body);
let html = format!(
"<html>\
<head><title>{}</title></head>\
<body><h1>{}</h1>\
<ul>\
{}\
</ul></body>\n</html>",
index_of, index_of, body
);
Ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(html))
.content_type("text/html; charset=utf-8")
.body(html))
}
}
@@ -411,12 +444,11 @@ pub struct StaticFiles<S> {
_follow_symlinks: bool,
}
lazy_static!{
lazy_static! {
static ref DEFAULT_CPUPOOL: Mutex<CpuPool> = Mutex::new(CpuPool::new(20));
}
impl<S: 'static> StaticFiles<S> {
/// Create new `StaticFiles` instance for specified base directory.
pub fn new<T: Into<PathBuf>>(dir: T) -> StaticFiles<S> {
let dir = dir.into();
@@ -429,7 +461,7 @@ impl<S: 'static> StaticFiles<S> {
warn!("Is not directory `{:?}`", dir);
(dir, false)
}
},
}
Err(err) => {
warn!("Static files directory `{:?}` error: {}", dir, err);
(dir, false)
@@ -437,9 +469,7 @@ impl<S: 'static> StaticFiles<S> {
};
// use default CpuPool
let pool = {
DEFAULT_CPUPOOL.lock().unwrap().clone()
};
let pool = { DEFAULT_CPUPOOL.lock().unwrap().clone() };
StaticFiles {
directory: dir,
@@ -447,8 +477,9 @@ impl<S: 'static> StaticFiles<S> {
index: None,
show_index: false,
cpu_pool: pool,
default: Box::new(WrapHandler::new(
|_| HttpResponse::new(StatusCode::NOT_FOUND))),
default: Box::new(WrapHandler::new(|_| {
HttpResponse::new(StatusCode::NOT_FOUND)
})),
_chunk_size: 0,
_follow_symlinks: false,
}
@@ -485,12 +516,13 @@ impl<S: 'static> Handler<S> for StaticFiles<S> {
if !self.accessible {
Ok(self.default.handle(req))
} else {
let relpath = match req.match_info().get("tail").map(
|tail| percent_decode(tail.as_bytes()).decode_utf8().unwrap())
let relpath = match req.match_info()
.get("tail")
.map(|tail| percent_decode(tail.as_bytes()).decode_utf8().unwrap())
.map(|tail| PathBuf::from_param(tail.as_ref()))
{
Some(Ok(path)) => path,
_ => return Ok(self.default.handle(req))
_ => return Ok(self.default.handle(req)),
};
// full filepath
@@ -499,7 +531,8 @@ impl<S: 'static> Handler<S> for StaticFiles<S> {
if path.is_dir() {
if let Some(ref redir_index) = self.index {
// TODO: Don't redirect, just return the index content.
// TODO: It'd be nice if there were a good usable URL manipulation library
// TODO: It'd be nice if there were a good usable URL manipulation
// library
let mut new_path: String = req.path().to_owned();
for el in relpath.iter() {
new_path.push_str(&el.to_string_lossy());
@@ -516,14 +549,15 @@ impl<S: 'static> Handler<S> for StaticFiles<S> {
} else if self.show_index {
Directory::new(self.directory.clone(), path)
.respond_to(req.drop_state())?
.respond_to(req.drop_state())
.respond_to(req.drop_state())
} else {
Ok(self.default.handle(req))
}
} else {
NamedFile::open(path)?.set_cpu_pool(self.cpu_pool.clone())
NamedFile::open(path)?
.set_cpu_pool(self.cpu_pool.clone())
.respond_to(req.drop_state())?
.respond_to(req.drop_state())
.respond_to(req.drop_state())
}
}
}
@@ -533,33 +567,49 @@ impl<S: 'static> Handler<S> for StaticFiles<S> {
mod tests {
use super::*;
use application::App;
use test::{self, TestRequest};
use http::{header, Method, StatusCode};
use test::{self, TestRequest};
#[test]
fn test_named_file() {
assert!(NamedFile::open("test--").is_err());
let mut file = NamedFile::open("Cargo.toml").unwrap()
let mut file = NamedFile::open("Cargo.toml")
.unwrap()
.set_cpu_pool(CpuPool::new(1));
{ file.file();
let _f: &File = &file; }
{ let _f: &mut File = &mut file; }
{
file.file();
let _f: &File = &file;
}
{
let _f: &mut File = &mut file;
}
let resp = file.respond_to(HttpRequest::default()).unwrap();
assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), "text/x-toml")
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
"text/x-toml"
)
}
#[test]
fn test_named_file_status_code() {
let mut file = NamedFile::open("Cargo.toml").unwrap()
let mut file = NamedFile::open("Cargo.toml")
.unwrap()
.set_status_code(StatusCode::NOT_FOUND)
.set_cpu_pool(CpuPool::new(1));
{ file.file();
let _f: &File = &file; }
{ let _f: &mut File = &mut file; }
{
file.file();
let _f: &File = &file;
}
{
let _f: &mut File = &mut file;
}
let resp = file.respond_to(HttpRequest::default()).unwrap();
assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), "text/x-toml");
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
"text/x-toml"
);
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
}
@@ -584,13 +634,17 @@ mod tests {
fn test_static_files() {
let mut st = StaticFiles::new(".").show_files_listing();
st.accessible = false;
let resp = st.handle(HttpRequest::default()).respond_to(HttpRequest::default()).unwrap();
let resp = st.handle(HttpRequest::default())
.respond_to(HttpRequest::default())
.unwrap();
let resp = resp.as_response().expect("HTTP Response");
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
st.accessible = true;
st.show_index = false;
let resp = st.handle(HttpRequest::default()).respond_to(HttpRequest::default()).unwrap();
let resp = st.handle(HttpRequest::default())
.respond_to(HttpRequest::default())
.unwrap();
let resp = resp.as_response().expect("HTTP Response");
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
@@ -598,9 +652,14 @@ mod tests {
req.match_info_mut().add("tail", "");
st.show_index = true;
let resp = st.handle(req).respond_to(HttpRequest::default()).unwrap();
let resp = st.handle(req)
.respond_to(HttpRequest::default())
.unwrap();
let resp = resp.as_response().expect("HTTP Response");
assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), "text/html; charset=utf-8");
assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(),
"text/html; charset=utf-8"
);
assert!(resp.body().is_binary());
assert!(format!("{:?}", resp.body()).contains("README.md"));
}
@@ -611,18 +670,28 @@ mod tests {
let mut req = HttpRequest::default();
req.match_info_mut().add("tail", "guide");
let resp = st.handle(req).respond_to(HttpRequest::default()).unwrap();
let resp = st.handle(req)
.respond_to(HttpRequest::default())
.unwrap();
let resp = resp.as_response().expect("HTTP Response");
assert_eq!(resp.status(), StatusCode::FOUND);
assert_eq!(resp.headers().get(header::LOCATION).unwrap(), "/guide/index.html");
assert_eq!(
resp.headers().get(header::LOCATION).unwrap(),
"/guide/index.html"
);
let mut req = HttpRequest::default();
req.match_info_mut().add("tail", "guide/");
let resp = st.handle(req).respond_to(HttpRequest::default()).unwrap();
let resp = st.handle(req)
.respond_to(HttpRequest::default())
.unwrap();
let resp = resp.as_response().expect("HTTP Response");
assert_eq!(resp.status(), StatusCode::FOUND);
assert_eq!(resp.headers().get(header::LOCATION).unwrap(), "/guide/index.html");
assert_eq!(
resp.headers().get(header::LOCATION).unwrap(),
"/guide/index.html"
);
}
#[test]
@@ -631,58 +700,87 @@ mod tests {
let mut req = HttpRequest::default();
req.match_info_mut().add("tail", "tools/wsload");
let resp = st.handle(req).respond_to(HttpRequest::default()).unwrap();
let resp = st.handle(req)
.respond_to(HttpRequest::default())
.unwrap();
let resp = resp.as_response().expect("HTTP Response");
assert_eq!(resp.status(), StatusCode::FOUND);
assert_eq!(resp.headers().get(header::LOCATION).unwrap(), "/tools/wsload/Cargo.toml");
assert_eq!(
resp.headers().get(header::LOCATION).unwrap(),
"/tools/wsload/Cargo.toml"
);
}
#[test]
fn integration_redirect_to_index_with_prefix() {
let mut srv = test::TestServer::with_factory(
|| App::new()
let mut srv = test::TestServer::with_factory(|| {
App::new()
.prefix("public")
.handler("/", StaticFiles::new(".").index_file("Cargo.toml")));
.handler("/", StaticFiles::new(".").index_file("Cargo.toml"))
});
let request = srv.get().uri(srv.url("/public")).finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::FOUND);
let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap();
let loc = response
.headers()
.get(header::LOCATION)
.unwrap()
.to_str()
.unwrap();
assert_eq!(loc, "/public/Cargo.toml");
let request = srv.get().uri(srv.url("/public/")).finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::FOUND);
let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap();
let loc = response
.headers()
.get(header::LOCATION)
.unwrap()
.to_str()
.unwrap();
assert_eq!(loc, "/public/Cargo.toml");
}
#[test]
fn integration_redirect_to_index() {
let mut srv = test::TestServer::with_factory(
|| App::new()
.handler("test", StaticFiles::new(".").index_file("Cargo.toml")));
let mut srv = test::TestServer::with_factory(|| {
App::new().handler("test", StaticFiles::new(".").index_file("Cargo.toml"))
});
let request = srv.get().uri(srv.url("/test")).finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::FOUND);
let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap();
let loc = response
.headers()
.get(header::LOCATION)
.unwrap()
.to_str()
.unwrap();
assert_eq!(loc, "/test/Cargo.toml");
let request = srv.get().uri(srv.url("/test/")).finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::FOUND);
let loc = response.headers().get(header::LOCATION).unwrap().to_str().unwrap();
let loc = response
.headers()
.get(header::LOCATION)
.unwrap()
.to_str()
.unwrap();
assert_eq!(loc, "/test/Cargo.toml");
}
#[test]
fn integration_percent_encoded() {
let mut srv = test::TestServer::with_factory(
|| App::new()
.handler("test", StaticFiles::new(".").index_file("Cargo.toml")));
let mut srv = test::TestServer::with_factory(|| {
App::new().handler("test", StaticFiles::new(".").index_file("Cargo.toml"))
});
let request = srv.get().uri(srv.url("/test/%43argo.toml")).finish().unwrap();
let request = srv.get()
.uri(srv.url("/test/%43argo.toml"))
.finish()
.unwrap();
let response = srv.execute(request.send()).unwrap();
assert_eq!(response.status(), StatusCode::OK);
}