Fix normalization and Windows issues.

This commit is contained in:
Sergio Benitez 2018-10-23 13:22:26 -07:00
parent e0973d95f1
commit 26db5ecb4e
15 changed files with 94 additions and 64 deletions

View File

@ -23,6 +23,10 @@ mod context {
crate fn context<'a>(&'a self) -> impl Deref<Target=Context> + 'a {
&self.0
}
crate fn is_reloading(&self) -> bool {
false
}
}
}
@ -75,6 +79,10 @@ mod context {
self.context.read().unwrap()
}
crate fn is_reloading(&self) -> bool {
self.watcher.is_some()
}
fn context_mut<'a>(&'a self) -> impl DerefMut<Target=Context> + 'a {
self.context.write().unwrap()
}

View File

@ -46,8 +46,13 @@ impl<'a> Metadata<'a> {
/// # Example
///
/// ```rust
/// # #![feature(proc_macro_hygiene, decl_macro)]
/// # #[macro_use] extern crate rocket;
/// # extern crate rocket_contrib;
/// #
/// use rocket_contrib::templates::Metadata;
///
/// #[get("/")]
/// fn handler(metadata: Metadata) {
/// // Returns `true` if the template with name `"name"` was loaded.
/// let loaded = metadata.contains_template("name");
@ -56,6 +61,27 @@ impl<'a> Metadata<'a> {
pub fn contains_template(&self, name: &str) -> bool {
self.0.context().templates.contains_key(name)
}
/// Returns `true` if template reloading is enabled.
///
/// # Example
///
/// ```rust
/// # #![feature(proc_macro_hygiene, decl_macro)]
/// # #[macro_use] extern crate rocket;
/// # extern crate rocket_contrib;
/// #
/// use rocket_contrib::templates::Metadata;
///
/// #[get("/")]
/// fn handler(metadata: Metadata) {
/// // Returns `true` if template reloading is enabled.
/// let reloading = metadata.reloading();
/// }
/// ```
pub fn reloading(&self) -> bool {
self.0.is_reloading()
}
}
/// Retrieves the template metadata. If a template fairing hasn't been attached,

View File

@ -22,6 +22,11 @@ mod templates_tests {
}
}
#[get("/is_reloading")]
fn is_reloading(md: Metadata) -> Option<()> {
if md.reloading() { Some(()) } else { None }
}
fn template_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests").join("templates")
}
@ -32,7 +37,7 @@ mod templates_tests {
.expect("valid configuration");
::rocket::custom(config).attach(Template::fairing())
.mount("/", routes![template_check])
.mount("/", routes![template_check, is_reloading])
}
#[cfg(feature = "tera_templates")]
@ -142,8 +147,14 @@ mod templates_tests {
let reload_path = template_root().join("hbs").join("reload.txt.hbs");
write_file(&reload_path, INITIAL_TEXT);
// verify that the initial content is correct
// set up the client. if we can't reload templates, then just quit
let client = Client::new(rocket()).unwrap();
let res = client.get("/is_reloading").dispatch();
if res.status() != Status::Ok {
return;
}
// verify that the initial content is correct
let initial_rendered = Template::show(client.rocket(), RELOAD_TEMPLATE, ());
assert_eq!(initial_rendered, Some(INITIAL_TEXT.into()));

View File

@ -1 +1 @@
reload
initial

View File

@ -5,7 +5,9 @@ use std::{io, fs::Metadata, time::SystemTime};
#[derive(Copy, Clone)]
enum Kind {
Dynamic, Static
#[allow(dead_code)]
Dynamic,
Static
}
impl Kind {
@ -17,11 +19,6 @@ impl Kind {
Kind::Static => ".rlib"
}
}
fn prefix(self) -> &'static str {
#[cfg(windows)] { "" }
#[cfg(not(windows))] { "lib" }
}
}
fn target_path() -> PathBuf {
@ -52,7 +49,7 @@ fn best_time_for(metadata: &Metadata) -> SystemTime {
fn extern_dep(name: &str, kind: Kind) -> io::Result<String> {
let deps_root = target_path().join("deps");
let dep_name = format!("{}{}", kind.prefix(), name);
let dep_name = format!("lib{}", name);
let mut dep_path: Option<PathBuf> = None;
for entry in deps_root.read_dir().expect("read_dir call failed") {

View File

@ -6,6 +6,7 @@ use std::fmt;
use std::path::PathBuf;
use rocket::{Request, Outcome::*};
use rocket::http::ext::Normalize;
use rocket::local::Client;
use rocket::data::{self, Data, FromDataSimple};
use rocket::request::Form;
@ -48,7 +49,7 @@ fn post1(
simple: Simple,
) -> String {
let string = format!("{}, {}, {}, {}, {}, {}",
sky, name, a, query.field, path.display(), simple.0);
sky, name, a, query.field, path.normalized_str(), simple.0);
let uri = uri!(post2: a, name.url_decode_lossy(), path, sky, query.into_inner());
@ -65,7 +66,7 @@ fn post2(
simple: Simple,
) -> String {
let string = format!("{}, {}, {}, {}, {}, {}",
sky, name, a, query.field, path.display(), simple.0);
sky, name, a, query.field, path.normalized_str(), simple.0);
let uri = uri!(post2: a, name.url_decode_lossy(), path, sky, query.into_inner());

View File

@ -98,3 +98,21 @@ impl<'a, B: 'static + ToOwned + ?Sized> IntoOwned for Cow<'a, B> {
Cow::Owned(self.into_owned())
}
}
use std::path::Path;
pub trait Normalize {
fn normalized_str(&self) -> Cow<str>;
}
impl<T: AsRef<Path>> Normalize for T {
#[cfg(windows)]
fn normalized_str(&self) -> Cow<str> {
self.as_ref().to_string_lossy().replace('\\', "/").into()
}
#[cfg(not(windows))]
fn normalized_str(&self) -> Cow<str> {
self.as_ref().to_string_lossy()
}
}

View File

@ -2,7 +2,7 @@ use std::fmt;
use std::path::{Path, PathBuf};
use std::borrow::Cow;
use {RawStr, uri::Uri};
use {RawStr, uri::Uri, ext::Normalize};
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
@ -203,21 +203,21 @@ impl UriDisplay for String {
}
}
/// Percent-encodes each segment in the path.
/// Percent-encodes each segment in the path and normalizes separators.
impl UriDisplay for PathBuf {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let string = self.to_string_lossy();
let string = self.normalized_str();
let enc: Cow<str> = utf8_percent_encode(&string, PATH_ENCODE_SET).into();
write!(f, "{}", enc)
}
}
/// Percent-encodes each segment in the path.
/// Percent-encodes each segment in the and normalizes separators.
impl UriDisplay for Path {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let string = self.to_string_lossy();
let string = self.normalized_str();
let enc: Cow<str> = utf8_percent_encode(&string, PATH_ENCODE_SET).into();
write!(f, "{}", enc)
}

