diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index cd1ad4c66..955522f61 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -240,6 +240,12 @@ impl Args { let is_route_macro = method.is_none(); if let Some(method) = method { + // Per RFC 7231 Section 4.3.2, a server that supports GET must also + // support HEAD. Automatically register a HEAD guard alongside GET + // so that `#[get(...)]` handlers respond to HEAD requests. + if method == MethodType::Get { + methods.insert(MethodTypeExt::Standard(MethodType::Head)); + } methods.insert(MethodTypeExt::Standard(method)); } diff --git a/actix-web-codegen/tests/routes.rs b/actix-web-codegen/tests/routes.rs index 1443f9a75..2dbf70c9a 100644 --- a/actix-web-codegen/tests/routes.rs +++ b/actix-web-codegen/tests/routes.rs @@ -384,3 +384,23 @@ async fn test_wrap() { let body = String::from_utf8(body.to_vec()).unwrap(); assert!(body.contains("wrong number of parameters")); } + +#[get("/head-from-get")] +async fn get_head_implicit() -> impl Responder { + HttpResponse::Ok().body("hello") +} + +#[actix_web::test] +async fn test_get_implies_head() { + let srv = actix_test::start(|| App::new().service(get_head_implicit)); + + // GET should work as usual + let request = srv.request(http::Method::GET, srv.url("/head-from-get")); + let response = request.send().await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + + // HEAD should also work on a #[get] endpoint per RFC 7231 + let request = srv.request(http::Method::HEAD, srv.url("/head-from-get")); + let response = request.send().await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); +}