Skip to content

Commit 4a04c60

Browse files
committed
Ensure translate_path is applied correctly
1 parent cc2a7a1 commit 4a04c60

File tree

5 files changed

+147
-12
lines changed

5 files changed

+147
-12
lines changed

crates/php/src/embed.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,15 @@ impl Handler for Embed {
160160
/// # Examples
161161
///
162162
/// ```
163-
/// use std::env::current_dir;
164-
/// use php::{Embed, Handler, Request, Response};
163+
/// use std::{env::temp_dir, fs::File, io::Write};
164+
/// use php::{Embed, Handler, Request, Response, MockRoot};
165165
///
166-
/// let docroot = current_dir()
167-
/// .expect("should have current_dir");
166+
/// let docroot = MockRoot::builder()
167+
/// .file("index.php", "<?php echo \"Hello, World!\"; ?>")
168+
/// .build()
169+
/// .expect("should prepare docroot");
168170
///
169-
/// let handler = Embed::new(docroot, None)
171+
/// let handler = Embed::new(docroot.clone(), None)
170172
/// .expect("should construct Embed");
171173
///
172174
/// let request = Request::builder()

crates/php/src/exception.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// Set of exceptions which may be produced by php::Embed
2-
#[derive(Debug)]
2+
#[derive(Debug, PartialEq, Eq, Hash)]
33
pub enum EmbedStartError {
44
DocRootNotFound(String),
55
ExeLocationNotFound,
@@ -20,7 +20,7 @@ impl std::fmt::Display for EmbedStartError {
2020
}
2121
}
2222

23-
#[derive(Debug)]
23+
#[derive(Debug, PartialEq, Eq, Hash)]
2424
pub enum EmbedRequestError {
2525
SapiNotStarted,
2626
SapiNotShutdown,

crates/php/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ mod request_context;
77
mod sapi;
88
mod scopes;
99
mod strings;
10+
mod test;
1011

1112
pub use lang_handler::{rewrite, Handler, Header, Headers, Request, RequestBuilder, Response, Url};
1213

1314
pub use embed::Embed;
1415
pub use exception::{EmbedRequestError, EmbedStartError};
1516
pub use request_context::RequestContext;
17+
pub use test::MockRoot;

crates/php/src/strings.rs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,58 @@ where
5656
{
5757
let docroot = docroot.as_ref().to_path_buf();
5858
let request_uri = request_uri.as_ref();
59+
5960
let relative_uri = request_uri.strip_prefix("/").map_err(|_| {
6061
let uri = request_uri.display().to_string();
6162
EmbedRequestError::ExpectedAbsoluteRequestUri(uri)
6263
})?;
6364

6465
let exact = docroot.join(relative_uri);
6566

66-
exact.join("index.php").canonicalize().or_else(|_| {
67-
exact
68-
.canonicalize()
69-
.map_err(|_| EmbedRequestError::ScriptNotFound(exact.display().to_string()))
70-
})
67+
// NOTE: String conversion is necessary. If Path::ends_with("/") is used it
68+
// will discard the trailing slash first.
69+
if request_uri.display().to_string().ends_with("/") {
70+
try_path(exact.join("index.php"))
71+
.or_else(|_| try_path(exact))
72+
} else {
73+
try_path(exact)
74+
}
75+
}
76+
77+
fn try_path<P: AsRef<Path>>(path: P) -> Result<PathBuf, EmbedRequestError> {
78+
let path = path.as_ref();
79+
let true_path = path.canonicalize()
80+
.map_err(|_| EmbedRequestError::ScriptNotFound(path.display().to_string()))?;
81+
82+
if true_path.is_file() {
83+
Ok(true_path)
84+
} else {
85+
Err(EmbedRequestError::ScriptNotFound(path.display().to_string()))
86+
}
87+
}
88+
89+
#[cfg(test)]
90+
mod test {
91+
use super::*;
92+
use crate::{MockRoot};
93+
94+
#[test]
95+
fn test_translate_path() {
96+
let docroot = MockRoot::builder()
97+
.file("/index.php", "<?php echo \"index\"; ?>")
98+
.file("/foo/index.php", "<?php echo \"sub\"; ?>")
99+
.build()
100+
.expect("should prepare docroot");
101+
102+
assert_eq!(
103+
translate_path(docroot.clone(), "/foo/"),
104+
Ok(docroot.join("foo/index.php"))
105+
);
106+
assert_eq!(
107+
translate_path(docroot.clone(), "/foo"),
108+
Err(EmbedRequestError::ScriptNotFound(
109+
docroot.join("foo").display().to_string()
110+
))
111+
);
112+
}
71113
}

crates/php/src/test.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use std::{
2+
collections::HashMap,
3+
env::temp_dir,
4+
fs::{create_dir_all, remove_dir_all, File},
5+
io::{Error, Write},
6+
ops::{Deref, DerefMut},
7+
path::{Path, PathBuf}
8+
};
9+
10+
pub struct MockRoot(PathBuf);
11+
12+
impl MockRoot {
13+
pub(crate) fn new<D, H>(docroot: D, files: H) -> Result<Self, Error>
14+
where
15+
D: AsRef<Path>,
16+
H: Into<HashMap<PathBuf, String>>
17+
{
18+
let docroot = docroot.as_ref();
19+
create_dir_all(docroot)?;
20+
21+
let map: HashMap<PathBuf, String> = files.into();
22+
for (path, contents) in map.iter() {
23+
let stripped = path.strip_prefix("/").unwrap_or(path);
24+
25+
let file_path = docroot.join(stripped);
26+
create_dir_all(file_path.parent().unwrap())?;
27+
28+
let mut file = File::create(file_path)?;
29+
file.write_all(contents.as_bytes())?;
30+
}
31+
32+
Ok(Self(docroot.canonicalize().unwrap()))
33+
}
34+
35+
pub fn builder() -> MockRootBuilder {
36+
MockRootBuilder::default()
37+
}
38+
}
39+
40+
impl Drop for MockRoot {
41+
fn drop(&mut self) {
42+
remove_dir_all(&self.0).unwrap();
43+
}
44+
}
45+
46+
impl Deref for MockRoot {
47+
type Target = PathBuf;
48+
49+
fn deref(&self) -> &Self::Target {
50+
&self.0
51+
}
52+
}
53+
54+
impl DerefMut for MockRoot {
55+
fn deref_mut(&mut self) -> &mut Self::Target {
56+
&mut self.0
57+
}
58+
}
59+
60+
#[derive(Debug)]
61+
pub struct MockRootBuilder(PathBuf, HashMap<PathBuf, String>);
62+
63+
impl MockRootBuilder {
64+
pub(crate) fn new<D>(docroot: D) -> Self where D: AsRef<Path> {
65+
Self(docroot.as_ref().to_owned(), HashMap::new())
66+
}
67+
68+
pub fn file<P, C>(mut self, path: P, contents: C) -> MockRootBuilder
69+
where
70+
P: AsRef<Path>,
71+
C: Into<String>
72+
{
73+
let path = path.as_ref().to_owned();
74+
let contents = contents.into();
75+
76+
self.1.insert(path, contents);
77+
self
78+
}
79+
80+
pub fn build(self) -> Result<MockRoot, Error> {
81+
MockRoot::new(self.0, self.1)
82+
}
83+
}
84+
85+
impl Default for MockRootBuilder {
86+
fn default() -> Self {
87+
Self::new(temp_dir().join("php-temp-dir-base"))
88+
}
89+
}

0 commit comments

Comments
 (0)