View File

@ -927,7 +927,10 @@ impl Config {
///
/// assert_eq!(config.root(), current_dir().unwrap());
/// assert_eq!(config.root_relative("abc"), config.root().join("abc"));
/// # #[cfg(not(windows))]
/// assert_eq!(config.root_relative("/abc"), Path::new("/abc"));
/// # #[cfg(windows)]
/// # assert_eq!(config.root_relative("C:\\abc"), Path::new("C:\\abc"));
/// ```
pub fn root_relative<P: AsRef<Path>>(&self, path: P) -> PathBuf {
let path = path.as_ref();

View File

@ -1,6 +1,4 @@
use std::{fmt, io};
use std::borrow::Cow;
use std::path::{Path, PathBuf, Component};
use std::io;
pub trait ReadExt: io::Read {
fn read_max(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
@ -19,37 +17,3 @@ pub trait ReadExt: io::Read {
}
impl<T: io::Read> ReadExt for T { }
pub struct NormalizedPath<'a>(&'a Path);
impl<'a> fmt::Display for NormalizedPath<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn to_str<'c>(c: &'c Component) -> Cow<'c, str> {
c.as_os_str().to_string_lossy()
}
let mut components = self.0.components();
match (components.next(), components.next()) {
(Some(Component::RootDir), Some(c)) => write!(f, "/{}", to_str(&c))?,
(Some(a), Some(b)) => write!(f, "{}/{}", to_str(&a), to_str(&b))?,
(Some(c), None) => write!(f, "{}", to_str(&c))?,
_ => return Ok(())
};
for c in components {
write!(f, "/{}", to_str(&c))?;
}
Ok(())
}
}
pub trait Normalize {
fn normalized(&self) -> NormalizedPath;
}
impl<T: AsRef<Path>> Normalize for T {
fn normalized(&self) -> NormalizedPath {
NormalizedPath(self.as_ref())
}
}

View File

@ -116,7 +116,6 @@ extern crate isatty;
#[cfg(test)] #[macro_use] extern crate lazy_static;
#[doc(hidden)] #[macro_use] pub mod logger;
#[doc(hidden)] pub mod ext;
pub mod local;
pub mod request;
pub mod response;
@ -142,6 +141,7 @@ mod router;
mod rocket;
mod codegen;
mod catcher;
mod ext;
#[doc(inline)] pub use response::Response;
#[doc(inline)] pub use handler::{Handler, ErrorHandler};

View File

@ -3,12 +3,12 @@
#[macro_use] extern crate rocket;
use std::path::{Path, PathBuf};
use rocket::ext::Normalize;
use rocket::http::ext::Normalize;
use rocket::Route;
#[get("/<path..>")]
fn files(route: &Route, path: PathBuf) -> String {
Path::new(route.base()).join(path).normalized().to_string()
Path::new(route.base()).join(path).normalized_str().to_string()
}
mod route_guard_tests {

View File

@ -3,7 +3,7 @@ extern crate rocket;
#[cfg(test)]
mod tests;
use std::io;
use std::{io, env};
use std::fs::File;
use rocket::{Request, Handler, Route, Data, Catcher};
@ -43,7 +43,7 @@ fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> {
return Outcome::failure(Status::BadRequest);
}
let file = File::create("/tmp/upload.txt");
let file = File::create(env::temp_dir().join("upload.txt"));
if let Ok(mut file) = file {
if let Ok(n) = io::copy(&mut data.open(), &mut file) {
return Outcome::from(req, format!("OK: {} bytes uploaded.", n));
@ -58,7 +58,7 @@ fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> {
}
fn get_upload<'r>(req: &'r Request, _: Data) -> Outcome<'r> {
Outcome::from(req, File::open("/tmp/upload.txt").ok())
Outcome::from(req, File::open(env::temp_dir().join("upload.txt")).ok())
}
fn not_found_handler<'r>(req: &'r Request) -> response::Result<'r> {

View File

@ -4,12 +4,12 @@
#[cfg(test)] mod tests;
use std::io;
use std::{io, env};
use rocket::Data;
#[post("/upload", format = "plain", data = "<data>")]
fn upload(data: Data) -> io::Result<String> {
data.stream_to_file("/tmp/upload.txt").map(|n| n.to_string())
data.stream_to_file(env::temp_dir().join("upload.txt")).map(|n| n.to_string())
}
#[get("/")]

View File

@ -1,6 +1,7 @@
use rocket::local::Client;
use rocket::http::{Status, ContentType};
use std::env;
use std::io::Read;
use std::fs::{self, File};
@ -16,7 +17,8 @@ fn test_index() {
#[test]
fn test_raw_upload() {
// Delete the upload file before we begin.
let _ = fs::remove_file("/tmp/upload.txt");
let upload_file = env::temp_dir().join("upload.txt");
let _ = fs::remove_file(&upload_file);
// Do the upload. Make sure we get the expected results.
let client = Client::new(super::rocket()).unwrap();
@ -30,7 +32,7 @@ fn test_raw_upload() {
// Ensure we find the body in the /tmp/upload.txt file.
let mut file_contents = String::new();
let mut file = File::open("/tmp/upload.txt").expect("open upload.txt file");
let mut file = File::open(&upload_file).expect("open upload.txt file");
file.read_to_string(&mut file_contents).expect("read upload.txt");
assert_eq!(&file_contents, UPLOAD_CONTENTS);
}