warp icon indicating copy to clipboard operation
warp copied to clipboard

`warp::filters::fs::dir` handles `index.html` redirection incorrectly

Open jimblandy opened this issue 2 years ago • 2 comments

When I run the following program:

use std::{io, path};
use warp::Filter;

#[tokio::main]
async fn main() -> io::Result<()> {
    let cwd = path::Path::new(".").canonicalize()?;
    warp::serve(warp::path("static").and(warp::fs::dir(cwd)))
        .run(([0, 0, 0, 0], 3030))
        .await;

    Ok(())
}

in a directory containing files index.html and index.css with the contents:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="index.css">
  </head>

  <body>
    <h1>Hi!</h1>
  </body>
</html>

and

h1 {
    background: blue;
}

and then visit localhost:3030/static in a web browser, I'm shipped the content of index.html, but it cannot find its index.css file. The header "Hi!" should appear in blue, but it does not.

This is because the page's document base url is set incorrectly: it is localhost:3030/static, when it should be localhost:3030/static/ (note the trailing slash). This leads the web browser to request http://akatsuki:3030/index.css instead of the correct http://akatsuki:3030/static/index.css. If I instead visit the url localhost:3030/static/, then the web browser is able to generate the correct relative URL for index.css.

The problem is in the way warp::fs::dir handles requests to directories. If the path refers to a directory, dir simply adds index.html to the end:

if is_dir {
    tracing::debug!("dir: appending index.html to directory path");
    buf.push("index.html");
}

If you look at the way other servers handle requests for URLs whose paths lack a trailing slash, you'll see that it first sends a 302 FOUND response with a Location response header that adds the slash. For URLs whose paths do have a trailing slash, they ship out index.html.

jimblandy avatar Apr 05 '22 05:04 jimblandy

Note that the warp::path("static") is essential to the problem, since browsers automatically request / when the URL's path is empty (that is, requesting localhost:3030 always sends GET / HTTP/1.1), so the document base URL is set correctly.

jimblandy avatar Apr 05 '22 05:04 jimblandy

If you look at the way other servers handle requests for URLs whose paths lack a trailing slash, you'll see that they first send a 302 FOUND response with a Location response header that adds the slash.

The Python 3 http.server module (i.e., python -m http.server 8000) sends a 301 MOVED PERMANENTLY redirect for this. So I guess it's not fixed exactly what response should be produced.

jimblandy avatar Apr 05 '22 20:04 jimblandy