Skip to content

Commit a310814

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

File tree

5 files changed

+152
-12
lines changed

5 files changed

+152
-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: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,60 @@ 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")).or_else(|_| try_path(exact))
71+
} else {
72+
try_path(exact)
73+
}
74+
}
75+
76+
fn try_path<P: AsRef<Path>>(path: P) -> Result<PathBuf, EmbedRequestError> {
77+
let path = path.as_ref();
78+
let true_path = path
79+
.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(
86+
path.display().to_string(),
87+
))
88+
}
89+
}
90+
91+
#[cfg(test)]
92+
mod test {
93+
use super::*;
94+
use crate::MockRoot;
95+
96+
#[test]
97+
fn test_translate_path() {
98+
let docroot = MockRoot::builder()
99+
.file("/index.php", "<?php echo \"index\"; ?>")
100+
.file("/foo/index.php", "<?php echo \"sub\"; ?>")
101+
.build()
102+
.expect("should prepare docroot");
103+
104+
assert_eq!(
105+
translate_path(docroot.clone(), "/foo/"),
106+
Ok(docroot.join("foo/index.php"))
107+
);
108+
assert_eq!(
109+
translate_path(docroot.clone(), "/foo"),
110+
Err(EmbedRequestError::ScriptNotFound(
111+
docroot.join("foo").display().to_string()
112+
))
113+
);
114+
}
71115
}

crates/php/src/test.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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
65+
where
66+
D: AsRef<Path>,
67+
{
68+
Self(docroot.as_ref().to_owned(), HashMap::new())
69+
}
70+
71+
pub fn file<P, C>(mut self, path: P, contents: C) -> MockRootBuilder
72+
where
73+
P: AsRef<Path>,
74+
C: Into<String>,
75+
{
76+
let path = path.as_ref().to_owned();
77+
let contents = contents.into();
78+
79+
self.1.insert(path, contents);
80+
self
81+
}
82+
83+
pub fn build(self) -> Result<MockRoot, Error> {
84+
MockRoot::new(self.0, self.1)
85+
}
86+
}
87+
88+
impl Default for MockRootBuilder {
89+
fn default() -> Self {
90+
Self::new(temp_dir().join("php-temp-dir-base"))
91+
}
92+
}

0 commit comments

Comments
 (0)