ibraheemdev / matchit

A high performance, zero-copy URL router.
https://docs.rs/matchit
MIT License
344 stars 35 forks source link

Route with underscore produces unexpected `ExtraTrailingSlash` error instead of `NotFound` #22

Closed arctic-alpaca closed 1 year ago

arctic-alpaca commented 2 years ago

Hi,

I encountered this behaviour when working with axum. I'm not sure whether this is intended or not but for me it was unexpected.

This code returns an MissingTrailingSlash error instead of a NotFound error:

let mut with_underscore = Router::new();
with_underscore.insert("/frontend_api", "Welcome!")?;
with_underscore.insert("/frontend/aaaa", "A User")?;

with_underscore.at("/frontend/").unwrap();

This seems to happen when adding two routes, one with an underscore at the same position where the other one has a slash.

I tried different combinations of routes and only the version described above returned an unintuitive error:

use matchit::Router;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // ExtraTrailingSlash, expected NotFound
    let mut with_underscore = Router::new();
    with_underscore.insert("/frontend_api", "Welcome!")?;
    with_underscore.insert("/frontend/aaaa", "A User")?;

    let with_underscore = with_underscore.at("/frontend/");
    match with_underscore{
        Ok(_) => {}
        Err(e) => {
            println!("with_underscore: \t {e}");
        }
    }

    // NotFound
    let mut without_underscore = Router::new();
    without_underscore.insert("/frontend/api", "Welcome!")?;
    without_underscore.insert("/frontend/aaaa", "A User")?;

    let without_underscore = without_underscore.at("/frontend/");
    match without_underscore{
        Ok(_) => {}
        Err(e) => {
            println!("without_underscore: \t {e}");
        }
    }

    // NotFound
    let mut underscore_at_dif_pos = Router::new();
    underscore_at_dif_pos.insert("/front_endapi", "Welcome!")?;
    underscore_at_dif_pos.insert("/frontend/aaaa", "A User")?;

    let underscore_at_dif_pos = underscore_at_dif_pos.at("/frontend/");
    match underscore_at_dif_pos{
        Ok(_) => {}
        Err(e) => {
            println!("underscore_at_dif_pos: \t {e}");
        }
    }

    // NotFound
    let mut without_second_route = Router::new();
    without_second_route.insert("/frontend/aaaa", "A User")?;

    let without_second_route = without_second_route.at("/frontend/");
    match without_second_route{
        Ok(_) => {}
        Err(e) => {
            println!("without_second_route: \t {e}");
        }
    }

    // ExtraTrailingSlash
    let mut only_frontend = Router::new();
    only_frontend.insert("/frontend", "A User")?;

    let only_frontend = only_frontend.at("/frontend/");
    match only_frontend{
        Ok(_) => {}
        Err(e) => {
            println!("only_frontend: \t\t {e}");
        }
    }

    // NotFound
    let mut different_name = Router::new();
    different_name.insert("/something_else", "Welcome!")?;
    different_name.insert("/frontend/aaaa", "A User")?;

    let different_name = different_name.at("/frontend/");
    match different_name{
        Ok(_) => {}
        Err(e) => {
            println!("different_name: \t {e}");
        }
    }

    Ok(())
}

Cargo.toml:

[dependencies]
matchit = "0.6.0"
ibraheemdev commented 2 years ago

This does look like a bug. I'll look into it.

ibraheemdev commented 2 years ago

Seems it's not only an underscore that causes an issue, any character after the matching prefix causes a false positive in the check here.

ibraheemdev commented 1 year ago

Keeping this open until the fix is released.

ibraheemdev commented 1 year ago

Released in 0.7.0.