From fd831deb95ade107d96aea051982b155e7f9992a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 21 Dec 2022 00:19:53 +0000 Subject: [PATCH] add Allow header to resource's default 405 handler --- actix-web/src/guard/mod.rs | 17 ++++++++++++++++- actix-web/src/resource.rs | 27 ++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/actix-web/src/guard/mod.rs b/actix-web/src/guard/mod.rs index 7cd846b66..467dd1592 100644 --- a/actix-web/src/guard/mod.rs +++ b/actix-web/src/guard/mod.rs @@ -286,11 +286,26 @@ pub fn Method(method: HttpMethod) -> impl Guard { MethodGuard(method) } +#[derive(Debug, Clone)] +pub(crate) struct RegisteredMethods(pub(crate) Vec); + /// HTTP method guard. -struct MethodGuard(HttpMethod); +#[derive(Debug)] +pub(crate) struct MethodGuard(HttpMethod); impl Guard for MethodGuard { fn check(&self, ctx: &GuardContext<'_>) -> bool { + let registered = ctx.req_data_mut().remove::(); + + if let Some(mut methods) = registered { + eprintln!("here one"); + methods.0.push(self.0.clone()); + ctx.req_data_mut().insert(methods); + } else { + ctx.req_data_mut() + .insert(RegisteredMethods(vec![self.0.clone()])); + } + ctx.head().method == self.0 } } diff --git a/actix-web/src/resource.rs b/actix-web/src/resource.rs index c5c6701e6..989f80e4c 100644 --- a/actix-web/src/resource.rs +++ b/actix-web/src/resource.rs @@ -13,8 +13,9 @@ use crate::{ body::MessageBody, data::Data, dev::{ensure_leading_slash, AppService, ResourceDef}, - guard::Guard, + guard::{self, Guard}, handler::Handler, + http::header, route::{Route, RouteService}, service::{ BoxedHttpService, BoxedHttpServiceFactory, HttpServiceFactory, ServiceRequest, @@ -66,7 +67,19 @@ impl Resource { guards: Vec::new(), app_data: None, default: boxed::factory(fn_service(|req: ServiceRequest| async { - Ok(req.into_response(HttpResponse::MethodNotAllowed())) + use crate::HttpMessage as _; + + let allowed = req.extensions().get::().cloned(); + + if let Some(methods) = allowed { + Ok(req.into_response( + HttpResponse::MethodNotAllowed() + .insert_header(header::Allow(methods.0)) + .finish(), + )) + } else { + Ok(req.into_response(HttpResponse::MethodNotAllowed())) + } })), } } @@ -606,7 +619,11 @@ mod tests { async fn test_default_resource() { let srv = init_service( App::new() - .service(web::resource("/test").route(web::get().to(HttpResponse::Ok))) + .service( + web::resource("/test") + .route(web::get().to(HttpResponse::Ok)) + .route(web::delete().to(HttpResponse::Ok)), + ) .default_service(|r: ServiceRequest| { ok(r.into_response(HttpResponse::BadRequest())) }), @@ -621,6 +638,10 @@ mod tests { .to_request(); let resp = call_service(&srv, req).await; assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + assert_eq!( + resp.headers().get(header::ALLOW).unwrap().as_bytes(), + b"GET, DELETE" + ); let srv = init_service( App::new().service(