Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

match_pattern/match_info/match_name may return incorrect values when using route guards #3346

Open
ethan-lowman-fp opened this issue May 1, 2024 · 0 comments

Comments

@ethan-lowman-fp
Copy link

Suppose we have two routes that may match the same path:

#[actix_web::get("/widgets/{id}")]
async fn get_widget(...) { ... }

#[actix_web::post("/widgets/action")]
async fn widget_action(...) { ... }

and they are registered in an order such that the first pattern shadows the second if we ignore route guards:

App::new().service(get_widget).service(widget_action)

A POST /widget/action request is properly routed to the widget_action handler, but match_pattern() function the HttpRequest incorrectly returns the /widgets/{id} pattern, since route guards are not considered (including the method guards).

Expected Behavior

I'd expect the match_pattern() function to return the actual pattern that was matched, not a synthetic routing evaluation that uses a subset of the full logic.

Current Behavior

match_pattern() may return incorrect results

Possible Solution

Perhaps the request could have internal state that indicates the actual routing evaluation, rather than re-evaluating without Guards when calling match_pattern().

Steps to Reproduce (for bugs)

The following code creates a minimal test server:

// cargo add [email protected] [email protected]

use actix_web::{dev::Service as _, web, App, HttpRequest, HttpResponse, HttpServer};
use futures_util::future::FutureExt;

#[actix_web::get("/widgets/{id}")]
async fn get_widget(req: HttpRequest, id: web::Path<String>) -> HttpResponse {
    println!(
        "[GET /widgets/{id}]  Path: {}, Match Pattern: {:?}",
        req.path(),
        req.match_pattern()
    );
    HttpResponse::Ok().body(format!("The widget ID is: {}\n", id))
}

#[actix_web::post("/widgets/action")]
async fn widget_action(req: HttpRequest) -> HttpResponse {
    println!(
        "[POST /widgets/action] Path: {}, Match Pattern: {:?}",
        req.path(),
        req.match_pattern()
    );
    HttpResponse::Ok().body("Performing the widget action\n")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap_fn(|req, srv| {
                println!(
                    "[Middleware] Path: {}, Match Pattern: {:?}",
                    req.path(),
                    req.match_pattern()
                );
                srv.call(req).map(|res| res)
            })
            .service(get_widget)
            .service(widget_action)
    })
    .bind(("127.0.0.1", 9999))?
    .run()
    .await
}

We run this with cargo run and then test with:

$ curl -X POST http://localhost:9999/widgets/action
Performing the widget action

The server logs the following:

[Middleware] Path: /widgets/action, Match Pattern: Some("/widgets/{id}")
[POST /widgets/action] Path: /widgets/action, Match Pattern: Some("/widgets/{id}")

If the implementations matched the true routing logic, I would expect it to log the following:

[Middleware] Path: /widgets/action, Match Pattern: Some("/widgets/action")
[POST /widgets/action] Path: /widgets/action, Match Pattern: Some("/widgets/action")

Context

Your Environment

  • Rust Version (I.e, output of rustc -V): rustc 1.74.0 (79e9716c9 2023-11-13)
  • Actix Web Version: 4.5.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant