I want Warp to serve the current working directory. Here is the entire main.rs
:
#[tokio::main]
async fn main() {
let current_dir = std::env::current_dir().expect("failed to read current directory");
warp::serve(warp::fs::dir(current_dir))
.run(([127, 0, 0, 1], 3030))
.await;
}
With the following dependencies:
[dependencies]
tokio = { version = "1.5", features = ["full"] }
warp = "0.3"
Then I run it on directory www
with the following structure:
www
├── foo
| └── index.html
| └── style.css
└── bar
└── index.html
└── style.css
The HTML pages are served, but their referenced CSS files are not. The HTML pages reference their respective CSS file using <link rel="stylesheet" href="style.css">
I have this working using node.js express, but with Warp it attempts to load www/style.css
, rather than www/foo/style.css
and www/bar/style.css
.
It works if I change the href to "foo/style.css"
and "bar/style.css"
, but I would like to avoid that if possible. Is there something I can change on Warp's end to fix this?
Edit: I learned that the pages render with the CSS properly if the URL contains a trailing slash.
So this doesn't work:
http://localhost:3030/foo
http://localhost:3030/bar
But this does:
http://localhost:3030/foo/
http://localhost:3030/bar/
Thanks to @Kitsu's comment for a similar question, I learned that this is currently an open issue.
I ended up using a slightly modified version of kolektiv's solution from that discussion. It gets the path using warp::path::full()
, then redirects with the trailing slash if needed. Here is code working as intended for my example.
cargo.toml
's dependencies:
[dependencies]
tokio = { version = "1.5", features = ["full"] }
warp = "0.3"
main.rs
:
use std::str::FromStr;
use warp::{filters::BoxedFilter, http::Uri, path::FullPath, redirect, Filter, Reply};
#[tokio::main]
async fn main() {
let current_dir = std::env::current_dir().expect("failed to read current directory");
let routes = root_redirect().or(warp::fs::dir(current_dir));
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
fn root_redirect() -> BoxedFilter<(impl Reply,)> {
warp::path::full()
.and_then(move |path: FullPath| async move {
let path = path.as_str();
// do not redirect if the path ends in a trailing slash
// or contains a period (indicating a specific file, e.g. style.css)
if path.ends_with("/") || path.contains(".") {
return Err(warp::reject());
}
Ok(redirect::redirect(
Uri::from_str(&[path, "/"].concat()).unwrap(),
))
})
.boxed()
}