From 0311f9a7eadf5117808c50dd2c7d3cfd6b95d064 Mon Sep 17 00:00:00 2001 From: Nicolas Bachschmidt Date: Tue, 23 Apr 2024 13:17:30 +0200 Subject: [PATCH] Remove HeadResponder (#3147) The HEAD method is identical to GET except that the server must not send content in the response (RFC 9110, section 9.3.2). The previous default behaviour of returning 200 OK to every HEAD request to a constant route is not standard-compliant. The new behaviour is to always forward the request to the GET route, unless the developer explicitly configured a custom HEAD route. --- .../Vapor/Responder/DefaultResponder.swift | 26 ------------------- Tests/VaporTests/RouteTests.swift | 21 +++------------ 2 files changed, 3 insertions(+), 44 deletions(-) diff --git a/Sources/Vapor/Responder/DefaultResponder.swift b/Sources/Vapor/Responder/DefaultResponder.swift index c342131261..1a2dece011 100644 --- a/Sources/Vapor/Responder/DefaultResponder.swift +++ b/Sources/Vapor/Responder/DefaultResponder.swift @@ -39,26 +39,6 @@ internal struct DefaultResponder: Responder { } } - // If the route isn't explicitly a HEAD route, - // and it's made up solely of .constant components, - // register a HEAD route with the same path - if route.method == .GET && - route.path.allSatisfy({ component in - if case .constant(_) = component { return true } - return false - }) { - let headRoute = Route( - method: .HEAD, - path: route.path, - responder: middleware.makeResponder(chainingTo: HeadResponder()), - requestType: route.requestType, - responseType: route.responseType) - - let headCachedRoute = CachedRoute(route: headRoute, responder: middleware.makeResponder(chainingTo: HeadResponder())) - - router.register(headCachedRoute, at: [.constant(HTTPMethod.HEAD.string)] + path) - } - router.register(cached, at: [.constant(route.method.string)] + path) } self.router = router @@ -155,12 +135,6 @@ internal struct DefaultResponder: Responder { } } -private struct HeadResponder: Responder { - func respond(to request: Request) -> EventLoopFuture { - request.eventLoop.makeSucceededFuture(.init(status: .ok)) - } -} - private struct NotFoundResponder: Responder { func respond(to request: Request) -> EventLoopFuture { request.eventLoop.makeFailedFuture(RouteNotFound()) diff --git a/Tests/VaporTests/RouteTests.swift b/Tests/VaporTests/RouteTests.swift index bf28872016..4ecbce263a 100644 --- a/Tests/VaporTests/RouteTests.swift +++ b/Tests/VaporTests/RouteTests.swift @@ -229,38 +229,23 @@ final class RouteTests: XCTestCase { } } - func testHeadRequestWithConstantPathReturnsOK() throws { + func testHeadRequestForwardedToGet() throws { let app = Application(.testing) defer { app.shutdown() } app.get("hello") { req -> String in - return "hi" - } - - try app.testable(method: .running(port: 0)).test(.HEAD, "/hello") { res in - XCTAssertEqual(res.status, .ok) - XCTAssertEqual(res.headers.first(name: .contentLength), "0") - XCTAssertEqual(res.body.readableBytes, 0) - } - } - - func testHeadRequestWithParameterForwardedToGet() throws { - let app = Application(.testing) - defer { app.shutdown() } - - app.get("hello", ":name") { req -> String in XCTAssertEqual(req.method, .HEAD) return "hi" } - try app.testable(method: .running(port: 0)).test(.HEAD, "/hello/joe") { res in + try app.testable(method: .running(port: 0)).test(.HEAD, "/hello") { res in XCTAssertEqual(res.status, .ok) XCTAssertEqual(res.headers.first(name: .contentLength), "2") XCTAssertEqual(res.body.readableBytes, 0) } } - func testExplicitHeadRouteHandlerOverridesGeneratedHandler() throws { + func testExplicitHeadRouteOverridesForwardingToGet() throws { let app = Application(.testing) defer { app.shutdown() }